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, 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, gmNetworkTools
17 from Gnumed.business import gmPerson, gmATC, gmSurgery, gmMedication, gmForms, gmStaff
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 drug_db = gmMedication.drug_data_source_interfaces[default_db]()
84
85 pat = gmPerson.gmCurrentPatient()
86 if pat.connected:
87 drug_db.patient = pat
88
89 return drug_db
90
97
98
100
101 dbcfg = gmCfg.cCfgSQL()
102
103 ifap_cmd = dbcfg.get2 (
104 option = 'external.ifap-win.shell_command',
105 workplace = gmSurgery.gmCurrentPractice().active_workplace,
106 bias = 'workplace',
107 default = 'wine "C:\Ifapwin\WIAMDB.EXE"'
108 )
109 found, binary = gmShellAPI.detect_external_binary(ifap_cmd)
110 if not found:
111 gmDispatcher.send('statustext', msg = _('Cannot call IFAP via [%s].') % ifap_cmd)
112 return False
113 ifap_cmd = binary
114
115 if import_drugs:
116 transfer_file = os.path.expanduser(dbcfg.get2 (
117 option = 'external.ifap-win.transfer_file',
118 workplace = gmSurgery.gmCurrentPractice().active_workplace,
119 bias = 'workplace',
120 default = '~/.wine/drive_c/Ifapwin/ifap2gnumed.csv'
121 ))
122
123 try:
124 f = open(transfer_file, 'w+b').close()
125 except IOError:
126 _log.exception('Cannot create IFAP <-> GNUmed transfer file [%s]', transfer_file)
127 gmDispatcher.send('statustext', msg = _('Cannot create IFAP <-> GNUmed transfer file [%s].') % transfer_file)
128 return False
129
130 wx.BeginBusyCursor()
131 gmShellAPI.run_command_in_shell(command = ifap_cmd, blocking = import_drugs)
132 wx.EndBusyCursor()
133
134 if import_drugs:
135
136
137 try:
138 csv_file = open(transfer_file, 'rb')
139 except:
140 _log.exception('cannot access [%s]', fname)
141 csv_file = None
142
143 if csv_file is not None:
144 import csv
145 csv_lines = csv.DictReader (
146 csv_file,
147 fieldnames = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split(),
148 delimiter = ';'
149 )
150 pat = gmPerson.gmCurrentPatient()
151 emr = pat.get_emr()
152
153 epi = emr.add_episode(episode_name = _('Current medication'))
154 for line in csv_lines:
155 narr = u'%sx %s %s %s (\u2258 %s %s) von %s (%s)' % (
156 line['Packungszahl'].strip(),
157 line['Handelsname'].strip(),
158 line['Form'].strip(),
159 line[u'Packungsgr\xf6\xdfe'].strip(),
160 line['Abpackungsmenge'].strip(),
161 line['Einheit'].strip(),
162 line['Hersteller'].strip(),
163 line['PZN'].strip()
164 )
165 emr.add_clin_narrative(note = narr, soap_cat = 's', episode = epi)
166 csv_file.close()
167
168 return True
169
170
171
172
173
175
176 if parent is None:
177 parent = wx.GetApp().GetTopWindow()
178
179 def refresh(lctrl):
180 atcs = gmATC.get_reference_atcs()
181
182 items = [ [
183 a['atc'],
184 a['term'],
185 u'%s' % gmTools.coalesce(a['ddd'], u''),
186 gmTools.coalesce(a['unit'], u''),
187 gmTools.coalesce(a['administrative_route'], u''),
188 gmTools.coalesce(a['comment'], u''),
189 a['version'],
190 a['lang']
191 ] for a in atcs ]
192 lctrl.set_string_items(items)
193 lctrl.set_data(atcs)
194
195 gmListWidgets.get_choices_from_list (
196 parent = parent,
197 msg = _('\nThe ATC codes as known to GNUmed.\n'),
198 caption = _('Showing ATC codes.'),
199 columns = [ u'ATC', _('Term'), u'DDD', _('Unit'), _(u'Route'), _('Comment'), _('Version'), _('Language') ],
200 single_selection = True,
201 refresh_callback = refresh
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_by_patients:
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_by_patients, _('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):
803 if brand is not None:
804 if brand.is_vaccine:
805 gmGuiHelpers.gm_show_info (
806 aTitle = _('Editing medication'),
807 aMessage = _(
808 'Cannot edit the medication\n'
809 '\n'
810 ' "%s" (%s)\n'
811 '\n'
812 'because it is a vaccine. Please edit it\n'
813 'from the vaccine management section !\n'
814 ) % (brand['brand'], brand['preparation'])
815 )
816 return False
817
818 return edit_branded_drug(parent = parent, branded_drug = brand, single_entry = True)
819
820 def delete(brand):
821 if brand.is_vaccine:
822 gmGuiHelpers.gm_show_info (
823 aTitle = _('Deleting medication'),
824 aMessage = _(
825 'Cannot delete the medication\n'
826 '\n'
827 ' "%s" (%s)\n'
828 '\n'
829 'because it is a vaccine. Please delete it\n'
830 'from the vaccine management section !\n'
831 ) % (brand['brand'], brand['preparation'])
832 )
833 return False
834 gmMedication.delete_branded_drug(brand = brand['pk_brand'])
835 return True
836
837 def new():
838 return edit_branded_drug(parent = parent, branded_drug = None, single_entry = False)
839
840 def refresh(lctrl):
841 drugs = gmMedication.get_branded_drugs()
842 items = [ [
843 u'%s%s' % (
844 d['brand'],
845 gmTools.bool2subst(d['is_fake_brand'], ' (%s)' % _('fake'), u'')
846 ),
847 d['preparation'],
848 gmTools.coalesce(d['atc'], u''),
849 gmTools.coalesce(d['components'], u''),
850 gmTools.coalesce(d['external_code'], u'', u'%%s [%s]' % d['external_code_type']),
851 d['pk_brand']
852 ] for d in drugs ]
853 lctrl.set_string_items(items)
854 lctrl.set_data(drugs)
855
856 msg = _('\nThese are the drug brands known to GNUmed.\n')
857
858 gmListWidgets.get_choices_from_list (
859 parent = parent,
860 msg = msg,
861 caption = _('Showing branded drugs.'),
862 columns = [_('Name'), _('Preparation'), _('ATC'), _('Components'), _('Code'), u'#'],
863 single_selection = True,
864 ignore_OK_button = ignore_OK_button,
865 refresh_callback = refresh,
866 new_callback = new,
867 edit_callback = edit,
868 delete_callback = delete,
869 list_tooltip_callback = get_tooltip,
870 left_extra_button = (_('Import'), _('Import substances and brands from a drug database.'), add_from_db)
871
872
873 )
874
875
877 if branded_drug is not None:
878 if branded_drug.is_in_use_by_patients:
879 gmGuiHelpers.gm_show_info (
880 aTitle = _('Editing drug'),
881 aMessage = _(
882 'Cannot edit the branded drug product\n'
883 '\n'
884 ' "%s" (%s)\n'
885 '\n'
886 'because it is currently taken by patients.\n'
887 ) % (branded_drug['brand'], branded_drug['preparation'])
888 )
889 return False
890
891 ea = cBrandedDrugEAPnl(parent = parent, id = -1)
892 ea.data = branded_drug
893 ea.mode = gmTools.coalesce(branded_drug, 'new', 'edit')
894 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
895 dlg.SetTitle(gmTools.coalesce(branded_drug, _('Adding new drug brand'), _('Editing drug brand')))
896 if dlg.ShowModal() == wx.ID_OK:
897 dlg.Destroy()
898 return True
899 dlg.Destroy()
900 return False
901
902
903 from Gnumed.wxGladeWidgets import wxgBrandedDrugEAPnl
904
905 -class cBrandedDrugEAPnl(wxgBrandedDrugEAPnl.wxgBrandedDrugEAPnl, gmEditArea.cGenericEditAreaMixin):
906
923
924
925
926
927
928
929
930
932
933 if self.data is not None:
934 if self.data.is_in_use_by_patients:
935 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit drug brand. It is in use.'), beep = True)
936 return False
937
938 validity = True
939
940 if self._PRW_brand.GetValue().strip() == u'':
941 validity = False
942 self._PRW_brand.display_as_valid(False)
943 else:
944 self._PRW_brand.display_as_valid(True)
945
946 if self._PRW_preparation.GetValue().strip() == u'':
947 validity = False
948 self._PRW_preparation.display_as_valid(False)
949 else:
950 self._PRW_preparation.display_as_valid(True)
951
952 if validity is True:
953 self._TCTRL_components.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BACKGROUND))
954 if len(self.__component_substances) == 0:
955 wants_empty = gmGuiHelpers.gm_show_question (
956 title = _('Checking brand data'),
957 question = _(
958 'You have not selected any substances\n'
959 'as drug components.\n'
960 '\n'
961 'Without components you will not be able to\n'
962 'use this drug for documenting patient care.\n'
963 '\n'
964 'Are you sure you want to save\n'
965 'it without components ?'
966 )
967 )
968 if not wants_empty:
969 validity = False
970 self.display_ctrl_as_valid(ctrl = self._TCTRL_components, valid = False)
971
972 if validity is False:
973 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save branded drug. Invalid or missing essential input.'))
974
975 return validity
976
978
979 drug = gmMedication.create_branded_drug (
980 brand_name = self._PRW_brand.GetValue().strip(),
981 preparation = gmTools.coalesce (
982 self._PRW_preparation.GetData(),
983 self._PRW_preparation.GetValue()
984 ).strip(),
985 return_existing = True
986 )
987 drug['is_fake_brand'] = self._CHBOX_is_fake.GetValue()
988 drug['atc'] = self._PRW_atc.GetData()
989 code = self._TCTRL_external_code.GetValue().strip()
990 if code != u'':
991 drug['external_code'] = code
992 drug['external_code_type'] = self._PRW_external_code_type.GetData().strip()
993
994 drug.save()
995
996 if len(self.__component_substances) > 0:
997 drug.set_substances_as_components(substances = self.__component_substances)
998
999 self.data = drug
1000
1001 return True
1002
1004 self.data['brand'] = self._PRW_brand.GetValue().strip()
1005 self.data['preparation'] = gmTools.coalesce (
1006 self._PRW_preparation.GetData(),
1007 self._PRW_preparation.GetValue()
1008 ).strip()
1009 self.data['is_fake_brand'] = self._CHBOX_is_fake.GetValue()
1010 self.data['atc'] = self._PRW_atc.GetData()
1011 code = self._TCTRL_external_code.GetValue().strip()
1012 if code != u'':
1013 self.data['external_code'] = code
1014 self.data['external_code_type'] = self._PRW_external_code_type.GetData().strip()
1015 success, data = self.data.save()
1016 if not success:
1017 err, msg = data
1018 _log.error('problem saving')
1019 _log.error('%s', err)
1020 _log.error('%s', msg)
1021 return (success is True)
1022
1024 self._PRW_brand.SetText(u'', None)
1025 self._PRW_preparation.SetText(u'', None)
1026 self._CHBOX_is_fake.SetValue(False)
1027 self._TCTRL_components.SetValue(u'')
1028 self._PRW_atc.SetText(u'', None)
1029 self._TCTRL_external_code.SetValue(u'')
1030 self._PRW_external_code_type.SetText(u'', None)
1031
1032 self._PRW_brand.SetFocus()
1033
1034 self.__component_substances = []
1035
1037 self._refresh_as_new()
1038
1055
1056
1057
1071
1073
1075
1076 query = u"""
1077 SELECT
1078 pk
1079 AS data,
1080 (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', ''))
1081 AS list_label,
1082 (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', ''))
1083 AS field_label
1084 FROM ref.branded_drug
1085 WHERE description %(fragment_condition)s
1086 ORDER BY list_label
1087 LIMIT 50"""
1088
1089 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
1090 mp.setThresholds(2, 3, 4)
1091 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1092 self.SetToolTipString(_(
1093 'The brand name of the drug.\n'
1094 '\n'
1095 'Note: a brand name will need to be linked to\n'
1096 'one or more components before it can be used,\n'
1097 'except in the case of fake (generic) vaccines.'
1098 ))
1099 self.matcher = mp
1100 self.selection_only = False
1101
1102
1103
1104
1106
1108
1109 query = u"""
1110 SELECT DISTINCT ON (sched)
1111 schedule as sched,
1112 schedule
1113 FROM clin.substance_intake
1114 WHERE schedule %(fragment_condition)s
1115 ORDER BY sched
1116 LIMIT 50"""
1117
1118 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
1119 mp.setThresholds(1, 2, 4)
1120 mp.word_separators = '[ \t=+&:@]+'
1121 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1122 self.SetToolTipString(_('The schedule for taking this substance.'))
1123 self.matcher = mp
1124 self.selection_only = False
1125
1127
1128 if intake['is_currently_active']:
1129 intake['discontinued'] = gmDateTime.pydt_now_here()
1130 if intake['discontinue_reason'] is None:
1131 intake['discontinue_reason'] = u'%s %s' % (_('not tolerated:'), _('discontinued due to allergy or intolerance'))
1132 else:
1133 if not intake['discontinue_reason'].startswith(_('not tolerated:')):
1134 intake['discontinue_reason'] = u'%s %s' % (_('not tolerated:'), intake['discontinue_reason'])
1135 if not intake.save():
1136 return False
1137
1138 allg = intake.turn_into_allergy(encounter_id = emr.active_encounter['pk_encounter'])
1139
1140 brand = intake.containing_drug
1141 if brand is not None:
1142 comps = [ c['substance'] for c in brand.components ]
1143 if len(comps) > 1:
1144 gmGuiHelpers.gm_show_info (
1145 aTitle = _(u'Documented an allergy'),
1146 aMessage = _(
1147 u'An allergy was documented against the substance:\n'
1148 u'\n'
1149 u' [%s]\n'
1150 u'\n'
1151 u'This substance was taken with the multi-component brand:\n'
1152 u'\n'
1153 u' [%s (%s)]\n'
1154 u'\n'
1155 u'Note that ALL components of this brand were discontinued.'
1156 ) % (
1157 intake['substance'],
1158 intake['brand'],
1159 u' & '.join(comps)
1160 )
1161 )
1162
1163 if parent is None:
1164 parent = wx.GetApp().GetTopWindow()
1165
1166 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent = parent, id = -1)
1167 dlg.ShowModal()
1168
1169 return True
1170
1171 from Gnumed.wxGladeWidgets import wxgCurrentMedicationEAPnl
1172
1173 -class cSubstanceIntakeEAPnl(wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl, gmEditArea.cGenericEditAreaMixin):
1174
1192
1194
1195 self._PRW_component.add_callback_on_lose_focus(callback = self._on_leave_component)
1196 self._PRW_component.selection_only = True
1197
1198 self._PRW_substance.add_callback_on_lose_focus(callback = self._on_leave_substance)
1199 self._PRW_substance.selection_only = True
1200
1202 emr = gmPerson.gmCurrentPatient().get_emr()
1203
1204 state = emr.allergy_state
1205 if state['last_confirmed'] is None:
1206 confirmed = _('never')
1207 else:
1208 confirmed = state['last_confirmed'].strftime('%Y %B %d').decode(gmI18N.get_encoding())
1209 msg = _(u'%s, last confirmed %s\n') % (state.state_string, confirmed)
1210 msg += gmTools.coalesce(state['comment'], u'', _('Comment (%s): %%s\n') % state['modified_by'])
1211 msg += u'\n'
1212
1213 for allergy in emr.get_allergies():
1214 msg += u'%s (%s, %s): %s\n' % (
1215 allergy['descriptor'],
1216 allergy['l10n_type'],
1217 gmTools.bool2subst(allergy['definite'], _('definite'), _('suspected'), u'?'),
1218 gmTools.coalesce(allergy['reaction'], _('reaction not recorded'))
1219 )
1220
1221 self._LBL_allergies.SetLabel(msg)
1222
1223
1224
1314
1316
1317 emr = gmPerson.gmCurrentPatient().get_emr()
1318 epi = self._PRW_episode.GetData(can_create = True)
1319
1320 if self._PRW_substance.GetData() is None:
1321
1322 intake = emr.add_substance_intake (
1323 pk_component = self._PRW_component.GetData(),
1324 episode = epi
1325 )
1326 else:
1327 intake = emr.add_substance_intake (
1328 pk_substance = self._PRW_substance.GetData(),
1329 episode = epi,
1330 preparation = self._PRW_preparation.GetValue().strip()
1331 )
1332
1333 if intake is None:
1334 gmDispatcher.send('statustext', msg = _('Cannot add duplicate of (maybe inactive) substance intake.'), beep = True)
1335 return False
1336
1337 intake['started'] = self._DP_started.GetData()
1338 intake['discontinued'] = self._DP_discontinued.GetData()
1339 if intake['discontinued'] is None:
1340 intake['discontinue_reason'] = None
1341 else:
1342 intake['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
1343 intake['schedule'] = self._PRW_schedule.GetValue().strip()
1344 intake['aim'] = self._PRW_aim.GetValue().strip()
1345 intake['notes'] = self._PRW_notes.GetValue().strip()
1346 intake['is_long_term'] = self._CHBOX_long_term.IsChecked()
1347 intake['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
1348 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
1349 intake['duration'] = None
1350 else:
1351 intake['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
1352 intake.save()
1353
1354 self.data = intake
1355
1356 return True
1357
1359
1360
1361 self.data['started'] = self._DP_started.GetData()
1362 self.data['discontinued'] = self._DP_discontinued.GetData()
1363 if self.data['discontinued'] is None:
1364 self.data['discontinue_reason'] = None
1365 else:
1366 self.data['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
1367 self.data['schedule'] = self._PRW_schedule.GetValue()
1368 self.data['is_long_term'] = self._CHBOX_long_term.IsChecked()
1369 self.data['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
1370 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
1371 self.data['duration'] = None
1372 else:
1373 self.data['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
1374
1375
1376 self.data['preparation'] = self._PRW_preparation.GetValue()
1377
1378
1379 self.data['aim'] = self._PRW_aim.GetValue()
1380 self.data['notes'] = self._PRW_notes.GetValue()
1381 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True)
1382
1383 self.data.save()
1384
1385 return True
1386
1388 self._PRW_component.SetText(u'', None)
1389 self._TCTRL_brand_ingredients.SetValue(u'')
1390 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1391
1392 self._PRW_substance.SetText(u'', None)
1393 self._PRW_substance.Enable(True)
1394
1395 self._PRW_preparation.SetText(u'', None)
1396 self._PRW_preparation.Enable(True)
1397
1398 self._PRW_schedule.SetText(u'', None)
1399 self._PRW_duration.SetText(u'', None)
1400 self._PRW_aim.SetText(u'', None)
1401 self._PRW_notes.SetText(u'', None)
1402 self._PRW_episode.SetText(u'', None)
1403
1404 self._CHBOX_long_term.SetValue(False)
1405 self._CHBOX_approved.SetValue(True)
1406
1407 self._DP_started.SetData(gmDateTime.pydt_now_here())
1408 self._DP_discontinued.SetData(None)
1409 self._PRW_discontinue_reason.SetValue(u'')
1410
1411 self.__refresh_allergies()
1412
1413 self._PRW_component.SetFocus()
1414
1416
1417 self._TCTRL_brand_ingredients.SetValue(u'')
1418 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1419
1420 if self.data['pk_brand'] is None:
1421 self.__refresh_from_existing_substance()
1422 else:
1423 self.__refresh_from_existing_component()
1424
1425 self._PRW_component.Enable(False)
1426 self._PRW_substance.Enable(False)
1427
1428 if self.data['is_long_term']:
1429 self._CHBOX_long_term.SetValue(True)
1430 self._PRW_duration.Enable(False)
1431 self._PRW_duration.SetText(gmTools.u_infinity, None)
1432 self._BTN_discontinued_as_planned.Enable(False)
1433 else:
1434 self._CHBOX_long_term.SetValue(False)
1435 self._PRW_duration.Enable(True)
1436 self._BTN_discontinued_as_planned.Enable(True)
1437 if self.data['duration'] is None:
1438 self._PRW_duration.SetText(u'', None)
1439 else:
1440 self._PRW_duration.SetText(gmDateTime.format_interval(self.data['duration'], gmDateTime.acc_days), self.data['duration'])
1441 self._PRW_aim.SetText(gmTools.coalesce(self.data['aim'], u''), self.data['aim'])
1442 self._PRW_notes.SetText(gmTools.coalesce(self.data['notes'], u''), self.data['notes'])
1443 self._PRW_episode.SetData(self.data['pk_episode'])
1444 self._PRW_schedule.SetText(gmTools.coalesce(self.data['schedule'], u''), self.data['schedule'])
1445
1446 self._CHBOX_approved.SetValue(self.data['intake_is_approved_of'])
1447
1448 self._DP_started.SetData(self.data['started'])
1449 self._DP_discontinued.SetData(self.data['discontinued'])
1450 self._PRW_discontinue_reason.SetValue(gmTools.coalesce(self.data['discontinue_reason'], u''))
1451 if self.data['discontinued'] is not None:
1452 self._PRW_discontinue_reason.Enable()
1453
1454 self.__refresh_allergies()
1455
1456 self._PRW_schedule.SetFocus()
1457
1459 self._LBL_component.Enable(False)
1460 self._PRW_component.SetText(u'', None)
1461 self._PRW_component.display_as_valid(True)
1462
1463 self._PRW_substance.SetText (
1464 u'%s %s%s' % (self.data['substance'], self.data['amount'], self.data['unit']),
1465 self.data['pk_substance']
1466 )
1467
1468 self._PRW_preparation.SetText(gmTools.coalesce(self.data['preparation'], u''), self.data['preparation'])
1469 self._PRW_preparation.Enable(True)
1470
1472 self._PRW_component.SetText (
1473 u'%s %s%s (%s)' % (self.data['substance'], self.data['amount'], self.data['unit'], self.data['brand']),
1474 self.data['pk_drug_component']
1475 )
1476
1477 brand = gmMedication.cBrandedDrug(aPK_obj = self.data['pk_brand'])
1478 if brand['components'] is not None:
1479 self._TCTRL_brand_ingredients.SetValue(u'; '.join(brand['components']))
1480 tt = u'%s:\n\n- %s' % (
1481 self.data['brand'],
1482 u'\n- '.join(brand['components'])
1483 )
1484 self._TCTRL_brand_ingredients.SetToolTipString(tt)
1485
1486 self._LBL_or.Enable(False)
1487 self._LBL_substance.Enable(False)
1488 self._PRW_substance.SetText(u'', None)
1489 self._PRW_substance.display_as_valid(True)
1490
1491 self._PRW_preparation.SetText(self.data['preparation'], self.data['preparation'])
1492 self._PRW_preparation.Enable(False)
1493
1495 self._refresh_as_new()
1496
1497
1498
1500 if self._PRW_component.GetData() is None:
1501 self._LBL_or.Enable(True)
1502 self._PRW_component.SetText(u'', None)
1503 self._LBL_substance.Enable(True)
1504 self._PRW_substance.Enable(True)
1505 self._LBL_preparation.Enable(True)
1506 self._PRW_preparation.Enable(True)
1507 self._PRW_preparation.SetText(u'', None)
1508 self._TCTRL_brand_ingredients.SetValue(u'')
1509 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1510 else:
1511 self._LBL_or.Enable(False)
1512 self._LBL_substance.Enable(False)
1513 self._PRW_substance.SetText(u'', None)
1514 self._PRW_substance.display_as_valid(True)
1515 self._PRW_substance.Enable(False)
1516 self._LBL_preparation.Enable(False)
1517 self._PRW_preparation.Enable(False)
1518 comp = gmMedication.cDrugComponent(aPK_obj = self._PRW_component.GetData())
1519 self._PRW_preparation.SetText(comp['preparation'], comp['preparation'])
1520 brand = comp.containing_drug
1521 if brand['components'] is not None:
1522 self._TCTRL_brand_ingredients.SetValue(u'; '.join(brand['components']))
1523 tt = u'%s:\n\n- %s' % (
1524 brand['brand'],
1525 u'\n- '.join(brand['components'])
1526 )
1527 self._TCTRL_brand_ingredients.SetToolTipString(tt)
1528
1530 if self._PRW_substance.GetData() is None:
1531 self._LBL_or.Enable(True)
1532 self._LBL_component.Enable(True)
1533 self._PRW_component.Enable(True)
1534 self._PRW_substance.SetText(u'', None)
1535 else:
1536 self._LBL_or.Enable(False)
1537 self._LBL_component.Enable(False)
1538 self._PRW_component.SetText(u'', None)
1539 self._PRW_component.display_as_valid(True)
1540 self._PRW_component.Enable(False)
1541 self._LBL_preparation.Enable(True)
1542 self._PRW_preparation.Enable(True)
1543 self._TCTRL_brand_ingredients.SetValue(u'')
1544 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1545
1547 if self._DP_discontinued.GetData() is None:
1548 self._PRW_discontinue_reason.Enable(False)
1549 else:
1550 self._PRW_discontinue_reason.Enable(True)
1551
1554
1557
1587
1589 if self._CHBOX_long_term.IsChecked() is True:
1590 self._PRW_duration.Enable(False)
1591 self._BTN_discontinued_as_planned.Enable(False)
1592 self._PRW_discontinue_reason.Enable(False)
1593 else:
1594 self._PRW_duration.Enable(True)
1595 self._BTN_discontinued_as_planned.Enable(True)
1596 self._PRW_discontinue_reason.Enable(True)
1597
1598 self.__refresh_allergies()
1599
1609
1611
1612 subst = gmMedication.cSubstanceIntakeEntry(aPK_obj = substance)
1613 msg = _(
1614 '\n'
1615 '[%s]\n'
1616 '\n'
1617 'It may be prudent to edit (before deletion) the details\n'
1618 'of this substance intake entry so as to leave behind\n'
1619 'some indication of why it was deleted.\n'
1620 ) % subst.format()
1621
1622 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
1623 parent,
1624 -1,
1625 caption = _('Deleting medication / substance intake'),
1626 question = msg,
1627 button_defs = [
1628 {'label': _('&Edit'), 'tooltip': _('Allow editing of substance intake entry before deletion.'), 'default': True},
1629 {'label': _('&Delete'), 'tooltip': _('Delete immediately without editing first.')},
1630 {'label': _('&Cancel'), 'tooltip': _('Abort. Do not delete or edit substance intake entry.')}
1631 ]
1632 )
1633
1634 edit_first = dlg.ShowModal()
1635 dlg.Destroy()
1636
1637 if edit_first == wx.ID_CANCEL:
1638 return
1639
1640 if edit_first == wx.ID_YES:
1641 edit_intake_of_substance(parent = parent, substance = subst)
1642 delete_it = gmGuiHelpers.gm_show_question (
1643 aMessage = _('Now delete substance intake entry ?'),
1644 aTitle = _('Deleting medication / substance intake')
1645 )
1646 else:
1647 delete_it = True
1648
1649 if not delete_it:
1650 return
1651
1652 gmMedication.delete_substance_intake(substance = substance)
1653
1668
1669
1670
1671
1699
1701
1702 if parent is None:
1703 parent = wx.GetApp().GetTopWindow()
1704
1705
1706 dbcfg = gmCfg.cCfgSQL()
1707 option = u'form_templates.medication_list'
1708
1709 template = dbcfg.get2 (
1710 option = option,
1711 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1712 bias = 'user'
1713 )
1714
1715 if template is None:
1716 template = configure_medication_list_template(parent = parent)
1717 if template is None:
1718 gmGuiHelpers.gm_show_error (
1719 aMessage = _('There is no medication list template configured.'),
1720 aTitle = _('Printing medication list')
1721 )
1722 return False
1723 else:
1724 try:
1725 name, ver = template.split(u' - ')
1726 except:
1727 _log.exception('problem splitting medication list template name [%s]', template)
1728 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading medication list template.'), beep = True)
1729 return False
1730 template = gmForms.get_form_template(name_long = name, external_version = ver)
1731 if template is None:
1732 gmGuiHelpers.gm_show_error (
1733 aMessage = _('Cannot load medication list template [%s - %s]') % (name, ver),
1734 aTitle = _('Printing medication list')
1735 )
1736 return False
1737
1738
1739 try:
1740 meds_list = template.instantiate()
1741 except KeyError:
1742 _log.exception('cannot instantiate medication list template [%s]', template)
1743 gmGuiHelpers.gm_show_error (
1744 aMessage = _('Invalid medication list template [%s - %s (%s)]') % (name, ver, template['engine']),
1745 aTitle = _('Printing medication list')
1746 )
1747 return False
1748
1749 ph = gmMacro.gmPlaceholderHandler()
1750
1751 meds_list.substitute_placeholders(data_source = ph)
1752 pdf_name = meds_list.generate_output()
1753 if pdf_name is None:
1754 gmGuiHelpers.gm_show_error (
1755 aMessage = _('Error generating the medication list.'),
1756 aTitle = _('Printing medication list')
1757 )
1758 return False
1759
1760
1761 printed = gmPrinting.print_files(filenames = [pdf_name], jobtype = 'medication_list')
1762 if not printed:
1763 gmGuiHelpers.gm_show_error (
1764 aMessage = _('Error printing the medication list.'),
1765 aTitle = _('Printing medication list')
1766 )
1767 return False
1768
1769 pat = gmPerson.gmCurrentPatient()
1770 emr = pat.get_emr()
1771 epi = emr.add_episode(episode_name = 'administration', is_open = False)
1772 emr.add_clin_narrative (
1773 soap_cat = None,
1774 note = _('medication list printed from template [%s - %s]') % (template['name_long'], template['external_version']),
1775 episode = epi
1776 )
1777
1778 return True
1779
1781
1782 if len(prescribed_drugs) == 0:
1783 return
1784
1785 curr_brands = [ i['pk_brand'] for i in emr.get_current_substance_intake() if i['pk_brand'] is not None ]
1786 new_drugs = []
1787 for drug in prescribed_drugs:
1788 if drug['pk_brand'] not in curr_brands:
1789 new_drugs.append(drug)
1790
1791 if len(new_drugs) == 0:
1792 return
1793
1794 if parent is None:
1795 parent = wx.GetApp().GetTopWindow()
1796
1797 dlg = gmListWidgets.cItemPickerDlg (
1798 parent,
1799 -1,
1800 msg = _(
1801 'These brands have been prescribed but are not listed\n'
1802 'in the current medication list of this patient.\n'
1803 '\n'
1804 'Please select those you want added to the medication list.'
1805 )
1806 )
1807 dlg.set_columns (
1808 columns = [_('Newly prescribed drugs')],
1809 columns_right = [_('Add to medication list')]
1810 )
1811 choices = [ (u'%s %s (%s)' % (d['brand'], d['preparation'], u'; '.join(d['components']))) for d in new_drugs ]
1812 dlg.set_choices (
1813 choices = choices,
1814 data = new_drugs
1815 )
1816 dlg.ShowModal()
1817 drugs2add = dlg.get_picks()
1818 dlg.Destroy()
1819
1820 if drugs2add is None:
1821 return
1822
1823 if len(drugs2add) == 0:
1824 return
1825
1826 for drug in drugs2add:
1827
1828 intake = emr.add_substance_intake (
1829 pk_component = drug['pk_components'][0],
1830 episode = emr.add_episode(episode_name = gmMedication.DEFAULT_MEDICATION_HISTORY_EPISODE)['pk_episode'],
1831 )
1832 if intake is None:
1833 continue
1834 intake['intake_is_approved_of'] = True
1835 intake.save()
1836
1837 return
1838
1840 """A grid class for displaying current substance intake.
1841
1842 - does NOT listen to the currently active patient
1843 - thereby it can display any patient at any time
1844 """
1846
1847 wx.grid.Grid.__init__(self, *args, **kwargs)
1848
1849 self.__patient = None
1850 self.__row_data = {}
1851 self.__prev_row = None
1852 self.__prev_tooltip_row = None
1853 self.__prev_cell_0 = None
1854 self.__grouping_mode = u'issue'
1855 self.__filter_show_unapproved = True
1856 self.__filter_show_inactive = True
1857
1858 self.__grouping2col_labels = {
1859 u'issue': [
1860 _('Health issue'),
1861 _('Substance'),
1862 _('Strength'),
1863 _('Schedule'),
1864 _('Started'),
1865 _('Duration / Until'),
1866 _('Brand'),
1867 _('Advice')
1868 ],
1869 u'brand': [
1870 _('Brand'),
1871 _('Schedule'),
1872 _('Substance'),
1873 _('Strength'),
1874 _('Started'),
1875 _('Duration / Until'),
1876 _('Health issue'),
1877 _('Advice')
1878 ],
1879 u'episode': [
1880 _('Episode'),
1881 _('Substance'),
1882 _('Strength'),
1883 _('Schedule'),
1884 _('Started'),
1885 _('Duration / Until'),
1886 _('Brand'),
1887 _('Advice')
1888 ]
1889 }
1890
1891 self.__grouping2order_by_clauses = {
1892 u'issue': u'pk_health_issue nulls first, substance, started',
1893 u'episode': u'pk_health_issue nulls first, episode, substance, started',
1894 u'brand': u'brand nulls last, substance, started'
1895 }
1896
1897 self.__init_ui()
1898 self.__register_events()
1899
1900
1901
1903
1904 sel_block_top_left = self.GetSelectionBlockTopLeft()
1905 sel_block_bottom_right = self.GetSelectionBlockBottomRight()
1906 sel_cols = self.GetSelectedCols()
1907 sel_rows = self.GetSelectedRows()
1908
1909 selected_cells = []
1910
1911
1912 selected_cells += self.GetSelectedCells()
1913
1914
1915 selected_cells += list (
1916 (row, col)
1917 for row in sel_rows
1918 for col in xrange(self.GetNumberCols())
1919 )
1920
1921
1922 selected_cells += list (
1923 (row, col)
1924 for row in xrange(self.GetNumberRows())
1925 for col in sel_cols
1926 )
1927
1928
1929 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()):
1930 selected_cells += [
1931 (row, col)
1932 for row in xrange(top_left[0], bottom_right[0] + 1)
1933 for col in xrange(top_left[1], bottom_right[1] + 1)
1934 ]
1935
1936 return set(selected_cells)
1937
1939 rows = {}
1940
1941 for row, col in self.get_selected_cells():
1942 rows[row] = True
1943
1944 return rows.keys()
1945
1948
1950
1951 self.empty_grid()
1952
1953 if self.__patient is None:
1954 return
1955
1956 emr = self.__patient.get_emr()
1957 meds = emr.get_current_substance_intake (
1958 order_by = self.__grouping2order_by_clauses[self.__grouping_mode],
1959 include_unapproved = self.__filter_show_unapproved,
1960 include_inactive = self.__filter_show_inactive
1961 )
1962 if not meds:
1963 return
1964
1965 self.BeginBatch()
1966
1967
1968 labels = self.__grouping2col_labels[self.__grouping_mode]
1969 if self.__filter_show_unapproved:
1970 self.AppendCols(numCols = len(labels) + 1)
1971 else:
1972 self.AppendCols(numCols = len(labels))
1973 for col_idx in range(len(labels)):
1974 self.SetColLabelValue(col_idx, labels[col_idx])
1975 if self.__filter_show_unapproved:
1976 self.SetColLabelValue(len(labels), u'OK?')
1977 self.SetColSize(len(labels), 40)
1978
1979 self.AppendRows(numRows = len(meds))
1980
1981
1982 for row_idx in range(len(meds)):
1983 med = meds[row_idx]
1984 self.__row_data[row_idx] = med
1985
1986 if med['is_currently_active'] is True:
1987 atcs = []
1988 if med['atc_substance'] is not None:
1989 atcs.append(med['atc_substance'])
1990
1991
1992
1993 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (med['substance'],))
1994 if allg not in [None, False]:
1995 attr = self.GetOrCreateCellAttr(row_idx, 0)
1996 if allg['type'] == u'allergy':
1997 attr.SetTextColour('red')
1998 else:
1999 attr.SetTextColour('yellow')
2000 self.SetRowAttr(row_idx, attr)
2001 else:
2002 attr = self.GetOrCreateCellAttr(row_idx, 0)
2003 attr.SetTextColour('grey')
2004 self.SetRowAttr(row_idx, attr)
2005
2006 if self.__grouping_mode == u'episode':
2007 if med['pk_episode'] is None:
2008 self.__prev_cell_0 = None
2009 epi = gmTools.u_diameter
2010 else:
2011 if self.__prev_cell_0 == med['episode']:
2012 epi = u''
2013 else:
2014 self.__prev_cell_0 = med['episode']
2015 epi = gmTools.coalesce(med['episode'], u'')
2016 self.SetCellValue(row_idx, 0, gmTools.wrap(text = epi, width = 40))
2017
2018 self.SetCellValue(row_idx, 1, med['substance'])
2019 self.SetCellValue(row_idx, 2, u'%s%s' % (med['amount'], med['unit']))
2020 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], u''))
2021 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
2022
2023 if med['is_long_term']:
2024 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
2025 else:
2026 if med['discontinued'] is None:
2027 if med['duration'] is None:
2028 self.SetCellValue(row_idx, 5, u'')
2029 else:
2030 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
2031 else:
2032 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d'))
2033
2034 if med['pk_brand'] is None:
2035 brand = u''
2036 else:
2037 if med['fake_brand']:
2038 brand = gmTools.coalesce(med['brand'], u'', _('%s (fake)'))
2039 else:
2040 brand = gmTools.coalesce(med['brand'], u'')
2041 self.SetCellValue(row_idx, 6, gmTools.wrap(text = brand, width = 35))
2042
2043 elif self.__grouping_mode == u'issue':
2044 if med['pk_health_issue'] is None:
2045 self.__prev_cell_0 = None
2046 issue = u'%s%s' % (
2047 gmTools.u_diameter,
2048 gmTools.coalesce(med['episode'], u'', u' (%s)')
2049 )
2050 else:
2051 if self.__prev_cell_0 == med['health_issue']:
2052 issue = u''
2053 else:
2054 self.__prev_cell_0 = med['health_issue']
2055 issue = med['health_issue']
2056 self.SetCellValue(row_idx, 0, gmTools.wrap(text = issue, width = 40))
2057
2058 self.SetCellValue(row_idx, 1, med['substance'])
2059 self.SetCellValue(row_idx, 2, u'%s%s' % (med['amount'], med['unit']))
2060 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], u''))
2061 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
2062
2063 if med['is_long_term']:
2064 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
2065 else:
2066 if med['discontinued'] is None:
2067 if med['duration'] is None:
2068 self.SetCellValue(row_idx, 5, u'')
2069 else:
2070 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
2071 else:
2072 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d'))
2073
2074 if med['pk_brand'] is None:
2075 brand = u''
2076 else:
2077 if med['fake_brand']:
2078 brand = gmTools.coalesce(med['brand'], u'', _('%s (fake)'))
2079 else:
2080 brand = gmTools.coalesce(med['brand'], u'')
2081 self.SetCellValue(row_idx, 6, gmTools.wrap(text = brand, width = 35))
2082
2083 elif self.__grouping_mode == u'brand':
2084
2085 if med['pk_brand'] is None:
2086 self.__prev_cell_0 = None
2087 brand = gmTools.u_diameter
2088 else:
2089 if self.__prev_cell_0 == med['brand']:
2090 brand = u''
2091 else:
2092 self.__prev_cell_0 = med['brand']
2093 if med['fake_brand']:
2094 brand = gmTools.coalesce(med['brand'], u'', _('%s (fake)'))
2095 else:
2096 brand = gmTools.coalesce(med['brand'], u'')
2097 self.SetCellValue(row_idx, 0, gmTools.wrap(text = brand, width = 35))
2098
2099 self.SetCellValue(row_idx, 1, gmTools.coalesce(med['schedule'], u''))
2100 self.SetCellValue(row_idx, 2, med['substance'])
2101 self.SetCellValue(row_idx, 3, u'%s%s' % (med['amount'], med['unit']))
2102 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
2103
2104 if med['is_long_term']:
2105 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
2106 else:
2107 if med['discontinued'] is None:
2108 if med['duration'] is None:
2109 self.SetCellValue(row_idx, 5, u'')
2110 else:
2111 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
2112 else:
2113 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d'))
2114
2115 if med['pk_health_issue'] is None:
2116 issue = u'%s%s' % (
2117 gmTools.u_diameter,
2118 gmTools.coalesce(med['episode'], u'', u' (%s)')
2119 )
2120 else:
2121 issue = gmTools.coalesce(med['health_issue'], u'')
2122 self.SetCellValue(row_idx, 6, gmTools.wrap(text = issue, width = 40))
2123
2124 else:
2125 raise ValueError('unknown grouping mode [%s]' % self.__grouping_mode)
2126
2127 if med['notes'] is not None:
2128 self.SetCellValue(row_idx, 7, gmTools.wrap(text = med['notes'], width = 50))
2129
2130 if self.__filter_show_unapproved:
2131 self.SetCellValue (
2132 row_idx,
2133 len(labels),
2134 gmTools.bool2subst(med['intake_is_approved_of'], gmTools.u_checkmark_thin, u'', u'?')
2135 )
2136
2137
2138
2139 self.AutoSize()
2140 self.EndBatch()
2141
2143 self.BeginBatch()
2144 self.ClearGrid()
2145
2146
2147 if self.GetNumberRows() > 0:
2148 self.DeleteRows(pos = 0, numRows = self.GetNumberRows())
2149 if self.GetNumberCols() > 0:
2150 self.DeleteCols(pos = 0, numCols = self.GetNumberCols())
2151 self.EndBatch()
2152 self.__row_data = {}
2153 self.__prev_cell_0 = None
2154
2156
2157 if len(self.__row_data) == 0:
2158 return
2159
2160 sel_rows = self.get_selected_rows()
2161 if len(sel_rows) != 1:
2162 return
2163
2164 drug_db = get_drug_database()
2165 if drug_db is None:
2166 return
2167
2168 intake = self.get_selected_data()[0]
2169 if intake['brand'] is None:
2170 drug_db.show_info_on_substance(substance_intake = intake)
2171 else:
2172 drug_db.show_info_on_drug(substance_intake = intake)
2173
2181
2193
2205
2219
2222
2236
2250
2266
2270
2375
2376
2377
2379 self.CreateGrid(0, 1)
2380 self.EnableEditing(0)
2381 self.EnableDragGridSize(1)
2382 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows)
2383
2384 self.SetColLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTER)
2385
2386 self.SetRowLabelSize(0)
2387 self.SetRowLabelAlignment(horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
2388
2389
2390
2392 return self.__patient
2393
2397
2398 patient = property(_get_patient, _set_patient)
2399
2401 return self.__grouping_mode
2402
2406
2407 grouping_mode = property(_get_grouping_mode, _set_grouping_mode)
2408
2410 return self.__filter_show_unapproved
2411
2415
2416 filter_show_unapproved = property(_get_filter_show_unapproved, _set_filter_show_unapproved)
2417
2419 return self.__filter_show_inactive
2420
2424
2425 filter_show_inactive = property(_get_filter_show_inactive, _set_filter_show_inactive)
2426
2427
2428
2430
2431 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells)
2432
2433
2434
2435
2436 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
2437
2439 """Calculate where the mouse is and set the tooltip dynamically."""
2440
2441
2442
2443 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457 row, col = self.XYToCell(x, y)
2458
2459 if row == self.__prev_tooltip_row:
2460 return
2461
2462 self.__prev_tooltip_row = row
2463
2464 try:
2465 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row))
2466 except KeyError:
2467 pass
2468
2473
2474 from Gnumed.wxGladeWidgets import wxgCurrentSubstancesPnl
2475
2476 -class cCurrentSubstancesPnl(wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl, gmRegetMixin.cRegetOnPaintMixin):
2477
2478 """Panel holding a grid with current substances. Used as notebook page."""
2479
2486
2487
2488
2497
2498
2499
2501 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
2502 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._schedule_data_reget)
2503 gmDispatcher.connect(signal = u'substance_intake_mod_db', receiver = self._schedule_data_reget)
2504
2505
2506
2508 wx.CallAfter(self.__on_pre_patient_selection)
2509
2511 self._grid_substances.patient = None
2512
2515
2518
2521
2524
2527
2530
2533
2536
2539
2542
2545
2548
2551
2554
2557
2558
2559
2560 if __name__ == '__main__':
2561
2562 if len(sys.argv) < 2:
2563 sys.exit()
2564
2565 if sys.argv[1] != 'test':
2566 sys.exit()
2567
2568 from Gnumed.pycommon import gmI18N
2569
2570 gmI18N.activate_locale()
2571 gmI18N.install_domain(domain = 'gnumed')
2572
2573
2574 app = wx.PyWidgetTester(size = (600, 600))
2575
2576 app.SetWidget(cSubstancePhraseWheel, -1)
2577 app.MainLoop()
2578
2579
2580