1
2
3
4
5 __doc__ = """GNUmed drug / substance reference widgets."""
6
7
8 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
9 __license__ = "GPL v2 or later"
10
11 import logging
12 import sys
13 import os.path
14 import decimal
15
16
17 import wx
18
19
20 if __name__ == '__main__':
21 sys.path.insert(0, '../../')
22 from Gnumed.pycommon import gmI18N
23 gmI18N.activate_locale()
24 gmI18N.install_domain(domain = 'gnumed')
25
26 from Gnumed.pycommon import gmDispatcher
27 from Gnumed.pycommon import gmCfg
28 from Gnumed.pycommon import gmShellAPI
29 from Gnumed.pycommon import gmTools
30 from Gnumed.pycommon import gmMatchProvider
31
32 from Gnumed.business import gmPerson
33 from Gnumed.business import gmPraxis
34 from Gnumed.business import gmMedication
35 from Gnumed.business import gmDrugDataSources
36 from Gnumed.business import gmATC
37
38 from Gnumed.wxpython import gmGuiHelpers
39 from Gnumed.wxpython import gmEditArea
40 from Gnumed.wxpython import gmCfgWidgets
41 from Gnumed.wxpython import gmListWidgets
42 from Gnumed.wxpython import gmPhraseWheel
43
44
45 _log = logging.getLogger('gm.ui')
46
47
48
49
67
68
70 dbcfg = gmCfg.cCfgSQL()
71
72
73 default_db = dbcfg.get2 (
74 option = 'external.drug_data.default_source',
75 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
76 bias = 'workplace'
77 )
78
79
80 if default_db is None:
81 gmDispatcher.send('statustext', msg = _('No default drug database configured.'), beep = True)
82 configure_drug_data_source(parent = parent)
83 default_db = dbcfg.get2 (
84 option = 'external.drug_data.default_source',
85 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
86 bias = 'workplace'
87 )
88
89 if default_db is None:
90 gmGuiHelpers.gm_show_error (
91 aMessage = _('There is no default drug database configured.'),
92 aTitle = _('Jumping to drug database')
93 )
94 return None
95
96
97
98 try:
99 drug_db = gmDrugDataSources.drug_data_source_interfaces[default_db]()
100 except KeyError:
101
102 _log.error('faulty default drug data source configuration: %s', default_db)
103
104 configure_drug_data_source(parent = parent)
105 default_db = dbcfg.get2 (
106 option = 'external.drug_data.default_source',
107 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
108 bias = 'workplace'
109 )
110
111 try:
112 drug_db = gmDrugDataSources.drug_data_source_interfaces[default_db]()
113 except KeyError:
114 _log.error('still faulty default drug data source configuration: %s', default_db)
115 return None
116
117 if patient is not None:
118 drug_db.patient = patient
119
120 return drug_db
121
122
128
129
131
132 if import_drugs and (emr is None):
133 gmDispatcher.send('statustext', msg = _('Cannot import drugs from IFAP into chart without chart.'))
134 return False
135
136 dbcfg = gmCfg.cCfgSQL()
137
138 ifap_cmd = dbcfg.get2 (
139 option = 'external.ifap-win.shell_command',
140 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
141 bias = 'workplace',
142 default = 'wine "C:\Ifapwin\WIAMDB.EXE"'
143 )
144 found, binary = gmShellAPI.detect_external_binary(ifap_cmd)
145 if not found:
146 gmDispatcher.send('statustext', msg = _('Cannot call IFAP via [%s].') % ifap_cmd)
147 return False
148 ifap_cmd = binary
149
150 if import_drugs:
151 transfer_file = os.path.expanduser(dbcfg.get2 (
152 option = 'external.ifap-win.transfer_file',
153 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
154 bias = 'workplace',
155 default = '~/.wine/drive_c/Ifapwin/ifap2gnumed.csv'
156 ))
157
158 try:
159 f = io.open(transfer_file, mode = 'wt').close()
160 except IOError:
161 _log.exception('Cannot create IFAP <-> GNUmed transfer file [%s]', transfer_file)
162 gmDispatcher.send('statustext', msg = _('Cannot create IFAP <-> GNUmed transfer file [%s].') % transfer_file)
163 return False
164
165 wx.BeginBusyCursor()
166 gmShellAPI.run_command_in_shell(command = ifap_cmd, blocking = import_drugs)
167 wx.EndBusyCursor()
168
169 if import_drugs:
170
171
172 try:
173 csv_file = io.open(transfer_file, mode = 'rt', encoding = 'latin1')
174 except:
175 _log.exception('cannot access [%s]', fname)
176 csv_file = None
177
178 if csv_file is not None:
179 import csv
180 csv_lines = csv.DictReader (
181 csv_file,
182 fieldnames = 'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split(),
183 delimiter = ';'
184 )
185
186 epi = emr.add_episode(episode_name = _('Current medication'))
187 for line in csv_lines:
188 narr = '%sx %s %s %s (\u2258 %s %s) von %s (%s)' % (
189 line['Packungszahl'].strip(),
190 line['Handelsname'].strip(),
191 line['Form'].strip(),
192 line['Packungsgr\xf6\xdfe'].strip(),
193 line['Abpackungsmenge'].strip(),
194 line['Einheit'].strip(),
195 line['Hersteller'].strip(),
196 line['PZN'].strip()
197 )
198 emr.add_clin_narrative(note = narr, soap_cat = 's', episode = epi)
199 csv_file.close()
200
201 return True
202
203
204
205
207
208 if substance is not None:
209 if substance.is_in_use_by_patients:
210 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit this substance. It is in use.'), beep = True)
211 return False
212
213 ea = cSubstanceEAPnl(parent, -1)
214 ea.data = substance
215 ea.mode = gmTools.coalesce(substance, 'new', 'edit')
216 dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = single_entry)
217 dlg.SetTitle(gmTools.coalesce(substance, _('Adding new substance'), _('Editing substance')))
218 if dlg.ShowModal() == wx.ID_OK:
219 dlg.Destroy()
220 return True
221 dlg.Destroy()
222 return False
223
224
237
238
239 def edit(substance=None):
240 return edit_substance(parent = parent, substance = substance, single_entry = (substance is not None))
241
242
243 def delete(substance):
244 if substance.is_in_use_by_patients:
245 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete this substance. It is in use.'), beep = True)
246 return False
247 return gmMedication.delete_substance(pk_substance = substance['pk_substance'])
248
249
250 def get_item_tooltip(substance):
251 if not isinstance(substance, gmMedication.cSubstance):
252 return None
253 return substance.format()
254
255
256 def refresh(lctrl):
257 substs = gmMedication.get_substances(order_by = 'substance')
258 items = [ [
259 s['substance'],
260 gmTools.coalesce(s['atc'], ''),
261 gmTools.coalesce(s['intake_instructions'], ''),
262 s['pk_substance']
263 ] for s in substs ]
264 lctrl.set_string_items(items)
265 lctrl.set_data(substs)
266
267
268 gmListWidgets.get_choices_from_list (
269 parent = parent,
270 caption = _('Substances registered with GNUmed.'),
271 columns = [_('Substance'), 'ATC', _('Instructions'), '#'],
272 single_selection = True,
273 new_callback = edit,
274 edit_callback = edit,
275 delete_callback = delete,
276 refresh_callback = refresh,
277 left_extra_button = (_('Import'), _('Import substances from a drug database.'), add_from_db),
278 list_tooltip_callback = get_item_tooltip
279 )
280
281
282 from Gnumed.wxGladeWidgets import wxgSubstanceEAPnl
283
284 -class cSubstanceEAPnl(wxgSubstanceEAPnl.wxgSubstanceEAPnl, gmEditArea.cGenericEditAreaMixin):
285
305
306
308 self._LCTRL_loincs.set_columns([_('LOINC'), _('Interval'), _('Comment')])
309
310
311
312
314
315 validity = True
316
317 if self._TCTRL_substance.GetValue().strip() == '':
318 validity = False
319 self.display_tctrl_as_valid(tctrl = self._TCTRL_substance, valid = False)
320 self._TCTRL_substance.SetFocus()
321 else:
322 self.display_tctrl_as_valid(tctrl = self._TCTRL_substance, valid = True)
323
324 if validity is False:
325 self.status_message = _('Cannot save: Substance name missing.')
326
327 return validity
328
329
350
351
369
370
379
380
393
394
396 self._refresh_as_new()
397
398
399
400
413
414
417
418
420
422
423 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
424 query = """
425 SELECT DISTINCT ON (list_label)
426 list_label,
427 field_label,
428 data
429 FROM (
430 SELECT
431 description AS list_label,
432 description AS field_label,
433 pk AS data
434 FROM ref.substance
435 WHERE
436 description %(fragment_condition)s
437
438 UNION ALL
439
440 SELECT
441 term AS list_label,
442 term AS field_label,
443 NULL::integer AS data
444 FROM ref.atc
445 WHERE
446 term %(fragment_condition)s
447
448 ) AS candidates
449 ORDER BY list_label
450 LIMIT 50
451 """
452 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
453 mp.setThresholds(1, 2, 4)
454
455 self.SetToolTip(_('The substance name.'))
456 self.matcher = mp
457 self.selection_only = False
458 self.phrase_separators = None
459
460
463
464
465
466
468
469 if substance_dose is not None:
470 if substance_dose.is_in_use_by_patients:
471 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit this substance. It is in use.'), beep = True)
472 return False
473
474 ea = cSubstanceDoseEAPnl(parent, -1)
475 ea.data = substance_dose
476 ea.mode = gmTools.coalesce(substance_dose, 'new', 'edit')
477 dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = single_entry)
478 dlg.SetTitle(gmTools.coalesce(substance_dose, _('Adding new substance dose'), _('Editing substance dose')))
479 if dlg.ShowModal() == wx.ID_OK:
480 dlg.Destroy()
481 return True
482 dlg.Destroy()
483 return False
484
485
498
499
500 def edit(substance_dose=None):
501 return edit_substance_dose(parent = parent, substance_dose = substance_dose, single_entry = (substance_dose is not None))
502
503
504 def delete(substance_dose):
505 if substance_dose.is_in_use_by_patients:
506 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete this substance. It is in use.'), beep = True)
507 return False
508 return gmMedication.delete_substance_dose(pk_dose = substance_dose['pk_dose'])
509
510
511 def refresh(lctrl):
512 if vaccine_indications_only:
513 substs = [ s for s in gmMedication.get_substance_doses(order_by = 'substance') if gmTools.coalesce(s['atc_substance'], '').startswith('J07') ]
514 else:
515 substs = gmMedication.get_substance_doses(order_by = 'substance')
516 items = [ [
517 s['substance'],
518 s['amount'],
519 s.formatted_units,
520 gmTools.coalesce(s['atc_substance'], ''),
521 gmTools.coalesce(s['intake_instructions'], ''),
522 s['pk_dose']
523 ] for s in substs ]
524 lctrl.set_string_items(items)
525 lctrl.set_data(substs)
526
527
528 def get_item_tooltip(substance_dose):
529 if not isinstance(substance_dose, gmMedication.cSubstanceDose):
530 return None
531 return substance_dose.format(include_loincs = True)
532
533
534 return gmListWidgets.get_choices_from_list (
535 parent = parent,
536 caption = _('Substance doses registered with GNUmed.'),
537 columns = [_('Substance'), _('Amount'), _('Unit'), 'ATC', _('Instructions'), '#'],
538 single_selection = False,
539 can_return_empty = False,
540 new_callback = edit,
541 edit_callback = edit,
542 delete_callback = delete,
543 refresh_callback = refresh,
544 left_extra_button = (_('Import'), _('Import substance doses from a drug database.'), add_from_db),
545 list_tooltip_callback = get_item_tooltip
546 )
547
548
549 from Gnumed.wxGladeWidgets import wxgSubstanceDoseEAPnl
550
551 -class cSubstanceDoseEAPnl(wxgSubstanceDoseEAPnl.wxgSubstanceDoseEAPnl, gmEditArea.cGenericEditAreaMixin):
552
572
573
582
583
585 subst = self._PRW_substance.GetValue().strip()
586 if subst == '':
587 subst = '?'
588 amount = self._TCTRL_amount.GetValue().strip()
589 if amount == '':
590 amount = '?'
591 unit = self._PRW_unit.GetValue().strip()
592 if unit == '':
593 unit = '?'
594 dose_unit = self._PRW_dose_unit.GetValue().strip()
595 if dose_unit == '':
596 dose_unit = _('<delivery unit>')
597 self._LBL_info.SetLabel('%s %s %s / %s' % (subst, amount, unit, dose_unit))
598 self.Refresh()
599
600
601
602
604
605 validity = True
606
607 if self._PRW_substance.GetValue().strip() == '':
608 validity = False
609 self._PRW_substance.display_as_valid(valid = False)
610 self._PRW_substance.SetFocus()
611 else:
612 self._PRW_substance.display_as_valid(valid = True)
613
614 try:
615 decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.'))
616 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
617 except (TypeError, decimal.InvalidOperation):
618 validity = False
619 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
620 self._TCTRL_amount.SetFocus()
621
622 if self._PRW_unit.GetValue().strip() == '':
623 validity = False
624 self._PRW_unit.display_as_valid(valid = False)
625 self._PRW_unit.SetFocus()
626 else:
627 self._PRW_unit.display_as_valid(valid = True)
628
629 if validity is False:
630 self.status_message = _('Cannot save substance dose. Missing essential input.')
631
632 return validity
633
634
636 dose = gmMedication.create_substance_dose (
637 pk_substance = self._PRW_substance.GetData(),
638 substance = self._PRW_substance.GetValue().strip(),
639 amount = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.')),
640 unit = gmTools.coalesce(self._PRW_unit.GetData(), self._PRW_unit.GetValue().strip(), function_initial = ('strip', None)),
641 dose_unit = gmTools.coalesce(self._PRW_dose_unit.GetData(), self._PRW_dose_unit.GetValue().strip(), function_initial = ('strip', None))
642 )
643 success, data = dose.save()
644 if not success:
645 err, msg = data
646 _log.error(err)
647 _log.error(msg)
648 self.status_message = _('Cannot create substance dose. %s') % msg
649 return False
650
651 self.data = dose
652 return True
653
654
656
657 self.data['amount'] = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.'))
658 self.data['unit'] = gmTools.coalesce(self._PRW_unit.GetData(), self._PRW_unit.GetValue().strip(), function_initial = ('strip', None))
659 self.data['dose_unit'] = gmTools.coalesce(self._PRW_dose_unit.GetData(), self._PRW_dose_unit.GetValue().strip(), function_initial = ('strip', None))
660 success, data = self.data.save()
661
662 if not success:
663 err, msg = data
664 _log.error(err)
665 _log.error(msg)
666 self.status_message = _('Cannot save substance dose. %s') % msg
667 return False
668
669 return True
670
671
673 self._PRW_substance.SetText('', None)
674 self._TCTRL_amount.SetValue('')
675 self._PRW_unit.SetText('', None)
676 self._PRW_dose_unit.SetText('', None)
677 self._LBL_info.SetLabel('')
678
679 self._PRW_substance.SetFocus()
680
681
690
691
693 self._refresh_as_new()
694
695
696
697
707
708
709 def delete(component):
710 if component.is_in_use_by_patients:
711 gmDispatcher.send(signal = 'statustext', msg = _('Cannot remove this component from the drug. It is in use.'), beep = True)
712 return False
713 return component.containing_drug.remove_component(pk_component = component['pk_component'])
714
715
716 def refresh(lctrl):
717 comps = gmMedication.get_drug_components()
718 items = [ [
719 '%s%s' % (c['substance'], gmTools.coalesce(c['atc_substance'], '', ' [%s]')),
720 '%s %s' % (c['amount'], c.formatted_units),
721 '%s%s' % (c['product'], gmTools.coalesce(c['atc_drug'], '', ' [%s]')),
722 c['l10n_preparation'],
723 gmTools.coalesce(c['external_code'], '', '%%s [%s]' % c['external_code_type']),
724 c['pk_component']
725 ] for c in comps ]
726 lctrl.set_string_items(items)
727 lctrl.set_data(comps)
728
729
730 def get_item_tooltip(component):
731 if not isinstance(component, gmMedication.cDrugComponent):
732 return None
733 return component.format(include_loincs = True)
734
735
736 gmListWidgets.get_choices_from_list (
737 parent = parent,
738 caption = _('Drug components currently known to GNUmed'),
739 columns = [_('Component'), _('Strength'), _('Product name'), _('Preparation'), _('Code'), '#'],
740 single_selection = True,
741
742 edit_callback = edit,
743 delete_callback = delete,
744 refresh_callback = refresh,
745 list_tooltip_callback = get_item_tooltip
746 )
747
748
760
761
762 from Gnumed.wxGladeWidgets import wxgDrugComponentEAPnl
763
764 -class cDrugComponentEAPnl(wxgDrugComponentEAPnl.wxgDrugComponentEAPnl, gmEditArea.cGenericEditAreaMixin):
765
783
784
785
786
787
788
789
790
792 if self.data is not None:
793 if self.data['is_in_use']:
794 self.status_message = _('Cannot edit drug component. It is in use.')
795 return False
796
797 validity = True
798
799 if self._PRW_substance.GetData() is None:
800 validity = False
801 self._PRW_substance.display_as_valid(False)
802 else:
803 self._PRW_substance.display_as_valid(True)
804
805 val = self._TCTRL_amount.GetValue().strip().replace(',', '.', 1)
806 try:
807 decimal.Decimal(val)
808 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
809 except:
810 validity = False
811 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
812
813 if self._PRW_unit.GetValue().strip() == '':
814 validity = False
815 self._PRW_unit.display_as_valid(False)
816 else:
817 self._PRW_unit.display_as_valid(True)
818
819 if validity is False:
820 self.status_message = _('Cannot save drug component. Invalid or missing essential input.')
821
822 return validity
823
825
826 data = 1
827 data[''] = 1
828 data[''] = 1
829
830
831
832
833
834
835 return False
836 return True
837
843
844
846 self._TCTRL_product_name.SetValue('')
847 self._TCTRL_components.SetValue('')
848 self._TCTRL_codes.SetValue('')
849 self._PRW_substance.SetText('', None)
850 self._TCTRL_amount.SetValue('')
851 self._PRW_unit.SetText('', None)
852
853 self._PRW_substance.SetFocus()
854
855
857 self._TCTRL_product_name.SetValue('%s (%s)' % (self.data['product'], self.data['l10n_preparation']))
858 self._TCTRL_components.SetValue(' / '.join(self.data.containing_drug['components']))
859 details = []
860 if self.data['atc_drug'] is not None:
861 details.append('ATC: %s' % self.data['atc_drug'])
862 if self.data['external_code_product'] is not None:
863 details.append('%s: %s' % (self.data['external_code_type_product'], self.data['external_code_product']))
864 self._TCTRL_codes.SetValue('; '.join(details))
865
866 self._PRW_substance.SetText(self.data['substance'], self.data['pk_dose'])
867 self._TCTRL_amount.SetValue('%s' % self.data['amount'])
868 self._PRW_unit.SetText(self.data['unit'], self.data['unit'])
869
870 self._PRW_substance.SetFocus()
871
872
874
875
876
877 self._PRW_substance.SetText('', None)
878 self._TCTRL_amount.SetValue('')
879 self._PRW_unit.SetText('', None)
880
881 self._PRW_substance.SetFocus()
882
883
897
898
899
900
902
903 if drug_product is not None:
904 if drug_product.is_in_use_by_patients:
905 gmGuiHelpers.gm_show_info (
906 aTitle = _('Editing drug'),
907 aMessage = _(
908 'Cannot edit the drug product\n'
909 '\n'
910 ' "%s" (%s)\n'
911 '\n'
912 'because it is currently taken by patients.\n'
913 ) % (drug_product['product'], drug_product['l10n_preparation'])
914 )
915 return False
916
917 if parent is None:
918 parent = wx.GetApp().GetTopWindow()
919
920
921 def manage_substances(drug):
922 manage_substance_doses(parent = parent)
923
924
925 ea = cDrugProductEAPnl(parent, -1)
926 ea.data = drug_product
927 ea.mode = gmTools.coalesce(drug_product, 'new', 'edit')
928 dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = single_entry)
929 dlg.SetTitle(gmTools.coalesce(drug_product, _('Adding new drug product'), _('Editing drug product')))
930 dlg.left_extra_button = (
931 _('Substances'),
932 _('Manage substances'),
933 manage_substances
934 )
935 if dlg.ShowModal() == wx.ID_OK:
936 dlg.Destroy()
937 return True
938 dlg.Destroy()
939 return False
940
941
954
955
956 def get_tooltip(product=None):
957 return product.format(include_component_details = True)
958
959
960 def edit(product):
961 if product is not None:
962 if product.is_in_use_as_vaccine:
963 gmGuiHelpers.gm_show_info (
964 aTitle = _('Editing medication'),
965 aMessage = _(
966 'Cannot edit the vaccine product\n'
967 '\n'
968 ' "%s" (%s)\n'
969 '\n'
970 'because it is in use.'
971 ) % (product['product'], product['l10n_preparation'])
972 )
973 return False
974
975 return edit_drug_product(parent = parent, drug_product = product, single_entry = True)
976
977
978 def delete(product):
979 if not product.delete_associated_vaccine():
980 gmGuiHelpers.gm_show_info (
981 aTitle = _('Deleting vaccine'),
982 aMessage = _(
983 'Cannot delete the vaccine product\n'
984 '\n'
985 ' "%s" (%s)\n'
986 '\n'
987 'because it is in use.'
988 ) % (product['product'], product['l10n_preparation'])
989 )
990 return False
991
992 gmMedication.delete_drug_product(pk_drug_product = product['pk_drug_product'])
993 return True
994
995
996 def new():
997 return edit_drug_product(parent = parent, drug_product = None, single_entry = False)
998
999
1000 def refresh(lctrl):
1001 drugs = gmMedication.get_drug_products()
1002 items = [ [
1003 '%s%s' % (
1004 d['product'],
1005 gmTools.bool2subst(d['is_fake_product'], ' (%s)' % _('fake'), '')
1006 ),
1007 d['l10n_preparation'],
1008 gmTools.coalesce(d['atc'], ''),
1009 '; '.join([ '%s %s%s' % (
1010 c['substance'],
1011 c['amount'],
1012 gmMedication.format_units(c['unit'], c['dose_unit'])
1013 ) for c in d['components']]),
1014 gmTools.coalesce(d['external_code'], '', '%%s [%s]' % d['external_code_type']),
1015 d['pk_drug_product']
1016 ] for d in drugs ]
1017 lctrl.set_string_items(items)
1018 lctrl.set_data(drugs)
1019
1020
1021 gmListWidgets.get_choices_from_list (
1022 parent = parent,
1023 caption = _('Drug products currently known to GNUmed.'),
1024 columns = [_('Name'), _('Preparation'), _('ATC'), _('Components'), _('Code'), '#'],
1025 single_selection = True,
1026 ignore_OK_button = ignore_OK_button,
1027 refresh_callback = refresh,
1028 new_callback = new,
1029 edit_callback = edit,
1030 delete_callback = delete,
1031 list_tooltip_callback = get_tooltip,
1032 left_extra_button = (_('Import'), _('Import substances and products from a drug database.'), add_from_db)
1033
1034
1035 )
1036
1037
1039
1040 if product is not None:
1041 if product.is_in_use_by_patients:
1042 gmGuiHelpers.gm_show_info (
1043 aTitle = _('Managing components of a drug'),
1044 aMessage = _(
1045 'Cannot manage the components of the drug product\n'
1046 '\n'
1047 ' "%s" (%s)\n'
1048 '\n'
1049 'because it is currently taken by patients.\n'
1050 ) % (product['product'], product['l10n_preparation'])
1051 )
1052 return False
1053
1054
1055 if parent is None:
1056 parent = wx.GetApp().GetTopWindow()
1057
1058
1059
1060
1061
1062
1063 if product is None:
1064 msg = _('Pick the substance doses which are components of this drug.')
1065 right_col = _('Components of drug')
1066 comp_doses = []
1067 else:
1068 right_col = '%s (%s)' % (product['product'], product['l10n_preparation'])
1069 msg = _(
1070 'Adjust the components of "%s"\n'
1071 '\n'
1072 'The drug must contain at least one component. Any given\n'
1073 'substance can only be included once per drug.'
1074 ) % right_col
1075 comp_doses = [ comp.substance_dose for comp in product.components ]
1076
1077 doses = gmMedication.get_substance_doses(order_by = 'substance')
1078 choices = [ '%s %s %s' % (d['substance'], d['amount'], d.formatted_units) for d in doses ]
1079 picks = [ '%s %s %s' % (d['substance'], d['amount'], d.formatted_units) for d in comp_doses ]
1080
1081 picker = gmListWidgets.cItemPickerDlg (
1082 parent,
1083 -1,
1084 title = _('Managing components of a drug ...'),
1085 msg = msg
1086 )
1087 picker.set_columns(['Substance doses'], [right_col])
1088 picker.set_choices(choices = choices, data = doses)
1089 picker.set_picks(picks = picks, data = comp_doses)
1090
1091
1092
1093
1094
1095
1096 btn_pressed = picker.ShowModal()
1097 doses2set = picker.get_picks()
1098 picker.Destroy()
1099
1100 if btn_pressed != wx.ID_OK:
1101 return (False, None)
1102
1103 if product is not None:
1104 product.set_substance_doses_as_components(substance_doses = doses2set)
1105
1106 return (True, doses2set)
1107
1108
1109 from Gnumed.wxGladeWidgets import wxgDrugProductEAPnl
1110
1111 -class cDrugProductEAPnl(wxgDrugProductEAPnl.wxgDrugProductEAPnl, gmEditArea.cGenericEditAreaMixin):
1112
1128
1129
1130
1131
1132
1133
1134
1135
1137
1138 if self.data is not None:
1139 if self.data.is_in_use_by_patients:
1140 self.status_message = _('Cannot edit drug product. It is in use.')
1141 return False
1142
1143 validity = True
1144
1145 product_name = self._PRW_product_name.GetValue().strip()
1146 if product_name == '':
1147 validity = False
1148 self._PRW_product_name.display_as_valid(False)
1149 else:
1150 self._PRW_product_name.display_as_valid(True)
1151
1152 preparation = self._PRW_preparation.GetValue().strip()
1153 if preparation == '':
1154 validity = False
1155 self._PRW_preparation.display_as_valid(False)
1156 else:
1157 self._PRW_preparation.display_as_valid(True)
1158
1159 if validity is True:
1160
1161 drug = gmMedication.get_drug_by_name(product_name = product_name, preparation = preparation)
1162 if drug is not None:
1163 if self.mode != 'edit':
1164 validity = False
1165 self._PRW_product_name.display_as_valid(False)
1166 self._PRW_preparation.display_as_valid(False)
1167 gmGuiHelpers.gm_show_error (
1168 title = _('Checking product data'),
1169 error = _(
1170 'The information you entered:\n'
1171 '\n'
1172 ' [%s %s]\n'
1173 '\n'
1174 'already exists as a drug product.'
1175 ) % (product_name, preparation)
1176 )
1177 else:
1178
1179 self._TCTRL_components.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND))
1180 if len(self.__component_doses) == 0:
1181 wants_empty = gmGuiHelpers.gm_show_question (
1182 title = _('Checking product data'),
1183 question = _(
1184 'You have not selected any substances\n'
1185 'as drug components.\n'
1186 '\n'
1187 'Without components you will not be able to\n'
1188 'use this drug for documenting patient care.\n'
1189 '\n'
1190 'Are you sure you want to save\n'
1191 'it without components ?'
1192 )
1193 )
1194 if not wants_empty:
1195 validity = False
1196 self.display_ctrl_as_valid(ctrl = self._TCTRL_components, valid = False)
1197
1198 if validity is False:
1199 self.status_message = _('Cannot save drug product. Invalid or missing essential input.')
1200
1201 return validity
1202
1203
1205
1206 drug = gmMedication.create_drug_product (
1207 product_name = self._PRW_product_name.GetValue().strip(),
1208 preparation = gmTools.coalesce (
1209 self._PRW_preparation.GetData(),
1210 self._PRW_preparation.GetValue()
1211 ).strip(),
1212 return_existing = True
1213 )
1214 drug['is_fake_product'] = self._CHBOX_is_fake.GetValue()
1215 drug['atc'] = self._PRW_atc.GetData()
1216 code = self._TCTRL_external_code.GetValue().strip()
1217 if code != '':
1218 drug['external_code'] = code
1219 drug['external_code_type'] = self._PRW_external_code_type.GetData().strip()
1220
1221 drug.save()
1222
1223 if len(self.__component_doses) > 0:
1224 drug.set_substance_doses_as_components(substance_doses = self.__component_doses)
1225
1226 self.data = drug
1227
1228 return True
1229
1230
1232 self.data['product'] = self._PRW_product_name.GetValue().strip()
1233 self.data['preparation'] = gmTools.coalesce (
1234 self._PRW_preparation.GetData(),
1235 self._PRW_preparation.GetValue()
1236 ).strip()
1237 self.data['is_fake_product'] = self._CHBOX_is_fake.GetValue()
1238 self.data['atc'] = self._PRW_atc.GetData()
1239 code = self._TCTRL_external_code.GetValue().strip()
1240 if code != '':
1241 self.data['external_code'] = code
1242 self.data['external_code_type'] = self._PRW_external_code_type.GetData().strip()
1243 success, data = self.data.save()
1244 if not success:
1245 err, msg = data
1246 _log.error('problem saving')
1247 _log.error('%s', err)
1248 _log.error('%s', msg)
1249 return (success is True)
1250
1251
1253 self._PRW_product_name.SetText('', None)
1254 self._PRW_preparation.SetText('', None)
1255 self._CHBOX_is_fake.SetValue(False)
1256 self._TCTRL_components.SetValue('')
1257 self._PRW_atc.SetText('', None)
1258 self._TCTRL_external_code.SetValue('')
1259 self._PRW_external_code_type.SetText('', None)
1260
1261 self._PRW_product_name.SetFocus()
1262
1263 self.__component_substances = []
1264
1265
1267 self._refresh_as_new()
1268
1269
1271 self._PRW_product_name.SetText(self.data['product'], self.data['pk_drug_product'])
1272 self._PRW_preparation.SetText(self.data['preparation'], self.data['preparation'])
1273 self._CHBOX_is_fake.SetValue(self.data['is_fake_product'])
1274 comp_str = ''
1275 if len(self.data['components']) > 0:
1276 comp_str = '- %s' % '\n- '.join([ '%s %s%s' % (c['substance'], c['amount'], gmMedication.format_units(c['unit'], c['dose_unit'])) for c in self.data['components'] ])
1277 self._TCTRL_components.SetValue(comp_str)
1278 self._PRW_atc.SetText(gmTools.coalesce(self.data['atc'], ''), self.data['atc'])
1279 self._TCTRL_external_code.SetValue(gmTools.coalesce(self.data['external_code'], ''))
1280 t = gmTools.coalesce(self.data['external_code_type'], '')
1281 self._PRW_external_code_type.SetText(t, t)
1282
1283 self._PRW_product_name.SetFocus()
1284
1285 self.__component_doses = self.data.components_as_doses
1286
1287
1288
1289
1303
1304
1306
1308
1309 query = """
1310 SELECT
1311 pk
1312 AS data,
1313 (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', ''))
1314 AS list_label,
1315 (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', ''))
1316 AS field_label
1317 FROM ref.drug_product
1318 WHERE description %(fragment_condition)s
1319 ORDER BY list_label
1320 LIMIT 50"""
1321
1322 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
1323 mp.setThresholds(2, 3, 4)
1324 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1325 self.SetToolTip(_(
1326 'The product name of the drug.\n'
1327 '\n'
1328 'Note: a product name will need to be linked to\n'
1329 'one or more components before it can be used,\n'
1330 'except in the case of fake (generic) vaccines.'
1331 ))
1332 self.matcher = mp
1333 self.selection_only = False
1334
1335
1336
1337
1338
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355 if parent is None:
1356 parent = wx.GetApp().GetTopWindow()
1357
1358
1359
1360
1361
1362
1363 ea = cSingleComponentGenericDrugEAPnl(parent, -1)
1364 ea.data = drug
1365 ea.mode = gmTools.coalesce(drug, 'new', 'edit')
1366 dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = single_entry)
1367 if fields is not None:
1368 ea.set_fields(fields)
1369 dlg.SetTitle(gmTools.coalesce(drug, _('Adding new single-component generic drug'), _('Editing single-component generic drug')))
1370
1371
1372
1373
1374
1375 if dlg.ShowModal() == wx.ID_OK:
1376 drug = ea.data
1377 dlg.Destroy()
1378 if return_drug:
1379 return drug
1380 return True
1381 dlg.Destroy()
1382 if return_drug:
1383 return None
1384 return False
1385
1386
1399
1400
1401 def get_tooltip(product=None):
1402 return product.format(include_component_details = True)
1403
1404
1405 def edit(product):
1406 if product is not None:
1407 if product.is_vaccine:
1408 gmGuiHelpers.gm_show_info (
1409 aTitle = _('Editing medication'),
1410 aMessage = _(
1411 'Cannot edit the medication\n'
1412 '\n'
1413 ' "%s" (%s)\n'
1414 '\n'
1415 'because it is a vaccine. Please edit it\n'
1416 'from the vaccine management section !\n'
1417 ) % (product['product'], product['l10n_preparation'])
1418 )
1419 return False
1420
1421 return edit_drug_product(parent = parent, drug_product = product, single_entry = True)
1422
1423
1424 def delete(product):
1425 if product.is_vaccine:
1426 gmGuiHelpers.gm_show_info (
1427 aTitle = _('Deleting medication'),
1428 aMessage = _(
1429 'Cannot delete the medication\n'
1430 '\n'
1431 ' "%s" (%s)\n'
1432 '\n'
1433 'because it is a vaccine. Please delete it\n'
1434 'from the vaccine management section !\n'
1435 ) % (product['product'], product['l10n_preparation'])
1436 )
1437 return False
1438 gmMedication.delete_drug_product(pk_drug_product = product['pk_drug_product'])
1439 return True
1440
1441
1442 def new():
1443 return edit_drug_product(parent = parent, drug_product = None, single_entry = False)
1444
1445
1446 def refresh(lctrl):
1447 drugs = gmMedication.get_drug_products()
1448 items = [ [
1449 '%s%s' % (
1450 d['product'],
1451 gmTools.bool2subst(d['is_fake_product'], ' (%s)' % _('fake'), '')
1452 ),
1453 d['l10n_preparation'],
1454 gmTools.coalesce(d['atc'], ''),
1455 '; '.join([ '%s %s%s' % (
1456 c['substance'],
1457 c['amount'],
1458 gmMedication.format_units(c['unit'], c['dose_unit'])
1459 ) for c in d['components']]),
1460 gmTools.coalesce(d['external_code'], '', '%%s [%s]' % d['external_code_type']),
1461 d['pk_drug_product']
1462 ] for d in drugs ]
1463 lctrl.set_string_items(items)
1464 lctrl.set_data(drugs)
1465
1466
1467 gmListWidgets.get_choices_from_list (
1468 parent = parent,
1469 caption = _('Drug products currently known to GNUmed.'),
1470 columns = [_('Name'), _('Preparation'), _('ATC'), _('Components'), _('Code'), '#'],
1471 single_selection = True,
1472 ignore_OK_button = ignore_OK_button,
1473 refresh_callback = refresh,
1474 new_callback = new,
1475 edit_callback = edit,
1476 delete_callback = delete,
1477 list_tooltip_callback = get_tooltip,
1478
1479
1480
1481 )
1482
1483
1484 from Gnumed.wxGladeWidgets import wxgSingleComponentGenericDrugEAPnl
1485
1487
1505
1506
1515
1516
1517
1518
1562
1563
1565
1566 dose = gmMedication.create_substance_dose (
1567 pk_substance = self._PRW_substance.GetData(),
1568 amount = self._TCTRL_amount.Value.strip(),
1569 unit = self._PRW_unit.GetData(),
1570 dose_unit = self._PRW_dose_unit.GetData()
1571 )
1572 dose_unit = self._PRW_dose_unit.GetValue().strip()
1573 if dose_unit != '':
1574 dose_unit = '/' + dose_unit
1575 name = '%s %s%s%s' % (
1576 self._PRW_substance.GetValue().strip(),
1577 self._TCTRL_amount.Value.strip(),
1578 self._PRW_unit.GetValue().strip(),
1579 dose_unit
1580 )
1581 drug = gmMedication.create_drug_product (
1582 product_name = name,
1583 preparation = self._PRW_preparation.GetValue().strip(),
1584 return_existing = True
1585 )
1586 drug['is_fake_product'] = True
1587 drug.save()
1588 drug.set_substance_doses_as_components(substance_doses = [dose])
1589 self.data = drug
1590 return True
1591
1592
1595
1596
1597
1598
1599
1601 self._LBL_drug_name.SetLabel('')
1602 self._PRW_substance.SetText('', None)
1603 self._TCTRL_amount.SetValue('')
1604 self._PRW_unit.SetText('', None)
1605 self._PRW_dose_unit.SetText('', None)
1606 self._PRW_preparation.SetText('', None)
1607
1608
1610 self._refresh_as_new()
1611
1612
1615
1616
1618 try:
1619 self._PRW_substance.SetText(fields['substance']['value'], fields['substance']['data'])
1620 except KeyError:
1621 _log.error('cannot set field [substance] from <%s>', fields)
1622
1623
1625 dose_unit = self._PRW_dose_unit.GetValue().strip()
1626 if dose_unit != '':
1627 dose_unit = '/' + dose_unit
1628 name = '%s %s%s%s' % (
1629 self._PRW_substance.GetValue().strip(),
1630 self._TCTRL_amount.Value.strip(),
1631 self._PRW_unit.GetValue().strip(),
1632 dose_unit
1633 )
1634 self._LBL_drug_name.SetLabel('"%s"' % name.strip())
1635
1636
1637
1638
1639 if __name__ == '__main__':
1640
1641 if len(sys.argv) < 2:
1642 sys.exit()
1643
1644 if sys.argv[1] != 'test':
1645 sys.exit()
1646
1647 from Gnumed.business import gmPersonSearch
1648
1649 pat = gmPersonSearch.ask_for_patient()
1650 if pat is None:
1651 sys.exit()
1652 gmPerson.set_active_patient(patient = pat)
1653
1654
1655 app = wx.PyWidgetTester(size = (600, 300))
1656 app.SetWidget(cSubstancePhraseWheel, -1)
1657 app.MainLoop()
1658
1659 edit_single_component_generic_drug (
1660 single_entry = True,
1661 fields = {'substance': {'value': val, 'data': None}},
1662 return_drug = True
1663 )
1664