1 """GNUmed medication/substances handling widgets.
2 """
3
4 __version__ = "$Revision: 1.33 $"
5 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
6
7 import logging, sys, os.path, decimal
8
9
10 import wx, wx.grid
11
12
13 if __name__ == '__main__':
14 sys.path.insert(0, '../../')
15 from Gnumed.pycommon import gmDispatcher, gmCfg, gmShellAPI, gmTools, gmDateTime
16 from Gnumed.pycommon import gmMatchProvider, gmI18N, gmPrinting, gmCfg2, gmNetworkTools
17 from Gnumed.business import gmPerson, gmATC, gmSurgery, gmMedication, gmForms, gmStaff
18 from Gnumed.wxpython import gmGuiHelpers, gmRegetMixin, gmAuthWidgets, gmEditArea, gmMacro
19 from Gnumed.wxpython import gmCfgWidgets, gmListWidgets, gmPhraseWheel, gmFormWidgets
20 from Gnumed.wxpython import gmAllergyWidgets
21
22
23 _log = logging.getLogger('gm.ui')
24 _log.info(__version__)
25
26
27
28
46
48 dbcfg = gmCfg.cCfgSQL()
49
50
51 default_db = dbcfg.get2 (
52 option = 'external.drug_data.default_source',
53 workplace = gmSurgery.gmCurrentPractice().active_workplace,
54 bias = 'workplace'
55 )
56
57
58 if default_db is None:
59 gmDispatcher.send('statustext', msg = _('No default drug database configured.'), beep = True)
60 configure_drug_data_source(parent = parent)
61 default_db = dbcfg.get2 (
62 option = 'external.drug_data.default_source',
63 workplace = gmSurgery.gmCurrentPractice().active_workplace,
64 bias = 'workplace'
65 )
66
67 if default_db is None:
68 gmGuiHelpers.gm_show_error (
69 aMessage = _('There is no default drug database configured.'),
70 aTitle = _('Jumping to drug database')
71 )
72 return None
73
74
75
76 try:
77 drug_db = gmMedication.drug_data_source_interfaces[default_db]()
78 except KeyError:
79
80 _log.error('faulty default drug data source configuration: %s', default_db)
81
82 configure_drug_data_source(parent = parent)
83 default_db = dbcfg.get2 (
84 option = 'external.drug_data.default_source',
85 workplace = gmSurgery.gmCurrentPractice().active_workplace,
86 bias = 'workplace'
87 )
88
89 try:
90 drug_db = gmMedication.drug_data_source_interfaces[default_db]()
91 except KeyError:
92 _log.error('still faulty default drug data source configuration: %s', default_db)
93 return None
94
95 pat = gmPerson.gmCurrentPatient()
96 if pat.connected:
97 drug_db.patient = pat
98
99 return drug_db
100
107
108
110
111 dbcfg = gmCfg.cCfgSQL()
112
113 ifap_cmd = dbcfg.get2 (
114 option = 'external.ifap-win.shell_command',
115 workplace = gmSurgery.gmCurrentPractice().active_workplace,
116 bias = 'workplace',
117 default = 'wine "C:\Ifapwin\WIAMDB.EXE"'
118 )
119 found, binary = gmShellAPI.detect_external_binary(ifap_cmd)
120 if not found:
121 gmDispatcher.send('statustext', msg = _('Cannot call IFAP via [%s].') % ifap_cmd)
122 return False
123 ifap_cmd = binary
124
125 if import_drugs:
126 transfer_file = os.path.expanduser(dbcfg.get2 (
127 option = 'external.ifap-win.transfer_file',
128 workplace = gmSurgery.gmCurrentPractice().active_workplace,
129 bias = 'workplace',
130 default = '~/.wine/drive_c/Ifapwin/ifap2gnumed.csv'
131 ))
132
133 try:
134 f = open(transfer_file, 'w+b').close()
135 except IOError:
136 _log.exception('Cannot create IFAP <-> GNUmed transfer file [%s]', transfer_file)
137 gmDispatcher.send('statustext', msg = _('Cannot create IFAP <-> GNUmed transfer file [%s].') % transfer_file)
138 return False
139
140 wx.BeginBusyCursor()
141 gmShellAPI.run_command_in_shell(command = ifap_cmd, blocking = import_drugs)
142 wx.EndBusyCursor()
143
144 if import_drugs:
145
146
147 try:
148 csv_file = open(transfer_file, 'rb')
149 except:
150 _log.exception('cannot access [%s]', fname)
151 csv_file = None
152
153 if csv_file is not None:
154 import csv
155 csv_lines = csv.DictReader (
156 csv_file,
157 fieldnames = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split(),
158 delimiter = ';'
159 )
160 pat = gmPerson.gmCurrentPatient()
161 emr = pat.get_emr()
162
163 epi = emr.add_episode(episode_name = _('Current medication'))
164 for line in csv_lines:
165 narr = u'%sx %s %s %s (\u2258 %s %s) von %s (%s)' % (
166 line['Packungszahl'].strip(),
167 line['Handelsname'].strip(),
168 line['Form'].strip(),
169 line[u'Packungsgr\xf6\xdfe'].strip(),
170 line['Abpackungsmenge'].strip(),
171 line['Einheit'].strip(),
172 line['Hersteller'].strip(),
173 line['PZN'].strip()
174 )
175 emr.add_clin_narrative(note = narr, soap_cat = 's', episode = epi)
176 csv_file.close()
177
178 return True
179
180
181
182
183
185
186 if parent is None:
187 parent = wx.GetApp().GetTopWindow()
188
189 def refresh(lctrl):
190 atcs = gmATC.get_reference_atcs()
191
192 items = [ [
193 a['atc'],
194 a['term'],
195 u'%s' % gmTools.coalesce(a['ddd'], u''),
196 gmTools.coalesce(a['unit'], u''),
197 gmTools.coalesce(a['administrative_route'], u''),
198 gmTools.coalesce(a['comment'], u''),
199 a['version'],
200 a['lang']
201 ] for a in atcs ]
202 lctrl.set_string_items(items)
203 lctrl.set_data(atcs)
204
205 gmListWidgets.get_choices_from_list (
206 parent = parent,
207 msg = _('\nThe ATC codes as known to GNUmed.\n'),
208 caption = _('Showing ATC codes.'),
209 columns = [ u'ATC', _('Term'), u'DDD', _('Unit'), _(u'Route'), _('Comment'), _('Version'), _('Language') ],
210 single_selection = True,
211 refresh_callback = refresh
212 )
213
214
216
217 dlg = wx.FileDialog (
218 parent = None,
219 message = _('Choose an ATC import config file'),
220 defaultDir = os.path.expanduser(os.path.join('~', 'gnumed')),
221 defaultFile = '',
222 wildcard = "%s (*.conf)|*.conf|%s (*)|*" % (_('config files'), _('all files')),
223 style = wx.OPEN | wx.HIDE_READONLY | wx.FILE_MUST_EXIST
224 )
225
226 result = dlg.ShowModal()
227 if result == wx.ID_CANCEL:
228 return
229
230 cfg_file = dlg.GetPath()
231 dlg.Destroy()
232
233 conn = gmAuthWidgets.get_dbowner_connection(procedure = _('importing ATC reference data'))
234 if conn is None:
235 return False
236
237 wx.BeginBusyCursor()
238
239 if gmATC.atc_import(cfg_fname = cfg_file, conn = conn):
240 gmDispatcher.send(signal = 'statustext', msg = _('Successfully imported ATC reference data.'))
241 else:
242 gmDispatcher.send(signal = 'statustext', msg = _('Importing ATC reference data failed.'), beep = True)
243
244 wx.EndBusyCursor()
245 return True
246
247
248
250
252
253 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
254
255 query = u"""
256
257 SELECT DISTINCT ON (label)
258 atc_code,
259 label
260 FROM (
261
262 SELECT
263 code as atc_code,
264 (code || ': ' || term || coalesce(' (' || ddd || unit || ')', ''))
265 AS label
266 FROM ref.atc
267 WHERE
268 term %(fragment_condition)s
269 OR
270 code %(fragment_condition)s
271
272 UNION ALL
273
274 SELECT
275 atc_code,
276 (atc_code || ': ' || description)
277 AS label
278 FROM ref.consumable_substance
279 WHERE
280 description %(fragment_condition)s
281 OR
282 atc_code %(fragment_condition)s
283
284 UNION ALL
285
286 SELECT
287 atc_code,
288 (atc_code || ': ' || description || ' (' || preparation || ')')
289 AS label
290 FROM ref.branded_drug
291 WHERE
292 description %(fragment_condition)s
293 OR
294 atc_code %(fragment_condition)s
295
296 -- it would be nice to be able to include clin.vacc_indication but that's hard to do in SQL
297
298 ) AS candidates
299 WHERE atc_code IS NOT NULL
300 ORDER BY label
301 LIMIT 50"""
302
303 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
304 mp.setThresholds(1, 2, 4)
305
306 self.SetToolTipString(_('Select an ATC (Anatomical-Therapeutic-Chemical) code.'))
307 self.matcher = mp
308 self.selection_only = True
309
310
311
312
314
315 if parent is None:
316 parent = wx.GetApp().GetTopWindow()
317
318 def add_from_db(substance):
319 drug_db = get_drug_database(parent = parent)
320 if drug_db is None:
321 return False
322 drug_db.import_drugs()
323 return True
324
325 def edit(substance=None):
326 return edit_consumable_substance(parent = parent, substance = substance, single_entry = (substance is not None))
327
328 def delete(substance):
329 if substance.is_in_use_by_patients:
330 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete this substance. It is in use.'), beep = True)
331 return False
332
333 return gmMedication.delete_consumable_substance(substance = substance['pk'])
334
335 def refresh(lctrl):
336 substs = gmMedication.get_consumable_substances(order_by = 'description')
337 items = [ [
338 s['description'],
339 s['amount'],
340 s['unit'],
341 gmTools.coalesce(s['atc_code'], u''),
342 s['pk']
343 ] for s in substs ]
344 lctrl.set_string_items(items)
345 lctrl.set_data(substs)
346
347 msg = _('\nThese are the consumable substances registered with GNUmed.\n')
348
349 gmListWidgets.get_choices_from_list (
350 parent = parent,
351 msg = msg,
352 caption = _('Showing consumable substances.'),
353 columns = [_('Substance'), _('Amount'), _('Unit'), 'ATC', u'#'],
354 single_selection = True,
355 new_callback = edit,
356 edit_callback = edit,
357 delete_callback = delete,
358 refresh_callback = refresh,
359 left_extra_button = (_('Import'), _('Import consumable substances from a drug database.'), add_from_db)
360 )
361
362
364
365 if substance is not None:
366 if substance.is_in_use_by_patients:
367 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit this substance. It is in use.'), beep = True)
368 return False
369
370 ea = cConsumableSubstanceEAPnl(parent = parent, id = -1)
371 ea.data = substance
372 ea.mode = gmTools.coalesce(substance, 'new', 'edit')
373 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
374 dlg.SetTitle(gmTools.coalesce(substance, _('Adding new consumable substance'), _('Editing consumable substance')))
375 if dlg.ShowModal() == wx.ID_OK:
376 dlg.Destroy()
377 return True
378 dlg.Destroy()
379 return False
380
381
382 from Gnumed.wxGladeWidgets import wxgConsumableSubstanceEAPnl
383
385
403
404
405
406
407
408
409
410
412
413 validity = True
414
415 if self._TCTRL_substance.GetValue().strip() == u'':
416 validity = False
417 self.display_tctrl_as_valid(tctrl = self._TCTRL_substance, valid = False)
418 self._TCTRL_substance.SetFocus()
419 else:
420 self.display_tctrl_as_valid(tctrl = self._TCTRL_substance, valid = True)
421
422 try:
423 decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.'))
424 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
425 except (TypeError, decimal.InvalidOperation):
426 validity = False
427 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
428 self._TCTRL_amount.SetFocus()
429
430 if self._PRW_unit.GetValue().strip() == u'':
431 validity = False
432 self._PRW_unit.display_as_valid(valid = False)
433 self._TCTRL_substance.SetFocus()
434 else:
435 self._PRW_unit.display_as_valid(valid = True)
436
437 if validity is False:
438 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save consumable substance. Missing essential input.'))
439
440 return validity
441
443 subst = gmMedication.create_consumable_substance (
444 substance = self._TCTRL_substance.GetValue().strip(),
445 atc = self._PRW_atc.GetData(),
446 amount = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.')),
447 unit = gmTools.coalesce(self._PRW_unit.GetData(), self._PRW_unit.GetValue().strip(), function_initial = ('strip', None))
448 )
449 success, data = subst.save()
450 if not success:
451 err, msg = data
452 _log.error(err)
453 _log.error(msg)
454 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save consumable substance. %s') % msg, beep = True)
455 return False
456
457 self.data = subst
458 return True
459
461 self.data['description'] = self._TCTRL_substance.GetValue().strip()
462 self.data['atc_code'] = self._PRW_atc.GetData()
463 self.data['amount'] = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.'))
464 self.data['unit'] = gmTools.coalesce(self._PRW_unit.GetData(), self._PRW_unit.GetValue().strip(), function_initial = ('strip', None))
465 success, data = self.data.save()
466
467 if not success:
468 err, msg = data
469 _log.error(err)
470 _log.error(msg)
471 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save consumable substance. %s') % msg, beep = True)
472 return False
473
474 return True
475
477 self._TCTRL_substance.SetValue(u'')
478 self._TCTRL_amount.SetValue(u'')
479 self._PRW_unit.SetText(u'', None)
480 self._PRW_atc.SetText(u'', None)
481
482 self._TCTRL_substance.SetFocus()
483
491
493 self._refresh_as_new()
494
495
496
497
507
508 def delete(component):
509 if component.is_in_use_by_patients:
510 gmDispatcher.send(signal = 'statustext', msg = _('Cannot remove this component from the drug. It is in use.'), beep = True)
511 return False
512
513 return component.containing_drug.remove_component(substance = component['pk_component'])
514
515 def refresh(lctrl):
516 comps = gmMedication.get_drug_components()
517 items = [ [
518 u'%s%s' % (c['brand'], gmTools.coalesce(c['atc_brand'], u'', u' [%s]')),
519 u'%s%s' % (c['substance'], gmTools.coalesce(c['atc_substance'], u'', u' [%s]')),
520 u'%s%s' % (c['amount'], c['unit']),
521 c['preparation'],
522 gmTools.coalesce(c['external_code_brand'], u'', u'%%s [%s]' % c['external_code_type_brand']),
523 c['pk_component']
524 ] for c in comps ]
525 lctrl.set_string_items(items)
526 lctrl.set_data(comps)
527
528 msg = _('\nThese are the components in the drug brands known to GNUmed.\n')
529
530 gmListWidgets.get_choices_from_list (
531 parent = parent,
532 msg = msg,
533 caption = _('Showing drug brand components.'),
534 columns = [_('Brand'), _('Substance'), _('Strength'), _('Preparation'), _('Code'), u'#'],
535 single_selection = True,
536
537 edit_callback = edit,
538 delete_callback = delete,
539 refresh_callback = refresh
540 )
541
542
554
555
556 from Gnumed.wxGladeWidgets import wxgDrugComponentEAPnl
557
558 -class cDrugComponentEAPnl(wxgDrugComponentEAPnl.wxgDrugComponentEAPnl, gmEditArea.cGenericEditAreaMixin):
559
577
578
579
580
581
582
583
584
586 if self.data is not None:
587 if self.data['is_in_use']:
588 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit drug component. It is in use.'), beep = True)
589 return False
590
591 validity = True
592
593 if self._PRW_substance.GetData() is None:
594 validity = False
595 self._PRW_substance.display_as_valid(False)
596 else:
597 self._PRW_substance.display_as_valid(True)
598
599 val = self._TCTRL_amount.GetValue().strip().replace(',', u'.', 1)
600 try:
601 decimal.Decimal(val)
602 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
603 except:
604 validity = False
605 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
606
607 if self._PRW_unit.GetValue().strip() == u'':
608 validity = False
609 self._PRW_unit.display_as_valid(False)
610 else:
611 self._PRW_unit.display_as_valid(True)
612
613 if validity is False:
614 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save drug component. Invalid or missing essential input.'))
615
616 return validity
617
619
620 data = 1
621 data[''] = 1
622 data[''] = 1
623
624
625
626
627
628
629 return False
630 return True
631
633 self.data['pk_consumable_substance'] = self._PRW_substance.GetData()
634 self.data['amount'] = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', u'.', 1))
635 self.data['unit'] = self._PRW_unit.GetValue().strip()
636 return self.data.save()
637
647
649 self._TCTRL_brand.SetValue(u'%s (%s)' % (self.data['brand'], self.data['preparation']))
650 self._TCTRL_components.SetValue(u' / '.join(self.data.containing_drug['components']))
651 details = []
652 if self.data['atc_brand'] is not None:
653 details.append(u'ATC: %s' % self.data['atc_brand'])
654 if self.data['external_code_brand'] is not None:
655 details.append(u'%s: %s' % (self.data['external_code_type_brand'], self.data['external_code_brand']))
656 self._TCTRL_codes.SetValue(u'; '.join(details))
657
658 self._PRW_substance.SetText(self.data['substance'], self.data['pk_consumable_substance'])
659 self._TCTRL_amount.SetValue(u'%s' % self.data['amount'])
660 self._PRW_unit.SetText(self.data['unit'], self.data['unit'])
661
662 self._PRW_substance.SetFocus()
663
665
666
667
668 self._PRW_substance.SetText(u'', None)
669 self._TCTRL_amount.SetValue(u'')
670 self._PRW_unit.SetText(u'', None)
671
672 self._PRW_substance.SetFocus()
673
674
688
689
691
693
694 query = u"""
695 (
696 SELECT DISTINCT ON (preparation)
697 preparation as prep, preparation
698 FROM ref.branded_drug
699 WHERE preparation %(fragment_condition)s
700 ) UNION (
701 SELECT DISTINCT ON (preparation)
702 preparation as prep, preparation
703 FROM clin.substance_intake
704 WHERE preparation %(fragment_condition)s
705 )
706 ORDER BY prep
707 limit 30"""
708
709 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
710 mp.setThresholds(1, 2, 4)
711 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
712 self.SetToolTipString(_('The preparation (form) of the substance or brand.'))
713 self.matcher = mp
714 self.selection_only = False
715
731
732
733
735
736 if brand is not None:
737 if brand.is_in_use_by_patients:
738 gmGuiHelpers.gm_show_info (
739 aTitle = _('Managing components of a drug'),
740 aMessage = _(
741 'Cannot manage the components of the branded drug product\n'
742 '\n'
743 ' "%s" (%s)\n'
744 '\n'
745 'because it is currently taken by patients.\n'
746 ) % (brand['brand'], brand['preparation'])
747 )
748 return False
749
750 if parent is None:
751 parent = wx.GetApp().GetTopWindow()
752
753
754
755
756 if brand is None:
757 msg = _('Pick the substances which are components of this drug.')
758 right_col = _('Components of drug')
759 comp_substs = []
760 else:
761 right_col = u'%s (%s)' % (brand['brand'], brand['preparation'])
762 msg = _(
763 'Adjust the components of "%s"\n'
764 '\n'
765 'The drug must contain at least one component. Any given\n'
766 'substance can only be included once per drug.'
767 ) % right_col
768 comp_substs = [ c.substance for c in brand.components ]
769
770 substs = gmMedication.get_consumable_substances(order_by = 'description')
771 choices = [ u'%s %s%s' % (s['description'], s['amount'], s['unit']) for s in substs ]
772 picks = [ u'%s %s%s' % (c['description'], c['amount'], c['unit']) for c in comp_substs ]
773
774 picker = gmListWidgets.cItemPickerDlg (
775 parent,
776 -1,
777 title = _('Managing components of a drug ...'),
778 msg = msg
779 )
780 picker.set_columns(['Substances'], [right_col])
781 picker.set_choices(choices = choices, data = substs)
782 picker.set_picks(picks = picks, data = comp_substs)
783
784
785
786
787
788
789 btn_pressed = picker.ShowModal()
790 substs = picker.get_picks()
791 picker.Destroy()
792
793 if btn_pressed != wx.ID_OK:
794 return (False, None)
795
796 if brand is not None:
797 brand.set_substances_as_components(substances = substs)
798
799 return (True, substs)
800
802
803 if parent is None:
804 parent = wx.GetApp().GetTopWindow()
805
806 def add_from_db(brand):
807 drug_db = get_drug_database(parent = parent)
808 if drug_db is None:
809 return False
810 drug_db.import_drugs()
811 return True
812
813 def get_tooltip(brand=None):
814 tt = u'%s %s\n' % (brand['brand'], brand['preparation'])
815 tt += u'\n'
816 tt += u'%s%s%s\n' % (
817 gmTools.bool2subst(brand.is_vaccine, u'%s, ' % _('Vaccine'), u''),
818 u'%s, ' % gmTools.bool2subst(brand.is_in_use_by_patients, _('in use'), _('not in use')),
819 gmTools.bool2subst(brand['is_fake_brand'], _('fake'), u'')
820 )
821 tt += gmTools.coalesce(brand['atc'], u'', _('ATC: %s\n'))
822 tt += gmTools.coalesce(brand['external_code'], u'', u'%s: %%s\n' % brand['external_code_type'])
823 if brand['components'] is not None:
824 tt += u'- %s' % u'\n- '.join(brand['components'])
825 return tt
826
827 def edit(brand):
828 if brand is not None:
829 if brand.is_vaccine:
830 gmGuiHelpers.gm_show_info (
831 aTitle = _('Editing medication'),
832 aMessage = _(
833 'Cannot edit the medication\n'
834 '\n'
835 ' "%s" (%s)\n'
836 '\n'
837 'because it is a vaccine. Please edit it\n'
838 'from the vaccine management section !\n'
839 ) % (brand['brand'], brand['preparation'])
840 )
841 return False
842
843 return edit_branded_drug(parent = parent, branded_drug = brand, single_entry = True)
844
845 def delete(brand):
846 if brand.is_vaccine:
847 gmGuiHelpers.gm_show_info (
848 aTitle = _('Deleting medication'),
849 aMessage = _(
850 'Cannot delete the medication\n'
851 '\n'
852 ' "%s" (%s)\n'
853 '\n'
854 'because it is a vaccine. Please delete it\n'
855 'from the vaccine management section !\n'
856 ) % (brand['brand'], brand['preparation'])
857 )
858 return False
859 gmMedication.delete_branded_drug(brand = brand['pk_brand'])
860 return True
861
862 def new():
863 return edit_branded_drug(parent = parent, branded_drug = None, single_entry = False)
864
865 def refresh(lctrl):
866 drugs = gmMedication.get_branded_drugs()
867 items = [ [
868 u'%s%s' % (
869 d['brand'],
870 gmTools.bool2subst(d['is_fake_brand'], ' (%s)' % _('fake'), u'')
871 ),
872 d['preparation'],
873 gmTools.coalesce(d['atc'], u''),
874 gmTools.coalesce(d['components'], u''),
875 gmTools.coalesce(d['external_code'], u'', u'%%s [%s]' % d['external_code_type']),
876 d['pk_brand']
877 ] for d in drugs ]
878 lctrl.set_string_items(items)
879 lctrl.set_data(drugs)
880
881 msg = _('\nThese are the drug brands known to GNUmed.\n')
882
883 gmListWidgets.get_choices_from_list (
884 parent = parent,
885 msg = msg,
886 caption = _('Showing branded drugs.'),
887 columns = [_('Name'), _('Preparation'), _('ATC'), _('Components'), _('Code'), u'#'],
888 single_selection = True,
889 ignore_OK_button = ignore_OK_button,
890 refresh_callback = refresh,
891 new_callback = new,
892 edit_callback = edit,
893 delete_callback = delete,
894 list_tooltip_callback = get_tooltip,
895 left_extra_button = (_('Import'), _('Import substances and brands from a drug database.'), add_from_db)
896
897
898 )
899
900
902
903 if branded_drug is not None:
904 if branded_drug.is_in_use_by_patients:
905 gmGuiHelpers.gm_show_info (
906 aTitle = _('Editing drug'),
907 aMessage = _(
908 'Cannot edit the branded drug product\n'
909 '\n'
910 ' "%s" (%s)\n'
911 '\n'
912 'because it is currently taken by patients.\n'
913 ) % (branded_drug['brand'], branded_drug['preparation'])
914 )
915 return False
916
917 if parent is None:
918 parent = wx.GetApp().GetTopWindow()
919
920 def manage_substances(drug):
921 manage_consumable_substances(parent = parent)
922
923 ea = cBrandedDrugEAPnl(parent = parent, id = -1)
924 ea.data = branded_drug
925 ea.mode = gmTools.coalesce(branded_drug, 'new', 'edit')
926 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
927 dlg.SetTitle(gmTools.coalesce(branded_drug, _('Adding new drug brand'), _('Editing drug brand')))
928 dlg.left_extra_button = (
929 _('Substances'),
930 _('Manage consumable substances'),
931 manage_substances
932 )
933 if dlg.ShowModal() == wx.ID_OK:
934 dlg.Destroy()
935 return True
936 dlg.Destroy()
937 return False
938
939
940 from Gnumed.wxGladeWidgets import wxgBrandedDrugEAPnl
941
942 -class cBrandedDrugEAPnl(wxgBrandedDrugEAPnl.wxgBrandedDrugEAPnl, gmEditArea.cGenericEditAreaMixin):
943
960
961
962
963
964
965
966
967
969
970 if self.data is not None:
971 if self.data.is_in_use_by_patients:
972 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit drug brand. It is in use.'), beep = True)
973 return False
974
975 validity = True
976
977 if self._PRW_brand.GetValue().strip() == u'':
978 validity = False
979 self._PRW_brand.display_as_valid(False)
980 else:
981 self._PRW_brand.display_as_valid(True)
982
983 if self._PRW_preparation.GetValue().strip() == u'':
984 validity = False
985 self._PRW_preparation.display_as_valid(False)
986 else:
987 self._PRW_preparation.display_as_valid(True)
988
989 if validity is True:
990 self._TCTRL_components.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BACKGROUND))
991 if len(self.__component_substances) == 0:
992 wants_empty = gmGuiHelpers.gm_show_question (
993 title = _('Checking brand data'),
994 question = _(
995 'You have not selected any substances\n'
996 'as drug components.\n'
997 '\n'
998 'Without components you will not be able to\n'
999 'use this drug for documenting patient care.\n'
1000 '\n'
1001 'Are you sure you want to save\n'
1002 'it without components ?'
1003 )
1004 )
1005 if not wants_empty:
1006 validity = False
1007 self.display_ctrl_as_valid(ctrl = self._TCTRL_components, valid = False)
1008
1009 if validity is False:
1010 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save branded drug. Invalid or missing essential input.'))
1011
1012 return validity
1013
1015
1016 drug = gmMedication.create_branded_drug (
1017 brand_name = self._PRW_brand.GetValue().strip(),
1018 preparation = gmTools.coalesce (
1019 self._PRW_preparation.GetData(),
1020 self._PRW_preparation.GetValue()
1021 ).strip(),
1022 return_existing = True
1023 )
1024 drug['is_fake_brand'] = self._CHBOX_is_fake.GetValue()
1025 drug['atc'] = self._PRW_atc.GetData()
1026 code = self._TCTRL_external_code.GetValue().strip()
1027 if code != u'':
1028 drug['external_code'] = code
1029 drug['external_code_type'] = self._PRW_external_code_type.GetData().strip()
1030
1031 drug.save()
1032
1033 if len(self.__component_substances) > 0:
1034 drug.set_substances_as_components(substances = self.__component_substances)
1035
1036 self.data = drug
1037
1038 return True
1039
1041 self.data['brand'] = self._PRW_brand.GetValue().strip()
1042 self.data['preparation'] = gmTools.coalesce (
1043 self._PRW_preparation.GetData(),
1044 self._PRW_preparation.GetValue()
1045 ).strip()
1046 self.data['is_fake_brand'] = self._CHBOX_is_fake.GetValue()
1047 self.data['atc'] = self._PRW_atc.GetData()
1048 code = self._TCTRL_external_code.GetValue().strip()
1049 if code != u'':
1050 self.data['external_code'] = code
1051 self.data['external_code_type'] = self._PRW_external_code_type.GetData().strip()
1052 success, data = self.data.save()
1053 if not success:
1054 err, msg = data
1055 _log.error('problem saving')
1056 _log.error('%s', err)
1057 _log.error('%s', msg)
1058 return (success is True)
1059
1061 self._PRW_brand.SetText(u'', None)
1062 self._PRW_preparation.SetText(u'', None)
1063 self._CHBOX_is_fake.SetValue(False)
1064 self._TCTRL_components.SetValue(u'')
1065 self._PRW_atc.SetText(u'', None)
1066 self._TCTRL_external_code.SetValue(u'')
1067 self._PRW_external_code_type.SetText(u'', None)
1068
1069 self._PRW_brand.SetFocus()
1070
1071 self.__component_substances = []
1072
1074 self._refresh_as_new()
1075
1092
1093
1094
1108
1110
1112
1113 query = u"""
1114 SELECT
1115 pk
1116 AS data,
1117 (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', ''))
1118 AS list_label,
1119 (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', ''))
1120 AS field_label
1121 FROM ref.branded_drug
1122 WHERE description %(fragment_condition)s
1123 ORDER BY list_label
1124 LIMIT 50"""
1125
1126 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
1127 mp.setThresholds(2, 3, 4)
1128 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1129 self.SetToolTipString(_(
1130 'The brand name of the drug.\n'
1131 '\n'
1132 'Note: a brand name will need to be linked to\n'
1133 'one or more components before it can be used,\n'
1134 'except in the case of fake (generic) vaccines.'
1135 ))
1136 self.matcher = mp
1137 self.selection_only = False
1138
1139
1140
1141
1143
1145
1146 query = u"""
1147 SELECT DISTINCT ON (sched)
1148 schedule as sched,
1149 schedule
1150 FROM clin.substance_intake
1151 WHERE schedule %(fragment_condition)s
1152 ORDER BY sched
1153 LIMIT 50"""
1154
1155 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
1156 mp.setThresholds(1, 2, 4)
1157 mp.word_separators = '[ \t=+&:@]+'
1158 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1159 self.SetToolTipString(_('The schedule for taking this substance.'))
1160 self.matcher = mp
1161 self.selection_only = False
1162
1164
1165 if intake['is_currently_active']:
1166 intake['discontinued'] = gmDateTime.pydt_now_here()
1167 if intake['discontinue_reason'] is None:
1168 intake['discontinue_reason'] = u'%s %s' % (_('not tolerated:'), _('discontinued due to allergy or intolerance'))
1169 else:
1170 if not intake['discontinue_reason'].startswith(_('not tolerated:')):
1171 intake['discontinue_reason'] = u'%s %s' % (_('not tolerated:'), intake['discontinue_reason'])
1172 if not intake.save():
1173 return False
1174
1175 allg = intake.turn_into_allergy(encounter_id = emr.active_encounter['pk_encounter'])
1176
1177 brand = intake.containing_drug
1178 if brand is not None:
1179 comps = [ c['substance'] for c in brand.components ]
1180 if len(comps) > 1:
1181 gmGuiHelpers.gm_show_info (
1182 aTitle = _(u'Documented an allergy'),
1183 aMessage = _(
1184 u'An allergy was documented against the substance:\n'
1185 u'\n'
1186 u' [%s]\n'
1187 u'\n'
1188 u'This substance was taken with the multi-component brand:\n'
1189 u'\n'
1190 u' [%s (%s)]\n'
1191 u'\n'
1192 u'Note that ALL components of this brand were discontinued.'
1193 ) % (
1194 intake['substance'],
1195 intake['brand'],
1196 u' & '.join(comps)
1197 )
1198 )
1199
1200 if parent is None:
1201 parent = wx.GetApp().GetTopWindow()
1202
1203 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent = parent, id = -1)
1204 dlg.ShowModal()
1205
1206 return True
1207
1208 from Gnumed.wxGladeWidgets import wxgCurrentMedicationEAPnl
1209
1210 -class cSubstanceIntakeEAPnl(wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl, gmEditArea.cGenericEditAreaMixin):
1211
1229
1231
1232 self._PRW_component.add_callback_on_lose_focus(callback = self._on_leave_component)
1233 self._PRW_component.selection_only = True
1234
1235 self._PRW_substance.add_callback_on_lose_focus(callback = self._on_leave_substance)
1236 self._PRW_substance.selection_only = True
1237
1239 emr = gmPerson.gmCurrentPatient().get_emr()
1240
1241 state = emr.allergy_state
1242 if state['last_confirmed'] is None:
1243 confirmed = _('never')
1244 else:
1245 confirmed = state['last_confirmed'].strftime('%Y %B %d').decode(gmI18N.get_encoding())
1246 msg = _(u'%s, last confirmed %s\n') % (state.state_string, confirmed)
1247 msg += gmTools.coalesce(state['comment'], u'', _('Comment (%s): %%s\n') % state['modified_by'])
1248 msg += u'\n'
1249
1250 for allergy in emr.get_allergies():
1251 msg += u'%s (%s, %s): %s\n' % (
1252 allergy['descriptor'],
1253 allergy['l10n_type'],
1254 gmTools.bool2subst(allergy['definite'], _('definite'), _('suspected'), u'?'),
1255 gmTools.coalesce(allergy['reaction'], _('reaction not recorded'))
1256 )
1257
1258 self._LBL_allergies.SetLabel(msg)
1259
1260
1261
1351
1353
1354 emr = gmPerson.gmCurrentPatient().get_emr()
1355 epi = self._PRW_episode.GetData(can_create = True)
1356
1357 if self._PRW_substance.GetData() is None:
1358
1359 intake = emr.add_substance_intake (
1360 pk_component = self._PRW_component.GetData(),
1361 episode = epi
1362 )
1363 else:
1364 intake = emr.add_substance_intake (
1365 pk_substance = self._PRW_substance.GetData(),
1366 episode = epi,
1367 preparation = self._PRW_preparation.GetValue().strip()
1368 )
1369
1370 if intake is None:
1371 gmDispatcher.send('statustext', msg = _('Cannot add duplicate of (maybe inactive) substance intake.'), beep = True)
1372 return False
1373
1374 intake['started'] = self._DP_started.GetData()
1375 intake['discontinued'] = self._DP_discontinued.GetData()
1376 if intake['discontinued'] is None:
1377 intake['discontinue_reason'] = None
1378 else:
1379 intake['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
1380 intake['schedule'] = self._PRW_schedule.GetValue().strip()
1381 intake['aim'] = self._PRW_aim.GetValue().strip()
1382 intake['notes'] = self._PRW_notes.GetValue().strip()
1383 intake['is_long_term'] = self._CHBOX_long_term.IsChecked()
1384 intake['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
1385 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
1386 intake['duration'] = None
1387 else:
1388 intake['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
1389 intake.save()
1390
1391 self.data = intake
1392
1393 return True
1394
1396
1397
1398 self.data['started'] = self._DP_started.GetData()
1399 self.data['discontinued'] = self._DP_discontinued.GetData()
1400 if self.data['discontinued'] is None:
1401 self.data['discontinue_reason'] = None
1402 else:
1403 self.data['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
1404 self.data['schedule'] = self._PRW_schedule.GetValue()
1405 self.data['is_long_term'] = self._CHBOX_long_term.IsChecked()
1406 self.data['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
1407 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
1408 self.data['duration'] = None
1409 else:
1410 self.data['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
1411
1412
1413 self.data['preparation'] = self._PRW_preparation.GetValue()
1414
1415
1416 self.data['aim'] = self._PRW_aim.GetValue()
1417 self.data['notes'] = self._PRW_notes.GetValue()
1418 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True)
1419
1420 self.data.save()
1421
1422 return True
1423
1425 self._PRW_component.SetText(u'', None)
1426 self._TCTRL_brand_ingredients.SetValue(u'')
1427 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1428
1429 self._PRW_substance.SetText(u'', None)
1430 self._PRW_substance.Enable(True)
1431
1432 self._PRW_preparation.SetText(u'', None)
1433 self._PRW_preparation.Enable(True)
1434
1435 self._PRW_schedule.SetText(u'', None)
1436 self._PRW_duration.SetText(u'', None)
1437 self._PRW_aim.SetText(u'', None)
1438 self._PRW_notes.SetText(u'', None)
1439 self._PRW_episode.SetText(u'', None)
1440
1441 self._CHBOX_long_term.SetValue(False)
1442 self._CHBOX_approved.SetValue(True)
1443
1444 self._DP_started.SetData(gmDateTime.pydt_now_here())
1445 self._DP_discontinued.SetData(None)
1446 self._PRW_discontinue_reason.SetValue(u'')
1447
1448 self.__refresh_allergies()
1449
1450 self._PRW_component.SetFocus()
1451
1453
1454 self._TCTRL_brand_ingredients.SetValue(u'')
1455 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1456
1457 if self.data['pk_brand'] is None:
1458 self.__refresh_from_existing_substance()
1459 else:
1460 self.__refresh_from_existing_component()
1461
1462 self._PRW_component.Enable(False)
1463 self._PRW_substance.Enable(False)
1464
1465 if self.data['is_long_term']:
1466 self._CHBOX_long_term.SetValue(True)
1467 self._PRW_duration.Enable(False)
1468 self._PRW_duration.SetText(gmTools.u_infinity, None)
1469 self._BTN_discontinued_as_planned.Enable(False)
1470 else:
1471 self._CHBOX_long_term.SetValue(False)
1472 self._PRW_duration.Enable(True)
1473 self._BTN_discontinued_as_planned.Enable(True)
1474 if self.data['duration'] is None:
1475 self._PRW_duration.SetText(u'', None)
1476 else:
1477 self._PRW_duration.SetText(gmDateTime.format_interval(self.data['duration'], gmDateTime.acc_days), self.data['duration'])
1478 self._PRW_aim.SetText(gmTools.coalesce(self.data['aim'], u''), self.data['aim'])
1479 self._PRW_notes.SetText(gmTools.coalesce(self.data['notes'], u''), self.data['notes'])
1480 self._PRW_episode.SetData(self.data['pk_episode'])
1481 self._PRW_schedule.SetText(gmTools.coalesce(self.data['schedule'], u''), self.data['schedule'])
1482
1483 self._CHBOX_approved.SetValue(self.data['intake_is_approved_of'])
1484
1485 self._DP_started.SetData(self.data['started'])
1486 self._DP_discontinued.SetData(self.data['discontinued'])
1487 self._PRW_discontinue_reason.SetValue(gmTools.coalesce(self.data['discontinue_reason'], u''))
1488 if self.data['discontinued'] is not None:
1489 self._PRW_discontinue_reason.Enable()
1490
1491 self.__refresh_allergies()
1492
1493 self._PRW_schedule.SetFocus()
1494
1496 self._LBL_component.Enable(False)
1497 self._PRW_component.SetText(u'', None)
1498 self._PRW_component.display_as_valid(True)
1499
1500 self._PRW_substance.SetText (
1501 u'%s %s%s' % (self.data['substance'], self.data['amount'], self.data['unit']),
1502 self.data['pk_substance']
1503 )
1504
1505 self._PRW_preparation.SetText(gmTools.coalesce(self.data['preparation'], u''), self.data['preparation'])
1506 self._PRW_preparation.Enable(True)
1507
1509 self._PRW_component.SetText (
1510 u'%s %s%s (%s)' % (self.data['substance'], self.data['amount'], self.data['unit'], self.data['brand']),
1511 self.data['pk_drug_component']
1512 )
1513
1514 brand = gmMedication.cBrandedDrug(aPK_obj = self.data['pk_brand'])
1515 if brand['components'] is not None:
1516 self._TCTRL_brand_ingredients.SetValue(u'; '.join(brand['components']))
1517 tt = u'%s:\n\n- %s' % (
1518 self.data['brand'],
1519 u'\n- '.join(brand['components'])
1520 )
1521 self._TCTRL_brand_ingredients.SetToolTipString(tt)
1522
1523 self._LBL_or.Enable(False)
1524 self._LBL_substance.Enable(False)
1525 self._PRW_substance.SetText(u'', None)
1526 self._PRW_substance.display_as_valid(True)
1527
1528 self._PRW_preparation.SetText(self.data['preparation'], self.data['preparation'])
1529 self._PRW_preparation.Enable(False)
1530
1532 self._refresh_as_new()
1533
1534
1535
1537 if self._PRW_component.GetData() is None:
1538 self._LBL_or.Enable(True)
1539 self._PRW_component.SetText(u'', None)
1540 self._LBL_substance.Enable(True)
1541 self._PRW_substance.Enable(True)
1542 self._LBL_preparation.Enable(True)
1543 self._PRW_preparation.Enable(True)
1544 self._PRW_preparation.SetText(u'', None)
1545 self._TCTRL_brand_ingredients.SetValue(u'')
1546 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1547 else:
1548 self._LBL_or.Enable(False)
1549 self._LBL_substance.Enable(False)
1550 self._PRW_substance.SetText(u'', None)
1551 self._PRW_substance.display_as_valid(True)
1552 self._PRW_substance.Enable(False)
1553 self._LBL_preparation.Enable(False)
1554 self._PRW_preparation.Enable(False)
1555 comp = gmMedication.cDrugComponent(aPK_obj = self._PRW_component.GetData())
1556 self._PRW_preparation.SetText(comp['preparation'], comp['preparation'])
1557 brand = comp.containing_drug
1558 if brand['components'] is not None:
1559 self._TCTRL_brand_ingredients.SetValue(u'; '.join(brand['components']))
1560 tt = u'%s:\n\n- %s' % (
1561 brand['brand'],
1562 u'\n- '.join(brand['components'])
1563 )
1564 self._TCTRL_brand_ingredients.SetToolTipString(tt)
1565
1567 if self._PRW_substance.GetData() is None:
1568 self._LBL_or.Enable(True)
1569 self._LBL_component.Enable(True)
1570 self._PRW_component.Enable(True)
1571 self._PRW_substance.SetText(u'', None)
1572 else:
1573 self._LBL_or.Enable(False)
1574 self._LBL_component.Enable(False)
1575 self._PRW_component.SetText(u'', None)
1576 self._PRW_component.display_as_valid(True)
1577 self._PRW_component.Enable(False)
1578 self._LBL_preparation.Enable(True)
1579 self._PRW_preparation.Enable(True)
1580 self._TCTRL_brand_ingredients.SetValue(u'')
1581 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1582
1584 if self._DP_discontinued.GetData() is None:
1585 self._PRW_discontinue_reason.Enable(False)
1586 else:
1587 self._PRW_discontinue_reason.Enable(True)
1588
1591
1594
1597
1609
1639
1641 if self._CHBOX_long_term.IsChecked() is True:
1642 self._PRW_duration.Enable(False)
1643 self._BTN_discontinued_as_planned.Enable(False)
1644 self._PRW_discontinue_reason.Enable(False)
1645 else:
1646 self._PRW_duration.Enable(True)
1647 self._BTN_discontinued_as_planned.Enable(True)
1648 self._PRW_discontinue_reason.Enable(True)
1649
1650 self.__refresh_allergies()
1651
1661
1663
1664 subst = gmMedication.cSubstanceIntakeEntry(aPK_obj = substance)
1665 msg = _(
1666 '\n'
1667 '[%s]\n'
1668 '\n'
1669 'It may be prudent to edit (before deletion) the details\n'
1670 'of this substance intake entry so as to leave behind\n'
1671 'some indication of why it was deleted.\n'
1672 ) % subst.format()
1673
1674 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
1675 parent,
1676 -1,
1677 caption = _('Deleting medication / substance intake'),
1678 question = msg,
1679 button_defs = [
1680 {'label': _('&Edit'), 'tooltip': _('Allow editing of substance intake entry before deletion.'), 'default': True},
1681 {'label': _('&Delete'), 'tooltip': _('Delete immediately without editing first.')},
1682 {'label': _('&Cancel'), 'tooltip': _('Abort. Do not delete or edit substance intake entry.')}
1683 ]
1684 )
1685
1686 edit_first = dlg.ShowModal()
1687 dlg.Destroy()
1688
1689 if edit_first == wx.ID_CANCEL:
1690 return
1691
1692 if edit_first == wx.ID_YES:
1693 edit_intake_of_substance(parent = parent, substance = subst)
1694 delete_it = gmGuiHelpers.gm_show_question (
1695 aMessage = _('Now delete substance intake entry ?'),
1696 aTitle = _('Deleting medication / substance intake')
1697 )
1698 else:
1699 delete_it = True
1700
1701 if not delete_it:
1702 return
1703
1704 gmMedication.delete_substance_intake(substance = substance)
1705
1720
1721
1722
1723
1751
1753
1754 if parent is None:
1755 parent = wx.GetApp().GetTopWindow()
1756
1757
1758 dbcfg = gmCfg.cCfgSQL()
1759 option = u'form_templates.medication_list'
1760
1761 template = dbcfg.get2 (
1762 option = option,
1763 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1764 bias = 'user'
1765 )
1766
1767 if template is None:
1768 template = configure_medication_list_template(parent = parent)
1769 if template is None:
1770 gmGuiHelpers.gm_show_error (
1771 aMessage = _('There is no medication list template configured.'),
1772 aTitle = _('Printing medication list')
1773 )
1774 return False
1775 else:
1776 try:
1777 name, ver = template.split(u' - ')
1778 except:
1779 _log.exception('problem splitting medication list template name [%s]', template)
1780 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading medication list template.'), beep = True)
1781 return False
1782 template = gmForms.get_form_template(name_long = name, external_version = ver)
1783 if template is None:
1784 gmGuiHelpers.gm_show_error (
1785 aMessage = _('Cannot load medication list template [%s - %s]') % (name, ver),
1786 aTitle = _('Printing medication list')
1787 )
1788 return False
1789
1790
1791 try:
1792 meds_list = template.instantiate()
1793 except KeyError:
1794 _log.exception('cannot instantiate medication list template [%s]', template)
1795 gmGuiHelpers.gm_show_error (
1796 aMessage = _('Invalid medication list template [%s - %s (%s)]') % (name, ver, template['engine']),
1797 aTitle = _('Printing medication list')
1798 )
1799 return False
1800
1801 ph = gmMacro.gmPlaceholderHandler()
1802
1803 meds_list.substitute_placeholders(data_source = ph)
1804 pdf_name = meds_list.generate_output()
1805 if pdf_name is None:
1806 gmGuiHelpers.gm_show_error (
1807 aMessage = _('Error generating the medication list.'),
1808 aTitle = _('Printing medication list')
1809 )
1810 return False
1811
1812
1813 printed = gmPrinting.print_files(filenames = [pdf_name], jobtype = 'medication_list')
1814 if not printed:
1815 gmGuiHelpers.gm_show_error (
1816 aMessage = _('Error printing the medication list.'),
1817 aTitle = _('Printing medication list')
1818 )
1819 return False
1820
1821 pat = gmPerson.gmCurrentPatient()
1822 emr = pat.get_emr()
1823 epi = emr.add_episode(episode_name = 'administration', is_open = False)
1824 emr.add_clin_narrative (
1825 soap_cat = None,
1826 note = _('medication list printed from template [%s - %s]') % (template['name_long'], template['external_version']),
1827 episode = epi
1828 )
1829
1830 return True
1831
1833
1834 if len(prescribed_drugs) == 0:
1835 return
1836
1837 curr_brands = [ i['pk_brand'] for i in emr.get_current_substance_intake() if i['pk_brand'] is not None ]
1838 new_drugs = []
1839 for drug in prescribed_drugs:
1840 if drug['pk_brand'] not in curr_brands:
1841 new_drugs.append(drug)
1842
1843 if len(new_drugs) == 0:
1844 return
1845
1846 if parent is None:
1847 parent = wx.GetApp().GetTopWindow()
1848
1849 dlg = gmListWidgets.cItemPickerDlg (
1850 parent,
1851 -1,
1852 msg = _(
1853 'These brands have been prescribed but are not listed\n'
1854 'in the current medication list of this patient.\n'
1855 '\n'
1856 'Please select those you want added to the medication list.'
1857 )
1858 )
1859 dlg.set_columns (
1860 columns = [_('Newly prescribed drugs')],
1861 columns_right = [_('Add to medication list')]
1862 )
1863 choices = [ (u'%s %s (%s)' % (d['brand'], d['preparation'], u'; '.join(d['components']))) for d in new_drugs ]
1864 dlg.set_choices (
1865 choices = choices,
1866 data = new_drugs
1867 )
1868 dlg.ShowModal()
1869 drugs2add = dlg.get_picks()
1870 dlg.Destroy()
1871
1872 if drugs2add is None:
1873 return
1874
1875 if len(drugs2add) == 0:
1876 return
1877
1878 for drug in drugs2add:
1879
1880 intake = emr.add_substance_intake (
1881 pk_component = drug['pk_components'][0],
1882 episode = emr.add_episode(episode_name = gmMedication.DEFAULT_MEDICATION_HISTORY_EPISODE)['pk_episode'],
1883 )
1884 if intake is None:
1885 continue
1886 intake['intake_is_approved_of'] = True
1887 intake.save()
1888
1889 return
1890
1892 """A grid class for displaying current substance intake.
1893
1894 - does NOT listen to the currently active patient
1895 - thereby it can display any patient at any time
1896 """
1898
1899 wx.grid.Grid.__init__(self, *args, **kwargs)
1900
1901 self.__patient = None
1902 self.__row_data = {}
1903 self.__prev_row = None
1904 self.__prev_tooltip_row = None
1905 self.__prev_cell_0 = None
1906 self.__grouping_mode = u'issue'
1907 self.__filter_show_unapproved = True
1908 self.__filter_show_inactive = True
1909
1910 self.__grouping2col_labels = {
1911 u'issue': [
1912 _('Health issue'),
1913 _('Substance'),
1914 _('Strength'),
1915 _('Schedule'),
1916 _('Started'),
1917 _('Duration / Until'),
1918 _('Brand'),
1919 _('Advice')
1920 ],
1921 u'brand': [
1922 _('Brand'),
1923 _('Schedule'),
1924 _('Substance'),
1925 _('Strength'),
1926 _('Started'),
1927 _('Duration / Until'),
1928 _('Health issue'),
1929 _('Advice')
1930 ],
1931 u'episode': [
1932 _('Episode'),
1933 _('Substance'),
1934 _('Strength'),
1935 _('Schedule'),
1936 _('Started'),
1937 _('Duration / Until'),
1938 _('Brand'),
1939 _('Advice')
1940 ]
1941 }
1942
1943 self.__grouping2order_by_clauses = {
1944 u'issue': u'pk_health_issue nulls first, substance, started',
1945 u'episode': u'pk_health_issue nulls first, episode, substance, started',
1946 u'brand': u'brand nulls last, substance, started'
1947 }
1948
1949 self.__init_ui()
1950 self.__register_events()
1951
1952
1953
1955
1956 sel_block_top_left = self.GetSelectionBlockTopLeft()
1957 sel_block_bottom_right = self.GetSelectionBlockBottomRight()
1958 sel_cols = self.GetSelectedCols()
1959 sel_rows = self.GetSelectedRows()
1960
1961 selected_cells = []
1962
1963
1964 selected_cells += self.GetSelectedCells()
1965
1966
1967 selected_cells += list (
1968 (row, col)
1969 for row in sel_rows
1970 for col in xrange(self.GetNumberCols())
1971 )
1972
1973
1974 selected_cells += list (
1975 (row, col)
1976 for row in xrange(self.GetNumberRows())
1977 for col in sel_cols
1978 )
1979
1980
1981 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()):
1982 selected_cells += [
1983 (row, col)
1984 for row in xrange(top_left[0], bottom_right[0] + 1)
1985 for col in xrange(top_left[1], bottom_right[1] + 1)
1986 ]
1987
1988 return set(selected_cells)
1989
1991 rows = {}
1992
1993 for row, col in self.get_selected_cells():
1994 rows[row] = True
1995
1996 return rows.keys()
1997
2000
2002
2003 self.empty_grid()
2004
2005 if self.__patient is None:
2006 return
2007
2008 emr = self.__patient.get_emr()
2009 meds = emr.get_current_substance_intake (
2010 order_by = self.__grouping2order_by_clauses[self.__grouping_mode],
2011 include_unapproved = self.__filter_show_unapproved,
2012 include_inactive = self.__filter_show_inactive
2013 )
2014 if not meds:
2015 return
2016
2017 self.BeginBatch()
2018
2019
2020 labels = self.__grouping2col_labels[self.__grouping_mode]
2021 if self.__filter_show_unapproved:
2022 self.AppendCols(numCols = len(labels) + 1)
2023 else:
2024 self.AppendCols(numCols = len(labels))
2025 for col_idx in range(len(labels)):
2026 self.SetColLabelValue(col_idx, labels[col_idx])
2027 if self.__filter_show_unapproved:
2028 self.SetColLabelValue(len(labels), u'OK?')
2029 self.SetColSize(len(labels), 40)
2030
2031 self.AppendRows(numRows = len(meds))
2032
2033
2034 for row_idx in range(len(meds)):
2035 med = meds[row_idx]
2036 self.__row_data[row_idx] = med
2037
2038 if med['is_currently_active'] is True:
2039 atcs = []
2040 if med['atc_substance'] is not None:
2041 atcs.append(med['atc_substance'])
2042
2043
2044
2045 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (med['substance'],))
2046 if allg not in [None, False]:
2047 attr = self.GetOrCreateCellAttr(row_idx, 0)
2048 if allg['type'] == u'allergy':
2049 attr.SetTextColour('red')
2050 else:
2051 attr.SetTextColour('yellow')
2052 self.SetRowAttr(row_idx, attr)
2053 else:
2054 attr = self.GetOrCreateCellAttr(row_idx, 0)
2055 attr.SetTextColour('grey')
2056 self.SetRowAttr(row_idx, attr)
2057
2058 if self.__grouping_mode == u'episode':
2059 if med['pk_episode'] is None:
2060 self.__prev_cell_0 = None
2061 epi = gmTools.u_diameter
2062 else:
2063 if self.__prev_cell_0 == med['episode']:
2064 epi = u''
2065 else:
2066 self.__prev_cell_0 = med['episode']
2067 epi = gmTools.coalesce(med['episode'], u'')
2068 self.SetCellValue(row_idx, 0, gmTools.wrap(text = epi, width = 40))
2069
2070 self.SetCellValue(row_idx, 1, med['substance'])
2071 self.SetCellValue(row_idx, 2, u'%s%s' % (med['amount'], med['unit']))
2072 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], u''))
2073 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
2074
2075 if med['is_long_term']:
2076 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
2077 else:
2078 if med['discontinued'] is None:
2079 if med['duration'] is None:
2080 self.SetCellValue(row_idx, 5, u'')
2081 else:
2082 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
2083 else:
2084 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d'))
2085
2086 if med['pk_brand'] is None:
2087 brand = u''
2088 else:
2089 if med['fake_brand']:
2090 brand = gmTools.coalesce(med['brand'], u'', _('%s (fake)'))
2091 else:
2092 brand = gmTools.coalesce(med['brand'], u'')
2093 self.SetCellValue(row_idx, 6, gmTools.wrap(text = brand, width = 35))
2094
2095 elif self.__grouping_mode == u'issue':
2096 if med['pk_health_issue'] is None:
2097 self.__prev_cell_0 = None
2098 issue = u'%s%s' % (
2099 gmTools.u_diameter,
2100 gmTools.coalesce(med['episode'], u'', u' (%s)')
2101 )
2102 else:
2103 if self.__prev_cell_0 == med['health_issue']:
2104 issue = u''
2105 else:
2106 self.__prev_cell_0 = med['health_issue']
2107 issue = med['health_issue']
2108 self.SetCellValue(row_idx, 0, gmTools.wrap(text = issue, width = 40))
2109
2110 self.SetCellValue(row_idx, 1, med['substance'])
2111 self.SetCellValue(row_idx, 2, u'%s%s' % (med['amount'], med['unit']))
2112 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], u''))
2113 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
2114
2115 if med['is_long_term']:
2116 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
2117 else:
2118 if med['discontinued'] is None:
2119 if med['duration'] is None:
2120 self.SetCellValue(row_idx, 5, u'')
2121 else:
2122 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
2123 else:
2124 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d'))
2125
2126 if med['pk_brand'] is None:
2127 brand = u''
2128 else:
2129 if med['fake_brand']:
2130 brand = gmTools.coalesce(med['brand'], u'', _('%s (fake)'))
2131 else:
2132 brand = gmTools.coalesce(med['brand'], u'')
2133 self.SetCellValue(row_idx, 6, gmTools.wrap(text = brand, width = 35))
2134
2135 elif self.__grouping_mode == u'brand':
2136
2137 if med['pk_brand'] is None:
2138 self.__prev_cell_0 = None
2139 brand = gmTools.u_diameter
2140 else:
2141 if self.__prev_cell_0 == med['brand']:
2142 brand = u''
2143 else:
2144 self.__prev_cell_0 = med['brand']
2145 if med['fake_brand']:
2146 brand = gmTools.coalesce(med['brand'], u'', _('%s (fake)'))
2147 else:
2148 brand = gmTools.coalesce(med['brand'], u'')
2149 self.SetCellValue(row_idx, 0, gmTools.wrap(text = brand, width = 35))
2150
2151 self.SetCellValue(row_idx, 1, gmTools.coalesce(med['schedule'], u''))
2152 self.SetCellValue(row_idx, 2, med['substance'])
2153 self.SetCellValue(row_idx, 3, u'%s%s' % (med['amount'], med['unit']))
2154 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
2155
2156 if med['is_long_term']:
2157 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
2158 else:
2159 if med['discontinued'] is None:
2160 if med['duration'] is None:
2161 self.SetCellValue(row_idx, 5, u'')
2162 else:
2163 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
2164 else:
2165 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d'))
2166
2167 if med['pk_health_issue'] is None:
2168 issue = u'%s%s' % (
2169 gmTools.u_diameter,
2170 gmTools.coalesce(med['episode'], u'', u' (%s)')
2171 )
2172 else:
2173 issue = gmTools.coalesce(med['health_issue'], u'')
2174 self.SetCellValue(row_idx, 6, gmTools.wrap(text = issue, width = 40))
2175
2176 else:
2177 raise ValueError('unknown grouping mode [%s]' % self.__grouping_mode)
2178
2179 if med['notes'] is not None:
2180 self.SetCellValue(row_idx, 7, gmTools.wrap(text = med['notes'], width = 50))
2181
2182 if self.__filter_show_unapproved:
2183 self.SetCellValue (
2184 row_idx,
2185 len(labels),
2186 gmTools.bool2subst(med['intake_is_approved_of'], gmTools.u_checkmark_thin, u'', u'?')
2187 )
2188
2189
2190
2191 self.AutoSize()
2192 self.EndBatch()
2193
2195 self.BeginBatch()
2196 self.ClearGrid()
2197
2198
2199 if self.GetNumberRows() > 0:
2200 self.DeleteRows(pos = 0, numRows = self.GetNumberRows())
2201 if self.GetNumberCols() > 0:
2202 self.DeleteCols(pos = 0, numCols = self.GetNumberCols())
2203 self.EndBatch()
2204 self.__row_data = {}
2205 self.__prev_cell_0 = None
2206
2208
2209 if len(self.__row_data) == 0:
2210 return
2211
2212 sel_rows = self.get_selected_rows()
2213 if len(sel_rows) != 1:
2214 return
2215
2216 drug_db = get_drug_database()
2217 if drug_db is None:
2218 return
2219
2220 intake = self.get_selected_data()[0]
2221 if intake['brand'] is None:
2222 drug_db.show_info_on_substance(substance_intake = intake)
2223 else:
2224 drug_db.show_info_on_drug(substance_intake = intake)
2225
2233
2236
2248
2260
2274
2277
2291
2305
2321
2325
2430
2431
2432
2434 self.CreateGrid(0, 1)
2435 self.EnableEditing(0)
2436 self.EnableDragGridSize(1)
2437 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows)
2438
2439 self.SetColLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTER)
2440
2441 self.SetRowLabelSize(0)
2442 self.SetRowLabelAlignment(horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
2443
2444
2445
2447 return self.__patient
2448
2452
2453 patient = property(_get_patient, _set_patient)
2454
2456 return self.__grouping_mode
2457
2461
2462 grouping_mode = property(_get_grouping_mode, _set_grouping_mode)
2463
2465 return self.__filter_show_unapproved
2466
2470
2471 filter_show_unapproved = property(_get_filter_show_unapproved, _set_filter_show_unapproved)
2472
2474 return self.__filter_show_inactive
2475
2479
2480 filter_show_inactive = property(_get_filter_show_inactive, _set_filter_show_inactive)
2481
2482
2483
2485
2486 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells)
2487
2488
2489
2490
2491 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
2492
2494 """Calculate where the mouse is and set the tooltip dynamically."""
2495
2496
2497
2498 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512 row, col = self.XYToCell(x, y)
2513
2514 if row == self.__prev_tooltip_row:
2515 return
2516
2517 self.__prev_tooltip_row = row
2518
2519 try:
2520 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row))
2521 except KeyError:
2522 pass
2523
2528
2529 from Gnumed.wxGladeWidgets import wxgCurrentSubstancesPnl
2530
2531 -class cCurrentSubstancesPnl(wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl, gmRegetMixin.cRegetOnPaintMixin):
2532
2533 """Panel holding a grid with current substances. Used as notebook page."""
2534
2541
2542
2543
2552
2553
2554
2556 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
2557 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._schedule_data_reget)
2558 gmDispatcher.connect(signal = u'substance_intake_mod_db', receiver = self._schedule_data_reget)
2559
2560
2561
2563 wx.CallAfter(self.__on_pre_patient_selection)
2564
2566 self._grid_substances.patient = None
2567
2570
2573
2576
2579
2582
2585
2588
2591
2594
2597
2600
2603
2606
2609
2612
2615
2616
2617
2618 if __name__ == '__main__':
2619
2620 if len(sys.argv) < 2:
2621 sys.exit()
2622
2623 if sys.argv[1] != 'test':
2624 sys.exit()
2625
2626 from Gnumed.pycommon import gmI18N
2627
2628 gmI18N.activate_locale()
2629 gmI18N.install_domain(domain = 'gnumed')
2630
2631
2632 app = wx.PyWidgetTester(size = (600, 600))
2633
2634 app.SetWidget(cSubstancePhraseWheel, -1)
2635 app.MainLoop()
2636
2637
2638