1 """GNUmed medication/substances handling widgets."""
2
3
4 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
5 __license__ = "GPL v2 or later"
6
7 import logging
8 import sys
9 import os.path
10 import decimal
11
12
13 import wx
14 import wx.grid
15
16
17 if __name__ == '__main__':
18 sys.path.insert(0, '../../')
19 from Gnumed.pycommon import gmDispatcher
20 from Gnumed.pycommon import gmCfg
21 from Gnumed.pycommon import gmTools
22 from Gnumed.pycommon import gmDateTime
23 from Gnumed.pycommon import gmMatchProvider
24 from Gnumed.pycommon import gmI18N
25 from Gnumed.pycommon import gmPrinting
26 from Gnumed.pycommon import gmCfg2
27 from Gnumed.pycommon import gmNetworkTools
28
29 from Gnumed.business import gmPerson
30 from Gnumed.business import gmATC
31 from Gnumed.business import gmSurgery
32 from Gnumed.business import gmMedication
33 from Gnumed.business import gmForms
34 from Gnumed.business import gmStaff
35 from Gnumed.business import gmDocuments
36 from Gnumed.business import gmLOINC
37 from Gnumed.business import gmClinicalRecord
38 from Gnumed.business import gmClinicalCalculator
39
40 from Gnumed.wxpython import gmGuiHelpers
41 from Gnumed.wxpython import gmRegetMixin
42 from Gnumed.wxpython import gmAuthWidgets
43 from Gnumed.wxpython import gmEditArea
44 from Gnumed.wxpython import gmMacro
45 from Gnumed.wxpython import gmCfgWidgets
46 from Gnumed.wxpython import gmListWidgets
47 from Gnumed.wxpython import gmPhraseWheel
48 from Gnumed.wxpython import gmFormWidgets
49 from Gnumed.wxpython import gmAllergyWidgets
50 from Gnumed.wxpython import gmDocumentWidgets
51
52
53 _log = logging.getLogger('gm.ui')
54
55
56
57
75
77 dbcfg = gmCfg.cCfgSQL()
78
79
80 default_db = dbcfg.get2 (
81 option = 'external.drug_data.default_source',
82 workplace = gmSurgery.gmCurrentPractice().active_workplace,
83 bias = 'workplace'
84 )
85
86
87 if default_db is None:
88 gmDispatcher.send('statustext', msg = _('No default drug database configured.'), beep = True)
89 configure_drug_data_source(parent = parent)
90 default_db = dbcfg.get2 (
91 option = 'external.drug_data.default_source',
92 workplace = gmSurgery.gmCurrentPractice().active_workplace,
93 bias = 'workplace'
94 )
95
96 if default_db is None:
97 gmGuiHelpers.gm_show_error (
98 aMessage = _('There is no default drug database configured.'),
99 aTitle = _('Jumping to drug database')
100 )
101 return None
102
103
104
105 try:
106 drug_db = gmMedication.drug_data_source_interfaces[default_db]()
107 except KeyError:
108
109 _log.error('faulty default drug data source configuration: %s', default_db)
110
111 configure_drug_data_source(parent = parent)
112 default_db = dbcfg.get2 (
113 option = 'external.drug_data.default_source',
114 workplace = gmSurgery.gmCurrentPractice().active_workplace,
115 bias = 'workplace'
116 )
117
118 try:
119 drug_db = gmMedication.drug_data_source_interfaces[default_db]()
120 except KeyError:
121 _log.error('still faulty default drug data source configuration: %s', default_db)
122 return None
123
124 pat = gmPerson.gmCurrentPatient()
125 if pat.connected:
126 drug_db.patient = pat
127
128 return drug_db
129
136
137
139
140 dbcfg = gmCfg.cCfgSQL()
141
142 ifap_cmd = dbcfg.get2 (
143 option = 'external.ifap-win.shell_command',
144 workplace = gmSurgery.gmCurrentPractice().active_workplace,
145 bias = 'workplace',
146 default = 'wine "C:\Ifapwin\WIAMDB.EXE"'
147 )
148 found, binary = gmShellAPI.detect_external_binary(ifap_cmd)
149 if not found:
150 gmDispatcher.send('statustext', msg = _('Cannot call IFAP via [%s].') % ifap_cmd)
151 return False
152 ifap_cmd = binary
153
154 if import_drugs:
155 transfer_file = os.path.expanduser(dbcfg.get2 (
156 option = 'external.ifap-win.transfer_file',
157 workplace = gmSurgery.gmCurrentPractice().active_workplace,
158 bias = 'workplace',
159 default = '~/.wine/drive_c/Ifapwin/ifap2gnumed.csv'
160 ))
161
162 try:
163 f = open(transfer_file, 'w+b').close()
164 except IOError:
165 _log.exception('Cannot create IFAP <-> GNUmed transfer file [%s]', transfer_file)
166 gmDispatcher.send('statustext', msg = _('Cannot create IFAP <-> GNUmed transfer file [%s].') % transfer_file)
167 return False
168
169 wx.BeginBusyCursor()
170 gmShellAPI.run_command_in_shell(command = ifap_cmd, blocking = import_drugs)
171 wx.EndBusyCursor()
172
173 if import_drugs:
174
175
176 try:
177 csv_file = open(transfer_file, 'rb')
178 except:
179 _log.exception('cannot access [%s]', fname)
180 csv_file = None
181
182 if csv_file is not None:
183 import csv
184 csv_lines = csv.DictReader (
185 csv_file,
186 fieldnames = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split(),
187 delimiter = ';'
188 )
189 pat = gmPerson.gmCurrentPatient()
190 emr = pat.get_emr()
191
192 epi = emr.add_episode(episode_name = _('Current medication'))
193 for line in csv_lines:
194 narr = u'%sx %s %s %s (\u2258 %s %s) von %s (%s)' % (
195 line['Packungszahl'].strip(),
196 line['Handelsname'].strip(),
197 line['Form'].strip(),
198 line[u'Packungsgr\xf6\xdfe'].strip(),
199 line['Abpackungsmenge'].strip(),
200 line['Einheit'].strip(),
201 line['Hersteller'].strip(),
202 line['PZN'].strip()
203 )
204 emr.add_clin_narrative(note = narr, soap_cat = 's', episode = epi)
205 csv_file.close()
206
207 return True
208
209
210
211
212
214
215 if parent is None:
216 parent = wx.GetApp().GetTopWindow()
217
218 def refresh(lctrl):
219 atcs = gmATC.get_reference_atcs()
220
221 items = [ [
222 a['atc'],
223 a['term'],
224 u'%s' % gmTools.coalesce(a['ddd'], u''),
225 gmTools.coalesce(a['unit'], u''),
226 gmTools.coalesce(a['administrative_route'], u''),
227 gmTools.coalesce(a['comment'], u''),
228 a['version'],
229 a['lang']
230 ] for a in atcs ]
231 lctrl.set_string_items(items)
232 lctrl.set_data(atcs)
233
234 gmListWidgets.get_choices_from_list (
235 parent = parent,
236 msg = _('\nThe ATC codes as known to GNUmed.\n'),
237 caption = _('Showing ATC codes.'),
238 columns = [ u'ATC', _('Term'), u'DDD', _('Unit'), _(u'Route'), _('Comment'), _('Version'), _('Language') ],
239 single_selection = True,
240 refresh_callback = refresh
241 )
242
243
245
246 dlg = wx.FileDialog (
247 parent = None,
248 message = _('Choose an ATC import config file'),
249 defaultDir = os.path.expanduser(os.path.join('~', 'gnumed')),
250 defaultFile = '',
251 wildcard = "%s (*.conf)|*.conf|%s (*)|*" % (_('config files'), _('all files')),
252 style = wx.OPEN | wx.HIDE_READONLY | wx.FILE_MUST_EXIST
253 )
254
255 result = dlg.ShowModal()
256 if result == wx.ID_CANCEL:
257 return
258
259 cfg_file = dlg.GetPath()
260 dlg.Destroy()
261
262 conn = gmAuthWidgets.get_dbowner_connection(procedure = _('importing ATC reference data'))
263 if conn is None:
264 return False
265
266 wx.BeginBusyCursor()
267
268 if gmATC.atc_import(cfg_fname = cfg_file, conn = conn):
269 gmDispatcher.send(signal = 'statustext', msg = _('Successfully imported ATC reference data.'))
270 else:
271 gmDispatcher.send(signal = 'statustext', msg = _('Importing ATC reference data failed.'), beep = True)
272
273 wx.EndBusyCursor()
274 return True
275
276
277
279
281
282 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
283
284 query = u"""
285
286 SELECT DISTINCT ON (label)
287 atc_code,
288 label
289 FROM (
290
291 SELECT
292 code as atc_code,
293 (code || ': ' || term || coalesce(' (' || ddd || unit || ')', ''))
294 AS label
295 FROM ref.atc
296 WHERE
297 term %(fragment_condition)s
298 OR
299 code %(fragment_condition)s
300
301 UNION ALL
302
303 SELECT
304 atc_code,
305 (atc_code || ': ' || description)
306 AS label
307 FROM ref.consumable_substance
308 WHERE
309 description %(fragment_condition)s
310 OR
311 atc_code %(fragment_condition)s
312
313 UNION ALL
314
315 SELECT
316 atc_code,
317 (atc_code || ': ' || description || ' (' || preparation || ')')
318 AS label
319 FROM ref.branded_drug
320 WHERE
321 description %(fragment_condition)s
322 OR
323 atc_code %(fragment_condition)s
324
325 -- it would be nice to be able to include clin.vacc_indication but that's hard to do in SQL
326
327 ) AS candidates
328 WHERE atc_code IS NOT NULL
329 ORDER BY label
330 LIMIT 50"""
331
332 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
333 mp.setThresholds(1, 2, 4)
334
335 self.SetToolTipString(_('Select an ATC (Anatomical-Therapeutic-Chemical) code.'))
336 self.matcher = mp
337 self.selection_only = True
338
339
340
341
343
344 if parent is None:
345 parent = wx.GetApp().GetTopWindow()
346
347 def add_from_db(substance):
348 drug_db = get_drug_database(parent = parent)
349 if drug_db is None:
350 return False
351 drug_db.import_drugs()
352 return True
353
354 def edit(substance=None):
355 return edit_consumable_substance(parent = parent, substance = substance, single_entry = (substance is not None))
356
357 def delete(substance):
358 if substance.is_in_use_by_patients:
359 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete this substance. It is in use.'), beep = True)
360 return False
361
362 return gmMedication.delete_consumable_substance(substance = substance['pk'])
363
364 def refresh(lctrl):
365 substs = gmMedication.get_consumable_substances(order_by = 'description')
366 items = [ [
367 s['description'],
368 s['amount'],
369 s['unit'],
370 gmTools.coalesce(s['atc_code'], u''),
371 s['pk']
372 ] for s in substs ]
373 lctrl.set_string_items(items)
374 lctrl.set_data(substs)
375
376 msg = _('\nThese are the consumable substances registered with GNUmed.\n')
377
378 gmListWidgets.get_choices_from_list (
379 parent = parent,
380 msg = msg,
381 caption = _('Showing consumable substances.'),
382 columns = [_('Substance'), _('Amount'), _('Unit'), 'ATC', u'#'],
383 single_selection = True,
384 new_callback = edit,
385 edit_callback = edit,
386 delete_callback = delete,
387 refresh_callback = refresh,
388 left_extra_button = (_('Import'), _('Import consumable substances from a drug database.'), add_from_db)
389 )
390
391
393
394 if substance is not None:
395 if substance.is_in_use_by_patients:
396 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit this substance. It is in use.'), beep = True)
397 return False
398
399 ea = cConsumableSubstanceEAPnl(parent = parent, id = -1)
400 ea.data = substance
401 ea.mode = gmTools.coalesce(substance, 'new', 'edit')
402 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
403 dlg.SetTitle(gmTools.coalesce(substance, _('Adding new consumable substance'), _('Editing consumable substance')))
404 if dlg.ShowModal() == wx.ID_OK:
405 dlg.Destroy()
406 return True
407 dlg.Destroy()
408 return False
409
410
411 from Gnumed.wxGladeWidgets import wxgConsumableSubstanceEAPnl
412
414
432
433
434
435
436
437
438
439
441
442 validity = True
443
444 if self._TCTRL_substance.GetValue().strip() == u'':
445 validity = False
446 self.display_tctrl_as_valid(tctrl = self._TCTRL_substance, valid = False)
447 self._TCTRL_substance.SetFocus()
448 else:
449 self.display_tctrl_as_valid(tctrl = self._TCTRL_substance, valid = True)
450
451 try:
452 decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.'))
453 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
454 except (TypeError, decimal.InvalidOperation):
455 validity = False
456 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
457 self._TCTRL_amount.SetFocus()
458
459 if self._PRW_unit.GetValue().strip() == u'':
460 validity = False
461 self._PRW_unit.display_as_valid(valid = False)
462 self._TCTRL_substance.SetFocus()
463 else:
464 self._PRW_unit.display_as_valid(valid = True)
465
466 if validity is False:
467 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save consumable substance. Missing essential input.'))
468
469 return validity
470
472 subst = gmMedication.create_consumable_substance (
473 substance = self._TCTRL_substance.GetValue().strip(),
474 atc = self._PRW_atc.GetData(),
475 amount = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.')),
476 unit = gmTools.coalesce(self._PRW_unit.GetData(), self._PRW_unit.GetValue().strip(), function_initial = ('strip', None))
477 )
478 success, data = subst.save()
479 if not success:
480 err, msg = data
481 _log.error(err)
482 _log.error(msg)
483 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save consumable substance. %s') % msg, beep = True)
484 return False
485
486 self.data = subst
487 return True
488
490 self.data['description'] = self._TCTRL_substance.GetValue().strip()
491 self.data['atc_code'] = self._PRW_atc.GetData()
492 self.data['amount'] = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.'))
493 self.data['unit'] = gmTools.coalesce(self._PRW_unit.GetData(), self._PRW_unit.GetValue().strip(), function_initial = ('strip', None))
494 success, data = self.data.save()
495
496 if not success:
497 err, msg = data
498 _log.error(err)
499 _log.error(msg)
500 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save consumable substance. %s') % msg, beep = True)
501 return False
502
503 return True
504
506 self._TCTRL_substance.SetValue(u'')
507 self._TCTRL_amount.SetValue(u'')
508 self._PRW_unit.SetText(u'', None)
509 self._PRW_atc.SetText(u'', None)
510
511 self._TCTRL_substance.SetFocus()
512
520
522 self._refresh_as_new()
523
524
525
526
536
537 def delete(component):
538 if component.is_in_use_by_patients:
539 gmDispatcher.send(signal = 'statustext', msg = _('Cannot remove this component from the drug. It is in use.'), beep = True)
540 return False
541
542 return component.containing_drug.remove_component(substance = component['pk_component'])
543
544 def refresh(lctrl):
545 comps = gmMedication.get_drug_components()
546 items = [ [
547 u'%s%s' % (c['brand'], gmTools.coalesce(c['atc_brand'], u'', u' [%s]')),
548 u'%s%s' % (c['substance'], gmTools.coalesce(c['atc_substance'], u'', u' [%s]')),
549 u'%s %s' % (c['amount'], c['unit']),
550 c['preparation'],
551 gmTools.coalesce(c['external_code_brand'], u'', u'%%s [%s]' % c['external_code_type_brand']),
552 c['pk_component']
553 ] for c in comps ]
554 lctrl.set_string_items(items)
555 lctrl.set_data(comps)
556
557 msg = _('\nThese are the components in the drug brands known to GNUmed.\n')
558
559 gmListWidgets.get_choices_from_list (
560 parent = parent,
561 msg = msg,
562 caption = _('Showing drug brand components.'),
563 columns = [_('Brand'), _('Substance'), _('Strength'), _('Preparation'), _('Code'), u'#'],
564 single_selection = True,
565
566 edit_callback = edit,
567 delete_callback = delete,
568 refresh_callback = refresh
569 )
570
571
583
584
585 from Gnumed.wxGladeWidgets import wxgDrugComponentEAPnl
586
587 -class cDrugComponentEAPnl(wxgDrugComponentEAPnl.wxgDrugComponentEAPnl, gmEditArea.cGenericEditAreaMixin):
588
606
607
608
609
610
611
612
613
615 if self.data is not None:
616 if self.data['is_in_use']:
617 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit drug component. It is in use.'), beep = True)
618 return False
619
620 validity = True
621
622 if self._PRW_substance.GetData() is None:
623 validity = False
624 self._PRW_substance.display_as_valid(False)
625 else:
626 self._PRW_substance.display_as_valid(True)
627
628 val = self._TCTRL_amount.GetValue().strip().replace(',', u'.', 1)
629 try:
630 decimal.Decimal(val)
631 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
632 except:
633 validity = False
634 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
635
636 if self._PRW_unit.GetValue().strip() == u'':
637 validity = False
638 self._PRW_unit.display_as_valid(False)
639 else:
640 self._PRW_unit.display_as_valid(True)
641
642 if validity is False:
643 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save drug component. Invalid or missing essential input.'))
644
645 return validity
646
648
649 data = 1
650 data[''] = 1
651 data[''] = 1
652
653
654
655
656
657
658 return False
659 return True
660
662 self.data['pk_consumable_substance'] = self._PRW_substance.GetData()
663 self.data['amount'] = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', u'.', 1))
664 self.data['unit'] = self._PRW_unit.GetValue().strip()
665 return self.data.save()
666
676
678 self._TCTRL_brand.SetValue(u'%s (%s)' % (self.data['brand'], self.data['preparation']))
679 self._TCTRL_components.SetValue(u' / '.join(self.data.containing_drug['components']))
680 details = []
681 if self.data['atc_brand'] is not None:
682 details.append(u'ATC: %s' % self.data['atc_brand'])
683 if self.data['external_code_brand'] is not None:
684 details.append(u'%s: %s' % (self.data['external_code_type_brand'], self.data['external_code_brand']))
685 self._TCTRL_codes.SetValue(u'; '.join(details))
686
687 self._PRW_substance.SetText(self.data['substance'], self.data['pk_consumable_substance'])
688 self._TCTRL_amount.SetValue(u'%s' % self.data['amount'])
689 self._PRW_unit.SetText(self.data['unit'], self.data['unit'])
690
691 self._PRW_substance.SetFocus()
692
694
695
696
697 self._PRW_substance.SetText(u'', None)
698 self._TCTRL_amount.SetValue(u'')
699 self._PRW_unit.SetText(u'', None)
700
701 self._PRW_substance.SetFocus()
702
703
717
718
720
722
723 query = u"""
724 (
725 SELECT DISTINCT ON (preparation)
726 preparation as prep, preparation
727 FROM ref.branded_drug
728 WHERE preparation %(fragment_condition)s
729 ) UNION (
730 SELECT DISTINCT ON (preparation)
731 preparation as prep, preparation
732 FROM clin.substance_intake
733 WHERE preparation %(fragment_condition)s
734 )
735 ORDER BY prep
736 limit 30"""
737
738 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
739 mp.setThresholds(1, 2, 4)
740 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
741 self.SetToolTipString(_('The preparation (form) of the substance or brand.'))
742 self.matcher = mp
743 self.selection_only = False
744
760
761
762
764
765 if brand is not None:
766 if brand.is_in_use_by_patients:
767 gmGuiHelpers.gm_show_info (
768 aTitle = _('Managing components of a drug'),
769 aMessage = _(
770 'Cannot manage the components of the branded drug product\n'
771 '\n'
772 ' "%s" (%s)\n'
773 '\n'
774 'because it is currently taken by patients.\n'
775 ) % (brand['brand'], brand['preparation'])
776 )
777 return False
778
779 if parent is None:
780 parent = wx.GetApp().GetTopWindow()
781
782
783
784
785 if brand is None:
786 msg = _('Pick the substances which are components of this drug.')
787 right_col = _('Components of drug')
788 comp_substs = []
789 else:
790 right_col = u'%s (%s)' % (brand['brand'], brand['preparation'])
791 msg = _(
792 'Adjust the components of "%s"\n'
793 '\n'
794 'The drug must contain at least one component. Any given\n'
795 'substance can only be included once per drug.'
796 ) % right_col
797 comp_substs = [ c.substance for c in brand.components ]
798
799 substs = gmMedication.get_consumable_substances(order_by = 'description')
800 choices = [ u'%s %s %s' % (s['description'], s['amount'], s['unit']) for s in substs ]
801 picks = [ u'%s %s %s' % (c['description'], c['amount'], c['unit']) for c in comp_substs ]
802
803 picker = gmListWidgets.cItemPickerDlg (
804 parent,
805 -1,
806 title = _('Managing components of a drug ...'),
807 msg = msg
808 )
809 picker.set_columns(['Substances'], [right_col])
810 picker.set_choices(choices = choices, data = substs)
811 picker.set_picks(picks = picks, data = comp_substs)
812
813
814
815
816
817
818 btn_pressed = picker.ShowModal()
819 substs = picker.get_picks()
820 picker.Destroy()
821
822 if btn_pressed != wx.ID_OK:
823 return (False, None)
824
825 if brand is not None:
826 brand.set_substances_as_components(substances = substs)
827
828 return (True, substs)
829
831
832 if parent is None:
833 parent = wx.GetApp().GetTopWindow()
834
835 def add_from_db(brand):
836 drug_db = get_drug_database(parent = parent)
837 if drug_db is None:
838 return False
839 drug_db.import_drugs()
840 return True
841
842 def get_tooltip(brand=None):
843 tt = u'%s %s\n' % (brand['brand'], brand['preparation'])
844 tt += u'\n'
845 tt += u'%s%s%s\n' % (
846 gmTools.bool2subst(brand.is_vaccine, u'%s, ' % _('Vaccine'), u''),
847 u'%s, ' % gmTools.bool2subst(brand.is_in_use_by_patients, _('in use'), _('not in use')),
848 gmTools.bool2subst(brand['is_fake_brand'], _('fake'), u'')
849 )
850 tt += gmTools.coalesce(brand['atc'], u'', _('ATC: %s\n'))
851 tt += gmTools.coalesce(brand['external_code'], u'', u'%s: %%s\n' % brand['external_code_type'])
852 if brand['components'] is not None:
853 tt += u'- %s' % u'\n- '.join(brand['components'])
854 return tt
855
856 def edit(brand):
857 if brand is not None:
858 if brand.is_vaccine:
859 gmGuiHelpers.gm_show_info (
860 aTitle = _('Editing medication'),
861 aMessage = _(
862 'Cannot edit the medication\n'
863 '\n'
864 ' "%s" (%s)\n'
865 '\n'
866 'because it is a vaccine. Please edit it\n'
867 'from the vaccine management section !\n'
868 ) % (brand['brand'], brand['preparation'])
869 )
870 return False
871
872 return edit_branded_drug(parent = parent, branded_drug = brand, single_entry = True)
873
874 def delete(brand):
875 if brand.is_vaccine:
876 gmGuiHelpers.gm_show_info (
877 aTitle = _('Deleting medication'),
878 aMessage = _(
879 'Cannot delete the medication\n'
880 '\n'
881 ' "%s" (%s)\n'
882 '\n'
883 'because it is a vaccine. Please delete it\n'
884 'from the vaccine management section !\n'
885 ) % (brand['brand'], brand['preparation'])
886 )
887 return False
888 gmMedication.delete_branded_drug(brand = brand['pk_brand'])
889 return True
890
891 def new():
892 return edit_branded_drug(parent = parent, branded_drug = None, single_entry = False)
893
894 def refresh(lctrl):
895 drugs = gmMedication.get_branded_drugs()
896 items = [ [
897 u'%s%s' % (
898 d['brand'],
899 gmTools.bool2subst(d['is_fake_brand'], ' (%s)' % _('fake'), u'')
900 ),
901 d['preparation'],
902 gmTools.coalesce(d['atc'], u''),
903 gmTools.coalesce(d['components'], u''),
904 gmTools.coalesce(d['external_code'], u'', u'%%s [%s]' % d['external_code_type']),
905 d['pk_brand']
906 ] for d in drugs ]
907 lctrl.set_string_items(items)
908 lctrl.set_data(drugs)
909
910 msg = _('\nThese are the drug brands known to GNUmed.\n')
911
912 gmListWidgets.get_choices_from_list (
913 parent = parent,
914 msg = msg,
915 caption = _('Showing branded drugs.'),
916 columns = [_('Name'), _('Preparation'), _('ATC'), _('Components'), _('Code'), u'#'],
917 single_selection = True,
918 ignore_OK_button = ignore_OK_button,
919 refresh_callback = refresh,
920 new_callback = new,
921 edit_callback = edit,
922 delete_callback = delete,
923 list_tooltip_callback = get_tooltip,
924 left_extra_button = (_('Import'), _('Import substances and brands from a drug database.'), add_from_db)
925
926
927 )
928
929
931
932 if branded_drug is not None:
933 if branded_drug.is_in_use_by_patients:
934 gmGuiHelpers.gm_show_info (
935 aTitle = _('Editing drug'),
936 aMessage = _(
937 'Cannot edit the branded drug product\n'
938 '\n'
939 ' "%s" (%s)\n'
940 '\n'
941 'because it is currently taken by patients.\n'
942 ) % (branded_drug['brand'], branded_drug['preparation'])
943 )
944 return False
945
946 if parent is None:
947 parent = wx.GetApp().GetTopWindow()
948
949 def manage_substances(drug):
950 manage_consumable_substances(parent = parent)
951
952 ea = cBrandedDrugEAPnl(parent = parent, id = -1)
953 ea.data = branded_drug
954 ea.mode = gmTools.coalesce(branded_drug, 'new', 'edit')
955 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
956 dlg.SetTitle(gmTools.coalesce(branded_drug, _('Adding new drug brand'), _('Editing drug brand')))
957 dlg.left_extra_button = (
958 _('Substances'),
959 _('Manage consumable substances'),
960 manage_substances
961 )
962 if dlg.ShowModal() == wx.ID_OK:
963 dlg.Destroy()
964 return True
965 dlg.Destroy()
966 return False
967
968
969 from Gnumed.wxGladeWidgets import wxgBrandedDrugEAPnl
970
971 -class cBrandedDrugEAPnl(wxgBrandedDrugEAPnl.wxgBrandedDrugEAPnl, gmEditArea.cGenericEditAreaMixin):
972
989
990
991
992
993
994
995
996
998
999 if self.data is not None:
1000 if self.data.is_in_use_by_patients:
1001 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit drug brand. It is in use.'), beep = True)
1002 return False
1003
1004 validity = True
1005
1006 if self._PRW_brand.GetValue().strip() == u'':
1007 validity = False
1008 self._PRW_brand.display_as_valid(False)
1009 else:
1010 self._PRW_brand.display_as_valid(True)
1011
1012 if self._PRW_preparation.GetValue().strip() == u'':
1013 validity = False
1014 self._PRW_preparation.display_as_valid(False)
1015 else:
1016 self._PRW_preparation.display_as_valid(True)
1017
1018 if validity is True:
1019 self._TCTRL_components.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BACKGROUND))
1020 if len(self.__component_substances) == 0:
1021 wants_empty = gmGuiHelpers.gm_show_question (
1022 title = _('Checking brand data'),
1023 question = _(
1024 'You have not selected any substances\n'
1025 'as drug components.\n'
1026 '\n'
1027 'Without components you will not be able to\n'
1028 'use this drug for documenting patient care.\n'
1029 '\n'
1030 'Are you sure you want to save\n'
1031 'it without components ?'
1032 )
1033 )
1034 if not wants_empty:
1035 validity = False
1036 self.display_ctrl_as_valid(ctrl = self._TCTRL_components, valid = False)
1037
1038 if validity is False:
1039 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save branded drug. Invalid or missing essential input.'))
1040
1041 return validity
1042
1044
1045 drug = gmMedication.create_branded_drug (
1046 brand_name = self._PRW_brand.GetValue().strip(),
1047 preparation = gmTools.coalesce (
1048 self._PRW_preparation.GetData(),
1049 self._PRW_preparation.GetValue()
1050 ).strip(),
1051 return_existing = True
1052 )
1053 drug['is_fake_brand'] = self._CHBOX_is_fake.GetValue()
1054 drug['atc'] = self._PRW_atc.GetData()
1055 code = self._TCTRL_external_code.GetValue().strip()
1056 if code != u'':
1057 drug['external_code'] = code
1058 drug['external_code_type'] = self._PRW_external_code_type.GetData().strip()
1059
1060 drug.save()
1061
1062 if len(self.__component_substances) > 0:
1063 drug.set_substances_as_components(substances = self.__component_substances)
1064
1065 self.data = drug
1066
1067 return True
1068
1070 self.data['brand'] = self._PRW_brand.GetValue().strip()
1071 self.data['preparation'] = gmTools.coalesce (
1072 self._PRW_preparation.GetData(),
1073 self._PRW_preparation.GetValue()
1074 ).strip()
1075 self.data['is_fake_brand'] = self._CHBOX_is_fake.GetValue()
1076 self.data['atc'] = self._PRW_atc.GetData()
1077 code = self._TCTRL_external_code.GetValue().strip()
1078 if code != u'':
1079 self.data['external_code'] = code
1080 self.data['external_code_type'] = self._PRW_external_code_type.GetData().strip()
1081 success, data = self.data.save()
1082 if not success:
1083 err, msg = data
1084 _log.error('problem saving')
1085 _log.error('%s', err)
1086 _log.error('%s', msg)
1087 return (success is True)
1088
1090 self._PRW_brand.SetText(u'', None)
1091 self._PRW_preparation.SetText(u'', None)
1092 self._CHBOX_is_fake.SetValue(False)
1093 self._TCTRL_components.SetValue(u'')
1094 self._PRW_atc.SetText(u'', None)
1095 self._TCTRL_external_code.SetValue(u'')
1096 self._PRW_external_code_type.SetText(u'', None)
1097
1098 self._PRW_brand.SetFocus()
1099
1100 self.__component_substances = []
1101
1103 self._refresh_as_new()
1104
1121
1122
1123
1137
1139
1141
1142 query = u"""
1143 SELECT
1144 pk
1145 AS data,
1146 (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', ''))
1147 AS list_label,
1148 (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', ''))
1149 AS field_label
1150 FROM ref.branded_drug
1151 WHERE description %(fragment_condition)s
1152 ORDER BY list_label
1153 LIMIT 50"""
1154
1155 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
1156 mp.setThresholds(2, 3, 4)
1157 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1158 self.SetToolTipString(_(
1159 'The brand name of the drug.\n'
1160 '\n'
1161 'Note: a brand name will need to be linked to\n'
1162 'one or more components before it can be used,\n'
1163 'except in the case of fake (generic) vaccines.'
1164 ))
1165 self.matcher = mp
1166 self.selection_only = False
1167
1168
1169
1170
1172
1174
1175 query = u"""
1176 SELECT DISTINCT ON (sched)
1177 schedule as sched,
1178 schedule
1179 FROM clin.substance_intake
1180 WHERE schedule %(fragment_condition)s
1181 ORDER BY sched
1182 LIMIT 50"""
1183
1184 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
1185 mp.setThresholds(1, 2, 4)
1186 mp.word_separators = '[ \t=+&:@]+'
1187 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1188 self.SetToolTipString(_('The schedule for taking this substance.'))
1189 self.matcher = mp
1190 self.selection_only = False
1191
1193
1194 if intake['is_currently_active']:
1195 intake['discontinued'] = gmDateTime.pydt_now_here()
1196 if intake['discontinue_reason'] is None:
1197 intake['discontinue_reason'] = u'%s %s' % (_('not tolerated:'), _('discontinued due to allergy or intolerance'))
1198 else:
1199 if not intake['discontinue_reason'].startswith(_('not tolerated:')):
1200 intake['discontinue_reason'] = u'%s %s' % (_('not tolerated:'), intake['discontinue_reason'])
1201 if not intake.save():
1202 return False
1203
1204 allg = intake.turn_into_allergy(encounter_id = emr.active_encounter['pk_encounter'])
1205
1206 brand = intake.containing_drug
1207 if brand is not None:
1208 comps = [ c['substance'] for c in brand.components ]
1209 if len(comps) > 1:
1210 gmGuiHelpers.gm_show_info (
1211 aTitle = _(u'Documented an allergy'),
1212 aMessage = _(
1213 u'An allergy was documented against the substance:\n'
1214 u'\n'
1215 u' [%s]\n'
1216 u'\n'
1217 u'This substance was taken with the multi-component brand:\n'
1218 u'\n'
1219 u' [%s (%s)]\n'
1220 u'\n'
1221 u'Note that ALL components of this brand were discontinued.'
1222 ) % (
1223 intake['substance'],
1224 intake['brand'],
1225 u' & '.join(comps)
1226 )
1227 )
1228
1229 if parent is None:
1230 parent = wx.GetApp().GetTopWindow()
1231
1232 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent = parent, id = -1)
1233 dlg.ShowModal()
1234
1235 return True
1236
1237
1239
1240 if parent is None:
1241 parent = wx.GetApp().GetTopWindow()
1242
1243 if emr is None:
1244 emr = gmPerson.gmCurrentPatient().emr
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263 def get_tooltip(intake=None):
1264 return intake.format(one_line = False, show_all_brand_components = True)
1265
1266 def refresh(lctrl):
1267 intakes = emr.get_current_substance_intake (
1268 include_inactive = False,
1269 include_unapproved = True,
1270 order_by = u'substance, brand, started'
1271 )
1272 items = [ [
1273 u'%s%s %s %s %s%s' % (
1274 i['substance'],
1275 gmTools.coalesce(i['brand'], u'', u' (%s)'),
1276 i['amount'],
1277 i['unit'],
1278 i['preparation'],
1279 gmTools.coalesce(i['external_code_brand'], u'', u' [%s::%s]' % (i['external_code_type_brand'], i['external_code_brand']))
1280 ),
1281 u'%s%s%s' % (
1282 gmTools.coalesce(i['started'], u'', u'%%s %s' % gmTools.u_right_arrow, function_initial = ('strftime', '%Y %B %d')),
1283 gmTools.coalesce(i['schedule'], u'', u' %s %s' % (i['schedule'], gmTools.u_right_arrow)),
1284 gmTools.coalesce(i['duration'], u'', u' %s')
1285 ),
1286 u'%s' % (
1287 gmTools.bool2subst (
1288 i['intake_is_approved_of'],
1289 u'',
1290 _('disapproved')
1291 )
1292 )
1293 ] for i in intakes ]
1294 lctrl.set_string_items(items)
1295 lctrl.set_data(intakes)
1296
1297 msg = _('Substances consumed by the patient:')
1298
1299 return gmListWidgets.get_choices_from_list (
1300 parent = parent,
1301 msg = msg,
1302 caption = _('Showing consumable substances.'),
1303 columns = [ _('Intake'), _('Application'), _('Status') ],
1304 single_selection = False,
1305
1306
1307
1308 refresh_callback = refresh,
1309 list_tooltip_callback = get_tooltip
1310
1311 )
1312
1313
1314 from Gnumed.wxGladeWidgets import wxgCurrentMedicationEAPnl
1315
1316 -class cSubstanceIntakeEAPnl(wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl, gmEditArea.cGenericEditAreaMixin):
1317
1337
1339
1340 self._PRW_component.add_callback_on_lose_focus(callback = self._on_leave_component)
1341 self._PRW_component.selection_only = True
1342
1343 self._PRW_substance.add_callback_on_lose_focus(callback = self._on_leave_substance)
1344 self._PRW_substance.selection_only = True
1345
1347 curr_pat = gmPerson.gmCurrentPatient()
1348 emr = curr_pat.emr
1349
1350 state = emr.allergy_state
1351 if state['last_confirmed'] is None:
1352 confirmed = _('never')
1353 else:
1354 confirmed = state['last_confirmed'].strftime('%Y %B %d').decode(gmI18N.get_encoding())
1355 msg = _(u'%s, last confirmed %s\n') % (state.state_string, confirmed)
1356 msg += gmTools.coalesce(state['comment'], u'', _('Comment (%s): %%s\n') % state['modified_by'])
1357
1358 tt = u''
1359
1360 allgs = emr.get_allergies()
1361 if len(allgs) > 0:
1362 msg += u'\n'
1363 for allergy in allgs:
1364 msg += u'%s: %s (%s)\n' % (
1365 allergy['descriptor'],
1366 allergy['l10n_type'],
1367 gmTools.bool2subst(allergy['definite'], _('definite'), _('suspected'), u'?')
1368 )
1369 tt += u'%s: %s\n' % (
1370 allergy['descriptor'],
1371 gmTools.coalesce(allergy['reaction'], _('reaction not recorded'))
1372 )
1373
1374 if len(allgs) > 0:
1375 msg += u'\n'
1376 tt += u'\n'
1377
1378 gfr = emr.get_most_recent_results(loinc = gmLOINC.LOINC_gfr_quantity, no_of_results = 1)
1379 if gfr is None:
1380 self.calc.patient = curr_pat
1381 gfr = self.calc.eGFR
1382 if gfr.numeric_value is None:
1383 msg += _('GFR: unknown')
1384 else:
1385 msg += gfr.message
1386 tt += gfr.format (
1387 left_margin = 0,
1388 width = 50,
1389 eol = u'\n',
1390 with_formula = True,
1391 with_warnings = True,
1392 with_variables = False,
1393 with_sub_results = True,
1394 return_list = False
1395 )
1396 else:
1397 msg += u'%s: %s %s (%s)\n' % (
1398 gfr['unified_abbrev'],
1399 gfr['unified_val'],
1400 gmTools.coalesce(gfr['abnormality_indicator'], u'', u' (%s)'),
1401 gmDateTime.pydt_strftime (
1402 gfr['clin_when'],
1403 format = '%Y %b %d'
1404 )
1405 )
1406 tt += _('GFR reported by path lab')
1407
1408 self._LBL_allergies.SetLabel(msg)
1409 self._LBL_allergies.SetToolTipString(tt)
1410
1411
1412
1502
1504
1505 emr = gmPerson.gmCurrentPatient().get_emr()
1506 epi = self._PRW_episode.GetData(can_create = True)
1507
1508 if self._PRW_substance.GetData() is None:
1509
1510 intake = emr.add_substance_intake (
1511 pk_component = self._PRW_component.GetData(),
1512 episode = epi
1513 )
1514 else:
1515 intake = emr.add_substance_intake (
1516 pk_substance = self._PRW_substance.GetData(),
1517 episode = epi,
1518 preparation = self._PRW_preparation.GetValue().strip()
1519 )
1520
1521 if intake is None:
1522 gmDispatcher.send('statustext', msg = _('Cannot add duplicate of (maybe inactive) substance intake.'), beep = True)
1523 return False
1524
1525 intake['started'] = self._DP_started.GetData()
1526 intake['discontinued'] = self._DP_discontinued.GetData()
1527 if intake['discontinued'] is None:
1528 intake['discontinue_reason'] = None
1529 else:
1530 intake['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
1531 intake['schedule'] = self._PRW_schedule.GetValue().strip()
1532 intake['aim'] = self._PRW_aim.GetValue().strip()
1533 intake['notes'] = self._PRW_notes.GetValue().strip()
1534 intake['is_long_term'] = self._CHBOX_long_term.IsChecked()
1535 intake['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
1536 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
1537 intake['duration'] = None
1538 else:
1539 intake['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
1540 intake.save()
1541
1542 self.data = intake
1543
1544 return True
1545
1547
1548
1549 self.data['started'] = self._DP_started.GetData()
1550 self.data['discontinued'] = self._DP_discontinued.GetData()
1551 if self.data['discontinued'] is None:
1552 self.data['discontinue_reason'] = None
1553 else:
1554 self.data['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
1555 self.data['schedule'] = self._PRW_schedule.GetValue()
1556 self.data['is_long_term'] = self._CHBOX_long_term.IsChecked()
1557 self.data['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
1558 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
1559 self.data['duration'] = None
1560 else:
1561 self.data['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
1562
1563
1564 self.data['preparation'] = self._PRW_preparation.GetValue()
1565
1566
1567 self.data['aim'] = self._PRW_aim.GetValue()
1568 self.data['notes'] = self._PRW_notes.GetValue()
1569 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True)
1570
1571 self.data.save()
1572
1573 return True
1574
1576 self._PRW_component.SetText(u'', None)
1577 self._LBL_component.Enable(True)
1578 self._PRW_component.Enable(True)
1579 self._TCTRL_brand_ingredients.SetValue(u'')
1580 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1581
1582 self._LBL_or.Enable(True)
1583
1584 self._PRW_substance.SetText(u'', None)
1585 self._PRW_substance.Enable(True)
1586
1587 self._PRW_preparation.SetText(u'', None)
1588 self._PRW_preparation.Enable(True)
1589
1590 self._PRW_schedule.SetText(u'', None)
1591 self._PRW_duration.SetText(u'', None)
1592 self._PRW_aim.SetText(u'', None)
1593 self._PRW_notes.SetText(u'', None)
1594 self._PRW_episode.SetText(u'', None)
1595
1596 self._CHBOX_long_term.SetValue(False)
1597 self._CHBOX_approved.SetValue(True)
1598
1599 self._DP_started.SetData(gmDateTime.pydt_now_here())
1600 self._DP_discontinued.SetData(None)
1601 self._PRW_discontinue_reason.SetValue(u'')
1602
1603 self.__refresh_allergies()
1604
1605 self._PRW_component.SetFocus()
1606
1608
1609 self._TCTRL_brand_ingredients.SetValue(u'')
1610 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1611
1612 if self.data['pk_brand'] is None:
1613 self.__refresh_from_existing_substance()
1614 else:
1615 self.__refresh_from_existing_component()
1616
1617
1618 self._LBL_component.Enable(False)
1619 self._PRW_component.Enable(False)
1620 self._LBL_or.Enable(False)
1621 self._PRW_substance.Enable(False)
1622
1623 if self.data['is_long_term']:
1624 self._CHBOX_long_term.SetValue(True)
1625 self._PRW_duration.Enable(False)
1626 self._PRW_duration.SetText(gmTools.u_infinity, None)
1627 self._BTN_discontinued_as_planned.Enable(False)
1628 else:
1629 self._CHBOX_long_term.SetValue(False)
1630 self._PRW_duration.Enable(True)
1631 self._BTN_discontinued_as_planned.Enable(True)
1632 if self.data['duration'] is None:
1633 self._PRW_duration.SetText(u'', None)
1634 else:
1635 self._PRW_duration.SetText(gmDateTime.format_interval(self.data['duration'], gmDateTime.acc_days), self.data['duration'])
1636 self._PRW_aim.SetText(gmTools.coalesce(self.data['aim'], u''), self.data['aim'])
1637 self._PRW_notes.SetText(gmTools.coalesce(self.data['notes'], u''), self.data['notes'])
1638 self._PRW_episode.SetData(self.data['pk_episode'])
1639 self._PRW_schedule.SetText(gmTools.coalesce(self.data['schedule'], u''), self.data['schedule'])
1640
1641 self._CHBOX_approved.SetValue(self.data['intake_is_approved_of'])
1642
1643 self._DP_started.SetData(self.data['started'])
1644 self._DP_discontinued.SetData(self.data['discontinued'])
1645 self._PRW_discontinue_reason.SetValue(gmTools.coalesce(self.data['discontinue_reason'], u''))
1646 if self.data['discontinued'] is not None:
1647 self._PRW_discontinue_reason.Enable()
1648
1649 self.__refresh_allergies()
1650
1651 self._PRW_schedule.SetFocus()
1652
1654 self._LBL_component.Enable(False)
1655 self._PRW_component.Enable(False)
1656 self._PRW_component.SetText(u'', None)
1657 self._PRW_component.display_as_valid(True)
1658
1659 self._LBL_or.Enable(False)
1660
1661 self._PRW_substance.Enable(True)
1662 self._PRW_substance.SetText (
1663 u'%s %s %s' % (self.data['substance'], self.data['amount'], self.data['unit']),
1664 self.data['pk_substance']
1665 )
1666
1667 self._PRW_preparation.SetText(gmTools.coalesce(self.data['preparation'], u''), self.data['preparation'])
1668 self._PRW_preparation.Enable(True)
1669
1671 self._LBL_component.Enable(True)
1672 self._PRW_component.Enable(True)
1673 self._PRW_component.SetText (
1674 u'%s %s %s (%s)' % (self.data['substance'], self.data['amount'], self.data['unit'], self.data['brand']),
1675 self.data['pk_drug_component']
1676 )
1677
1678 brand = gmMedication.cBrandedDrug(aPK_obj = self.data['pk_brand'])
1679 if brand['components'] is not None:
1680 self._TCTRL_brand_ingredients.SetValue(u'; '.join(brand['components']))
1681 tt = u'%s:\n\n- %s' % (
1682 self.data['brand'],
1683 u'\n- '.join(brand['components'])
1684 )
1685 self._TCTRL_brand_ingredients.SetToolTipString(tt)
1686
1687 self._LBL_or.Enable(False)
1688 self._LBL_substance.Enable(False)
1689 self._PRW_substance.SetText(u'', None)
1690 self._PRW_substance.display_as_valid(True)
1691
1692 self._PRW_preparation.SetText(self.data['preparation'], self.data['preparation'])
1693 self._PRW_preparation.Enable(False)
1694
1696 self._refresh_as_new()
1697
1698 self._PRW_episode.SetData(self.data['pk_episode'])
1699
1700 self._PRW_component.SetFocus()
1701
1702
1703
1705 if self._PRW_component.GetData() is None:
1706 self._LBL_or.Enable(True)
1707 self._PRW_component.SetText(u'', None)
1708 self._LBL_substance.Enable(True)
1709 self._PRW_substance.Enable(True)
1710 self._LBL_preparation.Enable(True)
1711 self._PRW_preparation.Enable(True)
1712 self._PRW_preparation.SetText(u'', None)
1713 self._TCTRL_brand_ingredients.SetValue(u'')
1714 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1715 else:
1716 self._LBL_or.Enable(False)
1717 self._LBL_substance.Enable(False)
1718 self._PRW_substance.SetText(u'', None)
1719 self._PRW_substance.display_as_valid(True)
1720 self._PRW_substance.Enable(False)
1721 self._LBL_preparation.Enable(False)
1722 self._PRW_preparation.Enable(False)
1723 comp = gmMedication.cDrugComponent(aPK_obj = self._PRW_component.GetData())
1724 self._PRW_preparation.SetText(comp['preparation'], comp['preparation'])
1725 brand = comp.containing_drug
1726 if brand['components'] is not None:
1727 self._TCTRL_brand_ingredients.SetValue(u'; '.join(brand['components']))
1728 tt = u'%s:\n\n- %s' % (
1729 brand['brand'],
1730 u'\n- '.join(brand['components'])
1731 )
1732 self._TCTRL_brand_ingredients.SetToolTipString(tt)
1733
1735 if self._PRW_substance.GetData() is None:
1736 self._LBL_or.Enable(True)
1737 self._LBL_component.Enable(True)
1738 self._PRW_component.Enable(True)
1739 self._PRW_substance.SetText(u'', None)
1740 else:
1741 self._LBL_or.Enable(False)
1742 self._LBL_component.Enable(False)
1743 self._PRW_component.SetText(u'', None)
1744 self._PRW_component.display_as_valid(True)
1745 self._PRW_component.Enable(False)
1746 self._LBL_preparation.Enable(True)
1747 self._PRW_preparation.Enable(True)
1748 self._TCTRL_brand_ingredients.SetValue(u'')
1749 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1750
1752 if self._DP_discontinued.GetData() is None:
1753 self._PRW_discontinue_reason.Enable(False)
1754 else:
1755 self._PRW_discontinue_reason.Enable(True)
1756
1759
1762
1765
1777
1807
1809 if self._CHBOX_long_term.IsChecked() is True:
1810 self._PRW_duration.Enable(False)
1811 self._BTN_discontinued_as_planned.Enable(False)
1812 self._PRW_discontinue_reason.Enable(False)
1813 else:
1814 self._PRW_duration.Enable(True)
1815 self._BTN_discontinued_as_planned.Enable(True)
1816 self._PRW_discontinue_reason.Enable(True)
1817
1818 self.__refresh_allergies()
1819
1829
1830
1832
1833 subst = gmMedication.cSubstanceIntakeEntry(aPK_obj = substance)
1834 msg = _(
1835 '\n'
1836 '[%s]\n'
1837 '\n'
1838 'It may be prudent to edit (before deletion) the details\n'
1839 'of this substance intake entry so as to leave behind\n'
1840 'some indication of why it was deleted.\n'
1841 ) % subst.format()
1842
1843 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
1844 parent,
1845 -1,
1846 caption = _('Deleting medication / substance intake'),
1847 question = msg,
1848 button_defs = [
1849 {'label': _('&Edit'), 'tooltip': _('Allow editing of substance intake entry before deletion.'), 'default': True},
1850 {'label': _('&Delete'), 'tooltip': _('Delete immediately without editing first.')},
1851 {'label': _('&Cancel'), 'tooltip': _('Abort. Do not delete or edit substance intake entry.')}
1852 ]
1853 )
1854
1855 edit_first = dlg.ShowModal()
1856 dlg.Destroy()
1857
1858 if edit_first == wx.ID_CANCEL:
1859 return
1860
1861 if edit_first == wx.ID_YES:
1862 edit_intake_of_substance(parent = parent, substance = subst)
1863 delete_it = gmGuiHelpers.gm_show_question (
1864 aMessage = _('Now delete substance intake entry ?'),
1865 aTitle = _('Deleting medication / substance intake')
1866 )
1867 else:
1868 delete_it = True
1869
1870 if not delete_it:
1871 return
1872
1873 gmMedication.delete_substance_intake(substance = substance)
1874
1889
1890
1891
1892
1920
1922
1923 if parent is None:
1924 parent = wx.GetApp().GetTopWindow()
1925
1926
1927 dbcfg = gmCfg.cCfgSQL()
1928 option = u'form_templates.medication_list'
1929
1930 template = dbcfg.get2 (
1931 option = option,
1932 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1933 bias = 'user'
1934 )
1935
1936 if template is None:
1937 template = configure_medication_list_template(parent = parent)
1938 if template is None:
1939 gmGuiHelpers.gm_show_error (
1940 aMessage = _('There is no medication list template configured.'),
1941 aTitle = _('Printing medication list')
1942 )
1943 return False
1944 else:
1945 try:
1946 name, ver = template.split(u' - ')
1947 except:
1948 _log.exception('problem splitting medication list template name [%s]', template)
1949 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading medication list template.'), beep = True)
1950 return False
1951 template = gmForms.get_form_template(name_long = name, external_version = ver)
1952 if template is None:
1953 gmGuiHelpers.gm_show_error (
1954 aMessage = _('Cannot load medication list template [%s - %s]') % (name, ver),
1955 aTitle = _('Printing medication list')
1956 )
1957 return False
1958
1959
1960 try:
1961 meds_list = template.instantiate()
1962 except KeyError:
1963 _log.exception('cannot instantiate medication list template [%s]', template)
1964 gmGuiHelpers.gm_show_error (
1965 aMessage = _('Invalid medication list template [%s - %s (%s)]') % (name, ver, template['engine']),
1966 aTitle = _('Printing medication list')
1967 )
1968 return False
1969
1970 ph = gmMacro.gmPlaceholderHandler()
1971
1972 meds_list.substitute_placeholders(data_source = ph)
1973 pdf_name = meds_list.generate_output()
1974 if pdf_name is None:
1975 gmGuiHelpers.gm_show_error (
1976 aMessage = _('Error generating the medication list.'),
1977 aTitle = _('Printing medication list')
1978 )
1979 return False
1980
1981
1982 printed = gmPrinting.print_files(filenames = [pdf_name], jobtype = 'medication_list')
1983 if not printed:
1984 gmGuiHelpers.gm_show_error (
1985 aMessage = _('Error printing the medication list.'),
1986 aTitle = _('Printing medication list')
1987 )
1988 return False
1989
1990
1991 pat = gmPerson.gmCurrentPatient()
1992 emr = pat.get_emr()
1993 epi = emr.add_episode(episode_name = 'administration', is_open = False)
1994 emr.add_clin_narrative (
1995 soap_cat = None,
1996 note = _('medication list printed from template [%s - %s]') % (template['name_long'], template['external_version']),
1997 episode = epi
1998 )
1999
2000 return True
2001
2002
2031
2033
2034 if parent is None:
2035 parent = wx.GetApp().GetTopWindow()
2036
2037 dbcfg = gmCfg.cCfgSQL()
2038 option = u'form_templates.prescription'
2039 template_name = dbcfg.get2 (
2040 option = option,
2041 workplace = gmSurgery.gmCurrentPractice().active_workplace,
2042 bias = 'user'
2043 )
2044
2045 if template_name is None:
2046 template = configure_prescription_template(parent = parent)
2047 if template is None:
2048 gmGuiHelpers.gm_show_error (
2049 aMessage = _('There is no prescription template configured.'),
2050 aTitle = _('Printing prescription')
2051 )
2052 return None
2053 return template
2054
2055 try:
2056 name, ver = template_name.split(u' - ')
2057 except:
2058 _log.exception('problem splitting prescription template name [%s]', template_name)
2059 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading prescription template.'), beep = True)
2060 return False
2061 template = gmForms.get_form_template(name_long = name, external_version = ver)
2062 if template is None:
2063 gmGuiHelpers.gm_show_error (
2064 aMessage = _('Cannot load prescription template [%s - %s]') % (name, ver),
2065 aTitle = _('Printing prescription')
2066 )
2067 return False
2068 return template
2069
2071
2072
2073 rx_template = get_prescription_template(parent = parent)
2074 if rx_template is None:
2075 return False
2076
2077
2078 try:
2079 rx = rx_template.instantiate()
2080 except KeyError:
2081 _log.exception('cannot instantiate prescription template [%s]', rx_template)
2082 gmGuiHelpers.gm_show_error (
2083 aMessage = _('Invalid prescription template [%s - %s (%s)]') % (rx_template['name_long'], rx_template['external_version'], rx_template['engine']),
2084 aTitle = _('Printing prescription list')
2085 )
2086 return False
2087 ph = gmMacro.gmPlaceholderHandler()
2088
2089 rx.substitute_placeholders(data_source = ph)
2090 pdf_name = rx.generate_output()
2091 if pdf_name is None:
2092 gmGuiHelpers.gm_show_error (
2093 aMessage = _('Error generating the prescription printout.'),
2094 aTitle = _('Printing prescription')
2095 )
2096 return False
2097
2098
2099 printed = gmPrinting.print_files(filenames = [pdf_name], jobtype = 'prescription')
2100 if not printed:
2101 gmGuiHelpers.gm_show_error (
2102 aMessage = _('Error printing the prescription.'),
2103 aTitle = _('Printing prescription')
2104 )
2105 return False
2106
2107 epi = emr.add_episode (
2108 episode_name = gmMedication.DEFAULT_MEDICATION_HISTORY_EPISODE,
2109 is_open = False
2110 )
2111
2112
2113 files2import = []
2114 files2import.extend(rx.final_output_filenames)
2115 files2import.extend(rx.re_editable_filenames)
2116 doc = gmDocumentWidgets.save_files_as_new_document (
2117 parent = parent,
2118 filenames = files2import,
2119 document_type = u'prescription',
2120
2121
2122
2123 episode = epi,
2124 review_as_normal = True
2125 )
2126
2127
2128 emr.add_clin_narrative (
2129 soap_cat = None,
2130 note = _('prescription printed from template [%s - %s]') % (rx_template['name_long'], rx_template['external_version']),
2131 episode = epi
2132 )
2133
2134 return True
2135
2136
2164
2165
2167
2168 if len(prescribed_drugs) == 0:
2169 return
2170
2171 curr_brands = [ i['pk_brand'] for i in emr.get_current_substance_intake() if i['pk_brand'] is not None ]
2172 new_drugs = []
2173 for drug in prescribed_drugs:
2174 if drug['pk_brand'] not in curr_brands:
2175 new_drugs.append(drug)
2176
2177 if len(new_drugs) == 0:
2178 return
2179
2180 if parent is None:
2181 parent = wx.GetApp().GetTopWindow()
2182
2183 dlg = gmListWidgets.cItemPickerDlg (
2184 parent,
2185 -1,
2186 msg = _(
2187 'These brands have been prescribed but are not listed\n'
2188 'in the current medication list of this patient.\n'
2189 '\n'
2190 'Please select those you want added to the medication list.'
2191 )
2192 )
2193 dlg.set_columns (
2194 columns = [_('Newly prescribed drugs')],
2195 columns_right = [_('Add to medication list')]
2196 )
2197 choices = [ (u'%s %s (%s)' % (d['brand'], d['preparation'], u'; '.join(d['components']))) for d in new_drugs ]
2198 dlg.set_choices (
2199 choices = choices,
2200 data = new_drugs
2201 )
2202 dlg.ShowModal()
2203 drugs2add = dlg.get_picks()
2204 dlg.Destroy()
2205
2206 if drugs2add is None:
2207 return
2208
2209 if len(drugs2add) == 0:
2210 return
2211
2212 for drug in drugs2add:
2213
2214 intake = emr.add_substance_intake (
2215 pk_component = drug['pk_components'][0],
2216 episode = emr.add_episode(episode_name = gmMedication.DEFAULT_MEDICATION_HISTORY_EPISODE)['pk_episode'],
2217 )
2218 if intake is None:
2219 continue
2220 intake['intake_is_approved_of'] = True
2221 intake.save()
2222
2223 return
2224
2226 """A grid class for displaying current substance intake.
2227
2228 - does NOT listen to the currently active patient
2229 - thereby it can display any patient at any time
2230 """
2232
2233 wx.grid.Grid.__init__(self, *args, **kwargs)
2234
2235 self.__patient = None
2236 self.__row_data = {}
2237 self.__prev_row = None
2238 self.__prev_tooltip_row = None
2239 self.__prev_cell_0 = None
2240 self.__grouping_mode = u'issue'
2241 self.__filter_show_unapproved = True
2242 self.__filter_show_inactive = True
2243
2244 self.__grouping2col_labels = {
2245 u'issue': [
2246 _('Health issue'),
2247 _('Substance'),
2248 _('Strength'),
2249 _('Schedule'),
2250 _('Started'),
2251 _('Duration / Until'),
2252 _('Brand'),
2253 _('Advice')
2254 ],
2255 u'brand': [
2256 _('Brand'),
2257 _('Schedule'),
2258 _('Substance'),
2259 _('Strength'),
2260 _('Started'),
2261 _('Duration / Until'),
2262 _('Health issue'),
2263 _('Advice')
2264 ],
2265 u'episode': [
2266 _('Episode'),
2267 _('Substance'),
2268 _('Strength'),
2269 _('Schedule'),
2270 _('Started'),
2271 _('Duration / Until'),
2272 _('Brand'),
2273 _('Advice')
2274 ]
2275 }
2276
2277 self.__grouping2order_by_clauses = {
2278 u'issue': u'pk_health_issue nulls first, substance, started',
2279 u'episode': u'pk_health_issue nulls first, episode, substance, started',
2280 u'brand': u'brand nulls last, substance, started'
2281 }
2282
2283 self.__init_ui()
2284 self.__register_events()
2285
2286
2287
2289
2290 sel_block_top_left = self.GetSelectionBlockTopLeft()
2291 sel_block_bottom_right = self.GetSelectionBlockBottomRight()
2292 sel_cols = self.GetSelectedCols()
2293 sel_rows = self.GetSelectedRows()
2294
2295 selected_cells = []
2296
2297
2298 selected_cells += self.GetSelectedCells()
2299
2300
2301 selected_cells += list (
2302 (row, col)
2303 for row in sel_rows
2304 for col in xrange(self.GetNumberCols())
2305 )
2306
2307
2308 selected_cells += list (
2309 (row, col)
2310 for row in xrange(self.GetNumberRows())
2311 for col in sel_cols
2312 )
2313
2314
2315 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()):
2316 selected_cells += [
2317 (row, col)
2318 for row in xrange(top_left[0], bottom_right[0] + 1)
2319 for col in xrange(top_left[1], bottom_right[1] + 1)
2320 ]
2321
2322 return set(selected_cells)
2323
2325 rows = {}
2326
2327 for row, col in self.get_selected_cells():
2328 rows[row] = True
2329
2330 return rows.keys()
2331
2334
2336
2337 self.empty_grid()
2338
2339 if self.__patient is None:
2340 return
2341
2342 emr = self.__patient.get_emr()
2343 meds = emr.get_current_substance_intake (
2344 order_by = self.__grouping2order_by_clauses[self.__grouping_mode],
2345 include_unapproved = self.__filter_show_unapproved,
2346 include_inactive = self.__filter_show_inactive
2347 )
2348 if not meds:
2349 return
2350
2351 self.BeginBatch()
2352
2353
2354 labels = self.__grouping2col_labels[self.__grouping_mode]
2355 if self.__filter_show_unapproved:
2356 self.AppendCols(numCols = len(labels) + 1)
2357 else:
2358 self.AppendCols(numCols = len(labels))
2359 for col_idx in range(len(labels)):
2360 self.SetColLabelValue(col_idx, labels[col_idx])
2361 if self.__filter_show_unapproved:
2362 self.SetColLabelValue(len(labels), u'OK?')
2363 self.SetColSize(len(labels), 40)
2364
2365 self.AppendRows(numRows = len(meds))
2366
2367
2368 for row_idx in range(len(meds)):
2369 med = meds[row_idx]
2370 self.__row_data[row_idx] = med
2371
2372 if med['is_currently_active'] is True:
2373 atcs = []
2374 if med['atc_substance'] is not None:
2375 atcs.append(med['atc_substance'])
2376
2377
2378
2379 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (med['substance'],))
2380 if allg not in [None, False]:
2381 attr = self.GetOrCreateCellAttr(row_idx, 0)
2382 if allg['type'] == u'allergy':
2383 attr.SetTextColour('red')
2384 else:
2385 attr.SetTextColour('yellow')
2386 self.SetRowAttr(row_idx, attr)
2387 else:
2388 attr = self.GetOrCreateCellAttr(row_idx, 0)
2389 attr.SetTextColour('grey')
2390 self.SetRowAttr(row_idx, attr)
2391
2392 if self.__grouping_mode == u'episode':
2393 if med['pk_episode'] is None:
2394 self.__prev_cell_0 = None
2395 epi = gmTools.u_diameter
2396 else:
2397 if self.__prev_cell_0 == med['episode']:
2398 epi = u''
2399 else:
2400 self.__prev_cell_0 = med['episode']
2401 epi = gmTools.coalesce(med['episode'], u'')
2402 self.SetCellValue(row_idx, 0, gmTools.wrap(text = epi, width = 40))
2403
2404 self.SetCellValue(row_idx, 1, med['substance'])
2405 self.SetCellValue(row_idx, 2, u'%s %s' % (med['amount'], med['unit']))
2406 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], u''))
2407 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
2408
2409 if med['is_long_term']:
2410 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
2411 else:
2412 if med['discontinued'] is None:
2413 if med['duration'] is None:
2414 self.SetCellValue(row_idx, 5, u'')
2415 else:
2416 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
2417 else:
2418 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d'))
2419
2420 if med['pk_brand'] is None:
2421 brand = med['preparation']
2422 else:
2423 if med['fake_brand']:
2424 brand = u'%s %s' % (
2425 gmTools.coalesce(med['brand'], u'', _('%s (fake)')),
2426 med['preparation']
2427 )
2428 else:
2429 brand = u'%s %s' % (
2430 gmTools.coalesce(med['brand'], u''),
2431 med['preparation']
2432 )
2433 self.SetCellValue(row_idx, 6, gmTools.wrap(text = brand, width = 35))
2434
2435 elif self.__grouping_mode == u'issue':
2436 if med['pk_health_issue'] is None:
2437 self.__prev_cell_0 = None
2438 issue = u'%s%s' % (
2439 gmTools.u_diameter,
2440 gmTools.coalesce(med['episode'], u'', u' (%s)')
2441 )
2442 else:
2443 if self.__prev_cell_0 == med['health_issue']:
2444 issue = u''
2445 else:
2446 self.__prev_cell_0 = med['health_issue']
2447 issue = med['health_issue']
2448 self.SetCellValue(row_idx, 0, gmTools.wrap(text = issue, width = 40))
2449
2450 self.SetCellValue(row_idx, 1, med['substance'])
2451 self.SetCellValue(row_idx, 2, u'%s %s' % (med['amount'], med['unit']))
2452 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], u''))
2453 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
2454
2455 if med['is_long_term']:
2456 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
2457 else:
2458 if med['discontinued'] is None:
2459 if med['duration'] is None:
2460 self.SetCellValue(row_idx, 5, u'')
2461 else:
2462 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
2463 else:
2464 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d'))
2465
2466 if med['pk_brand'] is None:
2467 brand = med['preparation']
2468 else:
2469 if med['fake_brand']:
2470 brand = u'%s %s' % (
2471 gmTools.coalesce(med['brand'], u'', _('%s (fake)')),
2472 med['preparation']
2473 )
2474 else:
2475 brand = u'%s %s' % (
2476 gmTools.coalesce(med['brand'], u''),
2477 med['preparation']
2478 )
2479 self.SetCellValue(row_idx, 6, gmTools.wrap(text = brand, width = 35))
2480
2481 elif self.__grouping_mode == u'brand':
2482
2483 if med['pk_brand'] is None:
2484 self.__prev_cell_0 = None
2485 brand = u'%s (%s)' % (
2486 gmTools.u_diameter,
2487 med['preparation']
2488 )
2489 else:
2490 if self.__prev_cell_0 == med['brand']:
2491 brand = u''
2492 else:
2493 self.__prev_cell_0 = med['brand']
2494 if med['fake_brand']:
2495 brand = u'%s %s' % (
2496 gmTools.coalesce(med['brand'], u'', _('%s (fake)')),
2497 med['preparation']
2498 )
2499 else:
2500 brand = u'%s %s' % (
2501 gmTools.coalesce(med['brand'], u''),
2502 med['preparation']
2503 )
2504 self.SetCellValue(row_idx, 0, gmTools.wrap(text = brand, width = 35))
2505
2506 self.SetCellValue(row_idx, 1, gmTools.coalesce(med['schedule'], u''))
2507 self.SetCellValue(row_idx, 2, med['substance'])
2508 self.SetCellValue(row_idx, 3, u'%s %s' % (med['amount'], med['unit']))
2509 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
2510
2511 if med['is_long_term']:
2512 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
2513 else:
2514 if med['discontinued'] is None:
2515 if med['duration'] is None:
2516 self.SetCellValue(row_idx, 5, u'')
2517 else:
2518 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
2519 else:
2520 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d'))
2521
2522 if med['pk_health_issue'] is None:
2523 issue = u'%s%s' % (
2524 gmTools.u_diameter,
2525 gmTools.coalesce(med['episode'], u'', u' (%s)')
2526 )
2527 else:
2528 issue = gmTools.coalesce(med['health_issue'], u'')
2529 self.SetCellValue(row_idx, 6, gmTools.wrap(text = issue, width = 40))
2530
2531 else:
2532 raise ValueError('unknown grouping mode [%s]' % self.__grouping_mode)
2533
2534 if med['notes'] is not None:
2535 self.SetCellValue(row_idx, 7, gmTools.wrap(text = med['notes'], width = 50))
2536
2537 if self.__filter_show_unapproved:
2538 self.SetCellValue (
2539 row_idx,
2540 len(labels),
2541 gmTools.bool2subst(med['intake_is_approved_of'], gmTools.u_checkmark_thin, u'', u'?')
2542 )
2543
2544
2545
2546 self.AutoSize()
2547 self.EndBatch()
2548
2550 self.BeginBatch()
2551 self.ClearGrid()
2552
2553
2554 if self.GetNumberRows() > 0:
2555 self.DeleteRows(pos = 0, numRows = self.GetNumberRows())
2556 if self.GetNumberCols() > 0:
2557 self.DeleteCols(pos = 0, numCols = self.GetNumberCols())
2558 self.EndBatch()
2559 self.__row_data = {}
2560 self.__prev_cell_0 = None
2561
2563
2564 if len(self.__row_data) == 0:
2565 return
2566
2567 sel_rows = self.get_selected_rows()
2568 if len(sel_rows) != 1:
2569 return
2570
2571 drug_db = get_drug_database()
2572 if drug_db is None:
2573 return
2574
2575 intake = self.get_selected_data()[0]
2576 if intake['brand'] is None:
2577 drug_db.show_info_on_substance(substance_intake = intake)
2578 else:
2579 drug_db.show_info_on_drug(substance_intake = intake)
2580
2588
2591
2601
2607
2621
2624
2638
2652
2668
2672
2777
2778
2779
2781 self.CreateGrid(0, 1)
2782 self.EnableEditing(0)
2783 self.EnableDragGridSize(1)
2784 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows)
2785
2786 self.SetColLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTER)
2787
2788 self.SetRowLabelSize(0)
2789 self.SetRowLabelAlignment(horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
2790
2791
2792
2794 return self.__patient
2795
2799
2800 patient = property(_get_patient, _set_patient)
2801
2803 return self.__grouping_mode
2804
2808
2809 grouping_mode = property(_get_grouping_mode, _set_grouping_mode)
2810
2812 return self.__filter_show_unapproved
2813
2817
2818 filter_show_unapproved = property(_get_filter_show_unapproved, _set_filter_show_unapproved)
2819
2821 return self.__filter_show_inactive
2822
2826
2827 filter_show_inactive = property(_get_filter_show_inactive, _set_filter_show_inactive)
2828
2829
2830
2832
2833 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells)
2834
2835
2836
2837
2838 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
2839
2841 """Calculate where the mouse is and set the tooltip dynamically."""
2842
2843
2844
2845 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859 row, col = self.XYToCell(x, y)
2860
2861 if row == self.__prev_tooltip_row:
2862 return
2863
2864 self.__prev_tooltip_row = row
2865
2866 try:
2867 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row))
2868 except KeyError:
2869 pass
2870
2875
2876 from Gnumed.wxGladeWidgets import wxgCurrentSubstancesPnl
2877
2878 -class cCurrentSubstancesPnl(wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl, gmRegetMixin.cRegetOnPaintMixin):
2879
2880 """Panel holding a grid with current substances. Used as notebook page."""
2881
2888
2889
2890
2892 """Populate cells with data from model."""
2893 pat = gmPerson.gmCurrentPatient()
2894 if pat.connected:
2895 self._grid_substances.patient = pat
2896 self.__refresh_gfr(pat)
2897 else:
2898 self._grid_substances.patient = None
2899 self.__clear_gfr()
2900 return True
2901
2903 gfr = patient.emr.get_most_recent_results(loinc = gmLOINC.LOINC_gfr_quantity, no_of_results = 1)
2904 if gfr is None:
2905 calc = gmClinicalCalculator.cClinicalCalculator()
2906 calc.patient = patient
2907 gfr = calc.eGFR
2908 if gfr.numeric_value is None:
2909 msg = _('GFR: ?')
2910 tt = gfr.message
2911 else:
2912 msg = _('eGFR: %.1f (%s)') % (
2913 gfr.numeric_value,
2914 gmDateTime.pydt_strftime (
2915 gfr.date_valid,
2916 format = '%b %Y'
2917 )
2918 )
2919 tt = gfr.format (
2920 left_margin = 0,
2921 width = 50,
2922 eol = u'\n',
2923 with_formula = True,
2924 with_warnings = True,
2925 with_variables = False,
2926 with_sub_results = True,
2927 return_list = False
2928 )
2929 else:
2930 msg = u'%s: %s %s (%s)\n' % (
2931 gfr['unified_abbrev'],
2932 gfr['unified_val'],
2933 gmTools.coalesce(gfr['abnormality_indicator'], u'', u' (%s)'),
2934 gmDateTime.pydt_strftime (
2935 gfr['clin_when'],
2936 format = '%b %Y'
2937 )
2938 )
2939 tt = _('GFR reported by path lab')
2940
2941 self._LBL_gfr.SetLabel(msg)
2942 self._LBL_gfr.SetToolTipString(tt)
2943 self._LBL_gfr.Refresh()
2944 self.Layout()
2945
2947 self._LBL_gfr.SetLabel(_('GFR: ?'))
2948 self._LBL_gfr.Refresh()
2949 self.Layout()
2950
2951
2952
2954 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
2955 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
2956 gmDispatcher.connect(signal = u'substance_intake_mod_db', receiver = self._schedule_data_reget)
2957
2958
2959
2961 wx.CallAfter(self.__on_pre_patient_selection)
2962
2964 self._grid_substances.patient = None
2965
2967 wx.CallAfter(self.__on_post_patient_selection)
2968
2970 self._schedule_data_reget()
2971
2974
2977
2980
2983
2986
2989
2992
2995
2998
3001
3004
3007
3010
3013
3016
3019
3020
3021
3022 if __name__ == '__main__':
3023
3024 if len(sys.argv) < 2:
3025 sys.exit()
3026
3027 if sys.argv[1] != 'test':
3028 sys.exit()
3029
3030 from Gnumed.pycommon import gmI18N
3031 from Gnumed.business import gmPersonSearch
3032
3033 gmI18N.activate_locale()
3034 gmI18N.install_domain(domain = 'gnumed')
3035
3036 pat = gmPersonSearch.ask_for_patient()
3037 if pat is None:
3038 sys.exit()
3039 gmPerson.set_active_patient(patient = pat)
3040
3041
3042
3043
3044
3045
3046 manage_substance_intakes()
3047
3048
3049