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