1 """GNUmed patient overview widgets.
2
3 copyright: authors
4 """
5
6 __author__ = "K.Hilbert"
7 __license__ = "GPL v2 or later (details at http://www.gnu.org)"
8
9 import logging, sys
10
11
12 import wx
13
14
15 if __name__ == '__main__':
16 sys.path.insert(0, '../../')
17 from Gnumed.pycommon import gmTools
18 from Gnumed.pycommon import gmDispatcher
19 from Gnumed.pycommon import gmDateTime
20 from Gnumed.pycommon import gmNetworkTools
21
22 from Gnumed.business import gmPerson
23 from Gnumed.business import gmStaff
24 from Gnumed.business import gmDemographicRecord
25 from Gnumed.business import gmEMRStructItems
26 from Gnumed.business import gmFamilyHistory
27 from Gnumed.business import gmVaccination
28 from Gnumed.business import gmDocuments
29 from Gnumed.business import gmProviderInbox
30
31 from Gnumed.wxpython import gmRegetMixin
32 from Gnumed.wxpython import gmDemographicsWidgets
33 from Gnumed.wxpython import gmContactWidgets
34 from Gnumed.wxpython import gmMedicationWidgets
35 from Gnumed.wxpython import gmEditArea
36 from Gnumed.wxpython import gmEMRStructWidgets
37 from Gnumed.wxpython import gmFamilyHistoryWidgets
38 from Gnumed.wxpython import gmVaccWidgets
39 from Gnumed.wxpython import gmDocumentWidgets
40 from Gnumed.wxpython import gmGuiHelpers
41
42
43 _log = logging.getLogger('gm.patient')
44
45 from Gnumed.wxGladeWidgets import wxgPatientOverviewPnl
46
47 -class cPatientOverviewPnl(wxgPatientOverviewPnl.wxgPatientOverviewPnl, gmRegetMixin.cRegetOnPaintMixin):
48
55
56
57
97
99 self._LCTRL_identity.set_string_items()
100 self._LCTRL_contacts.set_string_items()
101 self._LCTRL_encounters.set_string_items()
102 self._PRW_encounter_range.SetText(value = u'', data = None)
103
104 self._LCTRL_problems.set_string_items()
105 self._LCTRL_meds.set_string_items()
106 self._LCTRL_history.set_string_items()
107
108 self._LCTRL_inbox.set_string_items()
109 self._LCTRL_results.set_string_items()
110 self._LCTRL_documents.set_string_items()
111
112
113
114
115
116
117
119
120 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
121 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
122
123
124 gmDispatcher.connect(signal = u'identity_mod_db', receiver = self._on_post_patient_selection)
125 gmDispatcher.connect(signal = u'name_mod_db', receiver = self._on_post_patient_selection)
126 gmDispatcher.connect(signal = u'comm_channel_mod_db', receiver = self._on_post_patient_selection)
127 gmDispatcher.connect(signal = u'job_mod_db', receiver = self._on_post_patient_selection)
128
129
130
131
132
133 gmDispatcher.connect(signal = u'episode_mod_db', receiver = self._on_episode_issue_mod_db)
134 gmDispatcher.connect(signal = u'health_issue_mod_db', receiver = self._on_episode_issue_mod_db)
135
136 gmDispatcher.connect(signal = u'substance_intake_mod_db', receiver = self._on_post_patient_selection)
137
138 gmDispatcher.connect(signal = u'hospital_stay_mod_db', receiver = self._on_post_patient_selection)
139 gmDispatcher.connect(signal = u'family_history_mod_db', receiver = self._on_post_patient_selection)
140 gmDispatcher.connect(signal = u'procedure_mod_db', receiver = self._on_post_patient_selection)
141 gmDispatcher.connect(signal = u'vacc_mod_db', receiver = self._on_post_patient_selection)
142
143 gmDispatcher.connect(signal = u'message_inbox_mod_db', receiver = self._on_post_patient_selection)
144 gmDispatcher.connect(signal = u'test_result_mod_db', receiver = self._on_post_patient_selection)
145 gmDispatcher.connect(signal = u'reviewed_test_results_mod_db', receiver = self._on_post_patient_selection)
146 gmDispatcher.connect(signal = u'doc_mod_db', receiver = self._on_post_patient_selection)
147
148
149
150
151
152 self._PRW_encounter_range.add_callback_on_selection(callback = self._on_encounter_range_selected)
153
156
158
159
160
161 wx.CallAfter(self.__reset_ui_content)
162
164 wx.CallAfter(self._schedule_data_reget)
165
167 wx.CallAfter(self._schedule_data_reget)
168
169
170
172 pat = gmPerson.gmCurrentPatient()
173 if not pat.connected:
174 self.__reset_ui_content()
175 return True
176
177 self.__refresh_identity(patient = pat)
178 self.__refresh_contacts(patient = pat)
179 self.__refresh_encounters(patient = pat)
180
181 self.__refresh_problems(patient = pat)
182 self.__refresh_meds(patient = pat)
183 self.__refresh_history(patient = pat)
184
185 self.__refresh_inbox(patient = pat)
186 self.__refresh_results(patient = pat)
187 self.__refresh_documents(patient = pat)
188
189 return True
190
191
192
194 list_items = []
195 list_data = []
196
197 emr = patient.get_emr()
198 most_recent = emr.get_most_recent_results(no_of_results = 1)
199 if most_recent is None:
200 self._LCTRL_results.set_string_items(items = [])
201 self._LCTRL_results.set_data(data = [])
202 return
203
204 list_items.append(_('Latest: %s ago (%s %s %s %s%s)') % (
205 gmDateTime.format_interval_medically(gmDateTime.pydt_now_here() - most_recent['clin_when']),
206 most_recent['unified_abbrev'],
207 most_recent['unified_val'],
208 most_recent['val_unit'],
209 gmTools.coalesce(most_recent['abnormality_indicator'], u'', u' %s'),
210 gmTools.bool2subst(most_recent['reviewed'], u'', u' %s' % gmTools.u_writing_hand)
211 ))
212 list_data.append(most_recent)
213 most_recent_needs_red = False
214 if most_recent['is_technically_abnormal'] is True:
215 if most_recent['is_clinically_relevant']:
216 most_recent_needs_red = True
217 else:
218 if most_recent['abnormality_indicator'] not in [None, u'']:
219 most_recent_needs_red = True
220
221 unsigned = emr.get_unsigned_results(order_by = u"(trim(coalesce(abnormality_indicator), '') <> '') DESC NULLS LAST, unified_abbrev")
222 no_of_reds = 0
223 for result in unsigned:
224 if result['pk_test_result'] == most_recent['pk_test_result']:
225 continue
226 if result['abnormality_indicator'] is not None:
227 if result['abnormality_indicator'].strip() != u'':
228 no_of_reds += 1
229 list_items.append(_('%s %s %s %s (%s ago, %s)') % (
230 result['unified_abbrev'],
231 result['unified_val'],
232 result['val_unit'],
233 gmTools.coalesce(result['abnormality_indicator'], u'', u' %s'),
234 gmDateTime.format_interval_medically(gmDateTime.pydt_now_here() - result['clin_when']),
235 gmTools.u_writing_hand
236 ))
237 list_data.append(result)
238
239 self._LCTRL_results.set_string_items(items = list_items)
240 self._LCTRL_results.set_data(data = list_data)
241
242 if most_recent_needs_red:
243 self._LCTRL_results.SetItemTextColour(0, wx.NamedColour('RED'))
244 if no_of_reds > 0:
245 for idx in range(1, no_of_reds + 1):
246 self._LCTRL_results.SetItemTextColour(idx, wx.NamedColour('RED'))
247
250
252
253
254
255
256
257
258
259 wx.CallAfter(gmDispatcher.send, signal = 'display_widget', name = 'gmMeasurementsGridPlugin')
260 return
261
262
264 list_items = []
265 list_data = []
266
267 overdue_messages = patient.overdue_messages
268 no_of_overdues = len(overdue_messages)
269 for msg in overdue_messages:
270 list_items.append(_('overdue %s: %s') % (
271 gmDateTime.format_interval_medically(msg['interval_due']),
272 gmTools.coalesce(msg['comment'], u'?')
273 ))
274 list_data.append(msg)
275
276 for msg in patient.get_messages(order_by = u'due_date NULLS LAST, importance DESC, received_when DESC'):
277
278 if msg['is_overdue']:
279 continue
280
281 if msg['is_expired']:
282 continue
283 if msg['due_date'] is None:
284 label = u'%s%s' % (
285 msg['l10n_type'],
286 gmTools.coalesce(msg['comment'], u'', u': %s')
287 )
288 else:
289 label = _('due in %s%s') % (
290 gmDateTime.format_interval_medically(msg['interval_due']),
291 gmTools.coalesce(msg['comment'], u'', u': %s')
292 )
293
294 list_items.append(label)
295 list_data.append(msg)
296
297 for hint in patient.dynamic_hints:
298 list_items.append(hint['title'])
299 list_data.append(hint)
300
301 self._LCTRL_inbox.set_string_items(items = list_items)
302 self._LCTRL_inbox.set_data(data = list_data)
303
304 if no_of_overdues > 0:
305 for idx in range(no_of_overdues):
306 self._LCTRL_inbox.SetItemTextColour(idx, wx.NamedColour('RED'))
307
321
353
354
356 doc_folder = patient.get_document_folder()
357
358 list_items = []
359 list_data = []
360
361 docs = doc_folder.get_unsigned_documents()
362 no_of_unsigned = len(docs)
363 for doc in docs:
364 list_items.append(u'%s %s (%s)' % (
365 gmDateTime.pydt_strftime(doc['clin_when'], format = '%m/%Y', accuracy = gmDateTime.acc_months),
366 doc['l10n_type'],
367 gmTools.u_writing_hand
368 ))
369 list_data.append(doc)
370
371 docs = doc_folder.get_documents(order_by = u'ORDER BY clin_when DESC', exclude_unsigned = True)
372 for doc in docs[:5]:
373 list_items.append(u'%s %s' % (
374 gmDateTime.pydt_strftime(doc['clin_when'], format = '%m/%Y', accuracy = gmDateTime.acc_months),
375 doc['l10n_type']
376 ))
377 list_data.append(doc)
378 if len(docs) > 5:
379 list_items.append(_('%s %s more not shown %s') % (
380 gmTools.u_ellipsis,
381 len(docs) - 5,
382 gmTools.u_ellipsis
383 ))
384 list_data.append(u'')
385
386 self._LCTRL_documents.set_string_items(items = list_items)
387 self._LCTRL_documents.set_data(data = list_data)
388
389 if no_of_unsigned > 0:
390 for idx in range(no_of_unsigned):
391 self._LCTRL_documents.SetItemTextColour(idx, wx.NamedColour('RED'))
392
400
416
417
419
420 cover_period = self._PRW_encounter_range.GetData()
421 if cover_period is None:
422 if self._PRW_encounter_range.GetValue().strip() != u'':
423 return
424
425 emr = patient.get_emr()
426
427 list_items = []
428 list_data = []
429
430 is_waiting = False
431 wlist = patient.get_waiting_list_entry()
432 if len(wlist) > 0:
433 is_waiting = True
434 list_items.append(_('Currently %s entries in waiting list') % len(wlist))
435 tt = []
436 for w in wlist:
437 tt.append(u'%s %s%s%s' % (
438 gmTools.u_triangular_bullet,
439 gmDateTime.format_interval_medically(w['waiting_time']),
440 gmTools.coalesce(w['waiting_zone'], u'', u' in "%s"'),
441 gmTools.coalesce(w['comment'], u'', u': %s')
442 ))
443 if len(tt) > 0:
444 tt = u'\n'.join(tt)
445 else:
446 tt = None
447 list_data.append({'wlist': tt})
448
449 first = emr.get_first_encounter()
450 if first is not None:
451 list_items.append (
452 _('first: %s, %s') % (
453 gmDateTime.pydt_strftime (
454 first['started'],
455 format = '%Y %b %d',
456 accuracy = gmDateTime.acc_days
457 ),
458 first['l10n_type']
459 )
460 )
461 list_data.append(first)
462
463 last = emr.get_last_but_one_encounter()
464 if last is not None:
465 list_items.append (
466 _('last: %s, %s') % (
467 gmDateTime.pydt_strftime (
468 last['started'],
469 format = '%Y %b %d',
470 accuracy = gmDateTime.acc_days
471 ),
472 last['l10n_type']
473 )
474 )
475 list_data.append(last)
476
477 if cover_period is not None:
478 item = _('Last %s:') % self._PRW_encounter_range.GetValue().strip()
479 list_items.append(item)
480 list_data.append(_('Statistics cover period'))
481
482 encs = emr.get_encounter_stats_by_type(cover_period = cover_period)
483 for enc in encs:
484 item = u' %s x %s' % (enc['frequency'], enc['l10n_type'])
485 list_items.append(item)
486 list_data.append(item)
487
488 stays = emr.get_hospital_stay_stats_by_hospital(cover_period = cover_period)
489 for stay in stays:
490 item = u' %s x %s' % (
491 stay['frequency'],
492 stay['hospital']
493 )
494 list_items.append(item)
495 list_data.append({'stay': item})
496
497 self._LCTRL_encounters.set_string_items(items = list_items)
498 self._LCTRL_encounters.set_data(data = list_data)
499 if is_waiting:
500 self._LCTRL_encounters.SetItemTextColour(0, wx.NamedColour('RED'))
501
522
542
543
544 - def __refresh_history(self, patient=None):
545 emr = patient.get_emr()
546
547 list_items = []
548 list_data = []
549
550 issues = [
551 i for i in emr.get_health_issues()
552 if ((i['clinically_relevant'] is False) or (i['is_active'] is False))
553 ]
554 for issue in issues:
555 last_encounter = emr.get_last_encounter(issue_id = issue['pk_health_issue'])
556 if last_encounter is None:
557 last = issue['modified_when'].strftime('%m/%Y')
558 else:
559 last = last_encounter['last_affirmed'].strftime('%m/%Y')
560 list_items.append(u'%s %s' % (last, issue['description']))
561 list_data.append(issue)
562 del issues
563
564 fhxs = emr.get_family_history()
565 for fhx in fhxs:
566 list_items.append(u'%s: %s%s' % (
567 fhx['l10n_relation'],
568 fhx['condition'],
569 gmTools.coalesce(fhx['age_noted'], u'', u' (@ %s)')
570 ))
571 list_data.append(fhx)
572 del fhxs
573
574 stays = emr.get_hospital_stays()
575 for stay in stays:
576 if stay['discharge'] is not None:
577 discharge = u''
578 else:
579 discharge = gmTools.u_ellipsis
580 list_items.append(u'%s%s %s: %s' % (
581 gmDateTime.pydt_strftime(stay['admission'], format = '%Y %b %d'),
582 discharge,
583 stay['hospital'],
584 stay['episode']
585 ))
586 list_data.append(stay)
587 del stays
588
589 procs = emr.get_performed_procedures()
590 for proc in procs:
591 list_items.append(u'%s%s %s' % (
592 gmDateTime.pydt_strftime(proc['clin_when'], format = '%Y %b %d'),
593 gmTools.bool2subst(proc['is_ongoing'], gmTools.u_ellipsis, u'', u''),
594 proc['performed_procedure']
595 ))
596 list_data.append(proc)
597 del procs
598
599 vaccs = emr.get_latest_vaccinations()
600 for ind, tmp in vaccs.items():
601 tmp, vacc = tmp
602 list_items.append(_('%s Vacc: %s') % (
603 gmDateTime.pydt_strftime(vacc['date_given'], format = '%Y %b %d'),
604 ind
605 ))
606 list_data.append(vacc)
607 del vaccs
608
609 self._LCTRL_history.set_string_items(items = list_items)
610 self._LCTRL_history.set_data(data = list_data)
611
613
614 if isinstance(data, gmEMRStructItems.cHealthIssue):
615 return data.format (
616 patient = gmPerson.gmCurrentPatient(),
617 with_medications = False,
618 with_hospital_stays = False,
619 with_procedures = False,
620 with_family_history = False,
621 with_documents = False,
622 with_tests = False,
623 with_vaccinations = False
624 ).strip(u'\n')
625
626 if isinstance(data, gmFamilyHistory.cFamilyHistory):
627 return data.format(include_episode = True, include_comment = True)
628
629 if isinstance(data, gmEMRStructItems.cHospitalStay):
630 return data.format()
631
632 if isinstance(data, gmEMRStructItems.cPerformedProcedure):
633 return data.format(include_episode = True)
634
635 if isinstance(data, gmVaccination.cVaccination):
636 return u'\n'.join(data.format (
637 with_indications = True,
638 with_comment = True,
639 with_reaction = True,
640 date_format = '%Y %b %d'
641 ))
642
643 return None
644
646 data = self._LCTRL_history.get_selected_item_data(only_one = True)
647 if data is None:
648 return
649
650
651 if wx.GetKeyState(wx.WXK_CONTROL):
652 if isinstance(data, gmEMRStructItems.cHealthIssue):
653 gmEMRStructWidgets.edit_health_issue(parent = self, issue = data)
654 return
655 if isinstance(data, gmFamilyHistory.cFamilyHistory):
656 FamilyHistoryWidgets.edit_family_history(parent = self, family_history = data)
657 return
658 if isinstance(data, gmEMRStructItems.cHospitalStay):
659 gmEMRStructWidgets.edit_hospital_stay(parent = self, hospital_stay = data)
660 return
661 if isinstance(data, gmEMRStructItems.cPerformedProcedure):
662 gmEMRStructWidgets.edit_procedure(parent = self, procedure = data)
663 return
664 if isinstance(data, gmVaccination.cVaccination):
665 gmVaccWidgets.edit_vaccination(parent = self, vaccination = data, single_entry = True)
666 return
667 return
668
669 if isinstance(data, gmEMRStructItems.cHealthIssue):
670 wx.CallAfter(gmDispatcher.send, signal = 'display_widget', name = 'gmEMRBrowserPlugin')
671 return
672 if isinstance(data, gmFamilyHistory.cFamilyHistory):
673 FamilyHistoryWidgets.manage_family_history(parent = self)
674 return
675 if isinstance(data, gmEMRStructItems.cHospitalStay):
676 gmEMRStructWidgets.manage_hospital_stays(parent = self)
677 return
678 if isinstance(data, gmEMRStructItems.cPerformedProcedure):
679 gmEMRStructWidgets.manage_performed_procedures(parent = self)
680 return
681 if isinstance(data, gmVaccination.cVaccination):
682 gmVaccWidgets.manage_vaccinations(parent = self)
683 return
684
685 return
686
687
689
690 emr = patient.get_emr()
691 intakes = emr.get_current_substance_intakes(include_inactive = False, include_unapproved = True, order_by = u'substance')
692
693 list_items = []
694 multi_brands_already_seen = []
695 data_items = []
696 for intake in intakes:
697 brand = intake.containing_drug
698 if brand is None or len(brand['pk_components']) == 1:
699 list_items.append(_('%s %s %s%s') % (
700 intake['substance'],
701 intake['amount'],
702 intake['unit'],
703 gmTools.coalesce (
704 intake['schedule'],
705 u'',
706 u': %s'
707 )
708 ))
709 data_items.append(intake)
710 else:
711 if intake['brand'] in multi_brands_already_seen:
712 continue
713 multi_brands_already_seen.append(intake['brand'])
714 list_items.append(_('%s %s%s') % (
715 intake['brand'],
716 brand['preparation'],
717 gmTools.coalesce (
718 intake['schedule'],
719 u'',
720 u': %s'
721 )
722 ))
723 data_items.append(intake)
724 self._LCTRL_meds.set_string_items(items = list_items)
725 self._LCTRL_meds.set_data(data = data_items)
726
739
749
750
796
846
866
867
898
932
947
948
978
997
999 data = self._LCTRL_identity.get_selected_item_data(only_one = True)
1000 if data is not None:
1001
1002 if wx.GetKeyState(wx.WXK_CONTROL):
1003 if isinstance(data, gmPerson.cPersonName):
1004 ea = gmDemographicsWidgets.cPersonNameEAPnl(self, -1, name = data)
1005 dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True)
1006 dlg.SetTitle(_('Cloning name'))
1007 dlg.ShowModal()
1008 return
1009 if isinstance(data, type({})):
1010 key = data.keys()[0]
1011 val = data[key]
1012 if key == 'id':
1013 ea = gmDemographicsWidgets.cExternalIDEditAreaPnl(self, -1, external_id = val)
1014 ea.identity = gmPerson.gmCurrentPatient()
1015 dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True)
1016 dlg.SetTitle(_('Editing external ID'))
1017 dlg.ShowModal()
1018 return
1019 if key == 'job':
1020 gmDemographicsWidgets.edit_occupation()
1021 return
1022
1023 wx.CallAfter(gmDispatcher.send, signal = 'display_widget', name = 'gmNotebookedPatientEditionPlugin')
1024
1025
1026
1027 if __name__ == "__main__":
1028
1029 if len(sys.argv) < 2:
1030 sys.exit()
1031
1032 if sys.argv[1] != u'test':
1033 sys.exit()
1034
1035
1036
1037
1038
1039
1040
1041
1042