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, webbrowser
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
17 from Gnumed.business import gmPerson, gmATC, gmSurgery, gmMedication, gmForms
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
29
31
32 if parent is None:
33 parent = wx.GetApp().GetTopWindow()
34
35 def refresh(lctrl):
36 atcs = gmATC.get_reference_atcs()
37
38 items = [ [
39 a['atc'],
40 a['term'],
41 u'%s' % gmTools.coalesce(a['ddd'], u''),
42 gmTools.coalesce(a['unit'], u''),
43 gmTools.coalesce(a['administrative_route'], u''),
44 gmTools.coalesce(a['comment'], u''),
45 a['version'],
46 a['lang']
47 ] for a in atcs ]
48 lctrl.set_string_items(items)
49 lctrl.set_data(atcs)
50
51 gmListWidgets.get_choices_from_list (
52 parent = parent,
53 msg = _('\nThe ATC codes as known to GNUmed.\n'),
54 caption = _('Showing ATC codes.'),
55 columns = [ u'ATC', _('Term'), u'DDD', _('Unit'), _(u'Route'), _('Comment'), _('Version'), _('Language') ],
56 single_selection = True,
57 refresh_callback = refresh
58 )
59
60
61
63
64 dlg = wx.FileDialog (
65 parent = None,
66 message = _('Choose an ATC import config file'),
67 defaultDir = os.path.expanduser(os.path.join('~', 'gnumed')),
68 defaultFile = '',
69 wildcard = "%s (*.conf)|*.conf|%s (*)|*" % (_('config files'), _('all files')),
70 style = wx.OPEN | wx.HIDE_READONLY | wx.FILE_MUST_EXIST
71 )
72
73 result = dlg.ShowModal()
74 if result == wx.ID_CANCEL:
75 return
76
77 cfg_file = dlg.GetPath()
78 dlg.Destroy()
79
80 conn = gmAuthWidgets.get_dbowner_connection(procedure = _('importing ATC reference data'))
81 if conn is None:
82 return False
83
84 wx.BeginBusyCursor()
85
86 if gmATC.atc_import(cfg_fname = cfg_file, conn = conn):
87 gmDispatcher.send(signal = 'statustext', msg = _('Successfully imported ATC reference data.'))
88 else:
89 gmDispatcher.send(signal = 'statustext', msg = _('Importing ATC reference data failed.'), beep = True)
90
91 wx.EndBusyCursor()
92 return True
93
94
95
97
99
100 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
101
102 query = u"""
103
104 SELECT DISTINCT ON (label)
105 atc_code,
106 label
107 FROM (
108
109 SELECT
110 code as atc_code,
111 (code || ': ' || term || coalesce(' (' || ddd || unit || ')', ''))
112 AS label
113 FROM ref.atc
114 WHERE
115 term %(fragment_condition)s
116 OR
117 code %(fragment_condition)s
118
119 UNION ALL
120
121 SELECT
122 atc_code,
123 (atc_code || ': ' || description)
124 AS label
125 FROM ref.substance_in_brand
126 WHERE
127 description %(fragment_condition)s
128 OR
129 atc_code %(fragment_condition)s
130
131 UNION ALL
132
133 SELECT
134 atc_code,
135 (atc_code || ': ' || description || ' (' || preparation || ')')
136 AS label
137 FROM ref.branded_drug
138 WHERE
139 description %(fragment_condition)s
140 OR
141 atc_code %(fragment_condition)s
142
143 -- it would be nice to be able to include clin.vacc_indication but that's hard to do in SQL
144
145 ) AS candidates
146
147 ORDER BY label
148 LIMIT 50"""
149
150 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
151 mp.setThresholds(1, 2, 4)
152
153 self.SetToolTipString(_('Select an ATC (Anatomical-Therapeutic-Chemical) code.'))
154 self.matcher = mp
155 self.selection_only = True
156
157
158
159
161
162 if parent is None:
163 parent = wx.GetApp().GetTopWindow()
164
165
166 def delete(component):
167 gmMedication.delete_component_from_branded_drug (
168 brand = component['pk_brand'],
169 component = component['pk_substance_in_brand']
170 )
171 return True
172
173 def refresh(lctrl):
174 substs = gmMedication.get_substances_in_brands()
175 items = [ [
176 u'%s%s' % (s['brand'], gmTools.coalesce(s['atc_brand'], u'', u' (%s)')),
177 s['substance'],
178 gmTools.coalesce(s['atc_substance'], u''),
179 s['preparation'],
180 gmTools.coalesce(s['external_code_brand'], u'', u'%%s [%s]' % s['external_code_type_brand']),
181 s['pk_substance_in_brand']
182 ] for s in substs ]
183 lctrl.set_string_items(items)
184 lctrl.set_data(substs)
185
186 msg = _('\nThese are the substances in the drug brands known to GNUmed.\n')
187
188 gmListWidgets.get_choices_from_list (
189 parent = parent,
190 msg = msg,
191 caption = _('Showing drug brand components (substances).'),
192 columns = [_('Brand'), _('Substance'), u'ATC', _('Preparation'), _('Code'), u'#'],
193 single_selection = True,
194
195
196 delete_callback = delete,
197 refresh_callback = refresh
198 )
199
201
202 if parent is None:
203 parent = wx.GetApp().GetTopWindow()
204
205 def delete(brand):
206 if brand.is_vaccine:
207 gmGuiHelpers.gm_show_info (
208 aTitle = _('Deleting medication'),
209 aMessage = _(
210 'Cannot delete the medication\n'
211 '\n'
212 ' "%s" (%s)\n'
213 '\n'
214 'because it is a vaccine. Please delete it\n'
215 'from the vaccine management section !\n'
216 ) % (brand['description'], brand['preparation'])
217 )
218 return False
219 gmMedication.delete_branded_drug(brand = brand['pk'])
220 return True
221
222 def new():
223 drug_db = get_drug_database(parent = parent)
224
225 if drug_db is None:
226 return False
227
228 drug_db.import_drugs()
229
230 return True
231
232 def refresh(lctrl):
233 drugs = gmMedication.get_branded_drugs()
234 items = [ [
235 d['description'],
236 d['preparation'],
237 gmTools.coalesce(d['atc_code'], u''),
238 gmTools.coalesce(d['external_code'], u'', u'%%s [%s]' % d['external_code_type']),
239 d['pk']
240 ] for d in drugs ]
241 lctrl.set_string_items(items)
242 lctrl.set_data(drugs)
243
244 msg = _('\nThese are the drug brands known to GNUmed.\n')
245
246 gmListWidgets.get_choices_from_list (
247 parent = parent,
248 msg = msg,
249 caption = _('Showing branded drugs.'),
250 columns = [_('Name'), _('Preparation'), _('ATC'), _('Code'), u'#'],
251 single_selection = True,
252 refresh_callback = refresh,
253 new_callback = new,
254
255 delete_callback = delete
256 )
257
259
260 if parent is None:
261 parent = wx.GetApp().GetTopWindow()
262
263 def delete(substance):
264 gmMedication.delete_used_substance(substance = substance['pk'])
265 return True
266
267
268
269
270
271
272
273
274
275
276
277 def refresh(lctrl):
278 substs = gmMedication.get_substances_in_use()
279 items = [ [
280 s['description'],
281 gmTools.coalesce(s['atc_code'], u''),
282 s['pk']
283 ] for s in substs ]
284 lctrl.set_string_items(items)
285 lctrl.set_data(substs)
286
287 msg = _('Substances currently or previously consumed across all patients.')
288
289 gmListWidgets.get_choices_from_list (
290 parent = parent,
291 msg = msg,
292 caption = _('Showing consumed substances.'),
293 columns = [_('Name'), _('ATC'), u'#'],
294 single_selection = True,
295 refresh_callback = refresh,
296
297
298 delete_callback = delete
299 )
300
301
302
320
350
351
361
362
364
365 dbcfg = gmCfg.cCfgSQL()
366
367 ifap_cmd = dbcfg.get2 (
368 option = 'external.ifap-win.shell_command',
369 workplace = gmSurgery.gmCurrentPractice().active_workplace,
370 bias = 'workplace',
371 default = 'wine "C:\Ifapwin\WIAMDB.EXE"'
372 )
373 found, binary = gmShellAPI.detect_external_binary(ifap_cmd)
374 if not found:
375 gmDispatcher.send('statustext', msg = _('Cannot call IFAP via [%s].') % ifap_cmd)
376 return False
377 ifap_cmd = binary
378
379 if import_drugs:
380 transfer_file = os.path.expanduser(dbcfg.get2 (
381 option = 'external.ifap-win.transfer_file',
382 workplace = gmSurgery.gmCurrentPractice().active_workplace,
383 bias = 'workplace',
384 default = '~/.wine/drive_c/Ifapwin/ifap2gnumed.csv'
385 ))
386
387 try:
388 f = open(transfer_file, 'w+b').close()
389 except IOError:
390 _log.exception('Cannot create IFAP <-> GNUmed transfer file [%s]', transfer_file)
391 gmDispatcher.send('statustext', msg = _('Cannot create IFAP <-> GNUmed transfer file [%s].') % transfer_file)
392 return False
393
394 wx.BeginBusyCursor()
395 gmShellAPI.run_command_in_shell(command = ifap_cmd, blocking = import_drugs)
396 wx.EndBusyCursor()
397
398 if import_drugs:
399
400
401 try:
402 csv_file = open(transfer_file, 'rb')
403 except:
404 _log.exception('cannot access [%s]', fname)
405 csv_file = None
406
407 if csv_file is not None:
408 import csv
409 csv_lines = csv.DictReader (
410 csv_file,
411 fieldnames = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split(),
412 delimiter = ';'
413 )
414 pat = gmPerson.gmCurrentPatient()
415 emr = pat.get_emr()
416
417 epi = emr.add_episode(episode_name = _('Current medication'))
418 for line in csv_lines:
419 narr = u'%sx %s %s %s (\u2258 %s %s) von %s (%s)' % (
420 line['Packungszahl'].strip(),
421 line['Handelsname'].strip(),
422 line['Form'].strip(),
423 line[u'Packungsgr\xf6\xdfe'].strip(),
424 line['Abpackungsmenge'].strip(),
425 line['Einheit'].strip(),
426 line['Hersteller'].strip(),
427 line['PZN'].strip()
428 )
429 emr.add_clin_narrative(note = narr, soap_cat = 's', episode = epi)
430 csv_file.close()
431
432 return True
433
434
435
436
438
440
441 query = u"""
442 SELECT DISTINCT ON (sched)
443 schedule as sched,
444 schedule
445 FROM clin.substance_intake
446 WHERE schedule %(fragment_condition)s
447 ORDER BY sched
448 LIMIT 50"""
449
450 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
451 mp.setThresholds(1, 2, 4)
452 mp.word_separators = '[ \t=+&:@]+'
453 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
454 self.SetToolTipString(_('The schedule for taking this substance.'))
455 self.matcher = mp
456 self.selection_only = False
457
459
461
462 query = u"""
463 (
464 SELECT DISTINCT ON (preparation)
465 preparation as prep, preparation
466 FROM ref.branded_drug
467 WHERE preparation %(fragment_condition)s
468 ) UNION (
469 SELECT DISTINCT ON (preparation)
470 preparation as prep, preparation
471 FROM clin.substance_intake
472 WHERE preparation %(fragment_condition)s
473 )
474 ORDER BY prep
475 limit 30"""
476
477 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
478 mp.setThresholds(1, 2, 4)
479 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
480 self.SetToolTipString(_('The preparation (form) of the substance the patient is taking.'))
481 self.matcher = mp
482 self.selection_only = False
483
485
487
488 query = u"""
489 (
490 SELECT
491 pk::text,
492 description as subst
493 --(description || coalesce(' [' || atc_code || ']', '')) as subst
494 FROM clin.consumed_substance
495 WHERE description %(fragment_condition)s
496
497 ) UNION (
498
499 SELECT
500 description,
501 description as subst
502 --NULL,
503 --(description || coalesce(' [' || atc_code || ']', '')) as subst
504 FROM ref.substance_in_brand
505 WHERE description %(fragment_condition)s
506
507 ) UNION (
508
509 SELECT
510 term,
511 term as subst
512 --NULL,
513 --(term || ' [' || atc || ']') as subst
514 FROM ref.v_atc
515 WHERE
516 is_group_code IS FALSE
517 AND
518 term %(fragment_condition)s
519 )
520 ORDER BY subst
521 LIMIT 50"""
522
523 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
524 mp.setThresholds(1, 2, 4)
525 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
526 self.SetToolTipString(_('The INN / substance the patient is taking.'))
527 self.matcher = mp
528 self.selection_only = False
529
530 - def GetData(self, can_create=False, as_instance=False):
531
532 if self.data is not None:
533 try:
534 int(self.data)
535 except ValueError:
536 self.data = None
537
538 return super(cSubstancePhraseWheel, self).GetData(can_create = can_create, as_instance = as_instance)
539
541
543
544 query = u"""
545 SELECT
546 pk,
547 (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', ''))
548 AS brand
549 FROM ref.branded_drug
550 WHERE description %(fragment_condition)s
551 ORDER BY brand
552 LIMIT 50"""
553
554 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
555 mp.setThresholds(2, 3, 4)
556 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
557 self.SetToolTipString(_('The brand name of the drug the patient is taking.'))
558 self.matcher = mp
559 self.selection_only = False
560
561
562 from Gnumed.wxGladeWidgets import wxgCurrentMedicationEAPnl
563
564 -class cCurrentMedicationEAPnl(wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl, gmEditArea.cGenericEditAreaMixin):
565
582
587
589 emr = gmPerson.gmCurrentPatient().get_emr()
590
591 state = emr.allergy_state
592 if state['last_confirmed'] is None:
593 confirmed = _('never')
594 else:
595 confirmed = state['last_confirmed'].strftime('%Y %B %d').decode(gmI18N.get_encoding())
596 msg = _(u'%s, last confirmed %s\n') % (state.state_string, confirmed)
597 msg += gmTools.coalesce(state['comment'], u'', _('Comment (%s): %%s\n') % state['modified_by'])
598 msg += u'\n'
599
600 for allergy in emr.get_allergies():
601 msg += u'%s (%s, %s): %s\n' % (
602 allergy['descriptor'],
603 allergy['l10n_type'],
604 gmTools.bool2subst(allergy['definite'], _('definite'), _('suspected'), u'?'),
605 gmTools.coalesce(allergy['reaction'], _('reaction not recorded'))
606 )
607
608 self._LBL_allergies.SetLabel(msg)
609
611
612 if self._PRW_brand.GetData() is None:
613 self._TCTRL_brand_ingredients.SetValue(u'')
614 if self.data is None:
615 return
616 if self.data['pk_brand'] is None:
617 return
618 self._PRW_brand.SetText(self.data['brand'], self.data['pk_brand'])
619
620 brand = gmMedication.cBrandedDrug(aPK_obj = self._PRW_brand.GetData())
621
622 if self.data is None:
623 self._PRW_preparation.SetText(brand['preparation'], None)
624 else:
625 self._PRW_preparation.SetText (
626 gmTools.coalesce(self.data['preparation'], brand['preparation']),
627 self.data['preparation']
628 )
629
630 comps = brand.components
631
632 if comps is None:
633 return
634
635 if len(comps) == 0:
636 return
637
638 comps = u' / '.join([ u'%s%s' % (c['description'], gmTools.coalesce(c['atc_code'], u'', u' (%s)')) for c in comps ])
639 self._TCTRL_brand_ingredients.SetValue(comps)
640
641
642
711
713
714 pk_brand = self._PRW_brand.GetData()
715 brand = None
716
717 if pk_brand is None:
718 prep = self._PRW_preparation.GetValue()
719 if self._PRW_substance.GetData() is None:
720 subst = self._PRW_substance.GetValue().strip()
721 else:
722
723 subst = gmMedication.get_substance_by_pk(pk = self._PRW_substance.GetData())['description']
724 substances = [
725 [subst['description'], self._PRW_strength.GetValue()]
726 ]
727 else:
728 brand = gmMedication.cBrandedDrug(aPK_obj = pk_brand)
729 prep = brand['preparation']
730 comps = brand.components
731 if len(comps) == 1:
732 substances = [
733 [comps[0], self._PRW_strength.GetValue()]
734 ]
735 else:
736
737 print "missing"
738
739 emr = gmPerson.gmCurrentPatient().get_emr()
740 epi = self._PRW_episode.GetData(can_create = True)
741
742
743 last_intake = None
744 for subst, strength in substances:
745 intake = emr.add_substance_intake (
746 substance = subst,
747 episode = epi,
748 preparation = prep
749 )
750 intake['strength'] = strength
751 intake['started'] = self._DP_started.GetValue(as_pydt = True, invalid_as_none = True)
752 intake['discontinued'] = self._DP_discontinued.GetValue(as_pydt = True, invalid_as_none = True)
753 if intake['discontinued'] is None:
754 intake['discontinue_reason'] = None
755 else:
756 intake['discontinue_reason'] = self._PRW_discontinue_reason().GetValue().strip()
757 intake['schedule'] = self._PRW_schedule.GetValue()
758 intake['aim'] = self._PRW_aim.GetValue()
759 intake['notes'] = self._PRW_notes.GetValue()
760 intake['is_long_term'] = self._CHBOX_long_term.IsChecked()
761 intake['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
762 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
763 intake['duration'] = None
764 else:
765 intake['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
766 intake['pk_brand'] = pk_brand
767 intake.save()
768 last_intake = intake
769
770 self.data = last_intake
771
772 if self._CHBOX_is_allergy.IsChecked():
773 if brand is None:
774 allg = self.data.turn_into_allergy(encounter_id = emr.active_encounter['pk_encounter'])
775 else:
776 allg = brand.turn_into_allergy(encounter_id = emr.active_encounter['pk_encounter'])
777
778 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent = self, id = -1)
779 dlg.ShowModal()
780
781 return True
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
867
869 self._PRW_brand.SetText(u'', None)
870 self._TCTRL_brand_ingredients.SetValue(u'')
871
872 self._PRW_substance.SetText(u'', None)
873 self._PRW_substance.Enable(True)
874
875 self._PRW_strength.SetText(u'', None)
876 self._PRW_strength.Enable(True)
877
878 self._PRW_preparation.SetText(u'', None)
879 self._PRW_preparation.Enable(True)
880
881 self._PRW_schedule.SetText(u'', None)
882 self._PRW_duration.SetText(u'', None)
883 self._PRW_aim.SetText(u'', None)
884 self._PRW_notes.SetText(u'', None)
885 self._PRW_episode.SetText(u'', None)
886
887 self._CHBOX_long_term.SetValue(False)
888 self._CHBOX_approved.SetValue(True)
889
890 self._DP_started.SetValue(gmDateTime.pydt_now_here())
891 self._DP_discontinued.SetValue(None)
892 self._PRW_discontinue_reason.SetValue(u'')
893
894
895 self.__refresh_allergies()
896
897 self._PRW_brand.SetFocus()
898
900
901 self._PRW_substance.SetText(self.data['substance'], self.data['pk_substance'])
902 self._PRW_strength.SetText(gmTools.coalesce(self.data['strength'], u''), self.data['strength'])
903 self._PRW_preparation.SetText(self.data['preparation'], self.data['preparation'])
904
905 if self.data['is_long_term']:
906 self._CHBOX_long_term.SetValue(True)
907 self._PRW_duration.Enable(False)
908 self._PRW_duration.SetText(gmTools.u_infinity, None)
909 self._BTN_discontinued_as_planned.Enable(False)
910 else:
911 self._CHBOX_long_term.SetValue(False)
912 self._PRW_duration.Enable(True)
913 self._BTN_discontinued_as_planned.Enable(True)
914 if self.data['duration'] is None:
915 self._PRW_duration.SetText(u'', None)
916 else:
917 self._PRW_duration.SetText(gmDateTime.format_interval(self.data['duration'], gmDateTime.acc_days), self.data['duration'])
918 self._PRW_aim.SetText(gmTools.coalesce(self.data['aim'], u''), self.data['aim'])
919 self._PRW_notes.SetText(gmTools.coalesce(self.data['notes'], u''), self.data['notes'])
920 self._PRW_episode.SetData(self.data['pk_episode'])
921 self._PRW_schedule.SetText(gmTools.coalesce(self.data['schedule'], u''), self.data['schedule'])
922
923 self._CHBOX_approved.SetValue(self.data['intake_is_approved_of'])
924
925 self._DP_started.SetValue(self.data['started'])
926 self._DP_discontinued.SetValue(self.data['discontinued'])
927 self._PRW_discontinue_reason.SetValue(gmTools.coalesce(self.data['discontinue_reason'], u''))
928
929 self.__refresh_brand_and_components()
930 self.__refresh_allergies()
931
932 self._PRW_substance.SetFocus()
933
935 self._refresh_as_new()
936
937
938
940 if self._PRW_brand.GetData() is None:
941 self._PRW_brand.SetText(u'', None)
942 self._LBL_substance.Enable(True)
943 self._PRW_substance.Enable(True)
944 self._BTN_database_substance.Enable(True)
945 self._LBL_strength.Enable(True)
946 self._PRW_strength.Enable(True)
947 self._LBL_preparation.Enable(True)
948 self._PRW_preparation.Enable(True)
949 self._PRW_preparation.SetText(u'', None)
950 self._TCTRL_brand_ingredients.SetValue(u'')
951 else:
952 brand = gmMedication.cBrandedDrug(aPK_obj = self._PRW_brand.GetData())
953 comps = brand.components
954
955 self._LBL_substance.Enable(False)
956 self._PRW_substance.Enable(False)
957 self._BTN_database_substance.Enable(False)
958 if len(comps) == 1:
959 self._LBL_strength.Enable(True)
960 self._PRW_strength.Enable(True)
961 else:
962 self._LBL_strength.Enable(False)
963 self._PRW_strength.Enable(False)
964 self._LBL_preparation.Enable(False)
965 self._PRW_preparation.Enable(False)
966 self._PRW_preparation.SetText(brand['preparation'], None)
967 self._TCTRL_brand_ingredients.SetValue(u' / '.join ([
968 u'%s%s' % (
969 c['description'],
970 gmTools.coalesce(c['atc_code'], u'', u' (%s)')
971 ) for c in comps
972 ]))
973
975 if self._PRW_substance.GetValue().strip() == u'':
976 self._PRW_brand.Enable(True)
977 self._BTN_database_brand.Enable(True)
978
979 self._LBL_preparation.Enable(False)
980 self._PRW_preparation.Enable(False)
981 else:
982 self._PRW_brand.SetText(u'', None)
983 self._PRW_brand.Enable(False)
984 self._BTN_database_brand.Enable(False)
985 self._TCTRL_brand_ingredients.SetValue(u'')
986
987 self._LBL_strength.Enable(True)
988 self._PRW_strength.Enable(True)
989 self._LBL_preparation.Enable(True)
990 self._PRW_preparation.Enable(True)
991 self._PRW_preparation.SetText(u'', None)
992
994 if self._DP_discontinued.GetValue() is None:
995 self._PRW_discontinue_reason.Enable(False)
996 self._CHBOX_is_allergy.Enable(False)
997
998 else:
999 self._PRW_discontinue_reason.Enable(True)
1000 self._CHBOX_is_allergy.Enable(True)
1001
1002
1021
1043
1072
1074 if self._CHBOX_long_term.IsChecked() is True:
1075 self._PRW_duration.Enable(False)
1076 self._BTN_discontinued_as_planned.Enable(False)
1077 self._PRW_discontinue_reason.Enable(False)
1078 self._CHBOX_is_allergy.Enable(False)
1079 else:
1080 self._PRW_duration.Enable(True)
1081 self._BTN_discontinued_as_planned.Enable(True)
1082 self._PRW_discontinue_reason.Enable(True)
1083 self._CHBOX_is_allergy.Enable(True)
1084
1085 self.__refresh_allergies()
1086
1088 if self._CHBOX_is_allergy.IsChecked() is True:
1089 val = self._PRW_discontinue_reason.GetValue().strip()
1090 if not val.startswith(_('not tolerated:')):
1091 self._PRW_discontinue_reason.SetValue(u'%s %s' % (_('not tolerated:'), val))
1092
1093 self.__refresh_allergies()
1094
1096
1097 subst = gmMedication.cSubstanceIntakeEntry(aPK_obj = substance)
1098 msg = _(
1099 '\n'
1100 '[%s]\n'
1101 '\n'
1102 'It may be prudent to edit (before deletion) the details\n'
1103 'of this substance intake entry so as to leave behind\n'
1104 'some indication of why it was deleted.\n'
1105 ) % subst.format()
1106
1107 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
1108 parent,
1109 -1,
1110 caption = _('Deleting medication / substance intake'),
1111 question = msg,
1112 button_defs = [
1113 {'label': _('&Edit'), 'tooltip': _('Allow editing of substance intake entry before deletion.'), 'default': True},
1114 {'label': _('&Delete'), 'tooltip': _('Delete immediately without editing first.')},
1115 {'label': _('&Cancel'), 'tooltip': _('Abort. Do not delete or edit substance intake entry.')}
1116 ]
1117 )
1118
1119 edit_first = dlg.ShowModal()
1120 dlg.Destroy()
1121
1122 if edit_first == wx.ID_CANCEL:
1123 return
1124
1125 if edit_first == wx.ID_YES:
1126 edit_intake_of_substance(parent = parent, substance = subst)
1127 delete_it = gmGuiHelpers.gm_show_question (
1128 aMessage = _('Now delete substance intake entry ?'),
1129 aTitle = _('Deleting medication / substance intake')
1130 )
1131 else:
1132 delete_it = True
1133
1134 if not delete_it:
1135 return
1136
1137 gmMedication.delete_substance_intake(substance = substance)
1138
1140 ea = cCurrentMedicationEAPnl(parent = parent, id = -1, substance = substance)
1141 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = (substance is not None))
1142 dlg.SetTitle(gmTools.coalesce(substance, _('Adding substance intake'), _('Editing substance intake')))
1143 if dlg.ShowModal() == wx.ID_OK:
1144 dlg.Destroy()
1145 return True
1146 dlg.Destroy()
1147 return False
1148
1149
1150
1178
1180
1181 if parent is None:
1182 parent = wx.GetApp().GetTopWindow()
1183
1184
1185 dbcfg = gmCfg.cCfgSQL()
1186 option = u'form_templates.medication_list'
1187
1188 template = dbcfg.get2 (
1189 option = option,
1190 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1191 bias = 'user'
1192 )
1193
1194 if template is None:
1195 template = configure_medication_list_template(parent = parent)
1196 if template is None:
1197 gmGuiHelpers.gm_show_error (
1198 aMessage = _('There is no medication list template configured.'),
1199 aTitle = _('Printing medication list')
1200 )
1201 return False
1202 else:
1203 try:
1204 name, ver = template.split(u' - ')
1205 except:
1206 _log.exception('problem splitting medication list template name [%s]', template)
1207 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading medication list template.'), beep = True)
1208 return False
1209 template = gmForms.get_form_template(name_long = name, external_version = ver)
1210 if template is None:
1211 gmGuiHelpers.gm_show_error (
1212 aMessage = _('Cannot load medication list template [%s - %s]') % (name, ver),
1213 aTitle = _('Printing medication list')
1214 )
1215 return False
1216
1217
1218 try:
1219 meds_list = template.instantiate()
1220 except KeyError:
1221 _log.exception('cannot instantiate medication list template [%s]', template)
1222 gmGuiHelpers.gm_show_error (
1223 aMessage = _('Invalid medication list template [%s - %s (%s)]') % (name, ver, template['engine']),
1224 aTitle = _('Printing medication list')
1225 )
1226 return False
1227
1228 ph = gmMacro.gmPlaceholderHandler()
1229
1230 meds_list.substitute_placeholders(data_source = ph)
1231 pdf_name = meds_list.generate_output(cleanup = cleanup)
1232 if cleanup:
1233 meds_list.cleanup()
1234 if pdf_name is None:
1235 gmGuiHelpers.gm_show_error (
1236 aMessage = _('Error generating the medication list.'),
1237 aTitle = _('Printing medication list')
1238 )
1239 return False
1240
1241
1242 printed = gmPrinting.print_file_by_shellscript(filename = pdf_name, jobtype = 'medication_list')
1243 if not printed:
1244 gmGuiHelpers.gm_show_error (
1245 aMessage = _('Error printing the medication list.'),
1246 aTitle = _('Printing medication list')
1247 )
1248 return False
1249
1250 pat = gmPerson.gmCurrentPatient()
1251 emr = pat.get_emr()
1252 epi = emr.add_episode(episode_name = 'administration', is_open = False)
1253 emr.add_clin_narrative (
1254 soap_cat = None,
1255 note = _('medication list printed from template [%s - %s]') % (template['name_long'], template['external_version']),
1256 episode = epi
1257 )
1258
1259 return True
1260
1262 """A grid class for displaying current substance intake.
1263
1264 - does NOT listen to the currently active patient
1265 - thereby it can display any patient at any time
1266 """
1268
1269 wx.grid.Grid.__init__(self, *args, **kwargs)
1270
1271 self.__patient = None
1272 self.__row_data = {}
1273 self.__prev_row = None
1274 self.__prev_tooltip_row = None
1275 self.__prev_cell_0 = None
1276 self.__grouping_mode = u'episode'
1277 self.__filter_show_unapproved = False
1278 self.__filter_show_inactive = False
1279
1280 self.__grouping2col_labels = {
1281 u'episode': [
1282 _('Episode'),
1283 _('Substance'),
1284 _('Dose'),
1285 _('Schedule'),
1286 _('Started'),
1287 _('Duration'),
1288 _('Brand')
1289 ],
1290 u'brand': [
1291 _('Brand'),
1292 _('Schedule'),
1293 _('Substance'),
1294 _('Dose'),
1295 _('Started'),
1296 _('Duration'),
1297 _('Episode')
1298 ]
1299 }
1300
1301 self.__grouping2order_by_clauses = {
1302 u'episode': u'pk_health_issue nulls first, episode, substance, started',
1303 u'brand': u'brand nulls last, substance, started'
1304 }
1305
1306 self.__init_ui()
1307 self.__register_events()
1308
1309
1310
1312
1313 sel_block_top_left = self.GetSelectionBlockTopLeft()
1314 sel_block_bottom_right = self.GetSelectionBlockBottomRight()
1315 sel_cols = self.GetSelectedCols()
1316 sel_rows = self.GetSelectedRows()
1317
1318 selected_cells = []
1319
1320
1321 selected_cells += self.GetSelectedCells()
1322
1323
1324 selected_cells += list (
1325 (row, col)
1326 for row in sel_rows
1327 for col in xrange(self.GetNumberCols())
1328 )
1329
1330
1331 selected_cells += list (
1332 (row, col)
1333 for row in xrange(self.GetNumberRows())
1334 for col in sel_cols
1335 )
1336
1337
1338 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()):
1339 selected_cells += [
1340 (row, col)
1341 for row in xrange(top_left[0], bottom_right[0] + 1)
1342 for col in xrange(top_left[1], bottom_right[1] + 1)
1343 ]
1344
1345 return set(selected_cells)
1346
1348 rows = {}
1349
1350 for row, col in self.get_selected_cells():
1351 rows[row] = True
1352
1353 return rows.keys()
1354
1357
1359
1360 self.empty_grid()
1361
1362 if self.__patient is None:
1363 return
1364
1365 emr = self.__patient.get_emr()
1366 meds = emr.get_current_substance_intake (
1367 order_by = self.__grouping2order_by_clauses[self.__grouping_mode],
1368 include_unapproved = self.__filter_show_unapproved,
1369 include_inactive = self.__filter_show_inactive
1370 )
1371 if not meds:
1372 return
1373
1374 self.BeginBatch()
1375
1376
1377 labels = self.__grouping2col_labels[self.__grouping_mode]
1378 if self.__filter_show_unapproved:
1379 self.AppendCols(numCols = len(labels) + 1)
1380 else:
1381 self.AppendCols(numCols = len(labels))
1382 for col_idx in range(len(labels)):
1383 self.SetColLabelValue(col_idx, labels[col_idx])
1384 if self.__filter_show_unapproved:
1385 self.SetColLabelValue(len(labels), u'OK?')
1386 self.SetColSize(len(labels), 40)
1387
1388 self.AppendRows(numRows = len(meds))
1389
1390
1391 for row_idx in range(len(meds)):
1392 med = meds[row_idx]
1393 self.__row_data[row_idx] = med
1394
1395 if med['is_currently_active'] is True:
1396 atcs = []
1397 if med['atc_substance'] is not None:
1398 atcs.append(med['atc_substance'])
1399
1400
1401
1402 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (med['substance'],))
1403 if allg not in [None, False]:
1404 attr = self.GetOrCreateCellAttr(row_idx, 0)
1405 if allg['type'] == u'allergy':
1406 attr.SetTextColour('red')
1407 else:
1408 attr.SetTextColour('yellow')
1409 self.SetRowAttr(row_idx, attr)
1410 else:
1411 attr = self.GetOrCreateCellAttr(row_idx, 0)
1412 attr.SetTextColour('grey')
1413 self.SetRowAttr(row_idx, attr)
1414
1415 if self.__grouping_mode == u'episode':
1416 if med['pk_episode'] is None:
1417 self.__prev_cell_0 = None
1418 self.SetCellValue(row_idx, 0, gmTools.u_diameter)
1419 else:
1420 if self.__prev_cell_0 != med['episode']:
1421 self.__prev_cell_0 = med['episode']
1422 self.SetCellValue(row_idx, 0, gmTools.coalesce(med['episode'], u''))
1423
1424 self.SetCellValue(row_idx, 1, med['substance'])
1425 self.SetCellValue(row_idx, 2, gmTools.coalesce(med['strength'], u''))
1426 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], u''))
1427 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
1428
1429 if med['is_long_term']:
1430 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
1431 else:
1432 if med['duration'] is None:
1433 self.SetCellValue(row_idx, 5, u'')
1434 else:
1435 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
1436
1437 if med['pk_brand'] is None:
1438 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['brand'], u''))
1439 else:
1440 if med['fake_brand']:
1441 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['brand'], u'', _('%s (fake)')))
1442 else:
1443 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['brand'], u''))
1444
1445 elif self.__grouping_mode == u'brand':
1446
1447 if med['pk_brand'] is None:
1448 self.__prev_cell_0 = None
1449 self.SetCellValue(row_idx, 0, gmTools.u_diameter)
1450 else:
1451 if self.__prev_cell_0 != med['brand']:
1452 self.__prev_cell_0 = med['brand']
1453 if med['fake_brand']:
1454 self.SetCellValue(row_idx, 0, gmTools.coalesce(med['brand'], u'', _('%s (fake)')))
1455 else:
1456 self.SetCellValue(row_idx, 0, gmTools.coalesce(med['brand'], u''))
1457
1458 self.SetCellValue(row_idx, 1, gmTools.coalesce(med['schedule'], u''))
1459 self.SetCellValue(row_idx, 2, med['substance'])
1460 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['strength'], u''))
1461 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
1462
1463 if med['is_long_term']:
1464 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
1465 else:
1466 if med['duration'] is None:
1467 self.SetCellValue(row_idx, 5, u'')
1468 else:
1469 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
1470
1471 if med['pk_episode'] is None:
1472 self.SetCellValue(row_idx, 6, u'')
1473 else:
1474 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['episode'], u''))
1475
1476 else:
1477 raise ValueError('unknown grouping mode [%s]' % self.__grouping_mode)
1478
1479 if self.__filter_show_unapproved:
1480 self.SetCellValue (
1481 row_idx,
1482 len(labels),
1483 gmTools.bool2subst(med['intake_is_approved_of'], gmTools.u_checkmark_thin, u'', u'?')
1484 )
1485
1486
1487
1488 self.EndBatch()
1489
1491 self.BeginBatch()
1492 self.ClearGrid()
1493
1494
1495 if self.GetNumberRows() > 0:
1496 self.DeleteRows(pos = 0, numRows = self.GetNumberRows())
1497 if self.GetNumberCols() > 0:
1498 self.DeleteCols(pos = 0, numCols = self.GetNumberCols())
1499 self.EndBatch()
1500 self.__row_data = {}
1501 self.__prev_cell_0 = None
1502
1504
1505 if len(self.__row_data) == 0:
1506 return
1507
1508 sel_rows = self.get_selected_rows()
1509 if len(sel_rows) != 1:
1510 return
1511
1512 drug_db = get_drug_database()
1513 if drug_db is None:
1514 return
1515
1516 drug_db.show_info_on_substance(substance = self.get_selected_data()[0])
1517
1533
1546
1560
1563
1577
1579
1580 rows = self.get_selected_rows()
1581
1582 if len(rows) == 0:
1583 return
1584
1585 if len(rows) > 1:
1586 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete more than one substance at once.'), beep = True)
1587 return
1588
1589 subst = self.get_selected_data()[0]
1590 delete_substance_intake(parent = self, substance = subst['pk_substance_intake'])
1591
1613
1618
1727
1728
1729
1731 self.CreateGrid(0, 1)
1732 self.EnableEditing(0)
1733 self.EnableDragGridSize(1)
1734 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows)
1735
1736 self.SetColLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTER)
1737
1738 self.SetRowLabelSize(0)
1739 self.SetRowLabelAlignment(horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
1740
1741
1742
1744 return self.__patient
1745
1749
1750 patient = property(_get_patient, _set_patient)
1751
1753 return self.__grouping_mode
1754
1758
1759 grouping_mode = property(_get_grouping_mode, _set_grouping_mode)
1760
1762 return self.__filter_show_unapproved
1763
1767
1768 filter_show_unapproved = property(_get_filter_show_unapproved, _set_filter_show_unapproved)
1769
1771 return self.__filter_show_inactive
1772
1776
1777 filter_show_inactive = property(_get_filter_show_inactive, _set_filter_show_inactive)
1778
1779
1780
1782
1783 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells)
1784
1785
1786
1787
1788 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
1789
1791 """Calculate where the mouse is and set the tooltip dynamically."""
1792
1793
1794
1795 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809 row, col = self.XYToCell(x, y)
1810
1811 if row == self.__prev_tooltip_row:
1812 return
1813
1814 self.__prev_tooltip_row = row
1815
1816 try:
1817 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row))
1818 except KeyError:
1819 pass
1820
1825
1826 from Gnumed.wxGladeWidgets import wxgCurrentSubstancesPnl
1827
1828 -class cCurrentSubstancesPnl(wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl, gmRegetMixin.cRegetOnPaintMixin):
1829
1830 """Panel holding a grid with current substances. Used as notebook page."""
1831
1838
1839
1840
1849
1850
1851
1853 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
1854 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._schedule_data_reget)
1855 gmDispatcher.connect(signal = u'substance_intake_mod_db', receiver = self._schedule_data_reget)
1856
1857
1858
1860 wx.CallAfter(self.__on_pre_patient_selection)
1861
1863 self._grid_substances.patient = None
1864
1867
1870
1873
1876
1879
1882
1885
1888
1891
1894
1897
1900
1903
1904
1905
1906 if __name__ == '__main__':
1907
1908 if len(sys.argv) < 2:
1909 sys.exit()
1910
1911 if sys.argv[1] != 'test':
1912 sys.exit()
1913
1914 from Gnumed.pycommon import gmI18N
1915
1916 gmI18N.activate_locale()
1917 gmI18N.install_domain(domain = 'gnumed')
1918
1919
1920 app = wx.PyWidgetTester(size = (600, 600))
1921 app.SetWidget(cATCPhraseWheel, -1)
1922 app.MainLoop()
1923
1924
1925