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