| Home | Trees | Indices | Help |
|
|---|
|
|
1 """GNUmed medication/substances handling widgets."""
2
3 #================================================================
4 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
5 __license__ = "GPL v2 or later"
6
7 import logging
8 import sys
9 import os.path
10 import decimal
11
12
13 import wx
14 import wx.grid
15
16
17 if __name__ == '__main__':
18 sys.path.insert(0, '../../')
19 from Gnumed.pycommon import gmI18N
20 gmI18N.activate_locale()
21 gmI18N.install_domain(domain = 'gnumed')
22
23 from Gnumed.pycommon import gmDispatcher
24 from Gnumed.pycommon import gmCfg
25 from Gnumed.pycommon import gmTools
26 from Gnumed.pycommon import gmDateTime
27 from Gnumed.pycommon import gmMatchProvider
28 from Gnumed.pycommon import gmI18N
29 from Gnumed.pycommon import gmPrinting
30 from Gnumed.pycommon import gmCfg2
31 from Gnumed.pycommon import gmNetworkTools
32
33 from Gnumed.business import gmPerson
34 from Gnumed.business import gmATC
35 from Gnumed.business import gmPraxis
36 from Gnumed.business import gmMedication
37 from Gnumed.business import gmForms
38 from Gnumed.business import gmStaff
39 from Gnumed.business import gmDocuments
40 from Gnumed.business import gmLOINC
41 from Gnumed.business import gmClinicalRecord
42 from Gnumed.business import gmClinicalCalculator
43
44 from Gnumed.wxpython import gmGuiHelpers
45 from Gnumed.wxpython import gmRegetMixin
46 from Gnumed.wxpython import gmAuthWidgets
47 from Gnumed.wxpython import gmEditArea
48 from Gnumed.wxpython import gmMacro
49 from Gnumed.wxpython import gmCfgWidgets
50 from Gnumed.wxpython import gmListWidgets
51 from Gnumed.wxpython import gmPhraseWheel
52 from Gnumed.wxpython import gmFormWidgets
53 from Gnumed.wxpython import gmAllergyWidgets
54 from Gnumed.wxpython import gmDocumentWidgets
55
56
57 _log = logging.getLogger('gm.ui')
58
59 #============================================================
60 # generic drug database access
61 #============================================================
63 gmCfgWidgets.configure_string_from_list_option (
64 parent = parent,
65 message = _(
66 '\n'
67 'Please select the default drug data source from the list below.\n'
68 '\n'
69 'Note that to actually use it you need to have the database installed, too.'
70 ),
71 option = 'external.drug_data.default_source',
72 bias = 'user',
73 default_value = None,
74 choices = gmMedication.drug_data_source_interfaces.keys(),
75 columns = [_('Drug data source')],
76 data = gmMedication.drug_data_source_interfaces.keys(),
77 caption = _('Configuring default drug data source')
78 )
79 #============================================================
81 dbcfg = gmCfg.cCfgSQL()
82
83 # load from option
84 default_db = dbcfg.get2 (
85 option = 'external.drug_data.default_source',
86 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
87 bias = 'workplace'
88 )
89
90 # not configured -> try to configure
91 if default_db is None:
92 gmDispatcher.send('statustext', msg = _('No default drug database configured.'), beep = True)
93 configure_drug_data_source(parent = parent)
94 default_db = dbcfg.get2 (
95 option = 'external.drug_data.default_source',
96 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
97 bias = 'workplace'
98 )
99 # still not configured -> return
100 if default_db is None:
101 gmGuiHelpers.gm_show_error (
102 aMessage = _('There is no default drug database configured.'),
103 aTitle = _('Jumping to drug database')
104 )
105 return None
106
107 # now it MUST be configured (either newly or previously)
108 # but also *validly* ?
109 try:
110 drug_db = gmMedication.drug_data_source_interfaces[default_db]()
111 except KeyError:
112 # not valid
113 _log.error('faulty default drug data source configuration: %s', default_db)
114 # try to configure
115 configure_drug_data_source(parent = parent)
116 default_db = dbcfg.get2 (
117 option = 'external.drug_data.default_source',
118 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
119 bias = 'workplace'
120 )
121 # deconfigured or aborted (and thusly still misconfigured) ?
122 try:
123 drug_db = gmMedication.drug_data_source_interfaces[default_db]()
124 except KeyError:
125 _log.error('still faulty default drug data source configuration: %s', default_db)
126 return None
127
128 pat = gmPerson.gmCurrentPatient()
129 if pat.connected:
130 drug_db.patient = pat
131
132 return drug_db
133 #============================================================
135 dbcfg = gmCfg.cCfgSQL()
136 drug_db = get_drug_database()
137 if drug_db is None:
138 return
139 drug_db.switch_to_frontend(blocking = False)
140
141 #============================================================
143
144 dbcfg = gmCfg.cCfgSQL()
145
146 ifap_cmd = dbcfg.get2 (
147 option = 'external.ifap-win.shell_command',
148 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
149 bias = 'workplace',
150 default = 'wine "C:\Ifapwin\WIAMDB.EXE"'
151 )
152 found, binary = gmShellAPI.detect_external_binary(ifap_cmd)
153 if not found:
154 gmDispatcher.send('statustext', msg = _('Cannot call IFAP via [%s].') % ifap_cmd)
155 return False
156 ifap_cmd = binary
157
158 if import_drugs:
159 transfer_file = os.path.expanduser(dbcfg.get2 (
160 option = 'external.ifap-win.transfer_file',
161 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
162 bias = 'workplace',
163 default = '~/.wine/drive_c/Ifapwin/ifap2gnumed.csv'
164 ))
165 # file must exist for Ifap to write into it
166 try:
167 f = open(transfer_file, 'w+b').close()
168 except IOError:
169 _log.exception('Cannot create IFAP <-> GNUmed transfer file [%s]', transfer_file)
170 gmDispatcher.send('statustext', msg = _('Cannot create IFAP <-> GNUmed transfer file [%s].') % transfer_file)
171 return False
172
173 wx.BeginBusyCursor()
174 gmShellAPI.run_command_in_shell(command = ifap_cmd, blocking = import_drugs)
175 wx.EndBusyCursor()
176
177 if import_drugs:
178 # COMMENT: this file must exist PRIOR to invoking IFAP
179 # COMMENT: or else IFAP will not write data into it ...
180 try:
181 csv_file = open(transfer_file, 'rb') # FIXME: encoding
182 except:
183 _log.exception('cannot access [%s]', fname)
184 csv_file = None
185
186 if csv_file is not None:
187 import csv
188 csv_lines = csv.DictReader (
189 csv_file,
190 fieldnames = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split(),
191 delimiter = ';'
192 )
193 pat = gmPerson.gmCurrentPatient()
194 emr = pat.get_emr()
195 # dummy episode for now
196 epi = emr.add_episode(episode_name = _('Current medication'))
197 for line in csv_lines:
198 narr = u'%sx %s %s %s (\u2258 %s %s) von %s (%s)' % (
199 line['Packungszahl'].strip(),
200 line['Handelsname'].strip(),
201 line['Form'].strip(),
202 line[u'Packungsgr\xf6\xdfe'].strip(),
203 line['Abpackungsmenge'].strip(),
204 line['Einheit'].strip(),
205 line['Hersteller'].strip(),
206 line['PZN'].strip()
207 )
208 emr.add_clin_narrative(note = narr, soap_cat = 's', episode = epi)
209 csv_file.close()
210
211 return True
212
213 #============================================================
214 # ATC related widgets
215 #============================================================
216
218
219 if parent is None:
220 parent = wx.GetApp().GetTopWindow()
221 #------------------------------------------------------------
222 def refresh(lctrl):
223 atcs = gmATC.get_reference_atcs()
224
225 items = [ [
226 a['atc'],
227 a['term'],
228 u'%s' % gmTools.coalesce(a['ddd'], u''),
229 gmTools.coalesce(a['unit'], u''),
230 gmTools.coalesce(a['administrative_route'], u''),
231 gmTools.coalesce(a['comment'], u''),
232 a['version'],
233 a['lang']
234 ] for a in atcs ]
235 lctrl.set_string_items(items)
236 lctrl.set_data(atcs)
237 #------------------------------------------------------------
238 gmListWidgets.get_choices_from_list (
239 parent = parent,
240 msg = _('\nThe ATC codes as known to GNUmed.\n'),
241 caption = _('Showing ATC codes.'),
242 columns = [ u'ATC', _('Term'), u'DDD', _('Unit'), _(u'Route'), _('Comment'), _('Version'), _('Language') ],
243 single_selection = True,
244 refresh_callback = refresh
245 )
246
247 #============================================================
249
250 dlg = wx.FileDialog (
251 parent = None,
252 message = _('Choose an ATC import config file'),
253 defaultDir = os.path.expanduser(os.path.join('~', 'gnumed')),
254 defaultFile = '',
255 wildcard = "%s (*.conf)|*.conf|%s (*)|*" % (_('config files'), _('all files')),
256 style = wx.OPEN | wx.HIDE_READONLY | wx.FILE_MUST_EXIST
257 )
258
259 result = dlg.ShowModal()
260 if result == wx.ID_CANCEL:
261 return
262
263 cfg_file = dlg.GetPath()
264 dlg.Destroy()
265
266 conn = gmAuthWidgets.get_dbowner_connection(procedure = _('importing ATC reference data'))
267 if conn is None:
268 return False
269
270 wx.BeginBusyCursor()
271
272 if gmATC.atc_import(cfg_fname = cfg_file, conn = conn):
273 gmDispatcher.send(signal = 'statustext', msg = _('Successfully imported ATC reference data.'))
274 else:
275 gmDispatcher.send(signal = 'statustext', msg = _('Importing ATC reference data failed.'), beep = True)
276
277 wx.EndBusyCursor()
278 return True
279
280 #============================================================
281
283
285
286 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
287
288 query = u"""
289
290 SELECT DISTINCT ON (label)
291 atc_code,
292 label
293 FROM (
294
295 SELECT
296 code as atc_code,
297 (code || ': ' || term || coalesce(' (' || ddd || unit || ')', ''))
298 AS label
299 FROM ref.atc
300 WHERE
301 term %(fragment_condition)s
302 OR
303 code %(fragment_condition)s
304
305 UNION ALL
306
307 SELECT
308 atc_code,
309 (atc_code || ': ' || description)
310 AS label
311 FROM ref.consumable_substance
312 WHERE
313 description %(fragment_condition)s
314 OR
315 atc_code %(fragment_condition)s
316
317 UNION ALL
318
319 SELECT
320 atc_code,
321 (atc_code || ': ' || description || ' (' || preparation || ')')
322 AS label
323 FROM ref.branded_drug
324 WHERE
325 description %(fragment_condition)s
326 OR
327 atc_code %(fragment_condition)s
328
329 -- it would be nice to be able to include clin.vacc_indication but that's hard to do in SQL
330
331 ) AS candidates
332 WHERE atc_code IS NOT NULL
333 ORDER BY label
334 LIMIT 50"""
335
336 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
337 mp.setThresholds(1, 2, 4)
338 # mp.word_separators = '[ \t=+&:@]+'
339 self.SetToolTipString(_('Select an ATC (Anatomical-Therapeutic-Chemical) code.'))
340 self.matcher = mp
341 self.selection_only = True
342
343 #============================================================
344 # consumable substances widgets
345 #------------------------------------------------------------
347
348 if parent is None:
349 parent = wx.GetApp().GetTopWindow()
350 #------------------------------------------------------------
351 def add_from_db(substance):
352 drug_db = get_drug_database(parent = parent)
353 if drug_db is None:
354 return False
355 drug_db.import_drugs()
356 return True
357 #------------------------------------------------------------
358 def edit(substance=None):
359 return edit_consumable_substance(parent = parent, substance = substance, single_entry = (substance is not None))
360 #------------------------------------------------------------
361 def delete(substance):
362 if substance.is_in_use_by_patients:
363 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete this substance. It is in use.'), beep = True)
364 return False
365
366 return gmMedication.delete_consumable_substance(substance = substance['pk'])
367 #------------------------------------------------------------
368 def refresh(lctrl):
369 substs = gmMedication.get_consumable_substances(order_by = 'description')
370 items = [ [
371 s['description'],
372 s['amount'],
373 s['unit'],
374 gmTools.coalesce(s['atc_code'], u''),
375 s['pk']
376 ] for s in substs ]
377 lctrl.set_string_items(items)
378 lctrl.set_data(substs)
379 #------------------------------------------------------------
380 msg = _('\nThese are the consumable substances registered with GNUmed.\n')
381
382 gmListWidgets.get_choices_from_list (
383 parent = parent,
384 msg = msg,
385 caption = _('Showing consumable substances.'),
386 columns = [_('Substance'), _('Amount'), _('Unit'), 'ATC', u'#'],
387 single_selection = True,
388 new_callback = edit,
389 edit_callback = edit,
390 delete_callback = delete,
391 refresh_callback = refresh,
392 left_extra_button = (_('Import'), _('Import consumable substances from a drug database.'), add_from_db)
393 )
394
395 #------------------------------------------------------------
397
398 if substance is not None:
399 if substance.is_in_use_by_patients:
400 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit this substance. It is in use.'), beep = True)
401 return False
402
403 ea = cConsumableSubstanceEAPnl(parent = parent, id = -1)
404 ea.data = substance
405 ea.mode = gmTools.coalesce(substance, 'new', 'edit')
406 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
407 dlg.SetTitle(gmTools.coalesce(substance, _('Adding new consumable substance'), _('Editing consumable substance')))
408 if dlg.ShowModal() == wx.ID_OK:
409 dlg.Destroy()
410 return True
411 dlg.Destroy()
412 return False
413
414 #============================================================
415 from Gnumed.wxGladeWidgets import wxgConsumableSubstanceEAPnl
416
417 -class cConsumableSubstanceEAPnl(wxgConsumableSubstanceEAPnl.wxgConsumableSubstanceEAPnl, gmEditArea.cGenericEditAreaMixin):
418
420
421 try:
422 data = kwargs['substance']
423 del kwargs['substance']
424 except KeyError:
425 data = None
426
427 wxgConsumableSubstanceEAPnl.wxgConsumableSubstanceEAPnl.__init__(self, *args, **kwargs)
428 gmEditArea.cGenericEditAreaMixin.__init__(self)
429
430 # Code using this mixin should set mode and data
431 # after instantiating the class:
432 self.mode = 'new'
433 self.data = data
434 if data is not None:
435 self.mode = 'edit'
436
437 # self.__init_ui()
438 #----------------------------------------------------------------
439 # def __init_ui(self):
440 # self._PRW_atc.selection_only = False
441 #----------------------------------------------------------------
442 # generic Edit Area mixin API
443 #----------------------------------------------------------------
445
446 validity = True
447
448 if self._TCTRL_substance.GetValue().strip() == u'':
449 validity = False
450 self.display_tctrl_as_valid(tctrl = self._TCTRL_substance, valid = False)
451 self._TCTRL_substance.SetFocus()
452 else:
453 self.display_tctrl_as_valid(tctrl = self._TCTRL_substance, valid = True)
454
455 try:
456 decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.'))
457 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
458 except (TypeError, decimal.InvalidOperation):
459 validity = False
460 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
461 self._TCTRL_amount.SetFocus()
462
463 if self._PRW_unit.GetValue().strip() == u'':
464 validity = False
465 self._PRW_unit.display_as_valid(valid = False)
466 self._TCTRL_substance.SetFocus()
467 else:
468 self._PRW_unit.display_as_valid(valid = True)
469
470 if validity is False:
471 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save consumable substance. Missing essential input.'))
472
473 return validity
474 #----------------------------------------------------------------
476 subst = gmMedication.create_consumable_substance (
477 substance = self._TCTRL_substance.GetValue().strip(),
478 atc = self._PRW_atc.GetData(),
479 amount = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.')),
480 unit = gmTools.coalesce(self._PRW_unit.GetData(), self._PRW_unit.GetValue().strip(), function_initial = ('strip', None))
481 )
482 success, data = subst.save()
483 if not success:
484 err, msg = data
485 _log.error(err)
486 _log.error(msg)
487 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save consumable substance. %s') % msg, beep = True)
488 return False
489
490 self.data = subst
491 return True
492 #----------------------------------------------------------------
494 self.data['description'] = self._TCTRL_substance.GetValue().strip()
495 self.data['atc_code'] = self._PRW_atc.GetData()
496 self.data['amount'] = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.'))
497 self.data['unit'] = gmTools.coalesce(self._PRW_unit.GetData(), self._PRW_unit.GetValue().strip(), function_initial = ('strip', None))
498 success, data = self.data.save()
499
500 if not success:
501 err, msg = data
502 _log.error(err)
503 _log.error(msg)
504 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save consumable substance. %s') % msg, beep = True)
505 return False
506
507 return True
508 #----------------------------------------------------------------
510 self._TCTRL_substance.SetValue(u'')
511 self._TCTRL_amount.SetValue(u'')
512 self._PRW_unit.SetText(u'', None)
513 self._PRW_atc.SetText(u'', None)
514
515 self._TCTRL_substance.SetFocus()
516 #----------------------------------------------------------------
518 self._TCTRL_substance.SetValue(self.data['description'])
519 self._TCTRL_amount.SetValue(u'%s' % self.data['amount'])
520 self._PRW_unit.SetText(self.data['unit'], self.data['unit'])
521 self._PRW_atc.SetText(gmTools.coalesce(self.data['atc_code'], u''), self.data['atc_code'])
522
523 self._TCTRL_substance.SetFocus()
524 #----------------------------------------------------------------
527
528 #============================================================
529 # drug component widgets
530 #------------------------------------------------------------
532
533 if parent is None:
534 parent = wx.GetApp().GetTopWindow()
535
536 #------------------------------------------------------------
537 def edit(component=None):
538 substance = gmMedication.cConsumableSubstance(aPK_obj = component['pk_consumable_substance'])
539 return edit_consumable_substance(parent = parent, substance = substance, single_entry = True)
540 #------------------------------------------------------------
541 def delete(component):
542 if component.is_in_use_by_patients:
543 gmDispatcher.send(signal = 'statustext', msg = _('Cannot remove this component from the drug. It is in use.'), beep = True)
544 return False
545
546 return component.containing_drug.remove_component(substance = component['pk_component'])
547 #------------------------------------------------------------
548 def refresh(lctrl):
549 comps = gmMedication.get_drug_components()
550 items = [ [
551 u'%s%s' % (c['brand'], gmTools.coalesce(c['atc_brand'], u'', u' [%s]')),
552 u'%s%s' % (c['substance'], gmTools.coalesce(c['atc_substance'], u'', u' [%s]')),
553 u'%s %s' % (c['amount'], c['unit']),
554 c['preparation'],
555 gmTools.coalesce(c['external_code_brand'], u'', u'%%s [%s]' % c['external_code_type_brand']),
556 c['pk_component']
557 ] for c in comps ]
558 lctrl.set_string_items(items)
559 lctrl.set_data(comps)
560 #------------------------------------------------------------
561 msg = _('\nThese are the components in the drug brands known to GNUmed.\n')
562
563 gmListWidgets.get_choices_from_list (
564 parent = parent,
565 msg = msg,
566 caption = _('Showing drug brand components.'),
567 columns = [_('Brand'), _('Substance'), _('Strength'), _('Preparation'), _('Code'), u'#'],
568 single_selection = True,
569 #new_callback = edit,
570 edit_callback = edit,
571 delete_callback = delete,
572 refresh_callback = refresh
573 )
574
575 #------------------------------------------------------------
577 ea = cDrugComponentEAPnl(parent = parent, id = -1)
578 ea.data = drug_component
579 ea.mode = gmTools.coalesce(drug_component, 'new', 'edit')
580 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
581 dlg.SetTitle(gmTools.coalesce(drug_component, _('Adding new drug component'), _('Editing drug component')))
582 if dlg.ShowModal() == wx.ID_OK:
583 dlg.Destroy()
584 return True
585 dlg.Destroy()
586 return False
587
588 #============================================================
589 from Gnumed.wxGladeWidgets import wxgDrugComponentEAPnl
590
591 -class cDrugComponentEAPnl(wxgDrugComponentEAPnl.wxgDrugComponentEAPnl, gmEditArea.cGenericEditAreaMixin):
592
594
595 try:
596 data = kwargs['component']
597 del kwargs['component']
598 except KeyError:
599 data = None
600
601 wxgDrugComponentEAPnl.wxgDrugComponentEAPnl.__init__(self, *args, **kwargs)
602 gmEditArea.cGenericEditAreaMixin.__init__(self)
603
604 # Code using this mixin should set mode and data
605 # after instantiating the class:
606 self.mode = 'new'
607 self.data = data
608 if data is not None:
609 self.mode = 'edit'
610
611 #self.__init_ui()
612 #----------------------------------------------------------------
613 # def __init_ui(self):
614 # # adjust phrasewheels etc
615 #----------------------------------------------------------------
616 # generic Edit Area mixin API
617 #----------------------------------------------------------------
619 if self.data is not None:
620 if self.data['is_in_use']:
621 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit drug component. It is in use.'), beep = True)
622 return False
623
624 validity = True
625
626 if self._PRW_substance.GetData() is None:
627 validity = False
628 self._PRW_substance.display_as_valid(False)
629 else:
630 self._PRW_substance.display_as_valid(True)
631
632 val = self._TCTRL_amount.GetValue().strip().replace(',', u'.', 1)
633 try:
634 decimal.Decimal(val)
635 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
636 except:
637 validity = False
638 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
639
640 if self._PRW_unit.GetValue().strip() == u'':
641 validity = False
642 self._PRW_unit.display_as_valid(False)
643 else:
644 self._PRW_unit.display_as_valid(True)
645
646 if validity is False:
647 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save drug component. Invalid or missing essential input.'))
648
649 return validity
650 #----------------------------------------------------------------
652 # save the data as a new instance
653 data = 1
654 data[''] = 1
655 data[''] = 1
656 # data.save()
657
658 # must be done very late or else the property access
659 # will refresh the display such that later field
660 # access will return empty values
661 # self.data = data
662 return False
663 return True
664 #----------------------------------------------------------------
666 self.data['pk_consumable_substance'] = self._PRW_substance.GetData()
667 self.data['amount'] = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', u'.', 1))
668 self.data['unit'] = self._PRW_unit.GetValue().strip()
669 return self.data.save()
670 #----------------------------------------------------------------
672 self._TCTRL_brand.SetValue(u'')
673 self._TCTRL_components.SetValue(u'')
674 self._TCTRL_codes.SetValue(u'')
675 self._PRW_substance.SetText(u'', None)
676 self._TCTRL_amount.SetValue(u'')
677 self._PRW_unit.SetText(u'', None)
678
679 self._PRW_substance.SetFocus()
680 #----------------------------------------------------------------
682 self._TCTRL_brand.SetValue(u'%s (%s)' % (self.data['brand'], self.data['preparation']))
683 self._TCTRL_components.SetValue(u' / '.join(self.data.containing_drug['components']))
684 details = []
685 if self.data['atc_brand'] is not None:
686 details.append(u'ATC: %s' % self.data['atc_brand'])
687 if self.data['external_code_brand'] is not None:
688 details.append(u'%s: %s' % (self.data['external_code_type_brand'], self.data['external_code_brand']))
689 self._TCTRL_codes.SetValue(u'; '.join(details))
690
691 self._PRW_substance.SetText(self.data['substance'], self.data['pk_consumable_substance'])
692 self._TCTRL_amount.SetValue(u'%s' % self.data['amount'])
693 self._PRW_unit.SetText(self.data['unit'], self.data['unit'])
694
695 self._PRW_substance.SetFocus()
696 #----------------------------------------------------------------
706
707 #============================================================
709
711
712 mp = gmMedication.cDrugComponentMatchProvider()
713 mp.setThresholds(2, 3, 4)
714 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
715 self.SetToolTipString(_('A drug component with optional strength.'))
716 self.matcher = mp
717 self.selection_only = False
718 #--------------------------------------------------------
720 return gmMedication.cDrugComponent(aPK_obj = self.GetData(as_instance = False, can_create = False))
721
722 #============================================================
723 #============================================================
725
727
728 query = u"""
729 (
730 SELECT DISTINCT ON (list_label)
731 preparation AS data,
732 preparation AS list_label,
733 preparation AS field_label
734 FROM ref.branded_drug
735 WHERE preparation %(fragment_condition)s
736 ) UNION (
737 SELECT DISTINCT ON (list_label)
738 preparation AS data,
739 preparation AS list_label,
740 preparation AS field_label
741 FROM clin.substance_intake
742 WHERE preparation %(fragment_condition)s
743 )
744 ORDER BY list_label
745 LIMIT 30"""
746
747 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
748 mp.setThresholds(1, 2, 4)
749 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
750 self.SetToolTipString(_('The preparation (form) of the substance or brand.'))
751 self.matcher = mp
752 self.selection_only = False
753
754 #============================================================
756
758
759 mp = gmMedication.cSubstanceMatchProvider()
760 mp.setThresholds(1, 2, 4)
761 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
762 self.SetToolTipString(_('The substance with optional strength.'))
763 self.matcher = mp
764 self.selection_only = False
765 self.phrase_separators = None
766
767 #--------------------------------------------------------
769 return gmMedication.cConsumableSubstance(aPK_obj = self.GetData(as_instance = False, can_create = False))
770
771 #============================================================
772 # branded drugs widgets
773 #------------------------------------------------------------
775
776 if brand is not None:
777 if brand.is_in_use_by_patients:
778 gmGuiHelpers.gm_show_info (
779 aTitle = _('Managing components of a drug'),
780 aMessage = _(
781 'Cannot manage the components of the branded drug product\n'
782 '\n'
783 ' "%s" (%s)\n'
784 '\n'
785 'because it is currently taken by patients.\n'
786 ) % (brand['brand'], brand['preparation'])
787 )
788 return False
789 #--------------------------------------------------------
790 if parent is None:
791 parent = wx.GetApp().GetTopWindow()
792 #--------------------------------------------------------
793 # def manage_substances():
794 # pass
795 #--------------------------------------------------------
796 if brand is None:
797 msg = _('Pick the substances which are components of this drug.')
798 right_col = _('Components of drug')
799 comp_substs = []
800 else:
801 right_col = u'%s (%s)' % (brand['brand'], brand['preparation'])
802 msg = _(
803 'Adjust the components of "%s"\n'
804 '\n'
805 'The drug must contain at least one component. Any given\n'
806 'substance can only be included once per drug.'
807 ) % right_col
808 comp_substs = [ c.substance for c in brand.components ]
809
810 substs = gmMedication.get_consumable_substances(order_by = 'description')
811 choices = [ u'%s %s %s' % (s['description'], s['amount'], s['unit']) for s in substs ]
812 picks = [ u'%s %s %s' % (c['description'], c['amount'], c['unit']) for c in comp_substs ]
813
814 picker = gmListWidgets.cItemPickerDlg (
815 parent,
816 -1,
817 title = _('Managing components of a drug ...'),
818 msg = msg
819 )
820 picker.set_columns(['Substances'], [right_col])
821 picker.set_choices(choices = choices, data = substs)
822 picker.set_picks(picks = picks, data = comp_substs)
823 # picker.extra_button = (
824 # _('Substances'),
825 # _('Manage list of consumable substances'),
826 # manage_substances
827 # )
828
829 btn_pressed = picker.ShowModal()
830 substs = picker.get_picks()
831 picker.Destroy()
832
833 if btn_pressed != wx.ID_OK:
834 return (False, None)
835
836 if brand is not None:
837 brand.set_substances_as_components(substances = substs)
838
839 return (True, substs)
840 #------------------------------------------------------------
842
843 if parent is None:
844 parent = wx.GetApp().GetTopWindow()
845 #------------------------------------------------------------
846 def add_from_db(brand):
847 drug_db = get_drug_database(parent = parent)
848 if drug_db is None:
849 return False
850 drug_db.import_drugs()
851 return True
852 #------------------------------------------------------------
853 def get_tooltip(brand=None):
854 tt = u'%s %s\n' % (brand['brand'], brand['preparation'])
855 tt += u'\n'
856 tt += u'%s%s%s\n' % (
857 gmTools.bool2subst(brand.is_vaccine, u'%s, ' % _('Vaccine'), u''),
858 u'%s, ' % gmTools.bool2subst(brand.is_in_use_by_patients, _('in use'), _('not in use')),
859 gmTools.bool2subst(brand['is_fake_brand'], _('fake'), u'')
860 )
861 tt += gmTools.coalesce(brand['atc'], u'', _('ATC: %s\n'))
862 tt += gmTools.coalesce(brand['external_code'], u'', u'%s: %%s\n' % brand['external_code_type'])
863 if brand['components'] is not None:
864 tt += u'- %s' % u'\n- '.join(brand['components'])
865 return tt
866 #------------------------------------------------------------
867 def edit(brand):
868 if brand is not None:
869 if brand.is_vaccine:
870 gmGuiHelpers.gm_show_info (
871 aTitle = _('Editing medication'),
872 aMessage = _(
873 'Cannot edit the medication\n'
874 '\n'
875 ' "%s" (%s)\n'
876 '\n'
877 'because it is a vaccine. Please edit it\n'
878 'from the vaccine management section !\n'
879 ) % (brand['brand'], brand['preparation'])
880 )
881 return False
882
883 return edit_branded_drug(parent = parent, branded_drug = brand, single_entry = True)
884 #------------------------------------------------------------
885 def delete(brand):
886 if brand.is_vaccine:
887 gmGuiHelpers.gm_show_info (
888 aTitle = _('Deleting medication'),
889 aMessage = _(
890 'Cannot delete the medication\n'
891 '\n'
892 ' "%s" (%s)\n'
893 '\n'
894 'because it is a vaccine. Please delete it\n'
895 'from the vaccine management section !\n'
896 ) % (brand['brand'], brand['preparation'])
897 )
898 return False
899 gmMedication.delete_branded_drug(brand = brand['pk_brand'])
900 return True
901 #------------------------------------------------------------
902 def new():
903 return edit_branded_drug(parent = parent, branded_drug = None, single_entry = False)
904 #------------------------------------------------------------
905 def refresh(lctrl):
906 drugs = gmMedication.get_branded_drugs()
907 items = [ [
908 u'%s%s' % (
909 d['brand'],
910 gmTools.bool2subst(d['is_fake_brand'], ' (%s)' % _('fake'), u'')
911 ),
912 d['preparation'],
913 gmTools.coalesce(d['atc'], u''),
914 gmTools.coalesce(d['components'], u''),
915 gmTools.coalesce(d['external_code'], u'', u'%%s [%s]' % d['external_code_type']),
916 d['pk_brand']
917 ] for d in drugs ]
918 lctrl.set_string_items(items)
919 lctrl.set_data(drugs)
920 #------------------------------------------------------------
921 msg = _('\nThese are the drug brands known to GNUmed.\n')
922
923 gmListWidgets.get_choices_from_list (
924 parent = parent,
925 msg = msg,
926 caption = _('Showing branded drugs.'),
927 columns = [_('Name'), _('Preparation'), _('ATC'), _('Components'), _('Code'), u'#'],
928 single_selection = True,
929 ignore_OK_button = ignore_OK_button,
930 refresh_callback = refresh,
931 new_callback = new,
932 edit_callback = edit,
933 delete_callback = delete,
934 list_tooltip_callback = get_tooltip,
935 left_extra_button = (_('Import'), _('Import substances and brands from a drug database.'), add_from_db)
936 #, middle_extra_button = (_('Clone'), _('Clone selected drug into a new entry for editing.'), clone_from_existing)
937 #, right_extra_button = (_('Reassign'), _('Reassign all patients taking the selected drug to another drug.'), reassign_patients)
938 )
939
940 #------------------------------------------------------------
942
943 if branded_drug is not None:
944 if branded_drug.is_in_use_by_patients:
945 gmGuiHelpers.gm_show_info (
946 aTitle = _('Editing drug'),
947 aMessage = _(
948 'Cannot edit the branded drug product\n'
949 '\n'
950 ' "%s" (%s)\n'
951 '\n'
952 'because it is currently taken by patients.\n'
953 ) % (branded_drug['brand'], branded_drug['preparation'])
954 )
955 return False
956
957 if parent is None:
958 parent = wx.GetApp().GetTopWindow()
959 #--------------------------------------------
960 def manage_substances(drug):
961 manage_consumable_substances(parent = parent)
962 #--------------------------------------------
963 ea = cBrandedDrugEAPnl(parent = parent, id = -1)
964 ea.data = branded_drug
965 ea.mode = gmTools.coalesce(branded_drug, 'new', 'edit')
966 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
967 dlg.SetTitle(gmTools.coalesce(branded_drug, _('Adding new drug brand'), _('Editing drug brand')))
968 dlg.left_extra_button = (
969 _('Substances'),
970 _('Manage consumable substances'),
971 manage_substances
972 )
973 if dlg.ShowModal() == wx.ID_OK:
974 dlg.Destroy()
975 return True
976 dlg.Destroy()
977 return False
978
979 #============================================================
980 from Gnumed.wxGladeWidgets import wxgBrandedDrugEAPnl
981
982 -class cBrandedDrugEAPnl(wxgBrandedDrugEAPnl.wxgBrandedDrugEAPnl, gmEditArea.cGenericEditAreaMixin):
983
985
986 try:
987 data = kwargs['drug']
988 del kwargs['drug']
989 except KeyError:
990 data = None
991
992 wxgBrandedDrugEAPnl.wxgBrandedDrugEAPnl.__init__(self, *args, **kwargs)
993 gmEditArea.cGenericEditAreaMixin.__init__(self)
994
995 self.mode = 'new'
996 self.data = data
997 if data is not None:
998 self.mode = 'edit'
999 self.__component_substances = data.components_as_substances
1000
1001 #self.__init_ui()
1002 #----------------------------------------------------------------
1003 # def __init_ui(self):
1004 # adjust external type PRW
1005 #----------------------------------------------------------------
1006 # generic Edit Area mixin API
1007 #----------------------------------------------------------------
1009
1010 if self.data is not None:
1011 if self.data.is_in_use_by_patients:
1012 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit drug brand. It is in use.'), beep = True)
1013 return False
1014
1015 validity = True
1016
1017 brand_name = self._PRW_brand.GetValue().strip()
1018 if brand_name == u'':
1019 validity = False
1020 self._PRW_brand.display_as_valid(False)
1021 else:
1022 self._PRW_brand.display_as_valid(True)
1023
1024 preparation = self._PRW_preparation.GetValue().strip()
1025 if preparation == u'':
1026 validity = False
1027 self._PRW_preparation.display_as_valid(False)
1028 else:
1029 self._PRW_preparation.display_as_valid(True)
1030
1031 if validity is True:
1032 # dupe ?
1033 drug = gmMedication.get_drug_by_brand(brand_name = brand_name, preparation = preparation)
1034 if drug is not None:
1035 validity = False
1036 self._PRW_brand.display_as_valid(False)
1037 self._PRW_preparation.display_as_valid(False)
1038 gmGuiHelpers.gm_show_error (
1039 title = _('Checking brand data'),
1040 error = _(
1041 'The brand information you entered:\n'
1042 '\n'
1043 ' [%s %s]\n'
1044 '\n'
1045 'already exists as a drug product.'
1046 ) % (brand_name, preparation)
1047 )
1048
1049 else:
1050 # lacking components ?
1051 self._TCTRL_components.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BACKGROUND))
1052 if len(self.__component_substances) == 0:
1053 wants_empty = gmGuiHelpers.gm_show_question (
1054 title = _('Checking brand data'),
1055 question = _(
1056 'You have not selected any substances\n'
1057 'as drug components.\n'
1058 '\n'
1059 'Without components you will not be able to\n'
1060 'use this drug for documenting patient care.\n'
1061 '\n'
1062 'Are you sure you want to save\n'
1063 'it without components ?'
1064 )
1065 )
1066 if not wants_empty:
1067 validity = False
1068 self.display_ctrl_as_valid(ctrl = self._TCTRL_components, valid = False)
1069
1070 if validity is False:
1071 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save branded drug. Invalid or missing essential input.'))
1072
1073 return validity
1074 #----------------------------------------------------------------
1076
1077 drug = gmMedication.create_branded_drug (
1078 brand_name = self._PRW_brand.GetValue().strip(),
1079 preparation = gmTools.coalesce (
1080 self._PRW_preparation.GetData(),
1081 self._PRW_preparation.GetValue()
1082 ).strip(),
1083 return_existing = True
1084 )
1085 drug['is_fake_brand'] = self._CHBOX_is_fake.GetValue()
1086 drug['atc'] = self._PRW_atc.GetData()
1087 code = self._TCTRL_external_code.GetValue().strip()
1088 if code != u'':
1089 drug['external_code'] = code
1090 drug['external_code_type'] = self._PRW_external_code_type.GetData().strip()
1091
1092 drug.save()
1093
1094 if len(self.__component_substances) > 0:
1095 drug.set_substances_as_components(substances = self.__component_substances)
1096
1097 self.data = drug
1098
1099 return True
1100 #----------------------------------------------------------------
1102 self.data['brand'] = self._PRW_brand.GetValue().strip()
1103 self.data['preparation'] = gmTools.coalesce (
1104 self._PRW_preparation.GetData(),
1105 self._PRW_preparation.GetValue()
1106 ).strip()
1107 self.data['is_fake_brand'] = self._CHBOX_is_fake.GetValue()
1108 self.data['atc'] = self._PRW_atc.GetData()
1109 code = self._TCTRL_external_code.GetValue().strip()
1110 if code != u'':
1111 self.data['external_code'] = code
1112 self.data['external_code_type'] = self._PRW_external_code_type.GetData().strip()
1113 success, data = self.data.save()
1114 if not success:
1115 err, msg = data
1116 _log.error('problem saving')
1117 _log.error('%s', err)
1118 _log.error('%s', msg)
1119 return (success is True)
1120 #----------------------------------------------------------------
1122 self._PRW_brand.SetText(u'', None)
1123 self._PRW_preparation.SetText(u'', None)
1124 self._CHBOX_is_fake.SetValue(False)
1125 self._TCTRL_components.SetValue(u'')
1126 self._PRW_atc.SetText(u'', None)
1127 self._TCTRL_external_code.SetValue(u'')
1128 self._PRW_external_code_type.SetText(u'', None)
1129
1130 self._PRW_brand.SetFocus()
1131
1132 self.__component_substances = []
1133 #----------------------------------------------------------------
1136 #----------------------------------------------------------------
1138 self._PRW_brand.SetText(self.data['brand'], self.data['pk_brand'])
1139 self._PRW_preparation.SetText(self.data['preparation'], self.data['preparation'])
1140 self._CHBOX_is_fake.SetValue(self.data['is_fake_brand'])
1141 comps = u''
1142 if self.data['components'] is not None:
1143 comps = u'- %s' % u'\n- '.join(self.data['components'])
1144 self._TCTRL_components.SetValue(comps)
1145 self._PRW_atc.SetText(gmTools.coalesce(self.data['atc'], u''), self.data['atc'])
1146 self._TCTRL_external_code.SetValue(gmTools.coalesce(self.data['external_code'], u''))
1147 t = gmTools.coalesce(self.data['external_code_type'], u'')
1148 self._PRW_external_code_type.SetText(t, t)
1149
1150 self._PRW_brand.SetFocus()
1151
1152 self.__component_substances = self.data.components_as_substances
1153 #----------------------------------------------------------------
1154 # event handler
1155 #----------------------------------------------------------------
1169 #============================================================
1171
1173
1174 query = u"""
1175 SELECT
1176 pk
1177 AS data,
1178 (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', ''))
1179 AS list_label,
1180 (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', ''))
1181 AS field_label
1182 FROM ref.branded_drug
1183 WHERE description %(fragment_condition)s
1184 ORDER BY list_label
1185 LIMIT 50"""
1186
1187 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
1188 mp.setThresholds(2, 3, 4)
1189 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1190 self.SetToolTipString(_(
1191 'The brand name of the drug.\n'
1192 '\n'
1193 'Note: a brand name will need to be linked to\n'
1194 'one or more components before it can be used,\n'
1195 'except in the case of fake (generic) vaccines.'
1196 ))
1197 self.matcher = mp
1198 self.selection_only = False
1199
1200 #============================================================
1201 # current substance intake widgets
1202 #------------------------------------------------------------
1204
1206
1207 query = u"""
1208 SELECT DISTINCT ON (sched)
1209 schedule as sched,
1210 schedule
1211 FROM clin.substance_intake
1212 WHERE schedule %(fragment_condition)s
1213 ORDER BY sched
1214 LIMIT 50"""
1215
1216 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
1217 mp.setThresholds(1, 2, 4)
1218 mp.word_separators = '[ \t=+&:@]+'
1219 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1220 self.SetToolTipString(_('The schedule for taking this substance.'))
1221 self.matcher = mp
1222 self.selection_only = False
1223
1224 #============================================================
1226
1228
1229 query = u"""
1230 (
1231 SELECT DISTINCT ON (field_label)
1232 aim
1233 AS data,
1234 aim || ' (' || substance || ' ' || amount || ' ' || unit || ')'
1235 AS list_label,
1236 aim
1237 AS field_label
1238 FROM clin.v_pat_substance_intake
1239 WHERE
1240 aim %(fragment_condition)s
1241 %(ctxt_substance)s
1242 ) UNION (
1243 SELECT DISTINCT ON (field_label)
1244 aim
1245 AS data,
1246 aim || ' (' || substance || ' ' || amount || ' ' || unit || ')'
1247 AS list_label,
1248 aim
1249 AS field_label
1250 FROM clin.v_pat_substance_intake
1251 WHERE
1252 aim %(fragment_condition)s
1253 )
1254 ORDER BY list_label
1255 LIMIT 30"""
1256
1257 context = {'ctxt_substance': {
1258 'where_part': u'AND substance = %(substance)s',
1259 'placeholder': u'substance'
1260 }}
1261
1262 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query, context = context)
1263 mp.setThresholds(1, 2, 4)
1264 #mp.word_separators = '[ \t=+&:@]+'
1265 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1266 self.SetToolTipString(_('The medical aim for consuming this substance.'))
1267 self.matcher = mp
1268 self.selection_only = False
1269
1270 #============================================================
1272
1273 if intake['is_currently_active']:
1274 intake['discontinued'] = gmDateTime.pydt_now_here()
1275 if intake['discontinue_reason'] is None:
1276 intake['discontinue_reason'] = u'%s %s' % (_('not tolerated:'), _('discontinued due to allergy or intolerance'))
1277 else:
1278 if not intake['discontinue_reason'].startswith(_('not tolerated:')):
1279 intake['discontinue_reason'] = u'%s %s' % (_('not tolerated:'), intake['discontinue_reason'])
1280 if not intake.save():
1281 return False
1282
1283 allg = intake.turn_into_allergy(encounter_id = emr.active_encounter['pk_encounter'])
1284
1285 brand = intake.containing_drug
1286 if brand is not None:
1287 comps = [ c['substance'] for c in brand.components ]
1288 if len(comps) > 1:
1289 gmGuiHelpers.gm_show_info (
1290 aTitle = _(u'Documented an allergy'),
1291 aMessage = _(
1292 u'An allergy was documented against the substance:\n'
1293 u'\n'
1294 u' [%s]\n'
1295 u'\n'
1296 u'This substance was taken with the multi-component brand:\n'
1297 u'\n'
1298 u' [%s (%s)]\n'
1299 u'\n'
1300 u'Note that ALL components of this brand were discontinued.'
1301 ) % (
1302 intake['substance'],
1303 intake['brand'],
1304 u' & '.join(comps)
1305 )
1306 )
1307
1308 if parent is None:
1309 parent = wx.GetApp().GetTopWindow()
1310
1311 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent = parent, id = -1)
1312 dlg.ShowModal()
1313
1314 return True
1315
1316 #============================================================
1318
1319 if parent is None:
1320 parent = wx.GetApp().GetTopWindow()
1321
1322 if emr is None:
1323 emr = gmPerson.gmCurrentPatient().emr
1324 # #------------------------------------------------------------
1325 # def add_from_db(substance):
1326 # drug_db = get_drug_database(parent = parent)
1327 # if drug_db is None:
1328 # return False
1329 # drug_db.import_drugs()
1330 # return True
1331 # #------------------------------------------------------------
1332 # def edit(substance=None):
1333 # return edit_consumable_substance(parent = parent, substance = substance, single_entry = (substance is not None))
1334 # #------------------------------------------------------------
1335 # def delete(substance):
1336 # if substance.is_in_use_by_patients:
1337 # gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete this substance. It is in use.'), beep = True)
1338 # return False
1339 #
1340 # return gmMedication.delete_consumable_substance(substance = substance['pk'])
1341 #------------------------------------------------------------
1342 def get_tooltip(intake=None):
1343 return intake.format(one_line = False, show_all_brand_components = True)
1344 #------------------------------------------------------------
1345 def refresh(lctrl):
1346 intakes = emr.get_current_substance_intake (
1347 include_inactive = False,
1348 include_unapproved = True,
1349 order_by = u'substance, brand, started'
1350 )
1351 items = []
1352 for i in intakes:
1353 if i['started'] is None:
1354 started = u''
1355 else:
1356 started = u'%s:' % gmDateTime.pydt_strftime(i['started'], '%Y %b %d')
1357 items.append ([
1358 u'%s%s %s %s %s%s' % (
1359 i['substance'],
1360 gmTools.coalesce(i['brand'], u'', u' (%s)'),
1361 i['amount'],
1362 i['unit'],
1363 i['preparation'],
1364 gmTools.coalesce(i['external_code_brand'], u'', u' [%s::%s]' % (i['external_code_type_brand'], i['external_code_brand']))
1365 ),
1366 u'%s%s%s' % (
1367 started,
1368 gmTools.coalesce(i['schedule'], u'', u' %%s %s' % gmTools.u_right_arrow),
1369 gmTools.coalesce(i['duration'], u'', u' %s')
1370 ),
1371 u'%s' % (
1372 gmTools.bool2subst (
1373 i['intake_is_approved_of'],
1374 u'',
1375 _('disapproved')
1376 )
1377 )
1378 ])
1379 lctrl.set_string_items(items)
1380 lctrl.set_data(intakes)
1381 #------------------------------------------------------------
1382 msg = _('Substances consumed by the patient:')
1383
1384 return gmListWidgets.get_choices_from_list (
1385 parent = parent,
1386 msg = msg,
1387 caption = _('Showing consumable substances.'),
1388 columns = [ _('Intake'), _('Application'), _('Status') ],
1389 single_selection = False,
1390 # new_callback = edit,
1391 # edit_callback = edit,
1392 # delete_callback = delete,
1393 refresh_callback = refresh,
1394 list_tooltip_callback = get_tooltip
1395 # ,left_extra_button = (_('Import'), _('Import consumable substances from a drug database.'), add_from_db)
1396 )
1397
1398 #============================================================
1399 from Gnumed.wxGladeWidgets import wxgCurrentMedicationEAPnl
1400
1401 -class cSubstanceIntakeEAPnl(wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl, gmEditArea.cGenericEditAreaMixin):
1402
1404
1405 try:
1406 data = kwargs['substance']
1407 del kwargs['substance']
1408 except KeyError:
1409 data = None
1410
1411 self.calc = gmClinicalCalculator.cClinicalCalculator()
1412
1413 wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl.__init__(self, *args, **kwargs)
1414 gmEditArea.cGenericEditAreaMixin.__init__(self)
1415
1416 self.mode = 'new'
1417 self.data = data
1418 if data is not None:
1419 self.mode = 'edit'
1420
1421 self.__init_ui()
1422 #----------------------------------------------------------------
1424
1425 self._PRW_component.add_callback_on_lose_focus(callback = self._on_leave_component)
1426 self._PRW_component.selection_only = True
1427
1428 self._PRW_substance.add_callback_on_lose_focus(callback = self._on_leave_substance)
1429 self._PRW_substance.selection_only = True
1430
1431 self._PRW_duration.display_accuracy = gmDateTime.acc_days
1432
1433 self._PRW_aim.add_callback_on_set_focus(callback = self._on_enter_aim)
1434 #----------------------------------------------------------------
1436 curr_pat = gmPerson.gmCurrentPatient()
1437 emr = curr_pat.emr
1438
1439 state = emr.allergy_state
1440 if state['last_confirmed'] is None:
1441 confirmed = _('never')
1442 else:
1443 confirmed = gmDateTime.pydt_strftime(state['last_confirmed'], '%Y %b %d')
1444 msg = _(u'%s, last confirmed %s\n') % (state.state_string, confirmed)
1445 msg += gmTools.coalesce(state['comment'], u'', _('Comment (%s): %%s\n') % state['modified_by'])
1446
1447 tt = u''
1448
1449 allgs = emr.get_allergies()
1450 if len(allgs) > 0:
1451 msg += u'\n'
1452 for allergy in allgs:
1453 msg += u'%s: %s (%s)\n' % (
1454 allergy['descriptor'],
1455 allergy['l10n_type'],
1456 gmTools.bool2subst(allergy['definite'], _('definite'), _('suspected'), u'?')
1457 )
1458 tt += u'%s: %s\n' % (
1459 allergy['descriptor'],
1460 gmTools.coalesce(allergy['reaction'], _('reaction not recorded'))
1461 )
1462
1463 if len(allgs) > 0:
1464 msg += u'\n'
1465 tt += u'\n'
1466
1467 gfr = emr.get_most_recent_results(loinc = gmLOINC.LOINC_gfr_quantity, no_of_results = 1)
1468 if gfr is None:
1469 self.calc.patient = curr_pat
1470 gfr = self.calc.eGFR
1471 if gfr.numeric_value is None:
1472 msg += _('GFR: unknown')
1473 else:
1474 msg += gfr.message
1475 tt += gfr.format (
1476 left_margin = 0,
1477 width = 50,
1478 eol = u'\n',
1479 with_formula = True,
1480 with_warnings = True,
1481 with_variables = False,
1482 with_sub_results = True,
1483 return_list = False
1484 )
1485 else:
1486 msg += u'%s: %s %s (%s)\n' % (
1487 gfr['unified_abbrev'],
1488 gfr['unified_val'],
1489 gmTools.coalesce(gfr['abnormality_indicator'], u'', u' (%s)'),
1490 gmDateTime.pydt_strftime (
1491 gfr['clin_when'],
1492 format = '%Y %b %d'
1493 )
1494 )
1495 tt += _('GFR reported by path lab')
1496
1497 self._LBL_allergies.SetLabel(msg)
1498 self._LBL_allergies.SetToolTipString(tt)
1499 #----------------------------------------------------------------
1500 # generic Edit Area mixin API
1501 #----------------------------------------------------------------
1503
1504 validity = True
1505
1506 has_component = (self._PRW_component.GetData() is not None)
1507 has_substance = (self._PRW_substance.GetValue().strip() != u'')
1508
1509 self._PRW_component.display_as_valid(True)
1510
1511 # cannot add duplicate components
1512 if self.mode == 'new':
1513 msg = _(
1514 'The patient is already taking\n'
1515 '\n'
1516 ' %s\n'
1517 '\n'
1518 'You will want to adjust the schedule\n'
1519 'rather than document the intake twice.'
1520 )
1521 title = _('Adding substance intake entry')
1522 if has_component:
1523 emr = gmPerson.gmCurrentPatient().get_emr()
1524 if emr.substance_intake_exists(pk_component = self._PRW_component.GetData()):
1525 gmGuiHelpers.gm_show_warning (
1526 aTitle = title,
1527 aMessage = msg % self._PRW_component.GetValue().strip()
1528 )
1529 self._PRW_component.display_as_valid(False)
1530 validity = False
1531 pk_substance = self._PRW_substance.GetData()
1532 if pk_substance is not None:
1533 emr = gmPerson.gmCurrentPatient().get_emr()
1534 if emr.substance_intake_exists(pk_substance = pk_substance):
1535 gmGuiHelpers.gm_show_warning (
1536 aTitle = title,
1537 aMessage = msg % self._PRW_substance.GetValue().strip()
1538 )
1539 self._PRW_substance.display_as_valid(False)
1540 validity = False
1541
1542 # must have either brand or substance
1543 if (has_component is False) and (has_substance is False):
1544 self._PRW_substance.display_as_valid(False)
1545 self._PRW_component.display_as_valid(False)
1546 validity = False
1547 else:
1548 self._PRW_substance.display_as_valid(True)
1549
1550 # brands already have a preparation, so only required for substances
1551 if not has_component:
1552 if self._PRW_preparation.GetValue().strip() == u'':
1553 self._PRW_preparation.display_as_valid(False)
1554 validity = False
1555 else:
1556 self._PRW_preparation.display_as_valid(True)
1557
1558 # episode must be set if intake is to be approved of
1559 if self._CHBOX_approved.IsChecked():
1560 if self._PRW_episode.GetValue().strip() == u'':
1561 self._PRW_episode.display_as_valid(False)
1562 validity = False
1563 else:
1564 self._PRW_episode.display_as_valid(True)
1565
1566 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
1567 self._PRW_duration.display_as_valid(True)
1568 else:
1569 if self._PRW_duration.GetData() is None:
1570 # no data ...
1571 if gmDateTime.str2interval(self._PRW_duration.GetValue()) is None:
1572 self._PRW_duration.display_as_valid(False)
1573 validity = False
1574 # ... but valid string
1575 else:
1576 self._PRW_duration.display_as_valid(True)
1577 # has data
1578 else:
1579 self._PRW_duration.display_as_valid(True)
1580
1581 # end must be "< now()" AND "> start" if at all
1582 end = self._DP_discontinued.GetData()
1583 if end is not None:
1584 if end > gmDateTime.pydt_now_here():
1585 self._DP_discontinued.display_as_valid(False)
1586 validity = False
1587 else:
1588 start = self._DP_started.GetData()
1589 if start > end:
1590 self._DP_started.display_as_valid(False)
1591 self._DP_discontinued.display_as_valid(False)
1592 validity = False
1593 else:
1594 self._DP_started.display_as_valid(True)
1595 self._DP_discontinued.display_as_valid(True)
1596
1597 if validity is False:
1598 gmDispatcher.send(signal = 'statustext', msg = _('Input incomplete/invalid for saving as substance intake.'))
1599
1600 return validity
1601 #----------------------------------------------------------------
1603
1604 emr = gmPerson.gmCurrentPatient().get_emr()
1605 epi = self._PRW_episode.GetData(can_create = True)
1606
1607 if self._PRW_substance.GetData() is None:
1608 # auto-creates all components as intakes
1609 intake = emr.add_substance_intake (
1610 pk_component = self._PRW_component.GetData(),
1611 episode = epi
1612 )
1613 else:
1614 intake = emr.add_substance_intake (
1615 pk_substance = self._PRW_substance.GetData(),
1616 episode = epi,
1617 preparation = self._PRW_preparation.GetValue().strip()
1618 )
1619
1620 if intake is None:
1621 gmDispatcher.send('statustext', msg = _('Cannot add duplicate of (maybe inactive) substance intake.'), beep = True)
1622 return False
1623
1624 intake['started'] = self._DP_started.GetData()
1625 intake['discontinued'] = self._DP_discontinued.GetData()
1626 if intake['discontinued'] is None:
1627 intake['discontinue_reason'] = None
1628 else:
1629 intake['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
1630 intake['schedule'] = self._PRW_schedule.GetValue().strip()
1631 intake['aim'] = self._PRW_aim.GetValue().strip()
1632 intake['notes'] = self._PRW_notes.GetValue().strip()
1633 intake['is_long_term'] = self._CHBOX_long_term.IsChecked()
1634 intake['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
1635 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
1636 intake['duration'] = None
1637 else:
1638 if self._PRW_duration.GetData() is None:
1639 intake['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
1640 else:
1641 intake['duration'] = self._PRW_duration.GetData()
1642 intake.save()
1643
1644 self.data = intake
1645
1646 return True
1647 #----------------------------------------------------------------
1649
1650 # auto-applies to all components of drug if any:
1651 self.data['started'] = self._DP_started.GetData()
1652 self.data['discontinued'] = self._DP_discontinued.GetData()
1653 if self.data['discontinued'] is None:
1654 self.data['discontinue_reason'] = None
1655 else:
1656 self.data['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
1657 self.data['schedule'] = self._PRW_schedule.GetValue()
1658 self.data['is_long_term'] = self._CHBOX_long_term.IsChecked()
1659 self.data['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
1660 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
1661 self.data['duration'] = None
1662 else:
1663 # self.data['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
1664 if self._PRW_duration.GetData() is None:
1665 self.data['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
1666 else:
1667 self.data['duration'] = self._PRW_duration.GetData()
1668
1669 # applies to non-component substances only
1670 self.data['preparation'] = self._PRW_preparation.GetValue()
1671
1672 # per-component
1673 self.data['aim'] = self._PRW_aim.GetValue()
1674 self.data['notes'] = self._PRW_notes.GetValue()
1675 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True)
1676
1677 self.data.save()
1678
1679 return True
1680 #----------------------------------------------------------------
1682 self._PRW_component.SetText(u'', None)
1683 self._LBL_component.Enable(True)
1684 self._PRW_component.Enable(True)
1685 self._TCTRL_brand_ingredients.SetValue(u'')
1686 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1687
1688 self._LBL_or.Enable(True)
1689
1690 self._PRW_substance.SetText(u'', None)
1691 self._PRW_substance.Enable(True)
1692
1693 self._PRW_preparation.SetText(u'', None)
1694 self._PRW_preparation.Enable(True)
1695
1696 self._PRW_schedule.SetText(u'', None)
1697 self._PRW_duration.SetText(u'', None)
1698 self._PRW_aim.SetText(u'', None)
1699 self._PRW_notes.SetText(u'', None)
1700 self._PRW_episode.SetText(u'', None)
1701
1702 self._CHBOX_long_term.SetValue(False)
1703 self._CHBOX_approved.SetValue(True)
1704
1705 self._DP_started.SetData(gmDateTime.pydt_now_here())
1706 self._DP_discontinued.SetData(None)
1707 self._PRW_discontinue_reason.SetValue(u'')
1708
1709 self.__refresh_allergies()
1710
1711 self._PRW_component.SetFocus()
1712 #----------------------------------------------------------------
1714
1715 self._TCTRL_brand_ingredients.SetValue(u'')
1716 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1717
1718 if self.data['pk_brand'] is None:
1719 self.__refresh_from_existing_substance()
1720 else:
1721 self.__refresh_from_existing_component()
1722
1723 # no editing of substance or component
1724 self._LBL_component.Enable(False)
1725 self._PRW_component.Enable(False)
1726 self._LBL_or.Enable(False)
1727 self._PRW_substance.Enable(False)
1728
1729 if self.data['is_long_term']:
1730 self._CHBOX_long_term.SetValue(True)
1731 self._PRW_duration.Enable(False)
1732 self._PRW_duration.SetText(gmTools.u_infinity, None)
1733 self._BTN_discontinued_as_planned.Enable(False)
1734 else:
1735 self._CHBOX_long_term.SetValue(False)
1736 self._PRW_duration.Enable(True)
1737 self._BTN_discontinued_as_planned.Enable(True)
1738 self._PRW_duration.SetData(self.data['duration'])
1739 # if self.data['duration'] is None:
1740 # self._PRW_duration.SetText(u'', None)
1741 # else:
1742 # self._PRW_duration.SetText(gmDateTime.format_interval(self.data['duration'], gmDateTime.acc_days), self.data['duration'])
1743 self._PRW_aim.SetText(gmTools.coalesce(self.data['aim'], u''), self.data['aim'])
1744 self._PRW_notes.SetText(gmTools.coalesce(self.data['notes'], u''), self.data['notes'])
1745 self._PRW_episode.SetData(self.data['pk_episode'])
1746 self._PRW_schedule.SetText(gmTools.coalesce(self.data['schedule'], u''), self.data['schedule'])
1747
1748 self._CHBOX_approved.SetValue(self.data['intake_is_approved_of'])
1749
1750 self._DP_started.SetData(self.data['started'])
1751 self._DP_discontinued.SetData(self.data['discontinued'])
1752 self._PRW_discontinue_reason.SetValue(gmTools.coalesce(self.data['discontinue_reason'], u''))
1753 if self.data['discontinued'] is not None:
1754 self._PRW_discontinue_reason.Enable()
1755
1756 self.__refresh_allergies()
1757
1758 self._PRW_schedule.SetFocus()
1759 #----------------------------------------------------------------
1761 self._LBL_component.Enable(False)
1762 self._PRW_component.Enable(False)
1763 self._PRW_component.SetText(u'', None)
1764 self._PRW_component.display_as_valid(True)
1765
1766 self._LBL_or.Enable(False)
1767
1768 self._PRW_substance.Enable(True)
1769 self._PRW_substance.SetText (
1770 u'%s %s %s' % (self.data['substance'], self.data['amount'], self.data['unit']),
1771 self.data['pk_substance']
1772 )
1773
1774 # self._PRW_preparation.SetText(gmTools.coalesce(self.data['preparation'], u''), self.data['preparation'])
1775 self._PRW_preparation.SetText(self.data['preparation'], self.data['preparation'])
1776 self._PRW_preparation.Enable(True)
1777 #----------------------------------------------------------------
1779 self._LBL_component.Enable(True)
1780 self._PRW_component.Enable(True)
1781 self._PRW_component.SetText (
1782 u'%s %s %s (%s)' % (self.data['substance'], self.data['amount'], self.data['unit'], self.data['brand']),
1783 self.data['pk_drug_component']
1784 )
1785
1786 brand = gmMedication.cBrandedDrug(aPK_obj = self.data['pk_brand'])
1787 if brand['components'] is not None:
1788 self._TCTRL_brand_ingredients.SetValue(u'; '.join(brand['components']))
1789 tt = u'%s:\n\n- %s' % (
1790 self.data['brand'],
1791 u'\n- '.join(brand['components'])
1792 )
1793 self._TCTRL_brand_ingredients.SetToolTipString(tt)
1794
1795 self._LBL_or.Enable(False)
1796 self._LBL_substance.Enable(False)
1797 self._PRW_substance.SetText(u'', None)
1798 self._PRW_substance.display_as_valid(True)
1799
1800 self._PRW_preparation.SetText(self.data['preparation'], self.data['preparation'])
1801 self._PRW_preparation.Enable(False)
1802 #----------------------------------------------------------------
1804 self._refresh_as_new()
1805
1806 self._PRW_episode.SetData(self.data['pk_episode'])
1807
1808 self._PRW_component.SetFocus()
1809 #----------------------------------------------------------------
1810 # event handlers
1811 #----------------------------------------------------------------
1813 if self._PRW_component.GetData() is None:
1814 self._LBL_or.Enable(True)
1815 self._PRW_component.SetText(u'', None)
1816 self._LBL_substance.Enable(True)
1817 self._PRW_substance.Enable(True)
1818 self._LBL_preparation.Enable(True)
1819 self._PRW_preparation.Enable(True)
1820 #self._PRW_preparation.SetText(u'', None)
1821 self._TCTRL_brand_ingredients.SetValue(u'')
1822 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1823 else:
1824 self._LBL_or.Enable(False)
1825 self._LBL_substance.Enable(False)
1826 self._PRW_substance.SetText(u'', None)
1827 self._PRW_substance.display_as_valid(True)
1828 self._PRW_substance.Enable(False)
1829 self._LBL_preparation.Enable(False)
1830 self._PRW_preparation.Enable(False)
1831 comp = gmMedication.cDrugComponent(aPK_obj = self._PRW_component.GetData())
1832 self._PRW_preparation.SetText(comp['preparation'], comp['preparation'])
1833 brand = comp.containing_drug
1834 if brand['components'] is not None:
1835 self._TCTRL_brand_ingredients.SetValue(u'; '.join(brand['components']))
1836 tt = u'%s:\n\n- %s' % (
1837 brand['brand'],
1838 u'\n- '.join(brand['components'])
1839 )
1840 self._TCTRL_brand_ingredients.SetToolTipString(tt)
1841 #----------------------------------------------------------------
1843 if self._PRW_substance.GetData() is None:
1844 self._LBL_or.Enable(True)
1845 self._LBL_component.Enable(True)
1846 self._PRW_component.Enable(True)
1847 self._PRW_substance.SetText(u'', None)
1848 else:
1849 self._LBL_or.Enable(False)
1850 self._LBL_component.Enable(False)
1851 self._PRW_component.SetText(u'', None)
1852 self._PRW_component.display_as_valid(True)
1853 self._PRW_component.Enable(False)
1854 self._LBL_preparation.Enable(True)
1855 self._PRW_preparation.Enable(True)
1856 self._TCTRL_brand_ingredients.SetValue(u'')
1857 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1858 #----------------------------------------------------------------
1860 # when a drug component/substance is selected (that is, when .GetData()
1861 # returns not None) then we do not want to use the GetValue().strip()
1862 # result because that will also have amount and unit appended, hence
1863 # create the real component or substance instance and take the canonical
1864 # substance name from there
1865 subst = self._PRW_component.GetValue().strip()
1866 if subst != u'':
1867 comp = self._PRW_component.GetData(as_instance = True)
1868 if comp is None:
1869 self._PRW_aim.set_context(context = u'substance', val = subst)
1870 return
1871 self._PRW_aim.set_context(context = u'substance', val = comp['substance'])
1872 return
1873
1874 subst = self._PRW_substance.GetValue().strip()
1875 if subst == u'':
1876 self._PRW_aim.unset_context(context = u'substance')
1877 return
1878 comp = self._PRW_substance.GetData(as_instance = True)
1879 if comp is None:
1880 self._PRW_aim.set_context(context = u'substance', val = subst)
1881 return
1882 self._PRW_aim.set_context(context = u'substance', val = comp['description'])
1883 #----------------------------------------------------------------
1885 if self._DP_discontinued.GetData() is None:
1886 self._PRW_discontinue_reason.Enable(False)
1887 else:
1888 self._PRW_discontinue_reason.Enable(True)
1889 #----------------------------------------------------------------
1892 #----------------------------------------------------------------
1895 #----------------------------------------------------------------
1898 #----------------------------------------------------------------
1910 #----------------------------------------------------------------
1940 #----------------------------------------------------------------
1942 if self._CHBOX_long_term.IsChecked() is True:
1943 self._PRW_duration.Enable(False)
1944 self._BTN_discontinued_as_planned.Enable(False)
1945 self._PRW_discontinue_reason.Enable(False)
1946 else:
1947 self._PRW_duration.Enable(True)
1948 self._BTN_discontinued_as_planned.Enable(True)
1949 self._PRW_discontinue_reason.Enable(True)
1950
1951 self.__refresh_allergies()
1952 #----------------------------------------------------------------
1954 if not self.save():
1955 return False
1956
1957 return turn_substance_intake_into_allergy (
1958 parent = self,
1959 intake = self.data,
1960 emr = gmPerson.gmCurrentPatient().get_emr()
1961 )
1962
1963 #============================================================
1965
1966 subst = gmMedication.cSubstanceIntakeEntry(aPK_obj = substance)
1967 msg = _(
1968 '\n'
1969 '[%s]\n'
1970 '\n'
1971 'It may be prudent to edit (before deletion) the details\n'
1972 'of this substance intake entry so as to leave behind\n'
1973 'some indication of why it was deleted.\n'
1974 ) % subst.format()
1975
1976 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
1977 parent,
1978 -1,
1979 caption = _('Deleting medication / substance intake'),
1980 question = msg,
1981 button_defs = [
1982 {'label': _('&Edit'), 'tooltip': _('Allow editing of substance intake entry before deletion.'), 'default': True},
1983 {'label': _('&Delete'), 'tooltip': _('Delete immediately without editing first.')},
1984 {'label': _('&Cancel'), 'tooltip': _('Abort. Do not delete or edit substance intake entry.')}
1985 ]
1986 )
1987
1988 edit_first = dlg.ShowModal()
1989 dlg.Destroy()
1990
1991 if edit_first == wx.ID_CANCEL:
1992 return
1993
1994 if edit_first == wx.ID_YES:
1995 edit_intake_of_substance(parent = parent, substance = subst)
1996 delete_it = gmGuiHelpers.gm_show_question (
1997 aMessage = _('Now delete substance intake entry ?'),
1998 aTitle = _('Deleting medication / substance intake')
1999 )
2000 else:
2001 delete_it = True
2002
2003 if not delete_it:
2004 return
2005
2006 gmMedication.delete_substance_intake(substance = substance)
2007 #------------------------------------------------------------
2009 ea = cSubstanceIntakeEAPnl(parent = parent, id = -1, substance = substance)
2010 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = (substance is not None))
2011 dlg.SetTitle(gmTools.coalesce(substance, _('Adding medication/non-medication substance intake'), _('Editing medication/non-medication substance intake')))
2012 dlg.left_extra_button = (
2013 _('Allergy'),
2014 _('Document an allergy against this substance.'),
2015 ea.turn_into_allergy
2016 )
2017 if dlg.ShowModal() == wx.ID_OK:
2018 dlg.Destroy()
2019 return True
2020 dlg.Destroy()
2021 return False
2022
2023 #============================================================
2024 # current substances grid
2025 #------------------------------------------------------------
2027
2028 if parent is None:
2029 parent = wx.GetApp().GetTopWindow()
2030
2031 template = gmFormWidgets.manage_form_templates (
2032 parent = parent,
2033 template_types = ['current medication list']
2034 )
2035 option = u'form_templates.medication_list'
2036
2037 if template is None:
2038 gmDispatcher.send(signal = 'statustext', msg = _('No medication list template configured.'), beep = True)
2039 return None
2040
2041 if template['engine'] not in [u'L', u'X', u'T']:
2042 gmDispatcher.send(signal = 'statustext', msg = _('No medication list template configured.'), beep = True)
2043 return None
2044
2045 dbcfg = gmCfg.cCfgSQL()
2046 dbcfg.set (
2047 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
2048 option = option,
2049 value = u'%s - %s' % (template['name_long'], template['external_version'])
2050 )
2051
2052 return template
2053 #------------------------------------------------------------
2055
2056 if parent is None:
2057 parent = wx.GetApp().GetTopWindow()
2058
2059 # 1) get template
2060 dbcfg = gmCfg.cCfgSQL()
2061 option = u'form_templates.medication_list'
2062
2063 template = dbcfg.get2 (
2064 option = option,
2065 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
2066 bias = 'user'
2067 )
2068
2069 if template is None:
2070 template = configure_medication_list_template(parent = parent)
2071 if template is None:
2072 gmGuiHelpers.gm_show_error (
2073 aMessage = _('There is no medication list template configured.'),
2074 aTitle = _('Printing medication list')
2075 )
2076 return False
2077 else:
2078 try:
2079 name, ver = template.split(u' - ')
2080 except:
2081 _log.exception('problem splitting medication list template name [%s]', template)
2082 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading medication list template.'), beep = True)
2083 return False
2084 template = gmForms.get_form_template(name_long = name, external_version = ver)
2085 if template is None:
2086 gmGuiHelpers.gm_show_error (
2087 aMessage = _('Cannot load medication list template [%s - %s]') % (name, ver),
2088 aTitle = _('Printing medication list')
2089 )
2090 return False
2091
2092 # 2) process template
2093 try:
2094 meds_list = template.instantiate()
2095 except KeyError:
2096 _log.exception('cannot instantiate medication list template [%s]', template)
2097 gmGuiHelpers.gm_show_error (
2098 aMessage = _('Invalid medication list template [%s - %s (%s)]') % (name, ver, template['engine']),
2099 aTitle = _('Printing medication list')
2100 )
2101 return False
2102
2103 ph = gmMacro.gmPlaceholderHandler()
2104 #ph.debug = True
2105 meds_list.substitute_placeholders(data_source = ph)
2106 pdf_name = meds_list.generate_output()
2107 if pdf_name is None:
2108 gmGuiHelpers.gm_show_error (
2109 aMessage = _('Error generating the medication list.'),
2110 aTitle = _('Printing medication list')
2111 )
2112 return False
2113
2114 # 3) print template
2115 printed = gmPrinting.print_files(filenames = [pdf_name], jobtype = 'medication_list')
2116 if not printed:
2117 gmGuiHelpers.gm_show_error (
2118 aMessage = _('Error printing the medication list.'),
2119 aTitle = _('Printing medication list')
2120 )
2121 return False
2122
2123 # 4) keep notice on printing
2124 pat = gmPerson.gmCurrentPatient()
2125 emr = pat.get_emr()
2126 epi = emr.add_episode (
2127 episode_name = gmMedication.DEFAULT_MEDICATION_HISTORY_EPISODE,
2128 is_open = False
2129 )
2130 narr = emr.add_clin_narrative (
2131 soap_cat = None,
2132 note = _('medication list printed from template [%s - %s]') % (template['name_long'], template['external_version']),
2133 episode = epi
2134 )
2135
2136 # 5) keep a copy
2137 doc = gmDocumentWidgets.save_files_as_new_document (
2138 parent = parent,
2139 filenames = [pdf_name],
2140 document_type = template['instance_type'],
2141 episode = epi,
2142 review_as_normal = True
2143 )
2144
2145 # 6) link note and document
2146 # narr[pk_document] = doc[pk]
2147
2148 return True
2149 #------------------------------------------------------------
2151
2152 if parent is None:
2153 parent = wx.GetApp().GetTopWindow()
2154
2155 template = gmFormWidgets.manage_form_templates (
2156 parent = parent,
2157 msg = _('Select the default prescription template:'),
2158 template_types = ['prescription', 'current medication list']
2159 )
2160
2161 if template is None:
2162 gmDispatcher.send(signal = 'statustext', msg = _('No prescription template configured.'), beep = True)
2163 return None
2164
2165 if template['engine'] not in [u'L', u'X', u'T']:
2166 gmDispatcher.send(signal = 'statustext', msg = _('No prescription template configured.'), beep = True)
2167 return None
2168
2169 option = u'form_templates.prescription'
2170 dbcfg = gmCfg.cCfgSQL()
2171 dbcfg.set (
2172 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
2173 option = option,
2174 value = u'%s - %s' % (template['name_long'], template['external_version'])
2175 )
2176
2177 return template
2178 #------------------------------------------------------------
2180
2181 if parent is None:
2182 parent = wx.GetApp().GetTopWindow()
2183
2184 dbcfg = gmCfg.cCfgSQL()
2185 option = u'form_templates.prescription'
2186 template_name = dbcfg.get2 (
2187 option = option,
2188 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
2189 bias = 'user'
2190 )
2191
2192 if template_name is None:
2193 template = configure_prescription_template(parent = parent)
2194 if template is None:
2195 gmGuiHelpers.gm_show_error (
2196 aMessage = _('There is no prescription template configured.'),
2197 aTitle = _('Printing prescription')
2198 )
2199 return None
2200 return template
2201
2202 try:
2203 name, ver = template_name.split(u' - ')
2204 except:
2205 _log.exception('problem splitting prescription template name [%s]', template_name)
2206 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading prescription template.'), beep = True)
2207 return False
2208 template = gmForms.get_form_template(name_long = name, external_version = ver)
2209 if template is None:
2210 gmGuiHelpers.gm_show_error (
2211 aMessage = _('Cannot load prescription template [%s - %s]') % (name, ver),
2212 aTitle = _('Printing prescription')
2213 )
2214 return False
2215 return template
2216 #------------------------------------------------------------
2218
2219 # 1) get template
2220 rx_template = get_prescription_template(parent = parent)
2221 if rx_template is None:
2222 return False
2223
2224 # 2) process template
2225 try:
2226 rx = rx_template.instantiate()
2227 except KeyError:
2228 _log.exception('cannot instantiate prescription template [%s]', rx_template)
2229 gmGuiHelpers.gm_show_error (
2230 aMessage = _('Invalid prescription template [%s - %s (%s)]') % (rx_template['name_long'], rx_template['external_version'], rx_template['engine']),
2231 aTitle = _('Printing prescription list')
2232 )
2233 return False
2234 ph = gmMacro.gmPlaceholderHandler()
2235 #ph.debug = True
2236 rx.substitute_placeholders(data_source = ph)
2237 pdf_name = rx.generate_output()
2238 if pdf_name is None:
2239 gmGuiHelpers.gm_show_error (
2240 aMessage = _('Error generating the prescription printout.'),
2241 aTitle = _('Printing prescription')
2242 )
2243 return False
2244
2245 # 3) print template
2246 printed = gmPrinting.print_files(filenames = [pdf_name], jobtype = 'prescription')
2247 if not printed:
2248 gmGuiHelpers.gm_show_error (
2249 aMessage = _('Error printing the prescription.'),
2250 aTitle = _('Printing prescription')
2251 )
2252 return False
2253
2254 epi = emr.add_episode (
2255 episode_name = gmMedication.DEFAULT_MEDICATION_HISTORY_EPISODE,
2256 is_open = False
2257 )
2258
2259 # 4) keep a copy
2260 files2import = []
2261 files2import.extend(rx.final_output_filenames)
2262 files2import.extend(rx.re_editable_filenames)
2263 doc = gmDocumentWidgets.save_files_as_new_document (
2264 parent = parent,
2265 filenames = files2import,
2266 document_type = u'prescription',
2267 #document_type = rx_template['instance_type'],
2268 #document_type = gmMedication.DOCUMENT_TYPE_PRESCRIPTION,
2269 #document_type = gmDocuments.create_document_type(document_type = gmDocuments.DOCUMENT_TYPE_PRESCRIPTION)['pk_doc_type'],
2270 episode = epi,
2271 review_as_normal = True
2272 )
2273
2274 # 5) keep notice on printing
2275 emr.add_clin_narrative (
2276 soap_cat = None,
2277 note = _('prescription printed from template [%s - %s]') % (rx_template['name_long'], rx_template['external_version']),
2278 episode = epi
2279 )
2280
2281 return True
2282
2283 #------------------------------------------------------------
2285
2286 dbcfg = gmCfg.cCfgSQL()
2287 rx_mode = dbcfg.get2 (
2288 option = u'horst_space.default_prescription_mode',
2289 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
2290 bias = u'user',
2291 default = u'form' # set to 'database' to access database
2292 )
2293
2294 if parent is None:
2295 parent = wx.GetApp().GetTopWindow()
2296
2297 if rx_mode == 'form':
2298 return print_prescription(parent = parent, emr = emr)
2299
2300 if rx_mode == 'database':
2301 drug_db = get_drug_database()
2302 if drug_db is None:
2303 return
2304 drug_db.reviewer = gmStaff.gmCurrentProvider()
2305 prescribed_drugs = drug_db.prescribe()
2306 update_substance_intake_list_from_prescription (
2307 parent = parent,
2308 prescribed_drugs = prescribed_drugs,
2309 emr = emr
2310 )
2311
2312 #------------------------------------------------------------
2313 -def update_substance_intake_list_from_prescription(parent=None, prescribed_drugs=None, emr=None):
2314
2315 if len(prescribed_drugs) == 0:
2316 return
2317
2318 curr_brands = [ i['pk_brand'] for i in emr.get_current_substance_intake() if i['pk_brand'] is not None ]
2319 new_drugs = []
2320 for drug in prescribed_drugs:
2321 if drug['pk_brand'] not in curr_brands:
2322 new_drugs.append(drug)
2323
2324 if len(new_drugs) == 0:
2325 return
2326
2327 if parent is None:
2328 parent = wx.GetApp().GetTopWindow()
2329
2330 dlg = gmListWidgets.cItemPickerDlg (
2331 parent,
2332 -1,
2333 msg = _(
2334 'These brands have been prescribed but are not listed\n'
2335 'in the current medication list of this patient.\n'
2336 '\n'
2337 'Please select those you want added to the medication list.'
2338 )
2339 )
2340 dlg.set_columns (
2341 columns = [_('Newly prescribed drugs')],
2342 columns_right = [_('Add to medication list')]
2343 )
2344 choices = [ (u'%s %s (%s)' % (d['brand'], d['preparation'], u'; '.join(d['components']))) for d in new_drugs ]
2345 dlg.set_choices (
2346 choices = choices,
2347 data = new_drugs
2348 )
2349 dlg.ShowModal()
2350 drugs2add = dlg.get_picks()
2351 dlg.Destroy()
2352
2353 if drugs2add is None:
2354 return
2355
2356 if len(drugs2add) == 0:
2357 return
2358
2359 for drug in drugs2add:
2360 # only add first component since all other components get added by a trigger ...
2361 intake = emr.add_substance_intake (
2362 pk_component = drug['pk_components'][0],
2363 episode = emr.add_episode(episode_name = gmMedication.DEFAULT_MEDICATION_HISTORY_EPISODE)['pk_episode'],
2364 )
2365 if intake is None:
2366 continue
2367 intake['intake_is_approved_of'] = True
2368 intake.save()
2369
2370 return
2371 #------------------------------------------------------------
2373 """A grid class for displaying current substance intake.
2374
2375 - does NOT listen to the currently active patient
2376 - thereby it can display any patient at any time
2377 """
2379
2380 wx.grid.Grid.__init__(self, *args, **kwargs)
2381
2382 self.__patient = None
2383 self.__row_data = {}
2384 self.__prev_row = None
2385 self.__prev_tooltip_row = None
2386 self.__prev_cell_0 = None
2387 self.__grouping_mode = u'issue'
2388 self.__filter_show_unapproved = True
2389 self.__filter_show_inactive = True
2390
2391 self.__grouping2col_labels = {
2392 u'issue': [
2393 _('Health issue'),
2394 _('Substance'),
2395 _('Strength'),
2396 _('Schedule'),
2397 _('Started'),
2398 _('Duration / Until'),
2399 _('Brand'),
2400 _('Advice')
2401 ],
2402 u'brand': [
2403 _('Brand'),
2404 _('Schedule'),
2405 _('Substance'),
2406 _('Strength'),
2407 _('Started'),
2408 _('Duration / Until'),
2409 _('Health issue'),
2410 _('Advice')
2411 ],
2412 u'episode': [
2413 _('Episode'),
2414 _('Substance'),
2415 _('Strength'),
2416 _('Schedule'),
2417 _('Started'),
2418 _('Duration / Until'),
2419 _('Brand'),
2420 _('Advice')
2421 ]
2422 }
2423
2424 self.__grouping2order_by_clauses = {
2425 u'issue': u'pk_health_issue nulls first, substance, started',
2426 u'episode': u'pk_health_issue nulls first, episode, substance, started',
2427 u'brand': u'brand nulls last, substance, started'
2428 }
2429
2430 self.__init_ui()
2431 self.__register_events()
2432 #------------------------------------------------------------
2433 # external API
2434 #------------------------------------------------------------
2436
2437 sel_block_top_left = self.GetSelectionBlockTopLeft()
2438 sel_block_bottom_right = self.GetSelectionBlockBottomRight()
2439 sel_cols = self.GetSelectedCols()
2440 sel_rows = self.GetSelectedRows()
2441
2442 selected_cells = []
2443
2444 # individually selected cells (ctrl-click)
2445 selected_cells += self.GetSelectedCells()
2446
2447 # selected rows
2448 selected_cells += list (
2449 (row, col)
2450 for row in sel_rows
2451 for col in xrange(self.GetNumberCols())
2452 )
2453
2454 # selected columns
2455 selected_cells += list (
2456 (row, col)
2457 for row in xrange(self.GetNumberRows())
2458 for col in sel_cols
2459 )
2460
2461 # selection blocks
2462 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()):
2463 selected_cells += [
2464 (row, col)
2465 for row in xrange(top_left[0], bottom_right[0] + 1)
2466 for col in xrange(top_left[1], bottom_right[1] + 1)
2467 ]
2468
2469 return set(selected_cells)
2470 #------------------------------------------------------------
2472 rows = {}
2473
2474 for row, col in self.get_selected_cells():
2475 rows[row] = True
2476
2477 return rows.keys()
2478 #------------------------------------------------------------
2480 return [ self.__row_data[row] for row in self.get_selected_rows() ]
2481 #------------------------------------------------------------
2483
2484 self.empty_grid()
2485
2486 if self.__patient is None:
2487 return
2488
2489 emr = self.__patient.get_emr()
2490 meds = emr.get_current_substance_intake (
2491 order_by = self.__grouping2order_by_clauses[self.__grouping_mode],
2492 include_unapproved = self.__filter_show_unapproved,
2493 include_inactive = self.__filter_show_inactive
2494 )
2495 if not meds:
2496 return
2497
2498 self.BeginBatch()
2499
2500 # columns
2501 labels = self.__grouping2col_labels[self.__grouping_mode]
2502 if self.__filter_show_unapproved:
2503 self.AppendCols(numCols = len(labels) + 1)
2504 else:
2505 self.AppendCols(numCols = len(labels))
2506 for col_idx in range(len(labels)):
2507 self.SetColLabelValue(col_idx, labels[col_idx])
2508 if self.__filter_show_unapproved:
2509 self.SetColLabelValue(len(labels), u'OK?')
2510 self.SetColSize(len(labels), 40)
2511
2512 self.AppendRows(numRows = len(meds))
2513
2514 # loop over data
2515 for row_idx in range(len(meds)):
2516 med = meds[row_idx]
2517 self.__row_data[row_idx] = med
2518
2519 if med['is_currently_active'] is True:
2520 atcs = []
2521 if med['atc_substance'] is not None:
2522 atcs.append(med['atc_substance'])
2523 # if med['atc_brand'] is not None:
2524 # atcs.append(med['atc_brand'])
2525 # allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (med['substance'],), brand = med['brand'])
2526 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (med['substance'],))
2527 if allg not in [None, False]:
2528 attr = self.GetOrCreateCellAttr(row_idx, 0)
2529 if allg['type'] == u'allergy':
2530 attr.SetTextColour('red')
2531 else:
2532 attr.SetTextColour('yellow')
2533 self.SetRowAttr(row_idx, attr)
2534 else:
2535 attr = self.GetOrCreateCellAttr(row_idx, 0)
2536 attr.SetTextColour('grey')
2537 self.SetRowAttr(row_idx, attr)
2538
2539 if self.__grouping_mode == u'episode':
2540 if med['pk_episode'] is None:
2541 self.__prev_cell_0 = None
2542 epi = gmTools.u_diameter
2543 else:
2544 if self.__prev_cell_0 == med['episode']:
2545 epi = u''
2546 else:
2547 self.__prev_cell_0 = med['episode']
2548 epi = gmTools.coalesce(med['episode'], u'')
2549 self.SetCellValue(row_idx, 0, gmTools.wrap(text = epi, width = 40))
2550
2551 self.SetCellValue(row_idx, 1, med['substance'])
2552 self.SetCellValue(row_idx, 2, u'%s %s' % (med['amount'], med['unit']))
2553 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], u''))
2554 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
2555
2556 if med['is_long_term']:
2557 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
2558 else:
2559 if med['discontinued'] is None:
2560 if med['duration'] is None:
2561 self.SetCellValue(row_idx, 5, u'')
2562 else:
2563 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
2564 else:
2565 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d'))
2566
2567 if med['pk_brand'] is None:
2568 brand = med['preparation']
2569 else:
2570 if med['fake_brand']:
2571 brand = u'%s %s' % (
2572 gmTools.coalesce(med['brand'], u'', _('%s (fake)')),
2573 med['preparation']
2574 )
2575 else:
2576 brand = u'%s %s' % (
2577 gmTools.coalesce(med['brand'], u''),
2578 med['preparation']
2579 )
2580 self.SetCellValue(row_idx, 6, gmTools.wrap(text = brand, width = 35))
2581
2582 elif self.__grouping_mode == u'issue':
2583 if med['pk_health_issue'] is None:
2584 self.__prev_cell_0 = None
2585 issue = u'%s%s' % (
2586 gmTools.u_diameter,
2587 gmTools.coalesce(med['episode'], u'', u' (%s)')
2588 )
2589 else:
2590 if self.__prev_cell_0 == med['health_issue']:
2591 issue = u''
2592 else:
2593 self.__prev_cell_0 = med['health_issue']
2594 issue = med['health_issue']
2595 self.SetCellValue(row_idx, 0, gmTools.wrap(text = issue, width = 40))
2596
2597 self.SetCellValue(row_idx, 1, med['substance'])
2598 self.SetCellValue(row_idx, 2, u'%s %s' % (med['amount'], med['unit']))
2599 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], u''))
2600 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
2601
2602 if med['is_long_term']:
2603 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
2604 else:
2605 if med['discontinued'] is None:
2606 if med['duration'] is None:
2607 self.SetCellValue(row_idx, 5, u'')
2608 else:
2609 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
2610 else:
2611 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d'))
2612
2613 if med['pk_brand'] is None:
2614 brand = med['preparation']
2615 else:
2616 if med['fake_brand']:
2617 brand = u'%s %s' % (
2618 gmTools.coalesce(med['brand'], u'', _('%s (fake)')),
2619 med['preparation']
2620 )
2621 else:
2622 brand = u'%s %s' % (
2623 gmTools.coalesce(med['brand'], u''),
2624 med['preparation']
2625 )
2626 self.SetCellValue(row_idx, 6, gmTools.wrap(text = brand, width = 35))
2627
2628 elif self.__grouping_mode == u'brand':
2629
2630 if med['pk_brand'] is None:
2631 self.__prev_cell_0 = None
2632 brand = u'%s (%s)' % (
2633 gmTools.u_diameter,
2634 med['preparation']
2635 )
2636 else:
2637 if self.__prev_cell_0 == med['brand']:
2638 brand = u''
2639 else:
2640 self.__prev_cell_0 = med['brand']
2641 if med['fake_brand']:
2642 brand = u'%s %s' % (
2643 gmTools.coalesce(med['brand'], u'', _('%s (fake)')),
2644 med['preparation']
2645 )
2646 else:
2647 brand = u'%s %s' % (
2648 gmTools.coalesce(med['brand'], u''),
2649 med['preparation']
2650 )
2651 self.SetCellValue(row_idx, 0, gmTools.wrap(text = brand, width = 35))
2652
2653 self.SetCellValue(row_idx, 1, gmTools.coalesce(med['schedule'], u''))
2654 self.SetCellValue(row_idx, 2, med['substance'])
2655 self.SetCellValue(row_idx, 3, u'%s %s' % (med['amount'], med['unit']))
2656 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
2657
2658 if med['is_long_term']:
2659 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
2660 else:
2661 if med['discontinued'] is None:
2662 if med['duration'] is None:
2663 self.SetCellValue(row_idx, 5, u'')
2664 else:
2665 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
2666 else:
2667 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d'))
2668
2669 if med['pk_health_issue'] is None:
2670 issue = u'%s%s' % (
2671 gmTools.u_diameter,
2672 gmTools.coalesce(med['episode'], u'', u' (%s)')
2673 )
2674 else:
2675 issue = gmTools.coalesce(med['health_issue'], u'')
2676 self.SetCellValue(row_idx, 6, gmTools.wrap(text = issue, width = 40))
2677
2678 else:
2679 raise ValueError('unknown grouping mode [%s]' % self.__grouping_mode)
2680
2681 if med['notes'] is not None:
2682 self.SetCellValue(row_idx, 7, gmTools.wrap(text = med['notes'], width = 50))
2683
2684 if self.__filter_show_unapproved:
2685 self.SetCellValue (
2686 row_idx,
2687 len(labels),
2688 gmTools.bool2subst(med['intake_is_approved_of'], gmTools.u_checkmark_thin, u'', u'?')
2689 )
2690
2691 #self.SetCellAlignment(row, col, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
2692
2693 self.AutoSize()
2694 self.EndBatch()
2695 #------------------------------------------------------------
2697 self.BeginBatch()
2698 self.ClearGrid()
2699 # Windows cannot do "nothing", it rather decides to assert()
2700 # on thinking it is supposed to do nothing
2701 if self.GetNumberRows() > 0:
2702 self.DeleteRows(pos = 0, numRows = self.GetNumberRows())
2703 if self.GetNumberCols() > 0:
2704 self.DeleteCols(pos = 0, numCols = self.GetNumberCols())
2705 self.EndBatch()
2706 self.__row_data = {}
2707 self.__prev_cell_0 = None
2708 #------------------------------------------------------------
2710
2711 if len(self.__row_data) == 0:
2712 return
2713
2714 sel_rows = self.get_selected_rows()
2715 if len(sel_rows) != 1:
2716 return
2717
2718 drug_db = get_drug_database()
2719 if drug_db is None:
2720 return
2721
2722 intake = self.get_selected_data()[0] # just in case
2723 if intake['brand'] is None:
2724 drug_db.show_info_on_substance(substance_intake = intake)
2725 else:
2726 drug_db.show_info_on_drug(substance_intake = intake)
2727 #------------------------------------------------------------
2729 search_term = None
2730 if len(self.__row_data) > 0:
2731 sel_rows = self.get_selected_rows()
2732 if len(sel_rows) == 1:
2733 search_term = self.get_selected_data()[0]
2734 gmNetworkTools.open_url_in_browser(url = gmMedication.drug2renal_insufficiency_url(search_term = search_term))
2735 #------------------------------------------------------------
2738 #------------------------------------------------------------
2740 dbcfg = gmCfg.cCfgSQL()
2741 url = dbcfg.get2 (
2742 option = u'external.urls.report_ADR',
2743 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
2744 bias = u'user',
2745 default = u'https://dcgma.org/uaw/meldung.php' # http://www.akdae.de/Arzneimittelsicherheit/UAW-Meldung/UAW-Meldung-online.html
2746 )
2747 gmNetworkTools.open_url_in_browser(url = url)
2748 #------------------------------------------------------------
2754 #------------------------------------------------------------
2756
2757 if len(self.__row_data) == 0:
2758 return
2759
2760 drug_db = get_drug_database()
2761 if drug_db is None:
2762 return
2763
2764 if len(self.get_selected_rows()) > 1:
2765 drug_db.check_interactions(substance_intakes = self.get_selected_data())
2766 else:
2767 drug_db.check_interactions(substance_intakes = self.__row_data.values())
2768 #------------------------------------------------------------
2771 #------------------------------------------------------------
2773
2774 rows = self.get_selected_rows()
2775
2776 if len(rows) == 0:
2777 return
2778
2779 if len(rows) > 1:
2780 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit more than one substance at once.'), beep = True)
2781 return
2782
2783 subst = self.get_selected_data()[0]
2784 edit_intake_of_substance(parent = self, substance = subst)
2785 #------------------------------------------------------------
2787
2788 rows = self.get_selected_rows()
2789
2790 if len(rows) == 0:
2791 return
2792
2793 if len(rows) > 1:
2794 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete more than one substance at once.'), beep = True)
2795 return
2796
2797 subst = self.get_selected_data()[0]
2798 delete_substance_intake(parent = self, substance = subst['pk_substance_intake'])
2799 #------------------------------------------------------------
2801 rows = self.get_selected_rows()
2802
2803 if len(rows) == 0:
2804 return
2805
2806 if len(rows) > 1:
2807 gmDispatcher.send(signal = 'statustext', msg = _('Cannot create allergy from more than one substance at once.'), beep = True)
2808 return
2809
2810 return turn_substance_intake_into_allergy (
2811 parent = self,
2812 intake = self.get_selected_data()[0],
2813 emr = self.__patient.get_emr()
2814 )
2815 #------------------------------------------------------------
2817 # there could be some filtering/user interaction going on here
2818 print_medication_list(parent = self)
2819 #------------------------------------------------------------
2821
2822 try:
2823 entry = self.__row_data[row]
2824 except KeyError:
2825 return u' '
2826
2827 emr = self.__patient.get_emr()
2828 atcs = []
2829 if entry['atc_substance'] is not None:
2830 atcs.append(entry['atc_substance'])
2831 # if entry['atc_brand'] is not None:
2832 # atcs.append(entry['atc_brand'])
2833 # allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (entry['substance'],), brand = entry['brand'])
2834 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (entry['substance'],))
2835
2836 tt = _('Substance intake entry (%s, %s) [#%s] \n') % (
2837 gmTools.bool2subst (
2838 boolean = entry['is_currently_active'],
2839 true_return = gmTools.bool2subst (
2840 boolean = entry['seems_inactive'],
2841 true_return = _('active, needs check'),
2842 false_return = _('active'),
2843 none_return = _('assumed active')
2844 ),
2845 false_return = _('inactive')
2846 ),
2847 gmTools.bool2subst (
2848 boolean = entry['intake_is_approved_of'],
2849 true_return = _('approved'),
2850 false_return = _('unapproved')
2851 ),
2852 entry['pk_substance_intake']
2853 )
2854
2855 if allg not in [None, False]:
2856 certainty = gmTools.bool2subst(allg['definite'], _('definite'), _('suspected'))
2857 tt += u'\n'
2858 tt += u' !! ---- Cave ---- !!\n'
2859 tt += u' %s (%s): %s (%s)\n' % (
2860 allg['l10n_type'],
2861 certainty,
2862 allg['descriptor'],
2863 gmTools.coalesce(allg['reaction'], u'')[:40]
2864 )
2865 tt += u'\n'
2866
2867 tt += u' ' + _('Substance: %s [#%s]\n') % (entry['substance'], entry['pk_substance'])
2868 tt += u' ' + _('Preparation: %s\n') % entry['preparation']
2869 tt += u' ' + _('Amount per dose: %s %s') % (entry['amount'], entry['unit'])
2870 if entry.ddd is not None:
2871 tt += u' (DDD: %s %s)' % (entry.ddd['ddd'], entry.ddd['unit'])
2872 tt += u'\n'
2873 tt += gmTools.coalesce(entry['atc_substance'], u'', _(' ATC (substance): %s\n'))
2874
2875 tt += u'\n'
2876
2877 tt += gmTools.coalesce (
2878 entry['brand'],
2879 u'',
2880 _(' Brand name: %%s [#%s]\n') % entry['pk_brand']
2881 )
2882 tt += gmTools.coalesce(entry['atc_brand'], u'', _(' ATC (brand): %s\n'))
2883
2884 tt += u'\n'
2885
2886 tt += gmTools.coalesce(entry['schedule'], u'', _(' Regimen: %s\n'))
2887
2888 if entry['is_long_term']:
2889 duration = u' %s %s' % (gmTools.u_right_arrow, gmTools.u_infinity)
2890 else:
2891 if entry['duration'] is None:
2892 duration = u''
2893 else:
2894 duration = u' %s %s' % (gmTools.u_right_arrow, gmDateTime.format_interval(entry['duration'], gmDateTime.acc_days))
2895
2896 tt += _(' Started %s%s%s\n') % (
2897 gmDateTime.pydt_strftime(entry['started'], '%Y %b %d'),
2898 duration,
2899 gmTools.bool2subst(entry['is_long_term'], _(' (long-term)'), _(' (short-term)'), u'')
2900 )
2901
2902 if entry['discontinued'] is not None:
2903 tt += _(' Discontinued %s\n') % gmDateTime.pydt_strftime(entry['discontinued'], '%Y %b %d')
2904 tt += _(' Reason: %s\n') % entry['discontinue_reason']
2905
2906 tt += u'\n'
2907
2908 tt += gmTools.coalesce(entry['aim'], u'', _(' Aim: %s\n'))
2909 tt += gmTools.coalesce(entry['episode'], u'', _(' Episode: %s\n'))
2910 tt += gmTools.coalesce(entry['health_issue'], u'', _(' Health issue: %s\n'))
2911 tt += gmTools.coalesce(entry['notes'], u'', _(' Advice: %s\n'))
2912
2913 tt += u'\n'
2914
2915 tt += _(u'Revision: #%(row_ver)s, %(mod_when)s by %(mod_by)s.') % ({
2916 'row_ver': entry['row_version'],
2917 'mod_when': gmDateTime.pydt_strftime(entry['modified_when'], '%Y %b %d %H:%M:%S'),
2918 'mod_by': entry['modified_by']
2919 })
2920
2921 return tt
2922 #------------------------------------------------------------
2923 # internal helpers
2924 #------------------------------------------------------------
2926 self.CreateGrid(0, 1)
2927 self.EnableEditing(0)
2928 self.EnableDragGridSize(1)
2929 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows)
2930
2931 self.SetColLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTER)
2932
2933 self.SetRowLabelSize(0)
2934 self.SetRowLabelAlignment(horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
2935 #------------------------------------------------------------
2936 # properties
2937 #------------------------------------------------------------
2940
2944
2945 patient = property(_get_patient, _set_patient)
2946 #------------------------------------------------------------
2949
2953
2954 grouping_mode = property(_get_grouping_mode, _set_grouping_mode)
2955 #------------------------------------------------------------
2958
2962
2963 filter_show_unapproved = property(_get_filter_show_unapproved, _set_filter_show_unapproved)
2964 #------------------------------------------------------------
2967
2971
2972 filter_show_inactive = property(_get_filter_show_inactive, _set_filter_show_inactive)
2973 #------------------------------------------------------------
2974 # event handling
2975 #------------------------------------------------------------
2977 # dynamic tooltips: GridWindow, GridRowLabelWindow, GridColLabelWindow, GridCornerLabelWindow
2978 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells)
2979 #self.GetGridRowLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_row_labels)
2980 #self.GetGridColLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_col_labels)
2981
2982 # editing cells
2983 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
2984 #------------------------------------------------------------
2986 """Calculate where the mouse is and set the tooltip dynamically."""
2987
2988 # Use CalcUnscrolledPosition() to get the mouse position within the
2989 # entire grid including what's offscreen
2990 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
2991
2992 # use this logic to prevent tooltips outside the actual cells
2993 # apply to GetRowSize, too
2994 # tot = 0
2995 # for col in xrange(self.NumberCols):
2996 # tot += self.GetColSize(col)
2997 # if xpos <= tot:
2998 # self.tool_tip.Tip = 'Tool tip for Column %s' % (
2999 # self.GetColLabelValue(col))
3000 # break
3001 # else: # mouse is in label area beyond the right-most column
3002 # self.tool_tip.Tip = ''
3003
3004 row, col = self.XYToCell(x, y)
3005
3006 if row == self.__prev_tooltip_row:
3007 return
3008
3009 self.__prev_tooltip_row = row
3010
3011 try:
3012 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row))
3013 except KeyError:
3014 pass
3015 #------------------------------------------------------------
3017 row = evt.GetRow()
3018 data = self.__row_data[row]
3019 edit_intake_of_substance(parent = self, substance = data)
3020 #============================================================
3021 from Gnumed.wxGladeWidgets import wxgCurrentSubstancesPnl
3022
3023 -class cCurrentSubstancesPnl(wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl, gmRegetMixin.cRegetOnPaintMixin):
3024
3025 """Panel holding a grid with current substances. Used as notebook page."""
3026
3028
3029 wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl.__init__(self, *args, **kwargs)
3030 gmRegetMixin.cRegetOnPaintMixin.__init__(self)
3031
3032 self.__register_interests()
3033 #-----------------------------------------------------
3034 # reget-on-paint mixin API
3035 #-----------------------------------------------------
3037 """Populate cells with data from model."""
3038 pat = gmPerson.gmCurrentPatient()
3039 if pat.connected:
3040 self._grid_substances.patient = pat
3041 self.__refresh_gfr(pat)
3042 else:
3043 self._grid_substances.patient = None
3044 self.__clear_gfr()
3045 return True
3046 #--------------------------------------------------------
3048 gfr = patient.emr.get_most_recent_results(loinc = gmLOINC.LOINC_gfr_quantity, no_of_results = 1)
3049 if gfr is None:
3050 calc = gmClinicalCalculator.cClinicalCalculator()
3051 calc.patient = patient
3052 gfr = calc.eGFR
3053 if gfr.numeric_value is None:
3054 msg = _('GFR: ?')
3055 tt = gfr.message
3056 else:
3057 msg = _('eGFR: %.1f (%s)') % (
3058 gfr.numeric_value,
3059 gmDateTime.pydt_strftime (
3060 gfr.date_valid,
3061 format = '%b %Y'
3062 )
3063 )
3064 tt = gfr.format (
3065 left_margin = 0,
3066 width = 50,
3067 eol = u'\n',
3068 with_formula = True,
3069 with_warnings = True,
3070 with_variables = False,
3071 with_sub_results = True,
3072 return_list = False
3073 )
3074 else:
3075 msg = u'%s: %s %s (%s)\n' % (
3076 gfr['unified_abbrev'],
3077 gfr['unified_val'],
3078 gmTools.coalesce(gfr['abnormality_indicator'], u'', u' (%s)'),
3079 gmDateTime.pydt_strftime (
3080 gfr['clin_when'],
3081 format = '%b %Y'
3082 )
3083 )
3084 tt = _('GFR reported by path lab')
3085
3086 self._LBL_gfr.SetLabel(msg)
3087 self._LBL_gfr.SetToolTipString(tt)
3088 self._LBL_gfr.Refresh()
3089 self.Layout()
3090 #--------------------------------------------------------
3095 #--------------------------------------------------------
3096 # event handling
3097 #--------------------------------------------------------
3099 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
3100 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
3101 gmDispatcher.connect(signal = u'substance_intake_mod_db', receiver = self._schedule_data_reget)
3102 # active_substance_mod_db
3103 # substance_brand_mod_db
3104 #--------------------------------------------------------
3107
3109 self._grid_substances.patient = None
3110 #--------------------------------------------------------
3113
3116 #--------------------------------------------------------
3119 #--------------------------------------------------------
3122 #--------------------------------------------------------
3125 #--------------------------------------------------------
3128 #--------------------------------------------------------
3131 #--------------------------------------------------------
3133 self._grid_substances.grouping_mode = 'issue'
3134 #--------------------------------------------------------
3136 self._grid_substances.grouping_mode = 'episode'
3137 #--------------------------------------------------------
3139 self._grid_substances.grouping_mode = 'brand'
3140 #--------------------------------------------------------
3143 #--------------------------------------------------------
3146 #--------------------------------------------------------
3149 #--------------------------------------------------------
3152 #--------------------------------------------------------
3155 #--------------------------------------------------------
3158 #--------------------------------------------------------
3161 #--------------------------------------------------------
3164 #============================================================
3165 # main
3166 #------------------------------------------------------------
3167 if __name__ == '__main__':
3168
3169 if len(sys.argv) < 2:
3170 sys.exit()
3171
3172 if sys.argv[1] != 'test':
3173 sys.exit()
3174
3175 from Gnumed.business import gmPersonSearch
3176
3177 pat = gmPersonSearch.ask_for_patient()
3178 if pat is None:
3179 sys.exit()
3180 gmPerson.set_active_patient(patient = pat)
3181
3182 #----------------------------------------
3183 app = wx.PyWidgetTester(size = (600, 600))
3184 # #app.SetWidget(cATCPhraseWheel, -1)
3185 # app.SetWidget(cSubstancePhraseWheel, -1)
3186 # app.MainLoop()
3187 manage_substance_intakes()
3188
3189 #============================================================
3190
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Mon Jun 10 03:56:53 2013 | http://epydoc.sourceforge.net |