| 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 gfrs = emr.get_most_recent_results_in_loinc_group(loincs = gmLOINC.LOINC_gfr_quantity, max_no_of_results = 1)
408 if len(gfrs) == 0:
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 gfr = gfrs[0]
433 msg += '%s: %s %s (%s)\n' % (
434 gfr['unified_abbrev'],
435 gfr['unified_val'],
436 gmTools.coalesce(gfr['abnormality_indicator'], '', ' (%s)'),
437 gmDateTime.pydt_strftime (
438 gfr['clin_when'],
439 format = '%Y %b %d'
440 )
441 )
442 tooltip += _('GFR reported by path lab')
443
444 # pregnancy
445 edc = emr.EDC
446 if edc is not None:
447 msg += '\n\n'
448 if emr.EDC_is_fishy:
449 msg += _('EDC (!?!): %s') % gmDateTime.pydt_strftime(edc, format = '%Y %b %d')
450 tooltip += _(
451 'The Expected Date of Confinement is rather questionable.\n'
452 '\n'
453 'Please check patient age, patient gender, time until/since EDC.'
454 )
455 else:
456 msg += _('EDC: %s') % gmDateTime.pydt_strftime(edc, format = '%Y %b %d')
457
458 self._LBL_allergies.SetLabel(msg)
459 self._LBL_allergies.SetToolTip(tooltip)
460
461 #----------------------------------------------------------------
463
464 drug = self._PRW_drug.GetData(as_instance = True)
465 if drug is None:
466 self._LBL_drug_details.SetLabel('')
467 self._LBL_drug_details.SetToolTip('')
468 self.Layout()
469 return
470
471 if len(drug['components']) == 0:
472 comps = _('<no components>')
473 else:
474 comps = '\n'.join ([
475 ' %s %s%s%s' % (
476 c['substance'],
477 c['amount'],
478 c['unit'],
479 gmTools.coalesce(c['dose_unit'], '', '/%s')
480 )
481 for c in drug['components']
482 ])
483 self._LBL_drug_details.SetLabel('%s\n%s' % (drug['product'], comps))
484 self._LBL_drug_details.SetToolTip(drug.format())
485 self.Layout()
486 return
487
488 #----------------------------------------------------------------
489 # generic Edit Area mixin API
490 #----------------------------------------------------------------
492
493 self._PRW_drug.display_as_valid(True)
494
495 # if we are editing the drug SHOULD exist so don't error
496 if self.mode == 'edit':
497 return True
498
499 selected_drug = self._PRW_drug.GetData(as_instance = True)
500
501 # no drug selected
502 if selected_drug is None:
503 val = self._PRW_drug.GetValue().strip()
504 if val == '':
505 self._PRW_drug.display_as_valid(False)
506 self._PRW_drug.SetFocus()
507 return False
508 # create as a generic, single-substance drug if that does not exist
509 drug = gmSubstanceMgmtWidgets.edit_single_component_generic_drug (
510 parent = self,
511 drug = None,
512 single_entry = True,
513 fields = {'substance': {'value': val, 'data': None}},
514 return_drug = True
515 )
516 if drug is None:
517 self._PRW_drug.display_as_valid(False)
518 self._PRW_drug.SetFocus()
519 return False
520 comp = drug.components[0]
521 self._PRW_drug.SetText (
522 _('%s w/ %s%s%s of %s') % (
523 comp['product'],
524 comp['amount'],
525 comp['unit'],
526 gmTools.coalesce(comp['dose_unit'], '', '/%s'),
527 comp['substance']
528 ),
529 drug['pk_drug_product']
530 )
531 selected_drug = drug
532 self.__refresh_drug_details()
533 self._PRW_drug.display_as_valid(True)
534 self._PRW_drug.SetFocus()
535 # return False despite there's now a drug such
536 # that the user has another chance to inspect
537 # the edit area data after creating a new drug
538 return False
539
540 # drug already exists as intake
541 if selected_drug.exists_as_intake(pk_patient = gmPerson.gmCurrentPatient().ID):
542 title = _('Adding substance intake entry')
543 msg = _(
544 'The patient is already taking\n'
545 '\n'
546 ' %s\n'
547 '\n'
548 'You will want to adjust the schedule\n'
549 'rather than document the intake twice.'
550 ) % self._PRW_drug.GetValue().strip()
551 gmGuiHelpers.gm_show_warning(aTitle = title, aMessage = msg)
552 self._PRW_drug.display_as_valid(False)
553 self._PRW_drug.SetFocus()
554 return False
555
556 self._PRW_drug.display_as_valid(True)
557 return True
558
559 #----------------------------------------------------------------
561
562 validity = self._check_drug_is_valid()
563
564 # episode must be set if intake is to be approved of
565 if self._CHBOX_approved.IsChecked():
566 if self._PRW_episode.GetValue().strip() == '':
567 self._PRW_episode.display_as_valid(False)
568 validity = False
569 else:
570 self._PRW_episode.display_as_valid(True)
571
572 if self._PRW_duration.GetValue().strip() in ['', gmTools.u_infinity]:
573 self._PRW_duration.display_as_valid(True)
574 else:
575 if self._PRW_duration.GetData() is None:
576 # no data ...
577 if gmDateTime.str2interval(self._PRW_duration.GetValue()) is None:
578 self._PRW_duration.display_as_valid(False)
579 validity = False
580 # ... but valid string
581 else:
582 self._PRW_duration.display_as_valid(True)
583 # has data
584 else:
585 self._PRW_duration.display_as_valid(True)
586
587 # started must exist or be unknown
588 started = None
589 if self._CHBOX_start_unknown.IsChecked() is False:
590 started = self._DP_started.GetData()
591 if started is None:
592 self._DP_started.display_as_valid(False)
593 self._DP_started.SetFocus()
594 validity = False
595 else:
596 self._DP_started.display_as_valid(True)
597
598 if validity is False:
599 self.StatusText = _('Input incomplete/invalid for saving as substance intake.')
600
601 # discontinued must be "< now()" AND "> started" if at all
602 discontinued = self._DP_discontinued.GetData()
603 if discontinued is not None:
604 now = gmDateTime.pydt_now_here().replace (
605 hour = 23,
606 minute = 59,
607 second = 59,
608 microsecond = 111111
609 )
610 # not in the future
611 if discontinued > now:
612 self._DP_discontinued.display_as_valid(False)
613 validity = False
614 self.StatusText = _('Discontinued (%s) in the future (now: %s)!') % (discontinued, now)
615 else:
616 if started is not None:
617 started = started.replace (
618 hour = 0,
619 minute = 0,
620 second = 0,
621 microsecond = 1
622 )
623 # and not before it was started
624 if started > discontinued:
625 self._DP_started.display_as_valid(False)
626 self._DP_discontinued.display_as_valid(False)
627 validity = False
628 self.StatusText = _('Discontinued (%s) before started (%s) !') % (discontinued, started)
629 else:
630 self._DP_started.display_as_valid(True)
631 self._DP_discontinued.display_as_valid(True)
632
633 return validity
634
635 #----------------------------------------------------------------
637
638 epi = self._PRW_episode.GetData()
639 if epi is None:
640 # create new episode, Jim wants it to auto-open
641 epi = self._PRW_episode.GetData(can_create = True, is_open = True)
642
643 selected_drug = self._PRW_drug.GetData(as_instance = True)
644 intake = selected_drug.turn_into_intake (
645 encounter = gmPerson.gmCurrentPatient().emr.current_encounter['pk_encounter'],
646 episode = epi
647 )
648
649 if intake is None:
650 self.StatusText = _('Cannot add duplicate of (maybe inactive) substance intake.')
651 return False
652
653 intake['started'] = self._DP_started.GetData()
654 if self._CHBOX_start_unknown.IsChecked():
655 intake['comment_on_start'] = '?'
656 else:
657 intake['comment_on_start'] = self._PRW_start_certainty.GetValue().strip()
658 intake['discontinued'] = self._DP_discontinued.GetData()
659 if intake['discontinued'] is None:
660 intake['discontinue_reason'] = None
661 else:
662 intake['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
663 intake['schedule'] = self._PRW_schedule.GetValue().strip()
664 intake['aim'] = self._PRW_aim.GetValue().strip()
665 intake['notes'] = self._PRW_notes.GetValue().strip()
666 intake['is_long_term'] = self._CHBOX_long_term.IsChecked()
667 intake['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
668 if self._PRW_duration.GetValue().strip() in ['', gmTools.u_infinity]:
669 intake['duration'] = None
670 else:
671 if self._PRW_duration.GetData() is None:
672 intake['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
673 else:
674 intake['duration'] = self._PRW_duration.GetData()
675 intake.save()
676
677 self.data = intake
678
679 return True
680
681 #----------------------------------------------------------------
683
684 # auto-applies to all components of a multi-component drug if any:
685 self.data['started'] = self._DP_started.GetData()
686 if self._CHBOX_start_unknown.IsChecked():
687 self.data['comment_on_start'] = '?'
688 else:
689 self.data['comment_on_start'] = self._PRW_start_certainty.GetValue().strip()
690 self.data['discontinued'] = self._DP_discontinued.GetData()
691 if self.data['discontinued'] is None:
692 self.data['discontinue_reason'] = None
693 else:
694 self.data['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
695 self.data['schedule'] = self._PRW_schedule.GetValue()
696 self.data['is_long_term'] = self._CHBOX_long_term.IsChecked()
697 self.data['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
698 if self._PRW_duration.GetValue().strip() in ['', gmTools.u_infinity]:
699 self.data['duration'] = None
700 else:
701 if self._PRW_duration.GetData() is None:
702 self.data['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
703 else:
704 self.data['duration'] = self._PRW_duration.GetData()
705
706 # per-component
707 self.data['aim'] = self._PRW_aim.GetValue()
708 self.data['notes'] = self._PRW_notes.GetValue()
709 epi = self._PRW_episode.GetData()
710 if epi is None:
711 # create new episode, Jim wants it to auto-open
712 epi = self._PRW_episode.GetData(can_create = True, is_open = True)
713 self.data['pk_episode'] = epi
714
715 self.data.save()
716
717 return True
718
719 #----------------------------------------------------------------
721 self._PRW_drug.SetText('', None)
722
723 self._PRW_schedule.SetText('', None)
724 self._PRW_duration.SetText('', None)
725 self._PRW_aim.SetText('', None)
726 self._PRW_notes.SetText('', None)
727 self._PRW_episode.SetText('', None)
728
729 self._CHBOX_long_term.SetValue(False)
730 self._CHBOX_approved.SetValue(True)
731
732 self._CHBOX_start_unknown.SetValue(False)
733 self._DP_started.SetData(gmDateTime.pydt_now_here())
734 self._DP_started.Enable(True)
735 self._PRW_start_certainty.SetText('', None)
736 self._PRW_start_certainty.Enable(True)
737 self._DP_discontinued.SetData(None)
738 self._PRW_discontinue_reason.SetValue('')
739 self._PRW_discontinue_reason.Enable(False)
740
741 self.__refresh_drug_details()
742 self.__refresh_precautions()
743
744 self._PRW_drug.SetFocus()
745
746 #----------------------------------------------------------------
748
749 self._PRW_drug.SetText (
750 _('%s w/ %s%s%s of %s') % (
751 self.data['product'],
752 self.data['amount'],
753 self.data['unit'],
754 gmTools.coalesce(self.data['dose_unit'], '', '/%s'),
755 self.data['substance']
756 ),
757 self.data['pk_drug_product']
758 )
759
760 self._PRW_drug.Disable()
761
762 if self.data['is_long_term']:
763 self._CHBOX_long_term.SetValue(True)
764 self._PRW_duration.Enable(False)
765 self._PRW_duration.SetText(gmTools.u_infinity, None)
766 self._BTN_discontinued_as_planned.Enable(False)
767 else:
768 self._CHBOX_long_term.SetValue(False)
769 self._PRW_duration.Enable(True)
770 self._BTN_discontinued_as_planned.Enable(True)
771 self._PRW_duration.SetData(self.data['duration'])
772 self._PRW_aim.SetText(gmTools.coalesce(self.data['aim'], ''), self.data['aim'])
773 self._PRW_notes.SetText(gmTools.coalesce(self.data['notes'], ''), self.data['notes'])
774 self._PRW_episode.SetData(self.data['pk_episode'])
775 self._PRW_schedule.SetText(gmTools.coalesce(self.data['schedule'], ''), self.data['schedule'])
776
777 self._CHBOX_approved.SetValue(self.data['intake_is_approved_of'])
778
779 self._DP_started.SetData(self.data['started'])
780 self._PRW_start_certainty.SetText(self.data['comment_on_start'], None)
781 if self.data['start_is_unknown']:
782 self._CHBOX_start_unknown.SetValue(True)
783 self._DP_started.Enable(False)
784 self._PRW_start_certainty.Enable(False)
785 else:
786 self._CHBOX_start_unknown.SetValue(False)
787 self._DP_started.Enable(True)
788 self._PRW_start_certainty.Enable(True)
789
790 self._DP_discontinued.SetData(self.data['discontinued'])
791 self._PRW_discontinue_reason.SetValue(gmTools.coalesce(self.data['discontinue_reason'], ''))
792 if self.data['discontinued'] is not None:
793 self._PRW_discontinue_reason.Enable()
794
795 self.__refresh_drug_details()
796 self.__refresh_precautions()
797
798 self._PRW_schedule.SetFocus()
799
800 #----------------------------------------------------------------
802 self._refresh_as_new()
803
804 self._PRW_episode.SetData(self.data['pk_episode'])
805 self._DP_started.SetData(self.data['started'])
806
807 self._PRW_drug.SetFocus()
808
809 #----------------------------------------------------------------
810 # event handlers
811 #----------------------------------------------------------------
814
815 #----------------------------------------------------------------
817 drug = self._PRW_drug.GetData(as_instance = True)
818 if drug is None:
819 self._PRW_aim.unset_context(context = 'substance')
820 return
821 # do not set to self._PRW_drug.GetValue() as that will contain all
822 # sorts of additional info, rather set to the canonical drug['substance']
823 # self._PRW_aim.set_context(context = u'substance', val = drug['substance'])
824
825 #----------------------------------------------------------------
827 if self._DP_discontinued.GetData() is None:
828 self._PRW_discontinue_reason.Enable(False)
829 else:
830 self._PRW_discontinue_reason.Enable(True)
831
832 #----------------------------------------------------------------
835
836 #----------------------------------------------------------------
839
840 #----------------------------------------------------------------
843
844 #----------------------------------------------------------------
847
848 #----------------------------------------------------------------
851
852 #----------------------------------------------------------------
860
861 #----------------------------------------------------------------
891
892 #----------------------------------------------------------------
894 if self._CHBOX_long_term.IsChecked() is True:
895 self._PRW_duration.Enable(False)
896 self._BTN_discontinued_as_planned.Enable(False)
897 self._PRW_discontinue_reason.Enable(False)
898 else:
899 self._PRW_duration.Enable(True)
900 self._BTN_discontinued_as_planned.Enable(True)
901 self._PRW_discontinue_reason.Enable(True)
902
903 self.__refresh_precautions()
904
905 #----------------------------------------------------------------
907 event.Skip()
908 if self._CHBOX_start_unknown.IsChecked() is True:
909 self._DP_started.Enable(False)
910 self._PRW_start_certainty.Enable(False)
911 else:
912 self._DP_started.Enable(True)
913 self._PRW_start_certainty.Enable(True)
914
915 self.__refresh_precautions()
916
917 #----------------------------------------------------------------
919 if not self.save():
920 return False
921
922 return turn_substance_intake_into_allergy (
923 parent = self,
924 intake = self.data,
925 emr = gmPerson.gmCurrentPatient().emr
926 )
927
928 #============================================================
930
931 comps = intake.containing_drug.components
932 if len(comps) > 1:
933 msg = _(
934 'This intake is part of a multi-component drug product:\n'
935 '\n'
936 ' %s\n'
937 '\n'
938 'Really delete all intakes related to this drug product ?'
939 ) % '\n '.join (
940 [ '%s %s%s' % (c['substance'], c['amount'], c.formatted_units) for c in comps ]
941 )
942 delete_all = gmGuiHelpers.gm_show_question (
943 title = _('Deleting medication / substance intake'),
944 question = msg
945 )
946 if not delete_all:
947 return
948
949 msg = _(
950 '\n'
951 '[%s]\n'
952 '\n'
953 'It may be prudent to edit (before deletion) the details\n'
954 'of this substance intake entry so as to leave behind\n'
955 'some indication of why it was deleted.\n'
956 ) % intake.format()
957
958 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
959 parent,
960 -1,
961 caption = _('Deleting medication / substance intake'),
962 question = msg,
963 button_defs = [
964 {'label': _('&Edit'), 'tooltip': _('Allow editing of substance intake entry before deletion.'), 'default': True},
965 {'label': _('&Delete'), 'tooltip': _('Delete immediately without editing first.')},
966 {'label': _('&Cancel'), 'tooltip': _('Abort. Do not delete or edit substance intake entry.')}
967 ]
968 )
969
970 edit_first = dlg.ShowModal()
971 dlg.DestroyLater()
972
973 if edit_first == wx.ID_CANCEL:
974 return
975
976 if edit_first == wx.ID_YES:
977 edit_intake_of_substance(parent = parent, substance = intake)
978 delete_it = gmGuiHelpers.gm_show_question (
979 aMessage = _('Now delete substance intake entry ?'),
980 aTitle = _('Deleting medication / substance intake')
981 )
982 else:
983 delete_it = True
984
985 if not delete_it:
986 return
987
988 gmMedication.delete_substance_intake(pk_intake = intake['pk_substance_intake'], delete_siblings = True)
989
990 #------------------------------------------------------------
992 ea = cSubstanceIntakeEAPnl(parent, -1, substance = substance)
993 dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = single_entry)
994 dlg.SetTitle(gmTools.coalesce(substance, _('Adding medication/non-medication substance intake'), _('Editing medication/non-medication substance intake')))
995 dlg.left_extra_button = (
996 _('Allergy'),
997 _('Document an allergy against this substance.'),
998 ea.turn_into_allergy
999 )
1000 dlg.SetSize((650,500))
1001 if dlg.ShowModal() == wx.ID_OK:
1002 dlg.DestroyLater()
1003 return True
1004 dlg.DestroyLater()
1005 return False
1006
1007 #============================================================
1008 # current substances grid
1009 #------------------------------------------------------------
1011
1012 if parent is None:
1013 parent = wx.GetApp().GetTopWindow()
1014
1015 template = gmFormWidgets.manage_form_templates (
1016 parent = parent,
1017 template_types = ['current medication list']
1018 )
1019 option = 'form_templates.medication_list'
1020
1021 if template is None:
1022 gmDispatcher.send(signal = 'statustext', msg = _('No medication list template configured.'), beep = True)
1023 return None
1024
1025 if template['engine'] not in ['L', 'X', 'T']:
1026 gmDispatcher.send(signal = 'statustext', msg = _('No medication list template configured.'), beep = True)
1027 return None
1028
1029 dbcfg = gmCfg.cCfgSQL()
1030 dbcfg.set (
1031 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
1032 option = option,
1033 value = '%s - %s' % (template['name_long'], template['external_version'])
1034 )
1035
1036 return template
1037
1038 #------------------------------------------------------------
1040
1041 if parent is None:
1042 parent = wx.GetApp().GetTopWindow()
1043
1044 # 1) get template
1045 dbcfg = gmCfg.cCfgSQL()
1046 option = 'form_templates.medication_list'
1047
1048 template = dbcfg.get2 (
1049 option = option,
1050 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
1051 bias = 'user'
1052 )
1053
1054 if template is None:
1055 template = configure_medication_list_template(parent = parent)
1056 if template is None:
1057 gmGuiHelpers.gm_show_error (
1058 aMessage = _('There is no medication list template configured.'),
1059 aTitle = _('Printing medication list')
1060 )
1061 return False
1062 else:
1063 try:
1064 name, ver = template.split(' - ')
1065 except Exception:
1066 _log.exception('problem splitting medication list template name [%s]', template)
1067 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading medication list template.'), beep = True)
1068 return False
1069 template = gmForms.get_form_template(name_long = name, external_version = ver)
1070 if template is None:
1071 gmGuiHelpers.gm_show_error (
1072 aMessage = _('Cannot load medication list template [%s - %s]') % (name, ver),
1073 aTitle = _('Printing medication list')
1074 )
1075 return False
1076
1077 # 2) process template
1078 meds_list = gmFormWidgets.generate_form_from_template (
1079 parent = parent,
1080 template = template,
1081 edit = False
1082 )
1083 if meds_list is None:
1084 return False
1085
1086 # 3) print template
1087 return gmFormWidgets.act_on_generated_forms (
1088 parent = parent,
1089 forms = [meds_list],
1090 jobtype = 'medication_list',
1091 #episode_name = u'administrative',
1092 episode_name = gmMedication.DEFAULT_MEDICATION_HISTORY_EPISODE,
1093 progress_note = _('generated medication list document'),
1094 review_copy_as_normal = True
1095 )
1096
1097 #------------------------------------------------------------
1099
1100 if parent is None:
1101 parent = wx.GetApp().GetTopWindow()
1102
1103 template = gmFormWidgets.manage_form_templates (
1104 parent = parent,
1105 msg = _('Select the default prescription template:'),
1106 template_types = ['prescription', 'current medication list']
1107 )
1108
1109 if template is None:
1110 gmDispatcher.send(signal = 'statustext', msg = _('No prescription template configured.'), beep = True)
1111 return None
1112
1113 if template['engine'] not in ['L', 'X', 'T']:
1114 gmDispatcher.send(signal = 'statustext', msg = _('No prescription template configured.'), beep = True)
1115 return None
1116
1117 option = 'form_templates.prescription'
1118 dbcfg = gmCfg.cCfgSQL()
1119 dbcfg.set (
1120 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
1121 option = option,
1122 value = '%s - %s' % (template['name_long'], template['external_version'])
1123 )
1124
1125 return template
1126
1127 #------------------------------------------------------------
1129
1130 if parent is None:
1131 parent = wx.GetApp().GetTopWindow()
1132
1133 dbcfg = gmCfg.cCfgSQL()
1134 option = 'form_templates.prescription'
1135 template_name = dbcfg.get2 (
1136 option = option,
1137 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
1138 bias = 'user'
1139 )
1140
1141 if template_name is None:
1142 template = configure_prescription_template(parent = parent)
1143 if template is None:
1144 gmGuiHelpers.gm_show_error (
1145 aMessage = _('There is no prescription template configured.'),
1146 aTitle = _('Printing prescription')
1147 )
1148 return None
1149 return template
1150
1151 try:
1152 name, ver = template_name.split(' - ')
1153 except Exception:
1154 _log.exception('problem splitting prescription template name [%s]', template_name)
1155 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading prescription template.'), beep = True)
1156 return False
1157 template = gmForms.get_form_template(name_long = name, external_version = ver)
1158 if template is None:
1159 gmGuiHelpers.gm_show_error (
1160 aMessage = _('Cannot load prescription template [%s - %s]') % (name, ver),
1161 aTitle = _('Printing prescription')
1162 )
1163 return None
1164 return template
1165
1166 #------------------------------------------------------------
1168
1169 # 1) get template
1170 rx_template = get_prescription_template(parent = parent)
1171 if rx_template is None:
1172 return False
1173
1174 # 2) process template
1175 rx = gmFormWidgets.generate_form_from_template (
1176 parent = parent,
1177 template = rx_template,
1178 edit = False
1179 )
1180 if rx is None:
1181 return False
1182
1183 # 3) print template
1184 return gmFormWidgets.act_on_generated_forms (
1185 parent = parent,
1186 forms = [rx],
1187 jobtype = 'prescription',
1188 #episode_name = u'administrative',
1189 episode_name = gmMedication.DEFAULT_MEDICATION_HISTORY_EPISODE,
1190 progress_note = _('generated prescription'),
1191 review_copy_as_normal = True
1192 )
1193
1194 #------------------------------------------------------------
1196
1197 dbcfg = gmCfg.cCfgSQL()
1198 rx_mode = dbcfg.get2 (
1199 option = 'horst_space.default_prescription_mode',
1200 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
1201 bias = 'user',
1202 default = 'form' # set to 'database' to access database
1203 )
1204
1205 if parent is None:
1206 parent = wx.GetApp().GetTopWindow()
1207
1208 if rx_mode == 'form':
1209 return print_prescription(parent = parent, emr = emr)
1210
1211 if rx_mode == 'database':
1212 drug_db = gmSubstanceMgmtWidgets.get_drug_database() #gmPerson.gmCurrentPatient() xxxxxxx ?
1213 if drug_db is None:
1214 return
1215 drug_db.reviewer = gmStaff.gmCurrentProvider()
1216 prescribed_drugs = drug_db.prescribe()
1217 update_substance_intake_list_from_prescription (
1218 parent = parent,
1219 prescribed_drugs = prescribed_drugs,
1220 emr = emr
1221 )
1222
1223 #------------------------------------------------------------
1224 -def update_substance_intake_list_from_prescription(parent=None, prescribed_drugs=None, emr=None):
1225
1226 if len(prescribed_drugs) == 0:
1227 return
1228
1229 curr_meds = [ i['pk_drug_product'] for i in emr.get_current_medications() if i['pk_drug_product'] is not None ]
1230 new_drugs = []
1231 for drug in prescribed_drugs:
1232 if drug['pk_drug_product'] not in curr_meds:
1233 new_drugs.append(drug)
1234
1235 if len(new_drugs) == 0:
1236 return
1237
1238 if parent is None:
1239 parent = wx.GetApp().GetTopWindow()
1240
1241 picker = gmListWidgets.cItemPickerDlg (
1242 parent,
1243 -1,
1244 msg = _(
1245 'These products have been prescribed but are not listed\n'
1246 'in the current medication list of this patient.\n'
1247 '\n'
1248 'Please select those you want added to the medication list.'
1249 )
1250 )
1251 picker.set_columns (
1252 columns = [_('Newly prescribed drugs')],
1253 columns_right = [_('Add to medication list')]
1254 )
1255 choices = [ ('%s %s (%s)' % (d['product'], d['l10n_preparation'], '; '.join(d['components']))) for d in new_drugs ]
1256 picker.set_choices (
1257 choices = choices,
1258 data = new_drugs
1259 )
1260 picker.ShowModal()
1261 drugs2add = picker.get_picks()
1262 picker.DestroyLater()
1263
1264 if drugs2add is None:
1265 return
1266
1267 if len(drugs2add) == 0:
1268 return
1269
1270 for drug in drugs2add:
1271 # only add first component since all other components get added by a trigger ...
1272 intake = emr.add_substance_intake(pk_component = drug['components'][0]['pk_component'])
1273 if intake is None:
1274 continue
1275 intake['intake_is_approved_of'] = True
1276 intake.save()
1277
1278 return
1279
1280 #------------------------------------------------------------
1282 """A grid class for displaying current substance intake.
1283
1284 - does NOT listen to the currently active patient
1285 - thereby it can display any patient at any time
1286 """
1288
1289 wx.grid.Grid.__init__(self, *args, **kwargs)
1290
1291 self.__patient = None
1292 self.__row_data = {}
1293 self.__prev_row = None
1294 self.__prev_tooltip_row = None
1295 self.__prev_cell_0 = None
1296 self.__grouping_mode = 'issue'
1297 self.__filter_show_unapproved = True
1298 self.__filter_show_inactive = True
1299
1300 self.__grouping2col_labels = {
1301 'issue': [
1302 _('Health issue'),
1303 _('Substance'),
1304 _('Strength'),
1305 _('Schedule'),
1306 _('Timeframe'),
1307 _('Product'),
1308 _('Advice')
1309 ],
1310 'product': [
1311 _('Product'),
1312 _('Schedule'),
1313 _('Substance'),
1314 _('Strength'),
1315 _('Timeframe'),
1316 _('Health issue'),
1317 _('Advice')
1318 ],
1319 'episode': [
1320 _('Episode'),
1321 _('Substance'),
1322 _('Strength'),
1323 _('Schedule'),
1324 _('Timeframe'),
1325 _('Product'),
1326 _('Advice')
1327 ],
1328 'start': [
1329 _('Episode'),
1330 _('Substance'),
1331 _('Strength'),
1332 _('Schedule'),
1333 _('Timeframe'),
1334 _('Product'),
1335 _('Advice')
1336 ],
1337 }
1338
1339 self.__grouping2order_by_clauses = {
1340 'issue': 'pk_health_issue NULLS FIRST, substance, started',
1341 'episode': 'pk_health_issue NULLS FIRST, episode, substance, started',
1342 'product': 'product NULLS LAST, substance, started',
1343 'start': 'started DESC, substance, episode'
1344 }
1345
1346 self.__init_ui()
1347 self.__register_events()
1348
1349 #------------------------------------------------------------
1350 # external API
1351 #------------------------------------------------------------
1353
1354 sel_block_top_left = self.GetSelectionBlockTopLeft()
1355 sel_block_bottom_right = self.GetSelectionBlockBottomRight()
1356 sel_cols = self.GetSelectedCols()
1357 sel_rows = self.GetSelectedRows()
1358
1359 selected_cells = []
1360
1361 # individually selected cells (ctrl-click)
1362 selected_cells += self.GetSelectedCells()
1363
1364 # selected rows
1365 selected_cells += list (
1366 (row, col)
1367 for row in sel_rows
1368 for col in range(self.GetNumberCols())
1369 )
1370
1371 # selected columns
1372 selected_cells += list (
1373 (row, col)
1374 for row in range(self.GetNumberRows())
1375 for col in sel_cols
1376 )
1377
1378 # selection blocks
1379 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()):
1380 selected_cells += [
1381 (row, col)
1382 for row in range(top_left[0], bottom_right[0] + 1)
1383 for col in range(top_left[1], bottom_right[1] + 1)
1384 ]
1385
1386 return set(selected_cells)
1387
1388 #------------------------------------------------------------
1390 rows = {}
1391
1392 for row, col in self.get_selected_cells():
1393 rows[row] = True
1394
1395 return rows.keys()
1396
1397 #------------------------------------------------------------
1399 return [ self.__row_data[row] for row in self.get_selected_rows() ]
1400
1401 #------------------------------------------------------------
1404
1405 #------------------------------------------------------------
1407
1408 self.empty_grid()
1409
1410 if self.__patient is None:
1411 return
1412
1413 emr = self.__patient.emr
1414 meds = emr.get_current_medications (
1415 order_by = self.__grouping2order_by_clauses[self.__grouping_mode],
1416 include_unapproved = self.__filter_show_unapproved,
1417 include_inactive = self.__filter_show_inactive
1418 )
1419 if not meds:
1420 return
1421
1422 self.BeginBatch()
1423
1424 # columns
1425 labels = self.__grouping2col_labels[self.__grouping_mode]
1426 if self.__filter_show_unapproved:
1427 self.AppendCols(numCols = len(labels) + 1)
1428 else:
1429 self.AppendCols(numCols = len(labels))
1430 for col_idx in range(len(labels)):
1431 self.SetColLabelValue(col_idx, labels[col_idx])
1432 if self.__filter_show_unapproved:
1433 #self.SetColLabelValue(len(labels), u'OK?')
1434 self.SetColLabelValue(len(labels), '')
1435 self.SetColSize(len(labels), 40)
1436
1437 self.AppendRows(numRows = len(meds))
1438
1439 # loop over data
1440 for row_idx in range(len(meds)):
1441 med = meds[row_idx]
1442 self.__row_data[row_idx] = med
1443
1444 if med['is_currently_active'] is True:
1445 atcs = []
1446 if med['atc_substance'] is not None:
1447 atcs.append(med['atc_substance'])
1448 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (med['substance'],))
1449 if allg not in [None, False]:
1450 attr = self.GetOrCreateCellAttr(row_idx, 0)
1451 if allg['type'] == 'allergy':
1452 attr.SetTextColour('red')
1453 else:
1454 #attr.SetTextColour('yellow') # too light
1455 #attr.SetTextColour('pink') # too light
1456 #attr.SetTextColour('dark orange') # slightly better
1457 attr.SetTextColour('magenta')
1458 self.SetRowAttr(row_idx, attr)
1459 else:
1460 attr = self.GetOrCreateCellAttr(row_idx, 0)
1461 attr.SetTextColour('grey')
1462 self.SetRowAttr(row_idx, attr)
1463
1464 if self.__grouping_mode in ['episode', 'start']:
1465 if med['pk_episode'] is None:
1466 self.__prev_cell_0 = None
1467 epi = gmTools.u_diameter
1468 else:
1469 if self.__prev_cell_0 == med['episode']:
1470 epi = ''
1471 else:
1472 self.__prev_cell_0 = med['episode']
1473 epi = gmTools.coalesce(med['episode'], '')
1474 self.SetCellValue(row_idx, 0, gmTools.wrap(text = epi, width = 40))
1475
1476 self.SetCellValue(row_idx, 1, med['substance'])
1477 self.SetCellValue(row_idx, 2, '%s %s' % (med['amount'], med['unit']))
1478 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], ''))
1479 self.SetCellValue(row_idx, 4, med.medically_formatted_start_end)
1480
1481 if med['pk_drug_product'] is None:
1482 product = '%s (%s)' % (gmTools.u_diameter, med['l10n_preparation'])
1483 else:
1484 if med['is_fake_product']:
1485 product = '%s (%s)' % (
1486 gmTools.coalesce(med['product'], '', _('%s <fake>')),
1487 med['l10n_preparation']
1488 )
1489 else:
1490 product = '%s (%s)' % (
1491 gmTools.coalesce(med['product'], ''),
1492 med['l10n_preparation']
1493 )
1494 self.SetCellValue(row_idx, 5, gmTools.wrap(text = product, width = 35))
1495
1496 elif self.__grouping_mode == 'issue':
1497 if med['pk_health_issue'] is None:
1498 self.__prev_cell_0 = None
1499 issue = '%s%s' % (
1500 gmTools.u_diameter,
1501 gmTools.coalesce(med['episode'], '', ' (%s)')
1502 )
1503 else:
1504 if self.__prev_cell_0 == med['health_issue']:
1505 issue = ''
1506 else:
1507 self.__prev_cell_0 = med['health_issue']
1508 issue = med['health_issue']
1509 self.SetCellValue(row_idx, 0, gmTools.wrap(text = issue, width = 40))
1510
1511 self.SetCellValue(row_idx, 1, med['substance'])
1512 self.SetCellValue(row_idx, 2, '%s %s' % (med['amount'], med['unit']))
1513 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], ''))
1514 self.SetCellValue(row_idx, 4, med.medically_formatted_start_end)
1515
1516 if med['pk_drug_product'] is None:
1517 product = '%s (%s)' % (gmTools.u_diameter, med['l10n_preparation'])
1518 else:
1519 if med['is_fake_product']:
1520 product = '%s (%s)' % (
1521 gmTools.coalesce(med['product'], '', _('%s <fake>')),
1522 med['l10n_preparation']
1523 )
1524 else:
1525 product = '%s (%s)' % (
1526 gmTools.coalesce(med['product'], ''),
1527 med['l10n_preparation']
1528 )
1529 self.SetCellValue(row_idx, 5, gmTools.wrap(text = product, width = 35))
1530
1531 elif self.__grouping_mode == 'product':
1532
1533 if med['pk_drug_product'] is None:
1534 self.__prev_cell_0 = None
1535 product = '%s (%s)' % (
1536 gmTools.u_diameter,
1537 med['l10n_preparation']
1538 )
1539 else:
1540 if self.__prev_cell_0 == med['product']:
1541 product = ''
1542 else:
1543 self.__prev_cell_0 = med['product']
1544 if med['is_fake_product']:
1545 product = '%s (%s)' % (
1546 gmTools.coalesce(med['product'], '', _('%s <fake>')),
1547 med['l10n_preparation']
1548 )
1549 else:
1550 product = '%s (%s)' % (
1551 gmTools.coalesce(med['product'], ''),
1552 med['l10n_preparation']
1553 )
1554 self.SetCellValue(row_idx, 0, gmTools.wrap(text = product, width = 35))
1555
1556 self.SetCellValue(row_idx, 1, gmTools.coalesce(med['schedule'], ''))
1557 self.SetCellValue(row_idx, 2, med['substance'])
1558 self.SetCellValue(row_idx, 3, '%s %s' % (med['amount'], med['unit']))
1559 self.SetCellValue(row_idx, 4, med.medically_formatted_start_end)
1560
1561 if med['pk_health_issue'] is None:
1562 issue = '%s%s' % (
1563 gmTools.u_diameter,
1564 gmTools.coalesce(med['episode'], '', ' (%s)')
1565 )
1566 else:
1567 issue = gmTools.coalesce(med['health_issue'], '')
1568 self.SetCellValue(row_idx, 5, gmTools.wrap(text = issue, width = 40))
1569
1570 else:
1571 raise ValueError('unknown grouping mode [%s]' % self.__grouping_mode)
1572
1573 if med['notes'] is not None:
1574 self.SetCellValue(row_idx, 6, gmTools.wrap(text = med['notes'], width = 50))
1575
1576 if self.__filter_show_unapproved:
1577 self.SetCellValue (
1578 row_idx,
1579 len(labels),
1580 gmTools.bool2subst(med['intake_is_approved_of'], gmTools.u_checkmark_thin, gmTools.u_frowning_face, '?')
1581 )
1582 font = self.GetCellFont(row_idx, len(labels))
1583 font.SetPointSize(font.GetPointSize() + 2)
1584 self.SetCellFont(row_idx, len(labels), font)
1585
1586 #self.SetCellAlignment(row, col, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
1587
1588 self.AutoSize()
1589 self.EndBatch()
1590 #------------------------------------------------------------
1592 self.BeginBatch()
1593 self.ClearGrid()
1594 # Windows cannot do "nothing", it rather decides to assert()
1595 # on thinking it is supposed to do nothing
1596 if self.GetNumberRows() > 0:
1597 self.DeleteRows(pos = 0, numRows = self.GetNumberRows())
1598 if self.GetNumberCols() > 0:
1599 self.DeleteCols(pos = 0, numCols = self.GetNumberCols())
1600 self.EndBatch()
1601 self.__row_data = {}
1602 self.__prev_cell_0 = None
1603
1604 #------------------------------------------------------------
1606
1607 if len(self.__row_data) == 0:
1608 return
1609
1610 sel_rows = self.get_selected_rows()
1611 if len(sel_rows) != 1:
1612 return
1613
1614 drug_db = gmSubstanceMgmtWidgets.get_drug_database(patient = self.__patient)
1615 if drug_db is None:
1616 return
1617
1618 intake = self.get_selected_data()[0] # just in case
1619 if intake['product'] is None:
1620 drug_db.show_info_on_substance(substance_intake = intake)
1621 else:
1622 drug_db.show_info_on_drug(substance_intake = intake)
1623
1624 #------------------------------------------------------------
1626 search_term = None
1627 if len(self.__row_data) > 0:
1628 sel_rows = self.get_selected_rows()
1629 if len(sel_rows) == 1:
1630 search_term = self.get_selected_data()[0]
1631 gmNetworkTools.open_url_in_browser(url = gmMedication.drug2renal_insufficiency_url(search_term = search_term))
1632
1633 #------------------------------------------------------------
1636
1637 #------------------------------------------------------------
1639 dbcfg = gmCfg.cCfgSQL()
1640 url = dbcfg.get2 (
1641 option = 'external.urls.report_ADR',
1642 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
1643 bias = 'user',
1644 default = gmMedication.URL_drug_adr_german_default
1645 )
1646 gmNetworkTools.open_url_in_browser(url = url)
1647
1648 #------------------------------------------------------------
1654 #------------------------------------------------------------
1656
1657 if len(self.__row_data) == 0:
1658 return
1659
1660 drug_db = gmSubstanceMgmtWidgets.get_drug_database(patient = self.__patient)
1661 if drug_db is None:
1662 return
1663
1664 if len(self.get_selected_rows()) > 1:
1665 drug_db.check_interactions(substance_intakes = self.get_selected_data())
1666 else:
1667 drug_db.check_interactions(substance_intakes = self.__row_data.values())
1668 #------------------------------------------------------------
1671 #------------------------------------------------------------
1673
1674 rows = self.get_selected_rows()
1675
1676 if len(rows) == 0:
1677 return
1678
1679 if len(rows) > 1:
1680 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit more than one substance at once.'), beep = True)
1681 return
1682
1683 subst = self.get_selected_data()[0]
1684 edit_intake_of_substance(parent = self, substance = subst)
1685
1686 #------------------------------------------------------------
1688
1689 rows = self.get_selected_rows()
1690
1691 if len(rows) == 0:
1692 return
1693
1694 if len(rows) > 1:
1695 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete more than one substance at once.'), beep = True)
1696 return
1697
1698 intake = self.get_selected_data()[0]
1699 delete_substance_intake(parent = self, intake = intake)
1700
1701 #------------------------------------------------------------
1703 rows = self.get_selected_rows()
1704
1705 if len(rows) == 0:
1706 return
1707
1708 if len(rows) > 1:
1709 gmDispatcher.send(signal = 'statustext', msg = _('Cannot create allergy from more than one substance at once.'), beep = True)
1710 return
1711
1712 return turn_substance_intake_into_allergy (
1713 parent = self,
1714 intake = self.get_selected_data()[0],
1715 emr = self.__patient.emr
1716 )
1717 #------------------------------------------------------------
1719 # there could be some filtering/user interaction going on here
1720 print_medication_list(parent = self)
1721 #------------------------------------------------------------
1723
1724 try:
1725 entry = self.__row_data[row]
1726 except KeyError:
1727 return ' '
1728
1729 emr = self.__patient.emr
1730 atcs = []
1731 if entry['atc_substance'] is not None:
1732 atcs.append(entry['atc_substance'])
1733 # if entry['atc_drug'] is not None:
1734 # atcs.append(entry['atc_drug'])
1735 # allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (entry['substance'],), product_name = entry['product'])
1736 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (entry['substance'],))
1737
1738 tt = _('Substance intake entry (%s, %s) [#%s] \n') % (
1739 gmTools.bool2subst (
1740 boolean = entry['is_currently_active'],
1741 true_return = gmTools.bool2subst (
1742 boolean = entry['seems_inactive'],
1743 true_return = _('active, needs check'),
1744 false_return = _('active'),
1745 none_return = _('assumed active')
1746 ),
1747 false_return = _('inactive')
1748 ),
1749 gmTools.bool2subst (
1750 boolean = entry['intake_is_approved_of'],
1751 true_return = _('approved'),
1752 false_return = _('unapproved')
1753 ),
1754 entry['pk_substance_intake']
1755 )
1756
1757 if allg not in [None, False]:
1758 certainty = gmTools.bool2subst(allg['definite'], _('definite'), _('suspected'))
1759 tt += '\n'
1760 tt += ' !! ---- Cave ---- !!\n'
1761 tt += ' %s (%s): %s (%s)\n' % (
1762 allg['l10n_type'],
1763 certainty,
1764 allg['descriptor'],
1765 gmTools.coalesce(allg['reaction'], '')[:40]
1766 )
1767 tt += '\n'
1768
1769 tt += ' ' + _('Substance: %s [#%s]\n') % (entry['substance'], entry['pk_substance'])
1770 tt += ' ' + _('Preparation: %s\n') % entry['l10n_preparation']
1771 tt += ' ' + _('Amount per dose: %s %s') % (entry['amount'], entry['unit'])
1772 tt += '\n'
1773 tt += gmTools.coalesce(entry['atc_substance'], '', _(' ATC (substance): %s\n'))
1774
1775 tt += '\n'
1776
1777 tt += gmTools.coalesce (
1778 entry['product'],
1779 '',
1780 _(' Product name: %%s [#%s]\n') % entry['pk_drug_product']
1781 )
1782 tt += gmTools.coalesce(entry['atc_drug'], '', _(' ATC (drug): %s\n'))
1783
1784 tt += '\n'
1785
1786 tt += gmTools.coalesce(entry['schedule'], '', _(' Regimen: %s\n'))
1787
1788 if entry['is_long_term']:
1789 duration = ' %s %s' % (gmTools.u_arrow2right, gmTools.u_infinity)
1790 else:
1791 if entry['duration'] is None:
1792 duration = ''
1793 else:
1794 duration = ' %s %s' % (gmTools.u_arrow2right, gmDateTime.format_interval(entry['duration'], gmDateTime.acc_days))
1795
1796 tt += _(' Started %s%s%s\n') % (
1797 entry.medically_formatted_start,
1798 duration,
1799 gmTools.bool2subst(entry['is_long_term'], _(' (long-term)'), _(' (short-term)'), '')
1800 )
1801
1802 if entry['discontinued'] is not None:
1803 tt += _(' Discontinued %s\n') % gmDateTime.pydt_strftime(entry['discontinued'], '%Y %b %d')
1804 tt += gmTools.coalesce(entry['discontinue_reason'], '', _(' Reason: %s\n'))
1805
1806 tt += '\n'
1807
1808 tt += gmTools.coalesce(entry['aim'], '', _(' Aim: %s\n'))
1809 tt += gmTools.coalesce(entry['episode'], '', _(' Episode: %s\n'))
1810 tt += gmTools.coalesce(entry['health_issue'], '', _(' Health issue: %s\n'))
1811 tt += gmTools.coalesce(entry['notes'], '', _(' Advice: %s\n'))
1812
1813 tt += '\n'
1814
1815 tt += _('Revision: #%(row_ver)s, %(mod_when)s by %(mod_by)s.') % ({
1816 'row_ver': entry['row_version'],
1817 'mod_when': gmDateTime.pydt_strftime(entry['modified_when'], '%Y %b %d %H:%M:%S'),
1818 'mod_by': entry['modified_by']
1819 })
1820
1821 return tt
1822
1823 #------------------------------------------------------------
1824 # internal helpers
1825 #------------------------------------------------------------
1827 self.CreateGrid(0, 1)
1828 self.EnableEditing(0)
1829 self.EnableDragGridSize(1)
1830 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows)
1831
1832 self.SetColLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTER)
1833
1834 self.SetRowLabelSize(0)
1835 self.SetRowLabelAlignment(horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
1836
1837 #------------------------------------------------------------
1838 # properties
1839 #------------------------------------------------------------
1842
1846
1847 patient = property(_get_patient, _set_patient)
1848 #------------------------------------------------------------
1851
1855
1856 grouping_mode = property(_get_grouping_mode, _set_grouping_mode)
1857 #------------------------------------------------------------
1860
1864
1865 filter_show_unapproved = property(_get_filter_show_unapproved, _set_filter_show_unapproved)
1866 #------------------------------------------------------------
1869
1873
1874 filter_show_inactive = property(_get_filter_show_inactive, _set_filter_show_inactive)
1875 #------------------------------------------------------------
1876 # event handling
1877 #------------------------------------------------------------
1879 # dynamic tooltips: GridWindow, GridRowLabelWindow, GridColLabelWindow, GridCornerLabelWindow
1880 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells)
1881 #self.GetGridRowLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_row_labels)
1882 #self.GetGridColLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_col_labels)
1883
1884 # editing cells
1885 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
1886 #------------------------------------------------------------
1888 """Calculate where the mouse is and set the tooltip dynamically."""
1889
1890 # Use CalcUnscrolledPosition() to get the mouse position within the
1891 # entire grid including what's offscreen
1892 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
1893
1894 # use this logic to prevent tooltips outside the actual cells
1895 # apply to GetRowSize, too
1896 # tot = 0
1897 # for col in range(self.NumberCols):
1898 # tot += self.GetColSize(col)
1899 # if xpos <= tot:
1900 # self.tool_tip.Tip = 'Tool tip for Column %s' % (
1901 # self.GetColLabelValue(col))
1902 # break
1903 # else: # mouse is in label area beyond the right-most column
1904 # self.tool_tip.Tip = ''
1905
1906 row, col = self.XYToCell(x, y)
1907
1908 if row == self.__prev_tooltip_row:
1909 return
1910
1911 self.__prev_tooltip_row = row
1912
1913 try:
1914 evt.GetEventObject().SetToolTip(self.get_row_tooltip(row = row))
1915 except KeyError:
1916 pass
1917 #------------------------------------------------------------
1919 row = evt.GetRow()
1920 data = self.__row_data[row]
1921 edit_intake_of_substance(parent = self, substance = data)
1922
1923 #============================================================
1925
1926 panels = gmPathLab.get_test_panels(order_by = 'description')
1927 gmCfgWidgets.configure_string_from_list_option (
1928 parent = parent,
1929 message = _(
1930 '\n'
1931 'Select the measurements panel to show in the medications plugin.'
1932 '\n'
1933 ),
1934 option = 'horstspace.medications_plugin.lab_panel',
1935 bias = 'user',
1936 default_value = None,
1937 choices = [ '%s%s' % (p['description'], gmTools.coalesce(p['comment'], '', ' (%s)')) for p in panels ],
1938 columns = [_('Measurements panel')],
1939 data = [ p['pk_test_panel'] for p in panels ],
1940 caption = _('Configuring medications plugin measurements panel')
1941 )
1942
1943 #============================================================
1945
1946 def is_valid(value):
1947 value = value.strip()
1948 if value == '':
1949 return True, gmMedication.URL_drug_adr_german_default
1950 try:
1951 urllib.request.urlopen(value)
1952 return True, value
1953 except Exception:
1954 return True, value
1955
1956 gmCfgWidgets.configure_string_option (
1957 message = _(
1958 'GNUmed will use this URL to access a website which lets\n'
1959 'you report an adverse drug reaction (ADR).\n'
1960 '\n'
1961 'If you leave this empty it will fall back\n'
1962 'to an URL for reporting ADRs in Germany.'
1963 ),
1964 option = 'external.urls.report_ADR',
1965 bias = 'user',
1966 default_value = gmMedication.URL_drug_adr_german_default,
1967 validator = is_valid
1968 )
1969
1970 #============================================================
1971 from Gnumed.wxGladeWidgets import wxgCurrentSubstancesPnl
1972
1973 -class cCurrentSubstancesPnl(wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl, gmRegetMixin.cRegetOnPaintMixin):
1974
1975 """Panel holding a grid with current substances. Used as notebook page."""
1976
1978
1979 wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl.__init__(self, *args, **kwargs)
1980 gmRegetMixin.cRegetOnPaintMixin.__init__(self)
1981
1982 self.__grouping_choice_labels = [
1983 {'label': _('Health issue'), 'data': 'issue'} ,
1984 {'label': _('Drug product'), 'data': 'product'},
1985 {'label': _('Episode'), 'data': 'episode'},
1986 {'label': _('Started'), 'data': 'start'}
1987 ]
1988 self.__lab_panel = None
1989
1990 self.__init_ui()
1991 self.__register_interests()
1992
1993 #-----------------------------------------------------
1995 self._CHCE_grouping.Clear()
1996 for option in self.__grouping_choice_labels:
1997 self._CHCE_grouping.Append(option['label'], option['data'])
1998 self._CHCE_grouping.SetSelection(0)
1999
2000 tt = self._BTN_heart.GetToolTipText()
2001 try:
2002 self._BTN_heart.SetToolTip(tt % gmMedication.URL_long_qt)
2003 except TypeError:
2004 _log.exception('translation error: %s', tt)
2005
2006 tt = self._BTN_kidneys.GetToolTipText()
2007 try:
2008 self._BTN_kidneys.SetToolTip(tt % gmMedication.URL_renal_insufficiency)
2009 except TypeError:
2010 _log.exception('translation error: %s', tt)
2011
2012 #-----------------------------------------------------
2013 # reget-on-paint mixin API
2014 #-----------------------------------------------------
2016 """Populate cells with data from model."""
2017 pat = gmPerson.gmCurrentPatient()
2018 if pat.connected:
2019 self._grid_substances.patient = pat
2020 self.__refresh_gfr(pat)
2021 self.__refresh_lab(patient = pat)
2022 else:
2023 self._grid_substances.patient = None
2024 self.__clear_gfr()
2025 self.__refresh_lab(patient = None)
2026 return True
2027
2028 #--------------------------------------------------------
2030
2031 self._GSZR_lab.Clear(True) # also delete child windows
2032 self._HLINE_lab.Hide()
2033
2034 if patient is None:
2035 self.Layout()
2036 return
2037
2038 emr = patient.emr
2039 most_recent_results = {}
2040
2041 # get most recent results for "LOINCs to monitor"
2042 loincs2monitor = set()
2043 loincs2monitor_data = {}
2044 loinc_max_age = {}
2045 loinc_max_age_str = {}
2046 for intake in self._grid_substances.get_row_data():
2047 for l in intake['loincs']:
2048 loincs2monitor.add(l['loinc'])
2049 loincs2monitor_data[l['loinc']] = {
2050 'substance': intake['substance'],
2051 'comment': l['comment']
2052 }
2053 if l['max_age_in_secs'] is not None:
2054 try:
2055 if loinc_max_age[l['loinc']] > l['max_age_in_secs']:
2056 loinc_max_age[l['loinc']] = l['max_age_in_secs']
2057 loinc_max_age_str[l['loinc']] = l['max_age_str']
2058 except KeyError:
2059 loinc_max_age[l['loinc']] = l['max_age_in_secs']
2060 loinc_max_age_str[l['loinc']] = l['max_age_str']
2061 loincs2monitor_missing = loincs2monitor.copy()
2062 for loinc in loincs2monitor:
2063 results = emr.get_most_recent_results_in_loinc_group(loincs = [loinc], max_no_of_results = 1)
2064 if len(results) == 0:
2065 continue
2066 loincs2monitor_missing.remove(loinc)
2067 # make unique
2068 result = results[0]
2069 most_recent_results[result['pk_test_result']] = result
2070
2071 # get most recent results for "general medication monitoring lab panel"
2072 if self.__lab_panel is not None:
2073 for result in self.__lab_panel.get_most_recent_results (
2074 pk_patient = patient.ID,
2075 order_by = 'unified_abbrev',
2076 group_by_meta_type = True
2077 ):
2078 try: loincs2monitor_missing.remove(result['loinc_tt'])
2079 except KeyError: pass
2080 try: loincs2monitor_missing.remove(result['loinc_meta'])
2081 except KeyError: pass
2082 # make unique
2083 most_recent_results[result['pk_test_result']] = result
2084
2085 # those need special treatment
2086 gfrs = emr.get_most_recent_results_in_loinc_group(loincs = gmLOINC.LOINC_gfr_quantity, max_no_of_results = 1)
2087 gfr = gfrs[0] if len(gfrs) > 0 else None
2088 creas = emr.get_most_recent_results_in_loinc_group(loincs = gmLOINC.LOINC_creatinine_quantity, max_no_of_results = 1)
2089 crea = creas[0] if len(creas) > 0 else None
2090 edc = emr.EDC
2091
2092 # display EDC
2093 if edc is not None:
2094 if emr.EDC_is_fishy:
2095 lbl = wx.StaticText(self, -1, _('EDC (!?!):'))
2096 val = wx.StaticText(self, -1, gmDateTime.pydt_strftime(edc, format = '%Y %b %d'))
2097 else:
2098 lbl = wx.StaticText(self, -1, _('EDC:'))
2099 val = wx.StaticText(self, -1, gmDateTime.pydt_strftime(edc, format = '%Y %b %d'))
2100 lbl.SetForegroundColour('blue')
2101 szr = wx.BoxSizer(wx.HORIZONTAL)
2102 szr.Add(lbl, 0, wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3)
2103 szr.Add(val, 1, wx.ALIGN_CENTER_VERTICAL)
2104 self._GSZR_lab.Add(szr)
2105
2106 # decide which among Crea or GFR to show
2107 if crea is None:
2108 gfr_3_months_older_than_crea = False
2109 if gfr is not None:
2110 most_recent_results = [gfr] + most_recent_results
2111 elif gfr is None:
2112 gfr_3_months_older_than_crea = True
2113 else:
2114 three_months = pydt.timedelta(weeks = 14)
2115 gfr_3_months_older_than_crea = (crea['clin_when'] - gfr['clin_when']) > three_months
2116 if not gfr_3_months_older_than_crea:
2117 most_recent_results = [gfr] + most_recent_results
2118
2119 # if GFR not found in most_recent_results or old, then calculate
2120 now = gmDateTime.pydt_now_here()
2121 if gfr_3_months_older_than_crea:
2122 calc = gmClinicalCalculator.cClinicalCalculator()
2123 calc.patient = patient
2124 gfr = calc.eGFR
2125 if gfr.numeric_value is None:
2126 gfr_msg = '?'
2127 else:
2128 gfr_msg = _('%.1f (%s ago)') % (
2129 gfr.numeric_value,
2130 gmDateTime.format_interval_medically(now - gfr.date_valid)
2131 )
2132 lbl = wx.StaticText(self, -1, _('eGFR:'))
2133 lbl.SetForegroundColour('blue')
2134 val = wx.StaticText(self, -1, gfr_msg)
2135 tts = []
2136 for egfr in calc.eGFRs:
2137 if egfr.numeric_value is None:
2138 continue
2139 tts.append(egfr.format (
2140 left_margin = 0,
2141 width = 50,
2142 eol = '\n',
2143 with_formula = False,
2144 with_warnings = True,
2145 with_variables = False,
2146 with_sub_results = False,
2147 return_list = False
2148 ))
2149 val.SetToolTip('\n'.join(tts))
2150 szr = wx.BoxSizer(wx.HORIZONTAL)
2151 szr.Add(lbl, 0, wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3)
2152 szr.Add(val, 1, wx.ALIGN_CENTER_VERTICAL)
2153 self._GSZR_lab.Add(szr)
2154
2155 # eventually add most-recent results from monitoring panel and substances monitoring
2156 for pk_result in most_recent_results:
2157 result = most_recent_results[pk_result]
2158 # test type
2159 lbl = wx.StaticText(self, -1, '%s:' % result['unified_abbrev'])
2160 lbl.SetForegroundColour('blue')
2161 # calculate test result
2162 indicate_attention = False
2163 if result.is_considered_abnormal:
2164 indicate_attention = True
2165 # calculate tooltip data
2166 max_age = None
2167 try:
2168 max_age = loinc_max_age[result['loinc_tt']]
2169 max_age_str = loinc_max_age_str[result['loinc_tt']]
2170 except KeyError:
2171 try:
2172 max_age = loinc_max_age[result['loinc_meta']]
2173 max_age_str = loinc_max_age_str[result['loinc_meta']]
2174 except KeyError:
2175 pass
2176 subst2monitor = None
2177 try:
2178 subst2monitor = loincs2monitor_data[result['loinc_tt']]['substance']
2179 except KeyError:
2180 try:
2181 subst2monitor = loincs2monitor_data[result['loinc_meta']]['substance']
2182 except KeyError:
2183 pass
2184 monitor_comment = None
2185 try:
2186 monitor_comment = loincs2monitor_data[result['loinc_tt']]['comment']
2187 except KeyError:
2188 try:
2189 monitor_comment = loincs2monitor_data[result['loinc_meta']]['comment']
2190 except KeyError:
2191 pass
2192 result_age = now - result['clin_when']
2193 unhappy_reasons = []
2194 if result.is_considered_abnormal:
2195 indicator = result.formatted_abnormality_indicator
2196 if indicator == '':
2197 unhappy_reasons.append(_(' - abnormal'))
2198 else:
2199 unhappy_reasons.append(_(' - abnormal: %s') % indicator)
2200 if max_age is not None:
2201 if result_age.total_seconds() > max_age:
2202 unhappy_reasons.append(_(' - too old: %s ago (max: %s)') % (
2203 gmDateTime.format_interval_medically(result_age),
2204 max_age_str
2205 ))
2206 # generate tooltip
2207 tt = [_('Most recent: %s ago') % gmDateTime.format_interval_medically(result_age)]
2208 if subst2monitor is not None:
2209 tt.append(_('Why monitor: %s') % subst2monitor)
2210 if monitor_comment is not None:
2211 tt.append(' %s' % monitor_comment)
2212 if len(unhappy_reasons) > 0:
2213 indicate_attention = True
2214 tt.append(_('Problems:'))
2215 tt.extend(unhappy_reasons)
2216 tt = '%s\n\n%s' % (
2217 '\n'.join(tt),
2218 result.format()
2219 )
2220 # set test result and tooltip
2221 val = wx.StaticText(self, -1, '%s%s%s' % (
2222 result['unified_val'],
2223 gmTools.coalesce(result['val_unit'], '', ' %s'),
2224 gmTools.bool2subst(indicate_attention, gmTools.u_frowning_face, '', '')
2225 ))
2226 val.SetToolTip(tt)
2227 if result.is_considered_abnormal:
2228 val.SetForegroundColour('red')
2229 szr = wx.BoxSizer(wx.HORIZONTAL)
2230 szr.Add(lbl, 0, wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3)
2231 szr.Add(val, 1, wx.ALIGN_CENTER_VERTICAL)
2232 self._GSZR_lab.Add(szr)
2233
2234 # hint at missing, but required results (set to be
2235 # monitored under intakes based on LOINCs):
2236 for loinc in loincs2monitor_missing:
2237 #szr.Add(lbl, 0, wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3)
2238 loinc_data = gmLOINC.loinc2data(loinc)
2239 if loinc_data is None:
2240 loinc_str = loinc
2241 else:
2242 loinc_str = loinc_data['term']
2243 val = wx.StaticText(self, -1, '%s!' % loinc_str)
2244 tt = [
2245 _('No test result for: %s (%s)') % (loinc_str, loinc),
2246 '',
2247 _('Why monitor: %s' % loincs2monitor_data[loinc]['substance'])
2248 ]
2249 try:
2250 tt.append(' %s' % loincs2monitor_data[loinc]['comment'])
2251 except KeyError:
2252 pass
2253 val.SetToolTip('\n'.join(tt))
2254 val.SetForegroundColour('orange')
2255 szr = wx.BoxSizer(wx.HORIZONTAL)
2256 szr.Add(val, 1, wx.ALIGN_CENTER_VERTICAL)
2257 self._GSZR_lab.Add(szr)
2258
2259 self._HLINE_lab.Show()
2260 self.Layout()
2261
2262 #--------------------------------------------------------
2264 gfrs = patient.emr.get_most_recent_results_in_loinc_group(loincs = gmLOINC.LOINC_gfr_quantity, max_no_of_results = 1)
2265 if len(gfrs) == 0:
2266 calc = gmClinicalCalculator.cClinicalCalculator()
2267 calc.patient = patient
2268 gfr = calc.eGFR
2269 if gfr.numeric_value is None:
2270 msg = _('GFR: ?')
2271 tt = gfr.message
2272 else:
2273 msg = _('eGFR: %.1f (%s)') % (
2274 gfr.numeric_value,
2275 gmDateTime.pydt_strftime (
2276 gfr.date_valid,
2277 format = '%b %Y'
2278 )
2279 )
2280 egfrs = calc.eGFRs
2281 tts = []
2282 for egfr in egfrs:
2283 if egfr.numeric_value is None:
2284 continue
2285 tts.append(egfr.format (
2286 left_margin = 0,
2287 width = 50,
2288 eol = '\n',
2289 with_formula = False,
2290 with_warnings = True,
2291 with_variables = False,
2292 with_sub_results = False,
2293 return_list = False
2294 ))
2295 tt = '\n'.join(tts)
2296 else:
2297 gfr = gfrs[0]
2298 msg = '%s: %s %s (%s)\n' % (
2299 gfr['unified_abbrev'],
2300 gfr['unified_val'],
2301 gmTools.coalesce(gfr['abnormality_indicator'], '', ' (%s)'),
2302 gmDateTime.pydt_strftime (
2303 gfr['clin_when'],
2304 format = '%b %Y'
2305 )
2306 )
2307 tt = _('GFR reported by path lab')
2308
2309 self._LBL_gfr.SetLabel(msg)
2310 self._LBL_gfr.SetToolTip(tt)
2311 self._LBL_gfr.Refresh()
2312 self.Layout()
2313
2314 #--------------------------------------------------------
2319
2320 #--------------------------------------------------------
2321 # event handling
2322 #--------------------------------------------------------
2324 gmDispatcher.connect(signal = 'pre_patient_unselection', receiver = self._on_pre_patient_unselection)
2325 gmDispatcher.connect(signal = 'post_patient_selection', receiver = self._on_post_patient_selection)
2326 gmDispatcher.connect(signal = 'clin.substance_intake_mod_db', receiver = self._schedule_data_reget)
2327 gmDispatcher.connect(signal = 'clin.test_result_mod_db', receiver = self._on_test_result_mod)
2328
2329 #--------------------------------------------------------
2332
2333 #--------------------------------------------------------
2335 dbcfg = gmCfg.cCfgSQL()
2336 pk_panel = dbcfg.get2 (
2337 option = 'horstspace.medications_plugin.lab_panel',
2338 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
2339 bias = 'user'
2340 )
2341 if pk_panel is None:
2342 self.__lab_panel = None
2343 else:
2344 self.__lab_panel = gmPathLab.cTestPanel(aPK_obj = pk_panel)
2345 self._grid_substances.patient = None
2346 self.__refresh_lab(patient = None)
2347 #--------------------------------------------------------
2350 #--------------------------------------------------------
2353 #--------------------------------------------------------
2356 #--------------------------------------------------------
2359 #--------------------------------------------------------
2362 #--------------------------------------------------------
2365 #--------------------------------------------------------
2367 event.Skip()
2368 selected_item_idx = self._CHCE_grouping.GetSelection()
2369 if selected_item_idx is wx.NOT_FOUND:
2370 return
2371 self._grid_substances.grouping_mode = self._CHCE_grouping.GetClientData(selected_item_idx)
2372 #--------------------------------------------------------
2375 #--------------------------------------------------------
2378 #--------------------------------------------------------
2381 #--------------------------------------------------------
2384 #--------------------------------------------------------
2387 #--------------------------------------------------------
2390 #--------------------------------------------------------
2393 #--------------------------------------------------------
2396
2397 #============================================================
2398 # main
2399 #------------------------------------------------------------
2400 if __name__ == '__main__':
2401
2402 if len(sys.argv) < 2:
2403 sys.exit()
2404
2405 if sys.argv[1] != 'test':
2406 sys.exit()
2407
2408 from Gnumed.business import gmPersonSearch
2409
2410 pat = gmPersonSearch.ask_for_patient()
2411 if pat is None:
2412 sys.exit()
2413 gmPerson.set_active_patient(patient = pat)
2414
2415 #----------------------------------------
2416 app = wx.PyWidgetTester(size = (600, 300))
2417 app.SetWidget(cSubstanceIntakeObjectPhraseWheel, -1)
2418 app.MainLoop()
2419 #manage_substance_intakes()
2420
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Sat Feb 29 02:55:27 2020 | http://epydoc.sourceforge.net |