| Trees | Indices | Help |
|
|---|
|
|
1 """GNUmed medication/substances handling widgets.
2 """
3 #================================================================
4 # $Source: /cvsroot/gnumed/gnumed/gnumed/client/wxpython/gmMedicationWidgets.py,v $
5 # $Id: gmMedicationWidgets.py,v 1.33 2010/02/06 21:39:10 ncq Exp $
6 __version__ = "$Revision: 1.33 $"
7 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
8
9 import logging, sys, os.path
10
11
12 import wx, wx.grid
13
14
15 if __name__ == '__main__':
16 sys.path.insert(0, '../../')
17 from Gnumed.pycommon import gmDispatcher, gmCfg, gmShellAPI, gmTools, gmDateTime
18 from Gnumed.pycommon import gmMatchProvider, gmI18N, gmPrinting, gmCfg2
19 from Gnumed.business import gmPerson, gmATC, gmSurgery, gmMedication, gmForms
20 from Gnumed.wxpython import gmGuiHelpers, gmRegetMixin, gmAuthWidgets, gmEditArea, gmMacro
21 from Gnumed.wxpython import gmCfgWidgets, gmListWidgets, gmPhraseWheel, gmFormWidgets
22
23
24 _log = logging.getLogger('gm.ui')
25 _log.info(__version__)
26 #============================================================
28
29 if parent is None:
30 parent = wx.GetApp().GetTopWindow()
31 #------------------------------------------------------------
32 def refresh(lctrl):
33 atcs = gmATC.get_reference_atcs()
34
35 items = [ [
36 a['atc'],
37 a['term'],
38 u'%s' % gmTools.coalesce(a['ddd'], u''),
39 gmTools.coalesce(a['unit'], u''),
40 gmTools.coalesce(a['administrative_route'], u''),
41 gmTools.coalesce(a['comment'], u''),
42 a['version'],
43 a['lang']
44 ] for a in atcs ]
45 lctrl.set_string_items(items)
46 lctrl.set_data(atcs)
47 #------------------------------------------------------------
48 gmListWidgets.get_choices_from_list (
49 parent = parent,
50 msg = _('\nThe ATC codes as known to GNUmed.\n'),
51 caption = _('Showing ATC codes.'),
52 columns = [ u'ATC', _('Term'), u'DDD', _('Unit'), _(u'Route'), _('Comment'), _('Version'), _('Language') ],
53 single_selection = True,
54 refresh_callback = refresh
55 )
56
57 #============================================================
59
60 if parent is None:
61 parent = wx.GetApp().GetTopWindow()
62
63 #------------------------------------------------------------
64 def delete(component):
65 gmMedication.delete_component_from_branded_drug (
66 brand = component['pk_brand'],
67 component = component['pk_substance_in_brand']
68 )
69 return True
70 #------------------------------------------------------------
71 def refresh(lctrl):
72 substs = gmMedication.get_substances_in_brands()
73 items = [ [
74 u'%s%s' % (s['brand'], gmTools.coalesce(s['atc_brand'], u'', u' (%s)')),
75 s['substance'],
76 gmTools.coalesce(s['atc_substance'], u''),
77 s['preparation'],
78 gmTools.coalesce(s['external_code_brand'], u''),
79 s['pk_substance_in_brand']
80 ] for s in substs ]
81 lctrl.set_string_items(items)
82 lctrl.set_data(substs)
83 #------------------------------------------------------------
84 msg = _('\nThese are the substances in the drug brands known to GNUmed.\n')
85
86 gmListWidgets.get_choices_from_list (
87 parent = parent,
88 msg = msg,
89 caption = _('Showing drug brand components (substances).'),
90 columns = [_('Brand'), _('Substance'), u'ATC', _('Preparation'), _('Code'), u'#'],
91 single_selection = True,
92 #new_callback = new,
93 #edit_callback = edit,
94 delete_callback = delete,
95 refresh_callback = refresh
96 )
97 #============================================================
99
100 if parent is None:
101 parent = wx.GetApp().GetTopWindow()
102 #------------------------------------------------------------
103 def delete(brand):
104 gmMedication.delete_branded_drug(brand = brand['pk'])
105 return True
106 #------------------------------------------------------------
107 def new():
108 drug_db = get_drug_database(parent = parent)
109
110 if drug_db is None:
111 return False
112
113 drug_db.import_drugs()
114
115 return True
116 #------------------------------------------------------------
117 def refresh(lctrl):
118 drugs = gmMedication.get_branded_drugs()
119 items = [ [
120 d['description'],
121 d['preparation'],
122 gmTools.coalesce(d['atc_code'], u''),
123 gmTools.coalesce(d['external_code'], u''),
124 d['pk']
125 ] for d in drugs ]
126 lctrl.set_string_items(items)
127 lctrl.set_data(drugs)
128 #------------------------------------------------------------
129 msg = _('\nThese are the drug brands known to GNUmed.\n')
130
131 gmListWidgets.get_choices_from_list (
132 parent = parent,
133 msg = msg,
134 caption = _('Showing branded drugs.'),
135 columns = [_('Name'), _('Preparation'), _('ATC'), _('Code'), u'#'],
136 single_selection = True,
137 refresh_callback = refresh,
138 new_callback = new,
139 #edit_callback = edit,
140 delete_callback = delete
141 )
142 #============================================================
144
145 if parent is None:
146 parent = wx.GetApp().GetTopWindow()
147 #------------------------------------------------------------
148 def delete(substance):
149 gmMedication.delete_used_substance(substance = substance['pk'])
150 return True
151 #------------------------------------------------------------
152 def new():
153 drug_db = get_drug_database(parent = parent)
154
155 if drug_db is None:
156 return False
157
158 drug_db.import_drugs()
159
160 return True
161 #------------------------------------------------------------
162 def refresh(lctrl):
163 substs = gmMedication.get_substances_in_use()
164 items = [ [
165 s['description'],
166 gmTools.coalesce(s['atc_code'], u''),
167 s['pk']
168 ] for s in substs ]
169 lctrl.set_string_items(items)
170 lctrl.set_data(substs)
171 #------------------------------------------------------------
172 msg = _('\nThese are the substances currently or previously\nconsumed across all patients.\n')
173
174 gmListWidgets.get_choices_from_list (
175 parent = parent,
176 msg = msg,
177 caption = _('Showing consumed substances.'),
178 columns = [_('Name'), _('ATC'), u'#'],
179 single_selection = True,
180 refresh_callback = refresh,
181 new_callback = new,
182 #edit_callback = edit,
183 delete_callback = delete
184 )
185 #============================================================
186 # generic drug database access
187 #============================================================
189 gmCfgWidgets.configure_string_from_list_option (
190 parent = parent,
191 message = _(
192 '\n'
193 'Please select the default drug data source from the list below.\n'
194 '\n'
195 'Note that to actually use it you need to have the database installed, too.'
196 ),
197 option = 'external.drug_data.default_source',
198 bias = 'user',
199 default_value = None,
200 choices = gmMedication.drug_data_source_interfaces.keys(),
201 columns = [_('Drug data source')],
202 data = gmMedication.drug_data_source_interfaces.keys(),
203 caption = _('Configuring default drug data source')
204 )
205 #============================================================
207 dbcfg = gmCfg.cCfgSQL()
208
209 default_db = dbcfg.get2 (
210 option = 'external.drug_data.default_source',
211 workplace = gmSurgery.gmCurrentPractice().active_workplace,
212 bias = 'workplace'
213 )
214
215 if default_db is None:
216 gmDispatcher.send('statustext', msg = _('No default drug database configured.'), beep = True)
217 configure_drug_data_source(parent = parent)
218 default_db = dbcfg.get2 (
219 option = 'external.drug_data.default_source',
220 workplace = gmSurgery.gmCurrentPractice().active_workplace,
221 bias = 'workplace'
222 )
223 if default_db is None:
224 gmGuiHelpers.gm_show_error (
225 aMessage = _('There is no default drug database configured.'),
226 aTitle = _('Jumping to drug database')
227 )
228 return None
229
230 try:
231 return gmMedication.drug_data_source_interfaces[default_db]()
232 except KeyError:
233 _log.error('faulty default drug data source configuration: %s', default_db)
234 return None
235 #============================================================
237 dbcfg = gmCfg.cCfgSQL()
238 drug_db = get_drug_database()
239 if drug_db is None:
240 return
241 drug_db.switch_to_frontend(blocking = False)
242 #============================================================
244
245 dbcfg = gmCfg.cCfgSQL()
246
247 ifap_cmd = dbcfg.get2 (
248 option = 'external.ifap-win.shell_command',
249 workplace = gmSurgery.gmCurrentPractice().active_workplace,
250 bias = 'workplace',
251 default = 'wine "C:\Ifapwin\WIAMDB.EXE"'
252 )
253 found, binary = gmShellAPI.detect_external_binary(ifap_cmd)
254 if not found:
255 gmDispatcher.send('statustext', msg = _('Cannot call IFAP via [%s].') % ifap_cmd)
256 return False
257 ifap_cmd = binary
258
259 if import_drugs:
260 transfer_file = os.path.expanduser(dbcfg.get2 (
261 option = 'external.ifap-win.transfer_file',
262 workplace = gmSurgery.gmCurrentPractice().active_workplace,
263 bias = 'workplace',
264 default = '~/.wine/drive_c/Ifapwin/ifap2gnumed.csv'
265 ))
266 # file must exist for Ifap to write into it
267 try:
268 f = open(transfer_file, 'w+b').close()
269 except IOError:
270 _log.exception('Cannot create IFAP <-> GNUmed transfer file [%s]', transfer_file)
271 gmDispatcher.send('statustext', msg = _('Cannot create IFAP <-> GNUmed transfer file [%s].') % transfer_file)
272 return False
273
274 wx.BeginBusyCursor()
275 gmShellAPI.run_command_in_shell(command = ifap_cmd, blocking = import_drugs)
276 wx.EndBusyCursor()
277
278 if import_drugs:
279 # COMMENT: this file must exist PRIOR to invoking IFAP
280 # COMMENT: or else IFAP will not write data into it ...
281 try:
282 csv_file = open(transfer_file, 'rb') # FIXME: encoding
283 except:
284 _log.exception('cannot access [%s]', fname)
285 csv_file = None
286
287 if csv_file is not None:
288 import csv
289 csv_lines = csv.DictReader (
290 csv_file,
291 fieldnames = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split(),
292 delimiter = ';'
293 )
294 pat = gmPerson.gmCurrentPatient()
295 emr = pat.get_emr()
296 # dummy episode for now
297 epi = emr.add_episode(episode_name = _('Current medication'))
298 for line in csv_lines:
299 narr = u'%sx %s %s %s (\u2258 %s %s) von %s (%s)' % (
300 line['Packungszahl'].strip(),
301 line['Handelsname'].strip(),
302 line['Form'].strip(),
303 line[u'Packungsgr\xf6\xdfe'].strip(),
304 line['Abpackungsmenge'].strip(),
305 line['Einheit'].strip(),
306 line['Hersteller'].strip(),
307 line['PZN'].strip()
308 )
309 emr.add_clin_narrative(note = narr, soap_cat = 's', episode = epi)
310 csv_file.close()
311
312 return True
313 #============================================================
315
316 dlg = wx.FileDialog (
317 parent = None,
318 message = _('Choose an ATC import config file'),
319 defaultDir = os.path.expanduser(os.path.join('~', 'gnumed')),
320 defaultFile = '',
321 wildcard = "%s (*.conf)|*.conf|%s (*)|*" % (_('config files'), _('all files')),
322 style = wx.OPEN | wx.HIDE_READONLY | wx.FILE_MUST_EXIST
323 )
324
325 result = dlg.ShowModal()
326 if result == wx.ID_CANCEL:
327 return
328
329 cfg_file = dlg.GetPath()
330 dlg.Destroy()
331
332 conn = gmAuthWidgets.get_dbowner_connection(procedure = _('importing ATC reference data'))
333 if conn is None:
334 return False
335
336 wx.BeginBusyCursor()
337
338 if gmATC.atc_import(cfg_fname = cfg_file, conn = conn):
339 gmDispatcher.send(signal = 'statustext', msg = _('Successfully imported ATC reference data.'))
340 else:
341 gmDispatcher.send(signal = 'statustext', msg = _('Importing ATC reference data failed.'), beep = True)
342
343 wx.EndBusyCursor()
344 return True
345
346 #============================================================
347 # current medication widgets
348 #============================================================
350
352
353 query = u"""
354 SELECT schedule as sched, schedule
355 FROM clin.substance_intake
356 where schedule %(fragment_condition)s
357 ORDER BY sched
358 LIMIT 50"""
359
360 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
361 mp.setThresholds(1, 2, 4)
362 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
363 self.SetToolTipString(_('The schedule for taking this substance.'))
364 self.matcher = mp
365 self.selection_only = False
366 #============================================================
368
370
371 query = u"""
372 (
373 SELECT preparation as prep, preparation
374 FROM ref.branded_drug
375 where preparation %(fragment_condition)s
376 ) union (
377 SELECT preparation as prep, preparation
378 FROM clin.substance_intake
379 where preparation %(fragment_condition)s
380 )
381 order by prep
382 limit 30"""
383
384 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
385 mp.setThresholds(1, 2, 4)
386 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
387 self.SetToolTipString(_('The preparation (form) of the substance the patient is taking.'))
388 self.matcher = mp
389 self.selection_only = False
390 #============================================================
392
394
395 query = u"""
396 (
397 SELECT pk, (coalesce(atc_code || ': ', '') || description) as subst
398 FROM clin.consumed_substance
399 WHERE description %(fragment_condition)s
400 ) union (
401 SELECT NULL, (coalesce(atc_code || ': ', '') || description) as subst
402 FROM ref.substance_in_brand
403 WHERE description %(fragment_condition)s
404 )
405 order by subst
406 limit 50"""
407
408 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
409 mp.setThresholds(1, 2, 4)
410 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
411 self.SetToolTipString(_('The INN / substance the patient is taking.'))
412 self.matcher = mp
413 self.selection_only = False
414 #============================================================
416
418
419 query = u"""
420 SELECT pk, (coalesce(atc_code || ': ', '') || description || ' (' || preparation || ')') as brand
421 FROM ref.branded_drug
422 WHERE description %(fragment_condition)s
423 ORDER BY brand
424 LIMIT 50"""
425
426 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
427 mp.setThresholds(2, 3, 4)
428 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
429 self.SetToolTipString(_('The brand name of the drug the patient is taking.'))
430 self.matcher = mp
431 self.selection_only = False
432
433 #============================================================
434 from Gnumed.wxGladeWidgets import wxgCurrentMedicationEAPnl
435
436 -class cCurrentMedicationEAPnl(wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl, gmEditArea.cGenericEditAreaMixin):
437
439
440 try:
441 data = kwargs['substance']
442 del kwargs['substance']
443 except KeyError:
444 data = None
445
446 wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl.__init__(self, *args, **kwargs)
447 gmEditArea.cGenericEditAreaMixin.__init__(self)
448 self.mode = 'new'
449 self.data = data
450 if data is not None:
451 self.mode = 'edit'
452
453 self.__init_ui()
454 #----------------------------------------------------------------
456
457 # adjust phrasewheels
458
459 self._PRW_brand.add_callback_on_lose_focus(callback = self._on_leave_brand)
460
461 #----------------------------------------------------------------
462 # generic Edit Area mixin API
463 #----------------------------------------------------------------
465
466 validity = True
467
468 if self._PRW_substance.GetValue().strip() == u'':
469 self._PRW_substance.display_as_valid(False)
470 validity = False
471 else:
472 self._PRW_substance.display_as_valid(True)
473
474 if self._PRW_preparation.GetValue().strip() == u'':
475 self._PRW_preparation.display_as_valid(False)
476 validity = False
477 else:
478 self._PRW_preparation.display_as_valid(True)
479
480 if self._CHBOX_approved.IsChecked():
481 if self._PRW_episode.GetValue().strip() == u'':
482 self._PRW_episode.display_as_valid(False)
483 validity = False
484 else:
485 self._PRW_episode.display_as_valid(True)
486
487 if self._CHBOX_approved.IsChecked() is True:
488 self._PRW_duration.display_as_valid(True)
489 else:
490 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
491 self._PRW_duration.display_as_valid(True)
492 else:
493 if gmDateTime.str2interval(self._PRW_duration.GetValue()) is None:
494 self._PRW_duration.display_as_valid(False)
495 validity = False
496 else:
497 self._PRW_duration.display_as_valid(True)
498
499 if validity is False:
500 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save substance intake. Invalid or missing essential input.'))
501
502 return validity
503 #----------------------------------------------------------------
505
506 emr = gmPerson.gmCurrentPatient().get_emr()
507
508 # 1) create substance intake entry
509 if self._PRW_substance.GetData() is None:
510 subst = self._PRW_substance.GetValue().strip()
511 else:
512 # normalize, do not simply re-use name from phrasewheel
513 subst = gmMedication.get_substance_by_pk(pk = self._PRW_substance.GetData())['description']
514
515 intake = emr.add_substance_intake (
516 substance = subst,
517 episode = self._PRW_episode.GetData(can_create = True),
518 preparation = self._PRW_preparation.GetValue()
519 )
520
521 intake['strength'] = self._PRW_strength.GetValue()
522 intake['started'] = gmDateTime.wxDate2py_dt(wxDate = self._DP_started.GetValue())
523 intake['schedule'] = self._PRW_schedule.GetValue()
524 intake['aim'] = self._PRW_aim.GetValue()
525 intake['notes'] = self._PRW_notes.GetValue()
526 intake['is_long_term'] = self._CHBOX_long_term.IsChecked()
527 intake['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
528
529 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
530 intake['duration'] = None
531 else:
532 intake['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
533
534 # 2) create or retrieve brand
535 brand = None
536 pk_brand = self._PRW_brand.GetData()
537
538 # brand pre-selected ?
539 if pk_brand is None:
540 # no, so ...
541 desc = self._PRW_brand.GetValue().strip()
542 if desc != u'':
543 # ... create or get it
544 brand = gmMedication.create_branded_drug (
545 brand_name = desc,
546 preparation = self._PRW_preparation.GetValue().strip(),
547 return_existing = True
548 )
549 pk_brand = brand['pk']
550 else:
551 # yes, so get it
552 brand = gmMedication.cBrandedDrug(aPK_obj = pk_brand)
553
554 # 3) link brand, if available
555 intake['pk_brand'] = pk_brand
556 intake.save()
557
558 # brand neither creatable nor pre-selected
559 if brand is None:
560 self.data = intake
561 return True
562
563 # 4) add substance to brand as component (because
564 # that's effectively what we are saying here)
565 # FIXME: we may want to ask the user here
566 # FIXME: or only do it if there are no components yet
567 if self._PRW_substance.GetData() is None:
568 brand.add_component(substance = self._PRW_substance.GetValue().strip())
569 else:
570 # normalize substance name
571 subst = gmMedication.get_substance_by_pk(pk = self._PRW_substance.GetData())
572 if subst is not None:
573 brand.add_component(substance = subst['description'])
574
575 self.data = intake
576 return True
577 #----------------------------------------------------------------
579
580 if self._PRW_substance.GetData() is None:
581 self.data['pk_substance'] = gmMedication.create_used_substance (
582 substance = self._PRW_substance.GetValue().strip()
583 )['pk']
584 else:
585 self.data['pk_substance'] = self._PRW_substance.GetData()
586
587 self.data['started'] = gmDateTime.wxDate2py_dt(wxDate = self._DP_started.GetValue())
588 self.data['preparation'] = self._PRW_preparation.GetValue()
589 self.data['strength'] = self._PRW_strength.GetValue()
590 self.data['schedule'] = self._PRW_schedule.GetValue()
591 self.data['aim'] = self._PRW_aim.GetValue()
592 self.data['notes'] = self._PRW_notes.GetValue()
593 self.data['is_long_term'] = self._CHBOX_long_term.IsChecked()
594 self.data['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
595 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True)
596
597 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
598 self.data['duration'] = None
599 else:
600 self.data['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
601
602 if self._PRW_brand.GetData() is None:
603 desc = self._PRW_brand.GetValue().strip()
604 if desc != u'':
605 # create or get brand
606 self.data['pk_brand'] = gmMedication.create_branded_drug (
607 brand_name = desc,
608 preparation = self._PRW_preparation.GetValue().strip(),
609 return_existing = True
610 )['pk']
611 else:
612 self.data['pk_brand'] = self._PRW_brand.GetData()
613
614 self.data.save()
615 return True
616 #----------------------------------------------------------------
618 self._PRW_substance.SetText(u'', None)
619 self._PRW_strength.SetText(u'', None)
620 self._PRW_preparation.SetText(u'', None)
621 self._PRW_schedule.SetText(u'', None)
622 self._PRW_duration.SetText(u'', None)
623 self._PRW_aim.SetText(u'', None)
624 self._PRW_notes.SetText(u'', None)
625 self._PRW_episode.SetData(None)
626
627 self._CHBOX_long_term.SetValue(False)
628 self._CHBOX_approved.SetValue(True)
629
630 self._DP_started.SetValue(dt = gmDateTime.py_dt2wxDate(py_dt = gmDateTime.pydt_now_here(), wx = wx))
631
632 self._PRW_brand.SetText(u'', None)
633 self._TCTRL_brand_ingredients.SetValue(u'')
634
635 self._PRW_substance.SetFocus()
636 #----------------------------------------------------------------
638
639 self._PRW_substance.SetText(self.data['substance'], self.data['pk_substance'])
640 self._PRW_strength.SetText(gmTools.coalesce(self.data['strength'], u''), self.data['strength'])
641 self._PRW_preparation.SetText(gmTools.coalesce(self.data['preparation'], u''), self.data['preparation'])
642 if self.data['is_long_term']:
643 self._CHBOX_long_term.SetValue(True)
644 self._PRW_duration.Enable(False)
645 self._PRW_duration.SetText(gmTools.u_infinity, None)
646 else:
647 self._CHBOX_long_term.SetValue(False)
648 self._PRW_duration.Enable(True)
649 if self.data['duration'] is None:
650 self._PRW_duration.SetText(u'', None)
651 else:
652 self._PRW_duration.SetText(gmDateTime.format_interval(self.data['duration'], gmDateTime.acc_days), self.data['duration'])
653 self._PRW_aim.SetText(gmTools.coalesce(self.data['aim'], u''), self.data['aim'])
654 self._PRW_notes.SetText(gmTools.coalesce(self.data['notes'], u''), self.data['notes'])
655 self._PRW_episode.SetData(self.data['pk_episode'])
656 self._PRW_schedule.SetText(gmTools.coalesce(self.data['schedule'], u''), self.data['schedule'])
657
658 self._CHBOX_approved.SetValue(self.data['intake_is_approved_of'])
659
660 self._DP_started.SetValue(gmDateTime.py_dt2wxDate(py_dt = self.data['started'], wx = wx))
661
662 self._PRW_brand.SetText(u'', None)
663 self._TCTRL_brand_ingredients.SetValue(u'')
664 if self.data['pk_brand'] is not None:
665 brand = gmMedication.cBrandedDrug(aPK_obj = self.data['pk_brand'])
666 self._PRW_brand.SetText(brand['description'], self.data['pk_brand'])
667 comps = brand.components
668 if comps is not None:
669 comps = u' / '.join([ u'%s%s' % (c['description'], gmTools.coalesce(c['atc_code'], u'', u' (%s)')) for c in comps ])
670 self._TCTRL_brand_ingredients.SetValue(comps)
671
672 self._PRW_substance.SetFocus()
673 #----------------------------------------------------------------
675 self._refresh_as_new()
676
677 self._PRW_substance.SetText(u'', None)
678 self._PRW_strength.SetText(u'', None)
679 self._PRW_notes.SetText(u'', None)
680
681 self._PRW_substance.SetFocus()
682 #----------------------------------------------------------------
683 # event handlers
684 #----------------------------------------------------------------
686 self._TCTRL_brand_ingredients.SetValue(u'')
687
688 pk_brand = self._PRW_brand.GetData()
689 if pk_brand is None:
690 return
691
692 brand = gmMedication.cBrandedDrug(aPK_obj = pk_brand)
693 self._PRW_preparation.SetText(brand['preparation'], None)
694
695 comps = brand.components
696 if comps is None:
697 return
698 comps = u' / '.join([ u'%s%s' % (c['description'], gmTools.coalesce(c['atc_code'], u'', u' (%s)')) for c in comps ])
699 self._TCTRL_brand_ingredients.SetValue(comps)
700 #----------------------------------------------------------------
716 #----------------------------------------------------------------
732 #----------------------------------------------------------------
738 #============================================================
740 delete_it = gmGuiHelpers.gm_show_question (
741 aMessage = _(
742 'Do you really want to remove this substance intake ?\n'
743 '\n'
744 'It may be prudent to edit the details first so as to\n'
745 'leave behind some indication of why it was deleted.\n'
746 ),
747 aTitle = _('Deleting medication / substance intake')
748 )
749 if not delete_it:
750 return
751
752 gmMedication.delete_substance_intake(substance = substance)
753 #------------------------------------------------------------
755 ea = cCurrentMedicationEAPnl(parent = parent, id = -1, substance = substance)
756 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = (substance is not None))
757 dlg.SetTitle(gmTools.coalesce(substance, _('Adding substance intake'), _('Editing substance intake')))
758 if dlg.ShowModal() == wx.ID_OK:
759 dlg.Destroy()
760 return True
761 dlg.Destroy()
762 return False
763 #============================================================
764 # current substances grid
765 #------------------------------------------------------------
767
768 if parent is None:
769 parent = wx.GetApp().GetTopWindow()
770
771 template = gmFormWidgets.manage_form_templates(parent = parent)
772 option = u'form_templates.medication_list'
773
774 if template is None:
775 gmDispatcher.send(signal = 'statustext', msg = _('No medication list template configured.'), beep = True)
776 return None
777
778 dbcfg = gmCfg.cCfgSQL()
779 dbcfg.set (
780 workplace = gmSurgery.gmCurrentPractice().active_workplace,
781 option = option,
782 value = u'%s - %s' % (template['name_long'], template['external_version'])
783 )
784
785 return template
786 #------------------------------------------------------------
788
789 if parent is None:
790 parent = wx.GetApp().GetTopWindow()
791
792 # 1) get template
793 dbcfg = gmCfg.cCfgSQL()
794 option = u'form_templates.medication_list'
795
796 template = dbcfg.get2 (
797 option = option,
798 workplace = gmSurgery.gmCurrentPractice().active_workplace,
799 bias = 'user'
800 )
801
802 if template is None:
803 template = configure_medication_list_template(parent = parent)
804 if template is None:
805 gmGuiHelpers.gm_show_error (
806 aMessage = _('There is no medication list template configured.'),
807 aTitle = _('Printing medication list')
808 )
809 return False
810 else:
811 try:
812 name, ver = template.split(u' - ')
813 except:
814 _log.exception('problem splitting medication list template name [%s]', template)
815 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading medication list template.'), beep = True)
816 return False
817 template = gmForms.get_form_template(name_long = name, external_version = ver)
818 if template is None:
819 gmGuiHelpers.gm_show_error (
820 aMessage = _('Cannot load medication list template [%s - %s]') % (name, ver),
821 aTitle = _('Printing medication list')
822 )
823 return False
824
825 # 2) process template
826 meds_list = template.instantiate()
827 ph = gmMacro.gmPlaceholderHandler()
828 #ph.debug = True
829 meds_list.substitute_placeholders(data_source = ph)
830 pdf_name = meds_list.generate_output(cleanup = cleanup)
831 if cleanup:
832 meds_list.cleanup()
833 if pdf_name is None:
834 gmGuiHelpers.gm_show_error (
835 aMessage = _('Error generating the medication list.'),
836 aTitle = _('Printing medication list')
837 )
838 return False
839
840 # 3) print template
841 printed = gmPrinting.print_file_by_shellscript(filename = pdf_name, jobtype = 'medication_list')
842 if not printed:
843 gmGuiHelpers.gm_show_error (
844 aMessage = _('Error printing the medication list.'),
845 aTitle = _('Printing medication list')
846 )
847 return False
848
849 pat = gmPerson.gmCurrentPatient()
850 emr = pat.get_emr()
851 epi = emr.add_episode(episode_name = 'administration', is_open = False)
852 emr.add_clin_narrative (
853 soap_cat = None,
854 note = _('medication list printed from template [%s - %s]') % (template['name_long'], template['external_version']),
855 episode = epi
856 )
857
858 return True
859 #------------------------------------------------------------
861 """A grid class for displaying current substance intake.
862
863 - does NOT listen to the currently active patient
864 - thereby it can display any patient at any time
865 """
867
868 wx.grid.Grid.__init__(self, *args, **kwargs)
869
870 self.__patient = None
871 self.__row_data = {}
872 self.__row_tooltips = {}
873 self.__prev_row = None
874 self.__prev_tooltip_row = None
875 self.__prev_cell_0 = None
876 self.__grouping_mode = u'episode'
877 self.__filter_show_unapproved = False
878 self.__filter_show_inactive = False
879
880 self.__grouping2col_labels = {
881 u'episode': [
882 _('Episode'),
883 _('Substance'),
884 _('Dose'),
885 _('Schedule'),
886 _('Started'),
887 _('Duration'),
888 _('Brand')
889 ],
890 u'brand': [
891 _('Brand'),
892 _('Schedule'),
893 _('Substance'),
894 _('Dose'),
895 _('Started'),
896 _('Duration'),
897 _('Episode')
898 ]
899 }
900
901 self.__grouping2order_by_clauses = {
902 u'episode': u'pk_health_issue nulls first, episode, substance, started',
903 u'brand': u'brand nulls last, substance, started'
904 }
905
906 self.__init_ui()
907 self.__register_events()
908 #------------------------------------------------------------
909 # external API
910 #------------------------------------------------------------
912
913 sel_block_top_left = self.GetSelectionBlockTopLeft()
914 sel_block_bottom_right = self.GetSelectionBlockBottomRight()
915 sel_cols = self.GetSelectedCols()
916 sel_rows = self.GetSelectedRows()
917
918 selected_cells = []
919
920 # individually selected cells (ctrl-click)
921 selected_cells += self.GetSelectedCells()
922
923 # selected rows
924 selected_cells += list (
925 (row, col)
926 for row in sel_rows
927 for col in xrange(self.GetNumberCols())
928 )
929
930 # selected columns
931 selected_cells += list (
932 (row, col)
933 for row in xrange(self.GetNumberRows())
934 for col in sel_cols
935 )
936
937 # selection blocks
938 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()):
939 selected_cells += [
940 (row, col)
941 for row in xrange(top_left[0], bottom_right[0] + 1)
942 for col in xrange(top_left[1], bottom_right[1] + 1)
943 ]
944
945 return set(selected_cells)
946 #------------------------------------------------------------
948 rows = {}
949
950 for row, col in self.get_selected_cells():
951 rows[row] = True
952
953 return rows.keys()
954 #------------------------------------------------------------
956 return [ self.__row_data[row] for row in self.get_selected_rows() ]
957 #------------------------------------------------------------
959
960 self.empty_grid()
961
962 if self.__patient is None:
963 return
964
965 emr = self.__patient.get_emr()
966 meds = emr.get_current_substance_intake (
967 order_by = self.__grouping2order_by_clauses[self.__grouping_mode],
968 include_unapproved = self.__filter_show_unapproved,
969 include_inactive = self.__filter_show_inactive
970 )
971 if not meds:
972 return
973
974 self.BeginBatch()
975
976 # columns
977 labels = self.__grouping2col_labels[self.__grouping_mode]
978 if self.__filter_show_unapproved:
979 self.AppendCols(numCols = len(labels) + 1)
980 else:
981 self.AppendCols(numCols = len(labels))
982 for col_idx in range(len(labels)):
983 self.SetColLabelValue(col_idx, labels[col_idx])
984 if self.__filter_show_unapproved:
985 self.SetColLabelValue(len(labels), u'OK?')
986 self.SetColSize(len(labels), 40)
987
988 self.AppendRows(numRows = len(meds))
989
990 # loop over data
991 for row_idx in range(len(meds)):
992 med = meds[row_idx]
993 self.__row_data[row_idx] = med
994
995 if med['is_currently_active'] is not True:
996 attr = self.GetOrCreateCellAttr(row_idx, 0)
997 attr.SetTextColour('grey')
998 self.SetRowAttr(row_idx, attr)
999
1000 if self.__grouping_mode == u'episode':
1001 if med['pk_episode'] is None:
1002 self.__prev_cell_0 = None
1003 self.SetCellValue(row_idx, 0, gmTools.u_diameter)
1004 else:
1005 if self.__prev_cell_0 != med['episode']:
1006 self.__prev_cell_0 = med['episode']
1007 self.SetCellValue(row_idx, 0, gmTools.coalesce(med['episode'], u''))
1008
1009 self.SetCellValue(row_idx, 1, med['substance'])
1010 self.SetCellValue(row_idx, 2, gmTools.coalesce(med['strength'], u''))
1011 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], u''))
1012 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
1013
1014 if med['is_long_term']:
1015 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
1016 else:
1017 if med['duration'] is None:
1018 self.SetCellValue(row_idx, 5, u'')
1019 else:
1020 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
1021
1022 if med['pk_brand'] is None:
1023 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['brand'], u''))
1024 else:
1025 if med['fake_brand']:
1026 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['brand'], u'', _('%s (fake)')))
1027 else:
1028 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['brand'], u''))
1029
1030 elif self.__grouping_mode == u'brand':
1031
1032 if med['pk_brand'] is None:
1033 self.__prev_cell_0 = None
1034 self.SetCellValue(row_idx, 0, gmTools.u_diameter)
1035 else:
1036 if self.__prev_cell_0 != med['brand']:
1037 self.__prev_cell_0 = med['brand']
1038 if med['fake_brand']:
1039 self.SetCellValue(row_idx, 0, gmTools.coalesce(med['brand'], u'', _('%s (fake)')))
1040 else:
1041 self.SetCellValue(row_idx, 0, gmTools.coalesce(med['brand'], u''))
1042
1043 self.SetCellValue(row_idx, 1, gmTools.coalesce(med['schedule'], u''))
1044 self.SetCellValue(row_idx, 2, med['substance'])
1045 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['strength'], u''))
1046 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
1047
1048 if med['is_long_term']:
1049 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
1050 else:
1051 if med['duration'] is None:
1052 self.SetCellValue(row_idx, 5, u'')
1053 else:
1054 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
1055
1056 if med['pk_episode'] is None:
1057 self.SetCellValue(row_idx, 6, u'')
1058 else:
1059 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['episode'], u''))
1060
1061 else:
1062 raise ValueError('unknown grouping mode [%s]' % self.__grouping_mode)
1063
1064 if self.__filter_show_unapproved:
1065 self.SetCellValue (
1066 row_idx,
1067 len(labels),
1068 gmTools.bool2subst(med['intake_is_approved_of'], gmTools.u_checkmark_thin, u'', u'?')
1069 )
1070
1071 #self.SetCellAlignment(row, col, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
1072
1073 self.EndBatch()
1074 #------------------------------------------------------------
1076 self.BeginBatch()
1077 self.ClearGrid()
1078 # Windows cannot do "nothing", it rather decides to assert()
1079 # on thinking it is supposed to do nothing
1080 if self.GetNumberRows() > 0:
1081 self.DeleteRows(pos = 0, numRows = self.GetNumberRows())
1082 if self.GetNumberCols() > 0:
1083 self.DeleteCols(pos = 0, numCols = self.GetNumberCols())
1084 self.EndBatch()
1085 self.__row_data = {}
1086 self.__prev_cell_0 = None
1087 #------------------------------------------------------------
1089
1090 if len(self.__row_data) == 0:
1091 return
1092
1093 sel_rows = self.get_selected_rows()
1094 if len(sel_rows) != 1:
1095 return
1096
1097 drug_db = get_drug_database()
1098 if drug_db is None:
1099 return
1100
1101 drug_db.show_info_on_substance(substance = self.get_selected_data()[0])
1102 #------------------------------------------------------------
1104
1105 if len(self.__row_data) == 0:
1106 return
1107
1108 drug_db = get_drug_database()
1109 if drug_db is None:
1110 return
1111
1112 if len(self.get_selected_rows()) > 1:
1113 drug_db.check_drug_interactions(substances = self.get_selected_data())
1114 else:
1115 drug_db.check_drug_interactions(substances = self.__row_data.values())
1116 #------------------------------------------------------------
1118 edit_intake_of_substance(parent = self, substance = None)
1119 #------------------------------------------------------------
1121
1122 rows = self.get_selected_rows()
1123
1124 if len(rows) == 0:
1125 return
1126
1127 if len(rows) > 1:
1128 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit more than one substance at once.'), beep = True)
1129 return
1130
1131 subst = self.get_selected_data()[0]
1132 edit_intake_of_substance(parent = self, substance = subst)
1133 #------------------------------------------------------------
1135
1136 rows = self.get_selected_rows()
1137
1138 if len(rows) == 0:
1139 return
1140
1141 if len(rows) > 1:
1142 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete more than one substance at once.'), beep = True)
1143 return
1144
1145 subst = self.get_selected_data()[0]
1146 delete_substance_intake(parent = self, substance = subst['pk_substance_intake'])
1147 #------------------------------------------------------------
1149 # there could be some filtering/user interaction going on here
1150 _cfg = gmCfg2.gmCfgData()
1151 print_medication_list(parent = self, cleanup = _cfg.get(option = 'debug'))
1152 #------------------------------------------------------------
1154
1155 entry = self.__row_data[row]
1156
1157 tt = _('Substance intake entry (%s, %s) [#%s] \n') % (
1158 gmTools.bool2subst(entry['is_currently_active'], _('active'), _('inactive')),
1159 gmTools.bool2subst(entry['intake_is_approved_of'], _('approved'), _('unapproved')),
1160 entry['pk_substance_intake']
1161 )
1162
1163 tt += u' ' + _('Substance name: %s [#%s]\n') % (entry['substance'], entry['pk_substance'])
1164 tt += u' ' + _('Preparation: %s\n') % entry['preparation']
1165 if entry['strength'] is not None:
1166 tt += u' ' + _('Amount per dose: %s') % entry['strength']
1167 if entry.ddd is not None:
1168 tt += u' (DDD: %s %s)' % (entry.ddd['ddd'], entry.ddd['unit'])
1169 tt += u'\n'
1170 else:
1171 if entry.ddd is not None:
1172 tt += u' DDD: %s %s' % (entry.ddd['ddd'], entry.ddd['unit'])
1173 tt += u'\n'
1174 tt += gmTools.coalesce(entry['atc_substance'], u'', _(' ATC (substance): %s\n'))
1175
1176 tt += u'\n'
1177
1178 tt += gmTools.coalesce (
1179 entry['brand'],
1180 u'',
1181 _(' Brand name: %%s [#%s]\n') % entry['pk_brand']
1182 )
1183 tt += gmTools.coalesce(entry['atc_brand'], u'', _(' ATC (brand): %s\n'))
1184
1185 tt += u'\n'
1186
1187 tt += gmTools.coalesce(entry['schedule'], u'', _(' Regimen: %s\n'))
1188
1189 if entry['is_long_term']:
1190 duration = u' %s %s' % (gmTools.u_right_arrow, gmTools.u_infinity)
1191 else:
1192 if entry['duration'] is None:
1193 duration = u''
1194 else:
1195 duration = u' %s %s' % (gmTools.u_right_arrow, gmDateTime.format_interval(entry['duration'], gmDateTime.acc_days))
1196
1197 tt += _(' Started %s%s%s\n') % (
1198 entry['started'].strftime('%Y %B %d').decode(gmI18N.get_encoding()),
1199 duration,
1200 gmTools.bool2subst(entry['is_long_term'], _(' (long-term)'), _(' (short-term)'), u'')
1201 )
1202
1203 tt += u'\n'
1204
1205 tt += gmTools.coalesce(entry['aim'], u'', _(' Aim: %s\n'))
1206 tt += gmTools.coalesce(entry['episode'], u'', _(' Episode: %s\n'))
1207 tt += gmTools.coalesce(entry['notes'], u'', _(' Advice: %s\n'))
1208
1209 tt += u'\n'
1210
1211 # tt += _(u'Revisions: %(row_ver)s, last %(mod_when)s by %(mod_by)s.') % ({
1212 tt += _(u'Revisions: Last modified %(mod_when)s by %(mod_by)s.') % ({
1213 #'row_ver': entry['row_version'],
1214 'mod_when': entry['modified_when'].strftime('%c').decode(gmI18N.get_encoding()),
1215 'mod_by': entry['modified_by']
1216 })
1217
1218 return tt
1219 #------------------------------------------------------------
1220 # internal helpers
1221 #------------------------------------------------------------
1223 self.CreateGrid(0, 1)
1224 self.EnableEditing(0)
1225 self.EnableDragGridSize(1)
1226 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows)
1227
1228 self.SetColLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTER)
1229
1230 self.SetRowLabelSize(0)
1231 self.SetRowLabelAlignment(horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
1232 #------------------------------------------------------------
1233 # properties
1234 #------------------------------------------------------------
1237
1241
1242 patient = property(_get_patient, _set_patient)
1243 #------------------------------------------------------------
1246
1250
1251 grouping_mode = property(_get_grouping_mode, _set_grouping_mode)
1252 #------------------------------------------------------------
1255
1259
1260 filter_show_unapproved = property(_get_filter_show_unapproved, _set_filter_show_unapproved)
1261 #------------------------------------------------------------
1264
1268
1269 filter_show_inactive = property(_get_filter_show_inactive, _set_filter_show_inactive)
1270 #------------------------------------------------------------
1271 # event handling
1272 #------------------------------------------------------------
1274 # dynamic tooltips: GridWindow, GridRowLabelWindow, GridColLabelWindow, GridCornerLabelWindow
1275 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells)
1276 #self.GetGridRowLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_row_labels)
1277 #self.GetGridColLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_col_labels)
1278
1279 # editing cells
1280 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
1281 #------------------------------------------------------------
1283 """Calculate where the mouse is and set the tooltip dynamically."""
1284
1285 # Use CalcUnscrolledPosition() to get the mouse position within the
1286 # entire grid including what's offscreen
1287 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
1288
1289 # use this logic to prevent tooltips outside the actual cells
1290 # apply to GetRowSize, too
1291 # tot = 0
1292 # for col in xrange(self.NumberCols):
1293 # tot += self.GetColSize(col)
1294 # if xpos <= tot:
1295 # self.tool_tip.Tip = 'Tool tip for Column %s' % (
1296 # self.GetColLabelValue(col))
1297 # break
1298 # else: # mouse is in label area beyond the right-most column
1299 # self.tool_tip.Tip = ''
1300
1301 row, col = self.XYToCell(x, y)
1302
1303 if row == self.__prev_tooltip_row:
1304 return
1305
1306 self.__prev_tooltip_row = row
1307
1308 try:
1309 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row))
1310 except KeyError:
1311 pass
1312 #------------------------------------------------------------
1314 row = evt.GetRow()
1315 data = self.__row_data[row]
1316 edit_intake_of_substance(parent = self, substance = data)
1317 #============================================================
1318 from Gnumed.wxGladeWidgets import wxgCurrentSubstancesPnl
1319
1320 -class cCurrentSubstancesPnl(wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl, gmRegetMixin.cRegetOnPaintMixin):
1321
1322 """Panel holding a grid with current substances. Used as notebook page."""
1323
1325
1326 wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl.__init__(self, *args, **kwargs)
1327 gmRegetMixin.cRegetOnPaintMixin.__init__(self)
1328
1329 self.__register_interests()
1330 #-----------------------------------------------------
1331 # reget-on-paint mixin API
1332 #-----------------------------------------------------
1334 """Populate cells with data from model."""
1335 pat = gmPerson.gmCurrentPatient()
1336 if pat.connected:
1337 self._grid_substances.patient = pat
1338 else:
1339 self._grid_substances.patient = None
1340 return True
1341 #--------------------------------------------------------
1342 # event handling
1343 #--------------------------------------------------------
1345 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
1346 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._schedule_data_reget)
1347 gmDispatcher.connect(signal = u'substance_intake_mod_db', receiver = self._schedule_data_reget)
1348 # active_substance_mod_db
1349 # substance_brand_mod_db
1350 #--------------------------------------------------------
1353 #--------------------------------------------------------
1355 self._grid_substances.patient = None
1356 #--------------------------------------------------------
1359 #--------------------------------------------------------
1362 #--------------------------------------------------------
1365 #--------------------------------------------------------
1368 #--------------------------------------------------------
1371 #--------------------------------------------------------
1373 self._grid_substances.grouping_mode = 'episode'
1374 #--------------------------------------------------------
1376 self._grid_substances.grouping_mode = 'brand'
1377 #--------------------------------------------------------
1380 #--------------------------------------------------------
1383 #--------------------------------------------------------
1386 #============================================================
1387 # main
1388 #------------------------------------------------------------
1389 if __name__ == '__main__':
1390
1391 from Gnumed.pycommon import gmI18N
1392
1393 gmI18N.activate_locale()
1394 gmI18N.install_domain(domain = 'gnumed')
1395
1396 #----------------------------------------
1397 #----------------------------------------
1398 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
1399 # test_*()
1400 pass
1401
1402 #============================================================
1403 # $Log: gmMedicationWidgets.py,v $
1404 # Revision 1.33 2010/02/06 21:39:10 ncq
1405 # - browse-atc-reference
1406 # - include DDD in substance intake tooltip
1407 #
1408 # Revision 1.32 2010/01/13 21:52:04 ncq
1409 # - implement phrase wheel on schedule and preparation
1410 #
1411 # Revision 1.31 2010/01/09 20:15:06 ncq
1412 # - use Advice
1413 #
1414 # Revision 1.30 2010/01/08 12:21:04 ncq
1415 # - even better error detection on forms template loading
1416 #
1417 # Revision 1.29 2010/01/06 14:42:20 ncq
1418 # - medication list printing:
1419 # - tie cleanup to --debug
1420 # - better error detection
1421 #
1422 # Revision 1.28 2010/01/01 21:48:15 ncq
1423 # - remove confusing status message
1424 #
1425 # Revision 1.27 2009/12/26 19:08:38 ncq
1426 # - document printing of medication list in EMR
1427 #
1428 # Revision 1.26 2009/12/25 22:07:17 ncq
1429 # - cleanup
1430 # - configure-medication-list-template
1431 # - print-medication-list
1432 # - show-info-on-entry
1433 # - tooltips on substance entry rows
1434 #
1435 # Revision 1.25 2009/12/22 12:02:57 ncq
1436 # - cleanup
1437 #
1438 # Revision 1.24 2009/12/03 17:51:11 ncq
1439 # - explicit ATC col in brand component list
1440 #
1441 # Revision 1.23 2009/12/02 16:50:44 ncq
1442 # - enable brand component deletion
1443 # - normalize substance name before adding as component
1444 #
1445 # Revision 1.22 2009/12/01 21:55:24 ncq
1446 # - branded drug phrasewheel
1447 # - much improved substance intake EA implementation
1448 #
1449 # Revision 1.21 2009/11/30 13:15:20 ncq
1450 # - better meds grid column ordering as per list
1451 #
1452 # Revision 1.20 2009/11/29 20:01:46 ncq
1453 # - substance phrasewheel
1454 #
1455 # Revision 1.19 2009/11/29 16:05:41 ncq
1456 # - must set self.data *late* in _save-as-new or else !
1457 # - properly enable/disable duration PRW
1458 # - grid business logic moved into grid so no need to access self._grid_substances anymore
1459 #
1460 # Revision 1.18 2009/11/28 18:31:30 ncq
1461 # - implement drug brand management
1462 # - implement drug brand component management
1463 #
1464 # Revision 1.17 2009/11/24 20:59:59 ncq
1465 # - use import-drugs rather than import-drugs-as-substances
1466 # - improved grid layout as per list
1467 # - fix get-selected-data
1468 # - make grid wrapper do less
1469 #
1470 # Revision 1.16 2009/11/19 14:44:25 ncq
1471 # - improved plugin
1472 #
1473 # Revision 1.15 2009/11/15 01:09:07 ncq
1474 # - implement grouping/filtering
1475 # - mark unapproved vs approved if showing all substances
1476 #
1477 # Revision 1.14 2009/11/08 20:49:20 ncq
1478 # - implement deletion
1479 # - start grouping/filtering/ row tooltips
1480 #
1481 # Revision 1.13 2009/11/06 15:19:25 ncq
1482 # - implement saving as new substance intake
1483 #
1484 # Revision 1.12 2009/10/29 17:23:24 ncq
1485 # - consolidate get-drug-database
1486 # - much improved substance intake EA
1487 # - better naming and adjust to such
1488 #
1489 # Revision 1.11 2009/10/28 21:48:55 ncq
1490 # - further improved substances grid
1491 #
1492 # Revision 1.10 2009/10/28 16:43:42 ncq
1493 # - start implementing substances edit area
1494 # - enhance grid to allow actual substances management
1495 #
1496 # Revision 1.9 2009/10/26 22:30:58 ncq
1497 # - implement deletion of INN
1498 #
1499 # Revision 1.8 2009/10/21 20:41:53 ncq
1500 # - access MMI from substance management via NEW button
1501 #
1502 # Revision 1.7 2009/10/21 09:21:13 ncq
1503 # - manage substances
1504 # - jump to drug database
1505 #
1506 # Revision 1.6 2009/10/20 10:27:35 ncq
1507 # - implement configuration of drug data source
1508 #
1509 # Revision 1.5 2009/09/01 22:36:08 ncq
1510 # - add jump-to-mmi
1511 #
1512 # Revision 1.4 2009/06/20 12:46:04 ncq
1513 # - move IFAP handling here
1514 #
1515 # Revision 1.3 2009/06/10 21:02:34 ncq
1516 # - update-atc-reference-data
1517 #
1518 # Revision 1.2 2009/05/13 12:20:59 ncq
1519 # - improve and streamline
1520 #
1521 # Revision 1.1 2009/05/12 12:04:01 ncq
1522 # - substance intake handling
1523 #
1524 #
1525
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Tue Feb 9 04:01:56 2010 | http://epydoc.sourceforge.net |