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