1 """GNUmed medication/substances handling widgets.
2 """
3
4 __version__ = "$Revision: 1.33 $"
5 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
6
7 import logging, sys, os.path, webbrowser, decimal
8
9
10 import wx, wx.grid
11
12
13 if __name__ == '__main__':
14 sys.path.insert(0, '../../')
15 from Gnumed.pycommon import gmDispatcher, gmCfg, gmShellAPI, gmTools, gmDateTime
16 from Gnumed.pycommon import gmMatchProvider, gmI18N, gmPrinting, gmCfg2
17 from Gnumed.business import gmPerson, gmATC, gmSurgery, gmMedication, gmForms
18 from Gnumed.wxpython import gmGuiHelpers, gmRegetMixin, gmAuthWidgets, gmEditArea, gmMacro
19 from Gnumed.wxpython import gmCfgWidgets, gmListWidgets, gmPhraseWheel, gmFormWidgets
20 from Gnumed.wxpython import gmAllergyWidgets
21
22
23 _log = logging.getLogger('gm.ui')
24 _log.info(__version__)
25
26
27
28
46
48 dbcfg = gmCfg.cCfgSQL()
49
50 default_db = dbcfg.get2 (
51 option = 'external.drug_data.default_source',
52 workplace = gmSurgery.gmCurrentPractice().active_workplace,
53 bias = 'workplace'
54 )
55
56 if default_db is None:
57 gmDispatcher.send('statustext', msg = _('No default drug database configured.'), beep = True)
58 configure_drug_data_source(parent = parent)
59 default_db = dbcfg.get2 (
60 option = 'external.drug_data.default_source',
61 workplace = gmSurgery.gmCurrentPractice().active_workplace,
62 bias = 'workplace'
63 )
64 if default_db is None:
65 gmGuiHelpers.gm_show_error (
66 aMessage = _('There is no default drug database configured.'),
67 aTitle = _('Jumping to drug database')
68 )
69 return None
70
71 try:
72 drug_db = gmMedication.drug_data_source_interfaces[default_db]()
73 except KeyError:
74 _log.error('faulty default drug data source configuration: %s', default_db)
75 configure_drug_data_source(parent = parent)
76 default_db = dbcfg.get2 (
77 option = 'external.drug_data.default_source',
78 workplace = gmSurgery.gmCurrentPractice().active_workplace,
79 bias = 'workplace'
80 )
81 if default_db is None:
82 return None
83
84 pat = gmPerson.gmCurrentPatient()
85 if pat.connected:
86 drug_db.patient = pat
87
88 return drug_db
89
96
97
99
100 dbcfg = gmCfg.cCfgSQL()
101
102 ifap_cmd = dbcfg.get2 (
103 option = 'external.ifap-win.shell_command',
104 workplace = gmSurgery.gmCurrentPractice().active_workplace,
105 bias = 'workplace',
106 default = 'wine "C:\Ifapwin\WIAMDB.EXE"'
107 )
108 found, binary = gmShellAPI.detect_external_binary(ifap_cmd)
109 if not found:
110 gmDispatcher.send('statustext', msg = _('Cannot call IFAP via [%s].') % ifap_cmd)
111 return False
112 ifap_cmd = binary
113
114 if import_drugs:
115 transfer_file = os.path.expanduser(dbcfg.get2 (
116 option = 'external.ifap-win.transfer_file',
117 workplace = gmSurgery.gmCurrentPractice().active_workplace,
118 bias = 'workplace',
119 default = '~/.wine/drive_c/Ifapwin/ifap2gnumed.csv'
120 ))
121
122 try:
123 f = open(transfer_file, 'w+b').close()
124 except IOError:
125 _log.exception('Cannot create IFAP <-> GNUmed transfer file [%s]', transfer_file)
126 gmDispatcher.send('statustext', msg = _('Cannot create IFAP <-> GNUmed transfer file [%s].') % transfer_file)
127 return False
128
129 wx.BeginBusyCursor()
130 gmShellAPI.run_command_in_shell(command = ifap_cmd, blocking = import_drugs)
131 wx.EndBusyCursor()
132
133 if import_drugs:
134
135
136 try:
137 csv_file = open(transfer_file, 'rb')
138 except:
139 _log.exception('cannot access [%s]', fname)
140 csv_file = None
141
142 if csv_file is not None:
143 import csv
144 csv_lines = csv.DictReader (
145 csv_file,
146 fieldnames = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split(),
147 delimiter = ';'
148 )
149 pat = gmPerson.gmCurrentPatient()
150 emr = pat.get_emr()
151
152 epi = emr.add_episode(episode_name = _('Current medication'))
153 for line in csv_lines:
154 narr = u'%sx %s %s %s (\u2258 %s %s) von %s (%s)' % (
155 line['Packungszahl'].strip(),
156 line['Handelsname'].strip(),
157 line['Form'].strip(),
158 line[u'Packungsgr\xf6\xdfe'].strip(),
159 line['Abpackungsmenge'].strip(),
160 line['Einheit'].strip(),
161 line['Hersteller'].strip(),
162 line['PZN'].strip()
163 )
164 emr.add_clin_narrative(note = narr, soap_cat = 's', episode = epi)
165 csv_file.close()
166
167 return True
168
169
170
171
172
174
175 if parent is None:
176 parent = wx.GetApp().GetTopWindow()
177
178 def refresh(lctrl):
179 atcs = gmATC.get_reference_atcs()
180
181 items = [ [
182 a['atc'],
183 a['term'],
184 u'%s' % gmTools.coalesce(a['ddd'], u''),
185 gmTools.coalesce(a['unit'], u''),
186 gmTools.coalesce(a['administrative_route'], u''),
187 gmTools.coalesce(a['comment'], u''),
188 a['version'],
189 a['lang']
190 ] for a in atcs ]
191 lctrl.set_string_items(items)
192 lctrl.set_data(atcs)
193
194 gmListWidgets.get_choices_from_list (
195 parent = parent,
196 msg = _('\nThe ATC codes as known to GNUmed.\n'),
197 caption = _('Showing ATC codes.'),
198 columns = [ u'ATC', _('Term'), u'DDD', _('Unit'), _(u'Route'), _('Comment'), _('Version'), _('Language') ],
199 single_selection = True,
200 refresh_callback = refresh
201 )
202
203
204
206
207 dlg = wx.FileDialog (
208 parent = None,
209 message = _('Choose an ATC import config file'),
210 defaultDir = os.path.expanduser(os.path.join('~', 'gnumed')),
211 defaultFile = '',
212 wildcard = "%s (*.conf)|*.conf|%s (*)|*" % (_('config files'), _('all files')),
213 style = wx.OPEN | wx.HIDE_READONLY | wx.FILE_MUST_EXIST
214 )
215
216 result = dlg.ShowModal()
217 if result == wx.ID_CANCEL:
218 return
219
220 cfg_file = dlg.GetPath()
221 dlg.Destroy()
222
223 conn = gmAuthWidgets.get_dbowner_connection(procedure = _('importing ATC reference data'))
224 if conn is None:
225 return False
226
227 wx.BeginBusyCursor()
228
229 if gmATC.atc_import(cfg_fname = cfg_file, conn = conn):
230 gmDispatcher.send(signal = 'statustext', msg = _('Successfully imported ATC reference data.'))
231 else:
232 gmDispatcher.send(signal = 'statustext', msg = _('Importing ATC reference data failed.'), beep = True)
233
234 wx.EndBusyCursor()
235 return True
236
237
238
240
242
243 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
244
245 query = u"""
246
247 SELECT DISTINCT ON (label)
248 atc_code,
249 label
250 FROM (
251
252 SELECT
253 code as atc_code,
254 (code || ': ' || term || coalesce(' (' || ddd || unit || ')', ''))
255 AS label
256 FROM ref.atc
257 WHERE
258 term %(fragment_condition)s
259 OR
260 code %(fragment_condition)s
261
262 UNION ALL
263
264 SELECT
265 atc_code,
266 (atc_code || ': ' || description)
267 AS label
268 FROM ref.consumable_substance
269 WHERE
270 description %(fragment_condition)s
271 OR
272 atc_code %(fragment_condition)s
273
274 UNION ALL
275
276 SELECT
277 atc_code,
278 (atc_code || ': ' || description || ' (' || preparation || ')')
279 AS label
280 FROM ref.branded_drug
281 WHERE
282 description %(fragment_condition)s
283 OR
284 atc_code %(fragment_condition)s
285
286 -- it would be nice to be able to include clin.vacc_indication but that's hard to do in SQL
287
288 ) AS candidates
289 WHERE atc_code IS NOT NULL
290 ORDER BY label
291 LIMIT 50"""
292
293 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
294 mp.setThresholds(1, 2, 4)
295
296 self.SetToolTipString(_('Select an ATC (Anatomical-Therapeutic-Chemical) code.'))
297 self.matcher = mp
298 self.selection_only = True
299
300
301
302
304
305 if parent is None:
306 parent = wx.GetApp().GetTopWindow()
307
308 def add_from_db(substance):
309 drug_db = get_drug_database(parent = parent)
310 if drug_db is None:
311 return False
312 drug_db.import_drugs()
313 return True
314
315 def edit(substance=None):
316 return edit_consumable_substance(parent = parent, substance = substance, single_entry = (substance is not None))
317
318 def delete(substance):
319 if substance.is_in_use_by_patients:
320 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete this substance. It is in use.'), beep = True)
321 return False
322
323 return gmMedication.delete_consumable_substance(substance = substance['pk'])
324
325 def refresh(lctrl):
326 substs = gmMedication.get_consumable_substances(order_by = 'description')
327 items = [ [
328 s['description'],
329 s['amount'],
330 s['unit'],
331 gmTools.coalesce(s['atc_code'], u''),
332 s['pk']
333 ] for s in substs ]
334 lctrl.set_string_items(items)
335 lctrl.set_data(substs)
336
337 msg = _('\nThese are the consumable substances registered with GNUmed.\n')
338
339 gmListWidgets.get_choices_from_list (
340 parent = parent,
341 msg = msg,
342 caption = _('Showing consumable substances.'),
343 columns = [_('Substance'), _('Amount'), _('Unit'), 'ATC', u'#'],
344 single_selection = True,
345 new_callback = edit,
346 edit_callback = edit,
347 delete_callback = delete,
348 refresh_callback = refresh,
349 left_extra_button = (_('Import'), _('Import consumable substances from a drug database.'), add_from_db)
350 )
351
352
354
355 if substance is not None:
356 if substance.is_in_use_by_patients:
357 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit this substance. It is in use.'), beep = True)
358 return False
359
360 ea = cConsumableSubstanceEAPnl(parent = parent, id = -1)
361 ea.data = substance
362 ea.mode = gmTools.coalesce(substance, 'new', 'edit')
363 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
364 dlg.SetTitle(gmTools.coalesce(substance, _('Adding new consumable substance'), _('Editing consumable substance')))
365 if dlg.ShowModal() == wx.ID_OK:
366 dlg.Destroy()
367 return True
368 dlg.Destroy()
369 return False
370
371
372 from Gnumed.wxGladeWidgets import wxgConsumableSubstanceEAPnl
373
375
393
394
395
396
397
398
399
400
402
403 validity = True
404
405 if self._TCTRL_substance.GetValue().strip() == u'':
406 validity = False
407 self.display_tctrl_as_valid(tctrl = self._TCTRL_substance, valid = False)
408 self._TCTRL_substance.SetFocus()
409 else:
410 self.display_tctrl_as_valid(tctrl = self._TCTRL_substance, valid = True)
411
412 try:
413 decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.'))
414 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
415 except (TypeError, decimal.InvalidOperation):
416 validity = False
417 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
418 self._TCTRL_amount.SetFocus()
419
420 if self._PRW_unit.GetValue().strip() == u'':
421 validity = False
422 self._PRW_unit.display_as_valid(valid = False)
423 self._TCTRL_substance.SetFocus()
424 else:
425 self._PRW_unit.display_as_valid(valid = True)
426
427 if validity is False:
428 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save consumable substance. Missing essential input.'))
429
430 return validity
431
433 subst = gmMedication.create_consumable_substance (
434 substance = self._TCTRL_substance.GetValue().strip(),
435 atc = self._PRW_atc.GetData(),
436 amount = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.')),
437 unit = gmTools.coalesce(self._PRW_unit.GetData(), self._PRW_unit.GetValue().strip(), function_initial = ('strip', None))
438 )
439 success, data = subst.save()
440 if not success:
441 err, msg = data
442 _log.error(err)
443 _log.error(msg)
444 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save consumable substance. %s') % msg, beep = True)
445 return False
446
447 self.data = subst
448 return True
449
451 self.data['description'] = self._TCTRL_substance.GetValue().strip()
452 self.data['atc_code'] = self._PRW_atc.GetData()
453 self.data['amount'] = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.'))
454 self.data['unit'] = gmTools.coalesce(self._PRW_unit.GetData(), self._PRW_unit.GetValue().strip(), function_initial = ('strip', None))
455 success, data = self.data.save()
456
457 if not success:
458 err, msg = data
459 _log.error(err)
460 _log.error(msg)
461 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save consumable substance. %s') % msg, beep = True)
462 return False
463
464 return True
465
467 self._TCTRL_substance.SetValue(u'')
468 self._TCTRL_amount.SetValue(u'')
469 self._PRW_unit.SetText(u'', None)
470 self._PRW_atc.SetText(u'', None)
471
472 self._TCTRL_substance.SetFocus()
473
481
483 self._refresh_as_new()
484
485
486
487
497
498 def delete(component):
499 if component.is_in_use_by_patients:
500 gmDispatcher.send(signal = 'statustext', msg = _('Cannot remove this component from the drug. It is in use.'), beep = True)
501 return False
502
503 return component.containing_drug.remove_component(substance = component['pk_component'])
504
505 def refresh(lctrl):
506 comps = gmMedication.get_drug_components()
507 items = [ [
508 u'%s%s' % (c['brand'], gmTools.coalesce(c['atc_brand'], u'', u' [%s]')),
509 u'%s%s' % (c['substance'], gmTools.coalesce(c['atc_substance'], u'', u' [%s]')),
510 u'%s%s' % (c['amount'], c['unit']),
511 c['preparation'],
512 gmTools.coalesce(c['external_code_brand'], u'', u'%%s [%s]' % c['external_code_type_brand']),
513 c['pk_component']
514 ] for c in comps ]
515 lctrl.set_string_items(items)
516 lctrl.set_data(comps)
517
518 msg = _('\nThese are the components in the drug brands known to GNUmed.\n')
519
520 gmListWidgets.get_choices_from_list (
521 parent = parent,
522 msg = msg,
523 caption = _('Showing drug brand components.'),
524 columns = [_('Brand'), _('Substance'), _('Strength'), _('Preparation'), _('Code'), u'#'],
525 single_selection = True,
526
527 edit_callback = edit,
528 delete_callback = delete,
529 refresh_callback = refresh
530 )
531
532
544
545
546 from Gnumed.wxGladeWidgets import wxgDrugComponentEAPnl
547
548 -class cDrugComponentEAPnl(wxgDrugComponentEAPnl.wxgDrugComponentEAPnl, gmEditArea.cGenericEditAreaMixin):
549
567
568
569
570
571
572
573
574
576 if self.data is not None:
577 if self.data['is_in_use']:
578 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit drug component. It is in use.'), beep = True)
579 return False
580
581 validity = True
582
583 if self._PRW_substance.GetData() is None:
584 validity = False
585 self._PRW_substance.display_as_valid(False)
586 else:
587 self._PRW_substance.display_as_valid(True)
588
589 val = self._TCTRL_amount.GetValue().strip().replace(',', u'.', 1)
590 try:
591 decimal.Decimal(val)
592 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
593 except:
594 validity = False
595 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
596
597 if self._PRW_unit.GetValue().strip() == u'':
598 validity = False
599 self._PRW_unit.display_as_valid(False)
600 else:
601 self._PRW_unit.display_as_valid(True)
602
603 if validity is False:
604 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save drug component. Invalid or missing essential input.'))
605
606 return validity
607
609
610 data = 1
611 data[''] = 1
612 data[''] = 1
613
614
615
616
617
618
619 return False
620 return True
621
623 self.data['pk_consumable_substance'] = self._PRW_substance.GetData()
624 self.data['amount'] = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', u'.', 1))
625 self.data['unit'] = self._PRW_unit.GetValue().strip()
626 return self.data.save()
627
637
639 self._TCTRL_brand.SetValue(u'%s (%s)' % (self.data['brand'], self.data['preparation']))
640 self._TCTRL_components.SetValue(u' / '.join(self.data.containing_drug['components']))
641 details = []
642 if self.data['atc_brand'] is not None:
643 details.append(u'ATC: %s' % self.data['atc_brand'])
644 if self.data['external_code_brand'] is not None:
645 details.append(u'%s: %s' % (self.data['external_code_type_brand'], self.data['external_code_brand']))
646 self._TCTRL_codes.SetValue(u'; '.join(details))
647
648 self._PRW_substance.SetText(self.data['substance'], self.data['pk_consumable_substance'])
649 self._TCTRL_amount.SetValue(u'%s' % self.data['amount'])
650 self._PRW_unit.SetText(self.data['unit'], self.data['unit'])
651
652 self._PRW_substance.SetFocus()
653
655
656
657
658 self._PRW_substance.SetText(u'', None)
659 self._TCTRL_amount.SetValue(u'')
660 self._PRW_unit.SetText(u'', None)
661
662 self._PRW_substance.SetFocus()
663
664
675
676
678
680
681 query = u"""
682 (
683 SELECT DISTINCT ON (preparation)
684 preparation as prep, preparation
685 FROM ref.branded_drug
686 WHERE preparation %(fragment_condition)s
687 ) UNION (
688 SELECT DISTINCT ON (preparation)
689 preparation as prep, preparation
690 FROM clin.substance_intake
691 WHERE preparation %(fragment_condition)s
692 )
693 ORDER BY prep
694 limit 30"""
695
696 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
697 mp.setThresholds(1, 2, 4)
698 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
699 self.SetToolTipString(_('The preparation (form) of the substance or brand.'))
700 self.matcher = mp
701 self.selection_only = False
702
714
715
716
718
719 if brand is not None:
720 if brand['is_in_use']:
721 gmGuiHelpers.gm_show_info (
722 aTitle = _('Managing components of a drug'),
723 aMessage = _(
724 'Cannot manage the components of the branded drug product\n'
725 '\n'
726 ' "%s" (%s)\n'
727 '\n'
728 'because it is currently taken by patients.\n'
729 ) % (brand['brand'], brand['preparation'])
730 )
731 return False
732
733 if parent is None:
734 parent = wx.GetApp().GetTopWindow()
735
736 if brand is None:
737 msg = _('Pick the substances which are components of this drug.')
738 right_col = _('Components of drug')
739 comp_substs = []
740 else:
741 right_col = u'%s (%s)' % (brand['brand'], brand['preparation'])
742 msg = _(
743 'Adjust the components of "%s"\n'
744 '\n'
745 'The drug must contain at least one component. Any given\n'
746 'substance can only be included once per drug.'
747 ) % right_col
748 comp_substs = [ c.substance for c in brand.components ]
749
750 substs = gmMedication.get_consumable_substances(order_by = 'description')
751 choices = [ u'%s %s%s' % (s['description'], s['amount'], s['unit']) for s in substs ]
752 picks = [ u'%s %s%s' % (c['description'], c['amount'], c['unit']) for c in comp_substs ]
753
754 picker = gmListWidgets.cItemPickerDlg (
755 parent,
756 -1,
757 title = _('Managing components of a drug ...'),
758 msg = msg
759 )
760 picker.set_columns(['Substances'], [right_col])
761 picker.set_choices(choices = choices, data = substs)
762 picker.set_picks(picks = picks, data = comp_substs)
763
764 btn_pressed = picker.ShowModal()
765 substs = picker.get_picks()
766 picker.Destroy()
767
768 if btn_pressed != wx.ID_OK:
769 return (False, None)
770
771 if brand is not None:
772 brand.set_substances_as_components(substances = substs)
773
774 return (True, substs)
775
777
778 if parent is None:
779 parent = wx.GetApp().GetTopWindow()
780
781 def add_from_db(brand):
782 drug_db = get_drug_database(parent = parent)
783 if drug_db is None:
784 return False
785 drug_db.import_drugs()
786 return True
787
788 def get_tooltip(brand=None):
789 tt = u'%s %s\n' % (brand['brand'], brand['preparation'])
790 tt += u'\n'
791 tt += u'%s%s%s\n' % (
792 gmTools.bool2subst(brand.is_vaccine, u'%s, ' % _('Vaccine'), u''),
793 u'%s, ' % gmTools.bool2subst(brand['is_in_use'], _('in use'), _('not in use')),
794 gmTools.bool2subst(brand['is_fake_brand'], _('fake'), u'')
795 )
796 tt += gmTools.coalesce(brand['atc'], u'', _('ATC: %s\n'))
797 tt += gmTools.coalesce(brand['external_code'], u'', u'%s: %%s\n' % brand['external_code_type'])
798 if brand['components'] is not None:
799 tt += u'- %s' % u'\n- '.join(brand['components'])
800 return tt
801
802 def edit(brand=None):
803 if brand.is_vaccine:
804 gmGuiHelpers.gm_show_info (
805 aTitle = _('Editing medication'),
806 aMessage = _(
807 'Cannot edit the medication\n'
808 '\n'
809 ' "%s" (%s)\n'
810 '\n'
811 'because it is a vaccine. Please edit it\n'
812 'from the vaccine management section !\n'
813 ) % (brand['brand'], brand['preparation'])
814 )
815 return False
816
817 return edit_branded_drug(parent = parent, branded_drug = brand, single_entry = True)
818
819 def delete(brand):
820 if brand.is_vaccine:
821 gmGuiHelpers.gm_show_info (
822 aTitle = _('Deleting medication'),
823 aMessage = _(
824 'Cannot delete the medication\n'
825 '\n'
826 ' "%s" (%s)\n'
827 '\n'
828 'because it is a vaccine. Please delete it\n'
829 'from the vaccine management section !\n'
830 ) % (brand['brand'], brand['preparation'])
831 )
832 return False
833 gmMedication.delete_branded_drug(brand = brand['pk_brand'])
834 return True
835
836 def new():
837 return edit_branded_drug(parent = parent, branded_drug = None, single_entry = False)
838
839 def refresh(lctrl):
840 drugs = gmMedication.get_branded_drugs()
841 items = [ [
842 u'%s%s' % (
843 d['brand'],
844 gmTools.bool2subst(d['is_fake_brand'], ' (%s)' % _('fake'), u'')
845 ),
846 d['preparation'],
847 gmTools.coalesce(d['atc'], u''),
848 gmTools.coalesce(d['components'], u''),
849 gmTools.coalesce(d['external_code'], u'', u'%%s [%s]' % d['external_code_type']),
850 d['pk_brand']
851 ] for d in drugs ]
852 lctrl.set_string_items(items)
853 lctrl.set_data(drugs)
854
855 msg = _('\nThese are the drug brands known to GNUmed.\n')
856
857 gmListWidgets.get_choices_from_list (
858 parent = parent,
859 msg = msg,
860 caption = _('Showing branded drugs.'),
861 columns = [_('Name'), _('Preparation'), _('ATC'), _('Components'), _('Code'), u'#'],
862 single_selection = True,
863 ignore_OK_button = ignore_OK_button,
864 refresh_callback = refresh,
865 new_callback = new,
866 edit_callback = edit,
867 delete_callback = delete,
868 list_tooltip_callback = get_tooltip,
869 left_extra_button = (_('Import'), _('Import substances and brands from a drug database.'), add_from_db)
870
871
872 )
873
874
876 if branded_drug is not None:
877 if branded_drug['is_in_use']:
878 gmGuiHelpers.gm_show_info (
879 aTitle = _('Editing drug'),
880 aMessage = _(
881 'Cannot edit the branded drug product\n'
882 '\n'
883 ' "%s" (%s)\n'
884 '\n'
885 'because it is currently taken by patients.\n'
886 ) % (branded_drug['brand'], branded_drug['preparation'])
887 )
888 return False
889
890 ea = cBrandedDrugEAPnl(parent = parent, id = -1)
891 ea.data = branded_drug
892 ea.mode = gmTools.coalesce(branded_drug, 'new', 'edit')
893 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
894 dlg.SetTitle(gmTools.coalesce(branded_drug, _('Adding new drug brand'), _('Editing drug brand')))
895 if dlg.ShowModal() == wx.ID_OK:
896 dlg.Destroy()
897 return True
898 dlg.Destroy()
899 return False
900
901
902 from Gnumed.wxGladeWidgets import wxgBrandedDrugEAPnl
903
904 -class cBrandedDrugEAPnl(wxgBrandedDrugEAPnl.wxgBrandedDrugEAPnl, gmEditArea.cGenericEditAreaMixin):
905
922
923
924
925
926
927
928
929
931
932 if self.data is not None:
933 if self.data['is_in_use']:
934 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit drug brand. It is in use.'), beep = True)
935 return False
936
937 validity = True
938
939 if self._PRW_brand.GetValue().strip() == u'':
940 validity = False
941 self._PRW_brand.display_as_valid(False)
942 else:
943 self._PRW_brand.display_as_valid(True)
944
945 if self._PRW_preparation.GetValue().strip() == u'':
946 validity = False
947 self._PRW_preparation.display_as_valid(False)
948 else:
949 self._PRW_preparation.display_as_valid(True)
950
951 if validity is False:
952 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save branded drug. Invalid or missing essential input.'))
953
954 return validity
955
957
958 drug = gmMedication.create_branded_drug (
959 brand_name = self._PRW_brand.GetValue().strip(),
960 preparation = gmTools.coalesce (
961 self._PRW_preparation.GetData(),
962 self._PRW_preparation.GetValue()
963 ).strip(),
964 return_existing = True
965 )
966 drug['is_fake_brand'] = self._CHBOX_is_fake.GetValue()
967 drug['atc'] = self._PRW_atc.GetData()
968 code = self._TCTRL_external_code.GetValue().strip()
969 if code != u'':
970 drug['external_code'] = code
971 drug['external_code_type'] = self._PRW_external_code_type.GetData().strip()
972
973 drug.save()
974
975 if len(self.__component_substances) > 0:
976 drug.set_substances_as_components(substances = self.__component_substances)
977
978 self.data = drug
979
980 return True
981
983 self.data['brand'] = self._PRW_brand.GetValue().strip()
984 self.data['preparation'] = gmTools.coalesce (
985 self._PRW_preparation.GetData(),
986 self._PRW_preparation.GetValue()
987 ).strip()
988 self.data['is_fake_brand'] = self._CHBOX_is_fake.GetValue()
989 self.data['atc'] = self._PRW_atc.GetData()
990 code = self._TCTRL_external_code.GetValue().strip()
991 if code != u'':
992 self.data['external_code'] = code
993 self.data['external_code_type'] = self._PRW_external_code_type.GetData().strip()
994 success, data = self.data.save()
995 if not success:
996 err, msg = data
997 _log.error('problem saving')
998 _log.error('%s', err)
999 _log.error('%s', msg)
1000 return (success is True)
1001
1003 self._PRW_brand.SetText(u'', None)
1004 self._PRW_preparation.SetText(u'', None)
1005 self._CHBOX_is_fake.SetValue(False)
1006 self._TCTRL_components.SetValue(u'')
1007 self._PRW_atc.SetText(u'', None)
1008 self._TCTRL_external_code.SetValue(u'')
1009 self._PRW_external_code_type.SetText(u'', None)
1010
1011 self._PRW_brand.SetFocus()
1012
1013 self.__component_substances = []
1014
1016 self._refresh_as_new()
1017
1034
1035
1036
1046
1048
1050
1051 query = u"""
1052 SELECT
1053 pk,
1054 (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', ''))
1055 AS brand
1056 FROM ref.branded_drug
1057 WHERE description %(fragment_condition)s
1058 ORDER BY brand
1059 LIMIT 50"""
1060
1061 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
1062 mp.setThresholds(2, 3, 4)
1063 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1064 self.SetToolTipString(_('The brand name of the drug.'))
1065 self.matcher = mp
1066 self.selection_only = False
1067
1068
1069
1070
1072
1074
1075 query = u"""
1076 SELECT DISTINCT ON (sched)
1077 schedule as sched,
1078 schedule
1079 FROM clin.substance_intake
1080 WHERE schedule %(fragment_condition)s
1081 ORDER BY sched
1082 LIMIT 50"""
1083
1084 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
1085 mp.setThresholds(1, 2, 4)
1086 mp.word_separators = '[ \t=+&:@]+'
1087 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1088 self.SetToolTipString(_('The schedule for taking this substance.'))
1089 self.matcher = mp
1090 self.selection_only = False
1091
1092
1094
1095 if intake['is_currently_active']:
1096 intake['discontinued'] = gmDateTime.pydt_now_here()
1097 if intake['discontinue_reason'] is None:
1098 intake['discontinue_reason'] = u'%s %s' % (_('not tolerated:'), _('discontinued due to allergy or intolerance'))
1099 else:
1100 if not intake['discontinue_reason'].startswith(_('not tolerated:')):
1101 intake['discontinue_reason'] = u'%s %s' % (_('not tolerated:'), intake['discontinue_reason'])
1102 if not intake.save():
1103 return False
1104
1105 allg = intake.turn_into_allergy(encounter_id = emr.active_encounter['pk_encounter'])
1106
1107 brand = intake.containing_drug
1108 if brand is not None:
1109 comps = [ c['substance'] for c in brand.components ]
1110 if len(comps) > 1:
1111 gmGuiHelpers.gm_show_info (
1112 aTitle = _(u'Documented an allergy'),
1113 aMessage = _(
1114 u'An allergy was documented against the substance:\n'
1115 u'\n'
1116 u' [%s]\n'
1117 u'\n'
1118 u'This substance was taken with the multi-component brand:\n'
1119 u'\n'
1120 u' [%s (%s)]\n'
1121 u'\n'
1122 u'Note that ALL components of this brand were discontinued.'
1123 ) % (
1124 intake['substance'],
1125 intake['brand'],
1126 u' & '.join(comps)
1127 )
1128 )
1129
1130 if parent is None:
1131 parent = wx.GetApp().GetTopWindow()
1132
1133 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent = parent, id = -1)
1134 dlg.ShowModal()
1135
1136 return True
1137
1138 from Gnumed.wxGladeWidgets import wxgCurrentMedicationEAPnl
1139
1140 -class cSubstanceIntakeEAPnl(wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl, gmEditArea.cGenericEditAreaMixin):
1141
1159
1161
1162 self._PRW_component.add_callback_on_lose_focus(callback = self._on_leave_component)
1163 self._PRW_component.selection_only = True
1164
1165 self._PRW_substance.add_callback_on_lose_focus(callback = self._on_leave_substance)
1166 self._PRW_substance.selection_only = True
1167
1169 emr = gmPerson.gmCurrentPatient().get_emr()
1170
1171 state = emr.allergy_state
1172 if state['last_confirmed'] is None:
1173 confirmed = _('never')
1174 else:
1175 confirmed = state['last_confirmed'].strftime('%Y %B %d').decode(gmI18N.get_encoding())
1176 msg = _(u'%s, last confirmed %s\n') % (state.state_string, confirmed)
1177 msg += gmTools.coalesce(state['comment'], u'', _('Comment (%s): %%s\n') % state['modified_by'])
1178 msg += u'\n'
1179
1180 for allergy in emr.get_allergies():
1181 msg += u'%s (%s, %s): %s\n' % (
1182 allergy['descriptor'],
1183 allergy['l10n_type'],
1184 gmTools.bool2subst(allergy['definite'], _('definite'), _('suspected'), u'?'),
1185 gmTools.coalesce(allergy['reaction'], _('reaction not recorded'))
1186 )
1187
1188 self._LBL_allergies.SetLabel(msg)
1189
1190
1191
1193
1194 validity = True
1195
1196 has_component = (self._PRW_component.GetData() is not None)
1197 has_substance = (self._PRW_substance.GetValue().strip() != u'')
1198
1199
1200 if (has_component is False) and (has_substance is False):
1201 self._PRW_substance.display_as_valid(False)
1202 self._PRW_component.display_as_valid(False)
1203 validity = False
1204 else:
1205 self._PRW_substance.display_as_valid(True)
1206 self._PRW_component.display_as_valid(True)
1207
1208
1209 if not has_component:
1210 if self._PRW_preparation.GetValue().strip() == u'':
1211 self._PRW_preparation.display_as_valid(False)
1212 validity = False
1213 else:
1214 self._PRW_preparation.display_as_valid(True)
1215
1216
1217 if self._CHBOX_approved.IsChecked():
1218 if self._PRW_episode.GetValue().strip() == u'':
1219 self._PRW_episode.display_as_valid(False)
1220 validity = False
1221 else:
1222 self._PRW_episode.display_as_valid(True)
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238 end = self._DP_discontinued.GetData()
1239 if end is not None:
1240 start = self._DP_started.GetData()
1241 if start > end:
1242 self._DP_started.display_as_valid(False)
1243 self._DP_discontinued.display_as_valid(False)
1244 validity = False
1245 else:
1246 self._DP_started.display_as_valid(True)
1247 self._DP_discontinued.display_as_valid(True)
1248
1249 if validity is False:
1250 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save substance intake. Invalid or missing essential input.'))
1251
1252 return validity
1253
1255
1256 emr = gmPerson.gmCurrentPatient().get_emr()
1257 epi = self._PRW_episode.GetData(can_create = True)
1258
1259 if self._PRW_substance.GetData() is None:
1260
1261 intake = emr.add_substance_intake (
1262 pk_component = self._PRW_component.GetData(),
1263 episode = epi
1264 )
1265 else:
1266 intake = emr.add_substance_intake (
1267 pk_substance = self._PRW_substance.GetData(),
1268 episode = epi,
1269 preparation = self._PRW_preparation.GetValue().strip()
1270 )
1271
1272 intake['started'] = self._DP_started.GetData()
1273 intake['discontinued'] = self._DP_discontinued.GetData()
1274 if intake['discontinued'] is None:
1275 intake['discontinue_reason'] = None
1276 else:
1277 intake['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
1278 intake['schedule'] = self._PRW_schedule.GetValue().strip()
1279 intake['aim'] = self._PRW_aim.GetValue().strip()
1280 intake['notes'] = self._PRW_notes.GetValue().strip()
1281 intake['is_long_term'] = self._CHBOX_long_term.IsChecked()
1282 intake['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
1283 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
1284 intake['duration'] = None
1285 else:
1286 intake['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
1287 intake.save()
1288
1289 self.data = intake
1290
1291 return True
1292
1294
1295
1296 self.data['started'] = self._DP_started.GetData()
1297 self.data['discontinued'] = self._DP_discontinued.GetData()
1298 if self.data['discontinued'] is None:
1299 self.data['discontinue_reason'] = None
1300 else:
1301 self.data['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
1302 self.data['schedule'] = self._PRW_schedule.GetValue()
1303 self.data['is_long_term'] = self._CHBOX_long_term.IsChecked()
1304 self.data['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
1305 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
1306 self.data['duration'] = None
1307 else:
1308 self.data['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
1309
1310
1311 self.data['preparation'] = self._PRW_preparation.GetValue()
1312
1313
1314 self.data['aim'] = self._PRW_aim.GetValue()
1315 self.data['notes'] = self._PRW_notes.GetValue()
1316 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True)
1317
1318 self.data.save()
1319
1320 return True
1321
1323 self._PRW_component.SetText(u'', None)
1324 self._TCTRL_brand_ingredients.SetValue(u'')
1325
1326 self._PRW_substance.SetText(u'', None)
1327 self._PRW_substance.Enable(True)
1328
1329 self._PRW_preparation.SetText(u'', None)
1330 self._PRW_preparation.Enable(True)
1331
1332 self._PRW_schedule.SetText(u'', None)
1333 self._PRW_duration.SetText(u'', None)
1334 self._PRW_aim.SetText(u'', None)
1335 self._PRW_notes.SetText(u'', None)
1336 self._PRW_episode.SetText(u'', None)
1337
1338 self._CHBOX_long_term.SetValue(False)
1339 self._CHBOX_approved.SetValue(True)
1340
1341 self._DP_started.SetData(gmDateTime.pydt_now_here())
1342 self._DP_discontinued.SetData(None)
1343 self._PRW_discontinue_reason.SetValue(u'')
1344
1345 self.__refresh_allergies()
1346
1347 self._PRW_component.SetFocus()
1348
1350
1351 self._TCTRL_brand_ingredients.SetValue(u'')
1352
1353 if self.data['pk_brand'] is None:
1354 self.__refresh_from_existing_substance()
1355 else:
1356 self.__refresh_from_existing_component()
1357
1358 self._PRW_component.Enable(False)
1359 self._PRW_substance.Enable(False)
1360
1361 if self.data['is_long_term']:
1362 self._CHBOX_long_term.SetValue(True)
1363 self._PRW_duration.Enable(False)
1364 self._PRW_duration.SetText(gmTools.u_infinity, None)
1365 self._BTN_discontinued_as_planned.Enable(False)
1366 else:
1367 self._CHBOX_long_term.SetValue(False)
1368 self._PRW_duration.Enable(True)
1369 self._BTN_discontinued_as_planned.Enable(True)
1370 if self.data['duration'] is None:
1371 self._PRW_duration.SetText(u'', None)
1372 else:
1373 self._PRW_duration.SetText(gmDateTime.format_interval(self.data['duration'], gmDateTime.acc_days), self.data['duration'])
1374 self._PRW_aim.SetText(gmTools.coalesce(self.data['aim'], u''), self.data['aim'])
1375 self._PRW_notes.SetText(gmTools.coalesce(self.data['notes'], u''), self.data['notes'])
1376 self._PRW_episode.SetData(self.data['pk_episode'])
1377 self._PRW_schedule.SetText(gmTools.coalesce(self.data['schedule'], u''), self.data['schedule'])
1378
1379 self._CHBOX_approved.SetValue(self.data['intake_is_approved_of'])
1380
1381 self._DP_started.SetData(self.data['started'])
1382 self._DP_discontinued.SetData(self.data['discontinued'])
1383 self._PRW_discontinue_reason.SetValue(gmTools.coalesce(self.data['discontinue_reason'], u''))
1384 if self.data['discontinued'] is not None:
1385 self._PRW_discontinue_reason.Enable()
1386
1387 self.__refresh_allergies()
1388
1389 self._PRW_schedule.SetFocus()
1390
1392 self._LBL_component.Enable(False)
1393 self._PRW_component.SetText(u'', None)
1394 self._PRW_component.display_as_valid(True)
1395
1396 self._PRW_substance.SetText (
1397 u'%s %s%s' % (self.data['substance'], self.data['amount'], self.data['unit']),
1398 self.data['pk_substance']
1399 )
1400
1401 self._PRW_preparation.SetText(gmTools.coalesce(self.data['preparation'], u''), self.data['preparation'])
1402 self._PRW_preparation.Enable(True)
1403
1405 self._PRW_component.SetText (
1406 u'%s %s%s (%s)' % (self.data['substance'], self.data['amount'], self.data['unit'], self.data['brand']),
1407 self.data['pk_drug_component']
1408 )
1409
1410 brand = gmMedication.cBrandedDrug(aPK_obj = self.data['pk_brand'])
1411 if brand['components'] is not None:
1412 self._TCTRL_brand_ingredients.SetValue(u'%s\n- %s' % (self.data['brand'], u'\n- '.join(brand['components'])))
1413
1414 self._LBL_or.Enable(False)
1415 self._LBL_substance.Enable(False)
1416 self._PRW_substance.SetText(u'', None)
1417 self._PRW_substance.display_as_valid(True)
1418
1419 self._PRW_preparation.SetText(self.data['preparation'], self.data['preparation'])
1420 self._PRW_preparation.Enable(False)
1421
1423 self._refresh_as_new()
1424
1425
1426
1428 if self._PRW_component.GetData() is None:
1429 self._LBL_or.Enable(True)
1430 self._PRW_component.SetText(u'', None)
1431 self._LBL_substance.Enable(True)
1432 self._PRW_substance.Enable(True)
1433 self._LBL_preparation.Enable(True)
1434 self._PRW_preparation.Enable(True)
1435 self._PRW_preparation.SetText(u'', None)
1436 self._TCTRL_brand_ingredients.SetValue(u'')
1437 else:
1438 self._LBL_or.Enable(False)
1439 self._LBL_substance.Enable(False)
1440 self._PRW_substance.SetText(u'', None)
1441 self._PRW_substance.display_as_valid(True)
1442 self._PRW_substance.Enable(False)
1443 self._LBL_preparation.Enable(False)
1444 self._PRW_preparation.Enable(False)
1445 comp = gmMedication.cDrugComponent(aPK_obj = self._PRW_component.GetData())
1446 self._PRW_preparation.SetText(comp['preparation'], comp['preparation'])
1447 brand = comp.containing_drug
1448 if brand['components'] is not None:
1449 self._TCTRL_brand_ingredients.SetValue(u'%s\n- %s' % (brand['brand'], u'\n- '.join(brand['components'])))
1450
1452 if self._PRW_substance.GetData() is None:
1453 self._LBL_or.Enable(True)
1454 self._LBL_component.Enable(True)
1455 self._PRW_component.Enable(True)
1456 self._PRW_substance.SetText(u'', None)
1457 else:
1458 self._LBL_or.Enable(False)
1459 self._LBL_component.Enable(False)
1460 self._PRW_component.SetText(u'', None)
1461 self._PRW_component.display_as_valid(True)
1462 self._PRW_component.Enable(False)
1463 self._LBL_preparation.Enable(True)
1464 self._PRW_preparation.Enable(True)
1465 self._TCTRL_brand_ingredients.SetValue(u'')
1466
1468 if self._DP_discontinued.GetData() is None:
1469 self._PRW_discontinue_reason.Enable(False)
1470 else:
1471 self._PRW_discontinue_reason.Enable(True)
1472
1475
1478
1508
1510 if self._CHBOX_long_term.IsChecked() is True:
1511 self._PRW_duration.Enable(False)
1512 self._BTN_discontinued_as_planned.Enable(False)
1513 self._PRW_discontinue_reason.Enable(False)
1514 else:
1515 self._PRW_duration.Enable(True)
1516 self._BTN_discontinued_as_planned.Enable(True)
1517 self._PRW_discontinue_reason.Enable(True)
1518
1519 self.__refresh_allergies()
1520
1530
1532
1533 subst = gmMedication.cSubstanceIntakeEntry(aPK_obj = substance)
1534 msg = _(
1535 '\n'
1536 '[%s]\n'
1537 '\n'
1538 'It may be prudent to edit (before deletion) the details\n'
1539 'of this substance intake entry so as to leave behind\n'
1540 'some indication of why it was deleted.\n'
1541 ) % subst.format()
1542
1543 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
1544 parent,
1545 -1,
1546 caption = _('Deleting medication / substance intake'),
1547 question = msg,
1548 button_defs = [
1549 {'label': _('&Edit'), 'tooltip': _('Allow editing of substance intake entry before deletion.'), 'default': True},
1550 {'label': _('&Delete'), 'tooltip': _('Delete immediately without editing first.')},
1551 {'label': _('&Cancel'), 'tooltip': _('Abort. Do not delete or edit substance intake entry.')}
1552 ]
1553 )
1554
1555 edit_first = dlg.ShowModal()
1556 dlg.Destroy()
1557
1558 if edit_first == wx.ID_CANCEL:
1559 return
1560
1561 if edit_first == wx.ID_YES:
1562 edit_intake_of_substance(parent = parent, substance = subst)
1563 delete_it = gmGuiHelpers.gm_show_question (
1564 aMessage = _('Now delete substance intake entry ?'),
1565 aTitle = _('Deleting medication / substance intake')
1566 )
1567 else:
1568 delete_it = True
1569
1570 if not delete_it:
1571 return
1572
1573 gmMedication.delete_substance_intake(substance = substance)
1574
1589
1590
1591
1592
1620
1622
1623 if parent is None:
1624 parent = wx.GetApp().GetTopWindow()
1625
1626
1627 dbcfg = gmCfg.cCfgSQL()
1628 option = u'form_templates.medication_list'
1629
1630 template = dbcfg.get2 (
1631 option = option,
1632 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1633 bias = 'user'
1634 )
1635
1636 if template is None:
1637 template = configure_medication_list_template(parent = parent)
1638 if template is None:
1639 gmGuiHelpers.gm_show_error (
1640 aMessage = _('There is no medication list template configured.'),
1641 aTitle = _('Printing medication list')
1642 )
1643 return False
1644 else:
1645 try:
1646 name, ver = template.split(u' - ')
1647 except:
1648 _log.exception('problem splitting medication list template name [%s]', template)
1649 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading medication list template.'), beep = True)
1650 return False
1651 template = gmForms.get_form_template(name_long = name, external_version = ver)
1652 if template is None:
1653 gmGuiHelpers.gm_show_error (
1654 aMessage = _('Cannot load medication list template [%s - %s]') % (name, ver),
1655 aTitle = _('Printing medication list')
1656 )
1657 return False
1658
1659
1660 try:
1661 meds_list = template.instantiate()
1662 except KeyError:
1663 _log.exception('cannot instantiate medication list template [%s]', template)
1664 gmGuiHelpers.gm_show_error (
1665 aMessage = _('Invalid medication list template [%s - %s (%s)]') % (name, ver, template['engine']),
1666 aTitle = _('Printing medication list')
1667 )
1668 return False
1669
1670 ph = gmMacro.gmPlaceholderHandler()
1671
1672 meds_list.substitute_placeholders(data_source = ph)
1673 pdf_name = meds_list.generate_output()
1674 if pdf_name is None:
1675 gmGuiHelpers.gm_show_error (
1676 aMessage = _('Error generating the medication list.'),
1677 aTitle = _('Printing medication list')
1678 )
1679 return False
1680
1681
1682 printed = gmPrinting.print_file_by_shellscript(filename = pdf_name, jobtype = 'medication_list')
1683 if not printed:
1684 gmGuiHelpers.gm_show_error (
1685 aMessage = _('Error printing the medication list.'),
1686 aTitle = _('Printing medication list')
1687 )
1688 return False
1689
1690 pat = gmPerson.gmCurrentPatient()
1691 emr = pat.get_emr()
1692 epi = emr.add_episode(episode_name = 'administration', is_open = False)
1693 emr.add_clin_narrative (
1694 soap_cat = None,
1695 note = _('medication list printed from template [%s - %s]') % (template['name_long'], template['external_version']),
1696 episode = epi
1697 )
1698
1699 return True
1700
1702 """A grid class for displaying current substance intake.
1703
1704 - does NOT listen to the currently active patient
1705 - thereby it can display any patient at any time
1706 """
1708
1709 wx.grid.Grid.__init__(self, *args, **kwargs)
1710
1711 self.__patient = None
1712 self.__row_data = {}
1713 self.__prev_row = None
1714 self.__prev_tooltip_row = None
1715 self.__prev_cell_0 = None
1716 self.__grouping_mode = u'episode'
1717 self.__filter_show_unapproved = True
1718 self.__filter_show_inactive = False
1719
1720 self.__grouping2col_labels = {
1721 u'episode': [
1722 _('Episode'),
1723 _('Substance'),
1724 _('Dose'),
1725 _('Schedule'),
1726 _('Started'),
1727 _('Duration / Until'),
1728 _('Brand')
1729 ],
1730 u'brand': [
1731 _('Brand'),
1732 _('Schedule'),
1733 _('Substance'),
1734 _('Dose'),
1735 _('Started'),
1736 _('Duration / Until'),
1737 _('Episode')
1738 ]
1739 }
1740
1741 self.__grouping2order_by_clauses = {
1742 u'episode': u'pk_health_issue nulls first, episode, substance, started',
1743 u'brand': u'brand nulls last, substance, started'
1744 }
1745
1746 self.__init_ui()
1747 self.__register_events()
1748
1749
1750
1752
1753 sel_block_top_left = self.GetSelectionBlockTopLeft()
1754 sel_block_bottom_right = self.GetSelectionBlockBottomRight()
1755 sel_cols = self.GetSelectedCols()
1756 sel_rows = self.GetSelectedRows()
1757
1758 selected_cells = []
1759
1760
1761 selected_cells += self.GetSelectedCells()
1762
1763
1764 selected_cells += list (
1765 (row, col)
1766 for row in sel_rows
1767 for col in xrange(self.GetNumberCols())
1768 )
1769
1770
1771 selected_cells += list (
1772 (row, col)
1773 for row in xrange(self.GetNumberRows())
1774 for col in sel_cols
1775 )
1776
1777
1778 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()):
1779 selected_cells += [
1780 (row, col)
1781 for row in xrange(top_left[0], bottom_right[0] + 1)
1782 for col in xrange(top_left[1], bottom_right[1] + 1)
1783 ]
1784
1785 return set(selected_cells)
1786
1788 rows = {}
1789
1790 for row, col in self.get_selected_cells():
1791 rows[row] = True
1792
1793 return rows.keys()
1794
1797
1799
1800 self.empty_grid()
1801
1802 if self.__patient is None:
1803 return
1804
1805 emr = self.__patient.get_emr()
1806 meds = emr.get_current_substance_intake (
1807 order_by = self.__grouping2order_by_clauses[self.__grouping_mode],
1808 include_unapproved = self.__filter_show_unapproved,
1809 include_inactive = self.__filter_show_inactive
1810 )
1811 if not meds:
1812 return
1813
1814 self.BeginBatch()
1815
1816
1817 labels = self.__grouping2col_labels[self.__grouping_mode]
1818 if self.__filter_show_unapproved:
1819 self.AppendCols(numCols = len(labels) + 1)
1820 else:
1821 self.AppendCols(numCols = len(labels))
1822 for col_idx in range(len(labels)):
1823 self.SetColLabelValue(col_idx, labels[col_idx])
1824 if self.__filter_show_unapproved:
1825 self.SetColLabelValue(len(labels), u'OK?')
1826 self.SetColSize(len(labels), 40)
1827
1828 self.AppendRows(numRows = len(meds))
1829
1830
1831 for row_idx in range(len(meds)):
1832 med = meds[row_idx]
1833 self.__row_data[row_idx] = med
1834
1835 if med['is_currently_active'] is True:
1836 atcs = []
1837 if med['atc_substance'] is not None:
1838 atcs.append(med['atc_substance'])
1839
1840
1841
1842 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (med['substance'],))
1843 if allg not in [None, False]:
1844 attr = self.GetOrCreateCellAttr(row_idx, 0)
1845 if allg['type'] == u'allergy':
1846 attr.SetTextColour('red')
1847 else:
1848 attr.SetTextColour('yellow')
1849 self.SetRowAttr(row_idx, attr)
1850 else:
1851 attr = self.GetOrCreateCellAttr(row_idx, 0)
1852 attr.SetTextColour('grey')
1853 self.SetRowAttr(row_idx, attr)
1854
1855 if self.__grouping_mode == u'episode':
1856 if med['pk_episode'] is None:
1857 self.__prev_cell_0 = None
1858 epi = gmTools.u_diameter
1859 else:
1860 if self.__prev_cell_0 == med['episode']:
1861 epi = u''
1862 else:
1863 self.__prev_cell_0 = med['episode']
1864 epi = gmTools.coalesce(med['episode'], u'')
1865 self.SetCellValue(row_idx, 0, gmTools.wrap(text = epi, width = 40))
1866
1867 self.SetCellValue(row_idx, 1, med['substance'])
1868 self.SetCellValue(row_idx, 2, u'%s%s' % (med['amount'], med['unit']))
1869 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], u''))
1870 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
1871
1872 if med['is_long_term']:
1873 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
1874 else:
1875 if med['discontinued'] is None:
1876 if med['duration'] is None:
1877 self.SetCellValue(row_idx, 5, u'')
1878 else:
1879 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
1880 else:
1881 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d'))
1882
1883 if med['pk_brand'] is None:
1884 brand = u''
1885 else:
1886 if med['fake_brand']:
1887 brand = gmTools.coalesce(med['brand'], u'', _('%s (fake)'))
1888 else:
1889 brand = gmTools.coalesce(med['brand'], u'')
1890 self.SetCellValue(row_idx, 6, gmTools.wrap(text = brand, width = 35))
1891
1892 elif self.__grouping_mode == u'brand':
1893
1894 if med['pk_brand'] is None:
1895 self.__prev_cell_0 = None
1896 brand = gmTools.u_diameter
1897 else:
1898 if self.__prev_cell_0 == med['brand']:
1899 brand = u''
1900 else:
1901 self.__prev_cell_0 = med['brand']
1902 if med['fake_brand']:
1903 brand = gmTools.coalesce(med['brand'], u'', _('%s (fake)'))
1904 else:
1905 brand = gmTools.coalesce(med['brand'], u'')
1906 self.SetCellValue(row_idx, 0, gmTools.wrap(text = brand, width = 35))
1907
1908 self.SetCellValue(row_idx, 1, gmTools.coalesce(med['schedule'], u''))
1909 self.SetCellValue(row_idx, 2, med['substance'])
1910 self.SetCellValue(row_idx, 3, u'%s%s' % (med['amount'], med['unit']))
1911 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
1912
1913 if med['is_long_term']:
1914 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
1915 else:
1916 if med['discontinued'] is None:
1917 if med['duration'] is None:
1918 self.SetCellValue(row_idx, 5, u'')
1919 else:
1920 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
1921 else:
1922 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d'))
1923
1924 if med['pk_episode'] is None:
1925 epi = u''
1926 else:
1927 epi = gmTools.coalesce(med['episode'], u'')
1928 self.SetCellValue(row_idx, 6, gmTools.wrap(text = epi, width = 40))
1929
1930 else:
1931 raise ValueError('unknown grouping mode [%s]' % self.__grouping_mode)
1932
1933 if self.__filter_show_unapproved:
1934 self.SetCellValue (
1935 row_idx,
1936 len(labels),
1937 gmTools.bool2subst(med['intake_is_approved_of'], gmTools.u_checkmark_thin, u'', u'?')
1938 )
1939
1940
1941
1942 self.AutoSize()
1943 self.EndBatch()
1944
1946 self.BeginBatch()
1947 self.ClearGrid()
1948
1949
1950 if self.GetNumberRows() > 0:
1951 self.DeleteRows(pos = 0, numRows = self.GetNumberRows())
1952 if self.GetNumberCols() > 0:
1953 self.DeleteCols(pos = 0, numCols = self.GetNumberCols())
1954 self.EndBatch()
1955 self.__row_data = {}
1956 self.__prev_cell_0 = None
1957
1959
1960 if len(self.__row_data) == 0:
1961 return
1962
1963 sel_rows = self.get_selected_rows()
1964 if len(sel_rows) != 1:
1965 return
1966
1967 drug_db = get_drug_database()
1968 if drug_db is None:
1969 return
1970
1971 intake = self.get_selected_data()[0]
1972 if intake['brand'] is None:
1973 drug_db.show_info_on_substance(substance_intake = intake)
1974 else:
1975 drug_db.show_info_on_drug(substance_intake = intake)
1976
1992
2005
2013
2027
2030
2044
2058
2074
2078
2182
2183
2184
2186 self.CreateGrid(0, 1)
2187 self.EnableEditing(0)
2188 self.EnableDragGridSize(1)
2189 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows)
2190
2191 self.SetColLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTER)
2192
2193 self.SetRowLabelSize(0)
2194 self.SetRowLabelAlignment(horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
2195
2196
2197
2199 return self.__patient
2200
2204
2205 patient = property(_get_patient, _set_patient)
2206
2208 return self.__grouping_mode
2209
2213
2214 grouping_mode = property(_get_grouping_mode, _set_grouping_mode)
2215
2217 return self.__filter_show_unapproved
2218
2222
2223 filter_show_unapproved = property(_get_filter_show_unapproved, _set_filter_show_unapproved)
2224
2226 return self.__filter_show_inactive
2227
2231
2232 filter_show_inactive = property(_get_filter_show_inactive, _set_filter_show_inactive)
2233
2234
2235
2237
2238 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells)
2239
2240
2241
2242
2243 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
2244
2246 """Calculate where the mouse is and set the tooltip dynamically."""
2247
2248
2249
2250 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264 row, col = self.XYToCell(x, y)
2265
2266 if row == self.__prev_tooltip_row:
2267 return
2268
2269 self.__prev_tooltip_row = row
2270
2271 try:
2272 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row))
2273 except KeyError:
2274 pass
2275
2280
2281 from Gnumed.wxGladeWidgets import wxgCurrentSubstancesPnl
2282
2283 -class cCurrentSubstancesPnl(wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl, gmRegetMixin.cRegetOnPaintMixin):
2284
2285 """Panel holding a grid with current substances. Used as notebook page."""
2286
2293
2294
2295
2304
2305
2306
2308 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
2309 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._schedule_data_reget)
2310 gmDispatcher.connect(signal = u'substance_intake_mod_db', receiver = self._schedule_data_reget)
2311
2312
2313
2315 wx.CallAfter(self.__on_pre_patient_selection)
2316
2318 self._grid_substances.patient = None
2319
2322
2325
2328
2331
2334
2337
2340
2343
2346
2349
2352
2355
2358
2361
2362
2363
2364 if __name__ == '__main__':
2365
2366 if len(sys.argv) < 2:
2367 sys.exit()
2368
2369 if sys.argv[1] != 'test':
2370 sys.exit()
2371
2372 from Gnumed.pycommon import gmI18N
2373
2374 gmI18N.activate_locale()
2375 gmI18N.install_domain(domain = 'gnumed')
2376
2377
2378 app = wx.PyWidgetTester(size = (600, 600))
2379
2380 app.SetWidget(cSubstancePhraseWheel, -1)
2381 app.MainLoop()
2382
2383
2384