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