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 Exception:
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.DestroyLater()
220 return True
221 dlg.DestroyLater()
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.StatusText = _('Cannot save: Substance name missing.')
326
327 return validity
328
329
351
352
371
372
381
382
395
396
398 self._refresh_as_new()
399
400
401
402
415
416
419
420
422
424
425 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
426 query = """
427 SELECT DISTINCT ON (list_label)
428 list_label,
429 field_label,
430 data
431 FROM (
432 SELECT
433 description AS list_label,
434 description AS field_label,
435 pk AS data
436 FROM ref.substance
437 WHERE
438 description %(fragment_condition)s
439
440 UNION ALL
441
442 SELECT
443 term AS list_label,
444 term AS field_label,
445 NULL::integer AS data
446 FROM ref.atc
447 WHERE
448 term %(fragment_condition)s
449
450 ) AS candidates
451 ORDER BY list_label
452 LIMIT 50
453 """
454 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
455 mp.setThresholds(1, 2, 4)
456
457 self.SetToolTip(_('The substance name.'))
458 self.matcher = mp
459 self.selection_only = False
460 self.phrase_separators = None
461
462
465
466
467
468
470
471 if substance_dose is not None:
472 if substance_dose.is_in_use_by_patients:
473 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit this substance. It is in use.'), beep = True)
474 return False
475
476 ea = cSubstanceDoseEAPnl(parent, -1)
477 ea.data = substance_dose
478 ea.mode = gmTools.coalesce(substance_dose, 'new', 'edit')
479 dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = single_entry)
480 dlg.SetTitle(gmTools.coalesce(substance_dose, _('Adding new substance dose'), _('Editing substance dose')))
481 if dlg.ShowModal() == wx.ID_OK:
482 dlg.DestroyLater()
483 return True
484 dlg.DestroyLater()
485 return False
486
487
500
501
502 def edit(substance_dose=None):
503 return edit_substance_dose(parent = parent, substance_dose = substance_dose, single_entry = (substance_dose is not None))
504
505
506 def delete(substance_dose):
507 if substance_dose.is_in_use_by_patients:
508 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete this substance. It is in use.'), beep = True)
509 return False
510 return gmMedication.delete_substance_dose(pk_dose = substance_dose['pk_dose'])
511
512
513 def refresh(lctrl):
514 if vaccine_indications_only:
515 substs = [ s for s in gmMedication.get_substance_doses(order_by = 'substance') if gmTools.coalesce(s['atc_substance'], '').startswith('J07') ]
516 else:
517 substs = gmMedication.get_substance_doses(order_by = 'substance')
518 items = [ [
519 s['substance'],
520 s['amount'],
521 s.formatted_units,
522 gmTools.coalesce(s['atc_substance'], ''),
523 gmTools.coalesce(s['intake_instructions'], ''),
524 s['pk_dose']
525 ] for s in substs ]
526 lctrl.set_string_items(items)
527 lctrl.set_data(substs)
528
529
530 def get_item_tooltip(substance_dose):
531 if not isinstance(substance_dose, gmMedication.cSubstanceDose):
532 return None
533 return substance_dose.format(include_loincs = True)
534
535
536 return gmListWidgets.get_choices_from_list (
537 parent = parent,
538 caption = _('Substance doses registered with GNUmed.'),
539 columns = [_('Substance'), _('Amount'), _('Unit'), 'ATC', _('Instructions'), '#'],
540 single_selection = False,
541 can_return_empty = False,
542 new_callback = edit,
543 edit_callback = edit,
544 delete_callback = delete,
545 refresh_callback = refresh,
546 left_extra_button = (_('Import'), _('Import substance doses from a drug database.'), add_from_db),
547 list_tooltip_callback = get_item_tooltip
548 )
549
550
551 from Gnumed.wxGladeWidgets import wxgSubstanceDoseEAPnl
552
553 -class cSubstanceDoseEAPnl(wxgSubstanceDoseEAPnl.wxgSubstanceDoseEAPnl, gmEditArea.cGenericEditAreaMixin):
554
574
575
584
585
587 subst = self._PRW_substance.GetValue().strip()
588 if subst == '':
589 subst = '?'
590 amount = self._TCTRL_amount.GetValue().strip()
591 if amount == '':
592 amount = '?'
593 unit = self._PRW_unit.GetValue().strip()
594 if unit == '':
595 unit = '?'
596 dose_unit = self._PRW_dose_unit.GetValue().strip()
597 if dose_unit == '':
598 dose_unit = _('<delivery unit>')
599 self._LBL_info.SetLabel('%s %s %s / %s' % (subst, amount, unit, dose_unit))
600 self.Refresh()
601
602
603
604
606
607 validity = True
608
609 if self._PRW_substance.GetValue().strip() == '':
610 validity = False
611 self._PRW_substance.display_as_valid(valid = False)
612 self._PRW_substance.SetFocus()
613 else:
614 self._PRW_substance.display_as_valid(valid = True)
615
616 try:
617 decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.'))
618 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
619 except (TypeError, decimal.InvalidOperation):
620 validity = False
621 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
622 self._TCTRL_amount.SetFocus()
623
624 if self._PRW_unit.GetValue().strip() == '':
625 validity = False
626 self._PRW_unit.display_as_valid(valid = False)
627 self._PRW_unit.SetFocus()
628 else:
629 self._PRW_unit.display_as_valid(valid = True)
630
631 if validity is False:
632 self.StatusText = _('Cannot save substance dose. Missing essential input.')
633
634 return validity
635
636
638 pk_subst = self._PRW_substance.GetData()
639 dose = gmMedication.create_substance_dose (
640 pk_substance = pk_subst,
641 substance = self._PRW_substance.GetValue().strip() if (pk_subst is None) else None,
642 amount = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.')),
643 unit = gmTools.coalesce(self._PRW_unit.GetData(), self._PRW_unit.GetValue().strip(), function4value = ('strip', None)),
644 dose_unit = gmTools.coalesce(self._PRW_dose_unit.GetData(), self._PRW_dose_unit.GetValue().strip(), function4value = ('strip', None))
645 )
646 success, data = dose.save()
647 if not success:
648 err, msg = data
649 _log.error(err)
650 _log.error(msg)
651 self.StatusText = _('Cannot create substance dose. %s') % msg
652 return False
653
654 self.data = dose
655 return True
656
657
659
660 self.data['amount'] = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.'))
661 self.data['unit'] = gmTools.coalesce(self._PRW_unit.GetData(), self._PRW_unit.GetValue().strip(), function4value = ('strip', None))
662 self.data['dose_unit'] = gmTools.coalesce(self._PRW_dose_unit.GetData(), self._PRW_dose_unit.GetValue().strip(), function4value = ('strip', None))
663 success, data = self.data.save()
664
665 if not success:
666 err, msg = data
667 _log.error(err)
668 _log.error(msg)
669 self.StatusText = _('Cannot save substance dose. %s') % msg
670 return False
671
672 return True
673
674
676 self._PRW_substance.SetText('', None)
677 self._TCTRL_amount.SetValue('')
678 self._PRW_unit.SetText('', None)
679 self._PRW_dose_unit.SetText('', None)
680 self._LBL_info.SetLabel('')
681
682 self._PRW_substance.SetFocus()
683
684
693
694
696 self._refresh_as_new()
697
698
699
700
710
711
712 def delete(component):
713 if component.is_in_use_by_patients:
714 gmDispatcher.send(signal = 'statustext', msg = _('Cannot remove this component from the drug. It is in use.'), beep = True)
715 return False
716 return component.containing_drug.remove_component(pk_component = component['pk_component'])
717
718
719 def refresh(lctrl):
720 comps = gmMedication.get_drug_components()
721 items = [ [
722 '%s%s' % (c['substance'], gmTools.coalesce(c['atc_substance'], '', ' [%s]')),
723 '%s %s' % (c['amount'], c.formatted_units),
724 '%s%s' % (c['product'], gmTools.coalesce(c['atc_drug'], '', ' [%s]')),
725 c['l10n_preparation'],
726 gmTools.coalesce(c['external_code'], '', '%%s [%s]' % c['external_code_type']),
727 c['pk_component']
728 ] for c in comps ]
729 lctrl.set_string_items(items)
730 lctrl.set_data(comps)
731
732
733 def get_item_tooltip(component):
734 if not isinstance(component, gmMedication.cDrugComponent):
735 return None
736 return component.format(include_loincs = True)
737
738
739 gmListWidgets.get_choices_from_list (
740 parent = parent,
741 caption = _('Drug components currently known to GNUmed'),
742 columns = [_('Component'), _('Strength'), _('Product name'), _('Preparation'), _('Code'), '#'],
743 single_selection = True,
744
745 edit_callback = edit,
746 delete_callback = delete,
747 refresh_callback = refresh,
748 list_tooltip_callback = get_item_tooltip
749 )
750
751
763
764
765 from Gnumed.wxGladeWidgets import wxgDrugComponentEAPnl
766
767 -class cDrugComponentEAPnl(wxgDrugComponentEAPnl.wxgDrugComponentEAPnl, gmEditArea.cGenericEditAreaMixin):
768
786
787
788
789
790
791
792
793
795 if self.data is not None:
796 if self.data['is_in_use']:
797 self.StatusText = _('Cannot edit drug component. It is in use.')
798 return False
799
800 validity = True
801
802 if self._PRW_substance.GetData() is None:
803 validity = False
804 self._PRW_substance.display_as_valid(False)
805 else:
806 self._PRW_substance.display_as_valid(True)
807
808 val = self._TCTRL_amount.GetValue().strip().replace(',', '.', 1)
809 try:
810 decimal.Decimal(val)
811 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
812 except Exception:
813 validity = False
814 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
815
816 if self._PRW_unit.GetValue().strip() == '':
817 validity = False
818 self._PRW_unit.display_as_valid(False)
819 else:
820 self._PRW_unit.display_as_valid(True)
821
822 if validity is False:
823 self.StatusText = _('Cannot save drug component. Invalid or missing essential input.')
824
825 return validity
826
828
829 data = 1
830 data[''] = 1
831 data[''] = 1
832
833
834
835
836
837
838 return False
839 return True
840
846
847
849 self._TCTRL_product_name.SetValue('')
850 self._TCTRL_components.SetValue('')
851 self._TCTRL_codes.SetValue('')
852 self._PRW_substance.SetText('', None)
853 self._TCTRL_amount.SetValue('')
854 self._PRW_unit.SetText('', None)
855
856 self._PRW_substance.SetFocus()
857
858
860 self._TCTRL_product_name.SetValue('%s (%s)' % (self.data['product'], self.data['l10n_preparation']))
861 self._TCTRL_components.SetValue(' / '.join(self.data.containing_drug['components']))
862 details = []
863 if self.data['atc_drug'] is not None:
864 details.append('ATC: %s' % self.data['atc_drug'])
865 if self.data['external_code_product'] is not None:
866 details.append('%s: %s' % (self.data['external_code_type_product'], self.data['external_code_product']))
867 self._TCTRL_codes.SetValue('; '.join(details))
868
869 self._PRW_substance.SetText(self.data['substance'], self.data['pk_dose'])
870 self._TCTRL_amount.SetValue('%s' % self.data['amount'])
871 self._PRW_unit.SetText(self.data['unit'], self.data['unit'])
872
873 self._PRW_substance.SetFocus()
874
875
877
878
879
880 self._PRW_substance.SetText('', None)
881 self._TCTRL_amount.SetValue('')
882 self._PRW_unit.SetText('', None)
883
884 self._PRW_substance.SetFocus()
885
886
900
901
902
903
905
906 if drug_product is not None:
907 if drug_product.is_in_use_by_patients:
908 gmGuiHelpers.gm_show_info (
909 aTitle = _('Editing drug'),
910 aMessage = _(
911 'Cannot edit the drug product\n'
912 '\n'
913 ' "%s" (%s)\n'
914 '\n'
915 'because it is currently taken by patients.\n'
916 ) % (drug_product['product'], drug_product['l10n_preparation'])
917 )
918 return False
919
920 if parent is None:
921 parent = wx.GetApp().GetTopWindow()
922
923
924 def manage_substances(drug):
925 manage_substance_doses(parent = parent)
926
927
928 ea = cDrugProductEAPnl(parent, -1)
929 ea.data = drug_product
930 ea.mode = gmTools.coalesce(drug_product, 'new', 'edit')
931 dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = single_entry)
932 dlg.SetTitle(gmTools.coalesce(drug_product, _('Adding new drug product'), _('Editing drug product')))
933 dlg.left_extra_button = (
934 _('Substances'),
935 _('Manage substances'),
936 manage_substances
937 )
938 if dlg.ShowModal() == wx.ID_OK:
939 dlg.DestroyLater()
940 return True
941 dlg.DestroyLater()
942 return False
943
944
957
958
959 def get_tooltip(product=None):
960 return product.format(include_component_details = True)
961
962
963 def edit(product):
964 if product is not None:
965 if product.is_in_use_as_vaccine:
966 gmGuiHelpers.gm_show_info (
967 aTitle = _('Editing medication'),
968 aMessage = _(
969 'Cannot edit the vaccine product\n'
970 '\n'
971 ' "%s" (%s)\n'
972 '\n'
973 'because it is in use.'
974 ) % (product['product'], product['l10n_preparation'])
975 )
976 return False
977
978 return edit_drug_product(parent = parent, drug_product = product, single_entry = True)
979
980
981 def delete(product):
982 if not product.delete_associated_vaccine():
983 gmGuiHelpers.gm_show_info (
984 aTitle = _('Deleting vaccine'),
985 aMessage = _(
986 'Cannot delete the vaccine product\n'
987 '\n'
988 ' "%s" (%s)\n'
989 '\n'
990 'because it is in use.'
991 ) % (product['product'], product['l10n_preparation'])
992 )
993 return False
994
995 gmMedication.delete_drug_product(pk_drug_product = product['pk_drug_product'])
996 return True
997
998
999 def new():
1000 return edit_drug_product(parent = parent, drug_product = None, single_entry = False)
1001
1002
1003 def refresh(lctrl):
1004 drugs = gmMedication.get_drug_products()
1005 items = [ [
1006 '%s%s' % (
1007 d['product'],
1008 gmTools.bool2subst(d['is_fake_product'], ' (%s)' % _('fake'), '')
1009 ),
1010 d['l10n_preparation'],
1011 gmTools.coalesce(d['atc'], ''),
1012 '; '.join([ '%s %s%s' % (
1013 c['substance'],
1014 c['amount'],
1015 gmMedication.format_units(c['unit'], c['dose_unit'])
1016 ) for c in d['components']]),
1017 gmTools.coalesce(d['external_code'], '', '%%s [%s]' % d['external_code_type']),
1018 d['pk_drug_product']
1019 ] for d in drugs ]
1020 lctrl.set_string_items(items)
1021 lctrl.set_data(drugs)
1022
1023
1024 gmListWidgets.get_choices_from_list (
1025 parent = parent,
1026 caption = _('Drug products currently known to GNUmed.'),
1027 columns = [_('Name'), _('Preparation'), _('ATC'), _('Components'), _('Code'), '#'],
1028 single_selection = True,
1029 ignore_OK_button = ignore_OK_button,
1030 refresh_callback = refresh,
1031 new_callback = new,
1032 edit_callback = edit,
1033 delete_callback = delete,
1034 list_tooltip_callback = get_tooltip,
1035 left_extra_button = (_('Import'), _('Import substances and products from a drug database.'), add_from_db)
1036
1037
1038 )
1039
1040
1042
1043 if product is not None:
1044 if product.is_in_use_by_patients:
1045 gmGuiHelpers.gm_show_info (
1046 aTitle = _('Managing components of a drug'),
1047 aMessage = _(
1048 'Cannot manage the components of the drug product\n'
1049 '\n'
1050 ' "%s" (%s)\n'
1051 '\n'
1052 'because it is currently taken by patients.\n'
1053 ) % (product['product'], product['l10n_preparation'])
1054 )
1055 return False
1056
1057
1058 if parent is None:
1059 parent = wx.GetApp().GetTopWindow()
1060
1061
1062
1063
1064
1065
1066 if product is None:
1067 msg = _('Pick the substance doses which are components of this drug.')
1068 right_col = _('Components of drug')
1069 comp_doses = []
1070 else:
1071 right_col = '%s (%s)' % (product['product'], product['l10n_preparation'])
1072 msg = _(
1073 'Adjust the components of "%s"\n'
1074 '\n'
1075 'The drug must contain at least one component. Any given\n'
1076 'substance can only be included once per drug.'
1077 ) % right_col
1078 comp_doses = [ comp.substance_dose for comp in product.components ]
1079
1080 doses = gmMedication.get_substance_doses(order_by = 'substance')
1081 choices = [ '%s %s %s' % (d['substance'], d['amount'], d.formatted_units) for d in doses ]
1082 picks = [ '%s %s %s' % (d['substance'], d['amount'], d.formatted_units) for d in comp_doses ]
1083
1084 picker = gmListWidgets.cItemPickerDlg (
1085 parent,
1086 -1,
1087 title = _('Managing components of a drug ...'),
1088 msg = msg
1089 )
1090 picker.set_columns(['Substance doses'], [right_col])
1091 picker.set_choices(choices = choices, data = doses)
1092 picker.set_picks(picks = picks, data = comp_doses)
1093
1094
1095
1096
1097
1098
1099 btn_pressed = picker.ShowModal()
1100 doses2set = picker.get_picks()
1101 picker.DestroyLater()
1102
1103 if btn_pressed != wx.ID_OK:
1104 return (False, None)
1105
1106 if product is not None:
1107 product.set_substance_doses_as_components(substance_doses = doses2set)
1108
1109 return (True, doses2set)
1110
1111
1112 from Gnumed.wxGladeWidgets import wxgDrugProductEAPnl
1113
1114 -class cDrugProductEAPnl(wxgDrugProductEAPnl.wxgDrugProductEAPnl, gmEditArea.cGenericEditAreaMixin):
1115
1133
1134
1135
1136
1137
1138
1139
1140
1142
1143 if self.data is not None:
1144 if self.data.is_in_use_by_patients:
1145 self.StatusText = _('Cannot edit drug product. It is in use.')
1146 return False
1147
1148 validity = True
1149
1150 product_name = self._PRW_product_name.GetValue().strip()
1151 if product_name == '':
1152 validity = False
1153 self._PRW_product_name.display_as_valid(False)
1154 else:
1155 self._PRW_product_name.display_as_valid(True)
1156
1157 preparation = self._PRW_preparation.GetValue().strip()
1158 if preparation == '':
1159 validity = False
1160 self._PRW_preparation.display_as_valid(False)
1161 else:
1162 self._PRW_preparation.display_as_valid(True)
1163
1164 if validity is True:
1165
1166 drug = gmMedication.get_drug_by_name(product_name = product_name, preparation = preparation)
1167 if drug is not None:
1168 if self.mode != 'edit':
1169 validity = False
1170 self._PRW_product_name.display_as_valid(False)
1171 self._PRW_preparation.display_as_valid(False)
1172 gmGuiHelpers.gm_show_error (
1173 title = _('Checking product data'),
1174 error = _(
1175 'The information you entered:\n'
1176 '\n'
1177 ' [%s %s]\n'
1178 '\n'
1179 'already exists as a drug product.'
1180 ) % (product_name, preparation)
1181 )
1182 else:
1183
1184 self._TCTRL_components.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND))
1185 if len(self.__component_doses) == 0:
1186 wants_empty = gmGuiHelpers.gm_show_question (
1187 title = _('Checking product data'),
1188 question = _(
1189 'You have not selected any substances\n'
1190 'as drug components.\n'
1191 '\n'
1192 'Without components you will not be able to\n'
1193 'use this drug for documenting patient care.\n'
1194 '\n'
1195 'Are you sure you want to save\n'
1196 'it without components ?'
1197 )
1198 )
1199 if not wants_empty:
1200 validity = False
1201 self.display_ctrl_as_valid(ctrl = self._TCTRL_components, valid = False)
1202
1203 if validity is False:
1204 self.StatusText = _('Cannot save drug product. Invalid or missing essential input.')
1205
1206 return validity
1207
1208
1210 drug = gmMedication.create_drug_product (
1211 product_name = self._PRW_product_name.GetValue().strip(),
1212 preparation = gmTools.coalesce (
1213 self._PRW_preparation.GetData(),
1214 self._PRW_preparation.GetValue()
1215 ).strip(),
1216 doses = self.__component_doses if (len(self.__component_doses) > 0) else None,
1217 return_existing = True
1218 )
1219 drug['is_fake_product'] = self._CHBOX_is_fake.GetValue()
1220 drug['atc'] = self._PRW_atc.GetData()
1221 code = self._TCTRL_external_code.GetValue().strip()
1222 if code != '':
1223 drug['external_code'] = code
1224 drug['external_code_type'] = self._PRW_external_code_type.GetData().strip()
1225 drug.save()
1226 self.data = drug
1227 return True
1228
1229
1231 self.data['product'] = self._PRW_product_name.GetValue().strip()
1232 self.data['preparation'] = gmTools.coalesce (
1233 self._PRW_preparation.GetData(),
1234 self._PRW_preparation.GetValue()
1235 ).strip()
1236 self.data['is_fake_product'] = self._CHBOX_is_fake.GetValue()
1237 self.data['atc'] = self._PRW_atc.GetData()
1238 code = self._TCTRL_external_code.GetValue().strip()
1239 if code != '':
1240 self.data['external_code'] = code
1241 self.data['external_code_type'] = self._PRW_external_code_type.GetData().strip()
1242 success, data = self.data.save()
1243 if not success:
1244 err, msg = data
1245 _log.error('problem saving')
1246 _log.error('%s', err)
1247 _log.error('%s', msg)
1248 return (success is True)
1249
1250
1252 self._PRW_product_name.SetText('', None)
1253 self._PRW_preparation.SetText('', None)
1254 self._CHBOX_is_fake.SetValue(False)
1255 self._TCTRL_components.SetValue('')
1256 self._PRW_atc.SetText('', None)
1257 self._TCTRL_external_code.SetValue('')
1258 self._PRW_external_code_type.SetText('', None)
1259
1260 self._PRW_product_name.SetFocus()
1261
1262 self.__component_doses = []
1263
1264
1266 self._refresh_as_new()
1267
1268
1270 self._PRW_product_name.SetText(self.data['product'], self.data['pk_drug_product'])
1271 self._PRW_preparation.SetText(self.data['preparation'], self.data['preparation'])
1272 self._CHBOX_is_fake.SetValue(self.data['is_fake_product'])
1273 comp_str = ''
1274 if len(self.data['components']) > 0:
1275 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'] ])
1276 self._TCTRL_components.SetValue(comp_str)
1277 self._PRW_atc.SetText(gmTools.coalesce(self.data['atc'], ''), self.data['atc'])
1278 self._TCTRL_external_code.SetValue(gmTools.coalesce(self.data['external_code'], ''))
1279 t = gmTools.coalesce(self.data['external_code_type'], '')
1280 self._PRW_external_code_type.SetText(t, t)
1281
1282 self._PRW_product_name.SetFocus()
1283
1284 self.__component_doses = self.data.components_as_doses
1285
1286
1287
1288
1302
1303
1305
1307
1308 query = """
1309 SELECT
1310 pk
1311 AS data,
1312 (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', ''))
1313 AS list_label,
1314 (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', ''))
1315 AS field_label
1316 FROM ref.drug_product
1317 WHERE description %(fragment_condition)s
1318 ORDER BY list_label
1319 LIMIT 50"""
1320
1321 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
1322 mp.setThresholds(2, 3, 4)
1323 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1324 self.SetToolTip(_(
1325 'The product name of the drug.\n'
1326 '\n'
1327 'Note: a product name will need to be linked to\n'
1328 'one or more components before it can be used,\n'
1329 'except in the case of fake (generic) vaccines.'
1330 ))
1331 self.matcher = mp
1332 self.selection_only = False
1333
1334
1335
1336
1337
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354 if parent is None:
1355 parent = wx.GetApp().GetTopWindow()
1356
1357
1358
1359
1360
1361
1362 ea = cSingleComponentGenericDrugEAPnl(parent, -1)
1363 ea.data = drug
1364 ea.mode = gmTools.coalesce(drug, 'new', 'edit')
1365 dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = single_entry)
1366 if fields is not None:
1367 ea.set_fields(fields)
1368 dlg.SetTitle(gmTools.coalesce(drug, _('Adding new single-component generic drug'), _('Editing single-component generic drug')))
1369
1370
1371
1372
1373
1374 if dlg.ShowModal() == wx.ID_OK:
1375 drug = ea.data
1376 dlg.DestroyLater()
1377 if return_drug:
1378 return drug
1379 return True
1380 dlg.DestroyLater()
1381 if return_drug:
1382 return None
1383 return False
1384
1385
1398
1399
1400 def get_tooltip(product=None):
1401 return product.format(include_component_details = True)
1402
1403
1404 def edit(product):
1405 if product is not None:
1406 if product.is_vaccine:
1407 gmGuiHelpers.gm_show_info (
1408 aTitle = _('Editing medication'),
1409 aMessage = _(
1410 'Cannot edit the medication\n'
1411 '\n'
1412 ' "%s" (%s)\n'
1413 '\n'
1414 'because it is a vaccine. Please edit it\n'
1415 'from the vaccine management section !\n'
1416 ) % (product['product'], product['l10n_preparation'])
1417 )
1418 return False
1419
1420 return edit_drug_product(parent = parent, drug_product = product, single_entry = True)
1421
1422
1423 def delete(product):
1424 if product.is_vaccine:
1425 gmGuiHelpers.gm_show_info (
1426 aTitle = _('Deleting medication'),
1427 aMessage = _(
1428 'Cannot delete the medication\n'
1429 '\n'
1430 ' "%s" (%s)\n'
1431 '\n'
1432 'because it is a vaccine. Please delete it\n'
1433 'from the vaccine management section !\n'
1434 ) % (product['product'], product['l10n_preparation'])
1435 )
1436 return False
1437 gmMedication.delete_drug_product(pk_drug_product = product['pk_drug_product'])
1438 return True
1439
1440
1441 def new():
1442 return edit_drug_product(parent = parent, drug_product = None, single_entry = False)
1443
1444
1445 def refresh(lctrl):
1446 drugs = gmMedication.get_drug_products()
1447 items = [ [
1448 '%s%s' % (
1449 d['product'],
1450 gmTools.bool2subst(d['is_fake_product'], ' (%s)' % _('fake'), '')
1451 ),
1452 d['l10n_preparation'],
1453 gmTools.coalesce(d['atc'], ''),
1454 '; '.join([ '%s %s%s' % (
1455 c['substance'],
1456 c['amount'],
1457 gmMedication.format_units(c['unit'], c['dose_unit'])
1458 ) for c in d['components']]),
1459 gmTools.coalesce(d['external_code'], '', '%%s [%s]' % d['external_code_type']),
1460 d['pk_drug_product']
1461 ] for d in drugs ]
1462 lctrl.set_string_items(items)
1463 lctrl.set_data(drugs)
1464
1465
1466 gmListWidgets.get_choices_from_list (
1467 parent = parent,
1468 caption = _('Drug products currently known to GNUmed.'),
1469 columns = [_('Name'), _('Preparation'), _('ATC'), _('Components'), _('Code'), '#'],
1470 single_selection = True,
1471 ignore_OK_button = ignore_OK_button,
1472 refresh_callback = refresh,
1473 new_callback = new,
1474 edit_callback = edit,
1475 delete_callback = delete,
1476 list_tooltip_callback = get_tooltip,
1477
1478
1479
1480 )
1481
1482
1483 from Gnumed.wxGladeWidgets import wxgSingleComponentGenericDrugEAPnl
1484
1486
1504
1505
1514
1515
1516
1517
1561
1562
1564 dose = gmMedication.create_substance_dose (
1565 pk_substance = self._PRW_substance.GetData(),
1566 amount = self._TCTRL_amount.Value.strip(),
1567 unit = self._PRW_unit.GetData(),
1568 dose_unit = self._PRW_dose_unit.GetData()
1569 )
1570 dose_unit = self._PRW_dose_unit.GetValue().strip()
1571 if dose_unit != '':
1572 dose_unit = '/' + dose_unit
1573 name = '%s %s%s%s' % (
1574 self._PRW_substance.GetValue().strip(),
1575 self._TCTRL_amount.Value.strip(),
1576 self._PRW_unit.GetValue().strip(),
1577 dose_unit
1578 )
1579 drug = gmMedication.create_drug_product (
1580 product_name = name,
1581 preparation = self._PRW_preparation.GetValue().strip(),
1582 return_existing = True,
1583 doses = [dose]
1584 )
1585 drug['is_fake_product'] = True
1586 drug.save()
1587 self.data = drug
1588 return True
1589
1590
1593
1594
1595
1596
1597
1599 self._LBL_drug_name.SetLabel('')
1600 self._PRW_substance.SetText('', None)
1601 self._TCTRL_amount.SetValue('')
1602 self._PRW_unit.SetText('', None)
1603 self._PRW_dose_unit.SetText('', None)
1604 self._PRW_preparation.SetText('', None)
1605
1606
1608 self._refresh_as_new()
1609
1610
1613
1614
1616 try:
1617 self._PRW_substance.SetText(fields['substance']['value'], fields['substance']['data'])
1618 except KeyError:
1619 _log.error('cannot set field [substance] from <%s>', fields)
1620
1621
1623 dose_unit = self._PRW_dose_unit.GetValue().strip()
1624 if dose_unit != '':
1625 dose_unit = '/' + dose_unit
1626 name = '%s %s%s%s' % (
1627 self._PRW_substance.GetValue().strip(),
1628 self._TCTRL_amount.Value.strip(),
1629 self._PRW_unit.GetValue().strip(),
1630 dose_unit
1631 )
1632 self._LBL_drug_name.SetLabel('"%s"' % name.strip())
1633
1634
1635
1636
1637 if __name__ == '__main__':
1638
1639 if len(sys.argv) < 2:
1640 sys.exit()
1641
1642 if sys.argv[1] != 'test':
1643 sys.exit()
1644
1645 from Gnumed.business import gmPersonSearch
1646
1647 pat = gmPersonSearch.ask_for_patient()
1648 if pat is None:
1649 sys.exit()
1650 gmPerson.set_active_patient(patient = pat)
1651
1652
1653 app = wx.PyWidgetTester(size = (600, 300))
1654 app.SetWidget(cSubstancePhraseWheel, -1)
1655 app.MainLoop()
1656
1657 edit_single_component_generic_drug (
1658 single_entry = True,
1659 fields = {'substance': {'value': val, 'data': None}},
1660 return_drug = True
1661 )
1662