| Home | Trees | Indices | Help |
|
|---|
|
|
1 """GNUmed medication/substances handling widgets.
2 """
3 #================================================================
4 __version__ = "$Revision: 1.33 $"
5 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
6
7 import logging, sys, os.path, decimal
8
9
10 import wx, wx.grid
11
12
13 if __name__ == '__main__':
14 sys.path.insert(0, '../../')
15 from Gnumed.pycommon import gmDispatcher, gmCfg, gmShellAPI, gmTools, gmDateTime
16 from Gnumed.pycommon import gmMatchProvider, gmI18N, gmPrinting, gmCfg2, gmNetworkTools
17 from Gnumed.business import gmPerson, gmATC, gmSurgery, gmMedication, gmForms, gmStaff
18 from Gnumed.wxpython import gmGuiHelpers, gmRegetMixin, gmAuthWidgets, gmEditArea, gmMacro
19 from Gnumed.wxpython import gmCfgWidgets, gmListWidgets, gmPhraseWheel, gmFormWidgets
20 from Gnumed.wxpython import gmAllergyWidgets
21
22
23 _log = logging.getLogger('gm.ui')
24 _log.info(__version__)
25
26 #============================================================
27 # generic drug database access
28 #============================================================
30 gmCfgWidgets.configure_string_from_list_option (
31 parent = parent,
32 message = _(
33 '\n'
34 'Please select the default drug data source from the list below.\n'
35 '\n'
36 'Note that to actually use it you need to have the database installed, too.'
37 ),
38 option = 'external.drug_data.default_source',
39 bias = 'user',
40 default_value = None,
41 choices = gmMedication.drug_data_source_interfaces.keys(),
42 columns = [_('Drug data source')],
43 data = gmMedication.drug_data_source_interfaces.keys(),
44 caption = _('Configuring default drug data source')
45 )
46 #============================================================
48 dbcfg = gmCfg.cCfgSQL()
49
50 # load from option
51 default_db = dbcfg.get2 (
52 option = 'external.drug_data.default_source',
53 workplace = gmSurgery.gmCurrentPractice().active_workplace,
54 bias = 'workplace'
55 )
56
57 # not configured -> try to configure
58 if default_db is None:
59 gmDispatcher.send('statustext', msg = _('No default drug database configured.'), beep = True)
60 configure_drug_data_source(parent = parent)
61 default_db = dbcfg.get2 (
62 option = 'external.drug_data.default_source',
63 workplace = gmSurgery.gmCurrentPractice().active_workplace,
64 bias = 'workplace'
65 )
66 # still not configured -> return
67 if default_db is None:
68 gmGuiHelpers.gm_show_error (
69 aMessage = _('There is no default drug database configured.'),
70 aTitle = _('Jumping to drug database')
71 )
72 return None
73
74 # now it MUST be configured (either newly or previously)
75 # but also *validly* ?
76 try:
77 drug_db = gmMedication.drug_data_source_interfaces[default_db]()
78 except KeyError:
79 # not valid
80 _log.error('faulty default drug data source configuration: %s', default_db)
81 # try to configure
82 configure_drug_data_source(parent = parent)
83 default_db = dbcfg.get2 (
84 option = 'external.drug_data.default_source',
85 workplace = gmSurgery.gmCurrentPractice().active_workplace,
86 bias = 'workplace'
87 )
88 # deconfigured or aborted (and thusly still misconfigured) ?
89 try:
90 drug_db = gmMedication.drug_data_source_interfaces[default_db]()
91 except KeyError:
92 _log.error('still faulty default drug data source configuration: %s', default_db)
93 return None
94
95 pat = gmPerson.gmCurrentPatient()
96 if pat.connected:
97 drug_db.patient = pat
98
99 return drug_db
100 #============================================================
102 dbcfg = gmCfg.cCfgSQL()
103 drug_db = get_drug_database()
104 if drug_db is None:
105 return
106 drug_db.switch_to_frontend(blocking = False)
107
108 #============================================================
110
111 dbcfg = gmCfg.cCfgSQL()
112
113 ifap_cmd = dbcfg.get2 (
114 option = 'external.ifap-win.shell_command',
115 workplace = gmSurgery.gmCurrentPractice().active_workplace,
116 bias = 'workplace',
117 default = 'wine "C:\Ifapwin\WIAMDB.EXE"'
118 )
119 found, binary = gmShellAPI.detect_external_binary(ifap_cmd)
120 if not found:
121 gmDispatcher.send('statustext', msg = _('Cannot call IFAP via [%s].') % ifap_cmd)
122 return False
123 ifap_cmd = binary
124
125 if import_drugs:
126 transfer_file = os.path.expanduser(dbcfg.get2 (
127 option = 'external.ifap-win.transfer_file',
128 workplace = gmSurgery.gmCurrentPractice().active_workplace,
129 bias = 'workplace',
130 default = '~/.wine/drive_c/Ifapwin/ifap2gnumed.csv'
131 ))
132 # file must exist for Ifap to write into it
133 try:
134 f = open(transfer_file, 'w+b').close()
135 except IOError:
136 _log.exception('Cannot create IFAP <-> GNUmed transfer file [%s]', transfer_file)
137 gmDispatcher.send('statustext', msg = _('Cannot create IFAP <-> GNUmed transfer file [%s].') % transfer_file)
138 return False
139
140 wx.BeginBusyCursor()
141 gmShellAPI.run_command_in_shell(command = ifap_cmd, blocking = import_drugs)
142 wx.EndBusyCursor()
143
144 if import_drugs:
145 # COMMENT: this file must exist PRIOR to invoking IFAP
146 # COMMENT: or else IFAP will not write data into it ...
147 try:
148 csv_file = open(transfer_file, 'rb') # FIXME: encoding
149 except:
150 _log.exception('cannot access [%s]', fname)
151 csv_file = None
152
153 if csv_file is not None:
154 import csv
155 csv_lines = csv.DictReader (
156 csv_file,
157 fieldnames = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split(),
158 delimiter = ';'
159 )
160 pat = gmPerson.gmCurrentPatient()
161 emr = pat.get_emr()
162 # dummy episode for now
163 epi = emr.add_episode(episode_name = _('Current medication'))
164 for line in csv_lines:
165 narr = u'%sx %s %s %s (\u2258 %s %s) von %s (%s)' % (
166 line['Packungszahl'].strip(),
167 line['Handelsname'].strip(),
168 line['Form'].strip(),
169 line[u'Packungsgr\xf6\xdfe'].strip(),
170 line['Abpackungsmenge'].strip(),
171 line['Einheit'].strip(),
172 line['Hersteller'].strip(),
173 line['PZN'].strip()
174 )
175 emr.add_clin_narrative(note = narr, soap_cat = 's', episode = epi)
176 csv_file.close()
177
178 return True
179
180 #============================================================
181 # ATC related widgets
182 #============================================================
183
185
186 if parent is None:
187 parent = wx.GetApp().GetTopWindow()
188 #------------------------------------------------------------
189 def refresh(lctrl):
190 atcs = gmATC.get_reference_atcs()
191
192 items = [ [
193 a['atc'],
194 a['term'],
195 u'%s' % gmTools.coalesce(a['ddd'], u''),
196 gmTools.coalesce(a['unit'], u''),
197 gmTools.coalesce(a['administrative_route'], u''),
198 gmTools.coalesce(a['comment'], u''),
199 a['version'],
200 a['lang']
201 ] for a in atcs ]
202 lctrl.set_string_items(items)
203 lctrl.set_data(atcs)
204 #------------------------------------------------------------
205 gmListWidgets.get_choices_from_list (
206 parent = parent,
207 msg = _('\nThe ATC codes as known to GNUmed.\n'),
208 caption = _('Showing ATC codes.'),
209 columns = [ u'ATC', _('Term'), u'DDD', _('Unit'), _(u'Route'), _('Comment'), _('Version'), _('Language') ],
210 single_selection = True,
211 refresh_callback = refresh
212 )
213
214 #============================================================
216
217 dlg = wx.FileDialog (
218 parent = None,
219 message = _('Choose an ATC import config file'),
220 defaultDir = os.path.expanduser(os.path.join('~', 'gnumed')),
221 defaultFile = '',
222 wildcard = "%s (*.conf)|*.conf|%s (*)|*" % (_('config files'), _('all files')),
223 style = wx.OPEN | wx.HIDE_READONLY | wx.FILE_MUST_EXIST
224 )
225
226 result = dlg.ShowModal()
227 if result == wx.ID_CANCEL:
228 return
229
230 cfg_file = dlg.GetPath()
231 dlg.Destroy()
232
233 conn = gmAuthWidgets.get_dbowner_connection(procedure = _('importing ATC reference data'))
234 if conn is None:
235 return False
236
237 wx.BeginBusyCursor()
238
239 if gmATC.atc_import(cfg_fname = cfg_file, conn = conn):
240 gmDispatcher.send(signal = 'statustext', msg = _('Successfully imported ATC reference data.'))
241 else:
242 gmDispatcher.send(signal = 'statustext', msg = _('Importing ATC reference data failed.'), beep = True)
243
244 wx.EndBusyCursor()
245 return True
246
247 #============================================================
248
250
252
253 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
254
255 query = u"""
256
257 SELECT DISTINCT ON (label)
258 atc_code,
259 label
260 FROM (
261
262 SELECT
263 code as atc_code,
264 (code || ': ' || term || coalesce(' (' || ddd || unit || ')', ''))
265 AS label
266 FROM ref.atc
267 WHERE
268 term %(fragment_condition)s
269 OR
270 code %(fragment_condition)s
271
272 UNION ALL
273
274 SELECT
275 atc_code,
276 (atc_code || ': ' || description)
277 AS label
278 FROM ref.consumable_substance
279 WHERE
280 description %(fragment_condition)s
281 OR
282 atc_code %(fragment_condition)s
283
284 UNION ALL
285
286 SELECT
287 atc_code,
288 (atc_code || ': ' || description || ' (' || preparation || ')')
289 AS label
290 FROM ref.branded_drug
291 WHERE
292 description %(fragment_condition)s
293 OR
294 atc_code %(fragment_condition)s
295
296 -- it would be nice to be able to include clin.vacc_indication but that's hard to do in SQL
297
298 ) AS candidates
299 WHERE atc_code IS NOT NULL
300 ORDER BY label
301 LIMIT 50"""
302
303 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
304 mp.setThresholds(1, 2, 4)
305 # mp.word_separators = '[ \t=+&:@]+'
306 self.SetToolTipString(_('Select an ATC (Anatomical-Therapeutic-Chemical) code.'))
307 self.matcher = mp
308 self.selection_only = True
309
310 #============================================================
311 # consumable substances widgets
312 #------------------------------------------------------------
314
315 if parent is None:
316 parent = wx.GetApp().GetTopWindow()
317 #------------------------------------------------------------
318 def add_from_db(substance):
319 drug_db = get_drug_database(parent = parent)
320 if drug_db is None:
321 return False
322 drug_db.import_drugs()
323 return True
324 #------------------------------------------------------------
325 def edit(substance=None):
326 return edit_consumable_substance(parent = parent, substance = substance, single_entry = (substance is not None))
327 #------------------------------------------------------------
328 def delete(substance):
329 if substance.is_in_use_by_patients:
330 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete this substance. It is in use.'), beep = True)
331 return False
332
333 return gmMedication.delete_consumable_substance(substance = substance['pk'])
334 #------------------------------------------------------------
335 def refresh(lctrl):
336 substs = gmMedication.get_consumable_substances(order_by = 'description')
337 items = [ [
338 s['description'],
339 s['amount'],
340 s['unit'],
341 gmTools.coalesce(s['atc_code'], u''),
342 s['pk']
343 ] for s in substs ]
344 lctrl.set_string_items(items)
345 lctrl.set_data(substs)
346 #------------------------------------------------------------
347 msg = _('\nThese are the consumable substances registered with GNUmed.\n')
348
349 gmListWidgets.get_choices_from_list (
350 parent = parent,
351 msg = msg,
352 caption = _('Showing consumable substances.'),
353 columns = [_('Substance'), _('Amount'), _('Unit'), 'ATC', u'#'],
354 single_selection = True,
355 new_callback = edit,
356 edit_callback = edit,
357 delete_callback = delete,
358 refresh_callback = refresh,
359 left_extra_button = (_('Import'), _('Import consumable substances from a drug database.'), add_from_db)
360 )
361
362 #------------------------------------------------------------
364
365 if substance is not None:
366 if substance.is_in_use_by_patients:
367 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit this substance. It is in use.'), beep = True)
368 return False
369
370 ea = cConsumableSubstanceEAPnl(parent = parent, id = -1)
371 ea.data = substance
372 ea.mode = gmTools.coalesce(substance, 'new', 'edit')
373 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
374 dlg.SetTitle(gmTools.coalesce(substance, _('Adding new consumable substance'), _('Editing consumable substance')))
375 if dlg.ShowModal() == wx.ID_OK:
376 dlg.Destroy()
377 return True
378 dlg.Destroy()
379 return False
380
381 #============================================================
382 from Gnumed.wxGladeWidgets import wxgConsumableSubstanceEAPnl
383
384 -class cConsumableSubstanceEAPnl(wxgConsumableSubstanceEAPnl.wxgConsumableSubstanceEAPnl, gmEditArea.cGenericEditAreaMixin):
385
387
388 try:
389 data = kwargs['substance']
390 del kwargs['substance']
391 except KeyError:
392 data = None
393
394 wxgConsumableSubstanceEAPnl.wxgConsumableSubstanceEAPnl.__init__(self, *args, **kwargs)
395 gmEditArea.cGenericEditAreaMixin.__init__(self)
396
397 # Code using this mixin should set mode and data
398 # after instantiating the class:
399 self.mode = 'new'
400 self.data = data
401 if data is not None:
402 self.mode = 'edit'
403
404 # self.__init_ui()
405 #----------------------------------------------------------------
406 # def __init_ui(self):
407 # self._PRW_atc.selection_only = False
408 #----------------------------------------------------------------
409 # generic Edit Area mixin API
410 #----------------------------------------------------------------
412
413 validity = True
414
415 if self._TCTRL_substance.GetValue().strip() == u'':
416 validity = False
417 self.display_tctrl_as_valid(tctrl = self._TCTRL_substance, valid = False)
418 self._TCTRL_substance.SetFocus()
419 else:
420 self.display_tctrl_as_valid(tctrl = self._TCTRL_substance, valid = True)
421
422 try:
423 decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.'))
424 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
425 except (TypeError, decimal.InvalidOperation):
426 validity = False
427 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
428 self._TCTRL_amount.SetFocus()
429
430 if self._PRW_unit.GetValue().strip() == u'':
431 validity = False
432 self._PRW_unit.display_as_valid(valid = False)
433 self._TCTRL_substance.SetFocus()
434 else:
435 self._PRW_unit.display_as_valid(valid = True)
436
437 if validity is False:
438 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save consumable substance. Missing essential input.'))
439
440 return validity
441 #----------------------------------------------------------------
443 subst = gmMedication.create_consumable_substance (
444 substance = self._TCTRL_substance.GetValue().strip(),
445 atc = self._PRW_atc.GetData(),
446 amount = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.')),
447 unit = gmTools.coalesce(self._PRW_unit.GetData(), self._PRW_unit.GetValue().strip(), function_initial = ('strip', None))
448 )
449 success, data = subst.save()
450 if not success:
451 err, msg = data
452 _log.error(err)
453 _log.error(msg)
454 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save consumable substance. %s') % msg, beep = True)
455 return False
456
457 self.data = subst
458 return True
459 #----------------------------------------------------------------
461 self.data['description'] = self._TCTRL_substance.GetValue().strip()
462 self.data['atc_code'] = self._PRW_atc.GetData()
463 self.data['amount'] = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.'))
464 self.data['unit'] = gmTools.coalesce(self._PRW_unit.GetData(), self._PRW_unit.GetValue().strip(), function_initial = ('strip', None))
465 success, data = self.data.save()
466
467 if not success:
468 err, msg = data
469 _log.error(err)
470 _log.error(msg)
471 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save consumable substance. %s') % msg, beep = True)
472 return False
473
474 return True
475 #----------------------------------------------------------------
477 self._TCTRL_substance.SetValue(u'')
478 self._TCTRL_amount.SetValue(u'')
479 self._PRW_unit.SetText(u'', None)
480 self._PRW_atc.SetText(u'', None)
481
482 self._TCTRL_substance.SetFocus()
483 #----------------------------------------------------------------
485 self._TCTRL_substance.SetValue(self.data['description'])
486 self._TCTRL_amount.SetValue(u'%s' % self.data['amount'])
487 self._PRW_unit.SetText(self.data['unit'], self.data['unit'])
488 self._PRW_atc.SetText(gmTools.coalesce(self.data['atc_code'], u''), self.data['atc_code'])
489
490 self._TCTRL_substance.SetFocus()
491 #----------------------------------------------------------------
494
495 #============================================================
496 # drug component widgets
497 #------------------------------------------------------------
499
500 if parent is None:
501 parent = wx.GetApp().GetTopWindow()
502
503 #------------------------------------------------------------
504 def edit(component=None):
505 substance = gmMedication.cConsumableSubstance(aPK_obj = component['pk_consumable_substance'])
506 return edit_consumable_substance(parent = parent, substance = substance, single_entry = True)
507 #------------------------------------------------------------
508 def delete(component):
509 if component.is_in_use_by_patients:
510 gmDispatcher.send(signal = 'statustext', msg = _('Cannot remove this component from the drug. It is in use.'), beep = True)
511 return False
512
513 return component.containing_drug.remove_component(substance = component['pk_component'])
514 #------------------------------------------------------------
515 def refresh(lctrl):
516 comps = gmMedication.get_drug_components()
517 items = [ [
518 u'%s%s' % (c['brand'], gmTools.coalesce(c['atc_brand'], u'', u' [%s]')),
519 u'%s%s' % (c['substance'], gmTools.coalesce(c['atc_substance'], u'', u' [%s]')),
520 u'%s%s' % (c['amount'], c['unit']),
521 c['preparation'],
522 gmTools.coalesce(c['external_code_brand'], u'', u'%%s [%s]' % c['external_code_type_brand']),
523 c['pk_component']
524 ] for c in comps ]
525 lctrl.set_string_items(items)
526 lctrl.set_data(comps)
527 #------------------------------------------------------------
528 msg = _('\nThese are the components in the drug brands known to GNUmed.\n')
529
530 gmListWidgets.get_choices_from_list (
531 parent = parent,
532 msg = msg,
533 caption = _('Showing drug brand components.'),
534 columns = [_('Brand'), _('Substance'), _('Strength'), _('Preparation'), _('Code'), u'#'],
535 single_selection = True,
536 #new_callback = edit,
537 edit_callback = edit,
538 delete_callback = delete,
539 refresh_callback = refresh
540 )
541
542 #------------------------------------------------------------
544 ea = cDrugComponentEAPnl(parent = parent, id = -1)
545 ea.data = drug_component
546 ea.mode = gmTools.coalesce(drug_component, 'new', 'edit')
547 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
548 dlg.SetTitle(gmTools.coalesce(drug_component, _('Adding new drug component'), _('Editing drug component')))
549 if dlg.ShowModal() == wx.ID_OK:
550 dlg.Destroy()
551 return True
552 dlg.Destroy()
553 return False
554
555 #============================================================
556 from Gnumed.wxGladeWidgets import wxgDrugComponentEAPnl
557
558 -class cDrugComponentEAPnl(wxgDrugComponentEAPnl.wxgDrugComponentEAPnl, gmEditArea.cGenericEditAreaMixin):
559
561
562 try:
563 data = kwargs['component']
564 del kwargs['component']
565 except KeyError:
566 data = None
567
568 wxgDrugComponentEAPnl.wxgDrugComponentEAPnl.__init__(self, *args, **kwargs)
569 gmEditArea.cGenericEditAreaMixin.__init__(self)
570
571 # Code using this mixin should set mode and data
572 # after instantiating the class:
573 self.mode = 'new'
574 self.data = data
575 if data is not None:
576 self.mode = 'edit'
577
578 #self.__init_ui()
579 #----------------------------------------------------------------
580 # def __init_ui(self):
581 # # adjust phrasewheels etc
582 #----------------------------------------------------------------
583 # generic Edit Area mixin API
584 #----------------------------------------------------------------
586 if self.data is not None:
587 if self.data['is_in_use']:
588 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit drug component. It is in use.'), beep = True)
589 return False
590
591 validity = True
592
593 if self._PRW_substance.GetData() is None:
594 validity = False
595 self._PRW_substance.display_as_valid(False)
596 else:
597 self._PRW_substance.display_as_valid(True)
598
599 val = self._TCTRL_amount.GetValue().strip().replace(',', u'.', 1)
600 try:
601 decimal.Decimal(val)
602 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
603 except:
604 validity = False
605 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
606
607 if self._PRW_unit.GetValue().strip() == u'':
608 validity = False
609 self._PRW_unit.display_as_valid(False)
610 else:
611 self._PRW_unit.display_as_valid(True)
612
613 if validity is False:
614 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save drug component. Invalid or missing essential input.'))
615
616 return validity
617 #----------------------------------------------------------------
619 # save the data as a new instance
620 data = 1
621 data[''] = 1
622 data[''] = 1
623 # data.save()
624
625 # must be done very late or else the property access
626 # will refresh the display such that later field
627 # access will return empty values
628 # self.data = data
629 return False
630 return True
631 #----------------------------------------------------------------
633 self.data['pk_consumable_substance'] = self._PRW_substance.GetData()
634 self.data['amount'] = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', u'.', 1))
635 self.data['unit'] = self._PRW_unit.GetValue().strip()
636 return self.data.save()
637 #----------------------------------------------------------------
639 self._TCTRL_brand.SetValue(u'')
640 self._TCTRL_components.SetValue(u'')
641 self._TCTRL_codes.SetValue(u'')
642 self._PRW_substance.SetText(u'', None)
643 self._TCTRL_amount.SetValue(u'')
644 self._PRW_unit.SetText(u'', None)
645
646 self._PRW_substance.SetFocus()
647 #----------------------------------------------------------------
649 self._TCTRL_brand.SetValue(u'%s (%s)' % (self.data['brand'], self.data['preparation']))
650 self._TCTRL_components.SetValue(u' / '.join(self.data.containing_drug['components']))
651 details = []
652 if self.data['atc_brand'] is not None:
653 details.append(u'ATC: %s' % self.data['atc_brand'])
654 if self.data['external_code_brand'] is not None:
655 details.append(u'%s: %s' % (self.data['external_code_type_brand'], self.data['external_code_brand']))
656 self._TCTRL_codes.SetValue(u'; '.join(details))
657
658 self._PRW_substance.SetText(self.data['substance'], self.data['pk_consumable_substance'])
659 self._TCTRL_amount.SetValue(u'%s' % self.data['amount'])
660 self._PRW_unit.SetText(self.data['unit'], self.data['unit'])
661
662 self._PRW_substance.SetFocus()
663 #----------------------------------------------------------------
673
674 #============================================================
676
678
679 mp = gmMedication.cDrugComponentMatchProvider()
680 mp.setThresholds(2, 3, 4)
681 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
682 self.SetToolTipString(_('A drug component with optional strength.'))
683 self.matcher = mp
684 self.selection_only = False
685 #--------------------------------------------------------
687 return gmMedication.cDrugComponent(aPK_obj = self.GetData(as_instance = False, can_create = False))
688 #============================================================
689 #============================================================
691
693
694 query = u"""
695 (
696 SELECT DISTINCT ON (preparation)
697 preparation as prep, preparation
698 FROM ref.branded_drug
699 WHERE preparation %(fragment_condition)s
700 ) UNION (
701 SELECT DISTINCT ON (preparation)
702 preparation as prep, preparation
703 FROM clin.substance_intake
704 WHERE preparation %(fragment_condition)s
705 )
706 ORDER BY prep
707 limit 30"""
708
709 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
710 mp.setThresholds(1, 2, 4)
711 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
712 self.SetToolTipString(_('The preparation (form) of the substance or brand.'))
713 self.matcher = mp
714 self.selection_only = False
715 #============================================================
717
719
720 mp = gmMedication.cSubstanceMatchProvider()
721 mp.setThresholds(1, 2, 4)
722 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
723 self.SetToolTipString(_('The substance with optional strength.'))
724 self.matcher = mp
725 self.selection_only = False
726 self.phrase_separators = None
727
728 #--------------------------------------------------------
730 return gmMedication.cConsumableSubstance(aPK_obj = self.GetData(as_instance = False, can_create = False))
731 #============================================================
732 # branded drugs widgets
733 #------------------------------------------------------------
735
736 if brand is not None:
737 if brand.is_in_use_by_patients:
738 gmGuiHelpers.gm_show_info (
739 aTitle = _('Managing components of a drug'),
740 aMessage = _(
741 'Cannot manage the components of the branded drug product\n'
742 '\n'
743 ' "%s" (%s)\n'
744 '\n'
745 'because it is currently taken by patients.\n'
746 ) % (brand['brand'], brand['preparation'])
747 )
748 return False
749 #--------------------------------------------------------
750 if parent is None:
751 parent = wx.GetApp().GetTopWindow()
752 #--------------------------------------------------------
753 # def manage_substances():
754 # pass
755 #--------------------------------------------------------
756 if brand is None:
757 msg = _('Pick the substances which are components of this drug.')
758 right_col = _('Components of drug')
759 comp_substs = []
760 else:
761 right_col = u'%s (%s)' % (brand['brand'], brand['preparation'])
762 msg = _(
763 'Adjust the components of "%s"\n'
764 '\n'
765 'The drug must contain at least one component. Any given\n'
766 'substance can only be included once per drug.'
767 ) % right_col
768 comp_substs = [ c.substance for c in brand.components ]
769
770 substs = gmMedication.get_consumable_substances(order_by = 'description')
771 choices = [ u'%s %s%s' % (s['description'], s['amount'], s['unit']) for s in substs ]
772 picks = [ u'%s %s%s' % (c['description'], c['amount'], c['unit']) for c in comp_substs ]
773
774 picker = gmListWidgets.cItemPickerDlg (
775 parent,
776 -1,
777 title = _('Managing components of a drug ...'),
778 msg = msg
779 )
780 picker.set_columns(['Substances'], [right_col])
781 picker.set_choices(choices = choices, data = substs)
782 picker.set_picks(picks = picks, data = comp_substs)
783 # picker.extra_button = (
784 # _('Substances'),
785 # _('Manage list of consumable substances'),
786 # manage_substances
787 # )
788
789 btn_pressed = picker.ShowModal()
790 substs = picker.get_picks()
791 picker.Destroy()
792
793 if btn_pressed != wx.ID_OK:
794 return (False, None)
795
796 if brand is not None:
797 brand.set_substances_as_components(substances = substs)
798
799 return (True, substs)
800 #------------------------------------------------------------
802
803 if parent is None:
804 parent = wx.GetApp().GetTopWindow()
805 #------------------------------------------------------------
806 def add_from_db(brand):
807 drug_db = get_drug_database(parent = parent)
808 if drug_db is None:
809 return False
810 drug_db.import_drugs()
811 return True
812 #------------------------------------------------------------
813 def get_tooltip(brand=None):
814 tt = u'%s %s\n' % (brand['brand'], brand['preparation'])
815 tt += u'\n'
816 tt += u'%s%s%s\n' % (
817 gmTools.bool2subst(brand.is_vaccine, u'%s, ' % _('Vaccine'), u''),
818 u'%s, ' % gmTools.bool2subst(brand.is_in_use_by_patients, _('in use'), _('not in use')),
819 gmTools.bool2subst(brand['is_fake_brand'], _('fake'), u'')
820 )
821 tt += gmTools.coalesce(brand['atc'], u'', _('ATC: %s\n'))
822 tt += gmTools.coalesce(brand['external_code'], u'', u'%s: %%s\n' % brand['external_code_type'])
823 if brand['components'] is not None:
824 tt += u'- %s' % u'\n- '.join(brand['components'])
825 return tt
826 #------------------------------------------------------------
827 def edit(brand):
828 if brand is not None:
829 if brand.is_vaccine:
830 gmGuiHelpers.gm_show_info (
831 aTitle = _('Editing medication'),
832 aMessage = _(
833 'Cannot edit the medication\n'
834 '\n'
835 ' "%s" (%s)\n'
836 '\n'
837 'because it is a vaccine. Please edit it\n'
838 'from the vaccine management section !\n'
839 ) % (brand['brand'], brand['preparation'])
840 )
841 return False
842
843 return edit_branded_drug(parent = parent, branded_drug = brand, single_entry = True)
844 #------------------------------------------------------------
845 def delete(brand):
846 if brand.is_vaccine:
847 gmGuiHelpers.gm_show_info (
848 aTitle = _('Deleting medication'),
849 aMessage = _(
850 'Cannot delete the medication\n'
851 '\n'
852 ' "%s" (%s)\n'
853 '\n'
854 'because it is a vaccine. Please delete it\n'
855 'from the vaccine management section !\n'
856 ) % (brand['brand'], brand['preparation'])
857 )
858 return False
859 gmMedication.delete_branded_drug(brand = brand['pk_brand'])
860 return True
861 #------------------------------------------------------------
862 def new():
863 return edit_branded_drug(parent = parent, branded_drug = None, single_entry = False)
864 #------------------------------------------------------------
865 def refresh(lctrl):
866 drugs = gmMedication.get_branded_drugs()
867 items = [ [
868 u'%s%s' % (
869 d['brand'],
870 gmTools.bool2subst(d['is_fake_brand'], ' (%s)' % _('fake'), u'')
871 ),
872 d['preparation'],
873 gmTools.coalesce(d['atc'], u''),
874 gmTools.coalesce(d['components'], u''),
875 gmTools.coalesce(d['external_code'], u'', u'%%s [%s]' % d['external_code_type']),
876 d['pk_brand']
877 ] for d in drugs ]
878 lctrl.set_string_items(items)
879 lctrl.set_data(drugs)
880 #------------------------------------------------------------
881 msg = _('\nThese are the drug brands known to GNUmed.\n')
882
883 gmListWidgets.get_choices_from_list (
884 parent = parent,
885 msg = msg,
886 caption = _('Showing branded drugs.'),
887 columns = [_('Name'), _('Preparation'), _('ATC'), _('Components'), _('Code'), u'#'],
888 single_selection = True,
889 ignore_OK_button = ignore_OK_button,
890 refresh_callback = refresh,
891 new_callback = new,
892 edit_callback = edit,
893 delete_callback = delete,
894 list_tooltip_callback = get_tooltip,
895 left_extra_button = (_('Import'), _('Import substances and brands from a drug database.'), add_from_db)
896 #, middle_extra_button = (_('Clone'), _('Clone selected drug into a new entry for editing.'), clone_from_existing)
897 #, right_extra_button = (_('Reassign'), _('Reassign all patients taking the selected drug to another drug.'), reassign_patients)
898 )
899
900 #------------------------------------------------------------
902
903 if branded_drug is not None:
904 if branded_drug.is_in_use_by_patients:
905 gmGuiHelpers.gm_show_info (
906 aTitle = _('Editing drug'),
907 aMessage = _(
908 'Cannot edit the branded drug product\n'
909 '\n'
910 ' "%s" (%s)\n'
911 '\n'
912 'because it is currently taken by patients.\n'
913 ) % (branded_drug['brand'], branded_drug['preparation'])
914 )
915 return False
916
917 if parent is None:
918 parent = wx.GetApp().GetTopWindow()
919 #--------------------------------------------
920 def manage_substances(drug):
921 manage_consumable_substances(parent = parent)
922 #--------------------------------------------
923 ea = cBrandedDrugEAPnl(parent = parent, id = -1)
924 ea.data = branded_drug
925 ea.mode = gmTools.coalesce(branded_drug, 'new', 'edit')
926 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
927 dlg.SetTitle(gmTools.coalesce(branded_drug, _('Adding new drug brand'), _('Editing drug brand')))
928 dlg.left_extra_button = (
929 _('Substances'),
930 _('Manage consumable substances'),
931 manage_substances
932 )
933 if dlg.ShowModal() == wx.ID_OK:
934 dlg.Destroy()
935 return True
936 dlg.Destroy()
937 return False
938
939 #============================================================
940 from Gnumed.wxGladeWidgets import wxgBrandedDrugEAPnl
941
942 -class cBrandedDrugEAPnl(wxgBrandedDrugEAPnl.wxgBrandedDrugEAPnl, gmEditArea.cGenericEditAreaMixin):
943
945
946 try:
947 data = kwargs['drug']
948 del kwargs['drug']
949 except KeyError:
950 data = None
951
952 wxgBrandedDrugEAPnl.wxgBrandedDrugEAPnl.__init__(self, *args, **kwargs)
953 gmEditArea.cGenericEditAreaMixin.__init__(self)
954
955 self.mode = 'new'
956 self.data = data
957 if data is not None:
958 self.mode = 'edit'
959 self.__component_substances = data.components_as_substances
960
961 #self.__init_ui()
962 #----------------------------------------------------------------
963 # def __init_ui(self):
964 # adjust external type PRW
965 #----------------------------------------------------------------
966 # generic Edit Area mixin API
967 #----------------------------------------------------------------
969
970 if self.data is not None:
971 if self.data.is_in_use_by_patients:
972 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit drug brand. It is in use.'), beep = True)
973 return False
974
975 validity = True
976
977 if self._PRW_brand.GetValue().strip() == u'':
978 validity = False
979 self._PRW_brand.display_as_valid(False)
980 else:
981 self._PRW_brand.display_as_valid(True)
982
983 if self._PRW_preparation.GetValue().strip() == u'':
984 validity = False
985 self._PRW_preparation.display_as_valid(False)
986 else:
987 self._PRW_preparation.display_as_valid(True)
988
989 if validity is True:
990 self._TCTRL_components.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BACKGROUND))
991 if len(self.__component_substances) == 0:
992 wants_empty = gmGuiHelpers.gm_show_question (
993 title = _('Checking brand data'),
994 question = _(
995 'You have not selected any substances\n'
996 'as drug components.\n'
997 '\n'
998 'Without components you will not be able to\n'
999 'use this drug for documenting patient care.\n'
1000 '\n'
1001 'Are you sure you want to save\n'
1002 'it without components ?'
1003 )
1004 )
1005 if not wants_empty:
1006 validity = False
1007 self.display_ctrl_as_valid(ctrl = self._TCTRL_components, valid = False)
1008
1009 if validity is False:
1010 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save branded drug. Invalid or missing essential input.'))
1011
1012 return validity
1013 #----------------------------------------------------------------
1015
1016 drug = gmMedication.create_branded_drug (
1017 brand_name = self._PRW_brand.GetValue().strip(),
1018 preparation = gmTools.coalesce (
1019 self._PRW_preparation.GetData(),
1020 self._PRW_preparation.GetValue()
1021 ).strip(),
1022 return_existing = True
1023 )
1024 drug['is_fake_brand'] = self._CHBOX_is_fake.GetValue()
1025 drug['atc'] = self._PRW_atc.GetData()
1026 code = self._TCTRL_external_code.GetValue().strip()
1027 if code != u'':
1028 drug['external_code'] = code
1029 drug['external_code_type'] = self._PRW_external_code_type.GetData().strip()
1030
1031 drug.save()
1032
1033 if len(self.__component_substances) > 0:
1034 drug.set_substances_as_components(substances = self.__component_substances)
1035
1036 self.data = drug
1037
1038 return True
1039 #----------------------------------------------------------------
1041 self.data['brand'] = self._PRW_brand.GetValue().strip()
1042 self.data['preparation'] = gmTools.coalesce (
1043 self._PRW_preparation.GetData(),
1044 self._PRW_preparation.GetValue()
1045 ).strip()
1046 self.data['is_fake_brand'] = self._CHBOX_is_fake.GetValue()
1047 self.data['atc'] = self._PRW_atc.GetData()
1048 code = self._TCTRL_external_code.GetValue().strip()
1049 if code != u'':
1050 self.data['external_code'] = code
1051 self.data['external_code_type'] = self._PRW_external_code_type.GetData().strip()
1052 success, data = self.data.save()
1053 if not success:
1054 err, msg = data
1055 _log.error('problem saving')
1056 _log.error('%s', err)
1057 _log.error('%s', msg)
1058 return (success is True)
1059 #----------------------------------------------------------------
1061 self._PRW_brand.SetText(u'', None)
1062 self._PRW_preparation.SetText(u'', None)
1063 self._CHBOX_is_fake.SetValue(False)
1064 self._TCTRL_components.SetValue(u'')
1065 self._PRW_atc.SetText(u'', None)
1066 self._TCTRL_external_code.SetValue(u'')
1067 self._PRW_external_code_type.SetText(u'', None)
1068
1069 self._PRW_brand.SetFocus()
1070
1071 self.__component_substances = []
1072 #----------------------------------------------------------------
1075 #----------------------------------------------------------------
1077 self._PRW_brand.SetText(self.data['brand'], self.data['pk_brand'])
1078 self._PRW_preparation.SetText(self.data['preparation'], self.data['preparation'])
1079 self._CHBOX_is_fake.SetValue(self.data['is_fake_brand'])
1080 comps = u''
1081 if self.data['components'] is not None:
1082 comps = u'- %s' % u'\n- '.join(self.data['components'])
1083 self._TCTRL_components.SetValue(comps)
1084 self._PRW_atc.SetText(gmTools.coalesce(self.data['atc'], u''), self.data['atc'])
1085 self._TCTRL_external_code.SetValue(gmTools.coalesce(self.data['external_code'], u''))
1086 t = gmTools.coalesce(self.data['external_code_type'], u'')
1087 self._PRW_external_code_type.SetText(t, t)
1088
1089 self._PRW_brand.SetFocus()
1090
1091 self.__component_substances = self.data.components_as_substances
1092 #----------------------------------------------------------------
1093 # event handler
1094 #----------------------------------------------------------------
1108 #============================================================
1110
1112
1113 query = u"""
1114 SELECT
1115 pk
1116 AS data,
1117 (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', ''))
1118 AS list_label,
1119 (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', ''))
1120 AS field_label
1121 FROM ref.branded_drug
1122 WHERE description %(fragment_condition)s
1123 ORDER BY list_label
1124 LIMIT 50"""
1125
1126 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
1127 mp.setThresholds(2, 3, 4)
1128 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1129 self.SetToolTipString(_(
1130 'The brand name of the drug.\n'
1131 '\n'
1132 'Note: a brand name will need to be linked to\n'
1133 'one or more components before it can be used,\n'
1134 'except in the case of fake (generic) vaccines.'
1135 ))
1136 self.matcher = mp
1137 self.selection_only = False
1138
1139 #============================================================
1140 # current substance intake widgets
1141 #------------------------------------------------------------
1143
1145
1146 query = u"""
1147 SELECT DISTINCT ON (sched)
1148 schedule as sched,
1149 schedule
1150 FROM clin.substance_intake
1151 WHERE schedule %(fragment_condition)s
1152 ORDER BY sched
1153 LIMIT 50"""
1154
1155 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
1156 mp.setThresholds(1, 2, 4)
1157 mp.word_separators = '[ \t=+&:@]+'
1158 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1159 self.SetToolTipString(_('The schedule for taking this substance.'))
1160 self.matcher = mp
1161 self.selection_only = False
1162 #============================================================
1164
1165 if intake['is_currently_active']:
1166 intake['discontinued'] = gmDateTime.pydt_now_here()
1167 if intake['discontinue_reason'] is None:
1168 intake['discontinue_reason'] = u'%s %s' % (_('not tolerated:'), _('discontinued due to allergy or intolerance'))
1169 else:
1170 if not intake['discontinue_reason'].startswith(_('not tolerated:')):
1171 intake['discontinue_reason'] = u'%s %s' % (_('not tolerated:'), intake['discontinue_reason'])
1172 if not intake.save():
1173 return False
1174
1175 allg = intake.turn_into_allergy(encounter_id = emr.active_encounter['pk_encounter'])
1176
1177 brand = intake.containing_drug
1178 if brand is not None:
1179 comps = [ c['substance'] for c in brand.components ]
1180 if len(comps) > 1:
1181 gmGuiHelpers.gm_show_info (
1182 aTitle = _(u'Documented an allergy'),
1183 aMessage = _(
1184 u'An allergy was documented against the substance:\n'
1185 u'\n'
1186 u' [%s]\n'
1187 u'\n'
1188 u'This substance was taken with the multi-component brand:\n'
1189 u'\n'
1190 u' [%s (%s)]\n'
1191 u'\n'
1192 u'Note that ALL components of this brand were discontinued.'
1193 ) % (
1194 intake['substance'],
1195 intake['brand'],
1196 u' & '.join(comps)
1197 )
1198 )
1199
1200 if parent is None:
1201 parent = wx.GetApp().GetTopWindow()
1202
1203 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent = parent, id = -1)
1204 dlg.ShowModal()
1205
1206 return True
1207 #============================================================
1208 from Gnumed.wxGladeWidgets import wxgCurrentMedicationEAPnl
1209
1210 -class cSubstanceIntakeEAPnl(wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl, gmEditArea.cGenericEditAreaMixin):
1211
1213
1214 try:
1215 data = kwargs['substance']
1216 del kwargs['substance']
1217 except KeyError:
1218 data = None
1219
1220 wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl.__init__(self, *args, **kwargs)
1221 gmEditArea.cGenericEditAreaMixin.__init__(self)
1222
1223 self.mode = 'new'
1224 self.data = data
1225 if data is not None:
1226 self.mode = 'edit'
1227
1228 self.__init_ui()
1229 #----------------------------------------------------------------
1231
1232 self._PRW_component.add_callback_on_lose_focus(callback = self._on_leave_component)
1233 self._PRW_component.selection_only = True
1234
1235 self._PRW_substance.add_callback_on_lose_focus(callback = self._on_leave_substance)
1236 self._PRW_substance.selection_only = True
1237 #----------------------------------------------------------------
1239 emr = gmPerson.gmCurrentPatient().get_emr()
1240
1241 state = emr.allergy_state
1242 if state['last_confirmed'] is None:
1243 confirmed = _('never')
1244 else:
1245 confirmed = state['last_confirmed'].strftime('%Y %B %d').decode(gmI18N.get_encoding())
1246 msg = _(u'%s, last confirmed %s\n') % (state.state_string, confirmed)
1247 msg += gmTools.coalesce(state['comment'], u'', _('Comment (%s): %%s\n') % state['modified_by'])
1248 msg += u'\n'
1249
1250 for allergy in emr.get_allergies():
1251 msg += u'%s (%s, %s): %s\n' % (
1252 allergy['descriptor'],
1253 allergy['l10n_type'],
1254 gmTools.bool2subst(allergy['definite'], _('definite'), _('suspected'), u'?'),
1255 gmTools.coalesce(allergy['reaction'], _('reaction not recorded'))
1256 )
1257
1258 self._LBL_allergies.SetLabel(msg)
1259 #----------------------------------------------------------------
1260 # generic Edit Area mixin API
1261 #----------------------------------------------------------------
1263
1264 validity = True
1265
1266 has_component = (self._PRW_component.GetData() is not None)
1267 has_substance = (self._PRW_substance.GetValue().strip() != u'')
1268
1269 self._PRW_component.display_as_valid(True)
1270
1271 # cannot add duplicate components
1272 if self.mode == 'new':
1273 msg = _(
1274 'The patient is already taking\n'
1275 '\n'
1276 ' %s\n'
1277 '\n'
1278 'You will want to adjust the schedule\n'
1279 'rather than document the intake twice.'
1280 )
1281 title = _('Adding substance intake entry')
1282 if has_component:
1283 emr = gmPerson.gmCurrentPatient().get_emr()
1284 if emr.substance_intake_exists(pk_component = self._PRW_component.GetData()):
1285 gmGuiHelpers.gm_show_warning (
1286 aTitle = title,
1287 aMessage = msg % self._PRW_component.GetValue().strip()
1288 )
1289 self._PRW_component.display_as_valid(False)
1290 validity = False
1291 pk_substance = self._PRW_substance.GetData()
1292 if pk_substance is not None:
1293 emr = gmPerson.gmCurrentPatient().get_emr()
1294 if emr.substance_intake_exists(pk_substance = pk_substance):
1295 gmGuiHelpers.gm_show_warning (
1296 aTitle = title,
1297 aMessage = msg % self._PRW_substance.GetValue().strip()
1298 )
1299 self._PRW_substance.display_as_valid(False)
1300 validity = False
1301
1302 # must have either brand or substance
1303 if (has_component is False) and (has_substance is False):
1304 self._PRW_substance.display_as_valid(False)
1305 self._PRW_component.display_as_valid(False)
1306 validity = False
1307 else:
1308 self._PRW_substance.display_as_valid(True)
1309
1310 # brands already have a preparation, so only required for substances
1311 if not has_component:
1312 if self._PRW_preparation.GetValue().strip() == u'':
1313 self._PRW_preparation.display_as_valid(False)
1314 validity = False
1315 else:
1316 self._PRW_preparation.display_as_valid(True)
1317
1318 # episode must be set if intake is to be approved of
1319 if self._CHBOX_approved.IsChecked():
1320 if self._PRW_episode.GetValue().strip() == u'':
1321 self._PRW_episode.display_as_valid(False)
1322 validity = False
1323 else:
1324 self._PRW_episode.display_as_valid(True)
1325
1326 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
1327 self._PRW_duration.display_as_valid(True)
1328 else:
1329 if gmDateTime.str2interval(self._PRW_duration.GetValue()) is None:
1330 self._PRW_duration.display_as_valid(False)
1331 validity = False
1332 else:
1333 self._PRW_duration.display_as_valid(True)
1334
1335 # end must be > start if at all
1336 end = self._DP_discontinued.GetData()
1337 if end is not None:
1338 start = self._DP_started.GetData()
1339 if start > end:
1340 self._DP_started.display_as_valid(False)
1341 self._DP_discontinued.display_as_valid(False)
1342 validity = False
1343 else:
1344 self._DP_started.display_as_valid(True)
1345 self._DP_discontinued.display_as_valid(True)
1346
1347 if validity is False:
1348 gmDispatcher.send(signal = 'statustext', msg = _('Input incomplete/invalid for saving as substance intake.'))
1349
1350 return validity
1351 #----------------------------------------------------------------
1353
1354 emr = gmPerson.gmCurrentPatient().get_emr()
1355 epi = self._PRW_episode.GetData(can_create = True)
1356
1357 if self._PRW_substance.GetData() is None:
1358 # auto-creates all components as intakes
1359 intake = emr.add_substance_intake (
1360 pk_component = self._PRW_component.GetData(),
1361 episode = epi
1362 )
1363 else:
1364 intake = emr.add_substance_intake (
1365 pk_substance = self._PRW_substance.GetData(),
1366 episode = epi,
1367 preparation = self._PRW_preparation.GetValue().strip()
1368 )
1369
1370 if intake is None:
1371 gmDispatcher.send('statustext', msg = _('Cannot add duplicate of (maybe inactive) substance intake.'), beep = True)
1372 return False
1373
1374 intake['started'] = self._DP_started.GetData()
1375 intake['discontinued'] = self._DP_discontinued.GetData()
1376 if intake['discontinued'] is None:
1377 intake['discontinue_reason'] = None
1378 else:
1379 intake['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
1380 intake['schedule'] = self._PRW_schedule.GetValue().strip()
1381 intake['aim'] = self._PRW_aim.GetValue().strip()
1382 intake['notes'] = self._PRW_notes.GetValue().strip()
1383 intake['is_long_term'] = self._CHBOX_long_term.IsChecked()
1384 intake['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
1385 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
1386 intake['duration'] = None
1387 else:
1388 intake['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
1389 intake.save()
1390
1391 self.data = intake
1392
1393 return True
1394 #----------------------------------------------------------------
1396
1397 # auto-applies to all components of drug if any:
1398 self.data['started'] = self._DP_started.GetData()
1399 self.data['discontinued'] = self._DP_discontinued.GetData()
1400 if self.data['discontinued'] is None:
1401 self.data['discontinue_reason'] = None
1402 else:
1403 self.data['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
1404 self.data['schedule'] = self._PRW_schedule.GetValue()
1405 self.data['is_long_term'] = self._CHBOX_long_term.IsChecked()
1406 self.data['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
1407 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]:
1408 self.data['duration'] = None
1409 else:
1410 self.data['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
1411
1412 # applies to non-component substances only
1413 self.data['preparation'] = self._PRW_preparation.GetValue()
1414
1415 # per-component
1416 self.data['aim'] = self._PRW_aim.GetValue()
1417 self.data['notes'] = self._PRW_notes.GetValue()
1418 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True)
1419
1420 self.data.save()
1421
1422 return True
1423 #----------------------------------------------------------------
1425 self._PRW_component.SetText(u'', None)
1426 self._TCTRL_brand_ingredients.SetValue(u'')
1427 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1428
1429 self._PRW_substance.SetText(u'', None)
1430 self._PRW_substance.Enable(True)
1431
1432 self._PRW_preparation.SetText(u'', None)
1433 self._PRW_preparation.Enable(True)
1434
1435 self._PRW_schedule.SetText(u'', None)
1436 self._PRW_duration.SetText(u'', None)
1437 self._PRW_aim.SetText(u'', None)
1438 self._PRW_notes.SetText(u'', None)
1439 self._PRW_episode.SetText(u'', None)
1440
1441 self._CHBOX_long_term.SetValue(False)
1442 self._CHBOX_approved.SetValue(True)
1443
1444 self._DP_started.SetData(gmDateTime.pydt_now_here())
1445 self._DP_discontinued.SetData(None)
1446 self._PRW_discontinue_reason.SetValue(u'')
1447
1448 self.__refresh_allergies()
1449
1450 self._PRW_component.SetFocus()
1451 #----------------------------------------------------------------
1453
1454 self._TCTRL_brand_ingredients.SetValue(u'')
1455 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1456
1457 if self.data['pk_brand'] is None:
1458 self.__refresh_from_existing_substance()
1459 else:
1460 self.__refresh_from_existing_component()
1461
1462 self._PRW_component.Enable(False)
1463 self._PRW_substance.Enable(False)
1464
1465 if self.data['is_long_term']:
1466 self._CHBOX_long_term.SetValue(True)
1467 self._PRW_duration.Enable(False)
1468 self._PRW_duration.SetText(gmTools.u_infinity, None)
1469 self._BTN_discontinued_as_planned.Enable(False)
1470 else:
1471 self._CHBOX_long_term.SetValue(False)
1472 self._PRW_duration.Enable(True)
1473 self._BTN_discontinued_as_planned.Enable(True)
1474 if self.data['duration'] is None:
1475 self._PRW_duration.SetText(u'', None)
1476 else:
1477 self._PRW_duration.SetText(gmDateTime.format_interval(self.data['duration'], gmDateTime.acc_days), self.data['duration'])
1478 self._PRW_aim.SetText(gmTools.coalesce(self.data['aim'], u''), self.data['aim'])
1479 self._PRW_notes.SetText(gmTools.coalesce(self.data['notes'], u''), self.data['notes'])
1480 self._PRW_episode.SetData(self.data['pk_episode'])
1481 self._PRW_schedule.SetText(gmTools.coalesce(self.data['schedule'], u''), self.data['schedule'])
1482
1483 self._CHBOX_approved.SetValue(self.data['intake_is_approved_of'])
1484
1485 self._DP_started.SetData(self.data['started'])
1486 self._DP_discontinued.SetData(self.data['discontinued'])
1487 self._PRW_discontinue_reason.SetValue(gmTools.coalesce(self.data['discontinue_reason'], u''))
1488 if self.data['discontinued'] is not None:
1489 self._PRW_discontinue_reason.Enable()
1490
1491 self.__refresh_allergies()
1492
1493 self._PRW_schedule.SetFocus()
1494 #----------------------------------------------------------------
1496 self._LBL_component.Enable(False)
1497 self._PRW_component.SetText(u'', None)
1498 self._PRW_component.display_as_valid(True)
1499
1500 self._PRW_substance.SetText (
1501 u'%s %s%s' % (self.data['substance'], self.data['amount'], self.data['unit']),
1502 self.data['pk_substance']
1503 )
1504
1505 self._PRW_preparation.SetText(gmTools.coalesce(self.data['preparation'], u''), self.data['preparation'])
1506 self._PRW_preparation.Enable(True)
1507 #----------------------------------------------------------------
1509 self._PRW_component.SetText (
1510 u'%s %s%s (%s)' % (self.data['substance'], self.data['amount'], self.data['unit'], self.data['brand']),
1511 self.data['pk_drug_component']
1512 )
1513
1514 brand = gmMedication.cBrandedDrug(aPK_obj = self.data['pk_brand'])
1515 if brand['components'] is not None:
1516 self._TCTRL_brand_ingredients.SetValue(u'; '.join(brand['components']))
1517 tt = u'%s:\n\n- %s' % (
1518 self.data['brand'],
1519 u'\n- '.join(brand['components'])
1520 )
1521 self._TCTRL_brand_ingredients.SetToolTipString(tt)
1522
1523 self._LBL_or.Enable(False)
1524 self._LBL_substance.Enable(False)
1525 self._PRW_substance.SetText(u'', None)
1526 self._PRW_substance.display_as_valid(True)
1527
1528 self._PRW_preparation.SetText(self.data['preparation'], self.data['preparation'])
1529 self._PRW_preparation.Enable(False)
1530 #----------------------------------------------------------------
1533 #----------------------------------------------------------------
1534 # event handlers
1535 #----------------------------------------------------------------
1537 if self._PRW_component.GetData() is None:
1538 self._LBL_or.Enable(True)
1539 self._PRW_component.SetText(u'', None)
1540 self._LBL_substance.Enable(True)
1541 self._PRW_substance.Enable(True)
1542 self._LBL_preparation.Enable(True)
1543 self._PRW_preparation.Enable(True)
1544 self._PRW_preparation.SetText(u'', None)
1545 self._TCTRL_brand_ingredients.SetValue(u'')
1546 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1547 else:
1548 self._LBL_or.Enable(False)
1549 self._LBL_substance.Enable(False)
1550 self._PRW_substance.SetText(u'', None)
1551 self._PRW_substance.display_as_valid(True)
1552 self._PRW_substance.Enable(False)
1553 self._LBL_preparation.Enable(False)
1554 self._PRW_preparation.Enable(False)
1555 comp = gmMedication.cDrugComponent(aPK_obj = self._PRW_component.GetData())
1556 self._PRW_preparation.SetText(comp['preparation'], comp['preparation'])
1557 brand = comp.containing_drug
1558 if brand['components'] is not None:
1559 self._TCTRL_brand_ingredients.SetValue(u'; '.join(brand['components']))
1560 tt = u'%s:\n\n- %s' % (
1561 brand['brand'],
1562 u'\n- '.join(brand['components'])
1563 )
1564 self._TCTRL_brand_ingredients.SetToolTipString(tt)
1565 #----------------------------------------------------------------
1567 if self._PRW_substance.GetData() is None:
1568 self._LBL_or.Enable(True)
1569 self._LBL_component.Enable(True)
1570 self._PRW_component.Enable(True)
1571 self._PRW_substance.SetText(u'', None)
1572 else:
1573 self._LBL_or.Enable(False)
1574 self._LBL_component.Enable(False)
1575 self._PRW_component.SetText(u'', None)
1576 self._PRW_component.display_as_valid(True)
1577 self._PRW_component.Enable(False)
1578 self._LBL_preparation.Enable(True)
1579 self._PRW_preparation.Enable(True)
1580 self._TCTRL_brand_ingredients.SetValue(u'')
1581 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1582 #----------------------------------------------------------------
1584 if self._DP_discontinued.GetData() is None:
1585 self._PRW_discontinue_reason.Enable(False)
1586 else:
1587 self._PRW_discontinue_reason.Enable(True)
1588 #----------------------------------------------------------------
1591 #----------------------------------------------------------------
1594 #----------------------------------------------------------------
1597 #----------------------------------------------------------------
1609 #----------------------------------------------------------------
1639 #----------------------------------------------------------------
1641 if self._CHBOX_long_term.IsChecked() is True:
1642 self._PRW_duration.Enable(False)
1643 self._BTN_discontinued_as_planned.Enable(False)
1644 self._PRW_discontinue_reason.Enable(False)
1645 else:
1646 self._PRW_duration.Enable(True)
1647 self._BTN_discontinued_as_planned.Enable(True)
1648 self._PRW_discontinue_reason.Enable(True)
1649
1650 self.__refresh_allergies()
1651 #----------------------------------------------------------------
1653 if not self.save():
1654 return False
1655
1656 return turn_substance_intake_into_allergy (
1657 parent = self,
1658 intake = self.data,
1659 emr = gmPerson.gmCurrentPatient().get_emr()
1660 )
1661 #============================================================
1663
1664 subst = gmMedication.cSubstanceIntakeEntry(aPK_obj = substance)
1665 msg = _(
1666 '\n'
1667 '[%s]\n'
1668 '\n'
1669 'It may be prudent to edit (before deletion) the details\n'
1670 'of this substance intake entry so as to leave behind\n'
1671 'some indication of why it was deleted.\n'
1672 ) % subst.format()
1673
1674 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
1675 parent,
1676 -1,
1677 caption = _('Deleting medication / substance intake'),
1678 question = msg,
1679 button_defs = [
1680 {'label': _('&Edit'), 'tooltip': _('Allow editing of substance intake entry before deletion.'), 'default': True},
1681 {'label': _('&Delete'), 'tooltip': _('Delete immediately without editing first.')},
1682 {'label': _('&Cancel'), 'tooltip': _('Abort. Do not delete or edit substance intake entry.')}
1683 ]
1684 )
1685
1686 edit_first = dlg.ShowModal()
1687 dlg.Destroy()
1688
1689 if edit_first == wx.ID_CANCEL:
1690 return
1691
1692 if edit_first == wx.ID_YES:
1693 edit_intake_of_substance(parent = parent, substance = subst)
1694 delete_it = gmGuiHelpers.gm_show_question (
1695 aMessage = _('Now delete substance intake entry ?'),
1696 aTitle = _('Deleting medication / substance intake')
1697 )
1698 else:
1699 delete_it = True
1700
1701 if not delete_it:
1702 return
1703
1704 gmMedication.delete_substance_intake(substance = substance)
1705 #------------------------------------------------------------
1707 ea = cSubstanceIntakeEAPnl(parent = parent, id = -1, substance = substance)
1708 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = (substance is not None))
1709 dlg.SetTitle(gmTools.coalesce(substance, _('Adding medication/non-medication substance intake'), _('Editing medication/non-medication substance intake')))
1710 dlg.left_extra_button = (
1711 _('Allergy'),
1712 _('Document an allergy against this substance.'),
1713 ea.turn_into_allergy
1714 )
1715 if dlg.ShowModal() == wx.ID_OK:
1716 dlg.Destroy()
1717 return True
1718 dlg.Destroy()
1719 return False
1720
1721 #============================================================
1722 # current substances grid
1723 #------------------------------------------------------------
1725
1726 if parent is None:
1727 parent = wx.GetApp().GetTopWindow()
1728
1729 template = gmFormWidgets.manage_form_templates (
1730 parent = parent,
1731 template_types = ['current medication list']
1732 )
1733 option = u'form_templates.medication_list'
1734
1735 if template is None:
1736 gmDispatcher.send(signal = 'statustext', msg = _('No medication list template configured.'), beep = True)
1737 return None
1738
1739 if template['engine'] != u'L':
1740 gmDispatcher.send(signal = 'statustext', msg = _('No medication list template configured.'), beep = True)
1741 return None
1742
1743 dbcfg = gmCfg.cCfgSQL()
1744 dbcfg.set (
1745 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1746 option = option,
1747 value = u'%s - %s' % (template['name_long'], template['external_version'])
1748 )
1749
1750 return template
1751 #------------------------------------------------------------
1753
1754 if parent is None:
1755 parent = wx.GetApp().GetTopWindow()
1756
1757 # 1) get template
1758 dbcfg = gmCfg.cCfgSQL()
1759 option = u'form_templates.medication_list'
1760
1761 template = dbcfg.get2 (
1762 option = option,
1763 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1764 bias = 'user'
1765 )
1766
1767 if template is None:
1768 template = configure_medication_list_template(parent = parent)
1769 if template is None:
1770 gmGuiHelpers.gm_show_error (
1771 aMessage = _('There is no medication list template configured.'),
1772 aTitle = _('Printing medication list')
1773 )
1774 return False
1775 else:
1776 try:
1777 name, ver = template.split(u' - ')
1778 except:
1779 _log.exception('problem splitting medication list template name [%s]', template)
1780 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading medication list template.'), beep = True)
1781 return False
1782 template = gmForms.get_form_template(name_long = name, external_version = ver)
1783 if template is None:
1784 gmGuiHelpers.gm_show_error (
1785 aMessage = _('Cannot load medication list template [%s - %s]') % (name, ver),
1786 aTitle = _('Printing medication list')
1787 )
1788 return False
1789
1790 # 2) process template
1791 try:
1792 meds_list = template.instantiate()
1793 except KeyError:
1794 _log.exception('cannot instantiate medication list template [%s]', template)
1795 gmGuiHelpers.gm_show_error (
1796 aMessage = _('Invalid medication list template [%s - %s (%s)]') % (name, ver, template['engine']),
1797 aTitle = _('Printing medication list')
1798 )
1799 return False
1800
1801 ph = gmMacro.gmPlaceholderHandler()
1802 #ph.debug = True
1803 meds_list.substitute_placeholders(data_source = ph)
1804 pdf_name = meds_list.generate_output()
1805 if pdf_name is None:
1806 gmGuiHelpers.gm_show_error (
1807 aMessage = _('Error generating the medication list.'),
1808 aTitle = _('Printing medication list')
1809 )
1810 return False
1811
1812 # 3) print template
1813 printed = gmPrinting.print_files(filenames = [pdf_name], jobtype = 'medication_list')
1814 if not printed:
1815 gmGuiHelpers.gm_show_error (
1816 aMessage = _('Error printing the medication list.'),
1817 aTitle = _('Printing medication list')
1818 )
1819 return False
1820
1821 pat = gmPerson.gmCurrentPatient()
1822 emr = pat.get_emr()
1823 epi = emr.add_episode(episode_name = 'administration', is_open = False)
1824 emr.add_clin_narrative (
1825 soap_cat = None,
1826 note = _('medication list printed from template [%s - %s]') % (template['name_long'], template['external_version']),
1827 episode = epi
1828 )
1829
1830 return True
1831 #------------------------------------------------------------
1832 -def update_substance_intake_list_from_prescription(parent=None, prescribed_drugs=None, emr=None):
1833
1834 if len(prescribed_drugs) == 0:
1835 return
1836
1837 curr_brands = [ i['pk_brand'] for i in emr.get_current_substance_intake() if i['pk_brand'] is not None ]
1838 new_drugs = []
1839 for drug in prescribed_drugs:
1840 if drug['pk_brand'] not in curr_brands:
1841 new_drugs.append(drug)
1842
1843 if len(new_drugs) == 0:
1844 return
1845
1846 if parent is None:
1847 parent = wx.GetApp().GetTopWindow()
1848
1849 dlg = gmListWidgets.cItemPickerDlg (
1850 parent,
1851 -1,
1852 msg = _(
1853 'These brands have been prescribed but are not listed\n'
1854 'in the current medication list of this patient.\n'
1855 '\n'
1856 'Please select those you want added to the medication list.'
1857 )
1858 )
1859 dlg.set_columns (
1860 columns = [_('Newly prescribed drugs')],
1861 columns_right = [_('Add to medication list')]
1862 )
1863 choices = [ (u'%s %s (%s)' % (d['brand'], d['preparation'], u'; '.join(d['components']))) for d in new_drugs ]
1864 dlg.set_choices (
1865 choices = choices,
1866 data = new_drugs
1867 )
1868 dlg.ShowModal()
1869 drugs2add = dlg.get_picks()
1870 dlg.Destroy()
1871
1872 if drugs2add is None:
1873 return
1874
1875 if len(drugs2add) == 0:
1876 return
1877
1878 for drug in drugs2add:
1879 # only add first component since all other components get added by a trigger ...
1880 intake = emr.add_substance_intake (
1881 pk_component = drug['pk_components'][0],
1882 episode = emr.add_episode(episode_name = gmMedication.DEFAULT_MEDICATION_HISTORY_EPISODE)['pk_episode'],
1883 )
1884 if intake is None:
1885 continue
1886 intake['intake_is_approved_of'] = True
1887 intake.save()
1888
1889 return
1890 #------------------------------------------------------------
1892 """A grid class for displaying current substance intake.
1893
1894 - does NOT listen to the currently active patient
1895 - thereby it can display any patient at any time
1896 """
1898
1899 wx.grid.Grid.__init__(self, *args, **kwargs)
1900
1901 self.__patient = None
1902 self.__row_data = {}
1903 self.__prev_row = None
1904 self.__prev_tooltip_row = None
1905 self.__prev_cell_0 = None
1906 self.__grouping_mode = u'issue'
1907 self.__filter_show_unapproved = True
1908 self.__filter_show_inactive = True
1909
1910 self.__grouping2col_labels = {
1911 u'issue': [
1912 _('Health issue'),
1913 _('Substance'),
1914 _('Strength'),
1915 _('Schedule'),
1916 _('Started'),
1917 _('Duration / Until'),
1918 _('Brand'),
1919 _('Advice')
1920 ],
1921 u'brand': [
1922 _('Brand'),
1923 _('Schedule'),
1924 _('Substance'),
1925 _('Strength'),
1926 _('Started'),
1927 _('Duration / Until'),
1928 _('Health issue'),
1929 _('Advice')
1930 ],
1931 u'episode': [
1932 _('Episode'),
1933 _('Substance'),
1934 _('Strength'),
1935 _('Schedule'),
1936 _('Started'),
1937 _('Duration / Until'),
1938 _('Brand'),
1939 _('Advice')
1940 ]
1941 }
1942
1943 self.__grouping2order_by_clauses = {
1944 u'issue': u'pk_health_issue nulls first, substance, started',
1945 u'episode': u'pk_health_issue nulls first, episode, substance, started',
1946 u'brand': u'brand nulls last, substance, started'
1947 }
1948
1949 self.__init_ui()
1950 self.__register_events()
1951 #------------------------------------------------------------
1952 # external API
1953 #------------------------------------------------------------
1955
1956 sel_block_top_left = self.GetSelectionBlockTopLeft()
1957 sel_block_bottom_right = self.GetSelectionBlockBottomRight()
1958 sel_cols = self.GetSelectedCols()
1959 sel_rows = self.GetSelectedRows()
1960
1961 selected_cells = []
1962
1963 # individually selected cells (ctrl-click)
1964 selected_cells += self.GetSelectedCells()
1965
1966 # selected rows
1967 selected_cells += list (
1968 (row, col)
1969 for row in sel_rows
1970 for col in xrange(self.GetNumberCols())
1971 )
1972
1973 # selected columns
1974 selected_cells += list (
1975 (row, col)
1976 for row in xrange(self.GetNumberRows())
1977 for col in sel_cols
1978 )
1979
1980 # selection blocks
1981 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()):
1982 selected_cells += [
1983 (row, col)
1984 for row in xrange(top_left[0], bottom_right[0] + 1)
1985 for col in xrange(top_left[1], bottom_right[1] + 1)
1986 ]
1987
1988 return set(selected_cells)
1989 #------------------------------------------------------------
1991 rows = {}
1992
1993 for row, col in self.get_selected_cells():
1994 rows[row] = True
1995
1996 return rows.keys()
1997 #------------------------------------------------------------
1999 return [ self.__row_data[row] for row in self.get_selected_rows() ]
2000 #------------------------------------------------------------
2002
2003 self.empty_grid()
2004
2005 if self.__patient is None:
2006 return
2007
2008 emr = self.__patient.get_emr()
2009 meds = emr.get_current_substance_intake (
2010 order_by = self.__grouping2order_by_clauses[self.__grouping_mode],
2011 include_unapproved = self.__filter_show_unapproved,
2012 include_inactive = self.__filter_show_inactive
2013 )
2014 if not meds:
2015 return
2016
2017 self.BeginBatch()
2018
2019 # columns
2020 labels = self.__grouping2col_labels[self.__grouping_mode]
2021 if self.__filter_show_unapproved:
2022 self.AppendCols(numCols = len(labels) + 1)
2023 else:
2024 self.AppendCols(numCols = len(labels))
2025 for col_idx in range(len(labels)):
2026 self.SetColLabelValue(col_idx, labels[col_idx])
2027 if self.__filter_show_unapproved:
2028 self.SetColLabelValue(len(labels), u'OK?')
2029 self.SetColSize(len(labels), 40)
2030
2031 self.AppendRows(numRows = len(meds))
2032
2033 # loop over data
2034 for row_idx in range(len(meds)):
2035 med = meds[row_idx]
2036 self.__row_data[row_idx] = med
2037
2038 if med['is_currently_active'] is True:
2039 atcs = []
2040 if med['atc_substance'] is not None:
2041 atcs.append(med['atc_substance'])
2042 # if med['atc_brand'] is not None:
2043 # atcs.append(med['atc_brand'])
2044 # allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (med['substance'],), brand = med['brand'])
2045 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (med['substance'],))
2046 if allg not in [None, False]:
2047 attr = self.GetOrCreateCellAttr(row_idx, 0)
2048 if allg['type'] == u'allergy':
2049 attr.SetTextColour('red')
2050 else:
2051 attr.SetTextColour('yellow')
2052 self.SetRowAttr(row_idx, attr)
2053 else:
2054 attr = self.GetOrCreateCellAttr(row_idx, 0)
2055 attr.SetTextColour('grey')
2056 self.SetRowAttr(row_idx, attr)
2057
2058 if self.__grouping_mode == u'episode':
2059 if med['pk_episode'] is None:
2060 self.__prev_cell_0 = None
2061 epi = gmTools.u_diameter
2062 else:
2063 if self.__prev_cell_0 == med['episode']:
2064 epi = u''
2065 else:
2066 self.__prev_cell_0 = med['episode']
2067 epi = gmTools.coalesce(med['episode'], u'')
2068 self.SetCellValue(row_idx, 0, gmTools.wrap(text = epi, width = 40))
2069
2070 self.SetCellValue(row_idx, 1, med['substance'])
2071 self.SetCellValue(row_idx, 2, u'%s%s' % (med['amount'], med['unit']))
2072 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], u''))
2073 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
2074
2075 if med['is_long_term']:
2076 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
2077 else:
2078 if med['discontinued'] is None:
2079 if med['duration'] is None:
2080 self.SetCellValue(row_idx, 5, u'')
2081 else:
2082 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
2083 else:
2084 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d'))
2085
2086 if med['pk_brand'] is None:
2087 brand = u''
2088 else:
2089 if med['fake_brand']:
2090 brand = gmTools.coalesce(med['brand'], u'', _('%s (fake)'))
2091 else:
2092 brand = gmTools.coalesce(med['brand'], u'')
2093 self.SetCellValue(row_idx, 6, gmTools.wrap(text = brand, width = 35))
2094
2095 elif self.__grouping_mode == u'issue':
2096 if med['pk_health_issue'] is None:
2097 self.__prev_cell_0 = None
2098 issue = u'%s%s' % (
2099 gmTools.u_diameter,
2100 gmTools.coalesce(med['episode'], u'', u' (%s)')
2101 )
2102 else:
2103 if self.__prev_cell_0 == med['health_issue']:
2104 issue = u''
2105 else:
2106 self.__prev_cell_0 = med['health_issue']
2107 issue = med['health_issue']
2108 self.SetCellValue(row_idx, 0, gmTools.wrap(text = issue, width = 40))
2109
2110 self.SetCellValue(row_idx, 1, med['substance'])
2111 self.SetCellValue(row_idx, 2, u'%s%s' % (med['amount'], med['unit']))
2112 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], u''))
2113 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
2114
2115 if med['is_long_term']:
2116 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
2117 else:
2118 if med['discontinued'] is None:
2119 if med['duration'] is None:
2120 self.SetCellValue(row_idx, 5, u'')
2121 else:
2122 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
2123 else:
2124 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d'))
2125
2126 if med['pk_brand'] is None:
2127 brand = u''
2128 else:
2129 if med['fake_brand']:
2130 brand = gmTools.coalesce(med['brand'], u'', _('%s (fake)'))
2131 else:
2132 brand = gmTools.coalesce(med['brand'], u'')
2133 self.SetCellValue(row_idx, 6, gmTools.wrap(text = brand, width = 35))
2134
2135 elif self.__grouping_mode == u'brand':
2136
2137 if med['pk_brand'] is None:
2138 self.__prev_cell_0 = None
2139 brand = gmTools.u_diameter
2140 else:
2141 if self.__prev_cell_0 == med['brand']:
2142 brand = u''
2143 else:
2144 self.__prev_cell_0 = med['brand']
2145 if med['fake_brand']:
2146 brand = gmTools.coalesce(med['brand'], u'', _('%s (fake)'))
2147 else:
2148 brand = gmTools.coalesce(med['brand'], u'')
2149 self.SetCellValue(row_idx, 0, gmTools.wrap(text = brand, width = 35))
2150
2151 self.SetCellValue(row_idx, 1, gmTools.coalesce(med['schedule'], u''))
2152 self.SetCellValue(row_idx, 2, med['substance'])
2153 self.SetCellValue(row_idx, 3, u'%s%s' % (med['amount'], med['unit']))
2154 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d'))
2155
2156 if med['is_long_term']:
2157 self.SetCellValue(row_idx, 5, gmTools.u_infinity)
2158 else:
2159 if med['discontinued'] is None:
2160 if med['duration'] is None:
2161 self.SetCellValue(row_idx, 5, u'')
2162 else:
2163 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days))
2164 else:
2165 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d'))
2166
2167 if med['pk_health_issue'] is None:
2168 issue = u'%s%s' % (
2169 gmTools.u_diameter,
2170 gmTools.coalesce(med['episode'], u'', u' (%s)')
2171 )
2172 else:
2173 issue = gmTools.coalesce(med['health_issue'], u'')
2174 self.SetCellValue(row_idx, 6, gmTools.wrap(text = issue, width = 40))
2175
2176 else:
2177 raise ValueError('unknown grouping mode [%s]' % self.__grouping_mode)
2178
2179 if med['notes'] is not None:
2180 self.SetCellValue(row_idx, 7, gmTools.wrap(text = med['notes'], width = 50))
2181
2182 if self.__filter_show_unapproved:
2183 self.SetCellValue (
2184 row_idx,
2185 len(labels),
2186 gmTools.bool2subst(med['intake_is_approved_of'], gmTools.u_checkmark_thin, u'', u'?')
2187 )
2188
2189 #self.SetCellAlignment(row, col, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
2190
2191 self.AutoSize()
2192 self.EndBatch()
2193 #------------------------------------------------------------
2195 self.BeginBatch()
2196 self.ClearGrid()
2197 # Windows cannot do "nothing", it rather decides to assert()
2198 # on thinking it is supposed to do nothing
2199 if self.GetNumberRows() > 0:
2200 self.DeleteRows(pos = 0, numRows = self.GetNumberRows())
2201 if self.GetNumberCols() > 0:
2202 self.DeleteCols(pos = 0, numCols = self.GetNumberCols())
2203 self.EndBatch()
2204 self.__row_data = {}
2205 self.__prev_cell_0 = None
2206 #------------------------------------------------------------
2208
2209 if len(self.__row_data) == 0:
2210 return
2211
2212 sel_rows = self.get_selected_rows()
2213 if len(sel_rows) != 1:
2214 return
2215
2216 drug_db = get_drug_database()
2217 if drug_db is None:
2218 return
2219
2220 intake = self.get_selected_data()[0] # just in case
2221 if intake['brand'] is None:
2222 drug_db.show_info_on_substance(substance_intake = intake)
2223 else:
2224 drug_db.show_info_on_drug(substance_intake = intake)
2225 #------------------------------------------------------------
2227 search_term = None
2228 if len(self.__row_data) > 0:
2229 sel_rows = self.get_selected_rows()
2230 if len(sel_rows) == 1:
2231 search_term = self.get_selected_data()[0]
2232 gmNetworkTools.open_url_in_browser(url = gmMedication.drug2renal_insufficiency_url(search_term = search_term))
2233 #------------------------------------------------------------
2236 #------------------------------------------------------------
2238
2239 dbcfg = gmCfg.cCfgSQL()
2240
2241 url = dbcfg.get2 (
2242 option = u'external.urls.report_ADR',
2243 workplace = gmSurgery.gmCurrentPractice().active_workplace,
2244 bias = u'user',
2245 default = u'https://dcgma.org/uaw/meldung.php' # http://www.akdae.de/Arzneimittelsicherheit/UAW-Meldung/UAW-Meldung-online.html
2246 )
2247 gmNetworkTools.open_url_in_browser(url = url)
2248 #------------------------------------------------------------
2250 drug_db = get_drug_database()
2251 if drug_db is None:
2252 return
2253
2254 drug_db.reviewer = gmStaff.gmCurrentProvider()
2255 update_substance_intake_list_from_prescription (
2256 parent = self,
2257 prescribed_drugs = drug_db.prescribe(),
2258 emr = self.__patient.get_emr()
2259 )
2260 #------------------------------------------------------------
2262
2263 if len(self.__row_data) == 0:
2264 return
2265
2266 drug_db = get_drug_database()
2267 if drug_db is None:
2268 return
2269
2270 if len(self.get_selected_rows()) > 1:
2271 drug_db.check_interactions(substance_intakes = self.get_selected_data())
2272 else:
2273 drug_db.check_interactions(substance_intakes = self.__row_data.values())
2274 #------------------------------------------------------------
2277 #------------------------------------------------------------
2279
2280 rows = self.get_selected_rows()
2281
2282 if len(rows) == 0:
2283 return
2284
2285 if len(rows) > 1:
2286 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit more than one substance at once.'), beep = True)
2287 return
2288
2289 subst = self.get_selected_data()[0]
2290 edit_intake_of_substance(parent = self, substance = subst)
2291 #------------------------------------------------------------
2293
2294 rows = self.get_selected_rows()
2295
2296 if len(rows) == 0:
2297 return
2298
2299 if len(rows) > 1:
2300 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete more than one substance at once.'), beep = True)
2301 return
2302
2303 subst = self.get_selected_data()[0]
2304 delete_substance_intake(parent = self, substance = subst['pk_substance_intake'])
2305 #------------------------------------------------------------
2307 rows = self.get_selected_rows()
2308
2309 if len(rows) == 0:
2310 return
2311
2312 if len(rows) > 1:
2313 gmDispatcher.send(signal = 'statustext', msg = _('Cannot create allergy from more than one substance at once.'), beep = True)
2314 return
2315
2316 return turn_substance_intake_into_allergy (
2317 parent = self,
2318 intake = self.get_selected_data()[0],
2319 emr = self.__patient.get_emr()
2320 )
2321 #------------------------------------------------------------
2323 # there could be some filtering/user interaction going on here
2324 print_medication_list(parent = self)
2325 #------------------------------------------------------------
2327
2328 try:
2329 entry = self.__row_data[row]
2330 except KeyError:
2331 return u' '
2332
2333 emr = self.__patient.get_emr()
2334 atcs = []
2335 if entry['atc_substance'] is not None:
2336 atcs.append(entry['atc_substance'])
2337 # if entry['atc_brand'] is not None:
2338 # atcs.append(entry['atc_brand'])
2339 # allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (entry['substance'],), brand = entry['brand'])
2340 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (entry['substance'],))
2341
2342 tt = _('Substance intake entry (%s, %s) [#%s] \n') % (
2343 gmTools.bool2subst (
2344 boolean = entry['is_currently_active'],
2345 true_return = gmTools.bool2subst (
2346 boolean = entry['seems_inactive'],
2347 true_return = _('active, needs check'),
2348 false_return = _('active'),
2349 none_return = _('assumed active')
2350 ),
2351 false_return = _('inactive')
2352 ),
2353 gmTools.bool2subst (
2354 boolean = entry['intake_is_approved_of'],
2355 true_return = _('approved'),
2356 false_return = _('unapproved')
2357 ),
2358 entry['pk_substance_intake']
2359 )
2360
2361 if allg not in [None, False]:
2362 certainty = gmTools.bool2subst(allg['definite'], _('definite'), _('suspected'))
2363 tt += u'\n'
2364 tt += u' !! ---- Cave ---- !!\n'
2365 tt += u' %s (%s): %s (%s)\n' % (
2366 allg['l10n_type'],
2367 certainty,
2368 allg['descriptor'],
2369 gmTools.coalesce(allg['reaction'], u'')[:40]
2370 )
2371 tt += u'\n'
2372
2373 tt += u' ' + _('Substance: %s [#%s]\n') % (entry['substance'], entry['pk_substance'])
2374 tt += u' ' + _('Preparation: %s\n') % entry['preparation']
2375 tt += u' ' + _('Amount per dose: %s%s') % (entry['amount'], entry['unit'])
2376 if entry.ddd is not None:
2377 tt += u' (DDD: %s %s)' % (entry.ddd['ddd'], entry.ddd['unit'])
2378 tt += u'\n'
2379 tt += gmTools.coalesce(entry['atc_substance'], u'', _(' ATC (substance): %s\n'))
2380
2381 tt += u'\n'
2382
2383 tt += gmTools.coalesce (
2384 entry['brand'],
2385 u'',
2386 _(' Brand name: %%s [#%s]\n') % entry['pk_brand']
2387 )
2388 tt += gmTools.coalesce(entry['atc_brand'], u'', _(' ATC (brand): %s\n'))
2389
2390 tt += u'\n'
2391
2392 tt += gmTools.coalesce(entry['schedule'], u'', _(' Regimen: %s\n'))
2393
2394 if entry['is_long_term']:
2395 duration = u' %s %s' % (gmTools.u_right_arrow, gmTools.u_infinity)
2396 else:
2397 if entry['duration'] is None:
2398 duration = u''
2399 else:
2400 duration = u' %s %s' % (gmTools.u_right_arrow, gmDateTime.format_interval(entry['duration'], gmDateTime.acc_days))
2401
2402 tt += _(' Started %s%s%s\n') % (
2403 entry['started'].strftime('%Y %B %d').decode(gmI18N.get_encoding()),
2404 duration,
2405 gmTools.bool2subst(entry['is_long_term'], _(' (long-term)'), _(' (short-term)'), u'')
2406 )
2407
2408 if entry['discontinued'] is not None:
2409 tt += _(' Discontinued %s\n') % (
2410 entry['discontinued'].strftime('%Y %B %d').decode(gmI18N.get_encoding()),
2411 )
2412 tt += _(' Reason: %s\n') % entry['discontinue_reason']
2413
2414 tt += u'\n'
2415
2416 tt += gmTools.coalesce(entry['aim'], u'', _(' Aim: %s\n'))
2417 tt += gmTools.coalesce(entry['episode'], u'', _(' Episode: %s\n'))
2418 tt += gmTools.coalesce(entry['health_issue'], u'', _(' Health issue: %s\n'))
2419 tt += gmTools.coalesce(entry['notes'], u'', _(' Advice: %s\n'))
2420
2421 tt += u'\n'
2422
2423 tt += _(u'Revision: #%(row_ver)s, %(mod_when)s by %(mod_by)s.') % ({
2424 'row_ver': entry['row_version'],
2425 'mod_when': entry['modified_when'].strftime('%c').decode(gmI18N.get_encoding()),
2426 'mod_by': entry['modified_by']
2427 })
2428
2429 return tt
2430 #------------------------------------------------------------
2431 # internal helpers
2432 #------------------------------------------------------------
2434 self.CreateGrid(0, 1)
2435 self.EnableEditing(0)
2436 self.EnableDragGridSize(1)
2437 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows)
2438
2439 self.SetColLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTER)
2440
2441 self.SetRowLabelSize(0)
2442 self.SetRowLabelAlignment(horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
2443 #------------------------------------------------------------
2444 # properties
2445 #------------------------------------------------------------
2448
2452
2453 patient = property(_get_patient, _set_patient)
2454 #------------------------------------------------------------
2457
2461
2462 grouping_mode = property(_get_grouping_mode, _set_grouping_mode)
2463 #------------------------------------------------------------
2466
2470
2471 filter_show_unapproved = property(_get_filter_show_unapproved, _set_filter_show_unapproved)
2472 #------------------------------------------------------------
2475
2479
2480 filter_show_inactive = property(_get_filter_show_inactive, _set_filter_show_inactive)
2481 #------------------------------------------------------------
2482 # event handling
2483 #------------------------------------------------------------
2485 # dynamic tooltips: GridWindow, GridRowLabelWindow, GridColLabelWindow, GridCornerLabelWindow
2486 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells)
2487 #self.GetGridRowLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_row_labels)
2488 #self.GetGridColLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_col_labels)
2489
2490 # editing cells
2491 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
2492 #------------------------------------------------------------
2494 """Calculate where the mouse is and set the tooltip dynamically."""
2495
2496 # Use CalcUnscrolledPosition() to get the mouse position within the
2497 # entire grid including what's offscreen
2498 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
2499
2500 # use this logic to prevent tooltips outside the actual cells
2501 # apply to GetRowSize, too
2502 # tot = 0
2503 # for col in xrange(self.NumberCols):
2504 # tot += self.GetColSize(col)
2505 # if xpos <= tot:
2506 # self.tool_tip.Tip = 'Tool tip for Column %s' % (
2507 # self.GetColLabelValue(col))
2508 # break
2509 # else: # mouse is in label area beyond the right-most column
2510 # self.tool_tip.Tip = ''
2511
2512 row, col = self.XYToCell(x, y)
2513
2514 if row == self.__prev_tooltip_row:
2515 return
2516
2517 self.__prev_tooltip_row = row
2518
2519 try:
2520 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row))
2521 except KeyError:
2522 pass
2523 #------------------------------------------------------------
2525 row = evt.GetRow()
2526 data = self.__row_data[row]
2527 edit_intake_of_substance(parent = self, substance = data)
2528 #============================================================
2529 from Gnumed.wxGladeWidgets import wxgCurrentSubstancesPnl
2530
2531 -class cCurrentSubstancesPnl(wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl, gmRegetMixin.cRegetOnPaintMixin):
2532
2533 """Panel holding a grid with current substances. Used as notebook page."""
2534
2536
2537 wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl.__init__(self, *args, **kwargs)
2538 gmRegetMixin.cRegetOnPaintMixin.__init__(self)
2539
2540 self.__register_interests()
2541 #-----------------------------------------------------
2542 # reget-on-paint mixin API
2543 #-----------------------------------------------------
2545 """Populate cells with data from model."""
2546 pat = gmPerson.gmCurrentPatient()
2547 if pat.connected:
2548 self._grid_substances.patient = pat
2549 else:
2550 self._grid_substances.patient = None
2551 return True
2552 #--------------------------------------------------------
2553 # event handling
2554 #--------------------------------------------------------
2556 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
2557 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._schedule_data_reget)
2558 gmDispatcher.connect(signal = u'substance_intake_mod_db', receiver = self._schedule_data_reget)
2559 # active_substance_mod_db
2560 # substance_brand_mod_db
2561 #--------------------------------------------------------
2564 #--------------------------------------------------------
2566 self._grid_substances.patient = None
2567 #--------------------------------------------------------
2570 #--------------------------------------------------------
2573 #--------------------------------------------------------
2576 #--------------------------------------------------------
2579 #--------------------------------------------------------
2582 #--------------------------------------------------------
2584 self._grid_substances.grouping_mode = 'issue'
2585 #--------------------------------------------------------
2587 self._grid_substances.grouping_mode = 'episode'
2588 #--------------------------------------------------------
2590 self._grid_substances.grouping_mode = 'brand'
2591 #--------------------------------------------------------
2594 #--------------------------------------------------------
2597 #--------------------------------------------------------
2600 #--------------------------------------------------------
2603 #--------------------------------------------------------
2606 #--------------------------------------------------------
2609 #--------------------------------------------------------
2612 #--------------------------------------------------------
2615 #============================================================
2616 # main
2617 #------------------------------------------------------------
2618 if __name__ == '__main__':
2619
2620 if len(sys.argv) < 2:
2621 sys.exit()
2622
2623 if sys.argv[1] != 'test':
2624 sys.exit()
2625
2626 from Gnumed.pycommon import gmI18N
2627
2628 gmI18N.activate_locale()
2629 gmI18N.install_domain(domain = 'gnumed')
2630
2631 #----------------------------------------
2632 app = wx.PyWidgetTester(size = (600, 600))
2633 #app.SetWidget(cATCPhraseWheel, -1)
2634 app.SetWidget(cSubstancePhraseWheel, -1)
2635 app.MainLoop()
2636
2637 #============================================================
2638
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Mon Jun 25 03:59:05 2012 | http://epydoc.sourceforge.net |