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