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 due_messages = patient.due_messages
268 no_of_dues = len(due_messages)
269 for msg in due_messages:
270 list_items.append(_('due %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.messages:
277
278 if msg['is_due']:
279 continue
280
281 if msg['is_expired']:
282 continue
283 list_items.append(u'%s%s' % (
284 msg['l10n_type'],
285 gmTools.coalesce(msg['comment'], u'', u': %s')
286 ))
287 list_data.append(msg)
288
289 for hint in patient.dynamic_hints:
290 list_items.append(hint['title'])
291 list_data.append(hint)
292
293 self._LCTRL_inbox.set_string_items(items = list_items)
294 self._LCTRL_inbox.set_data(data = list_data)
295
296 if no_of_dues > 0:
297 for idx in range(no_of_dues):
298 self._LCTRL_inbox.SetItemTextColour(idx, wx.NamedColour('RED'))
299
313
345
346
348 doc_folder = patient.get_document_folder()
349
350 list_items = []
351 list_data = []
352
353 docs = doc_folder.get_unsigned_documents()
354 no_of_unsigned = len(docs)
355 for doc in docs:
356 list_items.append(u'%s %s (%s)' % (
357 gmDateTime.pydt_strftime(doc['clin_when'], format = '%m/%Y', accuracy = gmDateTime.acc_months),
358 doc['l10n_type'],
359 gmTools.u_writing_hand
360 ))
361 list_data.append(doc)
362
363 docs = doc_folder.get_documents(order_by = u'ORDER BY clin_when DESC', exclude_unsigned = True)
364 for doc in docs[:5]:
365 list_items.append(u'%s %s' % (
366 gmDateTime.pydt_strftime(doc['clin_when'], format = '%m/%Y', accuracy = gmDateTime.acc_months),
367 doc['l10n_type']
368 ))
369 list_data.append(doc)
370 if len(docs) > 5:
371 list_items.append(_('%s %s more not shown %s') % (
372 gmTools.u_ellipsis,
373 len(docs) - 5,
374 gmTools.u_ellipsis
375 ))
376 list_data.append(u'')
377
378 self._LCTRL_documents.set_string_items(items = list_items)
379 self._LCTRL_documents.set_data(data = list_data)
380
381 if no_of_unsigned > 0:
382 for idx in range(no_of_unsigned):
383 self._LCTRL_documents.SetItemTextColour(idx, wx.NamedColour('RED'))
384
392
408
409
411
412 cover_period = self._PRW_encounter_range.GetData()
413 if cover_period is None:
414 if self._PRW_encounter_range.GetValue().strip() != u'':
415 return
416
417 emr = patient.get_emr()
418
419 list_items = []
420 list_data = []
421
422 is_waiting = False
423 wlist = patient.get_waiting_list_entry()
424 if len(wlist) > 0:
425 is_waiting = True
426 w = wlist[0]
427 list_items.append(_('Currently in waiting list [%s]') % w['waiting_zone'])
428 list_data.append({'wlist': gmTools.coalesce(w['comment'], None)})
429
430 first = emr.get_first_encounter()
431 if first is not None:
432 list_items.append (
433 _('first: %s, %s') % (
434 gmDateTime.pydt_strftime (
435 first['started'],
436 format = '%Y %b %d',
437 accuracy = gmDateTime.acc_days
438 ),
439 first['l10n_type']
440 )
441 )
442 list_data.append(first)
443
444 last = emr.get_last_but_one_encounter()
445 if last is not None:
446 list_items.append (
447 _('last: %s, %s') % (
448 gmDateTime.pydt_strftime (
449 last['started'],
450 format = '%Y %b %d',
451 accuracy = gmDateTime.acc_days
452 ),
453 last['l10n_type']
454 )
455 )
456 list_data.append(last)
457
458 if cover_period is not None:
459 item = _('Last %s:') % self._PRW_encounter_range.GetValue().strip()
460 list_items.append(item)
461 list_data.append(_('Statistics cover period'))
462
463 encs = emr.get_encounter_stats_by_type(cover_period = cover_period)
464 for enc in encs:
465 item = u' %s x %s' % (enc['frequency'], enc['l10n_type'])
466 list_items.append(item)
467 list_data.append(item)
468
469 stays = emr.get_hospital_stay_stats_by_hospital(cover_period = cover_period)
470 for stay in stays:
471 item = u' %s x %s' % (
472 stay['frequency'],
473 stay['hospital']
474 )
475 list_items.append(item)
476 list_data.append({'stay': item})
477
478 self._LCTRL_encounters.set_string_items(items = list_items)
479 self._LCTRL_encounters.set_data(data = list_data)
480 if is_waiting:
481 self._LCTRL_encounters.SetItemTextColour(0, wx.NamedColour('RED'))
482
503
523
524
525 - def __refresh_history(self, patient=None):
526 emr = patient.get_emr()
527
528 list_items = []
529 list_data = []
530
531 issues = [
532 i for i in emr.get_health_issues()
533 if ((i['clinically_relevant'] is False) or (i['is_active'] is False))
534 ]
535 for issue in issues:
536 last_encounter = emr.get_last_encounter(issue_id = issue['pk_health_issue'])
537 if last_encounter is None:
538 last = issue['modified_when'].strftime('%m/%Y')
539 else:
540 last = last_encounter['last_affirmed'].strftime('%m/%Y')
541 list_items.append(u'%s %s' % (last, issue['description']))
542 list_data.append(issue)
543 del issues
544
545 fhxs = emr.get_family_history()
546 for fhx in fhxs:
547 list_items.append(u'%s: %s%s' % (
548 fhx['l10n_relation'],
549 fhx['condition'],
550 gmTools.coalesce(fhx['age_noted'], u'', u' (@ %s)')
551 ))
552 list_data.append(fhx)
553 del fhxs
554
555 stays = emr.get_hospital_stays()
556 for stay in stays:
557 if stay['discharge'] is not None:
558 discharge = u''
559 else:
560 discharge = gmTools.u_ellipsis
561 list_items.append(u'%s%s %s: %s' % (
562 gmDateTime.pydt_strftime(stay['admission'], format = '%Y %b %d'),
563 discharge,
564 stay['hospital'],
565 stay['episode']
566 ))
567 list_data.append(stay)
568 del stays
569
570 procs = emr.get_performed_procedures()
571 for proc in procs:
572 list_items.append(u'%s%s %s' % (
573 gmDateTime.pydt_strftime(proc['clin_when'], format = '%Y %b %d'),
574 gmTools.bool2subst(proc['is_ongoing'], gmTools.u_ellipsis, u'', u''),
575 proc['performed_procedure']
576 ))
577 list_data.append(proc)
578 del procs
579
580 vaccs = emr.get_latest_vaccinations()
581 for ind, tmp in vaccs.items():
582 tmp, vacc = tmp
583 list_items.append(_('%s Vacc: %s') % (
584 gmDateTime.pydt_strftime(vacc['date_given'], format = '%Y %b %d'),
585 ind
586 ))
587 list_data.append(vacc)
588 del vaccs
589
590 self._LCTRL_history.set_string_items(items = list_items)
591 self._LCTRL_history.set_data(data = list_data)
592
594
595 if isinstance(data, gmEMRStructItems.cHealthIssue):
596 return data.format (
597 patient = gmPerson.gmCurrentPatient(),
598 with_medications = False,
599 with_hospital_stays = False,
600 with_procedures = False,
601 with_family_history = False,
602 with_documents = False,
603 with_tests = False,
604 with_vaccinations = False
605 ).strip(u'\n')
606
607 if isinstance(data, gmFamilyHistory.cFamilyHistory):
608 return data.format(include_episode = True, include_comment = True)
609
610 if isinstance(data, gmEMRStructItems.cHospitalStay):
611 return data.format()
612
613 if isinstance(data, gmEMRStructItems.cPerformedProcedure):
614 return data.format(include_episode = True)
615
616 if isinstance(data, gmVaccination.cVaccination):
617 return u'\n'.join(data.format (
618 with_indications = True,
619 with_comment = True,
620 with_reaction = True,
621 date_format = '%Y %b %d'
622 ))
623
624 return None
625
627 data = self._LCTRL_history.get_selected_item_data(only_one = True)
628 if data is None:
629 return
630
631
632 if wx.GetKeyState(wx.WXK_CONTROL):
633 if isinstance(data, gmEMRStructItems.cHealthIssue):
634 gmEMRStructWidgets.edit_health_issue(parent = self, issue = data)
635 return
636 if isinstance(data, gmFamilyHistory.cFamilyHistory):
637 FamilyHistoryWidgets.edit_family_history(parent = self, family_history = data)
638 return
639 if isinstance(data, gmEMRStructItems.cHospitalStay):
640 gmEMRStructWidgets.edit_hospital_stay(parent = self, hospital_stay = data)
641 return
642 if isinstance(data, gmEMRStructItems.cPerformedProcedure):
643 gmEMRStructWidgets.edit_procedure(parent = self, procedure = data)
644 return
645 if isinstance(data, gmVaccination.cVaccination):
646 gmVaccWidgets.edit_vaccination(parent = self, vaccination = data, single_entry = True)
647 return
648 return
649
650 if isinstance(data, gmEMRStructItems.cHealthIssue):
651 wx.CallAfter(gmDispatcher.send, signal = 'display_widget', name = 'gmEMRBrowserPlugin')
652 return
653 if isinstance(data, gmFamilyHistory.cFamilyHistory):
654 FamilyHistoryWidgets.manage_family_history(parent = self)
655 return
656 if isinstance(data, gmEMRStructItems.cHospitalStay):
657 gmEMRStructWidgets.manage_hospital_stays(parent = self)
658 return
659 if isinstance(data, gmEMRStructItems.cPerformedProcedure):
660 gmEMRStructWidgets.manage_performed_procedures(parent = self)
661 return
662 if isinstance(data, gmVaccination.cVaccination):
663 gmVaccWidgets.manage_vaccinations(parent = self)
664 return
665
666 return
667
668
670
671 emr = patient.get_emr()
672 intakes = emr.get_current_substance_intake(include_inactive = False, include_unapproved = True, order_by = u'substance')
673
674 list_items = []
675 multi_brands_already_seen = []
676 for intake in intakes:
677 brand = intake.containing_drug
678 if brand is None or len(brand['pk_components']) == 1:
679 list_items.append(_('%s %s %s%s') % (
680 intake['substance'],
681 intake['amount'],
682 intake['unit'],
683 gmTools.coalesce (
684 intake['schedule'],
685 u'',
686 u': %s'
687 )
688 ))
689 else:
690 if intake['brand'] in multi_brands_already_seen:
691 continue
692 multi_brands_already_seen.append(intake['brand'])
693 list_items.append(_('%s %s%s') % (
694 intake['brand'],
695 brand['preparation'],
696 gmTools.coalesce (
697 intake['schedule'],
698 u'',
699 u': %s'
700 )
701 ))
702 self._LCTRL_meds.set_string_items(items = list_items)
703 self._LCTRL_meds.set_data(data = intakes)
704
717
727
728
773
822
842
843
874
908
923
924
926
927 names = patient.get_names(exclude_active = True)
928 items = [
929 _('aka: %(last)s, %(first)s%(nick)s') % {
930 'last': n['lastnames'],
931 'first': n['firstnames'],
932 'nick': gmTools.coalesce(n['preferred'], u'', u" '%s'")
933 } for n in names
934 ]
935 data = names
936
937
938 ids = patient.external_ids
939 for i in ids:
940 items.append(u'%(name)s: %(value)s' % i)
941 data.append({'id': i})
942
943
944 jobs = patient.get_occupations()
945 for j in jobs:
946 items.append(_('job: %s (%s)') % (
947 j['l10n_occupation'],
948 j['modified_when'].strftime('%m/%Y')
949 ))
950 data.append({'job': j})
951
952 self._LCTRL_identity.set_string_items(items = items)
953 self._LCTRL_identity.set_data(data = data)
954
973
975 data = self._LCTRL_identity.get_selected_item_data(only_one = True)
976 if data is not None:
977
978 if wx.GetKeyState(wx.WXK_CONTROL):
979 if isinstance(data, gmPerson.cPersonName):
980 ea = gmDemographicsWidgets.cPersonNameEAPnl(self, -1, name = data)
981 dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True)
982 dlg.SetTitle(_('Cloning name'))
983 dlg.ShowModal()
984 return
985 if isinstance(data, type({})):
986 key = data.keys()[0]
987 val = data[key]
988 if key == 'id':
989 ea = gmDemographicsWidgets.cExternalIDEditAreaPnl(self, -1, external_id = val)
990 ea.identity = gmPerson.gmCurrentPatient()
991 dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True)
992 dlg.SetTitle(_('Editing external ID'))
993 dlg.ShowModal()
994 return
995 if key == 'job':
996 gmDemographicsWidgets.edit_occupation()
997 return
998
999 wx.CallAfter(gmDispatcher.send, signal = 'display_widget', name = 'gmNotebookedPatientEditionPlugin')
1000
1001
1002
1003 if __name__ == "__main__":
1004
1005 if len(sys.argv) < 2:
1006 sys.exit()
1007
1008 if sys.argv[1] != u'test':
1009 sys.exit()
1010
1011
1012
1013
1014
1015
1016
1017
1018