| Home | Trees | Indices | Help |
|
|---|
|
|
1 """GNUmed medication 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 datetime as pydt
10
11
12 import wx
13 import wx.grid
14
15
16 if __name__ == '__main__':
17 sys.path.insert(0, '../../')
18 from Gnumed.pycommon import gmI18N
19 gmI18N.activate_locale()
20 gmI18N.install_domain(domain = 'gnumed')
21
22 from Gnumed.pycommon import gmDispatcher
23 from Gnumed.pycommon import gmCfg
24 from Gnumed.pycommon import gmTools
25 from Gnumed.pycommon import gmDateTime
26 from Gnumed.pycommon import gmMatchProvider
27 from Gnumed.pycommon import gmI18N
28 from Gnumed.pycommon import gmPrinting
29 from Gnumed.pycommon import gmCfg2
30 from Gnumed.pycommon import gmNetworkTools
31
32 from Gnumed.business import gmPerson
33 from Gnumed.business import gmPraxis
34 from Gnumed.business import gmMedication
35 from Gnumed.business import gmForms
36 from Gnumed.business import gmStaff
37 from Gnumed.business import gmDocuments
38 from Gnumed.business import gmLOINC
39 from Gnumed.business import gmClinicalRecord
40 from Gnumed.business import gmClinicalCalculator
41 from Gnumed.business import gmPathLab
42
43 from Gnumed.wxpython import gmGuiHelpers
44 from Gnumed.wxpython import gmRegetMixin
45 from Gnumed.wxpython import gmAuthWidgets
46 from Gnumed.wxpython import gmEditArea
47 from Gnumed.wxpython import gmMacro
48 from Gnumed.wxpython import gmCfgWidgets
49 from Gnumed.wxpython import gmListWidgets
50 from Gnumed.wxpython import gmPhraseWheel
51 from Gnumed.wxpython import gmFormWidgets
52 from Gnumed.wxpython import gmAllergyWidgets
53 from Gnumed.wxpython import gmDocumentWidgets
54 from Gnumed.wxpython import gmSubstanceMgmtWidgets
55
56
57 _log = logging.getLogger('gm.ui')
58
59 #============================================================
60 # perhaps leave this here:
61 #============================================================
63
65
66 query = """
67 SELECT DISTINCT ON (list_label)
68 preparation AS data,
69 preparation AS list_label,
70 preparation AS field_label
71 FROM ref.drug_product
72 WHERE preparation %(fragment_condition)s
73 ORDER BY list_label
74 LIMIT 30"""
75 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
76 mp.setThresholds(1, 2, 4)
77 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
78 self.SetToolTip(_('The preparation (form) of the substance or product.'))
79 self.matcher = mp
80 self.selection_only = False
81
82 #============================================================
83 # current substance intake widgets
84 #------------------------------------------------------------
86
88 mp = gmMedication.cSubstanceIntakeObjectMatchProvider()
89 mp.setThresholds(1, 2, 4)
90 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
91 self.SetToolTip(_('A drug product.'))
92 self.matcher = mp
93 self.selection_only = True
94 self.phrase_separators = None
95
96 #--------------------------------------------------------
98 pk = self.GetData(as_instance = False, can_create = False)
99 if pk is None:
100 return None
101 return gmMedication.cDrugProduct(aPK_obj = pk)
102
103 #------------------------------------------------------------
105
107
108 mp = gmMedication.cProductOrSubstanceMatchProvider()
109 mp.setThresholds(1, 2, 4)
110 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
111 self.SetToolTip(_('A substance with optional strength or a drug product.'))
112 self.matcher = mp
113 self.selection_only = False
114 self.phrase_separators = None
115 self.IS_PRODUCT = 1
116 self.IS_SUBSTANCE = 2
117 self.IS_COMPONENT = 3
118
119 #--------------------------------------------------------
121 entry_type, pk = self.GetData(as_instance = False, can_create = False)
122 if entry_type == 1:
123 return gmMedication.cDrugProduct(aPK_obj = pk)
124 if entry_type == 2:
125 return gmMedication.cConsumableSubstance(aPK_obj = pk)
126 if entry_type == 3:
127 return gmMedication.cDrugComponent(aPK_obj = pk)
128 raise ValueError('entry type must be 1=drug product or 2=substance or 3=component')
129
130 #============================================================
132
134
135 query = """
136 SELECT DISTINCT ON (sched)
137 schedule as sched,
138 schedule
139 FROM clin.substance_intake
140 WHERE schedule %(fragment_condition)s
141 ORDER BY sched
142 LIMIT 50"""
143
144 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
145 mp.setThresholds(1, 2, 4)
146 mp.word_separators = '[ \t=+&:@]+'
147 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
148 self.SetToolTip(_('The schedule for taking this substance.'))
149 self.matcher = mp
150 self.selection_only = False
151
152 #============================================================
154
156
157 query = """
158 (
159 SELECT DISTINCT ON (field_label)
160 aim
161 AS data,
162 aim || ' (' || substance || ' ' || amount || ' ' || unit || ')'
163 AS list_label,
164 aim
165 AS field_label
166 FROM clin.v_substance_intakes
167 WHERE
168 aim %(fragment_condition)s
169 %(ctxt_substance)s
170 ) UNION (
171 SELECT DISTINCT ON (field_label)
172 aim
173 AS data,
174 aim || ' (' || substance || ' ' || amount || ' ' || unit || ')'
175 AS list_label,
176 aim
177 AS field_label
178 FROM clin.v_substance_intakes
179 WHERE
180 aim %(fragment_condition)s
181 )
182 ORDER BY list_label
183 LIMIT 30"""
184
185 context = {'ctxt_substance': {
186 'where_part': 'AND substance = %(substance)s',
187 'placeholder': 'substance'
188 }}
189
190 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query, context = context)
191 mp.setThresholds(1, 2, 4)
192 #mp.word_separators = '[ \t=+&:@]+'
193 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
194 self.SetToolTip(_('The medical aim for consuming this substance.'))
195 self.matcher = mp
196 self.selection_only = False
197
198 #============================================================
200
201 if intake['is_currently_active']:
202 intake['discontinued'] = gmDateTime.pydt_now_here()
203 if intake['discontinue_reason'] is None:
204 intake['discontinue_reason'] = '%s %s' % (_('not tolerated:'), _('discontinued due to allergy or intolerance'))
205 else:
206 if not intake['discontinue_reason'].startswith(_('not tolerated:')):
207 intake['discontinue_reason'] = '%s %s' % (_('not tolerated:'), intake['discontinue_reason'])
208 if not intake.save():
209 return False
210
211 allg = intake.turn_into_allergy(encounter_id = emr.active_encounter['pk_encounter'])
212
213 drug = intake.containing_drug
214 comps = [ c['substance'] for c in drug.components ]
215 if len(comps) > 1:
216 gmGuiHelpers.gm_show_info (
217 aTitle = _('Documented an allergy'),
218 aMessage = _(
219 'An allergy was documented against the substance:\n'
220 '\n'
221 ' [%s]\n'
222 '\n'
223 'This substance was taken with the multi-component drug product:\n'
224 '\n'
225 ' [%s (%s)]\n'
226 '\n'
227 'Note that ALL components of this product were discontinued.'
228 ) % (
229 intake['substance'],
230 intake['product'],
231 ' & '.join(comps)
232 )
233 )
234
235 if parent is None:
236 parent = wx.GetApp().GetTopWindow()
237
238 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent, -1)
239 dlg.ShowModal()
240
241 return True
242
243 #============================================================
245
246 if parent is None:
247 parent = wx.GetApp().GetTopWindow()
248
249 if emr is None:
250 emr = gmPerson.gmCurrentPatient().emr
251 # #------------------------------------------------------------
252 # def add_from_db(substance):
253 # drug_db = gmSubstanceMgmtWidgets.get_drug_database(parent = parent, patient = gmPerson.gmCurrentPatient())
254 # if drug_db is None:
255 # return False
256 # drug_db.import_drugs()
257 # return True
258 # #------------------------------------------------------------
259 # def edit(substance=None):
260 # return gmSubstanceMgmtWidgets.edit_substance(parent = parent, substance = substance, single_entry = (substance is not None))
261 # #------------------------------------------------------------
262 # def delete(substance):
263 # if substance.is_in_use_by_patients:
264 # gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete this substance. It is in use.'), beep = True)
265 # return False
266 #
267 # xxxxx -> substance_dose
268 # return gmMedication.delete_xsubstance(substance = substance['pk'])
269 #------------------------------------------------------------
270 def get_tooltip(intake=None):
271 return intake.format(single_line = False, show_all_product_components = True)
272 #------------------------------------------------------------
273 def refresh(lctrl):
274 intakes = emr.get_current_medications (
275 include_inactive = False,
276 include_unapproved = True,
277 order_by = 'substance, product, started'
278 )
279 items = []
280 for i in intakes:
281 started = i.medically_formatted_start
282 items.append ([
283 '%s%s %s %s %s%s' % (
284 i['substance'],
285 gmTools.coalesce(i['product'], '', ' (%s)'),
286 i['amount'],
287 i['unit'],
288 i['l10n_preparation'],
289 gmTools.coalesce(i['external_code_product'], '', ' [%s::%s]' % (i['external_code_type_product'], i['external_code_product']))
290 ),
291 '%s%s%s' % (
292 started,
293 gmTools.coalesce(i['schedule'], '', ' %%s %s' % gmTools.u_arrow2right),
294 gmTools.coalesce(i['duration'], '', ' %s')
295 ),
296 '%s' % (
297 gmTools.bool2subst (
298 i['intake_is_approved_of'],
299 '',
300 _('disapproved')
301 )
302 )
303 ])
304 lctrl.set_string_items(items)
305 lctrl.set_data(intakes)
306
307 #------------------------------------------------------------
308 return gmListWidgets.get_choices_from_list (
309 parent = parent,
310 caption = _('Substances consumed by the patient'),
311 columns = [ _('Intake'), _('Application'), _('Status') ],
312 single_selection = False,
313 # new_callback = edit,
314 # edit_callback = edit,
315 # delete_callback = delete,
316 refresh_callback = refresh,
317 list_tooltip_callback = get_tooltip
318 # ,left_extra_button = (_('Import'), _('Import consumable substances from a drug database.'), add_from_db)
319 )
320
321 #============================================================
322 from Gnumed.wxGladeWidgets import wxgCurrentMedicationEAPnl
323
324 -class cSubstanceIntakeEAPnl(wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl, gmEditArea.cGenericEditAreaMixin):
325
327
328 try:
329 data = kwargs['substance']
330 del kwargs['substance']
331 except KeyError:
332 data = None
333
334 self.calc = gmClinicalCalculator.cClinicalCalculator()
335
336 wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl.__init__(self, *args, **kwargs)
337 gmEditArea.cGenericEditAreaMixin.__init__(self)
338
339 self.mode = 'new'
340 self.data = data
341 if data is not None:
342 self.mode = 'edit'
343
344 self.__init_ui()
345
346 #----------------------------------------------------------------
348
349 self._PRW_drug.add_callback_on_lose_focus(callback = self._on_leave_drug)
350 self._PRW_drug.selection_only = True
351
352 self._PRW_duration.display_accuracy = gmDateTime.acc_days
353
354 # this we want to adjust later
355 self._PRW_aim.add_callback_on_set_focus(callback = self._on_enter_aim)
356
357 self._DP_discontinued.add_callback_on_selection(callback = self._on_discontinued_date_changed)
358
359 #----------------------------------------------------------------
361
362 curr_pat = gmPerson.gmCurrentPatient()
363 emr = curr_pat.emr
364
365 # allergies
366 state = emr.allergy_state
367 if state['last_confirmed'] is None:
368 confirmed = _('never')
369 else:
370 confirmed = gmDateTime.pydt_strftime(state['last_confirmed'], '%Y %b %d')
371 msg = _('%s, last confirmed %s\n') % (state.state_string, confirmed)
372 msg += gmTools.coalesce(state['comment'], '', _('Comment (%s): %%s\n') % state['modified_by'])
373 tooltip = ''
374 allgs = emr.get_allergies()
375 if len(allgs) > 0:
376 msg += '\n'
377 for allergy in allgs:
378 msg += '%s: %s (%s)\n' % (
379 allergy['descriptor'],
380 allergy['l10n_type'],
381 gmTools.bool2subst(allergy['definite'], _('definite'), _('suspected'), '?')
382 )
383 tooltip += '%s: %s\n' % (
384 allergy['descriptor'],
385 gmTools.coalesce(allergy['reaction'], _('reaction not recorded'))
386 )
387 if len(allgs) > 0:
388 msg += '\n'
389 tooltip += '\n'
390 del allgs
391
392 # history of substance abuse
393 abuses = emr.abused_substances
394 for abuse in abuses:
395 tooltip += abuse.format(single_line = False, include_metadata = False)
396 tooltip += '\n'
397 if abuse['harmful_use_type'] in [None, 0]:
398 continue
399 msg += abuse.format(single_line = True)
400 msg += '\n'
401 if len(abuses) > 0:
402 msg += '\n'
403 tooltip += '\n'
404 del abuses
405
406 # kidney function
407 gfr = emr.get_most_recent_results_in_loinc_group(loincs = gmLOINC.LOINC_gfr_quantity, no_of_results = 1)
408 if gfr is None:
409 self.calc.patient = curr_pat
410 gfr = self.calc.eGFR
411 if gfr.numeric_value is None:
412 msg += _('GFR: unknown')
413 else:
414 msg += gfr.message
415 egfrs = self.calc.eGFRs
416 tts = []
417 for egfr in egfrs:
418 if egfr.numeric_value is None:
419 continue
420 tts.append(egfr.format (
421 left_margin = 0,
422 width = 50,
423 eol = '\n',
424 with_formula = False,
425 with_warnings = True,
426 with_variables = False,
427 with_sub_results = False,
428 return_list = False
429 ))
430 tooltip += '\n'.join(tts)
431 else:
432 msg += '%s: %s %s (%s)\n' % (
433 gfr['unified_abbrev'],
434 gfr['unified_val'],
435 gmTools.coalesce(gfr['abnormality_indicator'], '', ' (%s)'),
436 gmDateTime.pydt_strftime (
437 gfr['clin_when'],
438 format = '%Y %b %d'
439 )
440 )
441 tooltip += _('GFR reported by path lab')
442
443 # pregnancy
444 edc = emr.EDC
445 if edc is not None:
446 msg += '\n\n'
447 if emr.EDC_is_fishy:
448 msg += _('EDC (!?!): %s') % gmDateTime.pydt_strftime(edc, format = '%Y %b %d')
449 tooltip += _(
450 'The Expected Date of Confinement is rather questionable.\n'
451 '\n'
452 'Please check patient age, patient gender, time until/since EDC.'
453 )
454 else:
455 msg += _('EDC: %s') % gmDateTime.pydt_strftime(edc, format = '%Y %b %d')
456
457 self._LBL_allergies.SetLabel(msg)
458 self._LBL_allergies.SetToolTip(tooltip)
459
460 #----------------------------------------------------------------
462
463 drug = self._PRW_drug.GetData(as_instance = True)
464 if drug is None:
465 self._LBL_drug_details.SetLabel('')
466 self._LBL_drug_details.SetToolTip('')
467 self.Layout()
468 return
469
470 if len(drug['components']) == 0:
471 comps = _('<no components>')
472 else:
473 comps = '\n'.join ([
474 ' %s %s%s%s' % (
475 c['substance'],
476 c['amount'],
477 c['unit'],
478 gmTools.coalesce(c['dose_unit'], '', '/%s')
479 )
480 for c in drug['components']
481 ])
482 self._LBL_drug_details.SetLabel('%s\n%s' % (drug['product'], comps))
483 self._LBL_drug_details.SetToolTip(drug.format())
484 self.Layout()
485 return
486
487 #----------------------------------------------------------------
488 # generic Edit Area mixin API
489 #----------------------------------------------------------------
491
492 self._PRW_drug.display_as_valid(True)
493
494 # if we are editing the drug SHOULD exist so don't error
495 if self.mode == 'edit':
496 return True
497
498 selected_drug = self._PRW_drug.GetData(as_instance = True)
499
500 # no drug selected
501 if selected_drug is None:
502 val = self._PRW_drug.GetValue().strip()
503 if val == '':
504 self._PRW_drug.display_as_valid(False)
505 self._PRW_drug.SetFocus()
506 return False
507 # create as a generic, single-substance drug if that does not exist
508 drug = gmSubstanceMgmtWidgets.edit_single_component_generic_drug (
509 parent = self,
510 drug = None,
511 single_entry = True,
512 fields = {'substance': {'value': val, 'data': None}},
513 return_drug = True
514 )
515 if drug is None:
516 self._PRW_drug.display_as_valid(False)
517 self._PRW_drug.SetFocus()
518 return False
519 comp = drug.components[0]
520 self._PRW_drug.SetText (
521 _('%s w/ %s%s%s of %s') % (
522 comp['product'],
523 comp['amount'],
524 comp['unit'],
525 gmTools.coalesce(comp['dose_unit'], '', '/%s'),
526 comp['substance']
527 ),
528 drug['pk_drug_product']
529 )
530 selected_drug = drug
531 self.__refresh_drug_details()
532 self._PRW_drug.display_as_valid(True)
533 self._PRW_drug.SetFocus()
534 # return False despite there's now a drug such
535 # that the user has another chance to inspect
536 # the edit area data after creating a new drug
537 return False
538
539 # drug already exists as intake
540 if selected_drug.exists_as_intake(pk_patient = gmPerson.gmCurrentPatient().ID):
541 title = _('Adding substance intake entry')
542 msg = _(
543 'The patient is already taking\n'
544 '\n'
545 ' %s\n'
546 '\n'
547 'You will want to adjust the schedule\n'
548 'rather than document the intake twice.'
549 ) % self._PRW_drug.GetValue().strip()
550 gmGuiHelpers.gm_show_warning(aTitle = title, aMessage = msg)
551 self._PRW_drug.display_as_valid(False)
552 self._PRW_drug.SetFocus()
553 return False
554
555 self._PRW_drug.display_as_valid(True)
556 return True
557
558 #----------------------------------------------------------------
560
561 validity = self._check_drug_is_valid()
562
563 # episode must be set if intake is to be approved of
564 if self._CHBOX_approved.IsChecked():
565 if self._PRW_episode.GetValue().strip() == '':
566 self._PRW_episode.display_as_valid(False)
567 validity = False
568 else:
569 self._PRW_episode.display_as_valid(True)
570
571 if self._PRW_duration.GetValue().strip() in ['', gmTools.u_infinity]:
572 self._PRW_duration.display_as_valid(True)
573 else:
574 if self._PRW_duration.GetData() is None:
575 # no data ...
576 if gmDateTime.str2interval(self._PRW_duration.GetValue()) is None:
577 self._PRW_duration.display_as_valid(False)
578 validity = False
579 # ... but valid string
580 else:
581 self._PRW_duration.display_as_valid(True)
582 # has data
583 else:
584 self._PRW_duration.display_as_valid(True)
585
586 # started must exist or be unknown
587 started = None
588 if self._CHBOX_start_unknown.IsChecked() is False:
589 started = self._DP_started.GetData()
590 if started is None:
591 self._DP_started.display_as_valid(False)
592 self._DP_started.SetFocus()
593 validity = False
594 else:
595 self._DP_started.display_as_valid(True)
596
597 if validity is False:
598 gmDispatcher.send(signal = 'statustext', msg = _('Input incomplete/invalid for saving as substance intake.'))
599
600 # discontinued must be "< now()" AND "> started" if at all
601 discontinued = self._DP_discontinued.GetData()
602 if discontinued is not None:
603 now = gmDateTime.pydt_now_here().replace (
604 hour = 23,
605 minute = 59,
606 second = 59,
607 microsecond = 111111
608 )
609 # not in the future
610 if discontinued > now:
611 self._DP_discontinued.display_as_valid(False)
612 validity = False
613 gmDispatcher.send(signal = 'statustext', msg = _('Discontinued (%s) in the future (now: %s)!') % (discontinued, now))
614 else:
615 if started is not None:
616 started = started.replace (
617 hour = 0,
618 minute = 0,
619 second = 0,
620 microsecond = 1
621 )
622 # and not before it was started
623 if started > discontinued:
624 self._DP_started.display_as_valid(False)
625 self._DP_discontinued.display_as_valid(False)
626 validity = False
627 gmDispatcher.send(signal = 'statustext', msg = _('Discontinued (%s) before started (%s) !') % (discontinued, started))
628 else:
629 self._DP_started.display_as_valid(True)
630 self._DP_discontinued.display_as_valid(True)
631
632 return validity
633
634 #----------------------------------------------------------------
636
637 epi = self._PRW_episode.GetData()
638 if epi is None:
639 # create new episode, Jim wants it to auto-open
640 epi = self._PRW_episode.GetData(can_create = True, is_open = True)
641
642 selected_drug = self._PRW_drug.GetData(as_instance = True)
643 intake = selected_drug.turn_into_intake (
644 encounter = gmPerson.gmCurrentPatient().emr.current_encounter['pk_encounter'],
645 episode = epi
646 )
647
648 if intake is None:
649 gmDispatcher.send('statustext', msg = _('Cannot add duplicate of (maybe inactive) substance intake.'), beep = True)
650 return False
651
652 intake['started'] = self._DP_started.GetData()
653 if self._CHBOX_start_unknown.IsChecked():
654 intake['comment_on_start'] = '?'
655 else:
656 intake['comment_on_start'] = self._PRW_start_certainty.GetValue().strip()
657 intake['discontinued'] = self._DP_discontinued.GetData()
658 if intake['discontinued'] is None:
659 intake['discontinue_reason'] = None
660 else:
661 intake['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
662 intake['schedule'] = self._PRW_schedule.GetValue().strip()
663 intake['aim'] = self._PRW_aim.GetValue().strip()
664 intake['notes'] = self._PRW_notes.GetValue().strip()
665 intake['is_long_term'] = self._CHBOX_long_term.IsChecked()
666 intake['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
667 if self._PRW_duration.GetValue().strip() in ['', gmTools.u_infinity]:
668 intake['duration'] = None
669 else:
670 if self._PRW_duration.GetData() is None:
671 intake['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
672 else:
673 intake['duration'] = self._PRW_duration.GetData()
674 intake.save()
675
676 self.data = intake
677
678 return True
679
680 #----------------------------------------------------------------
682
683 # auto-applies to all components of a multi-component drug if any:
684 self.data['started'] = self._DP_started.GetData()
685 if self._CHBOX_start_unknown.IsChecked():
686 self.data['comment_on_start'] = '?'
687 else:
688 self.data['comment_on_start'] = self._PRW_start_certainty.GetValue().strip()
689 self.data['discontinued'] = self._DP_discontinued.GetData()
690 if self.data['discontinued'] is None:
691 self.data['discontinue_reason'] = None
692 else:
693 self.data['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
694 self.data['schedule'] = self._PRW_schedule.GetValue()
695 self.data['is_long_term'] = self._CHBOX_long_term.IsChecked()
696 self.data['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
697 if self._PRW_duration.GetValue().strip() in ['', gmTools.u_infinity]:
698 self.data['duration'] = None
699 else:
700 if self._PRW_duration.GetData() is None:
701 self.data['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
702 else:
703 self.data['duration'] = self._PRW_duration.GetData()
704
705 # per-component
706 self.data['aim'] = self._PRW_aim.GetValue()
707 self.data['notes'] = self._PRW_notes.GetValue()
708 epi = self._PRW_episode.GetData()
709 if epi is None:
710 # create new episode, Jim wants it to auto-open
711 epi = self._PRW_episode.GetData(can_create = True, is_open = True)
712 self.data['pk_episode'] = epi
713
714 self.data.save()
715
716 return True
717
718 #----------------------------------------------------------------
720 self._PRW_drug.SetText('', None)
721
722 self._PRW_schedule.SetText('', None)
723 self._PRW_duration.SetText('', None)
724 self._PRW_aim.SetText('', None)
725 self._PRW_notes.SetText('', None)
726 self._PRW_episode.SetText('', None)
727
728 self._CHBOX_long_term.SetValue(False)
729 self._CHBOX_approved.SetValue(True)
730
731 self._CHBOX_start_unknown.SetValue(False)
732 self._DP_started.SetData(gmDateTime.pydt_now_here())
733 self._DP_started.Enable(True)
734 self._PRW_start_certainty.SetText('', None)
735 self._PRW_start_certainty.Enable(True)
736 self._DP_discontinued.SetData(None)
737 self._PRW_discontinue_reason.SetValue('')
738 self._PRW_discontinue_reason.Enable(False)
739
740 self.__refresh_drug_details()
741 self.__refresh_precautions()
742
743 self._PRW_drug.SetFocus()
744
745 #----------------------------------------------------------------
747
748 self._PRW_drug.SetText (
749 _('%s w/ %s%s%s of %s') % (
750 self.data['product'],
751 self.data['amount'],
752 self.data['unit'],
753 gmTools.coalesce(self.data['dose_unit'], '', '/%s'),
754 self.data['substance']
755 ),
756 self.data['pk_drug_product']
757 )
758
759 self._PRW_drug.Disable()
760
761 if self.data['is_long_term']:
762 self._CHBOX_long_term.SetValue(True)
763 self._PRW_duration.Enable(False)
764 self._PRW_duration.SetText(gmTools.u_infinity, None)
765 self._BTN_discontinued_as_planned.Enable(False)
766 else:
767 self._CHBOX_long_term.SetValue(False)
768 self._PRW_duration.Enable(True)
769 self._BTN_discontinued_as_planned.Enable(True)
770 self._PRW_duration.SetData(self.data['duration'])
771 self._PRW_aim.SetText(gmTools.coalesce(self.data['aim'], ''), self.data['aim'])
772 self._PRW_notes.SetText(gmTools.coalesce(self.data['notes'], ''), self.data['notes'])
773 self._PRW_episode.SetData(self.data['pk_episode'])
774 self._PRW_schedule.SetText(gmTools.coalesce(self.data['schedule'], ''), self.data['schedule'])
775
776 self._CHBOX_approved.SetValue(self.data['intake_is_approved_of'])
777
778 self._DP_started.SetData(self.data['started'])
779 self._PRW_start_certainty.SetText(self.data['comment_on_start'], None)
780 if self.data['start_is_unknown']:
781 self._CHBOX_start_unknown.SetValue(True)
782 self._DP_started.Enable(False)
783 self._PRW_start_certainty.Enable(False)
784 else:
785 self._CHBOX_start_unknown.SetValue(False)
786 self._DP_started.Enable(True)
787 self._PRW_start_certainty.Enable(True)
788
789 self._DP_discontinued.SetData(self.data['discontinued'])
790 self._PRW_discontinue_reason.SetValue(gmTools.coalesce(self.data['discontinue_reason'], ''))
791 if self.data['discontinued'] is not None:
792 self._PRW_discontinue_reason.Enable()
793
794 self.__refresh_drug_details()
795 self.__refresh_precautions()
796
797 self._PRW_schedule.SetFocus()
798
799 #----------------------------------------------------------------
801 self._refresh_as_new()
802
803 self._PRW_episode.SetData(self.data['pk_episode'])
804 self._DP_started.SetData(self.data['started'])
805
806 self._PRW_drug.SetFocus()
807
808 #----------------------------------------------------------------
809 # event handlers
810 #----------------------------------------------------------------
813
814 #----------------------------------------------------------------
816 drug = self._PRW_drug.GetData(as_instance = True)
817 if drug is None:
818 self._PRW_aim.unset_context(context = 'substance')
819 return
820 # do not set to self._PRW_drug.GetValue() as that will contain all
821 # sorts of additional info, rather set to the canonical drug['substance']
822 # self._PRW_aim.set_context(context = u'substance', val = drug['substance'])
823
824 #----------------------------------------------------------------
826 if self._DP_discontinued.GetData() is None:
827 self._PRW_discontinue_reason.Enable(False)
828 else:
829 self._PRW_discontinue_reason.Enable(True)
830
831 #----------------------------------------------------------------
834
835 #----------------------------------------------------------------
838
839 #----------------------------------------------------------------
842
843 #----------------------------------------------------------------
846
847 #----------------------------------------------------------------
850
851 #----------------------------------------------------------------
859
860 #----------------------------------------------------------------
890
891 #----------------------------------------------------------------
893 if self._CHBOX_long_term.IsChecked() is True:
894 self._PRW_duration.Enable(False)
895 self._BTN_discontinued_as_planned.Enable(False)
896 self._PRW_discontinue_reason.Enable(False)
897 else:
898 self._PRW_duration.Enable(True)
899 self._BTN_discontinued_as_planned.Enable(True)
900 self._PRW_discontinue_reason.Enable(True)
901
902 self.__refresh_precautions()
903
904 #----------------------------------------------------------------
906 event.Skip()
907 if self._CHBOX_start_unknown.IsChecked() is True:
908 self._DP_started.Enable(False)
909 self._PRW_start_certainty.Enable(False)
910 else:
911 self._DP_started.Enable(True)
912 self._PRW_start_certainty.Enable(True)
913
914 self.__refresh_precautions()
915
916 #----------------------------------------------------------------
918 if not self.save():
919 return False
920
921 return turn_substance_intake_into_allergy (
922 parent = self,
923 intake = self.data,
924 emr = gmPerson.gmCurrentPatient().emr
925 )
926
927 #============================================================
929
930 comps = intake.containing_drug.components
931 if len(comps) > 1:
932 msg = _(
933 'This intake is part of a multi-component drug product:\n'
934 '\n'
935 ' %s\n'
936 '\n'
937 'Really delete all intakes related to this drug product ?'
938 ) % '\n '.join (
939 [ '%s %s%s' % (c['substance'], c['amount'], c.formatted_units) for c in comps ]
940 )
941 delete_all = gmGuiHelpers.gm_show_question (
942 title = _('Deleting medication / substance intake'),
943 question = msg
944 )
945 if not delete_all:
946 return
947
948 msg = _(
949 '\n'
950 '[%s]\n'
951 '\n'
952 'It may be prudent to edit (before deletion) the details\n'
953 'of this substance intake entry so as to leave behind\n'
954 'some indication of why it was deleted.\n'
955 ) % intake.format()
956
957 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
958 parent,
959 -1,
960 caption = _('Deleting medication / substance intake'),
961 question = msg,
962 button_defs = [
963 {'label': _('&Edit'), 'tooltip': _('Allow editing of substance intake entry before deletion.'), 'default': True},
964 {'label': _('&Delete'), 'tooltip': _('Delete immediately without editing first.')},
965 {'label': _('&Cancel'), 'tooltip': _('Abort. Do not delete or edit substance intake entry.')}
966 ]
967 )
968
969 edit_first = dlg.ShowModal()
970 dlg.Destroy()
971
972 if edit_first == wx.ID_CANCEL:
973 return
974
975 if edit_first == wx.ID_YES:
976 edit_intake_of_substance(parent = parent, substance = intake)
977 delete_it = gmGuiHelpers.gm_show_question (
978 aMessage = _('Now delete substance intake entry ?'),
979 aTitle = _('Deleting medication / substance intake')
980 )
981 else:
982 delete_it = True
983
984 if not delete_it:
985 return
986
987 gmMedication.delete_substance_intake(pk_intake = intake['pk_substance_intake'], delete_siblings = True)
988
989 #------------------------------------------------------------
991 ea = cSubstanceIntakeEAPnl(parent, -1, substance = substance)
992 dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = (substance is not None))
993 dlg.SetTitle(gmTools.coalesce(substance, _('Adding medication/non-medication substance intake'), _('Editing medication/non-medication substance intake')))
994 dlg.left_extra_button = (
995 _('Allergy'),
996 _('Document an allergy against this substance.'),
997 ea.turn_into_allergy
998 )
999 dlg.SetSize((650,500))
1000 if dlg.ShowModal() == wx.ID_OK:
1001 dlg.Destroy()
1002 return True
1003 dlg.Destroy()
1004 return False
1005
1006 #============================================================
1007 # current substances grid
1008 #------------------------------------------------------------
1010
1011 if parent is None:
1012 parent = wx.GetApp().GetTopWindow()
1013
1014 template = gmFormWidgets.manage_form_templates (
1015 parent = parent,
1016 template_types = ['current medication list']
1017 )
1018 option = 'form_templates.medication_list'
1019
1020 if template is None:
1021 gmDispatcher.send(signal = 'statustext', msg = _('No medication list template configured.'), beep = True)
1022 return None
1023
1024 if template['engine'] not in ['L', 'X', 'T']:
1025 gmDispatcher.send(signal = 'statustext', msg = _('No medication list template configured.'), beep = True)
1026 return None
1027
1028 dbcfg = gmCfg.cCfgSQL()
1029 dbcfg.set (
1030 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
1031 option = option,
1032 value = '%s - %s' % (template['name_long'], template['external_version'])
1033 )
1034
1035 return template
1036
1037 #------------------------------------------------------------
1039
1040 if parent is None:
1041 parent = wx.GetApp().GetTopWindow()
1042
1043 # 1) get template
1044 dbcfg = gmCfg.cCfgSQL()
1045 option = 'form_templates.medication_list'
1046
1047 template = dbcfg.get2 (
1048 option = option,
1049 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
1050 bias = 'user'
1051 )
1052
1053 if template is None:
1054 template = configure_medication_list_template(parent = parent)
1055 if template is None:
1056 gmGuiHelpers.gm_show_error (
1057 aMessage = _('There is no medication list template configured.'),
1058 aTitle = _('Printing medication list')
1059 )
1060 return False
1061 else:
1062 try:
1063 name, ver = template.split(' - ')
1064 except:
1065 _log.exception('problem splitting medication list template name [%s]', template)
1066 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading medication list template.'), beep = True)
1067 return False
1068 template = gmForms.get_form_template(name_long = name, external_version = ver)
1069 if template is None:
1070 gmGuiHelpers.gm_show_error (
1071 aMessage = _('Cannot load medication list template [%s - %s]') % (name, ver),
1072 aTitle = _('Printing medication list')
1073 )
1074 return False
1075
1076 # 2) process template
1077 meds_list = gmFormWidgets.generate_form_from_template (
1078 parent = parent,
1079 template = template,
1080 edit = False
1081 )
1082 if meds_list is None:
1083 return False
1084
1085 # 3) print template
1086 return gmFormWidgets.act_on_generated_forms (
1087 parent = parent,
1088 forms = [meds_list],
1089 jobtype = 'medication_list',
1090 #episode_name = u'administrative',
1091 episode_name = gmMedication.DEFAULT_MEDICATION_HISTORY_EPISODE,
1092 progress_note = _('generated medication list document'),
1093 review_copy_as_normal = True
1094 )
1095
1096 #------------------------------------------------------------
1098
1099 if parent is None:
1100 parent = wx.GetApp().GetTopWindow()
1101
1102 template = gmFormWidgets.manage_form_templates (
1103 parent = parent,
1104 msg = _('Select the default prescription template:'),
1105 template_types = ['prescription', 'current medication list']
1106 )
1107
1108 if template is None:
1109 gmDispatcher.send(signal = 'statustext', msg = _('No prescription template configured.'), beep = True)
1110 return None
1111
1112 if template['engine'] not in ['L', 'X', 'T']:
1113 gmDispatcher.send(signal = 'statustext', msg = _('No prescription template configured.'), beep = True)
1114 return None
1115
1116 option = 'form_templates.prescription'
1117 dbcfg = gmCfg.cCfgSQL()
1118 dbcfg.set (
1119 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
1120 option = option,
1121 value = '%s - %s' % (template['name_long'], template['external_version'])
1122 )
1123
1124 return template
1125
1126 #------------------------------------------------------------
1128
1129 if parent is None:
1130 parent = wx.GetApp().GetTopWindow()
1131
1132 dbcfg = gmCfg.cCfgSQL()
1133 option = 'form_templates.prescription'
1134 template_name = dbcfg.get2 (
1135 option = option,
1136 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
1137 bias = 'user'
1138 )
1139
1140 if template_name is None:
1141 template = configure_prescription_template(parent = parent)
1142 if template is None:
1143 gmGuiHelpers.gm_show_error (
1144 aMessage = _('There is no prescription template configured.'),
1145 aTitle = _('Printing prescription')
1146 )
1147 return None
1148 return template
1149
1150 try:
1151 name, ver = template_name.split(' - ')
1152 except:
1153 _log.exception('problem splitting prescription template name [%s]', template_name)
1154 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading prescription template.'), beep = True)
1155 return False
1156 template = gmForms.get_form_template(name_long = name, external_version = ver)
1157 if template is None:
1158 gmGuiHelpers.gm_show_error (
1159 aMessage = _('Cannot load prescription template [%s - %s]') % (name, ver),
1160 aTitle = _('Printing prescription')
1161 )
1162 return None
1163 return template
1164
1165 #------------------------------------------------------------
1167
1168 # 1) get template
1169 rx_template = get_prescription_template(parent = parent)
1170 if rx_template is None:
1171 return False
1172
1173 # 2) process template
1174 rx = gmFormWidgets.generate_form_from_template (
1175 parent = parent,
1176 template = rx_template,
1177 edit = False
1178 )
1179 if rx is None:
1180 return False
1181
1182 # 3) print template
1183 return gmFormWidgets.act_on_generated_forms (
1184 parent = parent,
1185 forms = [rx],
1186 jobtype = 'prescription',
1187 #episode_name = u'administrative',
1188 episode_name = gmMedication.DEFAULT_MEDICATION_HISTORY_EPISODE,
1189 progress_note = _('generated prescription'),
1190 review_copy_as_normal = True
1191 )
1192
1193 #------------------------------------------------------------
1195
1196 dbcfg = gmCfg.cCfgSQL()
1197 rx_mode = dbcfg.get2 (
1198 option = 'horst_space.default_prescription_mode',
1199 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
1200 bias = 'user',
1201 default = 'form' # set to 'database' to access database
1202 )
1203
1204 if parent is None:
1205 parent = wx.GetApp().GetTopWindow()
1206
1207 if rx_mode == 'form':
1208 return print_prescription(parent = parent, emr = emr)
1209
1210 if rx_mode == 'database':
1211 drug_db = gmSubstanceMgmtWidgets.get_drug_database() #gmPerson.gmCurrentPatient() xxxxxxx ?
1212 if drug_db is None:
1213 return
1214 drug_db.reviewer = gmStaff.gmCurrentProvider()
1215 prescribed_drugs = drug_db.prescribe()
1216 update_substance_intake_list_from_prescription (
1217 parent = parent,
1218 prescribed_drugs = prescribed_drugs,
1219 emr = emr
1220 )
1221
1222 #------------------------------------------------------------
1223 -def update_substance_intake_list_from_prescription(parent=None, prescribed_drugs=None, emr=None):
1224
1225 if len(prescribed_drugs) == 0:
1226 return
1227
1228 curr_meds = [ i['pk_drug_product'] for i in emr.get_current_medications() if i['pk_drug_product'] is not None ]
1229 new_drugs = []
1230 for drug in prescribed_drugs:
1231 if drug['pk_drug_product'] not in curr_meds:
1232 new_drugs.append(drug)
1233
1234 if len(new_drugs) == 0:
1235 return
1236
1237 if parent is None:
1238 parent = wx.GetApp().GetTopWindow()
1239
1240 picker = gmListWidgets.cItemPickerDlg (
1241 parent,
1242 -1,
1243 msg = _(
1244 'These products have been prescribed but are not listed\n'
1245 'in the current medication list of this patient.\n'
1246 '\n'
1247 'Please select those you want added to the medication list.'
1248 )
1249 )
1250 picker.set_columns (
1251 columns = [_('Newly prescribed drugs')],
1252 columns_right = [_('Add to medication list')]
1253 )
1254 choices = [ ('%s %s (%s)' % (d['product'], d['l10n_preparation'], '; '.join(d['components']))) for d in new_drugs ]
1255 picker.set_choices (
1256 choices = choices,
1257 data = new_drugs
1258 )
1259 picker.ShowModal()
1260 drugs2add = picker.get_picks()
1261 picker.Destroy()
1262
1263 if drugs2add is None:
1264 return
1265
1266 if len(drugs2add) == 0:
1267 return
1268
1269 for drug in drugs2add:
1270 # only add first component since all other components get added by a trigger ...
1271 intake = emr.add_substance_intake(pk_component = drug['components'][0]['pk_component'])
1272 if intake is None:
1273 continue
1274 intake['intake_is_approved_of'] = True
1275 intake.save()
1276
1277 return
1278
1279 #------------------------------------------------------------
1281 """A grid class for displaying current substance intake.
1282
1283 - does NOT listen to the currently active patient
1284 - thereby it can display any patient at any time
1285 """
1287
1288 wx.grid.Grid.__init__(self, *args, **kwargs)
1289
1290 self.__patient = None
1291 self.__row_data = {}
1292 self.__prev_row = None
1293 self.__prev_tooltip_row = None
1294 self.__prev_cell_0 = None
1295 self.__grouping_mode = 'issue'
1296 self.__filter_show_unapproved = True
1297 self.__filter_show_inactive = True
1298
1299 self.__grouping2col_labels = {
1300 'issue': [
1301 _('Health issue'),
1302 _('Substance'),
1303 _('Strength'),
1304 _('Schedule'),
1305 _('Timeframe'),
1306 _('Product'),
1307 _('Advice')
1308 ],
1309 'product': [
1310 _('Product'),
1311 _('Schedule'),
1312 _('Substance'),
1313 _('Strength'),
1314 _('Timeframe'),
1315 _('Health issue'),
1316 _('Advice')
1317 ],
1318 'episode': [
1319 _('Episode'),
1320 _('Substance'),
1321 _('Strength'),
1322 _('Schedule'),
1323 _('Timeframe'),
1324 _('Product'),
1325 _('Advice')
1326 ],
1327 'start': [
1328 _('Episode'),
1329 _('Substance'),
1330 _('Strength'),
1331 _('Schedule'),
1332 _('Timeframe'),
1333 _('Product'),
1334 _('Advice')
1335 ],
1336 }
1337
1338 self.__grouping2order_by_clauses = {
1339 'issue': 'pk_health_issue NULLS FIRST, substance, started',
1340 'episode': 'pk_health_issue NULLS FIRST, episode, substance, started',
1341 'product': 'product NULLS LAST, substance, started',
1342 'start': 'started DESC, substance, episode'
1343 }
1344
1345 self.__init_ui()
1346 self.__register_events()
1347
1348 #------------------------------------------------------------
1349 # external API
1350 #------------------------------------------------------------
1352
1353 sel_block_top_left = self.GetSelectionBlockTopLeft()
1354 sel_block_bottom_right = self.GetSelectionBlockBottomRight()
1355 sel_cols = self.GetSelectedCols()
1356 sel_rows = self.GetSelectedRows()
1357
1358 selected_cells = []
1359
1360 # individually selected cells (ctrl-click)
1361 selected_cells += self.GetSelectedCells()
1362
1363 # selected rows
1364 selected_cells += list (
1365 (row, col)
1366 for row in sel_rows
1367 for col in range(self.GetNumberCols())
1368 )
1369
1370 # selected columns
1371 selected_cells += list (
1372 (row, col)
1373 for row in range(self.GetNumberRows())
1374 for col in sel_cols
1375 )
1376
1377 # selection blocks
1378 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()):
1379 selected_cells += [
1380 (row, col)
1381 for row in range(top_left[0], bottom_right[0] + 1)
1382 for col in range(top_left[1], bottom_right[1] + 1)
1383 ]
1384
1385 return set(selected_cells)
1386
1387 #------------------------------------------------------------
1389 rows = {}
1390
1391 for row, col in self.get_selected_cells():
1392 rows[row] = True
1393
1394 return rows.keys()
1395
1396 #------------------------------------------------------------
1398 return [ self.__row_data[row] for row in self.get_selected_rows() ]
1399
1400 #------------------------------------------------------------
1403
1404 #------------------------------------------------------------
1406
1407 self.empty_grid()
1408
1409 if self.__patient is None:
1410 return
1411
1412 emr = self.__patient.emr
1413 meds = emr.get_current_medications (
1414 order_by = self.__grouping2order_by_clauses[self.__grouping_mode],
1415 include_unapproved = self.__filter_show_unapproved,
1416 include_inactive = self.__filter_show_inactive
1417 )
1418 if not meds:
1419 return
1420
1421 self.BeginBatch()
1422
1423 # columns
1424 labels = self.__grouping2col_labels[self.__grouping_mode]
1425 if self.__filter_show_unapproved:
1426 self.AppendCols(numCols = len(labels) + 1)
1427 else:
1428 self.AppendCols(numCols = len(labels))
1429 for col_idx in range(len(labels)):
1430 self.SetColLabelValue(col_idx, labels[col_idx])
1431 if self.__filter_show_unapproved:
1432 #self.SetColLabelValue(len(labels), u'OK?')
1433 self.SetColLabelValue(len(labels), '')
1434 self.SetColSize(len(labels), 40)
1435
1436 self.AppendRows(numRows = len(meds))
1437
1438 # loop over data
1439 for row_idx in range(len(meds)):
1440 med = meds[row_idx]
1441 self.__row_data[row_idx] = med
1442
1443 if med['is_currently_active'] is True:
1444 atcs = []
1445 if med['atc_substance'] is not None:
1446 atcs.append(med['atc_substance'])
1447 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (med['substance'],))
1448 if allg not in [None, False]:
1449 attr = self.GetOrCreateCellAttr(row_idx, 0)
1450 if allg['type'] == 'allergy':
1451 attr.SetTextColour('red')
1452 else:
1453 #attr.SetTextColour('yellow') # too light
1454 #attr.SetTextColour('pink') # too light
1455 #attr.SetTextColour('dark orange') # slightly better
1456 attr.SetTextColour('magenta')
1457 self.SetRowAttr(row_idx, attr)
1458 else:
1459 attr = self.GetOrCreateCellAttr(row_idx, 0)
1460 attr.SetTextColour('grey')
1461 self.SetRowAttr(row_idx, attr)
1462
1463 if self.__grouping_mode in ['episode', 'start']:
1464 if med['pk_episode'] is None:
1465 self.__prev_cell_0 = None
1466 epi = gmTools.u_diameter
1467 else:
1468 if self.__prev_cell_0 == med['episode']:
1469 epi = ''
1470 else:
1471 self.__prev_cell_0 = med['episode']
1472 epi = gmTools.coalesce(med['episode'], '')
1473 self.SetCellValue(row_idx, 0, gmTools.wrap(text = epi, width = 40))
1474
1475 self.SetCellValue(row_idx, 1, med['substance'])
1476 self.SetCellValue(row_idx, 2, '%s %s' % (med['amount'], med['unit']))
1477 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], ''))
1478 self.SetCellValue(row_idx, 4, med.medically_formatted_start_end)
1479
1480 if med['pk_drug_product'] is None:
1481 product = '%s (%s)' % (gmTools.u_diameter, med['l10n_preparation'])
1482 else:
1483 if med['is_fake_product']:
1484 product = '%s (%s)' % (
1485 gmTools.coalesce(med['product'], '', _('%s <fake>')),
1486 med['l10n_preparation']
1487 )
1488 else:
1489 product = '%s (%s)' % (
1490 gmTools.coalesce(med['product'], ''),
1491 med['l10n_preparation']
1492 )
1493 self.SetCellValue(row_idx, 5, gmTools.wrap(text = product, width = 35))
1494
1495 elif self.__grouping_mode == 'issue':
1496 if med['pk_health_issue'] is None:
1497 self.__prev_cell_0 = None
1498 issue = '%s%s' % (
1499 gmTools.u_diameter,
1500 gmTools.coalesce(med['episode'], '', ' (%s)')
1501 )
1502 else:
1503 if self.__prev_cell_0 == med['health_issue']:
1504 issue = ''
1505 else:
1506 self.__prev_cell_0 = med['health_issue']
1507 issue = med['health_issue']
1508 self.SetCellValue(row_idx, 0, gmTools.wrap(text = issue, width = 40))
1509
1510 self.SetCellValue(row_idx, 1, med['substance'])
1511 self.SetCellValue(row_idx, 2, '%s %s' % (med['amount'], med['unit']))
1512 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], ''))
1513 self.SetCellValue(row_idx, 4, med.medically_formatted_start_end)
1514
1515 if med['pk_drug_product'] is None:
1516 product = '%s (%s)' % (gmTools.u_diameter, med['l10n_preparation'])
1517 else:
1518 if med['is_fake_product']:
1519 product = '%s (%s)' % (
1520 gmTools.coalesce(med['product'], '', _('%s <fake>')),
1521 med['l10n_preparation']
1522 )
1523 else:
1524 product = '%s (%s)' % (
1525 gmTools.coalesce(med['product'], ''),
1526 med['l10n_preparation']
1527 )
1528 self.SetCellValue(row_idx, 5, gmTools.wrap(text = product, width = 35))
1529
1530 elif self.__grouping_mode == 'product':
1531
1532 if med['pk_drug_product'] is None:
1533 self.__prev_cell_0 = None
1534 product = '%s (%s)' % (
1535 gmTools.u_diameter,
1536 med['l10n_preparation']
1537 )
1538 else:
1539 if self.__prev_cell_0 == med['product']:
1540 product = ''
1541 else:
1542 self.__prev_cell_0 = med['product']
1543 if med['is_fake_product']:
1544 product = '%s (%s)' % (
1545 gmTools.coalesce(med['product'], '', _('%s <fake>')),
1546 med['l10n_preparation']
1547 )
1548 else:
1549 product = '%s (%s)' % (
1550 gmTools.coalesce(med['product'], ''),
1551 med['l10n_preparation']
1552 )
1553 self.SetCellValue(row_idx, 0, gmTools.wrap(text = product, width = 35))
1554
1555 self.SetCellValue(row_idx, 1, gmTools.coalesce(med['schedule'], ''))
1556 self.SetCellValue(row_idx, 2, med['substance'])
1557 self.SetCellValue(row_idx, 3, '%s %s' % (med['amount'], med['unit']))
1558 self.SetCellValue(row_idx, 4, med.medically_formatted_start_end)
1559
1560 if med['pk_health_issue'] is None:
1561 issue = '%s%s' % (
1562 gmTools.u_diameter,
1563 gmTools.coalesce(med['episode'], '', ' (%s)')
1564 )
1565 else:
1566 issue = gmTools.coalesce(med['health_issue'], '')
1567 self.SetCellValue(row_idx, 5, gmTools.wrap(text = issue, width = 40))
1568
1569 else:
1570 raise ValueError('unknown grouping mode [%s]' % self.__grouping_mode)
1571
1572 if med['notes'] is not None:
1573 self.SetCellValue(row_idx, 6, gmTools.wrap(text = med['notes'], width = 50))
1574
1575 if self.__filter_show_unapproved:
1576 self.SetCellValue (
1577 row_idx,
1578 len(labels),
1579 gmTools.bool2subst(med['intake_is_approved_of'], gmTools.u_checkmark_thin, gmTools.u_frowning_face, '?')
1580 )
1581 font = self.GetCellFont(row_idx, len(labels))
1582 font.SetPointSize(font.GetPointSize() + 2)
1583 self.SetCellFont(row_idx, len(labels), font)
1584
1585 #self.SetCellAlignment(row, col, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
1586
1587 self.AutoSize()
1588 self.EndBatch()
1589 #------------------------------------------------------------
1591 self.BeginBatch()
1592 self.ClearGrid()
1593 # Windows cannot do "nothing", it rather decides to assert()
1594 # on thinking it is supposed to do nothing
1595 if self.GetNumberRows() > 0:
1596 self.DeleteRows(pos = 0, numRows = self.GetNumberRows())
1597 if self.GetNumberCols() > 0:
1598 self.DeleteCols(pos = 0, numCols = self.GetNumberCols())
1599 self.EndBatch()
1600 self.__row_data = {}
1601 self.__prev_cell_0 = None
1602
1603 #------------------------------------------------------------
1605
1606 if len(self.__row_data) == 0:
1607 return
1608
1609 sel_rows = self.get_selected_rows()
1610 if len(sel_rows) != 1:
1611 return
1612
1613 drug_db = gmSubstanceMgmtWidgets.get_drug_database(patient = self.__patient)
1614 if drug_db is None:
1615 return
1616
1617 intake = self.get_selected_data()[0] # just in case
1618 if intake['product'] is None:
1619 drug_db.show_info_on_substance(substance_intake = intake)
1620 else:
1621 drug_db.show_info_on_drug(substance_intake = intake)
1622
1623 #------------------------------------------------------------
1625 search_term = None
1626 if len(self.__row_data) > 0:
1627 sel_rows = self.get_selected_rows()
1628 if len(sel_rows) == 1:
1629 search_term = self.get_selected_data()[0]
1630 gmNetworkTools.open_url_in_browser(url = gmMedication.drug2renal_insufficiency_url(search_term = search_term))
1631
1632 #------------------------------------------------------------
1635
1636 #------------------------------------------------------------
1638 dbcfg = gmCfg.cCfgSQL()
1639 url = dbcfg.get2 (
1640 option = 'external.urls.report_ADR',
1641 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
1642 bias = 'user',
1643 default = 'https://dcgma.org/uaw/meldung.php' # http://www.akdae.de/Arzneimittelsicherheit/UAW-Meldung/UAW-Meldung-online.html
1644 )
1645 gmNetworkTools.open_url_in_browser(url = url)
1646
1647 #------------------------------------------------------------
1653 #------------------------------------------------------------
1655
1656 if len(self.__row_data) == 0:
1657 return
1658
1659 drug_db = gmSubstanceMgmtWidgets.get_drug_database(patient = self.__patient)
1660 if drug_db is None:
1661 return
1662
1663 if len(self.get_selected_rows()) > 1:
1664 drug_db.check_interactions(substance_intakes = self.get_selected_data())
1665 else:
1666 drug_db.check_interactions(substance_intakes = self.__row_data.values())
1667 #------------------------------------------------------------
1670 #------------------------------------------------------------
1672
1673 rows = self.get_selected_rows()
1674
1675 if len(rows) == 0:
1676 return
1677
1678 if len(rows) > 1:
1679 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit more than one substance at once.'), beep = True)
1680 return
1681
1682 subst = self.get_selected_data()[0]
1683 edit_intake_of_substance(parent = self, substance = subst)
1684
1685 #------------------------------------------------------------
1687
1688 rows = self.get_selected_rows()
1689
1690 if len(rows) == 0:
1691 return
1692
1693 if len(rows) > 1:
1694 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete more than one substance at once.'), beep = True)
1695 return
1696
1697 intake = self.get_selected_data()[0]
1698 delete_substance_intake(parent = self, intake = intake)
1699
1700 #------------------------------------------------------------
1702 rows = self.get_selected_rows()
1703
1704 if len(rows) == 0:
1705 return
1706
1707 if len(rows) > 1:
1708 gmDispatcher.send(signal = 'statustext', msg = _('Cannot create allergy from more than one substance at once.'), beep = True)
1709 return
1710
1711 return turn_substance_intake_into_allergy (
1712 parent = self,
1713 intake = self.get_selected_data()[0],
1714 emr = self.__patient.emr
1715 )
1716 #------------------------------------------------------------
1718 # there could be some filtering/user interaction going on here
1719 print_medication_list(parent = self)
1720 #------------------------------------------------------------
1722
1723 try:
1724 entry = self.__row_data[row]
1725 except KeyError:
1726 return ' '
1727
1728 emr = self.__patient.emr
1729 atcs = []
1730 if entry['atc_substance'] is not None:
1731 atcs.append(entry['atc_substance'])
1732 # if entry['atc_drug'] is not None:
1733 # atcs.append(entry['atc_drug'])
1734 # allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (entry['substance'],), product_name = entry['product'])
1735 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (entry['substance'],))
1736
1737 tt = _('Substance intake entry (%s, %s) [#%s] \n') % (
1738 gmTools.bool2subst (
1739 boolean = entry['is_currently_active'],
1740 true_return = gmTools.bool2subst (
1741 boolean = entry['seems_inactive'],
1742 true_return = _('active, needs check'),
1743 false_return = _('active'),
1744 none_return = _('assumed active')
1745 ),
1746 false_return = _('inactive')
1747 ),
1748 gmTools.bool2subst (
1749 boolean = entry['intake_is_approved_of'],
1750 true_return = _('approved'),
1751 false_return = _('unapproved')
1752 ),
1753 entry['pk_substance_intake']
1754 )
1755
1756 if allg not in [None, False]:
1757 certainty = gmTools.bool2subst(allg['definite'], _('definite'), _('suspected'))
1758 tt += '\n'
1759 tt += ' !! ---- Cave ---- !!\n'
1760 tt += ' %s (%s): %s (%s)\n' % (
1761 allg['l10n_type'],
1762 certainty,
1763 allg['descriptor'],
1764 gmTools.coalesce(allg['reaction'], '')[:40]
1765 )
1766 tt += '\n'
1767
1768 tt += ' ' + _('Substance: %s [#%s]\n') % (entry['substance'], entry['pk_substance'])
1769 tt += ' ' + _('Preparation: %s\n') % entry['l10n_preparation']
1770 tt += ' ' + _('Amount per dose: %s %s') % (entry['amount'], entry['unit'])
1771 tt += '\n'
1772 tt += gmTools.coalesce(entry['atc_substance'], '', _(' ATC (substance): %s\n'))
1773
1774 tt += '\n'
1775
1776 tt += gmTools.coalesce (
1777 entry['product'],
1778 '',
1779 _(' Product name: %%s [#%s]\n') % entry['pk_drug_product']
1780 )
1781 tt += gmTools.coalesce(entry['atc_drug'], '', _(' ATC (drug): %s\n'))
1782
1783 tt += '\n'
1784
1785 tt += gmTools.coalesce(entry['schedule'], '', _(' Regimen: %s\n'))
1786
1787 if entry['is_long_term']:
1788 duration = ' %s %s' % (gmTools.u_arrow2right, gmTools.u_infinity)
1789 else:
1790 if entry['duration'] is None:
1791 duration = ''
1792 else:
1793 duration = ' %s %s' % (gmTools.u_arrow2right, gmDateTime.format_interval(entry['duration'], gmDateTime.acc_days))
1794
1795 tt += _(' Started %s%s%s\n') % (
1796 entry.medically_formatted_start,
1797 duration,
1798 gmTools.bool2subst(entry['is_long_term'], _(' (long-term)'), _(' (short-term)'), '')
1799 )
1800
1801 if entry['discontinued'] is not None:
1802 tt += _(' Discontinued %s\n') % gmDateTime.pydt_strftime(entry['discontinued'], '%Y %b %d')
1803 tt += gmTools.coalesce(entry['discontinue_reason'], '', _(' Reason: %s\n'))
1804
1805 tt += '\n'
1806
1807 tt += gmTools.coalesce(entry['aim'], '', _(' Aim: %s\n'))
1808 tt += gmTools.coalesce(entry['episode'], '', _(' Episode: %s\n'))
1809 tt += gmTools.coalesce(entry['health_issue'], '', _(' Health issue: %s\n'))
1810 tt += gmTools.coalesce(entry['notes'], '', _(' Advice: %s\n'))
1811
1812 tt += '\n'
1813
1814 tt += _('Revision: #%(row_ver)s, %(mod_when)s by %(mod_by)s.') % ({
1815 'row_ver': entry['row_version'],
1816 'mod_when': gmDateTime.pydt_strftime(entry['modified_when'], '%Y %b %d %H:%M:%S'),
1817 'mod_by': entry['modified_by']
1818 })
1819
1820 return tt
1821
1822 #------------------------------------------------------------
1823 # internal helpers
1824 #------------------------------------------------------------
1826 self.CreateGrid(0, 1)
1827 self.EnableEditing(0)
1828 self.EnableDragGridSize(1)
1829 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows)
1830
1831 self.SetColLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTER)
1832
1833 self.SetRowLabelSize(0)
1834 self.SetRowLabelAlignment(horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
1835
1836 #------------------------------------------------------------
1837 # properties
1838 #------------------------------------------------------------
1841
1845
1846 patient = property(_get_patient, _set_patient)
1847 #------------------------------------------------------------
1850
1854
1855 grouping_mode = property(_get_grouping_mode, _set_grouping_mode)
1856 #------------------------------------------------------------
1859
1863
1864 filter_show_unapproved = property(_get_filter_show_unapproved, _set_filter_show_unapproved)
1865 #------------------------------------------------------------
1868
1872
1873 filter_show_inactive = property(_get_filter_show_inactive, _set_filter_show_inactive)
1874 #------------------------------------------------------------
1875 # event handling
1876 #------------------------------------------------------------
1878 # dynamic tooltips: GridWindow, GridRowLabelWindow, GridColLabelWindow, GridCornerLabelWindow
1879 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells)
1880 #self.GetGridRowLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_row_labels)
1881 #self.GetGridColLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_col_labels)
1882
1883 # editing cells
1884 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
1885 #------------------------------------------------------------
1887 """Calculate where the mouse is and set the tooltip dynamically."""
1888
1889 # Use CalcUnscrolledPosition() to get the mouse position within the
1890 # entire grid including what's offscreen
1891 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
1892
1893 # use this logic to prevent tooltips outside the actual cells
1894 # apply to GetRowSize, too
1895 # tot = 0
1896 # for col in range(self.NumberCols):
1897 # tot += self.GetColSize(col)
1898 # if xpos <= tot:
1899 # self.tool_tip.Tip = 'Tool tip for Column %s' % (
1900 # self.GetColLabelValue(col))
1901 # break
1902 # else: # mouse is in label area beyond the right-most column
1903 # self.tool_tip.Tip = ''
1904
1905 row, col = self.XYToCell(x, y)
1906
1907 if row == self.__prev_tooltip_row:
1908 return
1909
1910 self.__prev_tooltip_row = row
1911
1912 try:
1913 evt.GetEventObject().SetToolTip(self.get_row_tooltip(row = row))
1914 except KeyError:
1915 pass
1916 #------------------------------------------------------------
1918 row = evt.GetRow()
1919 data = self.__row_data[row]
1920 edit_intake_of_substance(parent = self, substance = data)
1921
1922 #============================================================
1924
1925 panels = gmPathLab.get_test_panels(order_by = 'description')
1926 gmCfgWidgets.configure_string_from_list_option (
1927 parent = parent,
1928 message = _(
1929 '\n'
1930 'Select the measurements panel to show in the medications plugin.'
1931 '\n'
1932 ),
1933 option = 'horstspace.medications_plugin.lab_panel',
1934 bias = 'user',
1935 default_value = None,
1936 choices = [ '%s%s' % (p['description'], gmTools.coalesce(p['comment'], '', ' (%s)')) for p in panels ],
1937 columns = [_('Measurements panel')],
1938 data = [ p['pk_test_panel'] for p in panels ],
1939 caption = _('Configuring medications plugin measurements panel')
1940 )
1941
1942 #============================================================
1943 from Gnumed.wxGladeWidgets import wxgCurrentSubstancesPnl
1944
1945 -class cCurrentSubstancesPnl(wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl, gmRegetMixin.cRegetOnPaintMixin):
1946
1947 """Panel holding a grid with current substances. Used as notebook page."""
1948
1950
1951 wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl.__init__(self, *args, **kwargs)
1952 gmRegetMixin.cRegetOnPaintMixin.__init__(self)
1953
1954 self.__grouping_choice_labels = [
1955 {'label': _('Health issue'), 'data': 'issue'} ,
1956 {'label': _('Drug product'), 'data': 'product'},
1957 {'label': _('Episode'), 'data': 'episode'},
1958 {'label': _('Started'), 'data': 'start'}
1959 ]
1960 self.__lab_panel = None
1961
1962 self.__init_ui()
1963 self.__register_interests()
1964
1965 #-----------------------------------------------------
1967 self._CHCE_grouping.Clear()
1968 for option in self.__grouping_choice_labels:
1969 self._CHCE_grouping.Append(option['label'], option['data'])
1970 self._CHCE_grouping.SetSelection(0)
1971
1972 tt = self._BTN_heart.GetToolTipText()
1973 try:
1974 self._BTN_heart.SetToolTip(tt % gmMedication.URL_long_qt)
1975 except TypeError:
1976 _log.exception('translation error: %s', tt)
1977
1978 tt = self._BTN_kidneys.GetToolTipText()
1979 try:
1980 self._BTN_kidneys.SetToolTip(tt % gmMedication.URL_renal_insufficiency)
1981 except TypeError:
1982 _log.exception('translation error: %s', tt)
1983
1984 #-----------------------------------------------------
1985 # reget-on-paint mixin API
1986 #-----------------------------------------------------
1988 """Populate cells with data from model."""
1989 pat = gmPerson.gmCurrentPatient()
1990 if pat.connected:
1991 self._grid_substances.patient = pat
1992 self.__refresh_gfr(pat)
1993 self.__refresh_lab(patient = pat)
1994 else:
1995 self._grid_substances.patient = None
1996 self.__clear_gfr()
1997 self.__refresh_lab(patient = None)
1998 return True
1999
2000 #--------------------------------------------------------
2002
2003 self._GSZR_lab.Clear(True) # also delete child windows
2004 self._HLINE_lab.Hide()
2005
2006 if patient is None:
2007 self.Layout()
2008 return
2009
2010 emr = patient.emr
2011 most_recent_results = {}
2012
2013 # get most recent results for "LOINCs to monitor"
2014 loincs2monitor = set()
2015 loincs2monitor_data = {}
2016 loinc_max_age = {}
2017 loinc_max_age_str = {}
2018 for intake in self._grid_substances.get_row_data():
2019 for l in intake['loincs']:
2020 loincs2monitor.add(l['loinc'])
2021 loincs2monitor_data[l['loinc']] = {
2022 'substance': intake['substance'],
2023 'comment': l['comment']
2024 }
2025 if l['max_age_in_secs'] is not None:
2026 try:
2027 if loinc_max_age[l['loinc']] > l['max_age_in_secs']:
2028 loinc_max_age[l['loinc']] = l['max_age_in_secs']
2029 loinc_max_age_str[l['loinc']] = l['max_age_str']
2030 except KeyError:
2031 loinc_max_age[l['loinc']] = l['max_age_in_secs']
2032 loinc_max_age_str[l['loinc']] = l['max_age_str']
2033 loincs2monitor_missing = loincs2monitor.copy()
2034 for loinc in loincs2monitor:
2035 result = emr.get_most_recent_results_in_loinc_group (
2036 loincs = [loinc],
2037 no_of_results = 1,
2038 consider_meta_type = True
2039 )
2040 if result is None:
2041 continue
2042 loincs2monitor_missing.remove(loinc)
2043 # make unique
2044 most_recent_results[result['pk_test_result']] = result
2045
2046 # get most recent results for "general medication monitoring lab panel"
2047 if self.__lab_panel is not None:
2048 for result in self.__lab_panel.get_most_recent_results (
2049 pk_patient = patient.ID,
2050 order_by = 'unified_abbrev',
2051 group_by_meta_type = True
2052 ):
2053 try: loincs2monitor_missing.remove(result['loinc_tt'])
2054 except KeyError: pass
2055 try: loincs2monitor_missing.remove(result['loinc_meta'])
2056 except KeyError: pass
2057 # make unique
2058 most_recent_results[result['pk_test_result']] = result
2059
2060 # those need special treatment
2061 gfr = emr.get_most_recent_results_in_loinc_group(loincs = gmLOINC.LOINC_gfr_quantity, no_of_results = 1)
2062 crea = emr.get_most_recent_results_in_loinc_group(loincs = gmLOINC.LOINC_creatinine_quantity, no_of_results = 1)
2063 edc = emr.EDC
2064
2065 # display EDC
2066 if edc is not None:
2067 if emr.EDC_is_fishy:
2068 lbl = wx.StaticText(self, -1, _('EDC (!?!):'))
2069 val = wx.StaticText(self, -1, gmDateTime.pydt_strftime(edc, format = '%Y %b %d'))
2070 else:
2071 lbl = wx.StaticText(self, -1, _('EDC:'))
2072 val = wx.StaticText(self, -1, gmDateTime.pydt_strftime(edc, format = '%Y %b %d'))
2073 lbl.SetForegroundColour('blue')
2074 szr = wx.BoxSizer(wx.HORIZONTAL)
2075 szr.Add(lbl, 0, wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3)
2076 szr.Add(val, 1, wx.ALIGN_CENTER_VERTICAL)
2077 self._GSZR_lab.Add(szr)
2078
2079 # decide which among Crea or GFR to show
2080 if crea is None:
2081 gfr_3_months_older_than_crea = False
2082 if gfr is not None:
2083 most_recent_results = [gfr] + most_recent_results
2084 elif gfr is None:
2085 gfr_3_months_older_than_crea = True
2086 else:
2087 three_months = pydt.timedelta(weeks = 14)
2088 gfr_3_months_older_than_crea = (crea['clin_when'] - gfr['clin_when']) > three_months
2089 if not gfr_3_months_older_than_crea:
2090 most_recent_results = [gfr] + most_recent_results
2091
2092 # if GFR not found in most_recent_results or old, then calculate
2093 now = gmDateTime.pydt_now_here()
2094 if gfr_3_months_older_than_crea:
2095 calc = gmClinicalCalculator.cClinicalCalculator()
2096 calc.patient = patient
2097 gfr = calc.eGFR
2098 if gfr.numeric_value is None:
2099 gfr_msg = '?'
2100 else:
2101 gfr_msg = _('%.1f (%s ago)') % (
2102 gfr.numeric_value,
2103 gmDateTime.format_interval_medically(now - gfr.date_valid)
2104 )
2105 lbl = wx.StaticText(self, -1, _('eGFR:'))
2106 lbl.SetForegroundColour('blue')
2107 val = wx.StaticText(self, -1, gfr_msg)
2108 tts = []
2109 for egfr in calc.eGFRs:
2110 if egfr.numeric_value is None:
2111 continue
2112 tts.append(egfr.format (
2113 left_margin = 0,
2114 width = 50,
2115 eol = '\n',
2116 with_formula = False,
2117 with_warnings = True,
2118 with_variables = False,
2119 with_sub_results = False,
2120 return_list = False
2121 ))
2122 val.SetToolTip('\n'.join(tts))
2123 szr = wx.BoxSizer(wx.HORIZONTAL)
2124 szr.Add(lbl, 0, wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3)
2125 szr.Add(val, 1, wx.ALIGN_CENTER_VERTICAL)
2126 self._GSZR_lab.Add(szr)
2127
2128 # eventually add most-recent results from monitoring panel and substances monitoring
2129 for pk_result in most_recent_results:
2130 result = most_recent_results[pk_result]
2131 # test type
2132 lbl = wx.StaticText(self, -1, '%s:' % result['unified_abbrev'])
2133 lbl.SetForegroundColour('blue')
2134 # calculate test result
2135 indicate_attention = False
2136 if result.is_considered_abnormal:
2137 indicate_attention = True
2138 # calculate tooltip data
2139 max_age = None
2140 try:
2141 max_age = loinc_max_age[result['loinc_tt']]
2142 max_age_str = loinc_max_age_str[result['loinc_tt']]
2143 except KeyError:
2144 try:
2145 max_age = loinc_max_age[result['loinc_meta']]
2146 max_age_str = loinc_max_age_str[result['loinc_meta']]
2147 except KeyError:
2148 pass
2149 subst2monitor = None
2150 try:
2151 subst2monitor = loincs2monitor_data[result['loinc_tt']]['substance']
2152 except KeyError:
2153 try:
2154 subst2monitor = loincs2monitor_data[result['loinc_meta']]['substance']
2155 except KeyError:
2156 pass
2157 monitor_comment = None
2158 try:
2159 monitor_comment = loincs2monitor_data[result['loinc_tt']]['comment']
2160 except KeyError:
2161 try:
2162 monitor_comment = loincs2monitor_data[result['loinc_meta']]['comment']
2163 except KeyError:
2164 pass
2165 result_age = now - result['clin_when']
2166 unhappy_reasons = []
2167 if result.is_considered_abnormal:
2168 indicator = result.formatted_abnormality_indicator
2169 if indicator == '':
2170 unhappy_reasons.append(_(' - abnormal'))
2171 else:
2172 unhappy_reasons.append(_(' - abnormal: %s') % indicator)
2173 if max_age is not None:
2174 if result_age.total_seconds() > max_age:
2175 unhappy_reasons.append(_(' - too old: %s ago (max: %s)') % (
2176 gmDateTime.format_interval_medically(result_age),
2177 max_age_str
2178 ))
2179 # generate tooltip
2180 tt = [_('Most recent: %s ago') % gmDateTime.format_interval_medically(result_age)]
2181 if subst2monitor is not None:
2182 tt.append(_('Why monitor: %s') % subst2monitor)
2183 if monitor_comment is not None:
2184 tt.append(' %s' % monitor_comment)
2185 if len(unhappy_reasons) > 0:
2186 indicate_attention = True
2187 tt.append(_('Problems:'))
2188 tt.extend(unhappy_reasons)
2189 tt = '%s\n\n%s' % (
2190 '\n'.join(tt),
2191 result.format()
2192 )
2193 # set test result and tooltip
2194 val = wx.StaticText(self, -1, '%s%s%s' % (
2195 result['unified_val'],
2196 gmTools.coalesce(result['val_unit'], '', ' %s'),
2197 gmTools.bool2subst(indicate_attention, gmTools.u_frowning_face, '', '')
2198 ))
2199 val.SetToolTip(tt)
2200 if result.is_considered_abnormal:
2201 val.SetForegroundColour('red')
2202 szr = wx.BoxSizer(wx.HORIZONTAL)
2203 szr.Add(lbl, 0, wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3)
2204 szr.Add(val, 1, wx.ALIGN_CENTER_VERTICAL)
2205 self._GSZR_lab.Add(szr)
2206
2207 # hint at missing, but required results (set to be
2208 # monitored under intakes based on LOINCs):
2209 for loinc in loincs2monitor_missing:
2210 #szr.Add(lbl, 0, wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3)
2211 loinc_data = gmLOINC.loinc2data(loinc)
2212 if loinc_data is None:
2213 loinc_str = loinc
2214 else:
2215 loinc_str = loinc_data['term']
2216 val = wx.StaticText(self, -1, '%s!' % loinc_str)
2217 tt = [
2218 _('No test result for: %s (%s)') % (loinc_str, loinc),
2219 '',
2220 _('Why monitor: %s' % loincs2monitor_data[loinc]['substance'])
2221 ]
2222 try:
2223 tt.append(' %s' % loincs2monitor_data[loinc]['comment'])
2224 except KeyError:
2225 pass
2226 val.SetToolTip('\n'.join(tt))
2227 val.SetForegroundColour('orange')
2228 szr = wx.BoxSizer(wx.HORIZONTAL)
2229 szr.Add(val, 1, wx.ALIGN_CENTER_VERTICAL)
2230 self._GSZR_lab.Add(szr)
2231
2232 self._HLINE_lab.Show()
2233 self.Layout()
2234
2235 #--------------------------------------------------------
2237 gfr = patient.emr.get_most_recent_results_in_loinc_group(loincs = gmLOINC.LOINC_gfr_quantity, no_of_results = 1)
2238 if gfr is None:
2239 calc = gmClinicalCalculator.cClinicalCalculator()
2240 calc.patient = patient
2241 gfr = calc.eGFR
2242 if gfr.numeric_value is None:
2243 msg = _('GFR: ?')
2244 tt = gfr.message
2245 else:
2246 msg = _('eGFR: %.1f (%s)') % (
2247 gfr.numeric_value,
2248 gmDateTime.pydt_strftime (
2249 gfr.date_valid,
2250 format = '%b %Y'
2251 )
2252 )
2253 egfrs = calc.eGFRs
2254 tts = []
2255 for egfr in egfrs:
2256 if egfr.numeric_value is None:
2257 continue
2258 tts.append(egfr.format (
2259 left_margin = 0,
2260 width = 50,
2261 eol = '\n',
2262 with_formula = False,
2263 with_warnings = True,
2264 with_variables = False,
2265 with_sub_results = False,
2266 return_list = False
2267 ))
2268 tt = '\n'.join(tts)
2269 else:
2270 msg = '%s: %s %s (%s)\n' % (
2271 gfr['unified_abbrev'],
2272 gfr['unified_val'],
2273 gmTools.coalesce(gfr['abnormality_indicator'], '', ' (%s)'),
2274 gmDateTime.pydt_strftime (
2275 gfr['clin_when'],
2276 format = '%b %Y'
2277 )
2278 )
2279 tt = _('GFR reported by path lab')
2280
2281 self._LBL_gfr.SetLabel(msg)
2282 self._LBL_gfr.SetToolTip(tt)
2283 self._LBL_gfr.Refresh()
2284 self.Layout()
2285
2286 #--------------------------------------------------------
2291
2292 #--------------------------------------------------------
2293 # event handling
2294 #--------------------------------------------------------
2296 gmDispatcher.connect(signal = 'pre_patient_unselection', receiver = self._on_pre_patient_unselection)
2297 gmDispatcher.connect(signal = 'post_patient_selection', receiver = self._on_post_patient_selection)
2298 gmDispatcher.connect(signal = 'clin.substance_intake_mod_db', receiver = self._schedule_data_reget)
2299 gmDispatcher.connect(signal = 'clin.test_result_mod_db', receiver = self._on_test_result_mod)
2300
2301 #--------------------------------------------------------
2304
2305 #--------------------------------------------------------
2307 dbcfg = gmCfg.cCfgSQL()
2308 pk_panel = dbcfg.get2 (
2309 option = 'horstspace.medications_plugin.lab_panel',
2310 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
2311 bias = 'user'
2312 )
2313 if pk_panel is None:
2314 self.__lab_panel = None
2315 else:
2316 self.__lab_panel = gmPathLab.cTestPanel(aPK_obj = pk_panel)
2317 self._grid_substances.patient = None
2318 self.__refresh_lab(patient = None)
2319 #--------------------------------------------------------
2322 #--------------------------------------------------------
2325 #--------------------------------------------------------
2328 #--------------------------------------------------------
2331 #--------------------------------------------------------
2334 #--------------------------------------------------------
2337 #--------------------------------------------------------
2339 event.Skip()
2340 selected_item_idx = self._CHCE_grouping.GetSelection()
2341 if selected_item_idx is wx.NOT_FOUND:
2342 return
2343 self._grid_substances.grouping_mode = self._CHCE_grouping.GetClientData(selected_item_idx)
2344 #--------------------------------------------------------
2347 #--------------------------------------------------------
2350 #--------------------------------------------------------
2353 #--------------------------------------------------------
2356 #--------------------------------------------------------
2359 #--------------------------------------------------------
2362 #--------------------------------------------------------
2365 #--------------------------------------------------------
2368
2369 #============================================================
2370 # main
2371 #------------------------------------------------------------
2372 if __name__ == '__main__':
2373
2374 if len(sys.argv) < 2:
2375 sys.exit()
2376
2377 if sys.argv[1] != 'test':
2378 sys.exit()
2379
2380 from Gnumed.business import gmPersonSearch
2381
2382 pat = gmPersonSearch.ask_for_patient()
2383 if pat is None:
2384 sys.exit()
2385 gmPerson.set_active_patient(patient = pat)
2386
2387 #----------------------------------------
2388 app = wx.PyWidgetTester(size = (600, 300))
2389 app.SetWidget(cSubstanceIntakeObjectPhraseWheel, -1)
2390 app.MainLoop()
2391 #manage_substance_intakes()
2392
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Fri Jan 25 02:55:27 2019 | http://epydoc.sourceforge.net |