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
1067
1069
1071
1072 query = u"""
1073 SELECT
1074 pk
1075 AS data,
1076 (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', ''))
1077 AS list_label,
1078 (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', ''))
1079 AS field_label
1080 FROM ref.branded_drug
1081 WHERE description %(fragment_condition)s
1082 ORDER BY list_label
1083 LIMIT 50"""
1084
1085 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
1086 mp.setThresholds(2, 3, 4)
1087 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1088 self.SetToolTipString(_(
1089 'The brand name of the drug.\n'
1090 '\n'
1091 'Note: a brand name will need to be linked to\n'
1092 'one or more components before it can be used,\n'
1093 'except in the case of fake (generic) vaccines.'
1094 ))
1095 self.matcher = mp
1096 self.selection_only = False
1097
1098
1099
1100
1102
1104
1105 query = u"""
1106 SELECT DISTINCT ON (sched)
1107 schedule as sched,
1108 schedule
1109 FROM clin.substance_intake
1110 WHERE schedule %(fragment_condition)s
1111 ORDER BY sched
1112 LIMIT 50"""
1113
1114 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
1115 mp.setThresholds(1, 2, 4)
1116 mp.word_separators = '[ \t=+&:@]+'
1117 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1118 self.SetToolTipString(_('The schedule for taking this substance.'))
1119 self.matcher = mp
1120 self.selection_only = False
1121
1123
1124 if intake['is_currently_active']:
1125 intake['discontinued'] = gmDateTime.pydt_now_here()
1126 if intake['discontinue_reason'] is None:
1127 intake['discontinue_reason'] = u'%s %s' % (_('not tolerated:'), _('discontinued due to allergy or intolerance'))
1128 else:
1129 if not intake['discontinue_reason'].startswith(_('not tolerated:')):
1130 intake['discontinue_reason'] = u'%s %s' % (_('not tolerated:'), intake['discontinue_reason'])
1131 if not intake.save():
1132 return False
1133
1134 allg = intake.turn_into_allergy(encounter_id = emr.active_encounter['pk_encounter'])
1135
1136 brand = intake.containing_drug
1137 if brand is not None:
1138 comps = [ c['substance'] for c in brand.components ]
1139 if len(comps) > 1:
1140 gmGuiHelpers.gm_show_info (
1141 aTitle = _(u'Documented an allergy'),
1142 aMessage = _(
1143 u'An allergy was documented against the substance:\n'
1144 u'\n'
1145 u' [%s]\n'
1146 u'\n'
1147 u'This substance was taken with the multi-component brand:\n'
1148 u'\n'
1149 u' [%s (%s)]\n'
1150 u'\n'
1151 u'Note that ALL components of this brand were discontinued.'
1152 ) % (
1153 intake['substance'],
1154 intake['brand'],
1155 u' & '.join(comps)
1156 )
1157 )
1158
1159 if parent is None:
1160 parent = wx.GetApp().GetTopWindow()
1161
1162 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent = parent, id = -1)
1163 dlg.ShowModal()
1164
1165 return True
1166
1167 from Gnumed.wxGladeWidgets import wxgCurrentMedicationEAPnl
1168
1169 -class cSubstanceIntakeEAPnl(wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl, gmEditArea.cGenericEditAreaMixin):
1170
1188
1190
1191 self._PRW_component.add_callback_on_lose_focus(callback = self._on_leave_component)
1192 self._PRW_component.selection_only = True
1193
1194 self._PRW_substance.add_callback_on_lose_focus(callback = self._on_leave_substance)
1195 self._PRW_substance.selection_only = True
1196
1198 emr = gmPerson.gmCurrentPatient().get_emr()
1199
1200 state = emr.allergy_state
1201 if state['last_confirmed'] is None:
1202 confirmed = _('never')
1203 else:
1204 confirmed = state['last_confirmed'].strftime('%Y %B %d').decode(gmI18N.get_encoding())
1205 msg = _(u'%s, last confirmed %s\n') % (state.state_string, confirmed)
1206 msg += gmTools.coalesce(state['comment'], u'', _('Comment (%s): %%s\n') % state['modified_by'])
1207 msg += u'\n'
1208
1209 for allergy in emr.get_allergies():
1210 msg += u'%s (%s, %s): %s\n' % (
1211 allergy['descriptor'],
1212 allergy['l10n_type'],
1213 gmTools.bool2subst(allergy['definite'], _('definite'), _('suspected'), u'?'),
1214 gmTools.coalesce(allergy['reaction'], _('reaction not recorded'))
1215 )
1216
1217 self._LBL_allergies.SetLabel(msg)
1218
1219
1220
1310
1312
1313 emr = gmPerson.gmCurrentPatient().get_emr()
1314 epi = self._PRW_episode.GetData(can_create = True)
1315
1316 if self._PRW_substance.GetData() is None:
1317
1318 intake = emr.add_substance_intake (
1319 pk_component = self._PRW_component.GetData(),
1320 episode = epi
1321 )
1322 else:
1323 intake = emr.add_substance_intake (
1324 pk_substance = self._PRW_substance.GetData(),
1325 episode = epi,
1326 preparation = self._PRW_preparation.GetValue().strip()
1327 )
1328
1329 if intake is None:
1330 gmDispatcher.send('statustext', msg = _('Cannot add duplicate of (maybe inactive) substance intake.'), beep = True)
1331 return False
1332
1333 intake['started'] = self._DP_started.GetData()
1334 intake['discontinued'] = self._DP_discontinued.GetData()
1335 if intake['discontinued'] is None:
1336 intake['discontinue_reason'] = None
1337 else:
1338 intake['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
1339 intake['schedule'] = self._PRW_schedule.GetValue().strip()
1340 intake['aim'] = self._PRW_aim.GetValue().strip()
1341 intake['notes'] = self._PRW_notes.GetValue().strip()
1342 intake['is_long_term'] = self._CHBOX_long_term.IsChecked()
1343 intake['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
1344 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
1345 intake['duration'] = None
1346 else:
1347 intake['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
1348 intake.save()
1349
1350 self.data = intake
1351
1352 return True
1353
1355
1356
1357 self.data['started'] = self._DP_started.GetData()
1358 self.data['discontinued'] = self._DP_discontinued.GetData()
1359 if self.data['discontinued'] is None:
1360 self.data['discontinue_reason'] = None
1361 else:
1362 self.data['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
1363 self.data['schedule'] = self._PRW_schedule.GetValue()
1364 self.data['is_long_term'] = self._CHBOX_long_term.IsChecked()
1365 self.data['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
1366 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
1367 self.data['duration'] = None
1368 else:
1369 self.data['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
1370
1371
1372 self.data['preparation'] = self._PRW_preparation.GetValue()
1373
1374
1375 self.data['aim'] = self._PRW_aim.GetValue()
1376 self.data['notes'] = self._PRW_notes.GetValue()
1377 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True)
1378
1379 self.data.save()
1380
1381 return True
1382
1384 self._PRW_component.SetText(u'', None)
1385 self._TCTRL_brand_ingredients.SetValue(u'')
1386 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1387
1388 self._PRW_substance.SetText(u'', None)
1389 self._PRW_substance.Enable(True)
1390
1391 self._PRW_preparation.SetText(u'', None)
1392 self._PRW_preparation.Enable(True)
1393
1394 self._PRW_schedule.SetText(u'', None)
1395 self._PRW_duration.SetText(u'', None)
1396 self._PRW_aim.SetText(u'', None)
1397 self._PRW_notes.SetText(u'', None)
1398 self._PRW_episode.SetText(u'', None)
1399
1400 self._CHBOX_long_term.SetValue(False)
1401 self._CHBOX_approved.SetValue(True)
1402
1403 self._DP_started.SetData(gmDateTime.pydt_now_here())
1404 self._DP_discontinued.SetData(None)
1405 self._PRW_discontinue_reason.SetValue(u'')
1406
1407 self.__refresh_allergies()
1408
1409 self._PRW_component.SetFocus()
1410
1412
1413 self._TCTRL_brand_ingredients.SetValue(u'')
1414 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1415
1416 if self.data['pk_brand'] is None:
1417 self.__refresh_from_existing_substance()
1418 else:
1419 self.__refresh_from_existing_component()
1420
1421 self._PRW_component.Enable(False)
1422 self._PRW_substance.Enable(False)
1423
1424 if self.data['is_long_term']:
1425 self._CHBOX_long_term.SetValue(True)
1426 self._PRW_duration.Enable(False)
1427 self._PRW_duration.SetText(gmTools.u_infinity, None)
1428 self._BTN_discontinued_as_planned.Enable(False)
1429 else:
1430 self._CHBOX_long_term.SetValue(False)
1431 self._PRW_duration.Enable(True)
1432 self._BTN_discontinued_as_planned.Enable(True)
1433 if self.data['duration'] is None:
1434 self._PRW_duration.SetText(u'', None)
1435 else:
1436 self._PRW_duration.SetText(gmDateTime.format_interval(self.data['duration'], gmDateTime.acc_days), self.data['duration'])
1437 self._PRW_aim.SetText(gmTools.coalesce(self.data['aim'], u''), self.data['aim'])
1438 self._PRW_notes.SetText(gmTools.coalesce(self.data['notes'], u''), self.data['notes'])
1439 self._PRW_episode.SetData(self.data['pk_episode'])
1440 self._PRW_schedule.SetText(gmTools.coalesce(self.data['schedule'], u''), self.data['schedule'])
1441
1442 self._CHBOX_approved.SetValue(self.data['intake_is_approved_of'])
1443
1444 self._DP_started.SetData(self.data['started'])
1445 self._DP_discontinued.SetData(self.data['discontinued'])
1446 self._PRW_discontinue_reason.SetValue(gmTools.coalesce(self.data['discontinue_reason'], u''))
1447 if self.data['discontinued'] is not None:
1448 self._PRW_discontinue_reason.Enable()
1449
1450 self.__refresh_allergies()
1451
1452 self._PRW_schedule.SetFocus()
1453
1455 self._LBL_component.Enable(False)
1456 self._PRW_component.SetText(u'', None)
1457 self._PRW_component.display_as_valid(True)
1458
1459 self._PRW_substance.SetText (
1460 u'%s %s%s' % (self.data['substance'], self.data['amount'], self.data['unit']),
1461 self.data['pk_substance']
1462 )
1463
1464 self._PRW_preparation.SetText(gmTools.coalesce(self.data['preparation'], u''), self.data['preparation'])
1465 self._PRW_preparation.Enable(True)
1466
1468 self._PRW_component.SetText (
1469 u'%s %s%s (%s)' % (self.data['substance'], self.data['amount'], self.data['unit'], self.data['brand']),
1470 self.data['pk_drug_component']
1471 )
1472
1473 brand = gmMedication.cBrandedDrug(aPK_obj = self.data['pk_brand'])
1474 if brand['components'] is not None:
1475 self._TCTRL_brand_ingredients.SetValue(u'; '.join(brand['components']))
1476 tt = u'%s:\n\n- %s' % (
1477 self.data['brand'],
1478 u'\n- '.join(brand['components'])
1479 )
1480 self._TCTRL_brand_ingredients.SetToolTipString(tt)
1481
1482 self._LBL_or.Enable(False)
1483 self._LBL_substance.Enable(False)
1484 self._PRW_substance.SetText(u'', None)
1485 self._PRW_substance.display_as_valid(True)
1486
1487 self._PRW_preparation.SetText(self.data['preparation'], self.data['preparation'])
1488 self._PRW_preparation.Enable(False)
1489
1491 self._refresh_as_new()
1492
1493
1494
1496 if self._PRW_component.GetData() is None:
1497 self._LBL_or.Enable(True)
1498 self._PRW_component.SetText(u'', None)
1499 self._LBL_substance.Enable(True)
1500 self._PRW_substance.Enable(True)
1501 self._LBL_preparation.Enable(True)
1502 self._PRW_preparation.Enable(True)
1503 self._PRW_preparation.SetText(u'', None)
1504 self._TCTRL_brand_ingredients.SetValue(u'')
1505 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1506 else:
1507 self._LBL_or.Enable(False)
1508 self._LBL_substance.Enable(False)
1509 self._PRW_substance.SetText(u'', None)
1510 self._PRW_substance.display_as_valid(True)
1511 self._PRW_substance.Enable(False)
1512 self._LBL_preparation.Enable(False)
1513 self._PRW_preparation.Enable(False)
1514 comp = gmMedication.cDrugComponent(aPK_obj = self._PRW_component.GetData())
1515 self._PRW_preparation.SetText(comp['preparation'], comp['preparation'])
1516 brand = comp.containing_drug
1517 if brand['components'] is not None:
1518 self._TCTRL_brand_ingredients.SetValue(u'; '.join(brand['components']))
1519 tt = u'%s:\n\n- %s' % (
1520 brand['brand'],
1521 u'\n- '.join(brand['components'])
1522 )
1523 self._TCTRL_brand_ingredients.SetToolTipString(tt)
1524
1526 if self._PRW_substance.GetData() is None:
1527 self._LBL_or.Enable(True)
1528 self._LBL_component.Enable(True)
1529 self._PRW_component.Enable(True)
1530 self._PRW_substance.SetText(u'', None)
1531 else:
1532 self._LBL_or.Enable(False)
1533 self._LBL_component.Enable(False)
1534 self._PRW_component.SetText(u'', None)
1535 self._PRW_component.display_as_valid(True)
1536 self._PRW_component.Enable(False)
1537 self._LBL_preparation.Enable(True)
1538 self._PRW_preparation.Enable(True)
1539 self._TCTRL_brand_ingredients.SetValue(u'')
1540 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1541
1543 if self._DP_discontinued.GetData() is None:
1544 self._PRW_discontinue_reason.Enable(False)
1545 else:
1546 self._PRW_discontinue_reason.Enable(True)
1547
1550
1553
1583
1585 if self._CHBOX_long_term.IsChecked() is True:
1586 self._PRW_duration.Enable(False)
1587 self._BTN_discontinued_as_planned.Enable(False)
1588 self._PRW_discontinue_reason.Enable(False)
1589 else:
1590 self._PRW_duration.Enable(True)
1591 self._BTN_discontinued_as_planned.Enable(True)
1592 self._PRW_discontinue_reason.Enable(True)
1593
1594 self.__refresh_allergies()
1595
1605
1607
1608 subst = gmMedication.cSubstanceIntakeEntry(aPK_obj = substance)
1609 msg = _(
1610 '\n'
1611 '[%s]\n'
1612 '\n'
1613 'It may be prudent to edit (before deletion) the details\n'
1614 'of this substance intake entry so as to leave behind\n'
1615 'some indication of why it was deleted.\n'
1616 ) % subst.format()
1617
1618 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
1619 parent,
1620 -1,
1621 caption = _('Deleting medication / substance intake'),
1622 question = msg,
1623 button_defs = [
1624 {'label': _('&Edit'), 'tooltip': _('Allow editing of substance intake entry before deletion.'), 'default': True},
1625 {'label': _('&Delete'), 'tooltip': _('Delete immediately without editing first.')},
1626 {'label': _('&Cancel'), 'tooltip': _('Abort. Do not delete or edit substance intake entry.')}
1627 ]
1628 )
1629
1630 edit_first = dlg.ShowModal()
1631 dlg.Destroy()
1632
1633 if edit_first == wx.ID_CANCEL:
1634 return
1635
1636 if edit_first == wx.ID_YES:
1637 edit_intake_of_substance(parent = parent, substance = subst)
1638 delete_it = gmGuiHelpers.gm_show_question (
1639 aMessage = _('Now delete substance intake entry ?'),
1640 aTitle = _('Deleting medication / substance intake')
1641 )
1642 else:
1643 delete_it = True
1644
1645 if not delete_it:
1646 return
1647
1648 gmMedication.delete_substance_intake(substance = substance)
1649
1664
1665
1666
1667
1695
1697
1698 if parent is None:
1699 parent = wx.GetApp().GetTopWindow()
1700
1701
1702 dbcfg = gmCfg.cCfgSQL()
1703 option = u'form_templates.medication_list'
1704
1705 template = dbcfg.get2 (
1706 option = option,
1707 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1708 bias = 'user'
1709 )
1710
1711 if template is None:
1712 template = configure_medication_list_template(parent = parent)
1713 if template is None:
1714 gmGuiHelpers.gm_show_error (
1715 aMessage = _('There is no medication list template configured.'),
1716 aTitle = _('Printing medication list')
1717 )
1718 return False
1719 else:
1720 try:
1721 name, ver = template.split(u' - ')
1722 except:
1723 _log.exception('problem splitting medication list template name [%s]', template)
1724 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading medication list template.'), beep = True)
1725 return False
1726 template = gmForms.get_form_template(name_long = name, external_version = ver)
1727 if template is None:
1728 gmGuiHelpers.gm_show_error (
1729 aMessage = _('Cannot load medication list template [%s - %s]') % (name, ver),
1730 aTitle = _('Printing medication list')
1731 )
1732 return False
1733
1734
1735 try:
1736 meds_list = template.instantiate()
1737 except KeyError:
1738 _log.exception('cannot instantiate medication list template [%s]', template)
1739 gmGuiHelpers.gm_show_error (
1740 aMessage = _('Invalid medication list template [%s - %s (%s)]') % (name, ver, template['engine']),
1741 aTitle = _('Printing medication list')
1742 )
1743 return False
1744
1745 ph = gmMacro.gmPlaceholderHandler()
1746
1747 meds_list.substitute_placeholders(data_source = ph)
1748 pdf_name = meds_list.generate_output()
1749 if pdf_name is None:
1750 gmGuiHelpers.gm_show_error (
1751 aMessage = _('Error generating the medication list.'),
1752 aTitle = _('Printing medication list')
1753 )
1754 return False
1755
1756
1757 printed = gmPrinting.print_files(filenames = [pdf_name], jobtype = 'medication_list')
1758 if not printed:
1759 gmGuiHelpers.gm_show_error (
1760 aMessage = _('Error printing the medication list.'),
1761 aTitle = _('Printing medication list')
1762 )
1763 return False
1764
1765 pat = gmPerson.gmCurrentPatient()
1766 emr = pat.get_emr()
1767 epi = emr.add_episode(episode_name = 'administration', is_open = False)
1768 emr.add_clin_narrative (
1769 soap_cat = None,
1770 note = _('medication list printed from template [%s - %s]') % (template['name_long'], template['external_version']),
1771 episode = epi
1772 )
1773
1774 return True
1775
1777
1778 if len(prescribed_drugs) == 0:
1779 return
1780
1781 curr_brands = [ i['pk_brand'] for i in emr.get_current_substance_intake() if i['pk_brand'] is not None ]
1782 new_drugs = []
1783 for drug in prescribed_drugs:
1784 if drug['pk_brand'] not in curr_brands:
1785 new_drugs.append(drug)
1786
1787 if len(new_drugs) == 0:
1788 return
1789
1790 if parent is None:
1791 parent = wx.GetApp().GetTopWindow()
1792
1793 dlg = gmListWidgets.cItemPickerDlg (
1794 parent,
1795 -1,
1796 msg = _(
1797 'These brands have been prescribed but are not listed\n'
1798 'in the current medication list of this patient.\n'
1799 '\n'
1800 'Please select those you want added to the medication list.'
1801 )
1802 )
1803 dlg.set_columns (
1804 columns = [_('Newly prescribed drugs')],
1805 columns_right = [_('Add to medication list')]
1806 )
1807 choices = [ (u'%s %s (%s)' % (d['brand'], d['preparation'], u'; '.join(d['components']))) for d in new_drugs ]
1808 dlg.set_choices (
1809 choices = choices,
1810 data = new_drugs
1811 )
1812 dlg.ShowModal()
1813 drugs2add = dlg.get_picks()
1814 dlg.Destroy()
1815
1816 if drugs2add is None:
1817 return
1818
1819 if len(drugs2add) == 0:
1820 return
1821
1822 for drug in drugs2add:
1823
1824 intake = emr.add_substance_intake (
1825 pk_component = drug['pk_components'][0],
1826 episode = emr.add_episode(episode_name = gmMedication.DEFAULT_MEDICATION_HISTORY_EPISODE)['pk_episode'],
1827 )
1828 if intake is None:
1829 continue
1830 intake['intake_is_approved_of'] = True
1831 intake.save()
1832
1833 return
1834
1836 """A grid class for displaying current substance intake.
1837
1838 - does NOT listen to the currently active patient
1839 - thereby it can display any patient at any time
1840 """
1842
1843 wx.grid.Grid.__init__(self, *args, **kwargs)
1844
1845 self.__patient = None
1846 self.__row_data = {}
1847 self.__prev_row = None
1848 self.__prev_tooltip_row = None
1849 self.__prev_cell_0 = None
1850 self.__grouping_mode = u'episode'
1851 self.__filter_show_unapproved = True
1852 self.__filter_show_inactive = True
1853
1854 self.__grouping2col_labels = {
1855 u'episode': [
1856 _('Episode'),
1857 _('Substance'),
1858 _('Strength'),
1859 _('Schedule'),
1860 _('Started'),
1861 _('Duration / Until'),
1862 _('Brand'),
1863 _('Advice')
1864 ],
1865 u'brand': [
1866 _('Brand'),
1867 _('Schedule'),
1868 _('Substance'),
1869 _('Strength'),
1870 _('Started'),
1871 _('Duration / Until'),
1872 _('Episode'),
1873 _('Advice')
1874 ]
1875 }
1876
1877 self.__grouping2order_by_clauses = {
1878 u'episode': u'pk_health_issue nulls first, episode, substance, started',
1879 u'brand': u'brand nulls last, substance, started'
1880 }
1881
1882 self.__init_ui()
1883 self.__register_events()
1884
1885
1886
1888
1889 sel_block_top_left = self.GetSelectionBlockTopLeft()
1890 sel_block_bottom_right = self.GetSelectionBlockBottomRight()
1891 sel_cols = self.GetSelectedCols()
1892 sel_rows = self.GetSelectedRows()
1893
1894 selected_cells = []
1895
1896
1897 selected_cells += self.GetSelectedCells()
1898
1899
1900 selected_cells += list (
1901 (row, col)
1902 for row in sel_rows
1903 for col in xrange(self.GetNumberCols())
1904 )
1905
1906
1907 selected_cells += list (
1908 (row, col)
1909 for row in xrange(self.GetNumberRows())
1910 for col in sel_cols
1911 )
1912
1913
1914 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()):
1915 selected_cells += [
1916 (row, col)
1917 for row in xrange(top_left[0], bottom_right[0] + 1)
1918 for col in xrange(top_left[1], bottom_right[1] + 1)
1919 ]
1920
1921 return set(selected_cells)
1922
1924 rows = {}
1925
1926 for row, col in self.get_selected_cells():
1927 rows[row] = True
1928
1929 return rows.keys()
1930
1933
1935
1936 self.empty_grid()
1937
1938 if self.__patient is None:
1939 return
1940
1941 emr = self.__patient.get_emr()
1942 meds = emr.get_current_substance_intake (
1943 order_by = self.__grouping2order_by_clauses[self.__grouping_mode],
1944 include_unapproved = self.__filter_show_unapproved,
1945 include_inactive = self.__filter_show_inactive
1946 )
1947 if not meds:
1948 return
1949
1950 self.BeginBatch()
1951
1952
1953 labels = self.__grouping2col_labels[self.__grouping_mode]
1954 if self.__filter_show_unapproved:
1955 self.AppendCols(numCols = len(labels) + 1)
1956 else:
1957 self.AppendCols(numCols = len(labels))
1958 for col_idx in range(len(labels)):
1959 self.SetColLabelValue(col_idx, labels[col_idx])
1960 if self.__filter_show_unapproved:
1961 self.SetColLabelValue(len(labels), u'OK?')
1962 self.SetColSize(len(labels), 40)
1963
1964 self.AppendRows(numRows = len(meds))
1965
1966
1967 for row_idx in range(len(meds)):
1968 med = meds[row_idx]
1969 self.__row_data[row_idx] = med
1970
1971 if med['is_currently_active'] is True:
1972 atcs = []
1973 if med['atc_substance'] is not None:
1974 atcs.append(med['atc_substance'])
1975
1976
1977
1978 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (med['substance'],))
1979 if allg not in [None, False]:
1980 attr = self.GetOrCreateCellAttr(row_idx, 0)
1981 if allg['type'] == u'allergy':
1982 attr.SetTextColour('red')
1983 else:
1984 attr.SetTextColour('yellow')
1985 self.SetRowAttr(row_idx, attr)
1986 else:
1987 attr = self.GetOrCreateCellAttr(row_idx, 0)
1988 attr.SetTextColour('grey')
1989 self.SetRowAttr(row_idx, attr)
1990
1991 if self.__grouping_mode == u'episode':
1992 if med['pk_episode'] is None:
1993 self.__prev_cell_0 = None
1994 epi = gmTools.u_diameter
1995 else:
1996 if self.__prev_cell_0 == med['episode']:
1997 epi = u''
1998 else:
1999 self.__prev_cell_0 = med['episode']
2000 epi = gmTools.coalesce(med['episode'], u'')
2001 self.SetCellValue(row_idx, 0, gmTools.wrap(text = epi, width = 40))
2002
2003 self.SetCellValue(row_idx, 1, med['substance'])
2004 self.SetCellValue(row_idx, 2, u'%s%s' % (med['amount'], med['unit']))
2005 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], u''))
2006 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
2007
2008 if med['is_long_term']:
2009 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
2010 else:
2011 if med['discontinued'] is None:
2012 if med['duration'] is None:
2013 self.SetCellValue(row_idx, 5, u'')
2014 else:
2015 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
2016 else:
2017 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d'))
2018
2019 if med['pk_brand'] is None:
2020 brand = u''
2021 else:
2022 if med['fake_brand']:
2023 brand = gmTools.coalesce(med['brand'], u'', _('%s (fake)'))
2024 else:
2025 brand = gmTools.coalesce(med['brand'], u'')
2026 self.SetCellValue(row_idx, 6, gmTools.wrap(text = brand, width = 35))
2027
2028 elif self.__grouping_mode == u'brand':
2029
2030 if med['pk_brand'] is None:
2031 self.__prev_cell_0 = None
2032 brand = gmTools.u_diameter
2033 else:
2034 if self.__prev_cell_0 == med['brand']:
2035 brand = u''
2036 else:
2037 self.__prev_cell_0 = med['brand']
2038 if med['fake_brand']:
2039 brand = gmTools.coalesce(med['brand'], u'', _('%s (fake)'))
2040 else:
2041 brand = gmTools.coalesce(med['brand'], u'')
2042 self.SetCellValue(row_idx, 0, gmTools.wrap(text = brand, width = 35))
2043
2044 self.SetCellValue(row_idx, 1, gmTools.coalesce(med['schedule'], u''))
2045 self.SetCellValue(row_idx, 2, med['substance'])
2046 self.SetCellValue(row_idx, 3, u'%s%s' % (med['amount'], med['unit']))
2047 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
2048
2049 if med['is_long_term']:
2050 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
2051 else:
2052 if med['discontinued'] is None:
2053 if med['duration'] is None:
2054 self.SetCellValue(row_idx, 5, u'')
2055 else:
2056 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
2057 else:
2058 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d'))
2059
2060 if med['pk_episode'] is None:
2061 epi = u''
2062 else:
2063 epi = gmTools.coalesce(med['episode'], u'')
2064 self.SetCellValue(row_idx, 6, gmTools.wrap(text = epi, width = 40))
2065
2066 else:
2067 raise ValueError('unknown grouping mode [%s]' % self.__grouping_mode)
2068
2069 if med['notes'] is not None:
2070 self.SetCellValue(row_idx, 7, gmTools.wrap(text = med['notes'], width = 50))
2071
2072 if self.__filter_show_unapproved:
2073 self.SetCellValue (
2074 row_idx,
2075 len(labels),
2076 gmTools.bool2subst(med['intake_is_approved_of'], gmTools.u_checkmark_thin, u'', u'?')
2077 )
2078
2079
2080
2081 self.AutoSize()
2082 self.EndBatch()
2083
2085 self.BeginBatch()
2086 self.ClearGrid()
2087
2088
2089 if self.GetNumberRows() > 0:
2090 self.DeleteRows(pos = 0, numRows = self.GetNumberRows())
2091 if self.GetNumberCols() > 0:
2092 self.DeleteCols(pos = 0, numCols = self.GetNumberCols())
2093 self.EndBatch()
2094 self.__row_data = {}
2095 self.__prev_cell_0 = None
2096
2098
2099 if len(self.__row_data) == 0:
2100 return
2101
2102 sel_rows = self.get_selected_rows()
2103 if len(sel_rows) != 1:
2104 return
2105
2106 drug_db = get_drug_database()
2107 if drug_db is None:
2108 return
2109
2110 intake = self.get_selected_data()[0]
2111 if intake['brand'] is None:
2112 drug_db.show_info_on_substance(substance_intake = intake)
2113 else:
2114 drug_db.show_info_on_drug(substance_intake = intake)
2115
2123
2135
2147
2161
2164
2178
2192
2208
2212
2316
2317
2318
2320 self.CreateGrid(0, 1)
2321 self.EnableEditing(0)
2322 self.EnableDragGridSize(1)
2323 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows)
2324
2325 self.SetColLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTER)
2326
2327 self.SetRowLabelSize(0)
2328 self.SetRowLabelAlignment(horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
2329
2330
2331
2333 return self.__patient
2334
2338
2339 patient = property(_get_patient, _set_patient)
2340
2342 return self.__grouping_mode
2343
2347
2348 grouping_mode = property(_get_grouping_mode, _set_grouping_mode)
2349
2351 return self.__filter_show_unapproved
2352
2356
2357 filter_show_unapproved = property(_get_filter_show_unapproved, _set_filter_show_unapproved)
2358
2360 return self.__filter_show_inactive
2361
2365
2366 filter_show_inactive = property(_get_filter_show_inactive, _set_filter_show_inactive)
2367
2368
2369
2371
2372 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells)
2373
2374
2375
2376
2377 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
2378
2380 """Calculate where the mouse is and set the tooltip dynamically."""
2381
2382
2383
2384 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398 row, col = self.XYToCell(x, y)
2399
2400 if row == self.__prev_tooltip_row:
2401 return
2402
2403 self.__prev_tooltip_row = row
2404
2405 try:
2406 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row))
2407 except KeyError:
2408 pass
2409
2414
2415 from Gnumed.wxGladeWidgets import wxgCurrentSubstancesPnl
2416
2417 -class cCurrentSubstancesPnl(wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl, gmRegetMixin.cRegetOnPaintMixin):
2418
2419 """Panel holding a grid with current substances. Used as notebook page."""
2420
2427
2428
2429
2438
2439
2440
2442 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
2443 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._schedule_data_reget)
2444 gmDispatcher.connect(signal = u'substance_intake_mod_db', receiver = self._schedule_data_reget)
2445
2446
2447
2449 wx.CallAfter(self.__on_pre_patient_selection)
2450
2452 self._grid_substances.patient = None
2453
2456
2459
2462
2465
2468
2471
2474
2477
2480
2483
2486
2489
2492
2495
2496
2497
2498 if __name__ == '__main__':
2499
2500 if len(sys.argv) < 2:
2501 sys.exit()
2502
2503 if sys.argv[1] != 'test':
2504 sys.exit()
2505
2506 from Gnumed.pycommon import gmI18N
2507
2508 gmI18N.activate_locale()
2509 gmI18N.install_domain(domain = 'gnumed')
2510
2511
2512 app = wx.PyWidgetTester(size = (600, 600))
2513
2514 app.SetWidget(cSubstancePhraseWheel, -1)
2515 app.MainLoop()
2516
2517
2518