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
27
28 if parent is None:
29 parent = wx.GetApp().GetTopWindow()
30
31 def refresh(lctrl):
32 atcs = gmATC.get_reference_atcs()
33
34 items = [ [
35 a['atc'],
36 a['term'],
37 u'%s' % gmTools.coalesce(a['ddd'], u''),
38 gmTools.coalesce(a['unit'], u''),
39 gmTools.coalesce(a['administrative_route'], u''),
40 gmTools.coalesce(a['comment'], u''),
41 a['version'],
42 a['lang']
43 ] for a in atcs ]
44 lctrl.set_string_items(items)
45 lctrl.set_data(atcs)
46
47 gmListWidgets.get_choices_from_list (
48 parent = parent,
49 msg = _('\nThe ATC codes as known to GNUmed.\n'),
50 caption = _('Showing ATC codes.'),
51 columns = [ u'ATC', _('Term'), u'DDD', _('Unit'), _(u'Route'), _('Comment'), _('Version'), _('Language') ],
52 single_selection = True,
53 refresh_callback = refresh
54 )
55
56
69
70 def refresh(lctrl):
71 substs = gmMedication.get_substances_in_brands()
72 items = [ [
73 u'%s%s' % (s['brand'], gmTools.coalesce(s['atc_brand'], u'', u' (%s)')),
74 s['substance'],
75 gmTools.coalesce(s['atc_substance'], u''),
76 s['preparation'],
77 gmTools.coalesce(s['external_code_brand'], u'', u'%%s [%s]' % s['external_code_type_brand']),
78 s['pk_substance_in_brand']
79 ] for s in substs ]
80 lctrl.set_string_items(items)
81 lctrl.set_data(substs)
82
83 msg = _('\nThese are the substances in the drug brands known to GNUmed.\n')
84
85 gmListWidgets.get_choices_from_list (
86 parent = parent,
87 msg = msg,
88 caption = _('Showing drug brand components (substances).'),
89 columns = [_('Brand'), _('Substance'), u'ATC', _('Preparation'), _('Code'), u'#'],
90 single_selection = True,
91
92
93 delete_callback = delete,
94 refresh_callback = refresh
95 )
96
105
106 def new():
107 drug_db = get_drug_database(parent = parent)
108
109 if drug_db is None:
110 return False
111
112 drug_db.import_drugs()
113
114 return True
115
116 def refresh(lctrl):
117 drugs = gmMedication.get_branded_drugs()
118 items = [ [
119 d['description'],
120 d['preparation'],
121 gmTools.coalesce(d['atc_code'], u''),
122 gmTools.coalesce(d['external_code'], u'', u'%%s [%s]' % d['external_code_type']),
123 d['pk']
124 ] for d in drugs ]
125 lctrl.set_string_items(items)
126 lctrl.set_data(drugs)
127
128 msg = _('\nThese are the drug brands known to GNUmed.\n')
129
130 gmListWidgets.get_choices_from_list (
131 parent = parent,
132 msg = msg,
133 caption = _('Showing branded drugs.'),
134 columns = [_('Name'), _('Preparation'), _('ATC'), _('Code'), u'#'],
135 single_selection = True,
136 refresh_callback = refresh,
137 new_callback = new,
138
139 delete_callback = delete
140 )
141
143
144 if parent is None:
145 parent = wx.GetApp().GetTopWindow()
146
147 def delete(substance):
148 gmMedication.delete_used_substance(substance = substance['pk'])
149 return True
150
151 def new():
152 drug_db = get_drug_database(parent = parent)
153
154 if drug_db is None:
155 return False
156
157 drug_db.import_drugs()
158
159 return True
160
161 def refresh(lctrl):
162 substs = gmMedication.get_substances_in_use()
163 items = [ [
164 s['description'],
165 gmTools.coalesce(s['atc_code'], u''),
166 s['pk']
167 ] for s in substs ]
168 lctrl.set_string_items(items)
169 lctrl.set_data(substs)
170
171 msg = _('\nThese are the substances currently or previously\nconsumed across all patients.\n')
172
173 gmListWidgets.get_choices_from_list (
174 parent = parent,
175 msg = msg,
176 caption = _('Showing consumed substances.'),
177 columns = [_('Name'), _('ATC'), u'#'],
178 single_selection = True,
179 refresh_callback = refresh,
180 new_callback = new,
181
182 delete_callback = delete
183 )
184
185
186
204
234
244
246
247 dbcfg = gmCfg.cCfgSQL()
248
249 ifap_cmd = dbcfg.get2 (
250 option = 'external.ifap-win.shell_command',
251 workplace = gmSurgery.gmCurrentPractice().active_workplace,
252 bias = 'workplace',
253 default = 'wine "C:\Ifapwin\WIAMDB.EXE"'
254 )
255 found, binary = gmShellAPI.detect_external_binary(ifap_cmd)
256 if not found:
257 gmDispatcher.send('statustext', msg = _('Cannot call IFAP via [%s].') % ifap_cmd)
258 return False
259 ifap_cmd = binary
260
261 if import_drugs:
262 transfer_file = os.path.expanduser(dbcfg.get2 (
263 option = 'external.ifap-win.transfer_file',
264 workplace = gmSurgery.gmCurrentPractice().active_workplace,
265 bias = 'workplace',
266 default = '~/.wine/drive_c/Ifapwin/ifap2gnumed.csv'
267 ))
268
269 try:
270 f = open(transfer_file, 'w+b').close()
271 except IOError:
272 _log.exception('Cannot create IFAP <-> GNUmed transfer file [%s]', transfer_file)
273 gmDispatcher.send('statustext', msg = _('Cannot create IFAP <-> GNUmed transfer file [%s].') % transfer_file)
274 return False
275
276 wx.BeginBusyCursor()
277 gmShellAPI.run_command_in_shell(command = ifap_cmd, blocking = import_drugs)
278 wx.EndBusyCursor()
279
280 if import_drugs:
281
282
283 try:
284 csv_file = open(transfer_file, 'rb')
285 except:
286 _log.exception('cannot access [%s]', fname)
287 csv_file = None
288
289 if csv_file is not None:
290 import csv
291 csv_lines = csv.DictReader (
292 csv_file,
293 fieldnames = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split(),
294 delimiter = ';'
295 )
296 pat = gmPerson.gmCurrentPatient()
297 emr = pat.get_emr()
298
299 epi = emr.add_episode(episode_name = _('Current medication'))
300 for line in csv_lines:
301 narr = u'%sx %s %s %s (\u2258 %s %s) von %s (%s)' % (
302 line['Packungszahl'].strip(),
303 line['Handelsname'].strip(),
304 line['Form'].strip(),
305 line[u'Packungsgr\xf6\xdfe'].strip(),
306 line['Abpackungsmenge'].strip(),
307 line['Einheit'].strip(),
308 line['Hersteller'].strip(),
309 line['PZN'].strip()
310 )
311 emr.add_clin_narrative(note = narr, soap_cat = 's', episode = epi)
312 csv_file.close()
313
314 return True
315
317
318 dlg = wx.FileDialog (
319 parent = None,
320 message = _('Choose an ATC import config file'),
321 defaultDir = os.path.expanduser(os.path.join('~', 'gnumed')),
322 defaultFile = '',
323 wildcard = "%s (*.conf)|*.conf|%s (*)|*" % (_('config files'), _('all files')),
324 style = wx.OPEN | wx.HIDE_READONLY | wx.FILE_MUST_EXIST
325 )
326
327 result = dlg.ShowModal()
328 if result == wx.ID_CANCEL:
329 return
330
331 cfg_file = dlg.GetPath()
332 dlg.Destroy()
333
334 conn = gmAuthWidgets.get_dbowner_connection(procedure = _('importing ATC reference data'))
335 if conn is None:
336 return False
337
338 wx.BeginBusyCursor()
339
340 if gmATC.atc_import(cfg_fname = cfg_file, conn = conn):
341 gmDispatcher.send(signal = 'statustext', msg = _('Successfully imported ATC reference data.'))
342 else:
343 gmDispatcher.send(signal = 'statustext', msg = _('Importing ATC reference data failed.'), beep = True)
344
345 wx.EndBusyCursor()
346 return True
347
348
349
350
352
354
355 query = u"""
356 SELECT schedule as sched, schedule
357 FROM clin.substance_intake
358 where schedule %(fragment_condition)s
359 ORDER BY sched
360 LIMIT 50"""
361
362 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
363 mp.setThresholds(1, 2, 4)
364 mp.word_separators = '[ \t=+&:@]+'
365 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
366 self.SetToolTipString(_('The schedule for taking this substance.'))
367 self.matcher = mp
368 self.selection_only = False
369
371
373
374 query = u"""
375 (
376 SELECT preparation as prep, preparation
377 FROM ref.branded_drug
378 where preparation %(fragment_condition)s
379 ) union (
380 SELECT preparation as prep, preparation
381 FROM clin.substance_intake
382 where preparation %(fragment_condition)s
383 )
384 order by prep
385 limit 30"""
386
387 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
388 mp.setThresholds(1, 2, 4)
389 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
390 self.SetToolTipString(_('The preparation (form) of the substance the patient is taking.'))
391 self.matcher = mp
392 self.selection_only = False
393
395
397
398 query = u"""
399 (
400 SELECT pk, (coalesce(atc_code || ': ', '') || description) as subst
401 FROM clin.consumed_substance
402 WHERE description %(fragment_condition)s
403 ) union (
404 SELECT NULL, (coalesce(atc_code || ': ', '') || description) as subst
405 FROM ref.substance_in_brand
406 WHERE description %(fragment_condition)s
407 ) union (
408 SELECT NULL, (atc || ': ' || term) as subst
409 FROM ref.v_atc
410 WHERE
411 is_group_code IS FALSE
412 AND
413 term %(fragment_condition)s
414 )
415 order by subst
416 limit 50"""
417
418 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
419 mp.setThresholds(1, 2, 4)
420 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
421 self.SetToolTipString(_('The INN / substance the patient is taking.'))
422 self.matcher = mp
423 self.selection_only = False
424
426
428
429 query = u"""
430 SELECT pk, (coalesce(atc_code || ': ', '') || description || ' (' || preparation || ')') as brand
431 FROM ref.branded_drug
432 WHERE description %(fragment_condition)s
433 ORDER BY brand
434 LIMIT 50"""
435
436 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
437 mp.setThresholds(2, 3, 4)
438 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
439 self.SetToolTipString(_('The brand name of the drug the patient is taking.'))
440 self.matcher = mp
441 self.selection_only = False
442
443
444 from Gnumed.wxGladeWidgets import wxgCurrentMedicationEAPnl
445
446 -class cCurrentMedicationEAPnl(wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl, gmEditArea.cGenericEditAreaMixin):
447
464
470
472 emr = gmPerson.gmCurrentPatient().get_emr()
473
474 state = emr.allergy_state
475 if state['last_confirmed'] is None:
476 confirmed = _('never')
477 else:
478 confirmed = state['last_confirmed'].strftime('%Y %B %d').decode(gmI18N.get_encoding())
479 msg = _(u'%s, last confirmed %s\n') % (state.state_string, confirmed)
480 msg += gmTools.coalesce(state['comment'], u'', _('Comment (%s): %%s\n') % state['modified_by'])
481 msg += u'\n'
482
483 for allergy in emr.get_allergies():
484 msg += u'%s (%s, %s): %s\n' % (
485 allergy['descriptor'],
486 allergy['l10n_type'],
487 gmTools.bool2subst(allergy['definite'], _('definite'), _('suspected'), u'?'),
488 gmTools.coalesce(allergy['reaction'], _('reaction not recorded'))
489 )
490
491 self._LBL_allergies.SetLabel(msg)
492
494
495 if self._PRW_brand.GetData() is None:
496 self._TCTRL_brand_ingredients.SetValue(u'')
497 if self.data is None:
498 return
499 if self.data['pk_brand'] is None:
500 return
501 self._PRW_brand.SetText(self.data['brand'], self.data['pk_brand'])
502
503 brand = gmMedication.cBrandedDrug(aPK_obj = self._PRW_brand.GetData())
504
505 if self.data is None:
506 self._PRW_preparation.SetText(brand['preparation'], None)
507 else:
508 self._PRW_preparation.SetText (
509 gmTools.coalesce(self.data['preparation'], brand['preparation']),
510 self.data['preparation']
511 )
512
513 comps = brand.components
514
515 if comps is None:
516 return
517
518 if len(comps) == 0:
519 return
520
521 comps = u' / '.join([ u'%s%s' % (c['description'], gmTools.coalesce(c['atc_code'], u'', u' (%s)')) for c in comps ])
522 self._TCTRL_brand_ingredients.SetValue(comps)
523
524
525
576
662
713
715 self._PRW_substance.SetText(u'', None)
716 self._PRW_strength.SetText(u'', None)
717
718 self._PRW_schedule.SetText(u'', None)
719 self._PRW_duration.SetText(u'', None)
720 self._PRW_aim.SetText(u'', None)
721 self._PRW_notes.SetText(u'', None)
722 self._PRW_episode.SetData(None)
723
724 self._CHBOX_long_term.SetValue(False)
725 self._CHBOX_approved.SetValue(True)
726
727 self._DP_started.SetValue(gmDateTime.pydt_now_here())
728 self._DP_discontinued.SetValue(None)
729 self._PRW_discontinue_reason.SetValue(u'')
730
731 self.__refresh_brand_and_components()
732 self.__refresh_allergies()
733
734 self._PRW_substance.SetFocus()
735
737
738 self._PRW_substance.SetText(self.data['substance'], self.data['pk_substance'])
739 self._PRW_strength.SetText(gmTools.coalesce(self.data['strength'], u''), self.data['strength'])
740
741 if self.data['is_long_term']:
742 self._CHBOX_long_term.SetValue(True)
743 self._PRW_duration.Enable(False)
744 self._PRW_duration.SetText(gmTools.u_infinity, None)
745 self._BTN_discontinued_as_planned.Enable(False)
746 else:
747 self._CHBOX_long_term.SetValue(False)
748 self._PRW_duration.Enable(True)
749 self._BTN_discontinued_as_planned.Enable(True)
750 if self.data['duration'] is None:
751 self._PRW_duration.SetText(u'', None)
752 else:
753 self._PRW_duration.SetText(gmDateTime.format_interval(self.data['duration'], gmDateTime.acc_days), self.data['duration'])
754 self._PRW_aim.SetText(gmTools.coalesce(self.data['aim'], u''), self.data['aim'])
755 self._PRW_notes.SetText(gmTools.coalesce(self.data['notes'], u''), self.data['notes'])
756 self._PRW_episode.SetData(self.data['pk_episode'])
757 self._PRW_schedule.SetText(gmTools.coalesce(self.data['schedule'], u''), self.data['schedule'])
758
759 self._CHBOX_approved.SetValue(self.data['intake_is_approved_of'])
760
761 self._DP_started.SetValue(self.data['started'])
762 self._DP_discontinued.SetValue(self.data['discontinued'])
763 self._PRW_discontinue_reason.SetValue(gmTools.coalesce(self.data['discontinue_reason'], u''))
764
765 self.__refresh_brand_and_components()
766 self.__refresh_allergies()
767
768 self._PRW_substance.SetFocus()
769
771 self._refresh_as_new()
772
773 self._PRW_substance.SetText(u'', None)
774 self._PRW_strength.SetText(u'', None)
775 self._PRW_notes.SetText(u'', None)
776
777 self.__refresh_brand_and_components()
778 self.__refresh_allergies()
779
780 self._PRW_substance.SetFocus()
781
782
783
785 self.__refresh_brand_and_components()
786
788 if self._DP_discontinued.GetValue() is None:
789 self._PRW_discontinue_reason.Enable(False)
790 self._CHBOX_is_allergy.Enable(False)
791 else:
792 self._PRW_discontinue_reason.Enable(True)
793 self._CHBOX_is_allergy.Enable(True)
794
813
835
864
866 if self._CHBOX_long_term.IsChecked() is True:
867 self._PRW_duration.Enable(False)
868 self._BTN_discontinued_as_planned.Enable(False)
869 self._PRW_discontinue_reason.Enable(False)
870 self._CHBOX_is_allergy.Enable(False)
871 else:
872 self._PRW_duration.Enable(True)
873 self._BTN_discontinued_as_planned.Enable(True)
874 self._PRW_discontinue_reason.Enable(True)
875 self._CHBOX_is_allergy.Enable(True)
876
877 self.__refresh_allergies()
878
880 if self._CHBOX_is_allergy.IsChecked() is True:
881 val = self._PRW_discontinue_reason.GetValue().strip()
882 if not val.startswith(_('not tolerated:')):
883 self._PRW_discontinue_reason.SetValue(u'%s %s' % (_('not tolerated:'), val))
884
885 self.__refresh_allergies()
886
888 delete_it = gmGuiHelpers.gm_show_question (
889 aMessage = _(
890 'Do you really want to remove this substance intake ?\n'
891 '\n'
892 'It may be prudent to edit the details first so as to\n'
893 'leave behind some indication of why it was deleted.\n'
894 ),
895 aTitle = _('Deleting medication / substance intake')
896 )
897 if not delete_it:
898 return
899
900 gmMedication.delete_substance_intake(substance = substance)
901
911
912
913
934
936
937 if parent is None:
938 parent = wx.GetApp().GetTopWindow()
939
940
941 dbcfg = gmCfg.cCfgSQL()
942 option = u'form_templates.medication_list'
943
944 template = dbcfg.get2 (
945 option = option,
946 workplace = gmSurgery.gmCurrentPractice().active_workplace,
947 bias = 'user'
948 )
949
950 if template is None:
951 template = configure_medication_list_template(parent = parent)
952 if template is None:
953 gmGuiHelpers.gm_show_error (
954 aMessage = _('There is no medication list template configured.'),
955 aTitle = _('Printing medication list')
956 )
957 return False
958 else:
959 try:
960 name, ver = template.split(u' - ')
961 except:
962 _log.exception('problem splitting medication list template name [%s]', template)
963 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading medication list template.'), beep = True)
964 return False
965 template = gmForms.get_form_template(name_long = name, external_version = ver)
966 if template is None:
967 gmGuiHelpers.gm_show_error (
968 aMessage = _('Cannot load medication list template [%s - %s]') % (name, ver),
969 aTitle = _('Printing medication list')
970 )
971 return False
972
973
974 meds_list = template.instantiate()
975 ph = gmMacro.gmPlaceholderHandler()
976
977 meds_list.substitute_placeholders(data_source = ph)
978 pdf_name = meds_list.generate_output(cleanup = cleanup)
979 if cleanup:
980 meds_list.cleanup()
981 if pdf_name is None:
982 gmGuiHelpers.gm_show_error (
983 aMessage = _('Error generating the medication list.'),
984 aTitle = _('Printing medication list')
985 )
986 return False
987
988
989 printed = gmPrinting.print_file_by_shellscript(filename = pdf_name, jobtype = 'medication_list')
990 if not printed:
991 gmGuiHelpers.gm_show_error (
992 aMessage = _('Error printing the medication list.'),
993 aTitle = _('Printing medication list')
994 )
995 return False
996
997 pat = gmPerson.gmCurrentPatient()
998 emr = pat.get_emr()
999 epi = emr.add_episode(episode_name = 'administration', is_open = False)
1000 emr.add_clin_narrative (
1001 soap_cat = None,
1002 note = _('medication list printed from template [%s - %s]') % (template['name_long'], template['external_version']),
1003 episode = epi
1004 )
1005
1006 return True
1007
1009 """A grid class for displaying current substance intake.
1010
1011 - does NOT listen to the currently active patient
1012 - thereby it can display any patient at any time
1013 """
1015
1016 wx.grid.Grid.__init__(self, *args, **kwargs)
1017
1018 self.__patient = None
1019 self.__row_data = {}
1020 self.__prev_row = None
1021 self.__prev_tooltip_row = None
1022 self.__prev_cell_0 = None
1023 self.__grouping_mode = u'episode'
1024 self.__filter_show_unapproved = False
1025 self.__filter_show_inactive = False
1026
1027 self.__grouping2col_labels = {
1028 u'episode': [
1029 _('Episode'),
1030 _('Substance'),
1031 _('Dose'),
1032 _('Schedule'),
1033 _('Started'),
1034 _('Duration'),
1035 _('Brand')
1036 ],
1037 u'brand': [
1038 _('Brand'),
1039 _('Schedule'),
1040 _('Substance'),
1041 _('Dose'),
1042 _('Started'),
1043 _('Duration'),
1044 _('Episode')
1045 ]
1046 }
1047
1048 self.__grouping2order_by_clauses = {
1049 u'episode': u'pk_health_issue nulls first, episode, substance, started',
1050 u'brand': u'brand nulls last, substance, started'
1051 }
1052
1053 self.__init_ui()
1054 self.__register_events()
1055
1056
1057
1059
1060 sel_block_top_left = self.GetSelectionBlockTopLeft()
1061 sel_block_bottom_right = self.GetSelectionBlockBottomRight()
1062 sel_cols = self.GetSelectedCols()
1063 sel_rows = self.GetSelectedRows()
1064
1065 selected_cells = []
1066
1067
1068 selected_cells += self.GetSelectedCells()
1069
1070
1071 selected_cells += list (
1072 (row, col)
1073 for row in sel_rows
1074 for col in xrange(self.GetNumberCols())
1075 )
1076
1077
1078 selected_cells += list (
1079 (row, col)
1080 for row in xrange(self.GetNumberRows())
1081 for col in sel_cols
1082 )
1083
1084
1085 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()):
1086 selected_cells += [
1087 (row, col)
1088 for row in xrange(top_left[0], bottom_right[0] + 1)
1089 for col in xrange(top_left[1], bottom_right[1] + 1)
1090 ]
1091
1092 return set(selected_cells)
1093
1095 rows = {}
1096
1097 for row, col in self.get_selected_cells():
1098 rows[row] = True
1099
1100 return rows.keys()
1101
1104
1106
1107 self.empty_grid()
1108
1109 if self.__patient is None:
1110 return
1111
1112 emr = self.__patient.get_emr()
1113 meds = emr.get_current_substance_intake (
1114 order_by = self.__grouping2order_by_clauses[self.__grouping_mode],
1115 include_unapproved = self.__filter_show_unapproved,
1116 include_inactive = self.__filter_show_inactive
1117 )
1118 if not meds:
1119 return
1120
1121 self.BeginBatch()
1122
1123
1124 labels = self.__grouping2col_labels[self.__grouping_mode]
1125 if self.__filter_show_unapproved:
1126 self.AppendCols(numCols = len(labels) + 1)
1127 else:
1128 self.AppendCols(numCols = len(labels))
1129 for col_idx in range(len(labels)):
1130 self.SetColLabelValue(col_idx, labels[col_idx])
1131 if self.__filter_show_unapproved:
1132 self.SetColLabelValue(len(labels), u'OK?')
1133 self.SetColSize(len(labels), 40)
1134
1135 self.AppendRows(numRows = len(meds))
1136
1137
1138 for row_idx in range(len(meds)):
1139 med = meds[row_idx]
1140 self.__row_data[row_idx] = med
1141
1142 if med['is_currently_active'] is True:
1143 atcs = []
1144 if med['atc_substance'] is not None:
1145 atcs.append(med['atc_substance'])
1146 if med['atc_brand'] is not None:
1147 atcs.append(med['atc_brand'])
1148 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (med['substance'],), brand = med['brand'])
1149 if allg not in [None, False]:
1150 attr = self.GetOrCreateCellAttr(row_idx, 0)
1151 if allg['type'] == u'allergy':
1152 attr.SetTextColour('red')
1153 else:
1154 attr.SetTextColour('yellow')
1155 self.SetRowAttr(row_idx, attr)
1156 else:
1157 attr = self.GetOrCreateCellAttr(row_idx, 0)
1158 attr.SetTextColour('grey')
1159 self.SetRowAttr(row_idx, attr)
1160
1161 if self.__grouping_mode == u'episode':
1162 if med['pk_episode'] is None:
1163 self.__prev_cell_0 = None
1164 self.SetCellValue(row_idx, 0, gmTools.u_diameter)
1165 else:
1166 if self.__prev_cell_0 != med['episode']:
1167 self.__prev_cell_0 = med['episode']
1168 self.SetCellValue(row_idx, 0, gmTools.coalesce(med['episode'], u''))
1169
1170 self.SetCellValue(row_idx, 1, med['substance'])
1171 self.SetCellValue(row_idx, 2, gmTools.coalesce(med['strength'], u''))
1172 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], u''))
1173 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
1174
1175 if med['is_long_term']:
1176 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
1177 else:
1178 if med['duration'] is None:
1179 self.SetCellValue(row_idx, 5, u'')
1180 else:
1181 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
1182
1183 if med['pk_brand'] is None:
1184 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['brand'], u''))
1185 else:
1186 if med['fake_brand']:
1187 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['brand'], u'', _('%s (fake)')))
1188 else:
1189 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['brand'], u''))
1190
1191 elif self.__grouping_mode == u'brand':
1192
1193 if med['pk_brand'] is None:
1194 self.__prev_cell_0 = None
1195 self.SetCellValue(row_idx, 0, gmTools.u_diameter)
1196 else:
1197 if self.__prev_cell_0 != med['brand']:
1198 self.__prev_cell_0 = med['brand']
1199 if med['fake_brand']:
1200 self.SetCellValue(row_idx, 0, gmTools.coalesce(med['brand'], u'', _('%s (fake)')))
1201 else:
1202 self.SetCellValue(row_idx, 0, gmTools.coalesce(med['brand'], u''))
1203
1204 self.SetCellValue(row_idx, 1, gmTools.coalesce(med['schedule'], u''))
1205 self.SetCellValue(row_idx, 2, med['substance'])
1206 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['strength'], u''))
1207 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
1208
1209 if med['is_long_term']:
1210 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
1211 else:
1212 if med['duration'] is None:
1213 self.SetCellValue(row_idx, 5, u'')
1214 else:
1215 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
1216
1217 if med['pk_episode'] is None:
1218 self.SetCellValue(row_idx, 6, u'')
1219 else:
1220 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['episode'], u''))
1221
1222 else:
1223 raise ValueError('unknown grouping mode [%s]' % self.__grouping_mode)
1224
1225 if self.__filter_show_unapproved:
1226 self.SetCellValue (
1227 row_idx,
1228 len(labels),
1229 gmTools.bool2subst(med['intake_is_approved_of'], gmTools.u_checkmark_thin, u'', u'?')
1230 )
1231
1232
1233
1234 self.EndBatch()
1235
1237 self.BeginBatch()
1238 self.ClearGrid()
1239
1240
1241 if self.GetNumberRows() > 0:
1242 self.DeleteRows(pos = 0, numRows = self.GetNumberRows())
1243 if self.GetNumberCols() > 0:
1244 self.DeleteCols(pos = 0, numCols = self.GetNumberCols())
1245 self.EndBatch()
1246 self.__row_data = {}
1247 self.__prev_cell_0 = None
1248
1250
1251 if len(self.__row_data) == 0:
1252 return
1253
1254 sel_rows = self.get_selected_rows()
1255 if len(sel_rows) != 1:
1256 return
1257
1258 drug_db = get_drug_database()
1259 if drug_db is None:
1260 return
1261
1262 drug_db.show_info_on_substance(substance = self.get_selected_data()[0])
1263
1279
1293
1296
1310
1312
1313 rows = self.get_selected_rows()
1314
1315 if len(rows) == 0:
1316 return
1317
1318 if len(rows) > 1:
1319 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete more than one substance at once.'), beep = True)
1320 return
1321
1322 subst = self.get_selected_data()[0]
1323 delete_substance_intake(parent = self, substance = subst['pk_substance_intake'])
1324
1346
1351
1459
1460
1461
1463 self.CreateGrid(0, 1)
1464 self.EnableEditing(0)
1465 self.EnableDragGridSize(1)
1466 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows)
1467
1468 self.SetColLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTER)
1469
1470 self.SetRowLabelSize(0)
1471 self.SetRowLabelAlignment(horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
1472
1473
1474
1476 return self.__patient
1477
1481
1482 patient = property(_get_patient, _set_patient)
1483
1485 return self.__grouping_mode
1486
1490
1491 grouping_mode = property(_get_grouping_mode, _set_grouping_mode)
1492
1494 return self.__filter_show_unapproved
1495
1499
1500 filter_show_unapproved = property(_get_filter_show_unapproved, _set_filter_show_unapproved)
1501
1503 return self.__filter_show_inactive
1504
1508
1509 filter_show_inactive = property(_get_filter_show_inactive, _set_filter_show_inactive)
1510
1511
1512
1514
1515 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells)
1516
1517
1518
1519
1520 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
1521
1523 """Calculate where the mouse is and set the tooltip dynamically."""
1524
1525
1526
1527 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541 row, col = self.XYToCell(x, y)
1542
1543 if row == self.__prev_tooltip_row:
1544 return
1545
1546 self.__prev_tooltip_row = row
1547
1548 try:
1549 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row))
1550 except KeyError:
1551 pass
1552
1557
1558 from Gnumed.wxGladeWidgets import wxgCurrentSubstancesPnl
1559
1560 -class cCurrentSubstancesPnl(wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl, gmRegetMixin.cRegetOnPaintMixin):
1561
1562 """Panel holding a grid with current substances. Used as notebook page."""
1563
1570
1571
1572
1581
1582
1583
1585 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
1586 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._schedule_data_reget)
1587 gmDispatcher.connect(signal = u'substance_intake_mod_db', receiver = self._schedule_data_reget)
1588
1589
1590
1592 wx.CallAfter(self.__on_pre_patient_selection)
1593
1595 self._grid_substances.patient = None
1596
1599
1602
1605
1608
1611
1614
1617
1620
1623
1626
1629
1632
1633
1634
1635 if __name__ == '__main__':
1636
1637 if len(sys.argv) < 2:
1638 sys.exit()
1639
1640 if sys.argv[1] != 'test':
1641 sys.exit()
1642
1643 from Gnumed.pycommon import gmI18N
1644
1645 gmI18N.activate_locale()
1646 gmI18N.install_domain(domain = 'gnumed')
1647
1648
1649
1650 pass
1651
1652
1653