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 import datetime as pydt
12
13
14 import wx
15 import wx.grid
16
17
18 if __name__ == '__main__':
19 sys.path.insert(0, '../../')
20 from Gnumed.pycommon import gmI18N
21 gmI18N.activate_locale()
22 gmI18N.install_domain(domain = 'gnumed')
23
24 from Gnumed.pycommon import gmDispatcher
25 from Gnumed.pycommon import gmCfg
26 from Gnumed.pycommon import gmTools
27 from Gnumed.pycommon import gmDateTime
28 from Gnumed.pycommon import gmMatchProvider
29 from Gnumed.pycommon import gmI18N
30 from Gnumed.pycommon import gmPrinting
31 from Gnumed.pycommon import gmCfg2
32 from Gnumed.pycommon import gmNetworkTools
33
34 from Gnumed.business import gmPerson
35 from Gnumed.business import gmATC
36 from Gnumed.business import gmPraxis
37 from Gnumed.business import gmMedication
38 from Gnumed.business import gmForms
39 from Gnumed.business import gmStaff
40 from Gnumed.business import gmDocuments
41 from Gnumed.business import gmLOINC
42 from Gnumed.business import gmClinicalRecord
43 from Gnumed.business import gmClinicalCalculator
44 from Gnumed.business import gmPathLab
45
46 from Gnumed.wxpython import gmGuiHelpers
47 from Gnumed.wxpython import gmRegetMixin
48 from Gnumed.wxpython import gmAuthWidgets
49 from Gnumed.wxpython import gmEditArea
50 from Gnumed.wxpython import gmMacro
51 from Gnumed.wxpython import gmCfgWidgets
52 from Gnumed.wxpython import gmListWidgets
53 from Gnumed.wxpython import gmPhraseWheel
54 from Gnumed.wxpython import gmFormWidgets
55 from Gnumed.wxpython import gmAllergyWidgets
56 from Gnumed.wxpython import gmDocumentWidgets
57
58
59 _log = logging.getLogger('gm.ui')
60
61
62
63
81
83 dbcfg = gmCfg.cCfgSQL()
84
85
86 default_db = dbcfg.get2 (
87 option = 'external.drug_data.default_source',
88 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
89 bias = 'workplace'
90 )
91
92
93 if default_db is None:
94 gmDispatcher.send('statustext', msg = _('No default drug database configured.'), beep = True)
95 configure_drug_data_source(parent = parent)
96 default_db = dbcfg.get2 (
97 option = 'external.drug_data.default_source',
98 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
99 bias = 'workplace'
100 )
101
102 if default_db is None:
103 gmGuiHelpers.gm_show_error (
104 aMessage = _('There is no default drug database configured.'),
105 aTitle = _('Jumping to drug database')
106 )
107 return None
108
109
110
111 try:
112 drug_db = gmMedication.drug_data_source_interfaces[default_db]()
113 except KeyError:
114
115 _log.error('faulty default drug data source configuration: %s', default_db)
116
117 configure_drug_data_source(parent = parent)
118 default_db = dbcfg.get2 (
119 option = 'external.drug_data.default_source',
120 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
121 bias = 'workplace'
122 )
123
124 try:
125 drug_db = gmMedication.drug_data_source_interfaces[default_db]()
126 except KeyError:
127 _log.error('still faulty default drug data source configuration: %s', default_db)
128 return None
129
130 pat = gmPerson.gmCurrentPatient()
131 if pat.connected:
132 drug_db.patient = pat
133
134 return drug_db
135
142
143
145
146 dbcfg = gmCfg.cCfgSQL()
147
148 ifap_cmd = dbcfg.get2 (
149 option = 'external.ifap-win.shell_command',
150 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
151 bias = 'workplace',
152 default = 'wine "C:\Ifapwin\WIAMDB.EXE"'
153 )
154 found, binary = gmShellAPI.detect_external_binary(ifap_cmd)
155 if not found:
156 gmDispatcher.send('statustext', msg = _('Cannot call IFAP via [%s].') % ifap_cmd)
157 return False
158 ifap_cmd = binary
159
160 if import_drugs:
161 transfer_file = os.path.expanduser(dbcfg.get2 (
162 option = 'external.ifap-win.transfer_file',
163 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
164 bias = 'workplace',
165 default = '~/.wine/drive_c/Ifapwin/ifap2gnumed.csv'
166 ))
167
168 try:
169 f = open(transfer_file, 'w+b').close()
170 except IOError:
171 _log.exception('Cannot create IFAP <-> GNUmed transfer file [%s]', transfer_file)
172 gmDispatcher.send('statustext', msg = _('Cannot create IFAP <-> GNUmed transfer file [%s].') % transfer_file)
173 return False
174
175 wx.BeginBusyCursor()
176 gmShellAPI.run_command_in_shell(command = ifap_cmd, blocking = import_drugs)
177 wx.EndBusyCursor()
178
179 if import_drugs:
180
181
182 try:
183 csv_file = open(transfer_file, 'rb')
184 except:
185 _log.exception('cannot access [%s]', fname)
186 csv_file = None
187
188 if csv_file is not None:
189 import csv
190 csv_lines = csv.DictReader (
191 csv_file,
192 fieldnames = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split(),
193 delimiter = ';'
194 )
195 pat = gmPerson.gmCurrentPatient()
196 emr = pat.get_emr()
197
198 epi = emr.add_episode(episode_name = _('Current medication'))
199 for line in csv_lines:
200 narr = u'%sx %s %s %s (\u2258 %s %s) von %s (%s)' % (
201 line['Packungszahl'].strip(),
202 line['Handelsname'].strip(),
203 line['Form'].strip(),
204 line[u'Packungsgr\xf6\xdfe'].strip(),
205 line['Abpackungsmenge'].strip(),
206 line['Einheit'].strip(),
207 line['Hersteller'].strip(),
208 line['PZN'].strip()
209 )
210 emr.add_clin_narrative(note = narr, soap_cat = 's', episode = epi)
211 csv_file.close()
212
213 return True
214
215
216
217
218
238
239 gmListWidgets.get_choices_from_list (
240 parent = parent,
241 msg = _('\nThe ATC codes as known to GNUmed.\n'),
242 caption = _('Showing ATC codes.'),
243 columns = [ u'ATC', _('Term'), _('Unit'), _(u'Route'), _('Comment'), _('Version'), _('Language') ],
244 single_selection = True,
245 refresh_callback = refresh
246 )
247
248
250
251 dlg = wx.FileDialog (
252 parent = None,
253 message = _('Choose an ATC import config file'),
254 defaultDir = os.path.expanduser(os.path.join('~', 'gnumed')),
255 defaultFile = '',
256 wildcard = "%s (*.conf)|*.conf|%s (*)|*" % (_('config files'), _('all files')),
257 style = wx.OPEN | wx.HIDE_READONLY | wx.FILE_MUST_EXIST
258 )
259
260 result = dlg.ShowModal()
261 if result == wx.ID_CANCEL:
262 return
263
264 cfg_file = dlg.GetPath()
265 dlg.Destroy()
266
267 conn = gmAuthWidgets.get_dbowner_connection(procedure = _('importing ATC reference data'))
268 if conn is None:
269 return False
270
271 wx.BeginBusyCursor()
272
273 if gmATC.atc_import(cfg_fname = cfg_file, conn = conn):
274 gmDispatcher.send(signal = 'statustext', msg = _('Successfully imported ATC reference data.'))
275 else:
276 gmDispatcher.send(signal = 'statustext', msg = _('Importing ATC reference data failed.'), beep = True)
277
278 wx.EndBusyCursor()
279 return True
280
281
282
284
286
287 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
288
289 query = u"""
290
291 SELECT DISTINCT ON (label)
292 atc_code,
293 label
294 FROM (
295
296 SELECT
297 code as atc_code,
298 (code || ': ' || term)
299 AS label
300 FROM ref.atc
301 WHERE
302 term %(fragment_condition)s
303 OR
304 code %(fragment_condition)s
305
306 UNION ALL
307
308 SELECT
309 atc_code,
310 (atc_code || ': ' || description)
311 AS label
312 FROM ref.consumable_substance
313 WHERE
314 description %(fragment_condition)s
315 OR
316 atc_code %(fragment_condition)s
317
318 UNION ALL
319
320 SELECT
321 atc_code,
322 (atc_code || ': ' || description || ' (' || preparation || ')')
323 AS label
324 FROM ref.branded_drug
325 WHERE
326 description %(fragment_condition)s
327 OR
328 atc_code %(fragment_condition)s
329
330 -- it would be nice to be able to include clin.vacc_indication but that's hard to do in SQL
331
332 ) AS candidates
333 WHERE atc_code IS NOT NULL
334 ORDER BY label
335 LIMIT 50"""
336
337 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
338 mp.setThresholds(1, 2, 4)
339
340 self.SetToolTipString(_('Select an ATC (Anatomical-Therapeutic-Chemical) code.'))
341 self.matcher = mp
342 self.selection_only = True
343
344
345
346
348
349 if parent is None:
350 parent = wx.GetApp().GetTopWindow()
351
352 def add_from_db(substance):
353 drug_db = get_drug_database(parent = parent)
354 if drug_db is None:
355 return False
356 drug_db.import_drugs()
357 return True
358
359 def edit(substance=None):
360 return edit_consumable_substance(parent = parent, substance = substance, single_entry = (substance is not None))
361
362 def delete(substance):
363 if substance.is_in_use_by_patients:
364 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete this substance. It is in use.'), beep = True)
365 return False
366
367 return gmMedication.delete_consumable_substance(substance = substance['pk'])
368
369 def refresh(lctrl):
370 substs = gmMedication.get_consumable_substances(order_by = 'description')
371 items = [ [
372 s['description'],
373 s['amount'],
374 s['unit'],
375 gmTools.coalesce(s['atc_code'], u''),
376 s['pk']
377 ] for s in substs ]
378 lctrl.set_string_items(items)
379 lctrl.set_data(substs)
380
381 msg = _('\nThese are the consumable substances registered with GNUmed.\n')
382
383 gmListWidgets.get_choices_from_list (
384 parent = parent,
385 msg = msg,
386 caption = _('Showing consumable substances.'),
387 columns = [_('Substance'), _('Amount'), _('Unit'), 'ATC', u'#'],
388 single_selection = True,
389 new_callback = edit,
390 edit_callback = edit,
391 delete_callback = delete,
392 refresh_callback = refresh,
393 left_extra_button = (_('Import'), _('Import consumable substances from a drug database.'), add_from_db)
394 )
395
396
398
399 if substance is not None:
400 if substance.is_in_use_by_patients:
401 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit this substance. It is in use.'), beep = True)
402 return False
403
404 ea = cConsumableSubstanceEAPnl(parent = parent, id = -1)
405 ea.data = substance
406 ea.mode = gmTools.coalesce(substance, 'new', 'edit')
407 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
408 dlg.SetTitle(gmTools.coalesce(substance, _('Adding new consumable substance'), _('Editing consumable substance')))
409 if dlg.ShowModal() == wx.ID_OK:
410 dlg.Destroy()
411 return True
412 dlg.Destroy()
413 return False
414
415
416 from Gnumed.wxGladeWidgets import wxgConsumableSubstanceEAPnl
417
419
437
438
439
440
441
442
443
444
446
447 validity = True
448
449 if self._TCTRL_substance.GetValue().strip() == u'':
450 validity = False
451 self.display_tctrl_as_valid(tctrl = self._TCTRL_substance, valid = False)
452 self._TCTRL_substance.SetFocus()
453 else:
454 self.display_tctrl_as_valid(tctrl = self._TCTRL_substance, valid = True)
455
456 try:
457 decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.'))
458 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
459 except (TypeError, decimal.InvalidOperation):
460 validity = False
461 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
462 self._TCTRL_amount.SetFocus()
463
464 if self._PRW_unit.GetValue().strip() == u'':
465 validity = False
466 self._PRW_unit.display_as_valid(valid = False)
467 self._TCTRL_substance.SetFocus()
468 else:
469 self._PRW_unit.display_as_valid(valid = True)
470
471 if validity is False:
472 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save consumable substance. Missing essential input.'))
473
474 return validity
475
477 subst = gmMedication.create_consumable_substance (
478 substance = self._TCTRL_substance.GetValue().strip(),
479 atc = self._PRW_atc.GetData(),
480 amount = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.')),
481 unit = gmTools.coalesce(self._PRW_unit.GetData(), self._PRW_unit.GetValue().strip(), function_initial = ('strip', None))
482 )
483 success, data = subst.save()
484 if not success:
485 err, msg = data
486 _log.error(err)
487 _log.error(msg)
488 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save consumable substance. %s') % msg, beep = True)
489 return False
490
491 self.data = subst
492 return True
493
495 self.data['description'] = self._TCTRL_substance.GetValue().strip()
496 self.data['atc_code'] = self._PRW_atc.GetData()
497 self.data['amount'] = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.'))
498 self.data['unit'] = gmTools.coalesce(self._PRW_unit.GetData(), self._PRW_unit.GetValue().strip(), function_initial = ('strip', None))
499 success, data = self.data.save()
500
501 if not success:
502 err, msg = data
503 _log.error(err)
504 _log.error(msg)
505 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save consumable substance. %s') % msg, beep = True)
506 return False
507
508 return True
509
511 self._TCTRL_substance.SetValue(u'')
512 self._TCTRL_amount.SetValue(u'')
513 self._PRW_unit.SetText(u'', None)
514 self._PRW_atc.SetText(u'', None)
515
516 self._TCTRL_substance.SetFocus()
517
525
527 self._refresh_as_new()
528
529
530
531
541
542 def delete(component):
543 if component.is_in_use_by_patients:
544 gmDispatcher.send(signal = 'statustext', msg = _('Cannot remove this component from the drug. It is in use.'), beep = True)
545 return False
546
547 return component.containing_drug.remove_component(substance = component['pk_component'])
548
549 def refresh(lctrl):
550 comps = gmMedication.get_drug_components()
551 items = [ [
552 u'%s%s' % (c['brand'], gmTools.coalesce(c['atc_brand'], u'', u' [%s]')),
553 u'%s%s' % (c['substance'], gmTools.coalesce(c['atc_substance'], u'', u' [%s]')),
554 u'%s %s' % (c['amount'], c['unit']),
555 c['preparation'],
556 gmTools.coalesce(c['external_code_brand'], u'', u'%%s [%s]' % c['external_code_type_brand']),
557 c['pk_component']
558 ] for c in comps ]
559 lctrl.set_string_items(items)
560 lctrl.set_data(comps)
561
562 msg = _('\nThese are the components in the drug brands known to GNUmed.\n')
563
564 gmListWidgets.get_choices_from_list (
565 parent = parent,
566 msg = msg,
567 caption = _('Showing drug brand components.'),
568 columns = [_('Brand'), _('Substance'), _('Strength'), _('Preparation'), _('Code'), u'#'],
569 single_selection = True,
570
571 edit_callback = edit,
572 delete_callback = delete,
573 refresh_callback = refresh
574 )
575
576
588
589
590 from Gnumed.wxGladeWidgets import wxgDrugComponentEAPnl
591
592 -class cDrugComponentEAPnl(wxgDrugComponentEAPnl.wxgDrugComponentEAPnl, gmEditArea.cGenericEditAreaMixin):
593
611
612
613
614
615
616
617
618
620 if self.data is not None:
621 if self.data['is_in_use']:
622 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit drug component. It is in use.'), beep = True)
623 return False
624
625 validity = True
626
627 if self._PRW_substance.GetData() is None:
628 validity = False
629 self._PRW_substance.display_as_valid(False)
630 else:
631 self._PRW_substance.display_as_valid(True)
632
633 val = self._TCTRL_amount.GetValue().strip().replace(',', u'.', 1)
634 try:
635 decimal.Decimal(val)
636 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
637 except:
638 validity = False
639 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
640
641 if self._PRW_unit.GetValue().strip() == u'':
642 validity = False
643 self._PRW_unit.display_as_valid(False)
644 else:
645 self._PRW_unit.display_as_valid(True)
646
647 if validity is False:
648 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save drug component. Invalid or missing essential input.'))
649
650 return validity
651
653
654 data = 1
655 data[''] = 1
656 data[''] = 1
657
658
659
660
661
662
663 return False
664 return True
665
667 self.data['pk_consumable_substance'] = self._PRW_substance.GetData()
668 self.data['amount'] = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', u'.', 1))
669 self.data['unit'] = self._PRW_unit.GetValue().strip()
670 return self.data.save()
671
681
683 self._TCTRL_brand.SetValue(u'%s (%s)' % (self.data['brand'], self.data['preparation']))
684 self._TCTRL_components.SetValue(u' / '.join(self.data.containing_drug['components']))
685 details = []
686 if self.data['atc_brand'] is not None:
687 details.append(u'ATC: %s' % self.data['atc_brand'])
688 if self.data['external_code_brand'] is not None:
689 details.append(u'%s: %s' % (self.data['external_code_type_brand'], self.data['external_code_brand']))
690 self._TCTRL_codes.SetValue(u'; '.join(details))
691
692 self._PRW_substance.SetText(self.data['substance'], self.data['pk_consumable_substance'])
693 self._TCTRL_amount.SetValue(u'%s' % self.data['amount'])
694 self._PRW_unit.SetText(self.data['unit'], self.data['unit'])
695
696 self._PRW_substance.SetFocus()
697
699
700
701
702 self._PRW_substance.SetText(u'', None)
703 self._TCTRL_amount.SetValue(u'')
704 self._PRW_unit.SetText(u'', None)
705
706 self._PRW_substance.SetFocus()
707
708
722
723
724
726
728
729 query = u"""
730 (
731 SELECT DISTINCT ON (list_label)
732 preparation AS data,
733 preparation AS list_label,
734 preparation AS field_label
735 FROM ref.branded_drug
736 WHERE preparation %(fragment_condition)s
737 ) UNION (
738 SELECT DISTINCT ON (list_label)
739 preparation AS data,
740 preparation AS list_label,
741 preparation AS field_label
742 FROM clin.substance_intake
743 WHERE preparation %(fragment_condition)s
744 )
745 ORDER BY list_label
746 LIMIT 30"""
747
748 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
749 mp.setThresholds(1, 2, 4)
750 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
751 self.SetToolTipString(_('The preparation (form) of the substance or brand.'))
752 self.matcher = mp
753 self.selection_only = False
754
755
771
772
773
774
776
777 if brand is not None:
778 if brand.is_in_use_by_patients:
779 gmGuiHelpers.gm_show_info (
780 aTitle = _('Managing components of a drug'),
781 aMessage = _(
782 'Cannot manage the components of the branded drug product\n'
783 '\n'
784 ' "%s" (%s)\n'
785 '\n'
786 'because it is currently taken by patients.\n'
787 ) % (brand['brand'], brand['preparation'])
788 )
789 return False
790
791 if parent is None:
792 parent = wx.GetApp().GetTopWindow()
793
794
795
796
797 if brand is None:
798 msg = _('Pick the substances which are components of this drug.')
799 right_col = _('Components of drug')
800 comp_substs = []
801 else:
802 right_col = u'%s (%s)' % (brand['brand'], brand['preparation'])
803 msg = _(
804 'Adjust the components of "%s"\n'
805 '\n'
806 'The drug must contain at least one component. Any given\n'
807 'substance can only be included once per drug.'
808 ) % right_col
809 comp_substs = [ c.substance for c in brand.components ]
810
811 substs = gmMedication.get_consumable_substances(order_by = 'description')
812 choices = [ u'%s %s %s' % (s['description'], s['amount'], s['unit']) for s in substs ]
813 picks = [ u'%s %s %s' % (c['description'], c['amount'], c['unit']) for c in comp_substs ]
814
815 picker = gmListWidgets.cItemPickerDlg (
816 parent,
817 -1,
818 title = _('Managing components of a drug ...'),
819 msg = msg
820 )
821 picker.set_columns(['Substances'], [right_col])
822 picker.set_choices(choices = choices, data = substs)
823 picker.set_picks(picks = picks, data = comp_substs)
824
825
826
827
828
829
830 btn_pressed = picker.ShowModal()
831 substs = picker.get_picks()
832 picker.Destroy()
833
834 if btn_pressed != wx.ID_OK:
835 return (False, None)
836
837 if brand is not None:
838 brand.set_substances_as_components(substances = substs)
839
840 return (True, substs)
841
843
844 if parent is None:
845 parent = wx.GetApp().GetTopWindow()
846
847 def add_from_db(brand):
848 drug_db = get_drug_database(parent = parent)
849 if drug_db is None:
850 return False
851 drug_db.import_drugs()
852 return True
853
854 def get_tooltip(brand=None):
855 tt = u'%s %s\n' % (brand['brand'], brand['preparation'])
856 tt += u'\n'
857 tt += u'%s%s%s\n' % (
858 gmTools.bool2subst(brand.is_vaccine, u'%s, ' % _('Vaccine'), u''),
859 u'%s, ' % gmTools.bool2subst(brand.is_in_use_by_patients, _('in use'), _('not in use')),
860 gmTools.bool2subst(brand['is_fake_brand'], _('fake'), u'')
861 )
862 tt += gmTools.coalesce(brand['atc'], u'', _('ATC: %s\n'))
863 tt += gmTools.coalesce(brand['external_code'], u'', u'%s: %%s\n' % brand['external_code_type'])
864 if brand['components'] is not None:
865 tt += u'- %s' % u'\n- '.join(brand['components'])
866 return tt
867
868 def edit(brand):
869 if brand is not None:
870 if brand.is_vaccine:
871 gmGuiHelpers.gm_show_info (
872 aTitle = _('Editing medication'),
873 aMessage = _(
874 'Cannot edit the medication\n'
875 '\n'
876 ' "%s" (%s)\n'
877 '\n'
878 'because it is a vaccine. Please edit it\n'
879 'from the vaccine management section !\n'
880 ) % (brand['brand'], brand['preparation'])
881 )
882 return False
883
884 return edit_branded_drug(parent = parent, branded_drug = brand, single_entry = True)
885
886 def delete(brand):
887 if brand.is_vaccine:
888 gmGuiHelpers.gm_show_info (
889 aTitle = _('Deleting medication'),
890 aMessage = _(
891 'Cannot delete the medication\n'
892 '\n'
893 ' "%s" (%s)\n'
894 '\n'
895 'because it is a vaccine. Please delete it\n'
896 'from the vaccine management section !\n'
897 ) % (brand['brand'], brand['preparation'])
898 )
899 return False
900 gmMedication.delete_branded_drug(brand = brand['pk_brand'])
901 return True
902
903 def new():
904 return edit_branded_drug(parent = parent, branded_drug = None, single_entry = False)
905
906 def refresh(lctrl):
907 drugs = gmMedication.get_branded_drugs()
908 items = [ [
909 u'%s%s' % (
910 d['brand'],
911 gmTools.bool2subst(d['is_fake_brand'], ' (%s)' % _('fake'), u'')
912 ),
913 d['preparation'],
914 gmTools.coalesce(d['atc'], u''),
915 gmTools.coalesce(d['components'], u''),
916 gmTools.coalesce(d['external_code'], u'', u'%%s [%s]' % d['external_code_type']),
917 d['pk_brand']
918 ] for d in drugs ]
919 lctrl.set_string_items(items)
920 lctrl.set_data(drugs)
921
922 msg = _('\nThese are the drug brands known to GNUmed.\n')
923
924 gmListWidgets.get_choices_from_list (
925 parent = parent,
926 msg = msg,
927 caption = _('Showing branded drugs.'),
928 columns = [_('Name'), _('Preparation'), _('ATC'), _('Components'), _('Code'), u'#'],
929 single_selection = True,
930 ignore_OK_button = ignore_OK_button,
931 refresh_callback = refresh,
932 new_callback = new,
933 edit_callback = edit,
934 delete_callback = delete,
935 list_tooltip_callback = get_tooltip,
936 left_extra_button = (_('Import'), _('Import substances and brands from a drug database.'), add_from_db)
937
938
939 )
940
941
943
944 if branded_drug is not None:
945 if branded_drug.is_in_use_by_patients:
946 gmGuiHelpers.gm_show_info (
947 aTitle = _('Editing drug'),
948 aMessage = _(
949 'Cannot edit the branded drug product\n'
950 '\n'
951 ' "%s" (%s)\n'
952 '\n'
953 'because it is currently taken by patients.\n'
954 ) % (branded_drug['brand'], branded_drug['preparation'])
955 )
956 return False
957
958 if parent is None:
959 parent = wx.GetApp().GetTopWindow()
960
961 def manage_substances(drug):
962 manage_consumable_substances(parent = parent)
963
964 ea = cBrandedDrugEAPnl(parent = parent, id = -1)
965 ea.data = branded_drug
966 ea.mode = gmTools.coalesce(branded_drug, 'new', 'edit')
967 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
968 dlg.SetTitle(gmTools.coalesce(branded_drug, _('Adding new drug brand'), _('Editing drug brand')))
969 dlg.left_extra_button = (
970 _('Substances'),
971 _('Manage consumable substances'),
972 manage_substances
973 )
974 if dlg.ShowModal() == wx.ID_OK:
975 dlg.Destroy()
976 return True
977 dlg.Destroy()
978 return False
979
980
981 from Gnumed.wxGladeWidgets import wxgBrandedDrugEAPnl
982
983 -class cBrandedDrugEAPnl(wxgBrandedDrugEAPnl.wxgBrandedDrugEAPnl, gmEditArea.cGenericEditAreaMixin):
984
1001
1002
1003
1004
1005
1006
1007
1008
1010
1011 if self.data is not None:
1012 if self.data.is_in_use_by_patients:
1013 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit drug brand. It is in use.'), beep = True)
1014 return False
1015
1016 validity = True
1017
1018 brand_name = self._PRW_brand.GetValue().strip()
1019 if brand_name == u'':
1020 validity = False
1021 self._PRW_brand.display_as_valid(False)
1022 else:
1023 self._PRW_brand.display_as_valid(True)
1024
1025 preparation = self._PRW_preparation.GetValue().strip()
1026 if preparation == u'':
1027 validity = False
1028 self._PRW_preparation.display_as_valid(False)
1029 else:
1030 self._PRW_preparation.display_as_valid(True)
1031
1032 if validity is True:
1033
1034 drug = gmMedication.get_drug_by_brand(brand_name = brand_name, preparation = preparation)
1035 if drug is not None:
1036 validity = False
1037 self._PRW_brand.display_as_valid(False)
1038 self._PRW_preparation.display_as_valid(False)
1039 gmGuiHelpers.gm_show_error (
1040 title = _('Checking brand data'),
1041 error = _(
1042 'The brand information you entered:\n'
1043 '\n'
1044 ' [%s %s]\n'
1045 '\n'
1046 'already exists as a drug product.'
1047 ) % (brand_name, preparation)
1048 )
1049
1050 else:
1051
1052 self._TCTRL_components.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BACKGROUND))
1053 if len(self.__component_substances) == 0:
1054 wants_empty = gmGuiHelpers.gm_show_question (
1055 title = _('Checking brand data'),
1056 question = _(
1057 'You have not selected any substances\n'
1058 'as drug components.\n'
1059 '\n'
1060 'Without components you will not be able to\n'
1061 'use this drug for documenting patient care.\n'
1062 '\n'
1063 'Are you sure you want to save\n'
1064 'it without components ?'
1065 )
1066 )
1067 if not wants_empty:
1068 validity = False
1069 self.display_ctrl_as_valid(ctrl = self._TCTRL_components, valid = False)
1070
1071 if validity is False:
1072 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save branded drug. Invalid or missing essential input.'))
1073
1074 return validity
1075
1077
1078 drug = gmMedication.create_branded_drug (
1079 brand_name = self._PRW_brand.GetValue().strip(),
1080 preparation = gmTools.coalesce (
1081 self._PRW_preparation.GetData(),
1082 self._PRW_preparation.GetValue()
1083 ).strip(),
1084 return_existing = True
1085 )
1086 drug['is_fake_brand'] = self._CHBOX_is_fake.GetValue()
1087 drug['atc'] = self._PRW_atc.GetData()
1088 code = self._TCTRL_external_code.GetValue().strip()
1089 if code != u'':
1090 drug['external_code'] = code
1091 drug['external_code_type'] = self._PRW_external_code_type.GetData().strip()
1092
1093 drug.save()
1094
1095 if len(self.__component_substances) > 0:
1096 drug.set_substances_as_components(substances = self.__component_substances)
1097
1098 self.data = drug
1099
1100 return True
1101
1103 self.data['brand'] = self._PRW_brand.GetValue().strip()
1104 self.data['preparation'] = gmTools.coalesce (
1105 self._PRW_preparation.GetData(),
1106 self._PRW_preparation.GetValue()
1107 ).strip()
1108 self.data['is_fake_brand'] = self._CHBOX_is_fake.GetValue()
1109 self.data['atc'] = self._PRW_atc.GetData()
1110 code = self._TCTRL_external_code.GetValue().strip()
1111 if code != u'':
1112 self.data['external_code'] = code
1113 self.data['external_code_type'] = self._PRW_external_code_type.GetData().strip()
1114 success, data = self.data.save()
1115 if not success:
1116 err, msg = data
1117 _log.error('problem saving')
1118 _log.error('%s', err)
1119 _log.error('%s', msg)
1120 return (success is True)
1121
1123 self._PRW_brand.SetText(u'', None)
1124 self._PRW_preparation.SetText(u'', None)
1125 self._CHBOX_is_fake.SetValue(False)
1126 self._TCTRL_components.SetValue(u'')
1127 self._PRW_atc.SetText(u'', None)
1128 self._TCTRL_external_code.SetValue(u'')
1129 self._PRW_external_code_type.SetText(u'', None)
1130
1131 self._PRW_brand.SetFocus()
1132
1133 self.__component_substances = []
1134
1136 self._refresh_as_new()
1137
1154
1155
1156
1170
1172
1174
1175 query = u"""
1176 SELECT
1177 pk
1178 AS data,
1179 (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', ''))
1180 AS list_label,
1181 (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', ''))
1182 AS field_label
1183 FROM ref.branded_drug
1184 WHERE description %(fragment_condition)s
1185 ORDER BY list_label
1186 LIMIT 50"""
1187
1188 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
1189 mp.setThresholds(2, 3, 4)
1190 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1191 self.SetToolTipString(_(
1192 'The brand name of the drug.\n'
1193 '\n'
1194 'Note: a brand name will need to be linked to\n'
1195 'one or more components before it can be used,\n'
1196 'except in the case of fake (generic) vaccines.'
1197 ))
1198 self.matcher = mp
1199 self.selection_only = False
1200
1201
1202
1203
1205
1207
1208 query = u"""
1209 SELECT DISTINCT ON (sched)
1210 schedule as sched,
1211 schedule
1212 FROM clin.substance_intake
1213 WHERE schedule %(fragment_condition)s
1214 ORDER BY sched
1215 LIMIT 50"""
1216
1217 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
1218 mp.setThresholds(1, 2, 4)
1219 mp.word_separators = '[ \t=+&:@]+'
1220 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1221 self.SetToolTipString(_('The schedule for taking this substance.'))
1222 self.matcher = mp
1223 self.selection_only = False
1224
1225
1227
1229
1230 query = u"""
1231 (
1232 SELECT DISTINCT ON (field_label)
1233 aim
1234 AS data,
1235 aim || ' (' || substance || ' ' || amount || ' ' || unit || ')'
1236 AS list_label,
1237 aim
1238 AS field_label
1239 FROM clin.v_substance_intakes
1240 WHERE
1241 aim %(fragment_condition)s
1242 %(ctxt_substance)s
1243 ) UNION (
1244 SELECT DISTINCT ON (field_label)
1245 aim
1246 AS data,
1247 aim || ' (' || substance || ' ' || amount || ' ' || unit || ')'
1248 AS list_label,
1249 aim
1250 AS field_label
1251 FROM clin.v_substance_intakes
1252 WHERE
1253 aim %(fragment_condition)s
1254 )
1255 ORDER BY list_label
1256 LIMIT 30"""
1257
1258 context = {'ctxt_substance': {
1259 'where_part': u'AND substance = %(substance)s',
1260 'placeholder': u'substance'
1261 }}
1262
1263 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query, context = context)
1264 mp.setThresholds(1, 2, 4)
1265
1266 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1267 self.SetToolTipString(_('The medical aim for consuming this substance.'))
1268 self.matcher = mp
1269 self.selection_only = False
1270
1271
1273
1274 if intake['is_currently_active']:
1275 intake['discontinued'] = gmDateTime.pydt_now_here()
1276 if intake['discontinue_reason'] is None:
1277 intake['discontinue_reason'] = u'%s %s' % (_('not tolerated:'), _('discontinued due to allergy or intolerance'))
1278 else:
1279 if not intake['discontinue_reason'].startswith(_('not tolerated:')):
1280 intake['discontinue_reason'] = u'%s %s' % (_('not tolerated:'), intake['discontinue_reason'])
1281 if not intake.save():
1282 return False
1283
1284 allg = intake.turn_into_allergy(encounter_id = emr.active_encounter['pk_encounter'])
1285
1286 brand = intake.containing_drug
1287 if brand is not None:
1288 comps = [ c['substance'] for c in brand.components ]
1289 if len(comps) > 1:
1290 gmGuiHelpers.gm_show_info (
1291 aTitle = _(u'Documented an allergy'),
1292 aMessage = _(
1293 u'An allergy was documented against the substance:\n'
1294 u'\n'
1295 u' [%s]\n'
1296 u'\n'
1297 u'This substance was taken with the multi-component brand:\n'
1298 u'\n'
1299 u' [%s (%s)]\n'
1300 u'\n'
1301 u'Note that ALL components of this brand were discontinued.'
1302 ) % (
1303 intake['substance'],
1304 intake['brand'],
1305 u' & '.join(comps)
1306 )
1307 )
1308
1309 if parent is None:
1310 parent = wx.GetApp().GetTopWindow()
1311
1312 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent = parent, id = -1)
1313 dlg.ShowModal()
1314
1315 return True
1316
1317
1319
1320 if parent is None:
1321 parent = wx.GetApp().GetTopWindow()
1322
1323 if emr is None:
1324 emr = gmPerson.gmCurrentPatient().emr
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343 def get_tooltip(intake=None):
1344 return intake.format(one_line = False, show_all_brand_components = True)
1345
1346 def refresh(lctrl):
1347 intakes = emr.get_current_substance_intakes (
1348 include_inactive = False,
1349 include_unapproved = True,
1350 order_by = u'substance, brand, started'
1351 )
1352 items = []
1353 for i in intakes:
1354 started = i.medically_formatted_start
1355 items.append ([
1356 u'%s%s %s %s %s%s' % (
1357 i['substance'],
1358 gmTools.coalesce(i['brand'], u'', u' (%s)'),
1359 i['amount'],
1360 i['unit'],
1361 i['preparation'],
1362 gmTools.coalesce(i['external_code_brand'], u'', u' [%s::%s]' % (i['external_code_type_brand'], i['external_code_brand']))
1363 ),
1364 u'%s%s%s' % (
1365 started,
1366 gmTools.coalesce(i['schedule'], u'', u' %%s %s' % gmTools.u_right_arrow),
1367 gmTools.coalesce(i['duration'], u'', u' %s')
1368 ),
1369 u'%s' % (
1370 gmTools.bool2subst (
1371 i['intake_is_approved_of'],
1372 u'',
1373 _('disapproved')
1374 )
1375 )
1376 ])
1377 lctrl.set_string_items(items)
1378 lctrl.set_data(intakes)
1379
1380 msg = _('Substances consumed by the patient:')
1381
1382 return gmListWidgets.get_choices_from_list (
1383 parent = parent,
1384 msg = msg,
1385 caption = _('Showing consumable substances.'),
1386 columns = [ _('Intake'), _('Application'), _('Status') ],
1387 single_selection = False,
1388
1389
1390
1391 refresh_callback = refresh,
1392 list_tooltip_callback = get_tooltip
1393
1394 )
1395
1396
1397 from Gnumed.wxGladeWidgets import wxgCurrentMedicationEAPnl
1398
1399 -class cSubstanceIntakeEAPnl(wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl, gmEditArea.cGenericEditAreaMixin):
1400
1420
1432
1434 curr_pat = gmPerson.gmCurrentPatient()
1435 emr = curr_pat.emr
1436
1437 state = emr.allergy_state
1438 if state['last_confirmed'] is None:
1439 confirmed = _('never')
1440 else:
1441 confirmed = gmDateTime.pydt_strftime(state['last_confirmed'], '%Y %b %d')
1442 msg = _(u'%s, last confirmed %s\n') % (state.state_string, confirmed)
1443 msg += gmTools.coalesce(state['comment'], u'', _('Comment (%s): %%s\n') % state['modified_by'])
1444
1445 tt = u''
1446
1447 allgs = emr.get_allergies()
1448 if len(allgs) > 0:
1449 msg += u'\n'
1450 for allergy in allgs:
1451 msg += u'%s: %s (%s)\n' % (
1452 allergy['descriptor'],
1453 allergy['l10n_type'],
1454 gmTools.bool2subst(allergy['definite'], _('definite'), _('suspected'), u'?')
1455 )
1456 tt += u'%s: %s\n' % (
1457 allergy['descriptor'],
1458 gmTools.coalesce(allergy['reaction'], _('reaction not recorded'))
1459 )
1460
1461 if len(allgs) > 0:
1462 msg += u'\n'
1463 tt += u'\n'
1464
1465 gfr = emr.get_most_recent_results(loinc = gmLOINC.LOINC_gfr_quantity, no_of_results = 1)
1466 if gfr is None:
1467 self.calc.patient = curr_pat
1468 gfr = self.calc.eGFR
1469 if gfr.numeric_value is None:
1470 msg += _('GFR: unknown')
1471 else:
1472 msg += gfr.message
1473 tt += gfr.format (
1474 left_margin = 0,
1475 width = 50,
1476 eol = u'\n',
1477 with_formula = True,
1478 with_warnings = True,
1479 with_variables = False,
1480 with_sub_results = True,
1481 return_list = False
1482 )
1483 else:
1484 msg += u'%s: %s %s (%s)\n' % (
1485 gfr['unified_abbrev'],
1486 gfr['unified_val'],
1487 gmTools.coalesce(gfr['abnormality_indicator'], u'', u' (%s)'),
1488 gmDateTime.pydt_strftime (
1489 gfr['clin_when'],
1490 format = '%Y %b %d'
1491 )
1492 )
1493 tt += _('GFR reported by path lab')
1494
1495 self._LBL_allergies.SetLabel(msg)
1496 self._LBL_allergies.SetToolTipString(tt)
1497
1498
1499
1501
1502 validity = True
1503
1504 has_component = (self._PRW_component.GetData() is not None)
1505 has_substance = (self._PRW_substance.GetValue().strip() != u'')
1506
1507 self._PRW_component.display_as_valid(True)
1508
1509
1510 if self.mode == 'new':
1511 msg = _(
1512 'The patient is already taking\n'
1513 '\n'
1514 ' %s\n'
1515 '\n'
1516 'You will want to adjust the schedule\n'
1517 'rather than document the intake twice.'
1518 )
1519 title = _('Adding substance intake entry')
1520 if has_component:
1521 emr = gmPerson.gmCurrentPatient().get_emr()
1522 if emr.substance_intake_exists(pk_component = self._PRW_component.GetData()):
1523 gmGuiHelpers.gm_show_warning (
1524 aTitle = title,
1525 aMessage = msg % self._PRW_component.GetValue().strip()
1526 )
1527 self._PRW_component.display_as_valid(False)
1528 validity = False
1529 pk_substance = self._PRW_substance.GetData()
1530 if pk_substance is not None:
1531 emr = gmPerson.gmCurrentPatient().get_emr()
1532 if emr.substance_intake_exists(pk_substance = pk_substance):
1533 gmGuiHelpers.gm_show_warning (
1534 aTitle = title,
1535 aMessage = msg % self._PRW_substance.GetValue().strip()
1536 )
1537 self._PRW_substance.display_as_valid(False)
1538 validity = False
1539
1540
1541 if (has_component is False) and (has_substance is False):
1542 self._PRW_substance.display_as_valid(False)
1543 self._PRW_component.display_as_valid(False)
1544 validity = False
1545 else:
1546 self._PRW_substance.display_as_valid(True)
1547
1548
1549 if not has_component:
1550 if self._PRW_preparation.GetValue().strip() == u'':
1551 self._PRW_preparation.display_as_valid(False)
1552 validity = False
1553 else:
1554 self._PRW_preparation.display_as_valid(True)
1555
1556
1557 if self._CHBOX_approved.IsChecked():
1558 if self._PRW_episode.GetValue().strip() == u'':
1559 self._PRW_episode.display_as_valid(False)
1560 validity = False
1561 else:
1562 self._PRW_episode.display_as_valid(True)
1563
1564 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
1565 self._PRW_duration.display_as_valid(True)
1566 else:
1567 if self._PRW_duration.GetData() is None:
1568
1569 if gmDateTime.str2interval(self._PRW_duration.GetValue()) is None:
1570 self._PRW_duration.display_as_valid(False)
1571 validity = False
1572
1573 else:
1574 self._PRW_duration.display_as_valid(True)
1575
1576 else:
1577 self._PRW_duration.display_as_valid(True)
1578
1579
1580 started = self._DP_started.GetData()
1581 if started is None:
1582 self._DP_started.display_as_valid(False)
1583 validity = False
1584 else:
1585 self._DP_started.display_as_valid(True)
1586
1587 if validity is False:
1588 gmDispatcher.send(signal = 'statustext', msg = _('Input incomplete/invalid for saving as substance intake.'))
1589
1590
1591 discontinued = self._DP_discontinued.GetData()
1592 if discontinued is not None:
1593 now = gmDateTime.pydt_now_here().replace (
1594 hour = 23,
1595 minute = 59,
1596 second = 59,
1597 microsecond = 111111
1598 )
1599
1600 if discontinued > now:
1601 self._DP_discontinued.display_as_valid(False)
1602 validity = False
1603 gmDispatcher.send(signal = 'statustext', msg = _('Discontinued (%s) in the future (now: %s)!') % (discontinued, now))
1604 else:
1605 started = started.replace (
1606 hour = 0,
1607 minute = 0,
1608 second = 0,
1609 microsecond = 1
1610 )
1611
1612 if started > discontinued:
1613 self._DP_started.display_as_valid(False)
1614 self._DP_discontinued.display_as_valid(False)
1615 validity = False
1616 gmDispatcher.send(signal = 'statustext', msg = _('Discontinued (%s) before started (%s) !') % (discontinued, started))
1617 else:
1618 self._DP_started.display_as_valid(True)
1619 self._DP_discontinued.display_as_valid(True)
1620
1621 return validity
1622
1624
1625 epi = self._PRW_episode.GetData()
1626 if epi is None:
1627
1628 epi = self._PRW_episode.GetData(can_create = True, is_open = True)
1629
1630 emr = gmPerson.gmCurrentPatient().get_emr()
1631 if self._PRW_substance.GetData() is None:
1632
1633 intake = emr.add_substance_intake (
1634 pk_component = self._PRW_component.GetData(),
1635 episode = epi
1636 )
1637 else:
1638 intake = emr.add_substance_intake (
1639 pk_substance = self._PRW_substance.GetData(),
1640 episode = epi,
1641 preparation = self._PRW_preparation.GetValue().strip()
1642 )
1643
1644 if intake is None:
1645 gmDispatcher.send('statustext', msg = _('Cannot add duplicate of (maybe inactive) substance intake.'), beep = True)
1646 return False
1647
1648 intake['started'] = self._DP_started.GetData()
1649 intake['discontinued'] = self._DP_discontinued.GetData()
1650 if intake['discontinued'] is None:
1651 intake['discontinue_reason'] = None
1652 else:
1653 intake['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
1654 intake['schedule'] = self._PRW_schedule.GetValue().strip()
1655 intake['aim'] = self._PRW_aim.GetValue().strip()
1656 intake['notes'] = self._PRW_notes.GetValue().strip()
1657 intake['is_long_term'] = self._CHBOX_long_term.IsChecked()
1658 intake['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
1659 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
1660 intake['duration'] = None
1661 else:
1662 if self._PRW_duration.GetData() is None:
1663 intake['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
1664 else:
1665 intake['duration'] = self._PRW_duration.GetData()
1666 intake.save()
1667
1668 self.data = intake
1669
1670 return True
1671
1673
1674
1675 self.data['started'] = self._DP_started.GetData()
1676 self.data['discontinued'] = self._DP_discontinued.GetData()
1677 if self.data['discontinued'] is None:
1678 self.data['discontinue_reason'] = None
1679 else:
1680 self.data['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
1681 self.data['schedule'] = self._PRW_schedule.GetValue()
1682 self.data['is_long_term'] = self._CHBOX_long_term.IsChecked()
1683 self.data['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
1684 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
1685 self.data['duration'] = None
1686 else:
1687 if self._PRW_duration.GetData() is None:
1688 self.data['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
1689 else:
1690 self.data['duration'] = self._PRW_duration.GetData()
1691
1692
1693 self.data['preparation'] = self._PRW_preparation.GetValue()
1694
1695
1696 self.data['aim'] = self._PRW_aim.GetValue()
1697 self.data['notes'] = self._PRW_notes.GetValue()
1698 epi = self._PRW_episode.GetData()
1699 if epi is None:
1700
1701 epi = self._PRW_episode.GetData(can_create = True, is_open = True)
1702 self.data['pk_episode'] = epi
1703
1704 self.data.save()
1705
1706 return True
1707
1709 self._PRW_component.SetText(u'', None)
1710 self._LBL_component.Enable(True)
1711 self._PRW_component.Enable(True)
1712 self._TCTRL_brand_ingredients.SetValue(u'')
1713 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1714
1715 self._LBL_or.Enable(True)
1716
1717 self._PRW_substance.SetText(u'', None)
1718 self._PRW_substance.Enable(True)
1719
1720 self._PRW_preparation.SetText(u'', None)
1721 self._PRW_preparation.Enable(True)
1722
1723 self._PRW_schedule.SetText(u'', None)
1724 self._PRW_duration.SetText(u'', None)
1725 self._PRW_aim.SetText(u'', None)
1726 self._PRW_notes.SetText(u'', None)
1727 self._PRW_episode.SetText(u'', None)
1728
1729 self._CHBOX_long_term.SetValue(False)
1730 self._CHBOX_approved.SetValue(True)
1731
1732 self._DP_started.SetData(gmDateTime.pydt_now_here())
1733 self._DP_discontinued.SetData(None)
1734 self._PRW_discontinue_reason.SetValue(u'')
1735
1736 self.__refresh_allergies()
1737
1738 self._PRW_component.SetFocus()
1739
1741
1742 self._TCTRL_brand_ingredients.SetValue(u'')
1743 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1744
1745 if self.data['pk_brand'] is None:
1746 self.__refresh_from_existing_substance()
1747 else:
1748 self.__refresh_from_existing_component()
1749
1750
1751 self._LBL_component.Enable(False)
1752 self._PRW_component.Enable(False)
1753 self._LBL_or.Enable(False)
1754 self._PRW_substance.Enable(False)
1755
1756 if self.data['is_long_term']:
1757 self._CHBOX_long_term.SetValue(True)
1758 self._PRW_duration.Enable(False)
1759 self._PRW_duration.SetText(gmTools.u_infinity, None)
1760 self._BTN_discontinued_as_planned.Enable(False)
1761 else:
1762 self._CHBOX_long_term.SetValue(False)
1763 self._PRW_duration.Enable(True)
1764 self._BTN_discontinued_as_planned.Enable(True)
1765 self._PRW_duration.SetData(self.data['duration'])
1766
1767
1768
1769
1770 self._PRW_aim.SetText(gmTools.coalesce(self.data['aim'], u''), self.data['aim'])
1771 self._PRW_notes.SetText(gmTools.coalesce(self.data['notes'], u''), self.data['notes'])
1772 self._PRW_episode.SetData(self.data['pk_episode'])
1773 self._PRW_schedule.SetText(gmTools.coalesce(self.data['schedule'], u''), self.data['schedule'])
1774
1775 self._CHBOX_approved.SetValue(self.data['intake_is_approved_of'])
1776
1777 self._DP_started.SetData(self.data['started'])
1778 self._DP_discontinued.SetData(self.data['discontinued'])
1779 self._PRW_discontinue_reason.SetValue(gmTools.coalesce(self.data['discontinue_reason'], u''))
1780 if self.data['discontinued'] is not None:
1781 self._PRW_discontinue_reason.Enable()
1782
1783 self.__refresh_allergies()
1784
1785 self._PRW_schedule.SetFocus()
1786
1788 self._LBL_component.Enable(False)
1789 self._PRW_component.Enable(False)
1790 self._PRW_component.SetText(u'', None)
1791 self._PRW_component.display_as_valid(True)
1792
1793 self._LBL_or.Enable(False)
1794
1795
1796
1797
1798
1799 self._PRW_substance.Enable(False)
1800 self._PRW_substance.SetText (
1801 u'%s %s %s' % (self.data['substance'], self.data['amount'], self.data['unit']),
1802 self.data['pk_substance']
1803 )
1804
1805 self._PRW_preparation.SetText(self.data['preparation'], self.data['preparation'])
1806
1807 self._PRW_preparation.Enable(True)
1808 self._PRW_preparation.Enable(False)
1809
1811 self._LBL_component.Enable(True)
1812 self._PRW_component.Enable(True)
1813 self._PRW_component.SetText (
1814 u'%s %s %s (%s)' % (self.data['substance'], self.data['amount'], self.data['unit'], self.data['brand']),
1815 self.data['pk_drug_component']
1816 )
1817
1818 brand = gmMedication.cBrandedDrug(aPK_obj = self.data['pk_brand'])
1819 if brand['components'] is not None:
1820 self._TCTRL_brand_ingredients.SetValue(u'; '.join(brand['components']))
1821 tt = u'%s:\n\n- %s' % (
1822 self.data['brand'],
1823 u'\n- '.join(brand['components'])
1824 )
1825 self._TCTRL_brand_ingredients.SetToolTipString(tt)
1826
1827 self._LBL_or.Enable(False)
1828 self._LBL_substance.Enable(False)
1829 self._PRW_substance.SetText(u'', None)
1830 self._PRW_substance.display_as_valid(True)
1831
1832 self._PRW_preparation.SetText(self.data['preparation'], self.data['preparation'])
1833 self._PRW_preparation.Enable(False)
1834
1836 self._refresh_as_new()
1837
1838 self._PRW_episode.SetData(self.data['pk_episode'])
1839 self._DP_started.SetData(self.data['started'])
1840
1841 self._PRW_component.SetFocus()
1842
1843
1844
1846 if self._PRW_component.GetData() is None:
1847 self._LBL_or.Enable(True)
1848 self._PRW_component.SetText(u'', None)
1849 self._LBL_substance.Enable(True)
1850 self._PRW_substance.Enable(True)
1851 self._LBL_preparation.Enable(True)
1852 self._PRW_preparation.Enable(True)
1853
1854 self._TCTRL_brand_ingredients.SetValue(u'')
1855 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1856 else:
1857 self._LBL_or.Enable(False)
1858 self._LBL_substance.Enable(False)
1859 self._PRW_substance.SetText(u'', None)
1860 self._PRW_substance.display_as_valid(True)
1861 self._PRW_substance.Enable(False)
1862 self._LBL_preparation.Enable(False)
1863 self._PRW_preparation.Enable(False)
1864 comp = gmMedication.cDrugComponent(aPK_obj = self._PRW_component.GetData())
1865 self._PRW_preparation.SetText(comp['preparation'], comp['preparation'])
1866 brand = comp.containing_drug
1867 if brand['components'] is not None:
1868 self._TCTRL_brand_ingredients.SetValue(u'; '.join(brand['components']))
1869 tt = u'%s:\n\n- %s' % (
1870 brand['brand'],
1871 u'\n- '.join(brand['components'])
1872 )
1873 self._TCTRL_brand_ingredients.SetToolTipString(tt)
1874
1876 if self._PRW_substance.GetData() is None:
1877 self._LBL_or.Enable(True)
1878 self._LBL_component.Enable(True)
1879 self._PRW_component.Enable(True)
1880 self._PRW_substance.SetText(u'', None)
1881 else:
1882 self._LBL_or.Enable(False)
1883 self._LBL_component.Enable(False)
1884 self._PRW_component.SetText(u'', None)
1885 self._PRW_component.display_as_valid(True)
1886 self._PRW_component.Enable(False)
1887 self._LBL_preparation.Enable(True)
1888 self._PRW_preparation.Enable(True)
1889 self._TCTRL_brand_ingredients.SetValue(u'')
1890 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1891
1893
1894
1895
1896
1897
1898 subst = self._PRW_component.GetValue().strip()
1899 if subst != u'':
1900 comp = self._PRW_component.GetData(as_instance = True)
1901 if comp is None:
1902 self._PRW_aim.set_context(context = u'substance', val = subst)
1903 return
1904 self._PRW_aim.set_context(context = u'substance', val = comp['substance'])
1905 return
1906
1907 subst = self._PRW_substance.GetValue().strip()
1908 if subst == u'':
1909 self._PRW_aim.unset_context(context = u'substance')
1910 return
1911 comp = self._PRW_substance.GetData(as_instance = True)
1912 if comp is None:
1913 self._PRW_aim.set_context(context = u'substance', val = subst)
1914 return
1915 self._PRW_aim.set_context(context = u'substance', val = comp['description'])
1916
1918 if self._DP_discontinued.GetData() is None:
1919 self._PRW_discontinue_reason.Enable(False)
1920 else:
1921 self._PRW_discontinue_reason.Enable(True)
1922
1925
1928
1931
1943
1973
1975 if self._CHBOX_long_term.IsChecked() is True:
1976 self._PRW_duration.Enable(False)
1977 self._BTN_discontinued_as_planned.Enable(False)
1978 self._PRW_discontinue_reason.Enable(False)
1979 else:
1980 self._PRW_duration.Enable(True)
1981 self._BTN_discontinued_as_planned.Enable(True)
1982 self._PRW_discontinue_reason.Enable(True)
1983
1984 self.__refresh_allergies()
1985
1995
1996
1998
1999 subst = gmMedication.cSubstanceIntakeEntry(aPK_obj = substance)
2000 msg = _(
2001 '\n'
2002 '[%s]\n'
2003 '\n'
2004 'It may be prudent to edit (before deletion) the details\n'
2005 'of this substance intake entry so as to leave behind\n'
2006 'some indication of why it was deleted.\n'
2007 ) % subst.format()
2008
2009 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
2010 parent,
2011 -1,
2012 caption = _('Deleting medication / substance intake'),
2013 question = msg,
2014 button_defs = [
2015 {'label': _('&Edit'), 'tooltip': _('Allow editing of substance intake entry before deletion.'), 'default': True},
2016 {'label': _('&Delete'), 'tooltip': _('Delete immediately without editing first.')},
2017 {'label': _('&Cancel'), 'tooltip': _('Abort. Do not delete or edit substance intake entry.')}
2018 ]
2019 )
2020
2021 edit_first = dlg.ShowModal()
2022 dlg.Destroy()
2023
2024 if edit_first == wx.ID_CANCEL:
2025 return
2026
2027 if edit_first == wx.ID_YES:
2028 edit_intake_of_substance(parent = parent, substance = subst)
2029 delete_it = gmGuiHelpers.gm_show_question (
2030 aMessage = _('Now delete substance intake entry ?'),
2031 aTitle = _('Deleting medication / substance intake')
2032 )
2033 else:
2034 delete_it = True
2035
2036 if not delete_it:
2037 return
2038
2039 gmMedication.delete_substance_intake(substance = substance)
2040
2055
2056
2057
2058
2086
2088
2089 if parent is None:
2090 parent = wx.GetApp().GetTopWindow()
2091
2092
2093 dbcfg = gmCfg.cCfgSQL()
2094 option = u'form_templates.medication_list'
2095
2096 template = dbcfg.get2 (
2097 option = option,
2098 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
2099 bias = 'user'
2100 )
2101
2102 if template is None:
2103 template = configure_medication_list_template(parent = parent)
2104 if template is None:
2105 gmGuiHelpers.gm_show_error (
2106 aMessage = _('There is no medication list template configured.'),
2107 aTitle = _('Printing medication list')
2108 )
2109 return False
2110 else:
2111 try:
2112 name, ver = template.split(u' - ')
2113 except:
2114 _log.exception('problem splitting medication list template name [%s]', template)
2115 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading medication list template.'), beep = True)
2116 return False
2117 template = gmForms.get_form_template(name_long = name, external_version = ver)
2118 if template is None:
2119 gmGuiHelpers.gm_show_error (
2120 aMessage = _('Cannot load medication list template [%s - %s]') % (name, ver),
2121 aTitle = _('Printing medication list')
2122 )
2123 return False
2124
2125
2126 meds_list = gmFormWidgets.generate_form_from_template (
2127 parent = parent,
2128 template = template,
2129 edit = False
2130 )
2131 if meds_list is None:
2132 return False
2133
2134
2135 return gmFormWidgets.act_on_generated_forms (
2136 parent = parent,
2137 forms = [meds_list],
2138 jobtype = 'medication_list',
2139
2140 episode_name = gmMedication.DEFAULT_MEDICATION_HISTORY_EPISODE,
2141 progress_note = _('generated medication list document'),
2142 review_copy_as_normal = True
2143 )
2144
2145
2174
2176
2177 if parent is None:
2178 parent = wx.GetApp().GetTopWindow()
2179
2180 dbcfg = gmCfg.cCfgSQL()
2181 option = u'form_templates.prescription'
2182 template_name = dbcfg.get2 (
2183 option = option,
2184 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
2185 bias = 'user'
2186 )
2187
2188 if template_name is None:
2189 template = configure_prescription_template(parent = parent)
2190 if template is None:
2191 gmGuiHelpers.gm_show_error (
2192 aMessage = _('There is no prescription template configured.'),
2193 aTitle = _('Printing prescription')
2194 )
2195 return None
2196 return template
2197
2198 try:
2199 name, ver = template_name.split(u' - ')
2200 except:
2201 _log.exception('problem splitting prescription template name [%s]', template_name)
2202 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading prescription template.'), beep = True)
2203 return False
2204 template = gmForms.get_form_template(name_long = name, external_version = ver)
2205 if template is None:
2206 gmGuiHelpers.gm_show_error (
2207 aMessage = _('Cannot load prescription template [%s - %s]') % (name, ver),
2208 aTitle = _('Printing prescription')
2209 )
2210 return None
2211 return template
2212
2239
2240
2268
2269
2271
2272 if len(prescribed_drugs) == 0:
2273 return
2274
2275 curr_brands = [ i['pk_brand'] for i in emr.get_current_substance_intakes() if i['pk_brand'] is not None ]
2276 new_drugs = []
2277 for drug in prescribed_drugs:
2278 if drug['pk_brand'] not in curr_brands:
2279 new_drugs.append(drug)
2280
2281 if len(new_drugs) == 0:
2282 return
2283
2284 if parent is None:
2285 parent = wx.GetApp().GetTopWindow()
2286
2287 dlg = gmListWidgets.cItemPickerDlg (
2288 parent,
2289 -1,
2290 msg = _(
2291 'These brands have been prescribed but are not listed\n'
2292 'in the current medication list of this patient.\n'
2293 '\n'
2294 'Please select those you want added to the medication list.'
2295 )
2296 )
2297 dlg.set_columns (
2298 columns = [_('Newly prescribed drugs')],
2299 columns_right = [_('Add to medication list')]
2300 )
2301 choices = [ (u'%s %s (%s)' % (d['brand'], d['preparation'], u'; '.join(d['components']))) for d in new_drugs ]
2302 dlg.set_choices (
2303 choices = choices,
2304 data = new_drugs
2305 )
2306 dlg.ShowModal()
2307 drugs2add = dlg.get_picks()
2308 dlg.Destroy()
2309
2310 if drugs2add is None:
2311 return
2312
2313 if len(drugs2add) == 0:
2314 return
2315
2316 for drug in drugs2add:
2317
2318 intake = emr.add_substance_intake (
2319 pk_component = drug['pk_components'][0],
2320 episode = emr.add_episode(episode_name = gmMedication.DEFAULT_MEDICATION_HISTORY_EPISODE)['pk_episode'],
2321 )
2322 if intake is None:
2323 continue
2324 intake['intake_is_approved_of'] = True
2325 intake.save()
2326
2327 return
2328
2330 """A grid class for displaying current substance intake.
2331
2332 - does NOT listen to the currently active patient
2333 - thereby it can display any patient at any time
2334 """
2336
2337 wx.grid.Grid.__init__(self, *args, **kwargs)
2338
2339 self.__patient = None
2340 self.__row_data = {}
2341 self.__prev_row = None
2342 self.__prev_tooltip_row = None
2343 self.__prev_cell_0 = None
2344 self.__grouping_mode = u'issue'
2345 self.__filter_show_unapproved = True
2346 self.__filter_show_inactive = True
2347
2348 self.__grouping2col_labels = {
2349 u'issue': [
2350 _('Health issue'),
2351 _('Substance'),
2352 _('Strength'),
2353 _('Schedule'),
2354 _('Started'),
2355 _('Duration / Until'),
2356 _('Brand'),
2357 _('Advice')
2358 ],
2359 u'brand': [
2360 _('Brand'),
2361 _('Schedule'),
2362 _('Substance'),
2363 _('Strength'),
2364 _('Started'),
2365 _('Duration / Until'),
2366 _('Health issue'),
2367 _('Advice')
2368 ],
2369 u'episode': [
2370 _('Episode'),
2371 _('Substance'),
2372 _('Strength'),
2373 _('Schedule'),
2374 _('Started'),
2375 _('Duration / Until'),
2376 _('Brand'),
2377 _('Advice')
2378 ]
2379 }
2380
2381 self.__grouping2order_by_clauses = {
2382 u'issue': u'pk_health_issue nulls first, substance, started',
2383 u'episode': u'pk_health_issue nulls first, episode, substance, started',
2384 u'brand': u'brand nulls last, substance, started'
2385 }
2386
2387 self.__init_ui()
2388 self.__register_events()
2389
2390
2391
2393
2394 sel_block_top_left = self.GetSelectionBlockTopLeft()
2395 sel_block_bottom_right = self.GetSelectionBlockBottomRight()
2396 sel_cols = self.GetSelectedCols()
2397 sel_rows = self.GetSelectedRows()
2398
2399 selected_cells = []
2400
2401
2402 selected_cells += self.GetSelectedCells()
2403
2404
2405 selected_cells += list (
2406 (row, col)
2407 for row in sel_rows
2408 for col in xrange(self.GetNumberCols())
2409 )
2410
2411
2412 selected_cells += list (
2413 (row, col)
2414 for row in xrange(self.GetNumberRows())
2415 for col in sel_cols
2416 )
2417
2418
2419 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()):
2420 selected_cells += [
2421 (row, col)
2422 for row in xrange(top_left[0], bottom_right[0] + 1)
2423 for col in xrange(top_left[1], bottom_right[1] + 1)
2424 ]
2425
2426 return set(selected_cells)
2427
2429 rows = {}
2430
2431 for row, col in self.get_selected_cells():
2432 rows[row] = True
2433
2434 return rows.keys()
2435
2438
2440
2441 self.empty_grid()
2442
2443 if self.__patient is None:
2444 return
2445
2446 emr = self.__patient.get_emr()
2447 meds = emr.get_current_substance_intakes (
2448 order_by = self.__grouping2order_by_clauses[self.__grouping_mode],
2449 include_unapproved = self.__filter_show_unapproved,
2450 include_inactive = self.__filter_show_inactive
2451 )
2452 if not meds:
2453 return
2454
2455 self.BeginBatch()
2456
2457
2458 labels = self.__grouping2col_labels[self.__grouping_mode]
2459 if self.__filter_show_unapproved:
2460 self.AppendCols(numCols = len(labels) + 1)
2461 else:
2462 self.AppendCols(numCols = len(labels))
2463 for col_idx in range(len(labels)):
2464 self.SetColLabelValue(col_idx, labels[col_idx])
2465 if self.__filter_show_unapproved:
2466
2467 self.SetColLabelValue(len(labels), u'')
2468 self.SetColSize(len(labels), 40)
2469
2470 self.AppendRows(numRows = len(meds))
2471
2472
2473 for row_idx in range(len(meds)):
2474 med = meds[row_idx]
2475 self.__row_data[row_idx] = med
2476
2477 if med['is_currently_active'] is True:
2478 atcs = []
2479 if med['atc_substance'] is not None:
2480 atcs.append(med['atc_substance'])
2481
2482
2483
2484 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (med['substance'],))
2485 if allg not in [None, False]:
2486 attr = self.GetOrCreateCellAttr(row_idx, 0)
2487 if allg['type'] == u'allergy':
2488 attr.SetTextColour('red')
2489 else:
2490
2491
2492
2493 attr.SetTextColour('magenta')
2494 self.SetRowAttr(row_idx, attr)
2495 else:
2496 attr = self.GetOrCreateCellAttr(row_idx, 0)
2497 attr.SetTextColour('grey')
2498 self.SetRowAttr(row_idx, attr)
2499
2500 if self.__grouping_mode == u'episode':
2501 if med['pk_episode'] is None:
2502 self.__prev_cell_0 = None
2503 epi = gmTools.u_diameter
2504 else:
2505 if self.__prev_cell_0 == med['episode']:
2506 epi = u''
2507 else:
2508 self.__prev_cell_0 = med['episode']
2509 epi = gmTools.coalesce(med['episode'], u'')
2510 self.SetCellValue(row_idx, 0, gmTools.wrap(text = epi, width = 40))
2511
2512 self.SetCellValue(row_idx, 1, med['substance'])
2513 self.SetCellValue(row_idx, 2, u'%s %s' % (med['amount'], med['unit']))
2514 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], u''))
2515 self.SetCellValue(row_idx, 4, med.medically_formatted_start)
2516
2517 if med['is_long_term']:
2518 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
2519 else:
2520 if med['discontinued'] is None:
2521 if med['duration'] is None:
2522 self.SetCellValue(row_idx, 5, u'')
2523 else:
2524 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
2525 else:
2526 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d'))
2527
2528 if med['pk_brand'] is None:
2529 brand = u'%s (%s)' % (gmTools.u_diameter, med['preparation'])
2530 else:
2531 if med['fake_brand']:
2532 brand = u'%s (%s)' % (
2533 gmTools.coalesce(med['brand'], u'', _('%s <fake>')),
2534 med['preparation']
2535 )
2536 else:
2537 brand = u'%s (%s)' % (
2538 gmTools.coalesce(med['brand'], u''),
2539 med['preparation']
2540 )
2541 self.SetCellValue(row_idx, 6, gmTools.wrap(text = brand, width = 35))
2542
2543 elif self.__grouping_mode == u'issue':
2544 if med['pk_health_issue'] is None:
2545 self.__prev_cell_0 = None
2546 issue = u'%s%s' % (
2547 gmTools.u_diameter,
2548 gmTools.coalesce(med['episode'], u'', u' (%s)')
2549 )
2550 else:
2551 if self.__prev_cell_0 == med['health_issue']:
2552 issue = u''
2553 else:
2554 self.__prev_cell_0 = med['health_issue']
2555 issue = med['health_issue']
2556 self.SetCellValue(row_idx, 0, gmTools.wrap(text = issue, width = 40))
2557
2558 self.SetCellValue(row_idx, 1, med['substance'])
2559 self.SetCellValue(row_idx, 2, u'%s %s' % (med['amount'], med['unit']))
2560 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], u''))
2561 self.SetCellValue(row_idx, 4, med.medically_formatted_start)
2562
2563 if med['is_long_term']:
2564 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
2565 else:
2566 if med['discontinued'] is None:
2567 if med['duration'] is None:
2568 self.SetCellValue(row_idx, 5, u'')
2569 else:
2570 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
2571 else:
2572 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d'))
2573
2574 if med['pk_brand'] is None:
2575 brand = u'%s (%s)' % (gmTools.u_diameter, med['preparation'])
2576 else:
2577 if med['fake_brand']:
2578 brand = u'%s (%s)' % (
2579 gmTools.coalesce(med['brand'], u'', _('%s <fake>')),
2580 med['preparation']
2581 )
2582 else:
2583 brand = u'%s (%s)' % (
2584 gmTools.coalesce(med['brand'], u''),
2585 med['preparation']
2586 )
2587 self.SetCellValue(row_idx, 6, gmTools.wrap(text = brand, width = 35))
2588
2589 elif self.__grouping_mode == u'brand':
2590
2591 if med['pk_brand'] is None:
2592 self.__prev_cell_0 = None
2593 brand = u'%s (%s)' % (
2594 gmTools.u_diameter,
2595 med['preparation']
2596 )
2597 else:
2598 if self.__prev_cell_0 == med['brand']:
2599 brand = u''
2600 else:
2601 self.__prev_cell_0 = med['brand']
2602 if med['fake_brand']:
2603 brand = u'%s (%s)' % (
2604 gmTools.coalesce(med['brand'], u'', _('%s <fake>')),
2605 med['preparation']
2606 )
2607 else:
2608 brand = u'%s (%s)' % (
2609 gmTools.coalesce(med['brand'], u''),
2610 med['preparation']
2611 )
2612 self.SetCellValue(row_idx, 0, gmTools.wrap(text = brand, width = 35))
2613
2614 self.SetCellValue(row_idx, 1, gmTools.coalesce(med['schedule'], u''))
2615 self.SetCellValue(row_idx, 2, med['substance'])
2616 self.SetCellValue(row_idx, 3, u'%s %s' % (med['amount'], med['unit']))
2617 self.SetCellValue(row_idx, 4, med.medically_formatted_start)
2618
2619 if med['is_long_term']:
2620 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
2621 else:
2622 if med['discontinued'] is None:
2623 if med['duration'] is None:
2624 self.SetCellValue(row_idx, 5, u'')
2625 else:
2626 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
2627 else:
2628 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d'))
2629
2630 if med['pk_health_issue'] is None:
2631 issue = u'%s%s' % (
2632 gmTools.u_diameter,
2633 gmTools.coalesce(med['episode'], u'', u' (%s)')
2634 )
2635 else:
2636 issue = gmTools.coalesce(med['health_issue'], u'')
2637 self.SetCellValue(row_idx, 6, gmTools.wrap(text = issue, width = 40))
2638
2639 else:
2640 raise ValueError('unknown grouping mode [%s]' % self.__grouping_mode)
2641
2642 if med['notes'] is not None:
2643 self.SetCellValue(row_idx, 7, gmTools.wrap(text = med['notes'], width = 50))
2644
2645 if self.__filter_show_unapproved:
2646 self.SetCellValue (
2647 row_idx,
2648 len(labels),
2649
2650 gmTools.bool2subst(med['intake_is_approved_of'], gmTools.u_checkmark_thin, gmTools.u_frowning_face, u'?')
2651 )
2652 font = self.GetCellFont(row_idx, len(labels))
2653 font.SetPointSize(font.GetPointSize() + 2)
2654 self.SetCellFont(row_idx, len(labels), font)
2655
2656
2657
2658 self.AutoSize()
2659 self.EndBatch()
2660
2662 self.BeginBatch()
2663 self.ClearGrid()
2664
2665
2666 if self.GetNumberRows() > 0:
2667 self.DeleteRows(pos = 0, numRows = self.GetNumberRows())
2668 if self.GetNumberCols() > 0:
2669 self.DeleteCols(pos = 0, numCols = self.GetNumberCols())
2670 self.EndBatch()
2671 self.__row_data = {}
2672 self.__prev_cell_0 = None
2673
2675
2676 if len(self.__row_data) == 0:
2677 return
2678
2679 sel_rows = self.get_selected_rows()
2680 if len(sel_rows) != 1:
2681 return
2682
2683 drug_db = get_drug_database()
2684 if drug_db is None:
2685 return
2686
2687 intake = self.get_selected_data()[0]
2688 if intake['brand'] is None:
2689 drug_db.show_info_on_substance(substance_intake = intake)
2690 else:
2691 drug_db.show_info_on_drug(substance_intake = intake)
2692
2700
2703
2713
2719
2733
2736
2750
2764
2780
2784
2885
2886
2887
2889 self.CreateGrid(0, 1)
2890 self.EnableEditing(0)
2891 self.EnableDragGridSize(1)
2892 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows)
2893
2894 self.SetColLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTER)
2895
2896 self.SetRowLabelSize(0)
2897 self.SetRowLabelAlignment(horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
2898
2899
2900
2902 return self.__patient
2903
2907
2908 patient = property(_get_patient, _set_patient)
2909
2911 return self.__grouping_mode
2912
2916
2917 grouping_mode = property(_get_grouping_mode, _set_grouping_mode)
2918
2920 return self.__filter_show_unapproved
2921
2925
2926 filter_show_unapproved = property(_get_filter_show_unapproved, _set_filter_show_unapproved)
2927
2929 return self.__filter_show_inactive
2930
2934
2935 filter_show_inactive = property(_get_filter_show_inactive, _set_filter_show_inactive)
2936
2937
2938
2940
2941 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells)
2942
2943
2944
2945
2946 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
2947
2949 """Calculate where the mouse is and set the tooltip dynamically."""
2950
2951
2952
2953 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967 row, col = self.XYToCell(x, y)
2968
2969 if row == self.__prev_tooltip_row:
2970 return
2971
2972 self.__prev_tooltip_row = row
2973
2974 try:
2975 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row))
2976 except KeyError:
2977 pass
2978
2983
2984
3003
3004
3005 from Gnumed.wxGladeWidgets import wxgCurrentSubstancesPnl
3006
3007 -class cCurrentSubstancesPnl(wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl, gmRegetMixin.cRegetOnPaintMixin):
3008
3009 """Panel holding a grid with current substances. Used as notebook page."""
3010
3020
3021
3022
3024 """Populate cells with data from model."""
3025 pat = gmPerson.gmCurrentPatient()
3026 if pat.connected:
3027 self._grid_substances.patient = pat
3028 self.__refresh_gfr(pat)
3029 self.__refresh_lab(patient = pat)
3030 else:
3031 self._grid_substances.patient = None
3032 self.__clear_gfr()
3033 self.__refresh_lab(patient = None)
3034 return True
3035
3037 self._TCTRL_lab.SetDefaultStyle(wx.TextAttr(self.__lab_default_text_color))
3038 self._TCTRL_lab.SetValue(u'')
3039 self._TCTRL_lab.Hide()
3040
3041 if patient is None:
3042 self.Layout()
3043 return
3044
3045 if self.__lab_panel is None:
3046 self.Layout()
3047 return
3048
3049 results = self.__lab_panel.get_most_recent_results(pk_patient = patient.ID, order_by = u'unified_abbrev')
3050 if len(results) == 0:
3051 self.Layout()
3052 return
3053
3054 now = gmDateTime.pydt_now_here()
3055
3056
3057 gfr = patient.emr.get_most_recent_results(loinc = gmLOINC.LOINC_gfr_quantity, no_of_results = 1)
3058 crea = patient.emr.get_most_recent_results(loinc = gmLOINC.LOINC_creatinine_quantity, no_of_results = 1)
3059 if crea is None:
3060 gfr_3_months_older_than_crea = False
3061 else:
3062 three_months = pydt.timedelta(weeks = 14)
3063 gfr_3_months_older_than_crea = (crea['clin_when'] - gfr['clin_when']) > three_months
3064
3065 if (gfr is None) or gfr_3_months_older_than_crea:
3066 calc = gmClinicalCalculator.cClinicalCalculator()
3067 calc.patient = patient
3068 gfr = calc.eGFR
3069 if gfr.numeric_value is None:
3070 gfr_msg = u'?'
3071 else:
3072 gfr_msg = _(u'%.1f (%s ago)') % (
3073 gfr.numeric_value,
3074 gmDateTime.format_interval_medically(now - gfr.date_valid)
3075
3076 )
3077 self._TCTRL_lab.SetDefaultStyle(wx.TextAttr('blue'))
3078 self._TCTRL_lab.AppendText(_('eGFR:'))
3079 self._TCTRL_lab.SetDefaultStyle(wx.TextAttr(self.__lab_default_text_color))
3080 self._TCTRL_lab.AppendText(u' ' + gfr_msg)
3081 self._TCTRL_lab.AppendText(u' || ')
3082
3083 for most_recent in results:
3084 if most_recent.is_considered_abnormal:
3085 self._TCTRL_lab.SetDefaultStyle(wx.TextAttr('red'))
3086 txt = _('%s: %s%s%s (%s ago)') % (
3087 most_recent['unified_abbrev'],
3088 most_recent['unified_val'],
3089 gmTools.coalesce(most_recent['val_unit'], u'', u' %s'),
3090 gmTools.coalesce(most_recent.formatted_abnormality_indicator, u'', u' %s'),
3091 gmDateTime.format_interval_medically(now - most_recent['clin_when'])
3092 )
3093 self._TCTRL_lab.AppendText(txt)
3094 self._TCTRL_lab.SetDefaultStyle(wx.TextAttr(self.__lab_default_text_color))
3095 else:
3096 self._TCTRL_lab.SetDefaultStyle(wx.TextAttr('blue'))
3097 self._TCTRL_lab.AppendText(u'%s:' % most_recent['unified_abbrev'])
3098 self._TCTRL_lab.SetDefaultStyle(wx.TextAttr(self.__lab_default_text_color))
3099 txt = _(' %s%s%s (%s ago)') % (
3100 most_recent['unified_val'],
3101 gmTools.coalesce(most_recent['val_unit'], u'', u' %s'),
3102 gmTools.coalesce(most_recent.formatted_abnormality_indicator, u'', u' %s'),
3103 gmDateTime.format_interval_medically(now - most_recent['clin_when'])
3104 )
3105 self._TCTRL_lab.AppendText(txt)
3106 self._TCTRL_lab.AppendText(u' || ')
3107
3108 self._TCTRL_lab.Show()
3109 self.Layout()
3110
3112 gfr = patient.emr.get_most_recent_results(loinc = gmLOINC.LOINC_gfr_quantity, no_of_results = 1)
3113 if gfr is None:
3114 calc = gmClinicalCalculator.cClinicalCalculator()
3115 calc.patient = patient
3116 gfr = calc.eGFR
3117 if gfr.numeric_value is None:
3118 msg = _('GFR: ?')
3119 tt = gfr.message
3120 else:
3121 msg = _('eGFR: %.1f (%s)') % (
3122 gfr.numeric_value,
3123 gmDateTime.pydt_strftime (
3124 gfr.date_valid,
3125 format = '%b %Y'
3126 )
3127 )
3128 tt = gfr.format (
3129 left_margin = 0,
3130 width = 50,
3131 eol = u'\n',
3132 with_formula = True,
3133 with_warnings = True,
3134 with_variables = False,
3135 with_sub_results = True,
3136 return_list = False
3137 )
3138 else:
3139 msg = u'%s: %s %s (%s)\n' % (
3140 gfr['unified_abbrev'],
3141 gfr['unified_val'],
3142 gmTools.coalesce(gfr['abnormality_indicator'], u'', u' (%s)'),
3143 gmDateTime.pydt_strftime (
3144 gfr['clin_when'],
3145 format = '%b %Y'
3146 )
3147 )
3148 tt = _('GFR reported by path lab')
3149
3150 self._LBL_gfr.SetLabel(msg)
3151 self._LBL_gfr.SetToolTipString(tt)
3152 self._LBL_gfr.Refresh()
3153 self.Layout()
3154
3156 self._LBL_gfr.SetLabel(_('GFR: ?'))
3157 self._LBL_gfr.Refresh()
3158 self.Layout()
3159
3160
3161
3163 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
3164 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
3165 gmDispatcher.connect(signal = u'clin.substance_intake_mod_db', receiver = self._schedule_data_reget)
3166 gmDispatcher.connect(signal = u'clin.test_result_mod_db', receiver = self._on_test_result_mod)
3167
3168
3169
3171 wx.CallAfter(self.__on_test_result_mod)
3172
3175
3177 wx.CallAfter(self.__on_pre_patient_selection)
3178
3192
3194 wx.CallAfter(self.__on_post_patient_selection)
3195
3197 self._schedule_data_reget()
3198
3201
3204
3207
3210
3213
3216
3219
3222
3225
3228
3231
3234
3237
3240
3243
3246
3247
3248
3249 if __name__ == '__main__':
3250
3251 if len(sys.argv) < 2:
3252 sys.exit()
3253
3254 if sys.argv[1] != 'test':
3255 sys.exit()
3256
3257 from Gnumed.business import gmPersonSearch
3258
3259 pat = gmPersonSearch.ask_for_patient()
3260 if pat is None:
3261 sys.exit()
3262 gmPerson.set_active_patient(patient = pat)
3263
3264
3265 app = wx.PyWidgetTester(size = (600, 600))
3266
3267
3268
3269 manage_substance_intakes()
3270
3271
3272