1 """GNUmed medication/substances handling widgets.
2 """
3
4 __version__ = "$Revision: 1.33 $"
5 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
6
7 import logging, sys, os.path, webbrowser, decimal
8
9
10 import wx, wx.grid
11
12
13 if __name__ == '__main__':
14 sys.path.insert(0, '../../')
15 from Gnumed.pycommon import gmDispatcher, gmCfg, gmShellAPI, gmTools, gmDateTime
16 from Gnumed.pycommon import gmMatchProvider, gmI18N, gmPrinting, gmCfg2
17 from Gnumed.business import gmPerson, gmATC, gmSurgery, gmMedication, gmForms
18 from Gnumed.wxpython import gmGuiHelpers, gmRegetMixin, gmAuthWidgets, gmEditArea, gmMacro
19 from Gnumed.wxpython import gmCfgWidgets, gmListWidgets, gmPhraseWheel, gmFormWidgets
20 from Gnumed.wxpython import gmAllergyWidgets
21
22
23 _log = logging.getLogger('gm.ui')
24 _log.info(__version__)
25
26
27
28
46
82
89
90
92
93 dbcfg = gmCfg.cCfgSQL()
94
95 ifap_cmd = dbcfg.get2 (
96 option = 'external.ifap-win.shell_command',
97 workplace = gmSurgery.gmCurrentPractice().active_workplace,
98 bias = 'workplace',
99 default = 'wine "C:\Ifapwin\WIAMDB.EXE"'
100 )
101 found, binary = gmShellAPI.detect_external_binary(ifap_cmd)
102 if not found:
103 gmDispatcher.send('statustext', msg = _('Cannot call IFAP via [%s].') % ifap_cmd)
104 return False
105 ifap_cmd = binary
106
107 if import_drugs:
108 transfer_file = os.path.expanduser(dbcfg.get2 (
109 option = 'external.ifap-win.transfer_file',
110 workplace = gmSurgery.gmCurrentPractice().active_workplace,
111 bias = 'workplace',
112 default = '~/.wine/drive_c/Ifapwin/ifap2gnumed.csv'
113 ))
114
115 try:
116 f = open(transfer_file, 'w+b').close()
117 except IOError:
118 _log.exception('Cannot create IFAP <-> GNUmed transfer file [%s]', transfer_file)
119 gmDispatcher.send('statustext', msg = _('Cannot create IFAP <-> GNUmed transfer file [%s].') % transfer_file)
120 return False
121
122 wx.BeginBusyCursor()
123 gmShellAPI.run_command_in_shell(command = ifap_cmd, blocking = import_drugs)
124 wx.EndBusyCursor()
125
126 if import_drugs:
127
128
129 try:
130 csv_file = open(transfer_file, 'rb')
131 except:
132 _log.exception('cannot access [%s]', fname)
133 csv_file = None
134
135 if csv_file is not None:
136 import csv
137 csv_lines = csv.DictReader (
138 csv_file,
139 fieldnames = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split(),
140 delimiter = ';'
141 )
142 pat = gmPerson.gmCurrentPatient()
143 emr = pat.get_emr()
144
145 epi = emr.add_episode(episode_name = _('Current medication'))
146 for line in csv_lines:
147 narr = u'%sx %s %s %s (\u2258 %s %s) von %s (%s)' % (
148 line['Packungszahl'].strip(),
149 line['Handelsname'].strip(),
150 line['Form'].strip(),
151 line[u'Packungsgr\xf6\xdfe'].strip(),
152 line['Abpackungsmenge'].strip(),
153 line['Einheit'].strip(),
154 line['Hersteller'].strip(),
155 line['PZN'].strip()
156 )
157 emr.add_clin_narrative(note = narr, soap_cat = 's', episode = epi)
158 csv_file.close()
159
160 return True
161
162
163
164
165
167
168 if parent is None:
169 parent = wx.GetApp().GetTopWindow()
170
171 def refresh(lctrl):
172 atcs = gmATC.get_reference_atcs()
173
174 items = [ [
175 a['atc'],
176 a['term'],
177 u'%s' % gmTools.coalesce(a['ddd'], u''),
178 gmTools.coalesce(a['unit'], u''),
179 gmTools.coalesce(a['administrative_route'], u''),
180 gmTools.coalesce(a['comment'], u''),
181 a['version'],
182 a['lang']
183 ] for a in atcs ]
184 lctrl.set_string_items(items)
185 lctrl.set_data(atcs)
186
187 gmListWidgets.get_choices_from_list (
188 parent = parent,
189 msg = _('\nThe ATC codes as known to GNUmed.\n'),
190 caption = _('Showing ATC codes.'),
191 columns = [ u'ATC', _('Term'), u'DDD', _('Unit'), _(u'Route'), _('Comment'), _('Version'), _('Language') ],
192 single_selection = True,
193 refresh_callback = refresh
194 )
195
196
197
199
200 dlg = wx.FileDialog (
201 parent = None,
202 message = _('Choose an ATC import config file'),
203 defaultDir = os.path.expanduser(os.path.join('~', 'gnumed')),
204 defaultFile = '',
205 wildcard = "%s (*.conf)|*.conf|%s (*)|*" % (_('config files'), _('all files')),
206 style = wx.OPEN | wx.HIDE_READONLY | wx.FILE_MUST_EXIST
207 )
208
209 result = dlg.ShowModal()
210 if result == wx.ID_CANCEL:
211 return
212
213 cfg_file = dlg.GetPath()
214 dlg.Destroy()
215
216 conn = gmAuthWidgets.get_dbowner_connection(procedure = _('importing ATC reference data'))
217 if conn is None:
218 return False
219
220 wx.BeginBusyCursor()
221
222 if gmATC.atc_import(cfg_fname = cfg_file, conn = conn):
223 gmDispatcher.send(signal = 'statustext', msg = _('Successfully imported ATC reference data.'))
224 else:
225 gmDispatcher.send(signal = 'statustext', msg = _('Importing ATC reference data failed.'), beep = True)
226
227 wx.EndBusyCursor()
228 return True
229
230
231
233
235
236 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
237
238 query = u"""
239
240 SELECT DISTINCT ON (label)
241 atc_code,
242 label
243 FROM (
244
245 SELECT
246 code as atc_code,
247 (code || ': ' || term || coalesce(' (' || ddd || unit || ')', ''))
248 AS label
249 FROM ref.atc
250 WHERE
251 term %(fragment_condition)s
252 OR
253 code %(fragment_condition)s
254
255 UNION ALL
256
257 SELECT
258 atc_code,
259 (atc_code || ': ' || description)
260 AS label
261 FROM ref.consumable_substance
262 WHERE
263 description %(fragment_condition)s
264 OR
265 atc_code %(fragment_condition)s
266
267 UNION ALL
268
269 SELECT
270 atc_code,
271 (atc_code || ': ' || description || ' (' || preparation || ')')
272 AS label
273 FROM ref.branded_drug
274 WHERE
275 description %(fragment_condition)s
276 OR
277 atc_code %(fragment_condition)s
278
279 -- it would be nice to be able to include clin.vacc_indication but that's hard to do in SQL
280
281 ) AS candidates
282 WHERE atc_code IS NOT NULL
283 ORDER BY label
284 LIMIT 50"""
285
286 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
287 mp.setThresholds(1, 2, 4)
288
289 self.SetToolTipString(_('Select an ATC (Anatomical-Therapeutic-Chemical) code.'))
290 self.matcher = mp
291 self.selection_only = True
292
293
294
295
297
298 if parent is None:
299 parent = wx.GetApp().GetTopWindow()
300
301 def add_from_db(substance):
302 drug_db = get_drug_database(parent = parent)
303 if drug_db is None:
304 return False
305 drug_db.import_drugs()
306 return True
307
308 def edit(substance=None):
309 return edit_consumable_substance(parent = parent, substance = substance, single_entry = (substance is not None))
310
311 def delete(substance):
312 if substance.is_in_use_by_patients:
313 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete this substance. It is in use.'), beep = True)
314 return False
315
316 return gmMedication.delete_consumable_substance(substance = substance['pk'])
317
318 def refresh(lctrl):
319 substs = gmMedication.get_consumable_substances(order_by = 'description')
320 items = [ [
321 s['description'],
322 s['amount'],
323 s['unit'],
324 gmTools.coalesce(s['atc_code'], u''),
325 s['pk']
326 ] for s in substs ]
327 lctrl.set_string_items(items)
328 lctrl.set_data(substs)
329
330 msg = _('\nThese are the consumable substances registered with GNUmed.\n')
331
332 gmListWidgets.get_choices_from_list (
333 parent = parent,
334 msg = msg,
335 caption = _('Showing consumable substances.'),
336 columns = [_('Substance'), _('Amount'), _('Unit'), 'ATC', u'#'],
337 single_selection = True,
338 new_callback = edit,
339 edit_callback = edit,
340 delete_callback = delete,
341 refresh_callback = refresh,
342 left_extra_button = (_('Import'), _('Import consumable substances from a drug database.'), add_from_db)
343 )
344
345
347
348 if substance is not None:
349 if substance.is_in_use_by_patients:
350 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit this substance. It is in use.'), beep = True)
351 return False
352
353 ea = cConsumableSubstanceEAPnl(parent = parent, id = -1)
354 ea.data = substance
355 ea.mode = gmTools.coalesce(substance, 'new', 'edit')
356 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
357 dlg.SetTitle(gmTools.coalesce(substance, _('Adding new consumable substance'), _('Editing consumable substance')))
358 if dlg.ShowModal() == wx.ID_OK:
359 dlg.Destroy()
360 return True
361 dlg.Destroy()
362 return False
363
364
365 from Gnumed.wxGladeWidgets import wxgConsumableSubstanceEAPnl
366
368
386
387
388
389
390
391
392
393
395
396 validity = True
397
398 if self._TCTRL_substance.GetValue().strip() == u'':
399 validity = False
400 self.display_tctrl_as_valid(tctrl = self._TCTRL_substance, valid = False)
401 self._TCTRL_substance.SetFocus()
402 else:
403 self.display_tctrl_as_valid(tctrl = self._TCTRL_substance, valid = True)
404
405 try:
406 decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.'))
407 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
408 except (TypeError, decimal.InvalidOperation):
409 validity = False
410 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
411 self._TCTRL_amount.SetFocus()
412
413 if self._PRW_unit.GetValue().strip() == u'':
414 validity = False
415 self._PRW_unit.display_as_valid(valid = False)
416 self._TCTRL_substance.SetFocus()
417 else:
418 self._PRW_unit.display_as_valid(valid = True)
419
420 if validity is False:
421 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save consumable substance. Missing essential input.'))
422
423 return validity
424
426 subst = gmMedication.create_consumable_substance (
427 substance = self._TCTRL_substance.GetValue().strip(),
428 atc = self._PRW_atc.GetData(),
429 amount = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.')),
430 unit = gmTools.coalesce(self._PRW_unit.GetData(), self._PRW_unit.GetValue().strip(), function_initial = ('strip', None))
431 )
432 success, data = subst.save()
433 if not success:
434 err, msg = data
435 _log.error(err)
436 _log.error(msg)
437 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save consumable substance. %s') % msg, beep = True)
438 return False
439
440 self.data = subst
441 return True
442
444 self.data['description'] = self._TCTRL_substance.GetValue().strip()
445 self.data['atc_code'] = self._PRW_atc.GetData()
446 self.data['amount'] = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.'))
447 self.data['unit'] = gmTools.coalesce(self._PRW_unit.GetData(), self._PRW_unit.GetValue().strip(), function_initial = ('strip', None))
448 success, data = self.data.save()
449
450 if not success:
451 err, msg = data
452 _log.error(err)
453 _log.error(msg)
454 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save consumable substance. %s') % msg, beep = True)
455 return False
456
457 return True
458
460 self._TCTRL_substance.SetValue(u'')
461 self._TCTRL_amount.SetValue(u'')
462 self._PRW_unit.SetText(u'', None)
463 self._PRW_atc.SetText(u'', None)
464
465 self._TCTRL_substance.SetFocus()
466
474
476 self._refresh_as_new()
477
478
479
480
490
491 def delete(component):
492 if component.is_in_use_by_patients:
493 gmDispatcher.send(signal = 'statustext', msg = _('Cannot remove this component from the drug. It is in use.'), beep = True)
494 return False
495
496 return component.containing_drug.remove_component(substance = component['pk_component'])
497
498 def refresh(lctrl):
499 comps = gmMedication.get_drug_components()
500 items = [ [
501 u'%s%s' % (c['brand'], gmTools.coalesce(c['atc_brand'], u'', u' [%s]')),
502 u'%s%s' % (c['substance'], gmTools.coalesce(c['atc_substance'], u'', u' [%s]')),
503 u'%s%s' % (c['amount'], c['unit']),
504 c['preparation'],
505 gmTools.coalesce(c['external_code_brand'], u'', u'%%s [%s]' % c['external_code_type_brand']),
506 c['pk_component']
507 ] for c in comps ]
508 lctrl.set_string_items(items)
509 lctrl.set_data(comps)
510
511 msg = _('\nThese are the components in the drug brands known to GNUmed.\n')
512
513 gmListWidgets.get_choices_from_list (
514 parent = parent,
515 msg = msg,
516 caption = _('Showing drug brand components.'),
517 columns = [_('Brand'), _('Substance'), _('Strength'), _('Preparation'), _('Code'), u'#'],
518 single_selection = True,
519
520 edit_callback = edit,
521 delete_callback = delete,
522 refresh_callback = refresh
523 )
524
525
537
538
539 from Gnumed.wxGladeWidgets import wxgDrugComponentEAPnl
540
541 -class cDrugComponentEAPnl(wxgDrugComponentEAPnl.wxgDrugComponentEAPnl, gmEditArea.cGenericEditAreaMixin):
542
560
561
562
563
564
565
566
567
569 if self.data is not None:
570 if self.data['is_in_use']:
571 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit drug component. It is in use.'), beep = True)
572 return False
573
574 validity = True
575
576 if self._PRW_substance.GetData() is None:
577 validity = False
578 self._PRW_substance.display_as_valid(False)
579 else:
580 self._PRW_substance.display_as_valid(True)
581
582 val = self._TCTRL_amount.GetValue().strip().replace(',', u'.', 1)
583 try:
584 decimal.Decimal(val)
585 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
586 except:
587 validity = False
588 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
589
590 if self._PRW_unit.GetValue().strip() == u'':
591 validity = False
592 self._PRW_unit.display_as_valid(False)
593 else:
594 self._PRW_unit.display_as_valid(True)
595
596 if validity is False:
597 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save drug component. Invalid or missing essential input.'))
598
599 return validity
600
602
603 data = 1
604 data[''] = 1
605 data[''] = 1
606
607
608
609
610
611
612 return False
613 return True
614
616 self.data['pk_consumable_substance'] = self._PRW_substance.GetData(can_create = True)
617 self.data['amount'] = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', u'.', 1))
618 self.data['unit'] = self._PRW_unit.GetValue().strip()
619 return self.data.save()
620
630
632 self._TCTRL_brand.SetValue(u'%s (%s)' % (self.data['brand'], self.data['preparation']))
633 self._TCTRL_components.SetValue(u' / '.join(self.data.containing_drug['components']))
634 details = []
635 if self.data['atc_brand'] is not None:
636 details.append(u'ATC: %s' % self.data['atc_brand'])
637 if self.data['external_code_brand'] is not None:
638 details.append(u'%s: %s' % (self.data['external_code_type_brand'], self.data['external_code_brand']))
639 self._TCTRL_codes.SetValue(u'; '.join(details))
640
641 self._PRW_substance.SetText(self.data['substance'], self.data['pk_consumable_substance'])
642 self._TCTRL_amount.SetValue(u'%s' % self.data['amount'])
643 self._PRW_unit.SetText(self.data['unit'], self.data['unit'])
644
645 self._PRW_substance.SetFocus()
646
648
649
650
651 self._PRW_substance.SetText(u'', None)
652 self._TCTRL_amount.SetValue(u'')
653 self._PRW_unit.SetText(u'', None)
654
655 self._PRW_substance.SetFocus()
656
657
659
661
662 query = u"""
663 SELECT DISTINCT ON (component)
664 pk_component,
665 (substance || ' ' || amount || unit || ' ' || preparation || ' (' || brand || ')')
666 AS component
667 FROM ref.v_drug_components
668 WHERE
669 substance %(fragment_condition)s
670 OR
671 brand %(fragment_condition)s
672 ORDER BY component
673 LIMIT 50
674 """
675 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
676 mp.setThresholds(2, 3, 4)
677 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
678 self.SetToolTipString(_('A drug component.'))
679 self.matcher = mp
680 self.selection_only = False
681
682
683
685
687
688 query = u"""
689 (
690 SELECT DISTINCT ON (preparation)
691 preparation as prep, preparation
692 FROM ref.branded_drug
693 WHERE preparation %(fragment_condition)s
694 ) UNION (
695 SELECT DISTINCT ON (preparation)
696 preparation as prep, preparation
697 FROM clin.substance_intake
698 WHERE preparation %(fragment_condition)s
699 )
700 ORDER BY prep
701 limit 30"""
702
703 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
704 mp.setThresholds(1, 2, 4)
705 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
706 self.SetToolTipString(_('The preparation (form) of the substance or brand.'))
707 self.matcher = mp
708 self.selection_only = False
709
711
713
714 query = u"""
715 (
716 SELECT
717 pk::text,
718 (description || ' ' || amount || unit) as subst
719 --(description || coalesce(' [' || atc_code || ']', '')) as subst
720 FROM ref.consumable_substance
721 WHERE description %(fragment_condition)s
722
723 ) UNION (
724
725 SELECT
726 term,
727 term as subst
728 --NULL,
729 --(term || ' [' || atc || ']') as subst
730 FROM ref.v_atc
731 WHERE
732 is_group_code IS FALSE
733 AND
734 term %(fragment_condition)s
735 )
736 ORDER BY subst
737 LIMIT 50"""
738
739 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
740 mp.setThresholds(1, 2, 4)
741 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
742 self.SetToolTipString(_('The preparation (form) of the substance or brand in question.'))
743 self.matcher = mp
744 self.selection_only = False
745
746 - def GetData(self, can_create=False, as_instance=False):
747
748 if self.data is not None:
749 try:
750 int(self.data)
751 except ValueError:
752 self.data = None
753
754 return super(cSubstancePhraseWheel, self).GetData(can_create = can_create, as_instance = as_instance)
755
756
757
758
760
761 if brand is not None:
762 if brand['is_in_use']:
763 gmGuiHelpers.gm_show_info (
764 aTitle = _('Managing components of a drug'),
765 aMessage = _(
766 'Cannot manage the components of the branded drug product\n'
767 '\n'
768 ' "%s" (%s)\n'
769 '\n'
770 'because it is currently taken by patients.\n'
771 ) % (brand['brand'], brand['preparation'])
772 )
773 return False
774
775 if parent is None:
776 parent = wx.GetApp().GetTopWindow()
777
778 if brand is None:
779 msg = _('Pick the substances which are components of this drug.')
780 right_col = _('Components of drug')
781 comp_substs = []
782 else:
783 right_col = u'%s (%s)' % (brand['brand'], brand['preparation'])
784 msg = _(
785 'Adjust the components of "%s"\n'
786 '\n'
787 'The drug must contain at least one component. Any given\n'
788 'substance can only be included once per drug.'
789 ) % right_col
790 comp_substs = [ c.substance for c in brand.components ]
791
792 substs = gmMedication.get_consumable_substances(order_by = 'description')
793 choices = [ u'%s %s%s' % (s['description'], s['amount'], s['unit']) for s in substs ]
794 picks = [ u'%s %s%s' % (c['description'], c['amount'], c['unit']) for c in comp_substs ]
795
796 picker = gmListWidgets.cItemPickerDlg (
797 parent,
798 -1,
799 title = _('Managing components of a drug ...'),
800 msg = msg
801 )
802 picker.set_columns(['Substances'], [right_col])
803 picker.set_choices(choices = choices, data = substs)
804 picker.set_picks(picks = picks, data = comp_substs)
805
806 btn_pressed = picker.ShowModal()
807 substs = picker.get_picks()
808 picker.Destroy()
809
810 if btn_pressed != wx.ID_OK:
811 return (False, None)
812
813 if brand is not None:
814 brand.set_substances_as_components(substances = substs)
815
816 return (True, substs)
817
819
820 if parent is None:
821 parent = wx.GetApp().GetTopWindow()
822
823 def add_from_db(brand):
824 drug_db = get_drug_database(parent = parent)
825 if drug_db is None:
826 return False
827 drug_db.import_drugs()
828 return True
829
830 def get_tooltip(brand=None):
831 tt = u'%s %s\n' % (brand['brand'], brand['preparation'])
832 tt += u'\n'
833 tt += u'%s%s%s\n' % (
834 gmTools.bool2subst(brand.is_vaccine, u'%s, ' % _('Vaccine'), u''),
835 u'%s, ' % gmTools.bool2subst(brand['is_in_use'], _('in use'), _('not in use')),
836 gmTools.bool2subst(brand['is_fake_brand'], _('fake'), u'')
837 )
838 tt += gmTools.coalesce(brand['atc'], u'', _('ATC: %s\n'))
839 tt += gmTools.coalesce(brand['external_code'], u'', u'%s: %%s\n' % brand['external_code_type'])
840 if brand['components'] is not None:
841 tt += u'- %s' % u'\n- '.join(brand['components'])
842 return tt
843
844 def edit(brand=None):
845 if brand.is_vaccine:
846 gmGuiHelpers.gm_show_info (
847 aTitle = _('Editing medication'),
848 aMessage = _(
849 'Cannot edit the medication\n'
850 '\n'
851 ' "%s" (%s)\n'
852 '\n'
853 'because it is a vaccine. Please edit it\n'
854 'from the vaccine management section !\n'
855 ) % (brand['brand'], brand['preparation'])
856 )
857 return False
858
859 return edit_branded_drug(parent = parent, branded_drug = brand, single_entry = True)
860
861 def delete(brand):
862 if brand.is_vaccine:
863 gmGuiHelpers.gm_show_info (
864 aTitle = _('Deleting medication'),
865 aMessage = _(
866 'Cannot delete the medication\n'
867 '\n'
868 ' "%s" (%s)\n'
869 '\n'
870 'because it is a vaccine. Please delete it\n'
871 'from the vaccine management section !\n'
872 ) % (brand['brand'], brand['preparation'])
873 )
874 return False
875 gmMedication.delete_branded_drug(brand = brand['pk_brand'])
876 return True
877
878 def new():
879 return edit_branded_drug(parent = parent, branded_drug = None, single_entry = False)
880
881 def refresh(lctrl):
882 drugs = gmMedication.get_branded_drugs()
883 items = [ [
884 u'%s%s' % (
885 d['brand'],
886 gmTools.bool2subst(d['is_fake_brand'], ' (%s)' % _('fake'), u'')
887 ),
888 d['preparation'],
889 gmTools.coalesce(d['atc'], u''),
890 gmTools.coalesce(d['components'], u''),
891 gmTools.coalesce(d['external_code'], u'', u'%%s [%s]' % d['external_code_type']),
892 d['pk_brand']
893 ] for d in drugs ]
894 lctrl.set_string_items(items)
895 lctrl.set_data(drugs)
896
897 msg = _('\nThese are the drug brands known to GNUmed.\n')
898
899 gmListWidgets.get_choices_from_list (
900 parent = parent,
901 msg = msg,
902 caption = _('Showing branded drugs.'),
903 columns = [_('Name'), _('Preparation'), _('ATC'), _('Components'), _('Code'), u'#'],
904 single_selection = True,
905 refresh_callback = refresh,
906 new_callback = new,
907 edit_callback = edit,
908 delete_callback = delete,
909 list_tooltip_callback = get_tooltip,
910 left_extra_button = (_('Import'), _('Import substances and brands from a drug database.'), add_from_db)
911
912
913 )
914
915
917 if branded_drug is not None:
918 if branded_drug['is_in_use']:
919 gmGuiHelpers.gm_show_info (
920 aTitle = _('Editing drug'),
921 aMessage = _(
922 'Cannot edit the branded drug product\n'
923 '\n'
924 ' "%s" (%s)\n'
925 '\n'
926 'because it is currently taken by patients.\n'
927 ) % (branded_drug['brand'], branded_drug['preparation'])
928 )
929 return False
930
931 ea = cBrandedDrugEAPnl(parent = parent, id = -1)
932 ea.data = branded_drug
933 ea.mode = gmTools.coalesce(branded_drug, 'new', 'edit')
934 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
935 dlg.SetTitle(gmTools.coalesce(branded_drug, _('Adding new drug brand'), _('Editing drug brand')))
936 if dlg.ShowModal() == wx.ID_OK:
937 dlg.Destroy()
938 return True
939 dlg.Destroy()
940 return False
941
942
943 from Gnumed.wxGladeWidgets import wxgBrandedDrugEAPnl
944
945 -class cBrandedDrugEAPnl(wxgBrandedDrugEAPnl.wxgBrandedDrugEAPnl, gmEditArea.cGenericEditAreaMixin):
946
963
964
965
966
967
968
969
970
972
973 if self.data is not None:
974 if self.data['is_in_use']:
975 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit drug brand. It is in use.'), beep = True)
976 return False
977
978 validity = True
979
980 if self._PRW_brand.GetValue().strip() == u'':
981 validity = False
982 self._PRW_brand.display_as_valid(False)
983 else:
984 self._PRW_brand.display_as_valid(True)
985
986 if self._PRW_preparation.GetValue().strip() == u'':
987 validity = False
988 self._PRW_preparation.display_as_valid(False)
989 else:
990 self._PRW_preparation.display_as_valid(True)
991
992 if validity is False:
993 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save branded drug. Invalid or missing essential input.'))
994
995 return validity
996
998
999 drug = gmMedication.create_branded_drug (
1000 brand_name = self._PRW_brand.GetValue().strip(),
1001 preparation = gmTools.coalesce (
1002 self._PRW_preparation.GetData(),
1003 self._PRW_preparation.GetValue()
1004 ).strip(),
1005 return_existing = True
1006 )
1007 drug['is_fake_brand'] = self._CHBOX_is_fake.GetValue()
1008 drug['atc'] = self._PRW_atc.GetData()
1009 code = self._TCTRL_external_code.GetValue().strip()
1010 if code != u'':
1011 drug['external_code'] = code
1012 drug['external_code_type'] = self._PRW_external_code_type.GetData().strip()
1013
1014 drug.save()
1015
1016 if len(self.__component_substances) > 0:
1017 drug.set_substances_as_components(substances = self.__component_substances)
1018
1019 self.data = drug
1020
1021 return True
1022
1024 self.data['brand'] = self._PRW_brand.GetValue().strip()
1025 self.data['preparation'] = gmTools.coalesce (
1026 self._PRW_preparation.GetData(),
1027 self._PRW_preparation.GetValue()
1028 ).strip()
1029 self.data['is_fake_brand'] = self._CHBOX_is_fake.GetValue()
1030 self.data['atc'] = self._PRW_atc.GetData()
1031 code = self._TCTRL_external_code.GetValue().strip()
1032 if code != u'':
1033 self.data['external_code'] = code
1034 self.data['external_code_type'] = self._PRW_external_code_type.GetData().strip()
1035 success, data = self.data.save()
1036 if not success:
1037 err, msg = data
1038 _log.error('problem saving')
1039 _log.error('%s', err)
1040 _log.error('%s', msg)
1041 return (success is True)
1042
1044 self._PRW_brand.SetText(u'', None)
1045 self._PRW_preparation.SetText(u'', None)
1046 self._CHBOX_is_fake.SetValue(False)
1047 self._TCTRL_components.SetValue(u'')
1048 self._PRW_atc.SetText(u'', None)
1049 self._TCTRL_external_code.SetValue(u'')
1050 self._PRW_external_code_type.SetText(u'', None)
1051
1052 self._PRW_brand.SetFocus()
1053
1054 self.__component_substances = []
1055
1057 self._refresh_as_new()
1058
1075
1076
1077
1087
1089
1091
1092 query = u"""
1093 SELECT
1094 pk,
1095 (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', ''))
1096 AS brand
1097 FROM ref.branded_drug
1098 WHERE description %(fragment_condition)s
1099 ORDER BY brand
1100 LIMIT 50"""
1101
1102 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
1103 mp.setThresholds(2, 3, 4)
1104 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1105 self.SetToolTipString(_('The brand name of the drug.'))
1106 self.matcher = mp
1107 self.selection_only = False
1108
1109
1110
1111
1113
1115
1116 query = u"""
1117 SELECT DISTINCT ON (sched)
1118 schedule as sched,
1119 schedule
1120 FROM clin.substance_intake
1121 WHERE schedule %(fragment_condition)s
1122 ORDER BY sched
1123 LIMIT 50"""
1124
1125 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
1126 mp.setThresholds(1, 2, 4)
1127 mp.word_separators = '[ \t=+&:@]+'
1128 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1129 self.SetToolTipString(_('The schedule for taking this substance.'))
1130 self.matcher = mp
1131 self.selection_only = False
1132
1133
1134 from Gnumed.wxGladeWidgets import wxgCurrentMedicationEAPnl
1135
1136 -class cCurrentMedicationEAPnl(wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl, gmEditArea.cGenericEditAreaMixin):
1137
1155
1157
1158 self._PRW_component.add_callback_on_lose_focus(callback = self._on_leave_component)
1159 self._PRW_component.selection_only = True
1160
1161 self._PRW_substance.add_callback_on_lose_focus(callback = self._on_leave_substance)
1162 self._PRW_substance.selection_only = True
1163
1165 emr = gmPerson.gmCurrentPatient().get_emr()
1166
1167 state = emr.allergy_state
1168 if state['last_confirmed'] is None:
1169 confirmed = _('never')
1170 else:
1171 confirmed = state['last_confirmed'].strftime('%Y %B %d').decode(gmI18N.get_encoding())
1172 msg = _(u'%s, last confirmed %s\n') % (state.state_string, confirmed)
1173 msg += gmTools.coalesce(state['comment'], u'', _('Comment (%s): %%s\n') % state['modified_by'])
1174 msg += u'\n'
1175
1176 for allergy in emr.get_allergies():
1177 msg += u'%s (%s, %s): %s\n' % (
1178 allergy['descriptor'],
1179 allergy['l10n_type'],
1180 gmTools.bool2subst(allergy['definite'], _('definite'), _('suspected'), u'?'),
1181 gmTools.coalesce(allergy['reaction'], _('reaction not recorded'))
1182 )
1183
1184 self._LBL_allergies.SetLabel(msg)
1185
1186
1187
1189
1190 validity = True
1191
1192 has_component = (self._PRW_component.GetData() is not None)
1193 has_substance = (self._PRW_substance.GetValue().strip() == u'')
1194
1195
1196 if not (has_component or has_substance):
1197 self._PRW_substance.display_as_valid(False)
1198 self._PRW_component.display_as_valid(False)
1199 validity = False
1200 else:
1201 self._PRW_substance.display_as_valid(True)
1202 self._PRW_component.display_as_valid(True)
1203
1204
1205 if not has_component:
1206 if self._PRW_preparation.GetValue().strip() == u'':
1207 self._PRW_preparation.display_as_valid(False)
1208 validity = False
1209 else:
1210 self._PRW_preparation.display_as_valid(True)
1211
1212
1213 if self._CHBOX_approved.IsChecked():
1214 if self._PRW_episode.GetValue().strip() == u'':
1215 self._PRW_episode.display_as_valid(False)
1216 validity = False
1217 else:
1218 self._PRW_episode.display_as_valid(True)
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234 end = self._DP_discontinued.GetValue(as_pydt = True, invalid_as_none = True)
1235 if end is not None:
1236 start = self._DP_started.GetValue(as_pydt = True)
1237 if start > end:
1238 self._DP_started.display_as_valid(False)
1239 self._DP_discontinued.display_as_valid(False)
1240 validity = False
1241 else:
1242 self._DP_started.display_as_valid(True)
1243 self._DP_discontinued.display_as_valid(True)
1244
1245 if validity is False:
1246 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save substance intake. Invalid or missing essential input.'))
1247
1248 return validity
1249
1251
1252 emr = gmPerson.gmCurrentPatient().get_emr()
1253 epi = self._PRW_episode.GetData(can_create = True)
1254
1255 intakes = []
1256 if self._PRW_substance.GetData() is None:
1257
1258
1259 comp = gmMedication.cDrugComponent(aPK_obj = self._PRW_component.GetData())
1260 brand = comp.containing_drug
1261 for pk in brand['pk_components']:
1262 intakes.append(emr.add_substance_intake (
1263 pk_component = pk,
1264 episode = epi
1265 ))
1266 else:
1267
1268 intakes.append(emr.add_substance_intake (
1269 pk_substance = self._PRW_substance.GetData(),
1270 episode = epi,
1271 preparation = self._PRW_preparation.GetValue().strip()
1272 ))
1273
1274
1275 entered_intake = None
1276 for intake in intakes:
1277 intake['started'] = self._DP_started.GetValue(as_pydt = True, invalid_as_none = True)
1278 intake['discontinued'] = self._DP_discontinued.GetValue(as_pydt = True, invalid_as_none = True)
1279 if intake['discontinued'] is None:
1280 intake['discontinue_reason'] = None
1281 else:
1282 intake['discontinue_reason'] = self._PRW_discontinue_reason().GetValue().strip()
1283 intake['schedule'] = self._PRW_schedule.GetValue().strip()
1284 intake['aim'] = self._PRW_aim.GetValue().strip()
1285 intake['notes'] = self._PRW_notes.GetValue().strip()
1286 intake['is_long_term'] = self._CHBOX_long_term.IsChecked()
1287 intake['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
1288 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
1289 intake['duration'] = None
1290 else:
1291 intake['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
1292 intake.save()
1293
1294 if intake['pk_brand'] is None:
1295 if intake['pk_substance'] == self._PRW_substance.GetData():
1296 entered_intake = intake
1297 else:
1298 if intake['pk_substance'] == self._PRW_component.GetData():
1299 entered_intake = intake
1300
1301 self.data = entered_intake
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312 return True
1313
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324 self.data['started'] = self._DP_started.GetValue(as_pydt = True, invalid_as_none = True)
1325 self.data['discontinued'] = self._DP_discontinued.GetValue(as_pydt = True, invalid_as_none = True)
1326 if self.data['discontinued'] is None:
1327 self.data['discontinue_reason'] = None
1328 else:
1329 self.data['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
1330 self.data['preparation'] = self._PRW_preparation.GetValue()
1331 self.data['strength'] = self._PRW_strength.GetValue()
1332 self.data['schedule'] = self._PRW_schedule.GetValue()
1333 self.data['aim'] = self._PRW_aim.GetValue()
1334 self.data['notes'] = self._PRW_notes.GetValue()
1335 self.data['is_long_term'] = self._CHBOX_long_term.IsChecked()
1336 self.data['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
1337 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True)
1338
1339 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
1340 self.data['duration'] = None
1341 else:
1342 self.data['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356 self.data.save()
1357
1358
1359
1360
1361
1362
1363
1364 return True
1365
1367 self._PRW_component.SetText(u'', None)
1368 self._TCTRL_brand_ingredients.SetValue(u'')
1369
1370 self._PRW_substance.SetText(u'', None)
1371 self._PRW_substance.Enable(True)
1372
1373 self._PRW_preparation.SetText(u'', None)
1374 self._PRW_preparation.Enable(True)
1375
1376 self._PRW_schedule.SetText(u'', None)
1377 self._PRW_duration.SetText(u'', None)
1378 self._PRW_aim.SetText(u'', None)
1379 self._PRW_notes.SetText(u'', None)
1380 self._PRW_episode.SetText(u'', None)
1381
1382 self._CHBOX_long_term.SetValue(False)
1383 self._CHBOX_approved.SetValue(True)
1384
1385 self._DP_started.SetValue(gmDateTime.pydt_now_here())
1386 self._DP_discontinued.SetValue(None)
1387 self._PRW_discontinue_reason.SetValue(u'')
1388
1389 self.__refresh_allergies()
1390
1391 self._PRW_component.SetFocus()
1392
1394
1395 self._TCTRL_brand_ingredients.SetValue(u'')
1396
1397 if self.data['pk_brand'] is None:
1398 self.__refresh_from_existing_substance()
1399 else:
1400 self.__refresh_from_existing_component()
1401
1402 self._PRW_component.Enable(False)
1403 self._PRW_substance.Enable(False)
1404
1405 if self.data['is_long_term']:
1406 self._CHBOX_long_term.SetValue(True)
1407 self._PRW_duration.Enable(False)
1408 self._PRW_duration.SetText(gmTools.u_infinity, None)
1409 self._BTN_discontinued_as_planned.Enable(False)
1410 else:
1411 self._CHBOX_long_term.SetValue(False)
1412 self._PRW_duration.Enable(True)
1413 self._BTN_discontinued_as_planned.Enable(True)
1414 if self.data['duration'] is None:
1415 self._PRW_duration.SetText(u'', None)
1416 else:
1417 self._PRW_duration.SetText(gmDateTime.format_interval(self.data['duration'], gmDateTime.acc_days), self.data['duration'])
1418 self._PRW_aim.SetText(gmTools.coalesce(self.data['aim'], u''), self.data['aim'])
1419 self._PRW_notes.SetText(gmTools.coalesce(self.data['notes'], u''), self.data['notes'])
1420 self._PRW_episode.SetData(self.data['pk_episode'])
1421 self._PRW_schedule.SetText(gmTools.coalesce(self.data['schedule'], u''), self.data['schedule'])
1422
1423 self._CHBOX_approved.SetValue(self.data['intake_is_approved_of'])
1424
1425 self._DP_started.SetValue(self.data['started'])
1426 self._DP_discontinued.SetValue(self.data['discontinued'])
1427 self._PRW_discontinue_reason.SetValue(gmTools.coalesce(self.data['discontinue_reason'], u''))
1428
1429 self.__refresh_allergies()
1430
1431 self._PRW_schedule.SetFocus()
1432
1434 self._LBL_component.Enable(False)
1435 self._PRW_component.SetText(u'', None)
1436 self._PRW_component.display_as_valid(True)
1437
1438 self._PRW_substance.SetText (
1439 u'%s %s%s' % (self.data['substance'], self.data['amount'], self.data['unit']),
1440 self.data['pk_substance']
1441 )
1442
1443 self._PRW_preparation.SetText(gmTools.coalesce(self.data['preparation'], u''), self.data['preparation'])
1444 self._PRW_preparation.Enable(True)
1445
1447 self._PRW_component.SetText (
1448 u'%s %s%s (%s)' % (self.data['substance'], self.data['amount'], self.data['unit'], self.data['brand']),
1449 self.data['pk_substance']
1450 )
1451
1452 brand = gmMedication.cBrandedDrug(aPK_obj = self.data['pk_brand'])
1453 if brand['components'] is not None:
1454 self._TCTRL_brand_ingredients.SetValue(u'%s\n- %s' % (self.data['brand'], u'\n- '.join(brand['components'])))
1455
1456 self._LBL_or.Enable(False)
1457 self._LBL_substance.Enable(False)
1458 self._PRW_substance.SetText(u'', None)
1459 self._PRW_substance.display_as_valid(True)
1460
1461 self._PRW_preparation.SetText(self.data['preparation'], self.data['preparation'])
1462 self._PRW_preparation.Enable(False)
1463
1465 self._refresh_as_new()
1466
1467
1468
1470 if self._PRW_component.GetData() is None:
1471 self._LBL_or.Enable(True)
1472 self._PRW_component.SetText(u'', None)
1473 self._LBL_substance.Enable(True)
1474 self._PRW_substance.Enable(True)
1475 self._LBL_preparation.Enable(True)
1476 self._PRW_preparation.Enable(True)
1477 self._PRW_preparation.SetText(u'', None)
1478 self._TCTRL_brand_ingredients.SetValue(u'')
1479 else:
1480 self._LBL_or.Enable(False)
1481 self._LBL_substance.Enable(False)
1482 self._PRW_substance.SetText(u'', None)
1483 self._PRW_substance.display_as_valid(True)
1484 self._PRW_substance.Enable(False)
1485 self._LBL_preparation.Enable(False)
1486 self._PRW_preparation.Enable(False)
1487 comp = gmMedication.cDrugComponent(aPK_obj = self._PRW_component.GetData())
1488 self._PRW_preparation.SetText(comp['preparation'], comp['preparation'])
1489 brand = comp.containing_drug
1490 if brand['components'] is not None:
1491 self._TCTRL_brand_ingredients.SetValue(u'%s\n- %s' % (brand['brand'], u'\n- '.join(brand['components'])))
1492
1494 if self._PRW_substance.GetData() is None:
1495 self._LBL_or.Enable(True)
1496 self._LBL_component.Enable(True)
1497 self._PRW_component.Enable(True)
1498 self._PRW_substance.SetText(u'', None)
1499 else:
1500 self._LBL_or.Enable(False)
1501 self._LBL_component.Enable(False)
1502 self._PRW_component.SetText(u'', None)
1503 self._PRW_component.display_as_valid(True)
1504 self._PRW_component.Enable(False)
1505 self._LBL_preparation.Enable(True)
1506 self._PRW_preparation.Enable(True)
1507 self._TCTRL_brand_ingredients.SetValue(u'')
1508
1510 if self._DP_discontinued.GetValue() is None:
1511 self._PRW_discontinue_reason.Enable(False)
1512 self._CHBOX_is_allergy.Enable(False)
1513
1514 else:
1515 self._PRW_discontinue_reason.Enable(True)
1516 self._CHBOX_is_allergy.Enable(True)
1517
1518
1521
1524
1553
1555 if self._CHBOX_long_term.IsChecked() is True:
1556 self._PRW_duration.Enable(False)
1557 self._BTN_discontinued_as_planned.Enable(False)
1558 self._PRW_discontinue_reason.Enable(False)
1559 self._CHBOX_is_allergy.Enable(False)
1560 else:
1561 self._PRW_duration.Enable(True)
1562 self._BTN_discontinued_as_planned.Enable(True)
1563 self._PRW_discontinue_reason.Enable(True)
1564 self._CHBOX_is_allergy.Enable(True)
1565
1566 self.__refresh_allergies()
1567
1569 if self._CHBOX_is_allergy.IsChecked() is True:
1570 val = self._PRW_discontinue_reason.GetValue().strip()
1571 if not val.startswith(_('not tolerated:')):
1572 self._PRW_discontinue_reason.SetValue(u'%s %s' % (_('not tolerated:'), val))
1573
1574 self.__refresh_allergies()
1575
1577
1578 subst = gmMedication.cSubstanceIntakeEntry(aPK_obj = substance)
1579 msg = _(
1580 '\n'
1581 '[%s]\n'
1582 '\n'
1583 'It may be prudent to edit (before deletion) the details\n'
1584 'of this substance intake entry so as to leave behind\n'
1585 'some indication of why it was deleted.\n'
1586 ) % subst.format()
1587
1588 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
1589 parent,
1590 -1,
1591 caption = _('Deleting medication / substance intake'),
1592 question = msg,
1593 button_defs = [
1594 {'label': _('&Edit'), 'tooltip': _('Allow editing of substance intake entry before deletion.'), 'default': True},
1595 {'label': _('&Delete'), 'tooltip': _('Delete immediately without editing first.')},
1596 {'label': _('&Cancel'), 'tooltip': _('Abort. Do not delete or edit substance intake entry.')}
1597 ]
1598 )
1599
1600 edit_first = dlg.ShowModal()
1601 dlg.Destroy()
1602
1603 if edit_first == wx.ID_CANCEL:
1604 return
1605
1606 if edit_first == wx.ID_YES:
1607 edit_intake_of_substance(parent = parent, substance = subst)
1608 delete_it = gmGuiHelpers.gm_show_question (
1609 aMessage = _('Now delete substance intake entry ?'),
1610 aTitle = _('Deleting medication / substance intake')
1611 )
1612 else:
1613 delete_it = True
1614
1615 if not delete_it:
1616 return
1617
1618 gmMedication.delete_substance_intake(substance = substance)
1619
1629
1630
1631
1632
1660
1662
1663 if parent is None:
1664 parent = wx.GetApp().GetTopWindow()
1665
1666
1667 dbcfg = gmCfg.cCfgSQL()
1668 option = u'form_templates.medication_list'
1669
1670 template = dbcfg.get2 (
1671 option = option,
1672 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1673 bias = 'user'
1674 )
1675
1676 if template is None:
1677 template = configure_medication_list_template(parent = parent)
1678 if template is None:
1679 gmGuiHelpers.gm_show_error (
1680 aMessage = _('There is no medication list template configured.'),
1681 aTitle = _('Printing medication list')
1682 )
1683 return False
1684 else:
1685 try:
1686 name, ver = template.split(u' - ')
1687 except:
1688 _log.exception('problem splitting medication list template name [%s]', template)
1689 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading medication list template.'), beep = True)
1690 return False
1691 template = gmForms.get_form_template(name_long = name, external_version = ver)
1692 if template is None:
1693 gmGuiHelpers.gm_show_error (
1694 aMessage = _('Cannot load medication list template [%s - %s]') % (name, ver),
1695 aTitle = _('Printing medication list')
1696 )
1697 return False
1698
1699
1700 try:
1701 meds_list = template.instantiate()
1702 except KeyError:
1703 _log.exception('cannot instantiate medication list template [%s]', template)
1704 gmGuiHelpers.gm_show_error (
1705 aMessage = _('Invalid medication list template [%s - %s (%s)]') % (name, ver, template['engine']),
1706 aTitle = _('Printing medication list')
1707 )
1708 return False
1709
1710 ph = gmMacro.gmPlaceholderHandler()
1711
1712 meds_list.substitute_placeholders(data_source = ph)
1713 pdf_name = meds_list.generate_output(cleanup = cleanup)
1714 if cleanup:
1715 meds_list.cleanup()
1716 if pdf_name is None:
1717 gmGuiHelpers.gm_show_error (
1718 aMessage = _('Error generating the medication list.'),
1719 aTitle = _('Printing medication list')
1720 )
1721 return False
1722
1723
1724 printed = gmPrinting.print_file_by_shellscript(filename = pdf_name, jobtype = 'medication_list')
1725 if not printed:
1726 gmGuiHelpers.gm_show_error (
1727 aMessage = _('Error printing the medication list.'),
1728 aTitle = _('Printing medication list')
1729 )
1730 return False
1731
1732 pat = gmPerson.gmCurrentPatient()
1733 emr = pat.get_emr()
1734 epi = emr.add_episode(episode_name = 'administration', is_open = False)
1735 emr.add_clin_narrative (
1736 soap_cat = None,
1737 note = _('medication list printed from template [%s - %s]') % (template['name_long'], template['external_version']),
1738 episode = epi
1739 )
1740
1741 return True
1742
1744 """A grid class for displaying current substance intake.
1745
1746 - does NOT listen to the currently active patient
1747 - thereby it can display any patient at any time
1748 """
1750
1751 wx.grid.Grid.__init__(self, *args, **kwargs)
1752
1753 self.__patient = None
1754 self.__row_data = {}
1755 self.__prev_row = None
1756 self.__prev_tooltip_row = None
1757 self.__prev_cell_0 = None
1758 self.__grouping_mode = u'episode'
1759 self.__filter_show_unapproved = False
1760 self.__filter_show_inactive = False
1761
1762 self.__grouping2col_labels = {
1763 u'episode': [
1764 _('Episode'),
1765 _('Substance'),
1766 _('Dose'),
1767 _('Schedule'),
1768 _('Started'),
1769 _('Duration'),
1770 _('Brand')
1771 ],
1772 u'brand': [
1773 _('Brand'),
1774 _('Schedule'),
1775 _('Substance'),
1776 _('Dose'),
1777 _('Started'),
1778 _('Duration'),
1779 _('Episode')
1780 ]
1781 }
1782
1783 self.__grouping2order_by_clauses = {
1784 u'episode': u'pk_health_issue nulls first, episode, substance, started',
1785 u'brand': u'brand nulls last, substance, started'
1786 }
1787
1788 self.__init_ui()
1789 self.__register_events()
1790
1791
1792
1794
1795 sel_block_top_left = self.GetSelectionBlockTopLeft()
1796 sel_block_bottom_right = self.GetSelectionBlockBottomRight()
1797 sel_cols = self.GetSelectedCols()
1798 sel_rows = self.GetSelectedRows()
1799
1800 selected_cells = []
1801
1802
1803 selected_cells += self.GetSelectedCells()
1804
1805
1806 selected_cells += list (
1807 (row, col)
1808 for row in sel_rows
1809 for col in xrange(self.GetNumberCols())
1810 )
1811
1812
1813 selected_cells += list (
1814 (row, col)
1815 for row in xrange(self.GetNumberRows())
1816 for col in sel_cols
1817 )
1818
1819
1820 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()):
1821 selected_cells += [
1822 (row, col)
1823 for row in xrange(top_left[0], bottom_right[0] + 1)
1824 for col in xrange(top_left[1], bottom_right[1] + 1)
1825 ]
1826
1827 return set(selected_cells)
1828
1830 rows = {}
1831
1832 for row, col in self.get_selected_cells():
1833 rows[row] = True
1834
1835 return rows.keys()
1836
1839
1841
1842 self.empty_grid()
1843
1844 if self.__patient is None:
1845 return
1846
1847 emr = self.__patient.get_emr()
1848 meds = emr.get_current_substance_intake (
1849 order_by = self.__grouping2order_by_clauses[self.__grouping_mode],
1850 include_unapproved = self.__filter_show_unapproved,
1851 include_inactive = self.__filter_show_inactive
1852 )
1853 if not meds:
1854 return
1855
1856 self.BeginBatch()
1857
1858
1859 labels = self.__grouping2col_labels[self.__grouping_mode]
1860 if self.__filter_show_unapproved:
1861 self.AppendCols(numCols = len(labels) + 1)
1862 else:
1863 self.AppendCols(numCols = len(labels))
1864 for col_idx in range(len(labels)):
1865 self.SetColLabelValue(col_idx, labels[col_idx])
1866 if self.__filter_show_unapproved:
1867 self.SetColLabelValue(len(labels), u'OK?')
1868 self.SetColSize(len(labels), 40)
1869
1870 self.AppendRows(numRows = len(meds))
1871
1872
1873 for row_idx in range(len(meds)):
1874 med = meds[row_idx]
1875 self.__row_data[row_idx] = med
1876
1877 if med['is_currently_active'] is True:
1878 atcs = []
1879 if med['atc_substance'] is not None:
1880 atcs.append(med['atc_substance'])
1881
1882
1883
1884 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (med['substance'],))
1885 if allg not in [None, False]:
1886 attr = self.GetOrCreateCellAttr(row_idx, 0)
1887 if allg['type'] == u'allergy':
1888 attr.SetTextColour('red')
1889 else:
1890 attr.SetTextColour('yellow')
1891 self.SetRowAttr(row_idx, attr)
1892 else:
1893 attr = self.GetOrCreateCellAttr(row_idx, 0)
1894 attr.SetTextColour('grey')
1895 self.SetRowAttr(row_idx, attr)
1896
1897 if self.__grouping_mode == u'episode':
1898 if med['pk_episode'] is None:
1899 self.__prev_cell_0 = None
1900 self.SetCellValue(row_idx, 0, gmTools.u_diameter)
1901 else:
1902 if self.__prev_cell_0 != med['episode']:
1903 self.__prev_cell_0 = med['episode']
1904 self.SetCellValue(row_idx, 0, gmTools.coalesce(med['episode'], u''))
1905
1906 self.SetCellValue(row_idx, 1, med['substance'])
1907 self.SetCellValue(row_idx, 2, u'%s%s' % (med['amount'], med['unit']))
1908 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], u''))
1909 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
1910
1911 if med['is_long_term']:
1912 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
1913 else:
1914 if med['duration'] is None:
1915 self.SetCellValue(row_idx, 5, u'')
1916 else:
1917 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
1918
1919 if med['pk_brand'] is None:
1920 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['brand'], u''))
1921 else:
1922 if med['fake_brand']:
1923 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['brand'], u'', _('%s (fake)')))
1924 else:
1925 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['brand'], u''))
1926
1927 elif self.__grouping_mode == u'brand':
1928
1929 if med['pk_brand'] is None:
1930 self.__prev_cell_0 = None
1931 self.SetCellValue(row_idx, 0, gmTools.u_diameter)
1932 else:
1933 if self.__prev_cell_0 != med['brand']:
1934 self.__prev_cell_0 = med['brand']
1935 if med['fake_brand']:
1936 self.SetCellValue(row_idx, 0, gmTools.coalesce(med['brand'], u'', _('%s (fake)')))
1937 else:
1938 self.SetCellValue(row_idx, 0, gmTools.coalesce(med['brand'], u''))
1939
1940 self.SetCellValue(row_idx, 1, gmTools.coalesce(med['schedule'], u''))
1941 self.SetCellValue(row_idx, 2, med['substance'])
1942 self.SetCellValue(row_idx, 3, u'%s%s' % (med['amount'], med['unit']))
1943 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
1944
1945 if med['is_long_term']:
1946 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
1947 else:
1948 if med['duration'] is None:
1949 self.SetCellValue(row_idx, 5, u'')
1950 else:
1951 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
1952
1953 if med['pk_episode'] is None:
1954 self.SetCellValue(row_idx, 6, u'')
1955 else:
1956 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['episode'], u''))
1957
1958 else:
1959 raise ValueError('unknown grouping mode [%s]' % self.__grouping_mode)
1960
1961 if self.__filter_show_unapproved:
1962 self.SetCellValue (
1963 row_idx,
1964 len(labels),
1965 gmTools.bool2subst(med['intake_is_approved_of'], gmTools.u_checkmark_thin, u'', u'?')
1966 )
1967
1968
1969
1970 self.EndBatch()
1971
1973 self.BeginBatch()
1974 self.ClearGrid()
1975
1976
1977 if self.GetNumberRows() > 0:
1978 self.DeleteRows(pos = 0, numRows = self.GetNumberRows())
1979 if self.GetNumberCols() > 0:
1980 self.DeleteCols(pos = 0, numCols = self.GetNumberCols())
1981 self.EndBatch()
1982 self.__row_data = {}
1983 self.__prev_cell_0 = None
1984
1986
1987 if len(self.__row_data) == 0:
1988 return
1989
1990 sel_rows = self.get_selected_rows()
1991 if len(sel_rows) != 1:
1992 return
1993
1994 drug_db = get_drug_database()
1995 if drug_db is None:
1996 return
1997
1998 drug_db.show_info_on_substance(substance = self.get_selected_data()[0])
1999
2015
2028
2042
2045
2059
2073
2095
2100
2204
2205
2206
2208 self.CreateGrid(0, 1)
2209 self.EnableEditing(0)
2210 self.EnableDragGridSize(1)
2211 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows)
2212
2213 self.SetColLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTER)
2214
2215 self.SetRowLabelSize(0)
2216 self.SetRowLabelAlignment(horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
2217
2218
2219
2221 return self.__patient
2222
2226
2227 patient = property(_get_patient, _set_patient)
2228
2230 return self.__grouping_mode
2231
2235
2236 grouping_mode = property(_get_grouping_mode, _set_grouping_mode)
2237
2239 return self.__filter_show_unapproved
2240
2244
2245 filter_show_unapproved = property(_get_filter_show_unapproved, _set_filter_show_unapproved)
2246
2248 return self.__filter_show_inactive
2249
2253
2254 filter_show_inactive = property(_get_filter_show_inactive, _set_filter_show_inactive)
2255
2256
2257
2259
2260 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells)
2261
2262
2263
2264
2265 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
2266
2268 """Calculate where the mouse is and set the tooltip dynamically."""
2269
2270
2271
2272 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286 row, col = self.XYToCell(x, y)
2287
2288 if row == self.__prev_tooltip_row:
2289 return
2290
2291 self.__prev_tooltip_row = row
2292
2293 try:
2294 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row))
2295 except KeyError:
2296 pass
2297
2302
2303 from Gnumed.wxGladeWidgets import wxgCurrentSubstancesPnl
2304
2305 -class cCurrentSubstancesPnl(wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl, gmRegetMixin.cRegetOnPaintMixin):
2306
2307 """Panel holding a grid with current substances. Used as notebook page."""
2308
2315
2316
2317
2326
2327
2328
2330 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
2331 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._schedule_data_reget)
2332 gmDispatcher.connect(signal = u'substance_intake_mod_db', receiver = self._schedule_data_reget)
2333
2334
2335
2337 wx.CallAfter(self.__on_pre_patient_selection)
2338
2340 self._grid_substances.patient = None
2341
2344
2347
2350
2353
2356
2359
2362
2365
2368
2371
2374
2377
2380
2381
2382
2383 if __name__ == '__main__':
2384
2385 if len(sys.argv) < 2:
2386 sys.exit()
2387
2388 if sys.argv[1] != 'test':
2389 sys.exit()
2390
2391 from Gnumed.pycommon import gmI18N
2392
2393 gmI18N.activate_locale()
2394 gmI18N.install_domain(domain = 'gnumed')
2395
2396
2397 app = wx.PyWidgetTester(size = (600, 600))
2398 app.SetWidget(cATCPhraseWheel, -1)
2399 app.MainLoop()
2400
2401
2402