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 started = u'%s:' % gmDateTime.pydt_strftime(i['started'], '%Y %b %d')
1357 items.append ([
1358 u'%s%s %s %s %s%s' % (
1359 i['substance'],
1360 gmTools.coalesce(i['brand'], u'', u' (%s)'),
1361 i['amount'],
1362 i['unit'],
1363 i['preparation'],
1364 gmTools.coalesce(i['external_code_brand'], u'', u' [%s::%s]' % (i['external_code_type_brand'], i['external_code_brand']))
1365 ),
1366 u'%s%s%s' % (
1367 started,
1368 gmTools.coalesce(i['schedule'], u'', u' %%s %s' % gmTools.u_right_arrow),
1369 gmTools.coalesce(i['duration'], u'', u' %s')
1370 ),
1371 u'%s' % (
1372 gmTools.bool2subst (
1373 i['intake_is_approved_of'],
1374 u'',
1375 _('disapproved')
1376 )
1377 )
1378 ])
1379 lctrl.set_string_items(items)
1380 lctrl.set_data(intakes)
1381
1382 msg = _('Substances consumed by the patient:')
1383
1384 return gmListWidgets.get_choices_from_list (
1385 parent = parent,
1386 msg = msg,
1387 caption = _('Showing consumable substances.'),
1388 columns = [ _('Intake'), _('Application'), _('Status') ],
1389 single_selection = False,
1390
1391
1392
1393 refresh_callback = refresh,
1394 list_tooltip_callback = get_tooltip
1395
1396 )
1397
1398
1399 from Gnumed.wxGladeWidgets import wxgCurrentMedicationEAPnl
1400
1401 -class cSubstanceIntakeEAPnl(wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl, gmEditArea.cGenericEditAreaMixin):
1402
1422
1434
1436 curr_pat = gmPerson.gmCurrentPatient()
1437 emr = curr_pat.emr
1438
1439 state = emr.allergy_state
1440 if state['last_confirmed'] is None:
1441 confirmed = _('never')
1442 else:
1443 confirmed = gmDateTime.pydt_strftime(state['last_confirmed'], '%Y %b %d')
1444 msg = _(u'%s, last confirmed %s\n') % (state.state_string, confirmed)
1445 msg += gmTools.coalesce(state['comment'], u'', _('Comment (%s): %%s\n') % state['modified_by'])
1446
1447 tt = u''
1448
1449 allgs = emr.get_allergies()
1450 if len(allgs) > 0:
1451 msg += u'\n'
1452 for allergy in allgs:
1453 msg += u'%s: %s (%s)\n' % (
1454 allergy['descriptor'],
1455 allergy['l10n_type'],
1456 gmTools.bool2subst(allergy['definite'], _('definite'), _('suspected'), u'?')
1457 )
1458 tt += u'%s: %s\n' % (
1459 allergy['descriptor'],
1460 gmTools.coalesce(allergy['reaction'], _('reaction not recorded'))
1461 )
1462
1463 if len(allgs) > 0:
1464 msg += u'\n'
1465 tt += u'\n'
1466
1467 gfr = emr.get_most_recent_results(loinc = gmLOINC.LOINC_gfr_quantity, no_of_results = 1)
1468 if gfr is None:
1469 self.calc.patient = curr_pat
1470 gfr = self.calc.eGFR
1471 if gfr.numeric_value is None:
1472 msg += _('GFR: unknown')
1473 else:
1474 msg += gfr.message
1475 tt += gfr.format (
1476 left_margin = 0,
1477 width = 50,
1478 eol = u'\n',
1479 with_formula = True,
1480 with_warnings = True,
1481 with_variables = False,
1482 with_sub_results = True,
1483 return_list = False
1484 )
1485 else:
1486 msg += u'%s: %s %s (%s)\n' % (
1487 gfr['unified_abbrev'],
1488 gfr['unified_val'],
1489 gmTools.coalesce(gfr['abnormality_indicator'], u'', u' (%s)'),
1490 gmDateTime.pydt_strftime (
1491 gfr['clin_when'],
1492 format = '%Y %b %d'
1493 )
1494 )
1495 tt += _('GFR reported by path lab')
1496
1497 self._LBL_allergies.SetLabel(msg)
1498 self._LBL_allergies.SetToolTipString(tt)
1499
1500
1501
1601
1603
1604 emr = gmPerson.gmCurrentPatient().get_emr()
1605 epi = self._PRW_episode.GetData(can_create = True)
1606
1607 if self._PRW_substance.GetData() is None:
1608
1609 intake = emr.add_substance_intake (
1610 pk_component = self._PRW_component.GetData(),
1611 episode = epi
1612 )
1613 else:
1614 intake = emr.add_substance_intake (
1615 pk_substance = self._PRW_substance.GetData(),
1616 episode = epi,
1617 preparation = self._PRW_preparation.GetValue().strip()
1618 )
1619
1620 if intake is None:
1621 gmDispatcher.send('statustext', msg = _('Cannot add duplicate of (maybe inactive) substance intake.'), beep = True)
1622 return False
1623
1624 intake['started'] = self._DP_started.GetData()
1625 intake['discontinued'] = self._DP_discontinued.GetData()
1626 if intake['discontinued'] is None:
1627 intake['discontinue_reason'] = None
1628 else:
1629 intake['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
1630 intake['schedule'] = self._PRW_schedule.GetValue().strip()
1631 intake['aim'] = self._PRW_aim.GetValue().strip()
1632 intake['notes'] = self._PRW_notes.GetValue().strip()
1633 intake['is_long_term'] = self._CHBOX_long_term.IsChecked()
1634 intake['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
1635 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
1636 intake['duration'] = None
1637 else:
1638 if self._PRW_duration.GetData() is None:
1639 intake['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
1640 else:
1641 intake['duration'] = self._PRW_duration.GetData()
1642 intake.save()
1643
1644 self.data = intake
1645
1646 return True
1647
1649
1650
1651 self.data['started'] = self._DP_started.GetData()
1652 self.data['discontinued'] = self._DP_discontinued.GetData()
1653 if self.data['discontinued'] is None:
1654 self.data['discontinue_reason'] = None
1655 else:
1656 self.data['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
1657 self.data['schedule'] = self._PRW_schedule.GetValue()
1658 self.data['is_long_term'] = self._CHBOX_long_term.IsChecked()
1659 self.data['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
1660 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
1661 self.data['duration'] = None
1662 else:
1663
1664 if self._PRW_duration.GetData() is None:
1665 self.data['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
1666 else:
1667 self.data['duration'] = self._PRW_duration.GetData()
1668
1669
1670 self.data['preparation'] = self._PRW_preparation.GetValue()
1671
1672
1673 self.data['aim'] = self._PRW_aim.GetValue()
1674 self.data['notes'] = self._PRW_notes.GetValue()
1675 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True)
1676
1677 self.data.save()
1678
1679 return True
1680
1682 self._PRW_component.SetText(u'', None)
1683 self._LBL_component.Enable(True)
1684 self._PRW_component.Enable(True)
1685 self._TCTRL_brand_ingredients.SetValue(u'')
1686 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1687
1688 self._LBL_or.Enable(True)
1689
1690 self._PRW_substance.SetText(u'', None)
1691 self._PRW_substance.Enable(True)
1692
1693 self._PRW_preparation.SetText(u'', None)
1694 self._PRW_preparation.Enable(True)
1695
1696 self._PRW_schedule.SetText(u'', None)
1697 self._PRW_duration.SetText(u'', None)
1698 self._PRW_aim.SetText(u'', None)
1699 self._PRW_notes.SetText(u'', None)
1700 self._PRW_episode.SetText(u'', None)
1701
1702 self._CHBOX_long_term.SetValue(False)
1703 self._CHBOX_approved.SetValue(True)
1704
1705 self._DP_started.SetData(gmDateTime.pydt_now_here())
1706 self._DP_discontinued.SetData(None)
1707 self._PRW_discontinue_reason.SetValue(u'')
1708
1709 self.__refresh_allergies()
1710
1711 self._PRW_component.SetFocus()
1712
1714
1715 self._TCTRL_brand_ingredients.SetValue(u'')
1716 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1717
1718 if self.data['pk_brand'] is None:
1719 self.__refresh_from_existing_substance()
1720 else:
1721 self.__refresh_from_existing_component()
1722
1723
1724 self._LBL_component.Enable(False)
1725 self._PRW_component.Enable(False)
1726 self._LBL_or.Enable(False)
1727 self._PRW_substance.Enable(False)
1728
1729 if self.data['is_long_term']:
1730 self._CHBOX_long_term.SetValue(True)
1731 self._PRW_duration.Enable(False)
1732 self._PRW_duration.SetText(gmTools.u_infinity, None)
1733 self._BTN_discontinued_as_planned.Enable(False)
1734 else:
1735 self._CHBOX_long_term.SetValue(False)
1736 self._PRW_duration.Enable(True)
1737 self._BTN_discontinued_as_planned.Enable(True)
1738 self._PRW_duration.SetData(self.data['duration'])
1739
1740
1741
1742
1743 self._PRW_aim.SetText(gmTools.coalesce(self.data['aim'], u''), self.data['aim'])
1744 self._PRW_notes.SetText(gmTools.coalesce(self.data['notes'], u''), self.data['notes'])
1745 self._PRW_episode.SetData(self.data['pk_episode'])
1746 self._PRW_schedule.SetText(gmTools.coalesce(self.data['schedule'], u''), self.data['schedule'])
1747
1748 self._CHBOX_approved.SetValue(self.data['intake_is_approved_of'])
1749
1750 self._DP_started.SetData(self.data['started'])
1751 self._DP_discontinued.SetData(self.data['discontinued'])
1752 self._PRW_discontinue_reason.SetValue(gmTools.coalesce(self.data['discontinue_reason'], u''))
1753 if self.data['discontinued'] is not None:
1754 self._PRW_discontinue_reason.Enable()
1755
1756 self.__refresh_allergies()
1757
1758 self._PRW_schedule.SetFocus()
1759
1761 self._LBL_component.Enable(False)
1762 self._PRW_component.Enable(False)
1763 self._PRW_component.SetText(u'', None)
1764 self._PRW_component.display_as_valid(True)
1765
1766 self._LBL_or.Enable(False)
1767
1768 self._PRW_substance.Enable(True)
1769 self._PRW_substance.SetText (
1770 u'%s %s %s' % (self.data['substance'], self.data['amount'], self.data['unit']),
1771 self.data['pk_substance']
1772 )
1773
1774
1775 self._PRW_preparation.SetText(self.data['preparation'], self.data['preparation'])
1776 self._PRW_preparation.Enable(True)
1777
1779 self._LBL_component.Enable(True)
1780 self._PRW_component.Enable(True)
1781 self._PRW_component.SetText (
1782 u'%s %s %s (%s)' % (self.data['substance'], self.data['amount'], self.data['unit'], self.data['brand']),
1783 self.data['pk_drug_component']
1784 )
1785
1786 brand = gmMedication.cBrandedDrug(aPK_obj = self.data['pk_brand'])
1787 if brand['components'] is not None:
1788 self._TCTRL_brand_ingredients.SetValue(u'; '.join(brand['components']))
1789 tt = u'%s:\n\n- %s' % (
1790 self.data['brand'],
1791 u'\n- '.join(brand['components'])
1792 )
1793 self._TCTRL_brand_ingredients.SetToolTipString(tt)
1794
1795 self._LBL_or.Enable(False)
1796 self._LBL_substance.Enable(False)
1797 self._PRW_substance.SetText(u'', None)
1798 self._PRW_substance.display_as_valid(True)
1799
1800 self._PRW_preparation.SetText(self.data['preparation'], self.data['preparation'])
1801 self._PRW_preparation.Enable(False)
1802
1804 self._refresh_as_new()
1805
1806 self._PRW_episode.SetData(self.data['pk_episode'])
1807
1808 self._PRW_component.SetFocus()
1809
1810
1811
1813 if self._PRW_component.GetData() is None:
1814 self._LBL_or.Enable(True)
1815 self._PRW_component.SetText(u'', None)
1816 self._LBL_substance.Enable(True)
1817 self._PRW_substance.Enable(True)
1818 self._LBL_preparation.Enable(True)
1819 self._PRW_preparation.Enable(True)
1820
1821 self._TCTRL_brand_ingredients.SetValue(u'')
1822 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1823 else:
1824 self._LBL_or.Enable(False)
1825 self._LBL_substance.Enable(False)
1826 self._PRW_substance.SetText(u'', None)
1827 self._PRW_substance.display_as_valid(True)
1828 self._PRW_substance.Enable(False)
1829 self._LBL_preparation.Enable(False)
1830 self._PRW_preparation.Enable(False)
1831 comp = gmMedication.cDrugComponent(aPK_obj = self._PRW_component.GetData())
1832 self._PRW_preparation.SetText(comp['preparation'], comp['preparation'])
1833 brand = comp.containing_drug
1834 if brand['components'] is not None:
1835 self._TCTRL_brand_ingredients.SetValue(u'; '.join(brand['components']))
1836 tt = u'%s:\n\n- %s' % (
1837 brand['brand'],
1838 u'\n- '.join(brand['components'])
1839 )
1840 self._TCTRL_brand_ingredients.SetToolTipString(tt)
1841
1843 if self._PRW_substance.GetData() is None:
1844 self._LBL_or.Enable(True)
1845 self._LBL_component.Enable(True)
1846 self._PRW_component.Enable(True)
1847 self._PRW_substance.SetText(u'', None)
1848 else:
1849 self._LBL_or.Enable(False)
1850 self._LBL_component.Enable(False)
1851 self._PRW_component.SetText(u'', None)
1852 self._PRW_component.display_as_valid(True)
1853 self._PRW_component.Enable(False)
1854 self._LBL_preparation.Enable(True)
1855 self._PRW_preparation.Enable(True)
1856 self._TCTRL_brand_ingredients.SetValue(u'')
1857 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1858
1860
1861
1862
1863
1864
1865 subst = self._PRW_component.GetValue().strip()
1866 if subst != u'':
1867 comp = self._PRW_component.GetData(as_instance = True)
1868 if comp is None:
1869 self._PRW_aim.set_context(context = u'substance', val = subst)
1870 return
1871 self._PRW_aim.set_context(context = u'substance', val = comp['substance'])
1872 return
1873
1874 subst = self._PRW_substance.GetValue().strip()
1875 if subst == u'':
1876 self._PRW_aim.unset_context(context = u'substance')
1877 return
1878 comp = self._PRW_substance.GetData(as_instance = True)
1879 if comp is None:
1880 self._PRW_aim.set_context(context = u'substance', val = subst)
1881 return
1882 self._PRW_aim.set_context(context = u'substance', val = comp['description'])
1883
1885 if self._DP_discontinued.GetData() is None:
1886 self._PRW_discontinue_reason.Enable(False)
1887 else:
1888 self._PRW_discontinue_reason.Enable(True)
1889
1892
1895
1898
1910
1940
1942 if self._CHBOX_long_term.IsChecked() is True:
1943 self._PRW_duration.Enable(False)
1944 self._BTN_discontinued_as_planned.Enable(False)
1945 self._PRW_discontinue_reason.Enable(False)
1946 else:
1947 self._PRW_duration.Enable(True)
1948 self._BTN_discontinued_as_planned.Enable(True)
1949 self._PRW_discontinue_reason.Enable(True)
1950
1951 self.__refresh_allergies()
1952
1962
1963
1965
1966 subst = gmMedication.cSubstanceIntakeEntry(aPK_obj = substance)
1967 msg = _(
1968 '\n'
1969 '[%s]\n'
1970 '\n'
1971 'It may be prudent to edit (before deletion) the details\n'
1972 'of this substance intake entry so as to leave behind\n'
1973 'some indication of why it was deleted.\n'
1974 ) % subst.format()
1975
1976 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
1977 parent,
1978 -1,
1979 caption = _('Deleting medication / substance intake'),
1980 question = msg,
1981 button_defs = [
1982 {'label': _('&Edit'), 'tooltip': _('Allow editing of substance intake entry before deletion.'), 'default': True},
1983 {'label': _('&Delete'), 'tooltip': _('Delete immediately without editing first.')},
1984 {'label': _('&Cancel'), 'tooltip': _('Abort. Do not delete or edit substance intake entry.')}
1985 ]
1986 )
1987
1988 edit_first = dlg.ShowModal()
1989 dlg.Destroy()
1990
1991 if edit_first == wx.ID_CANCEL:
1992 return
1993
1994 if edit_first == wx.ID_YES:
1995 edit_intake_of_substance(parent = parent, substance = subst)
1996 delete_it = gmGuiHelpers.gm_show_question (
1997 aMessage = _('Now delete substance intake entry ?'),
1998 aTitle = _('Deleting medication / substance intake')
1999 )
2000 else:
2001 delete_it = True
2002
2003 if not delete_it:
2004 return
2005
2006 gmMedication.delete_substance_intake(substance = substance)
2007
2022
2023
2024
2025
2053
2055
2056 if parent is None:
2057 parent = wx.GetApp().GetTopWindow()
2058
2059
2060 dbcfg = gmCfg.cCfgSQL()
2061 option = u'form_templates.medication_list'
2062
2063 template = dbcfg.get2 (
2064 option = option,
2065 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
2066 bias = 'user'
2067 )
2068
2069 if template is None:
2070 template = configure_medication_list_template(parent = parent)
2071 if template is None:
2072 gmGuiHelpers.gm_show_error (
2073 aMessage = _('There is no medication list template configured.'),
2074 aTitle = _('Printing medication list')
2075 )
2076 return False
2077 else:
2078 try:
2079 name, ver = template.split(u' - ')
2080 except:
2081 _log.exception('problem splitting medication list template name [%s]', template)
2082 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading medication list template.'), beep = True)
2083 return False
2084 template = gmForms.get_form_template(name_long = name, external_version = ver)
2085 if template is None:
2086 gmGuiHelpers.gm_show_error (
2087 aMessage = _('Cannot load medication list template [%s - %s]') % (name, ver),
2088 aTitle = _('Printing medication list')
2089 )
2090 return False
2091
2092
2093 try:
2094 meds_list = template.instantiate()
2095 except KeyError:
2096 _log.exception('cannot instantiate medication list template [%s]', template)
2097 gmGuiHelpers.gm_show_error (
2098 aMessage = _('Invalid medication list template [%s - %s (%s)]') % (name, ver, template['engine']),
2099 aTitle = _('Printing medication list')
2100 )
2101 return False
2102
2103 ph = gmMacro.gmPlaceholderHandler()
2104
2105 meds_list.substitute_placeholders(data_source = ph)
2106 pdf_name = meds_list.generate_output()
2107 if pdf_name is None:
2108 gmGuiHelpers.gm_show_error (
2109 aMessage = _('Error generating the medication list.'),
2110 aTitle = _('Printing medication list')
2111 )
2112 return False
2113
2114
2115 printed = gmPrinting.print_files(filenames = [pdf_name], jobtype = 'medication_list')
2116 if not printed:
2117 gmGuiHelpers.gm_show_error (
2118 aMessage = _('Error printing the medication list.'),
2119 aTitle = _('Printing medication list')
2120 )
2121 return False
2122
2123
2124 pat = gmPerson.gmCurrentPatient()
2125 emr = pat.get_emr()
2126 epi = emr.add_episode (
2127 episode_name = gmMedication.DEFAULT_MEDICATION_HISTORY_EPISODE,
2128 is_open = False
2129 )
2130 narr = emr.add_clin_narrative (
2131 soap_cat = None,
2132 note = _('medication list printed from template [%s - %s]') % (template['name_long'], template['external_version']),
2133 episode = epi
2134 )
2135
2136
2137 doc = gmDocumentWidgets.save_files_as_new_document (
2138 parent = parent,
2139 filenames = [pdf_name],
2140 document_type = template['instance_type'],
2141 episode = epi,
2142 review_as_normal = True
2143 )
2144
2145
2146
2147
2148 return True
2149
2178
2180
2181 if parent is None:
2182 parent = wx.GetApp().GetTopWindow()
2183
2184 dbcfg = gmCfg.cCfgSQL()
2185 option = u'form_templates.prescription'
2186 template_name = dbcfg.get2 (
2187 option = option,
2188 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
2189 bias = 'user'
2190 )
2191
2192 if template_name is None:
2193 template = configure_prescription_template(parent = parent)
2194 if template is None:
2195 gmGuiHelpers.gm_show_error (
2196 aMessage = _('There is no prescription template configured.'),
2197 aTitle = _('Printing prescription')
2198 )
2199 return None
2200 return template
2201
2202 try:
2203 name, ver = template_name.split(u' - ')
2204 except:
2205 _log.exception('problem splitting prescription template name [%s]', template_name)
2206 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading prescription template.'), beep = True)
2207 return False
2208 template = gmForms.get_form_template(name_long = name, external_version = ver)
2209 if template is None:
2210 gmGuiHelpers.gm_show_error (
2211 aMessage = _('Cannot load prescription template [%s - %s]') % (name, ver),
2212 aTitle = _('Printing prescription')
2213 )
2214 return False
2215 return template
2216
2218
2219
2220 rx_template = get_prescription_template(parent = parent)
2221 if rx_template is None:
2222 return False
2223
2224
2225 try:
2226 rx = rx_template.instantiate()
2227 except KeyError:
2228 _log.exception('cannot instantiate prescription template [%s]', rx_template)
2229 gmGuiHelpers.gm_show_error (
2230 aMessage = _('Invalid prescription template [%s - %s (%s)]') % (rx_template['name_long'], rx_template['external_version'], rx_template['engine']),
2231 aTitle = _('Printing prescription list')
2232 )
2233 return False
2234 ph = gmMacro.gmPlaceholderHandler()
2235
2236 rx.substitute_placeholders(data_source = ph)
2237 pdf_name = rx.generate_output()
2238 if pdf_name is None:
2239 gmGuiHelpers.gm_show_error (
2240 aMessage = _('Error generating the prescription printout.'),
2241 aTitle = _('Printing prescription')
2242 )
2243 return False
2244
2245
2246 printed = gmPrinting.print_files(filenames = [pdf_name], jobtype = 'prescription')
2247 if not printed:
2248 gmGuiHelpers.gm_show_error (
2249 aMessage = _('Error printing the prescription.'),
2250 aTitle = _('Printing prescription')
2251 )
2252 return False
2253
2254 epi = emr.add_episode (
2255 episode_name = gmMedication.DEFAULT_MEDICATION_HISTORY_EPISODE,
2256 is_open = False
2257 )
2258
2259
2260 files2import = []
2261 files2import.extend(rx.final_output_filenames)
2262 files2import.extend(rx.re_editable_filenames)
2263 doc = gmDocumentWidgets.save_files_as_new_document (
2264 parent = parent,
2265 filenames = files2import,
2266 document_type = u'prescription',
2267
2268
2269
2270 episode = epi,
2271 review_as_normal = True
2272 )
2273
2274
2275 emr.add_clin_narrative (
2276 soap_cat = None,
2277 note = _('prescription printed from template [%s - %s]') % (rx_template['name_long'], rx_template['external_version']),
2278 episode = epi
2279 )
2280
2281 return True
2282
2283
2311
2312
2314
2315 if len(prescribed_drugs) == 0:
2316 return
2317
2318 curr_brands = [ i['pk_brand'] for i in emr.get_current_substance_intakes() if i['pk_brand'] is not None ]
2319 new_drugs = []
2320 for drug in prescribed_drugs:
2321 if drug['pk_brand'] not in curr_brands:
2322 new_drugs.append(drug)
2323
2324 if len(new_drugs) == 0:
2325 return
2326
2327 if parent is None:
2328 parent = wx.GetApp().GetTopWindow()
2329
2330 dlg = gmListWidgets.cItemPickerDlg (
2331 parent,
2332 -1,
2333 msg = _(
2334 'These brands have been prescribed but are not listed\n'
2335 'in the current medication list of this patient.\n'
2336 '\n'
2337 'Please select those you want added to the medication list.'
2338 )
2339 )
2340 dlg.set_columns (
2341 columns = [_('Newly prescribed drugs')],
2342 columns_right = [_('Add to medication list')]
2343 )
2344 choices = [ (u'%s %s (%s)' % (d['brand'], d['preparation'], u'; '.join(d['components']))) for d in new_drugs ]
2345 dlg.set_choices (
2346 choices = choices,
2347 data = new_drugs
2348 )
2349 dlg.ShowModal()
2350 drugs2add = dlg.get_picks()
2351 dlg.Destroy()
2352
2353 if drugs2add is None:
2354 return
2355
2356 if len(drugs2add) == 0:
2357 return
2358
2359 for drug in drugs2add:
2360
2361 intake = emr.add_substance_intake (
2362 pk_component = drug['pk_components'][0],
2363 episode = emr.add_episode(episode_name = gmMedication.DEFAULT_MEDICATION_HISTORY_EPISODE)['pk_episode'],
2364 )
2365 if intake is None:
2366 continue
2367 intake['intake_is_approved_of'] = True
2368 intake.save()
2369
2370 return
2371
2373 """A grid class for displaying current substance intake.
2374
2375 - does NOT listen to the currently active patient
2376 - thereby it can display any patient at any time
2377 """
2379
2380 wx.grid.Grid.__init__(self, *args, **kwargs)
2381
2382 self.__patient = None
2383 self.__row_data = {}
2384 self.__prev_row = None
2385 self.__prev_tooltip_row = None
2386 self.__prev_cell_0 = None
2387 self.__grouping_mode = u'issue'
2388 self.__filter_show_unapproved = True
2389 self.__filter_show_inactive = True
2390
2391 self.__grouping2col_labels = {
2392 u'issue': [
2393 _('Health issue'),
2394 _('Substance'),
2395 _('Strength'),
2396 _('Schedule'),
2397 _('Started'),
2398 _('Duration / Until'),
2399 _('Brand'),
2400 _('Advice')
2401 ],
2402 u'brand': [
2403 _('Brand'),
2404 _('Schedule'),
2405 _('Substance'),
2406 _('Strength'),
2407 _('Started'),
2408 _('Duration / Until'),
2409 _('Health issue'),
2410 _('Advice')
2411 ],
2412 u'episode': [
2413 _('Episode'),
2414 _('Substance'),
2415 _('Strength'),
2416 _('Schedule'),
2417 _('Started'),
2418 _('Duration / Until'),
2419 _('Brand'),
2420 _('Advice')
2421 ]
2422 }
2423
2424 self.__grouping2order_by_clauses = {
2425 u'issue': u'pk_health_issue nulls first, substance, started',
2426 u'episode': u'pk_health_issue nulls first, episode, substance, started',
2427 u'brand': u'brand nulls last, substance, started'
2428 }
2429
2430 self.__init_ui()
2431 self.__register_events()
2432
2433
2434
2436
2437 sel_block_top_left = self.GetSelectionBlockTopLeft()
2438 sel_block_bottom_right = self.GetSelectionBlockBottomRight()
2439 sel_cols = self.GetSelectedCols()
2440 sel_rows = self.GetSelectedRows()
2441
2442 selected_cells = []
2443
2444
2445 selected_cells += self.GetSelectedCells()
2446
2447
2448 selected_cells += list (
2449 (row, col)
2450 for row in sel_rows
2451 for col in xrange(self.GetNumberCols())
2452 )
2453
2454
2455 selected_cells += list (
2456 (row, col)
2457 for row in xrange(self.GetNumberRows())
2458 for col in sel_cols
2459 )
2460
2461
2462 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()):
2463 selected_cells += [
2464 (row, col)
2465 for row in xrange(top_left[0], bottom_right[0] + 1)
2466 for col in xrange(top_left[1], bottom_right[1] + 1)
2467 ]
2468
2469 return set(selected_cells)
2470
2472 rows = {}
2473
2474 for row, col in self.get_selected_cells():
2475 rows[row] = True
2476
2477 return rows.keys()
2478
2481
2483
2484 self.empty_grid()
2485
2486 if self.__patient is None:
2487 return
2488
2489 emr = self.__patient.get_emr()
2490 meds = emr.get_current_substance_intakes (
2491 order_by = self.__grouping2order_by_clauses[self.__grouping_mode],
2492 include_unapproved = self.__filter_show_unapproved,
2493 include_inactive = self.__filter_show_inactive
2494 )
2495 if not meds:
2496 return
2497
2498 self.BeginBatch()
2499
2500
2501 labels = self.__grouping2col_labels[self.__grouping_mode]
2502 if self.__filter_show_unapproved:
2503 self.AppendCols(numCols = len(labels) + 1)
2504 else:
2505 self.AppendCols(numCols = len(labels))
2506 for col_idx in range(len(labels)):
2507 self.SetColLabelValue(col_idx, labels[col_idx])
2508 if self.__filter_show_unapproved:
2509 self.SetColLabelValue(len(labels), u'OK?')
2510 self.SetColSize(len(labels), 40)
2511
2512 self.AppendRows(numRows = len(meds))
2513
2514
2515 for row_idx in range(len(meds)):
2516 med = meds[row_idx]
2517 self.__row_data[row_idx] = med
2518
2519 if med['is_currently_active'] is True:
2520 atcs = []
2521 if med['atc_substance'] is not None:
2522 atcs.append(med['atc_substance'])
2523
2524
2525
2526 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (med['substance'],))
2527 if allg not in [None, False]:
2528 attr = self.GetOrCreateCellAttr(row_idx, 0)
2529 if allg['type'] == u'allergy':
2530 attr.SetTextColour('red')
2531 else:
2532 attr.SetTextColour('yellow')
2533 self.SetRowAttr(row_idx, attr)
2534 else:
2535 attr = self.GetOrCreateCellAttr(row_idx, 0)
2536 attr.SetTextColour('grey')
2537 self.SetRowAttr(row_idx, attr)
2538
2539 if self.__grouping_mode == u'episode':
2540 if med['pk_episode'] is None:
2541 self.__prev_cell_0 = None
2542 epi = gmTools.u_diameter
2543 else:
2544 if self.__prev_cell_0 == med['episode']:
2545 epi = u''
2546 else:
2547 self.__prev_cell_0 = med['episode']
2548 epi = gmTools.coalesce(med['episode'], u'')
2549 self.SetCellValue(row_idx, 0, gmTools.wrap(text = epi, width = 40))
2550
2551 self.SetCellValue(row_idx, 1, med['substance'])
2552 self.SetCellValue(row_idx, 2, u'%s %s' % (med['amount'], med['unit']))
2553 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], u''))
2554 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
2555
2556 if med['is_long_term']:
2557 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
2558 else:
2559 if med['discontinued'] is None:
2560 if med['duration'] is None:
2561 self.SetCellValue(row_idx, 5, u'')
2562 else:
2563 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
2564 else:
2565 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d'))
2566
2567 if med['pk_brand'] is None:
2568 brand = med['preparation']
2569 else:
2570 if med['fake_brand']:
2571 brand = u'%s %s' % (
2572 gmTools.coalesce(med['brand'], u'', _('%s (fake)')),
2573 med['preparation']
2574 )
2575 else:
2576 brand = u'%s %s' % (
2577 gmTools.coalesce(med['brand'], u''),
2578 med['preparation']
2579 )
2580 self.SetCellValue(row_idx, 6, gmTools.wrap(text = brand, width = 35))
2581
2582 elif self.__grouping_mode == u'issue':
2583 if med['pk_health_issue'] is None:
2584 self.__prev_cell_0 = None
2585 issue = u'%s%s' % (
2586 gmTools.u_diameter,
2587 gmTools.coalesce(med['episode'], u'', u' (%s)')
2588 )
2589 else:
2590 if self.__prev_cell_0 == med['health_issue']:
2591 issue = u''
2592 else:
2593 self.__prev_cell_0 = med['health_issue']
2594 issue = med['health_issue']
2595 self.SetCellValue(row_idx, 0, gmTools.wrap(text = issue, width = 40))
2596
2597 self.SetCellValue(row_idx, 1, med['substance'])
2598 self.SetCellValue(row_idx, 2, u'%s %s' % (med['amount'], med['unit']))
2599 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], u''))
2600 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
2601
2602 if med['is_long_term']:
2603 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
2604 else:
2605 if med['discontinued'] is None:
2606 if med['duration'] is None:
2607 self.SetCellValue(row_idx, 5, u'')
2608 else:
2609 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
2610 else:
2611 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d'))
2612
2613 if med['pk_brand'] is None:
2614 brand = med['preparation']
2615 else:
2616 if med['fake_brand']:
2617 brand = u'%s %s' % (
2618 gmTools.coalesce(med['brand'], u'', _('%s (fake)')),
2619 med['preparation']
2620 )
2621 else:
2622 brand = u'%s %s' % (
2623 gmTools.coalesce(med['brand'], u''),
2624 med['preparation']
2625 )
2626 self.SetCellValue(row_idx, 6, gmTools.wrap(text = brand, width = 35))
2627
2628 elif self.__grouping_mode == u'brand':
2629
2630 if med['pk_brand'] is None:
2631 self.__prev_cell_0 = None
2632 brand = u'%s (%s)' % (
2633 gmTools.u_diameter,
2634 med['preparation']
2635 )
2636 else:
2637 if self.__prev_cell_0 == med['brand']:
2638 brand = u''
2639 else:
2640 self.__prev_cell_0 = med['brand']
2641 if med['fake_brand']:
2642 brand = u'%s %s' % (
2643 gmTools.coalesce(med['brand'], u'', _('%s (fake)')),
2644 med['preparation']
2645 )
2646 else:
2647 brand = u'%s %s' % (
2648 gmTools.coalesce(med['brand'], u''),
2649 med['preparation']
2650 )
2651 self.SetCellValue(row_idx, 0, gmTools.wrap(text = brand, width = 35))
2652
2653 self.SetCellValue(row_idx, 1, gmTools.coalesce(med['schedule'], u''))
2654 self.SetCellValue(row_idx, 2, med['substance'])
2655 self.SetCellValue(row_idx, 3, u'%s %s' % (med['amount'], med['unit']))
2656 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
2657
2658 if med['is_long_term']:
2659 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
2660 else:
2661 if med['discontinued'] is None:
2662 if med['duration'] is None:
2663 self.SetCellValue(row_idx, 5, u'')
2664 else:
2665 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
2666 else:
2667 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d'))
2668
2669 if med['pk_health_issue'] is None:
2670 issue = u'%s%s' % (
2671 gmTools.u_diameter,
2672 gmTools.coalesce(med['episode'], u'', u' (%s)')
2673 )
2674 else:
2675 issue = gmTools.coalesce(med['health_issue'], u'')
2676 self.SetCellValue(row_idx, 6, gmTools.wrap(text = issue, width = 40))
2677
2678 else:
2679 raise ValueError('unknown grouping mode [%s]' % self.__grouping_mode)
2680
2681 if med['notes'] is not None:
2682 self.SetCellValue(row_idx, 7, gmTools.wrap(text = med['notes'], width = 50))
2683
2684 if self.__filter_show_unapproved:
2685 self.SetCellValue (
2686 row_idx,
2687 len(labels),
2688 gmTools.bool2subst(med['intake_is_approved_of'], gmTools.u_checkmark_thin, u'', u'?')
2689 )
2690
2691
2692
2693 self.AutoSize()
2694 self.EndBatch()
2695
2697 self.BeginBatch()
2698 self.ClearGrid()
2699
2700
2701 if self.GetNumberRows() > 0:
2702 self.DeleteRows(pos = 0, numRows = self.GetNumberRows())
2703 if self.GetNumberCols() > 0:
2704 self.DeleteCols(pos = 0, numCols = self.GetNumberCols())
2705 self.EndBatch()
2706 self.__row_data = {}
2707 self.__prev_cell_0 = None
2708
2710
2711 if len(self.__row_data) == 0:
2712 return
2713
2714 sel_rows = self.get_selected_rows()
2715 if len(sel_rows) != 1:
2716 return
2717
2718 drug_db = get_drug_database()
2719 if drug_db is None:
2720 return
2721
2722 intake = self.get_selected_data()[0]
2723 if intake['brand'] is None:
2724 drug_db.show_info_on_substance(substance_intake = intake)
2725 else:
2726 drug_db.show_info_on_drug(substance_intake = intake)
2727
2735
2738
2748
2754
2768
2771
2785
2799
2815
2819
2922
2923
2924
2926 self.CreateGrid(0, 1)
2927 self.EnableEditing(0)
2928 self.EnableDragGridSize(1)
2929 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows)
2930
2931 self.SetColLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTER)
2932
2933 self.SetRowLabelSize(0)
2934 self.SetRowLabelAlignment(horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
2935
2936
2937
2939 return self.__patient
2940
2944
2945 patient = property(_get_patient, _set_patient)
2946
2948 return self.__grouping_mode
2949
2953
2954 grouping_mode = property(_get_grouping_mode, _set_grouping_mode)
2955
2957 return self.__filter_show_unapproved
2958
2962
2963 filter_show_unapproved = property(_get_filter_show_unapproved, _set_filter_show_unapproved)
2964
2966 return self.__filter_show_inactive
2967
2971
2972 filter_show_inactive = property(_get_filter_show_inactive, _set_filter_show_inactive)
2973
2974
2975
2977
2978 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells)
2979
2980
2981
2982
2983 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
2984
2986 """Calculate where the mouse is and set the tooltip dynamically."""
2987
2988
2989
2990 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004 row, col = self.XYToCell(x, y)
3005
3006 if row == self.__prev_tooltip_row:
3007 return
3008
3009 self.__prev_tooltip_row = row
3010
3011 try:
3012 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row))
3013 except KeyError:
3014 pass
3015
3020
3021 from Gnumed.wxGladeWidgets import wxgCurrentSubstancesPnl
3022
3023 -class cCurrentSubstancesPnl(wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl, gmRegetMixin.cRegetOnPaintMixin):
3024
3025 """Panel holding a grid with current substances. Used as notebook page."""
3026
3033
3034
3035
3037 """Populate cells with data from model."""
3038 pat = gmPerson.gmCurrentPatient()
3039 if pat.connected:
3040 self._grid_substances.patient = pat
3041 self.__refresh_gfr(pat)
3042 else:
3043 self._grid_substances.patient = None
3044 self.__clear_gfr()
3045 return True
3046
3048 gfr = patient.emr.get_most_recent_results(loinc = gmLOINC.LOINC_gfr_quantity, no_of_results = 1)
3049 if gfr is None:
3050 calc = gmClinicalCalculator.cClinicalCalculator()
3051 calc.patient = patient
3052 gfr = calc.eGFR
3053 if gfr.numeric_value is None:
3054 msg = _('GFR: ?')
3055 tt = gfr.message
3056 else:
3057 msg = _('eGFR: %.1f (%s)') % (
3058 gfr.numeric_value,
3059 gmDateTime.pydt_strftime (
3060 gfr.date_valid,
3061 format = '%b %Y'
3062 )
3063 )
3064 tt = gfr.format (
3065 left_margin = 0,
3066 width = 50,
3067 eol = u'\n',
3068 with_formula = True,
3069 with_warnings = True,
3070 with_variables = False,
3071 with_sub_results = True,
3072 return_list = False
3073 )
3074 else:
3075 msg = u'%s: %s %s (%s)\n' % (
3076 gfr['unified_abbrev'],
3077 gfr['unified_val'],
3078 gmTools.coalesce(gfr['abnormality_indicator'], u'', u' (%s)'),
3079 gmDateTime.pydt_strftime (
3080 gfr['clin_when'],
3081 format = '%b %Y'
3082 )
3083 )
3084 tt = _('GFR reported by path lab')
3085
3086 self._LBL_gfr.SetLabel(msg)
3087 self._LBL_gfr.SetToolTipString(tt)
3088 self._LBL_gfr.Refresh()
3089 self.Layout()
3090
3092 self._LBL_gfr.SetLabel(_('GFR: ?'))
3093 self._LBL_gfr.Refresh()
3094 self.Layout()
3095
3096
3097
3099 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
3100 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
3101 gmDispatcher.connect(signal = u'substance_intake_mod_db', receiver = self._schedule_data_reget)
3102
3103
3104
3106 wx.CallAfter(self.__on_pre_patient_selection)
3107
3109 self._grid_substances.patient = None
3110
3112 wx.CallAfter(self.__on_post_patient_selection)
3113
3115 self._schedule_data_reget()
3116
3119
3122
3125
3128
3131
3134
3137
3140
3143
3146
3149
3152
3155
3158
3161
3164
3165
3166
3167 if __name__ == '__main__':
3168
3169 if len(sys.argv) < 2:
3170 sys.exit()
3171
3172 if sys.argv[1] != 'test':
3173 sys.exit()
3174
3175 from Gnumed.business import gmPersonSearch
3176
3177 pat = gmPersonSearch.ask_for_patient()
3178 if pat is None:
3179 sys.exit()
3180 gmPerson.set_active_patient(patient = pat)
3181
3182
3183 app = wx.PyWidgetTester(size = (600, 600))
3184
3185
3186
3187 manage_substance_intakes()
3188
3189
3190