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