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
84 pat = gmPerson.gmCurrentPatient()
85 if pat.connected:
86 drug_db.patient = pat
87
88 return drug_db
89
96
97
99
100 dbcfg = gmCfg.cCfgSQL()
101
102 ifap_cmd = dbcfg.get2 (
103 option = 'external.ifap-win.shell_command',
104 workplace = gmSurgery.gmCurrentPractice().active_workplace,
105 bias = 'workplace',
106 default = 'wine "C:\Ifapwin\WIAMDB.EXE"'
107 )
108 found, binary = gmShellAPI.detect_external_binary(ifap_cmd)
109 if not found:
110 gmDispatcher.send('statustext', msg = _('Cannot call IFAP via [%s].') % ifap_cmd)
111 return False
112 ifap_cmd = binary
113
114 if import_drugs:
115 transfer_file = os.path.expanduser(dbcfg.get2 (
116 option = 'external.ifap-win.transfer_file',
117 workplace = gmSurgery.gmCurrentPractice().active_workplace,
118 bias = 'workplace',
119 default = '~/.wine/drive_c/Ifapwin/ifap2gnumed.csv'
120 ))
121
122 try:
123 f = open(transfer_file, 'w+b').close()
124 except IOError:
125 _log.exception('Cannot create IFAP <-> GNUmed transfer file [%s]', transfer_file)
126 gmDispatcher.send('statustext', msg = _('Cannot create IFAP <-> GNUmed transfer file [%s].') % transfer_file)
127 return False
128
129 wx.BeginBusyCursor()
130 gmShellAPI.run_command_in_shell(command = ifap_cmd, blocking = import_drugs)
131 wx.EndBusyCursor()
132
133 if import_drugs:
134
135
136 try:
137 csv_file = open(transfer_file, 'rb')
138 except:
139 _log.exception('cannot access [%s]', fname)
140 csv_file = None
141
142 if csv_file is not None:
143 import csv
144 csv_lines = csv.DictReader (
145 csv_file,
146 fieldnames = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split(),
147 delimiter = ';'
148 )
149 pat = gmPerson.gmCurrentPatient()
150 emr = pat.get_emr()
151
152 epi = emr.add_episode(episode_name = _('Current medication'))
153 for line in csv_lines:
154 narr = u'%sx %s %s %s (\u2258 %s %s) von %s (%s)' % (
155 line['Packungszahl'].strip(),
156 line['Handelsname'].strip(),
157 line['Form'].strip(),
158 line[u'Packungsgr\xf6\xdfe'].strip(),
159 line['Abpackungsmenge'].strip(),
160 line['Einheit'].strip(),
161 line['Hersteller'].strip(),
162 line['PZN'].strip()
163 )
164 emr.add_clin_narrative(note = narr, soap_cat = 's', episode = epi)
165 csv_file.close()
166
167 return True
168
169
170
171
172
174
175 if parent is None:
176 parent = wx.GetApp().GetTopWindow()
177
178 def refresh(lctrl):
179 atcs = gmATC.get_reference_atcs()
180
181 items = [ [
182 a['atc'],
183 a['term'],
184 u'%s' % gmTools.coalesce(a['ddd'], u''),
185 gmTools.coalesce(a['unit'], u''),
186 gmTools.coalesce(a['administrative_route'], u''),
187 gmTools.coalesce(a['comment'], u''),
188 a['version'],
189 a['lang']
190 ] for a in atcs ]
191 lctrl.set_string_items(items)
192 lctrl.set_data(atcs)
193
194 gmListWidgets.get_choices_from_list (
195 parent = parent,
196 msg = _('\nThe ATC codes as known to GNUmed.\n'),
197 caption = _('Showing ATC codes.'),
198 columns = [ u'ATC', _('Term'), u'DDD', _('Unit'), _(u'Route'), _('Comment'), _('Version'), _('Language') ],
199 single_selection = True,
200 refresh_callback = refresh
201 )
202
203
205
206 dlg = wx.FileDialog (
207 parent = None,
208 message = _('Choose an ATC import config file'),
209 defaultDir = os.path.expanduser(os.path.join('~', 'gnumed')),
210 defaultFile = '',
211 wildcard = "%s (*.conf)|*.conf|%s (*)|*" % (_('config files'), _('all files')),
212 style = wx.OPEN | wx.HIDE_READONLY | wx.FILE_MUST_EXIST
213 )
214
215 result = dlg.ShowModal()
216 if result == wx.ID_CANCEL:
217 return
218
219 cfg_file = dlg.GetPath()
220 dlg.Destroy()
221
222 conn = gmAuthWidgets.get_dbowner_connection(procedure = _('importing ATC reference data'))
223 if conn is None:
224 return False
225
226 wx.BeginBusyCursor()
227
228 if gmATC.atc_import(cfg_fname = cfg_file, conn = conn):
229 gmDispatcher.send(signal = 'statustext', msg = _('Successfully imported ATC reference data.'))
230 else:
231 gmDispatcher.send(signal = 'statustext', msg = _('Importing ATC reference data failed.'), beep = True)
232
233 wx.EndBusyCursor()
234 return True
235
236
237
239
241
242 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
243
244 query = u"""
245
246 SELECT DISTINCT ON (label)
247 atc_code,
248 label
249 FROM (
250
251 SELECT
252 code as atc_code,
253 (code || ': ' || term || coalesce(' (' || ddd || unit || ')', ''))
254 AS label
255 FROM ref.atc
256 WHERE
257 term %(fragment_condition)s
258 OR
259 code %(fragment_condition)s
260
261 UNION ALL
262
263 SELECT
264 atc_code,
265 (atc_code || ': ' || description)
266 AS label
267 FROM ref.consumable_substance
268 WHERE
269 description %(fragment_condition)s
270 OR
271 atc_code %(fragment_condition)s
272
273 UNION ALL
274
275 SELECT
276 atc_code,
277 (atc_code || ': ' || description || ' (' || preparation || ')')
278 AS label
279 FROM ref.branded_drug
280 WHERE
281 description %(fragment_condition)s
282 OR
283 atc_code %(fragment_condition)s
284
285 -- it would be nice to be able to include clin.vacc_indication but that's hard to do in SQL
286
287 ) AS candidates
288 WHERE atc_code IS NOT NULL
289 ORDER BY label
290 LIMIT 50"""
291
292 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
293 mp.setThresholds(1, 2, 4)
294
295 self.SetToolTipString(_('Select an ATC (Anatomical-Therapeutic-Chemical) code.'))
296 self.matcher = mp
297 self.selection_only = True
298
299
300
301
303
304 if parent is None:
305 parent = wx.GetApp().GetTopWindow()
306
307 def add_from_db(substance):
308 drug_db = get_drug_database(parent = parent)
309 if drug_db is None:
310 return False
311 drug_db.import_drugs()
312 return True
313
314 def edit(substance=None):
315 return edit_consumable_substance(parent = parent, substance = substance, single_entry = (substance is not None))
316
317 def delete(substance):
318 if substance.is_in_use_by_patients:
319 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete this substance. It is in use.'), beep = True)
320 return False
321
322 return gmMedication.delete_consumable_substance(substance = substance['pk'])
323
324 def refresh(lctrl):
325 substs = gmMedication.get_consumable_substances(order_by = 'description')
326 items = [ [
327 s['description'],
328 s['amount'],
329 s['unit'],
330 gmTools.coalesce(s['atc_code'], u''),
331 s['pk']
332 ] for s in substs ]
333 lctrl.set_string_items(items)
334 lctrl.set_data(substs)
335
336 msg = _('\nThese are the consumable substances registered with GNUmed.\n')
337
338 gmListWidgets.get_choices_from_list (
339 parent = parent,
340 msg = msg,
341 caption = _('Showing consumable substances.'),
342 columns = [_('Substance'), _('Amount'), _('Unit'), 'ATC', u'#'],
343 single_selection = True,
344 new_callback = edit,
345 edit_callback = edit,
346 delete_callback = delete,
347 refresh_callback = refresh,
348 left_extra_button = (_('Import'), _('Import consumable substances from a drug database.'), add_from_db)
349 )
350
351
353
354 if substance is not None:
355 if substance.is_in_use_by_patients:
356 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit this substance. It is in use.'), beep = True)
357 return False
358
359 ea = cConsumableSubstanceEAPnl(parent = parent, id = -1)
360 ea.data = substance
361 ea.mode = gmTools.coalesce(substance, 'new', 'edit')
362 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
363 dlg.SetTitle(gmTools.coalesce(substance, _('Adding new consumable substance'), _('Editing consumable substance')))
364 if dlg.ShowModal() == wx.ID_OK:
365 dlg.Destroy()
366 return True
367 dlg.Destroy()
368 return False
369
370
371 from Gnumed.wxGladeWidgets import wxgConsumableSubstanceEAPnl
372
374
392
393
394
395
396
397
398
399
401
402 validity = True
403
404 if self._TCTRL_substance.GetValue().strip() == u'':
405 validity = False
406 self.display_tctrl_as_valid(tctrl = self._TCTRL_substance, valid = False)
407 self._TCTRL_substance.SetFocus()
408 else:
409 self.display_tctrl_as_valid(tctrl = self._TCTRL_substance, valid = True)
410
411 try:
412 decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.'))
413 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
414 except (TypeError, decimal.InvalidOperation):
415 validity = False
416 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
417 self._TCTRL_amount.SetFocus()
418
419 if self._PRW_unit.GetValue().strip() == u'':
420 validity = False
421 self._PRW_unit.display_as_valid(valid = False)
422 self._TCTRL_substance.SetFocus()
423 else:
424 self._PRW_unit.display_as_valid(valid = True)
425
426 if validity is False:
427 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save consumable substance. Missing essential input.'))
428
429 return validity
430
432 subst = gmMedication.create_consumable_substance (
433 substance = self._TCTRL_substance.GetValue().strip(),
434 atc = self._PRW_atc.GetData(),
435 amount = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.')),
436 unit = gmTools.coalesce(self._PRW_unit.GetData(), self._PRW_unit.GetValue().strip(), function_initial = ('strip', None))
437 )
438 success, data = subst.save()
439 if not success:
440 err, msg = data
441 _log.error(err)
442 _log.error(msg)
443 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save consumable substance. %s') % msg, beep = True)
444 return False
445
446 self.data = subst
447 return True
448
450 self.data['description'] = self._TCTRL_substance.GetValue().strip()
451 self.data['atc_code'] = self._PRW_atc.GetData()
452 self.data['amount'] = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.'))
453 self.data['unit'] = gmTools.coalesce(self._PRW_unit.GetData(), self._PRW_unit.GetValue().strip(), function_initial = ('strip', None))
454 success, data = self.data.save()
455
456 if not success:
457 err, msg = data
458 _log.error(err)
459 _log.error(msg)
460 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save consumable substance. %s') % msg, beep = True)
461 return False
462
463 return True
464
466 self._TCTRL_substance.SetValue(u'')
467 self._TCTRL_amount.SetValue(u'')
468 self._PRW_unit.SetText(u'', None)
469 self._PRW_atc.SetText(u'', None)
470
471 self._TCTRL_substance.SetFocus()
472
480
482 self._refresh_as_new()
483
484
485
486
496
497 def delete(component):
498 if component.is_in_use_by_patients:
499 gmDispatcher.send(signal = 'statustext', msg = _('Cannot remove this component from the drug. It is in use.'), beep = True)
500 return False
501
502 return component.containing_drug.remove_component(substance = component['pk_component'])
503
504 def refresh(lctrl):
505 comps = gmMedication.get_drug_components()
506 items = [ [
507 u'%s%s' % (c['brand'], gmTools.coalesce(c['atc_brand'], u'', u' [%s]')),
508 u'%s%s' % (c['substance'], gmTools.coalesce(c['atc_substance'], u'', u' [%s]')),
509 u'%s%s' % (c['amount'], c['unit']),
510 c['preparation'],
511 gmTools.coalesce(c['external_code_brand'], u'', u'%%s [%s]' % c['external_code_type_brand']),
512 c['pk_component']
513 ] for c in comps ]
514 lctrl.set_string_items(items)
515 lctrl.set_data(comps)
516
517 msg = _('\nThese are the components in the drug brands known to GNUmed.\n')
518
519 gmListWidgets.get_choices_from_list (
520 parent = parent,
521 msg = msg,
522 caption = _('Showing drug brand components.'),
523 columns = [_('Brand'), _('Substance'), _('Strength'), _('Preparation'), _('Code'), u'#'],
524 single_selection = True,
525
526 edit_callback = edit,
527 delete_callback = delete,
528 refresh_callback = refresh
529 )
530
531
543
544
545 from Gnumed.wxGladeWidgets import wxgDrugComponentEAPnl
546
547 -class cDrugComponentEAPnl(wxgDrugComponentEAPnl.wxgDrugComponentEAPnl, gmEditArea.cGenericEditAreaMixin):
548
566
567
568
569
570
571
572
573
575 if self.data is not None:
576 if self.data['is_in_use']:
577 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit drug component. It is in use.'), beep = True)
578 return False
579
580 validity = True
581
582 if self._PRW_substance.GetData() is None:
583 validity = False
584 self._PRW_substance.display_as_valid(False)
585 else:
586 self._PRW_substance.display_as_valid(True)
587
588 val = self._TCTRL_amount.GetValue().strip().replace(',', u'.', 1)
589 try:
590 decimal.Decimal(val)
591 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
592 except:
593 validity = False
594 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
595
596 if self._PRW_unit.GetValue().strip() == u'':
597 validity = False
598 self._PRW_unit.display_as_valid(False)
599 else:
600 self._PRW_unit.display_as_valid(True)
601
602 if validity is False:
603 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save drug component. Invalid or missing essential input.'))
604
605 return validity
606
608
609 data = 1
610 data[''] = 1
611 data[''] = 1
612
613
614
615
616
617
618 return False
619 return True
620
622 self.data['pk_consumable_substance'] = self._PRW_substance.GetData()
623 self.data['amount'] = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', u'.', 1))
624 self.data['unit'] = self._PRW_unit.GetValue().strip()
625 return self.data.save()
626
636
638 self._TCTRL_brand.SetValue(u'%s (%s)' % (self.data['brand'], self.data['preparation']))
639 self._TCTRL_components.SetValue(u' / '.join(self.data.containing_drug['components']))
640 details = []
641 if self.data['atc_brand'] is not None:
642 details.append(u'ATC: %s' % self.data['atc_brand'])
643 if self.data['external_code_brand'] is not None:
644 details.append(u'%s: %s' % (self.data['external_code_type_brand'], self.data['external_code_brand']))
645 self._TCTRL_codes.SetValue(u'; '.join(details))
646
647 self._PRW_substance.SetText(self.data['substance'], self.data['pk_consumable_substance'])
648 self._TCTRL_amount.SetValue(u'%s' % self.data['amount'])
649 self._PRW_unit.SetText(self.data['unit'], self.data['unit'])
650
651 self._PRW_substance.SetFocus()
652
654
655
656
657 self._PRW_substance.SetText(u'', None)
658 self._TCTRL_amount.SetValue(u'')
659 self._PRW_unit.SetText(u'', None)
660
661 self._PRW_substance.SetFocus()
662
663
674
675
677
679
680 query = u"""
681 (
682 SELECT DISTINCT ON (preparation)
683 preparation as prep, preparation
684 FROM ref.branded_drug
685 WHERE preparation %(fragment_condition)s
686 ) UNION (
687 SELECT DISTINCT ON (preparation)
688 preparation as prep, preparation
689 FROM clin.substance_intake
690 WHERE preparation %(fragment_condition)s
691 )
692 ORDER BY prep
693 limit 30"""
694
695 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
696 mp.setThresholds(1, 2, 4)
697 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
698 self.SetToolTipString(_('The preparation (form) of the substance or brand.'))
699 self.matcher = mp
700 self.selection_only = False
701
713
714
715
717
718 if brand is not None:
719 if brand.is_in_use_by_patients:
720 gmGuiHelpers.gm_show_info (
721 aTitle = _('Managing components of a drug'),
722 aMessage = _(
723 'Cannot manage the components of the branded drug product\n'
724 '\n'
725 ' "%s" (%s)\n'
726 '\n'
727 'because it is currently taken by patients.\n'
728 ) % (brand['brand'], brand['preparation'])
729 )
730 return False
731
732 if parent is None:
733 parent = wx.GetApp().GetTopWindow()
734
735 if brand is None:
736 msg = _('Pick the substances which are components of this drug.')
737 right_col = _('Components of drug')
738 comp_substs = []
739 else:
740 right_col = u'%s (%s)' % (brand['brand'], brand['preparation'])
741 msg = _(
742 'Adjust the components of "%s"\n'
743 '\n'
744 'The drug must contain at least one component. Any given\n'
745 'substance can only be included once per drug.'
746 ) % right_col
747 comp_substs = [ c.substance for c in brand.components ]
748
749 substs = gmMedication.get_consumable_substances(order_by = 'description')
750 choices = [ u'%s %s%s' % (s['description'], s['amount'], s['unit']) for s in substs ]
751 picks = [ u'%s %s%s' % (c['description'], c['amount'], c['unit']) for c in comp_substs ]
752
753 picker = gmListWidgets.cItemPickerDlg (
754 parent,
755 -1,
756 title = _('Managing components of a drug ...'),
757 msg = msg
758 )
759 picker.set_columns(['Substances'], [right_col])
760 picker.set_choices(choices = choices, data = substs)
761 picker.set_picks(picks = picks, data = comp_substs)
762
763 btn_pressed = picker.ShowModal()
764 substs = picker.get_picks()
765 picker.Destroy()
766
767 if btn_pressed != wx.ID_OK:
768 return (False, None)
769
770 if brand is not None:
771 brand.set_substances_as_components(substances = substs)
772
773 return (True, substs)
774
776
777 if parent is None:
778 parent = wx.GetApp().GetTopWindow()
779
780 def add_from_db(brand):
781 drug_db = get_drug_database(parent = parent)
782 if drug_db is None:
783 return False
784 drug_db.import_drugs()
785 return True
786
787 def get_tooltip(brand=None):
788 tt = u'%s %s\n' % (brand['brand'], brand['preparation'])
789 tt += u'\n'
790 tt += u'%s%s%s\n' % (
791 gmTools.bool2subst(brand.is_vaccine, u'%s, ' % _('Vaccine'), u''),
792 u'%s, ' % gmTools.bool2subst(brand.is_in_use_by_patients, _('in use'), _('not in use')),
793 gmTools.bool2subst(brand['is_fake_brand'], _('fake'), u'')
794 )
795 tt += gmTools.coalesce(brand['atc'], u'', _('ATC: %s\n'))
796 tt += gmTools.coalesce(brand['external_code'], u'', u'%s: %%s\n' % brand['external_code_type'])
797 if brand['components'] is not None:
798 tt += u'- %s' % u'\n- '.join(brand['components'])
799 return tt
800
801 def edit(brand=None):
802 if brand.is_vaccine:
803 gmGuiHelpers.gm_show_info (
804 aTitle = _('Editing medication'),
805 aMessage = _(
806 'Cannot edit the medication\n'
807 '\n'
808 ' "%s" (%s)\n'
809 '\n'
810 'because it is a vaccine. Please edit it\n'
811 'from the vaccine management section !\n'
812 ) % (brand['brand'], brand['preparation'])
813 )
814 return False
815
816 return edit_branded_drug(parent = parent, branded_drug = brand, single_entry = True)
817
818 def delete(brand):
819 if brand.is_vaccine:
820 gmGuiHelpers.gm_show_info (
821 aTitle = _('Deleting medication'),
822 aMessage = _(
823 'Cannot delete the medication\n'
824 '\n'
825 ' "%s" (%s)\n'
826 '\n'
827 'because it is a vaccine. Please delete it\n'
828 'from the vaccine management section !\n'
829 ) % (brand['brand'], brand['preparation'])
830 )
831 return False
832 gmMedication.delete_branded_drug(brand = brand['pk_brand'])
833 return True
834
835 def new():
836 return edit_branded_drug(parent = parent, branded_drug = None, single_entry = False)
837
838 def refresh(lctrl):
839 drugs = gmMedication.get_branded_drugs()
840 items = [ [
841 u'%s%s' % (
842 d['brand'],
843 gmTools.bool2subst(d['is_fake_brand'], ' (%s)' % _('fake'), u'')
844 ),
845 d['preparation'],
846 gmTools.coalesce(d['atc'], u''),
847 gmTools.coalesce(d['components'], u''),
848 gmTools.coalesce(d['external_code'], u'', u'%%s [%s]' % d['external_code_type']),
849 d['pk_brand']
850 ] for d in drugs ]
851 lctrl.set_string_items(items)
852 lctrl.set_data(drugs)
853
854 msg = _('\nThese are the drug brands known to GNUmed.\n')
855
856 gmListWidgets.get_choices_from_list (
857 parent = parent,
858 msg = msg,
859 caption = _('Showing branded drugs.'),
860 columns = [_('Name'), _('Preparation'), _('ATC'), _('Components'), _('Code'), u'#'],
861 single_selection = True,
862 ignore_OK_button = ignore_OK_button,
863 refresh_callback = refresh,
864 new_callback = new,
865 edit_callback = edit,
866 delete_callback = delete,
867 list_tooltip_callback = get_tooltip,
868 left_extra_button = (_('Import'), _('Import substances and brands from a drug database.'), add_from_db)
869
870
871 )
872
873
875 if branded_drug is not None:
876 if branded_drug.is_in_use_by_patients:
877 gmGuiHelpers.gm_show_info (
878 aTitle = _('Editing drug'),
879 aMessage = _(
880 'Cannot edit the branded drug product\n'
881 '\n'
882 ' "%s" (%s)\n'
883 '\n'
884 'because it is currently taken by patients.\n'
885 ) % (branded_drug['brand'], branded_drug['preparation'])
886 )
887 return False
888
889 ea = cBrandedDrugEAPnl(parent = parent, id = -1)
890 ea.data = branded_drug
891 ea.mode = gmTools.coalesce(branded_drug, 'new', 'edit')
892 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
893 dlg.SetTitle(gmTools.coalesce(branded_drug, _('Adding new drug brand'), _('Editing drug brand')))
894 if dlg.ShowModal() == wx.ID_OK:
895 dlg.Destroy()
896 return True
897 dlg.Destroy()
898 return False
899
900
901 from Gnumed.wxGladeWidgets import wxgBrandedDrugEAPnl
902
903 -class cBrandedDrugEAPnl(wxgBrandedDrugEAPnl.wxgBrandedDrugEAPnl, gmEditArea.cGenericEditAreaMixin):
904
921
922
923
924
925
926
927
928
930
931 if self.data is not None:
932 if self.data.is_in_use_by_patients:
933 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit drug brand. It is in use.'), beep = True)
934 return False
935
936 validity = True
937
938 if self._PRW_brand.GetValue().strip() == u'':
939 validity = False
940 self._PRW_brand.display_as_valid(False)
941 else:
942 self._PRW_brand.display_as_valid(True)
943
944 if self._PRW_preparation.GetValue().strip() == u'':
945 validity = False
946 self._PRW_preparation.display_as_valid(False)
947 else:
948 self._PRW_preparation.display_as_valid(True)
949
950 if validity is False:
951 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save branded drug. Invalid or missing essential input.'))
952
953 return validity
954
956
957 drug = gmMedication.create_branded_drug (
958 brand_name = self._PRW_brand.GetValue().strip(),
959 preparation = gmTools.coalesce (
960 self._PRW_preparation.GetData(),
961 self._PRW_preparation.GetValue()
962 ).strip(),
963 return_existing = True
964 )
965 drug['is_fake_brand'] = self._CHBOX_is_fake.GetValue()
966 drug['atc'] = self._PRW_atc.GetData()
967 code = self._TCTRL_external_code.GetValue().strip()
968 if code != u'':
969 drug['external_code'] = code
970 drug['external_code_type'] = self._PRW_external_code_type.GetData().strip()
971
972 drug.save()
973
974 if len(self.__component_substances) > 0:
975 drug.set_substances_as_components(substances = self.__component_substances)
976
977 self.data = drug
978
979 return True
980
982 self.data['brand'] = self._PRW_brand.GetValue().strip()
983 self.data['preparation'] = gmTools.coalesce (
984 self._PRW_preparation.GetData(),
985 self._PRW_preparation.GetValue()
986 ).strip()
987 self.data['is_fake_brand'] = self._CHBOX_is_fake.GetValue()
988 self.data['atc'] = self._PRW_atc.GetData()
989 code = self._TCTRL_external_code.GetValue().strip()
990 if code != u'':
991 self.data['external_code'] = code
992 self.data['external_code_type'] = self._PRW_external_code_type.GetData().strip()
993 success, data = self.data.save()
994 if not success:
995 err, msg = data
996 _log.error('problem saving')
997 _log.error('%s', err)
998 _log.error('%s', msg)
999 return (success is True)
1000
1002 self._PRW_brand.SetText(u'', None)
1003 self._PRW_preparation.SetText(u'', None)
1004 self._CHBOX_is_fake.SetValue(False)
1005 self._TCTRL_components.SetValue(u'')
1006 self._PRW_atc.SetText(u'', None)
1007 self._TCTRL_external_code.SetValue(u'')
1008 self._PRW_external_code_type.SetText(u'', None)
1009
1010 self._PRW_brand.SetFocus()
1011
1012 self.__component_substances = []
1013
1015 self._refresh_as_new()
1016
1033
1034
1035
1045
1047
1049
1050 query = u"""
1051 SELECT
1052 pk,
1053 (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', ''))
1054 AS brand
1055 FROM ref.branded_drug
1056 WHERE description %(fragment_condition)s
1057 ORDER BY brand
1058 LIMIT 50"""
1059
1060 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
1061 mp.setThresholds(2, 3, 4)
1062 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1063 self.SetToolTipString(_('The brand name of the drug.'))
1064 self.matcher = mp
1065 self.selection_only = False
1066
1067
1068
1069
1071
1073
1074 query = u"""
1075 SELECT DISTINCT ON (sched)
1076 schedule as sched,
1077 schedule
1078 FROM clin.substance_intake
1079 WHERE schedule %(fragment_condition)s
1080 ORDER BY sched
1081 LIMIT 50"""
1082
1083 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
1084 mp.setThresholds(1, 2, 4)
1085 mp.word_separators = '[ \t=+&:@]+'
1086 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1087 self.SetToolTipString(_('The schedule for taking this substance.'))
1088 self.matcher = mp
1089 self.selection_only = False
1090
1092
1093 if intake['is_currently_active']:
1094 intake['discontinued'] = gmDateTime.pydt_now_here()
1095 if intake['discontinue_reason'] is None:
1096 intake['discontinue_reason'] = u'%s %s' % (_('not tolerated:'), _('discontinued due to allergy or intolerance'))
1097 else:
1098 if not intake['discontinue_reason'].startswith(_('not tolerated:')):
1099 intake['discontinue_reason'] = u'%s %s' % (_('not tolerated:'), intake['discontinue_reason'])
1100 if not intake.save():
1101 return False
1102
1103 allg = intake.turn_into_allergy(encounter_id = emr.active_encounter['pk_encounter'])
1104
1105 brand = intake.containing_drug
1106 if brand is not None:
1107 comps = [ c['substance'] for c in brand.components ]
1108 if len(comps) > 1:
1109 gmGuiHelpers.gm_show_info (
1110 aTitle = _(u'Documented an allergy'),
1111 aMessage = _(
1112 u'An allergy was documented against the substance:\n'
1113 u'\n'
1114 u' [%s]\n'
1115 u'\n'
1116 u'This substance was taken with the multi-component brand:\n'
1117 u'\n'
1118 u' [%s (%s)]\n'
1119 u'\n'
1120 u'Note that ALL components of this brand were discontinued.'
1121 ) % (
1122 intake['substance'],
1123 intake['brand'],
1124 u' & '.join(comps)
1125 )
1126 )
1127
1128 if parent is None:
1129 parent = wx.GetApp().GetTopWindow()
1130
1131 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent = parent, id = -1)
1132 dlg.ShowModal()
1133
1134 return True
1135
1136 from Gnumed.wxGladeWidgets import wxgCurrentMedicationEAPnl
1137
1138 -class cSubstanceIntakeEAPnl(wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl, gmEditArea.cGenericEditAreaMixin):
1139
1157
1159
1160 self._PRW_component.add_callback_on_lose_focus(callback = self._on_leave_component)
1161 self._PRW_component.selection_only = True
1162
1163 self._PRW_substance.add_callback_on_lose_focus(callback = self._on_leave_substance)
1164 self._PRW_substance.selection_only = True
1165
1167 emr = gmPerson.gmCurrentPatient().get_emr()
1168
1169 state = emr.allergy_state
1170 if state['last_confirmed'] is None:
1171 confirmed = _('never')
1172 else:
1173 confirmed = state['last_confirmed'].strftime('%Y %B %d').decode(gmI18N.get_encoding())
1174 msg = _(u'%s, last confirmed %s\n') % (state.state_string, confirmed)
1175 msg += gmTools.coalesce(state['comment'], u'', _('Comment (%s): %%s\n') % state['modified_by'])
1176 msg += u'\n'
1177
1178 for allergy in emr.get_allergies():
1179 msg += u'%s (%s, %s): %s\n' % (
1180 allergy['descriptor'],
1181 allergy['l10n_type'],
1182 gmTools.bool2subst(allergy['definite'], _('definite'), _('suspected'), u'?'),
1183 gmTools.coalesce(allergy['reaction'], _('reaction not recorded'))
1184 )
1185
1186 self._LBL_allergies.SetLabel(msg)
1187
1188
1189
1255
1257
1258 emr = gmPerson.gmCurrentPatient().get_emr()
1259 epi = self._PRW_episode.GetData(can_create = True)
1260
1261 if self._PRW_substance.GetData() is None:
1262
1263 intake = emr.add_substance_intake (
1264 pk_component = self._PRW_component.GetData(),
1265 episode = epi
1266 )
1267 else:
1268 intake = emr.add_substance_intake (
1269 pk_substance = self._PRW_substance.GetData(),
1270 episode = epi,
1271 preparation = self._PRW_preparation.GetValue().strip()
1272 )
1273
1274 if intake is None:
1275 gmDispatcher.send('statustext', msg = _('Cannot add duplicate of (maybe inactive) substance intake.'), beep = True)
1276 return False
1277
1278 intake['started'] = self._DP_started.GetData()
1279 intake['discontinued'] = self._DP_discontinued.GetData()
1280 if intake['discontinued'] is None:
1281 intake['discontinue_reason'] = None
1282 else:
1283 intake['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
1284 intake['schedule'] = self._PRW_schedule.GetValue().strip()
1285 intake['aim'] = self._PRW_aim.GetValue().strip()
1286 intake['notes'] = self._PRW_notes.GetValue().strip()
1287 intake['is_long_term'] = self._CHBOX_long_term.IsChecked()
1288 intake['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
1289 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
1290 intake['duration'] = None
1291 else:
1292 intake['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
1293 intake.save()
1294
1295 self.data = intake
1296
1297 return True
1298
1300
1301
1302 self.data['started'] = self._DP_started.GetData()
1303 self.data['discontinued'] = self._DP_discontinued.GetData()
1304 if self.data['discontinued'] is None:
1305 self.data['discontinue_reason'] = None
1306 else:
1307 self.data['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
1308 self.data['schedule'] = self._PRW_schedule.GetValue()
1309 self.data['is_long_term'] = self._CHBOX_long_term.IsChecked()
1310 self.data['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
1311 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
1312 self.data['duration'] = None
1313 else:
1314 self.data['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
1315
1316
1317 self.data['preparation'] = self._PRW_preparation.GetValue()
1318
1319
1320 self.data['aim'] = self._PRW_aim.GetValue()
1321 self.data['notes'] = self._PRW_notes.GetValue()
1322 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True)
1323
1324 self.data.save()
1325
1326 return True
1327
1329 self._PRW_component.SetText(u'', None)
1330 self._TCTRL_brand_ingredients.SetValue(u'')
1331 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1332
1333 self._PRW_substance.SetText(u'', None)
1334 self._PRW_substance.Enable(True)
1335
1336 self._PRW_preparation.SetText(u'', None)
1337 self._PRW_preparation.Enable(True)
1338
1339 self._PRW_schedule.SetText(u'', None)
1340 self._PRW_duration.SetText(u'', None)
1341 self._PRW_aim.SetText(u'', None)
1342 self._PRW_notes.SetText(u'', None)
1343 self._PRW_episode.SetText(u'', None)
1344
1345 self._CHBOX_long_term.SetValue(False)
1346 self._CHBOX_approved.SetValue(True)
1347
1348 self._DP_started.SetData(gmDateTime.pydt_now_here())
1349 self._DP_discontinued.SetData(None)
1350 self._PRW_discontinue_reason.SetValue(u'')
1351
1352 self.__refresh_allergies()
1353
1354 self._PRW_component.SetFocus()
1355
1357
1358 self._TCTRL_brand_ingredients.SetValue(u'')
1359 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1360
1361 if self.data['pk_brand'] is None:
1362 self.__refresh_from_existing_substance()
1363 else:
1364 self.__refresh_from_existing_component()
1365
1366 self._PRW_component.Enable(False)
1367 self._PRW_substance.Enable(False)
1368
1369 if self.data['is_long_term']:
1370 self._CHBOX_long_term.SetValue(True)
1371 self._PRW_duration.Enable(False)
1372 self._PRW_duration.SetText(gmTools.u_infinity, None)
1373 self._BTN_discontinued_as_planned.Enable(False)
1374 else:
1375 self._CHBOX_long_term.SetValue(False)
1376 self._PRW_duration.Enable(True)
1377 self._BTN_discontinued_as_planned.Enable(True)
1378 if self.data['duration'] is None:
1379 self._PRW_duration.SetText(u'', None)
1380 else:
1381 self._PRW_duration.SetText(gmDateTime.format_interval(self.data['duration'], gmDateTime.acc_days), self.data['duration'])
1382 self._PRW_aim.SetText(gmTools.coalesce(self.data['aim'], u''), self.data['aim'])
1383 self._PRW_notes.SetText(gmTools.coalesce(self.data['notes'], u''), self.data['notes'])
1384 self._PRW_episode.SetData(self.data['pk_episode'])
1385 self._PRW_schedule.SetText(gmTools.coalesce(self.data['schedule'], u''), self.data['schedule'])
1386
1387 self._CHBOX_approved.SetValue(self.data['intake_is_approved_of'])
1388
1389 self._DP_started.SetData(self.data['started'])
1390 self._DP_discontinued.SetData(self.data['discontinued'])
1391 self._PRW_discontinue_reason.SetValue(gmTools.coalesce(self.data['discontinue_reason'], u''))
1392 if self.data['discontinued'] is not None:
1393 self._PRW_discontinue_reason.Enable()
1394
1395 self.__refresh_allergies()
1396
1397 self._PRW_schedule.SetFocus()
1398
1400 self._LBL_component.Enable(False)
1401 self._PRW_component.SetText(u'', None)
1402 self._PRW_component.display_as_valid(True)
1403
1404 self._PRW_substance.SetText (
1405 u'%s %s%s' % (self.data['substance'], self.data['amount'], self.data['unit']),
1406 self.data['pk_substance']
1407 )
1408
1409 self._PRW_preparation.SetText(gmTools.coalesce(self.data['preparation'], u''), self.data['preparation'])
1410 self._PRW_preparation.Enable(True)
1411
1413 self._PRW_component.SetText (
1414 u'%s %s%s (%s)' % (self.data['substance'], self.data['amount'], self.data['unit'], self.data['brand']),
1415 self.data['pk_drug_component']
1416 )
1417
1418 brand = gmMedication.cBrandedDrug(aPK_obj = self.data['pk_brand'])
1419 if brand['components'] is not None:
1420 self._TCTRL_brand_ingredients.SetValue(u'; '.join(brand['components']))
1421 tt = u'%s:\n\n- %s' % (
1422 self.data['brand'],
1423 u'\n- '.join(brand['components'])
1424 )
1425 self._TCTRL_brand_ingredients.SetToolTipString(tt)
1426
1427 self._LBL_or.Enable(False)
1428 self._LBL_substance.Enable(False)
1429 self._PRW_substance.SetText(u'', None)
1430 self._PRW_substance.display_as_valid(True)
1431
1432 self._PRW_preparation.SetText(self.data['preparation'], self.data['preparation'])
1433 self._PRW_preparation.Enable(False)
1434
1436 self._refresh_as_new()
1437
1438
1439
1441 if self._PRW_component.GetData() is None:
1442 self._LBL_or.Enable(True)
1443 self._PRW_component.SetText(u'', None)
1444 self._LBL_substance.Enable(True)
1445 self._PRW_substance.Enable(True)
1446 self._LBL_preparation.Enable(True)
1447 self._PRW_preparation.Enable(True)
1448 self._PRW_preparation.SetText(u'', None)
1449 self._TCTRL_brand_ingredients.SetValue(u'')
1450 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1451 else:
1452 self._LBL_or.Enable(False)
1453 self._LBL_substance.Enable(False)
1454 self._PRW_substance.SetText(u'', None)
1455 self._PRW_substance.display_as_valid(True)
1456 self._PRW_substance.Enable(False)
1457 self._LBL_preparation.Enable(False)
1458 self._PRW_preparation.Enable(False)
1459 comp = gmMedication.cDrugComponent(aPK_obj = self._PRW_component.GetData())
1460 self._PRW_preparation.SetText(comp['preparation'], comp['preparation'])
1461 brand = comp.containing_drug
1462 if brand['components'] is not None:
1463 self._TCTRL_brand_ingredients.SetValue(u'; '.join(brand['components']))
1464 tt = u'%s:\n\n- %s' % (
1465 brand['brand'],
1466 u'\n- '.join(brand['components'])
1467 )
1468 self._TCTRL_brand_ingredients.SetToolTipString(tt)
1469
1471 if self._PRW_substance.GetData() is None:
1472 self._LBL_or.Enable(True)
1473 self._LBL_component.Enable(True)
1474 self._PRW_component.Enable(True)
1475 self._PRW_substance.SetText(u'', None)
1476 else:
1477 self._LBL_or.Enable(False)
1478 self._LBL_component.Enable(False)
1479 self._PRW_component.SetText(u'', None)
1480 self._PRW_component.display_as_valid(True)
1481 self._PRW_component.Enable(False)
1482 self._LBL_preparation.Enable(True)
1483 self._PRW_preparation.Enable(True)
1484 self._TCTRL_brand_ingredients.SetValue(u'')
1485 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1486
1488 if self._DP_discontinued.GetData() is None:
1489 self._PRW_discontinue_reason.Enable(False)
1490 else:
1491 self._PRW_discontinue_reason.Enable(True)
1492
1495
1498
1528
1530 if self._CHBOX_long_term.IsChecked() is True:
1531 self._PRW_duration.Enable(False)
1532 self._BTN_discontinued_as_planned.Enable(False)
1533 self._PRW_discontinue_reason.Enable(False)
1534 else:
1535 self._PRW_duration.Enable(True)
1536 self._BTN_discontinued_as_planned.Enable(True)
1537 self._PRW_discontinue_reason.Enable(True)
1538
1539 self.__refresh_allergies()
1540
1550
1552
1553 subst = gmMedication.cSubstanceIntakeEntry(aPK_obj = substance)
1554 msg = _(
1555 '\n'
1556 '[%s]\n'
1557 '\n'
1558 'It may be prudent to edit (before deletion) the details\n'
1559 'of this substance intake entry so as to leave behind\n'
1560 'some indication of why it was deleted.\n'
1561 ) % subst.format()
1562
1563 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
1564 parent,
1565 -1,
1566 caption = _('Deleting medication / substance intake'),
1567 question = msg,
1568 button_defs = [
1569 {'label': _('&Edit'), 'tooltip': _('Allow editing of substance intake entry before deletion.'), 'default': True},
1570 {'label': _('&Delete'), 'tooltip': _('Delete immediately without editing first.')},
1571 {'label': _('&Cancel'), 'tooltip': _('Abort. Do not delete or edit substance intake entry.')}
1572 ]
1573 )
1574
1575 edit_first = dlg.ShowModal()
1576 dlg.Destroy()
1577
1578 if edit_first == wx.ID_CANCEL:
1579 return
1580
1581 if edit_first == wx.ID_YES:
1582 edit_intake_of_substance(parent = parent, substance = subst)
1583 delete_it = gmGuiHelpers.gm_show_question (
1584 aMessage = _('Now delete substance intake entry ?'),
1585 aTitle = _('Deleting medication / substance intake')
1586 )
1587 else:
1588 delete_it = True
1589
1590 if not delete_it:
1591 return
1592
1593 gmMedication.delete_substance_intake(substance = substance)
1594
1609
1610
1611
1612
1640
1642
1643 if parent is None:
1644 parent = wx.GetApp().GetTopWindow()
1645
1646
1647 dbcfg = gmCfg.cCfgSQL()
1648 option = u'form_templates.medication_list'
1649
1650 template = dbcfg.get2 (
1651 option = option,
1652 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1653 bias = 'user'
1654 )
1655
1656 if template is None:
1657 template = configure_medication_list_template(parent = parent)
1658 if template is None:
1659 gmGuiHelpers.gm_show_error (
1660 aMessage = _('There is no medication list template configured.'),
1661 aTitle = _('Printing medication list')
1662 )
1663 return False
1664 else:
1665 try:
1666 name, ver = template.split(u' - ')
1667 except:
1668 _log.exception('problem splitting medication list template name [%s]', template)
1669 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading medication list template.'), beep = True)
1670 return False
1671 template = gmForms.get_form_template(name_long = name, external_version = ver)
1672 if template is None:
1673 gmGuiHelpers.gm_show_error (
1674 aMessage = _('Cannot load medication list template [%s - %s]') % (name, ver),
1675 aTitle = _('Printing medication list')
1676 )
1677 return False
1678
1679
1680 try:
1681 meds_list = template.instantiate()
1682 except KeyError:
1683 _log.exception('cannot instantiate medication list template [%s]', template)
1684 gmGuiHelpers.gm_show_error (
1685 aMessage = _('Invalid medication list template [%s - %s (%s)]') % (name, ver, template['engine']),
1686 aTitle = _('Printing medication list')
1687 )
1688 return False
1689
1690 ph = gmMacro.gmPlaceholderHandler()
1691
1692 meds_list.substitute_placeholders(data_source = ph)
1693 pdf_name = meds_list.generate_output()
1694 if pdf_name is None:
1695 gmGuiHelpers.gm_show_error (
1696 aMessage = _('Error generating the medication list.'),
1697 aTitle = _('Printing medication list')
1698 )
1699 return False
1700
1701
1702 printed = gmPrinting.print_files(filenames = [pdf_name], jobtype = 'medication_list')
1703 if not printed:
1704 gmGuiHelpers.gm_show_error (
1705 aMessage = _('Error printing the medication list.'),
1706 aTitle = _('Printing medication list')
1707 )
1708 return False
1709
1710 pat = gmPerson.gmCurrentPatient()
1711 emr = pat.get_emr()
1712 epi = emr.add_episode(episode_name = 'administration', is_open = False)
1713 emr.add_clin_narrative (
1714 soap_cat = None,
1715 note = _('medication list printed from template [%s - %s]') % (template['name_long'], template['external_version']),
1716 episode = epi
1717 )
1718
1719 return True
1720
1722
1723 if len(prescribed_drugs) == 0:
1724 return
1725
1726 curr_brands = [ i['pk_brand'] for i in emr.get_current_substance_intake() if i['pk_brand'] is not None ]
1727 new_drugs = []
1728 for drug in prescribed_drugs:
1729 if drug['pk_brand'] not in curr_brands:
1730 new_drugs.append(drug)
1731
1732 if len(new_drugs) == 0:
1733 return
1734
1735 if parent is None:
1736 parent = wx.GetApp().GetTopWindow()
1737
1738 dlg = gmListWidgets.cItemPickerDlg (
1739 parent,
1740 -1,
1741 msg = _(
1742 'These brands have been prescribed but are not listed\n'
1743 'in the current medication list of this patient.\n'
1744 '\n'
1745 'Please select those you want added to the medication list.'
1746 )
1747 )
1748 dlg.set_columns (
1749 columns = [_('Newly prescribed drugs')],
1750 columns_right = [_('Add to medication list')]
1751 )
1752 choices = [ (u'%s %s (%s)' % (d['brand'], d['preparation'], u'; '.join(d['components']))) for d in new_drugs ]
1753 dlg.set_choices (
1754 choices = choices,
1755 data = new_drugs
1756 )
1757 dlg.ShowModal()
1758 drugs2add = dlg.get_picks()
1759 dlg.Destroy()
1760
1761 if drugs2add is None:
1762 return
1763
1764 if len(drugs2add) == 0:
1765 return
1766
1767 for drug in drugs2add:
1768
1769 intake = emr.add_substance_intake (
1770 pk_component = drug['pk_components'][0],
1771 episode = emr.add_episode(episode_name = gmMedication.DEFAULT_MEDICATION_HISTORY_EPISODE)['pk_episode'],
1772 )
1773 if intake is None:
1774 continue
1775 intake['intake_is_approved_of'] = True
1776 intake.save()
1777
1778 return
1779
1781 """A grid class for displaying current substance intake.
1782
1783 - does NOT listen to the currently active patient
1784 - thereby it can display any patient at any time
1785 """
1787
1788 wx.grid.Grid.__init__(self, *args, **kwargs)
1789
1790 self.__patient = None
1791 self.__row_data = {}
1792 self.__prev_row = None
1793 self.__prev_tooltip_row = None
1794 self.__prev_cell_0 = None
1795 self.__grouping_mode = u'episode'
1796 self.__filter_show_unapproved = True
1797 self.__filter_show_inactive = True
1798
1799 self.__grouping2col_labels = {
1800 u'episode': [
1801 _('Episode'),
1802 _('Substance'),
1803 _('Strength'),
1804 _('Schedule'),
1805 _('Started'),
1806 _('Duration / Until'),
1807 _('Brand'),
1808 _('Advice')
1809 ],
1810 u'brand': [
1811 _('Brand'),
1812 _('Schedule'),
1813 _('Substance'),
1814 _('Strength'),
1815 _('Started'),
1816 _('Duration / Until'),
1817 _('Episode'),
1818 _('Advice')
1819 ]
1820 }
1821
1822 self.__grouping2order_by_clauses = {
1823 u'episode': u'pk_health_issue nulls first, episode, substance, started',
1824 u'brand': u'brand nulls last, substance, started'
1825 }
1826
1827 self.__init_ui()
1828 self.__register_events()
1829
1830
1831
1833
1834 sel_block_top_left = self.GetSelectionBlockTopLeft()
1835 sel_block_bottom_right = self.GetSelectionBlockBottomRight()
1836 sel_cols = self.GetSelectedCols()
1837 sel_rows = self.GetSelectedRows()
1838
1839 selected_cells = []
1840
1841
1842 selected_cells += self.GetSelectedCells()
1843
1844
1845 selected_cells += list (
1846 (row, col)
1847 for row in sel_rows
1848 for col in xrange(self.GetNumberCols())
1849 )
1850
1851
1852 selected_cells += list (
1853 (row, col)
1854 for row in xrange(self.GetNumberRows())
1855 for col in sel_cols
1856 )
1857
1858
1859 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()):
1860 selected_cells += [
1861 (row, col)
1862 for row in xrange(top_left[0], bottom_right[0] + 1)
1863 for col in xrange(top_left[1], bottom_right[1] + 1)
1864 ]
1865
1866 return set(selected_cells)
1867
1869 rows = {}
1870
1871 for row, col in self.get_selected_cells():
1872 rows[row] = True
1873
1874 return rows.keys()
1875
1878
1880
1881 self.empty_grid()
1882
1883 if self.__patient is None:
1884 return
1885
1886 emr = self.__patient.get_emr()
1887 meds = emr.get_current_substance_intake (
1888 order_by = self.__grouping2order_by_clauses[self.__grouping_mode],
1889 include_unapproved = self.__filter_show_unapproved,
1890 include_inactive = self.__filter_show_inactive
1891 )
1892 if not meds:
1893 return
1894
1895 self.BeginBatch()
1896
1897
1898 labels = self.__grouping2col_labels[self.__grouping_mode]
1899 if self.__filter_show_unapproved:
1900 self.AppendCols(numCols = len(labels) + 1)
1901 else:
1902 self.AppendCols(numCols = len(labels))
1903 for col_idx in range(len(labels)):
1904 self.SetColLabelValue(col_idx, labels[col_idx])
1905 if self.__filter_show_unapproved:
1906 self.SetColLabelValue(len(labels), u'OK?')
1907 self.SetColSize(len(labels), 40)
1908
1909 self.AppendRows(numRows = len(meds))
1910
1911
1912 for row_idx in range(len(meds)):
1913 med = meds[row_idx]
1914 self.__row_data[row_idx] = med
1915
1916 if med['is_currently_active'] is True:
1917 atcs = []
1918 if med['atc_substance'] is not None:
1919 atcs.append(med['atc_substance'])
1920
1921
1922
1923 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (med['substance'],))
1924 if allg not in [None, False]:
1925 attr = self.GetOrCreateCellAttr(row_idx, 0)
1926 if allg['type'] == u'allergy':
1927 attr.SetTextColour('red')
1928 else:
1929 attr.SetTextColour('yellow')
1930 self.SetRowAttr(row_idx, attr)
1931 else:
1932 attr = self.GetOrCreateCellAttr(row_idx, 0)
1933 attr.SetTextColour('grey')
1934 self.SetRowAttr(row_idx, attr)
1935
1936 if self.__grouping_mode == u'episode':
1937 if med['pk_episode'] is None:
1938 self.__prev_cell_0 = None
1939 epi = gmTools.u_diameter
1940 else:
1941 if self.__prev_cell_0 == med['episode']:
1942 epi = u''
1943 else:
1944 self.__prev_cell_0 = med['episode']
1945 epi = gmTools.coalesce(med['episode'], u'')
1946 self.SetCellValue(row_idx, 0, gmTools.wrap(text = epi, width = 40))
1947
1948 self.SetCellValue(row_idx, 1, med['substance'])
1949 self.SetCellValue(row_idx, 2, u'%s%s' % (med['amount'], med['unit']))
1950 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], u''))
1951 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
1952
1953 if med['is_long_term']:
1954 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
1955 else:
1956 if med['discontinued'] is None:
1957 if med['duration'] is None:
1958 self.SetCellValue(row_idx, 5, u'')
1959 else:
1960 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
1961 else:
1962 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d'))
1963
1964 if med['pk_brand'] is None:
1965 brand = u''
1966 else:
1967 if med['fake_brand']:
1968 brand = gmTools.coalesce(med['brand'], u'', _('%s (fake)'))
1969 else:
1970 brand = gmTools.coalesce(med['brand'], u'')
1971 self.SetCellValue(row_idx, 6, gmTools.wrap(text = brand, width = 35))
1972
1973 elif self.__grouping_mode == u'brand':
1974
1975 if med['pk_brand'] is None:
1976 self.__prev_cell_0 = None
1977 brand = gmTools.u_diameter
1978 else:
1979 if self.__prev_cell_0 == med['brand']:
1980 brand = u''
1981 else:
1982 self.__prev_cell_0 = med['brand']
1983 if med['fake_brand']:
1984 brand = gmTools.coalesce(med['brand'], u'', _('%s (fake)'))
1985 else:
1986 brand = gmTools.coalesce(med['brand'], u'')
1987 self.SetCellValue(row_idx, 0, gmTools.wrap(text = brand, width = 35))
1988
1989 self.SetCellValue(row_idx, 1, gmTools.coalesce(med['schedule'], u''))
1990 self.SetCellValue(row_idx, 2, med['substance'])
1991 self.SetCellValue(row_idx, 3, u'%s%s' % (med['amount'], med['unit']))
1992 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
1993
1994 if med['is_long_term']:
1995 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
1996 else:
1997 if med['discontinued'] is None:
1998 if med['duration'] is None:
1999 self.SetCellValue(row_idx, 5, u'')
2000 else:
2001 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
2002 else:
2003 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d'))
2004
2005 if med['pk_episode'] is None:
2006 epi = u''
2007 else:
2008 epi = gmTools.coalesce(med['episode'], u'')
2009 self.SetCellValue(row_idx, 6, gmTools.wrap(text = epi, width = 40))
2010
2011 else:
2012 raise ValueError('unknown grouping mode [%s]' % self.__grouping_mode)
2013
2014 if med['notes'] is not None:
2015 self.SetCellValue(row_idx, 7, gmTools.wrap(text = med['notes'], width = 50))
2016
2017 if self.__filter_show_unapproved:
2018 self.SetCellValue (
2019 row_idx,
2020 len(labels),
2021 gmTools.bool2subst(med['intake_is_approved_of'], gmTools.u_checkmark_thin, u'', u'?')
2022 )
2023
2024
2025
2026 self.AutoSize()
2027 self.EndBatch()
2028
2030 self.BeginBatch()
2031 self.ClearGrid()
2032
2033
2034 if self.GetNumberRows() > 0:
2035 self.DeleteRows(pos = 0, numRows = self.GetNumberRows())
2036 if self.GetNumberCols() > 0:
2037 self.DeleteCols(pos = 0, numCols = self.GetNumberCols())
2038 self.EndBatch()
2039 self.__row_data = {}
2040 self.__prev_cell_0 = None
2041
2043
2044 if len(self.__row_data) == 0:
2045 return
2046
2047 sel_rows = self.get_selected_rows()
2048 if len(sel_rows) != 1:
2049 return
2050
2051 drug_db = get_drug_database()
2052 if drug_db is None:
2053 return
2054
2055 intake = self.get_selected_data()[0]
2056 if intake['brand'] is None:
2057 drug_db.show_info_on_substance(substance_intake = intake)
2058 else:
2059 drug_db.show_info_on_drug(substance_intake = intake)
2060
2068
2080
2092
2106
2109
2123
2137
2153
2157
2261
2262
2263
2265 self.CreateGrid(0, 1)
2266 self.EnableEditing(0)
2267 self.EnableDragGridSize(1)
2268 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows)
2269
2270 self.SetColLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTER)
2271
2272 self.SetRowLabelSize(0)
2273 self.SetRowLabelAlignment(horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
2274
2275
2276
2278 return self.__patient
2279
2283
2284 patient = property(_get_patient, _set_patient)
2285
2287 return self.__grouping_mode
2288
2292
2293 grouping_mode = property(_get_grouping_mode, _set_grouping_mode)
2294
2296 return self.__filter_show_unapproved
2297
2301
2302 filter_show_unapproved = property(_get_filter_show_unapproved, _set_filter_show_unapproved)
2303
2305 return self.__filter_show_inactive
2306
2310
2311 filter_show_inactive = property(_get_filter_show_inactive, _set_filter_show_inactive)
2312
2313
2314
2316
2317 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells)
2318
2319
2320
2321
2322 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
2323
2325 """Calculate where the mouse is and set the tooltip dynamically."""
2326
2327
2328
2329 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343 row, col = self.XYToCell(x, y)
2344
2345 if row == self.__prev_tooltip_row:
2346 return
2347
2348 self.__prev_tooltip_row = row
2349
2350 try:
2351 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row))
2352 except KeyError:
2353 pass
2354
2359
2360 from Gnumed.wxGladeWidgets import wxgCurrentSubstancesPnl
2361
2362 -class cCurrentSubstancesPnl(wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl, gmRegetMixin.cRegetOnPaintMixin):
2363
2364 """Panel holding a grid with current substances. Used as notebook page."""
2365
2372
2373
2374
2383
2384
2385
2387 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
2388 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._schedule_data_reget)
2389 gmDispatcher.connect(signal = u'substance_intake_mod_db', receiver = self._schedule_data_reget)
2390
2391
2392
2394 wx.CallAfter(self.__on_pre_patient_selection)
2395
2397 self._grid_substances.patient = None
2398
2401
2404
2407
2410
2413
2416
2419
2422
2425
2428
2431
2434
2437
2440
2441
2442
2443 if __name__ == '__main__':
2444
2445 if len(sys.argv) < 2:
2446 sys.exit()
2447
2448 if sys.argv[1] != 'test':
2449 sys.exit()
2450
2451 from Gnumed.pycommon import gmI18N
2452
2453 gmI18N.activate_locale()
2454 gmI18N.install_domain(domain = 'gnumed')
2455
2456
2457 app = wx.PyWidgetTester(size = (600, 600))
2458
2459 app.SetWidget(cSubstancePhraseWheel, -1)
2460 app.MainLoop()
2461
2462
2463