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_result()
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 encs = emr.get_encounter_stats_by_type(cover_period = cover_period)
459 for enc in encs:
460 item = u'%s x %s' % (enc['frequency'], enc['l10n_type'])
461 list_items.append(item)
462 list_data.append(item)
463
464 stays = emr.get_hospital_stay_stats_by_hospital(cover_period = cover_period)
465 for stay in stays:
466 item = u'%s x %s' % (
467 stay['frequency'],
468 stay['hospital']
469 )
470 list_items.append(item)
471 list_data.append({'stay': item})
472
473 self._LCTRL_encounters.set_string_items(items = list_items)
474 self._LCTRL_encounters.set_data(data = list_data)
475 if is_waiting:
476 self._LCTRL_encounters.SetItemTextColour(0, wx.NamedColour('RED'))
477
498
518
519
520 - def __refresh_history(self, patient=None):
521 emr = patient.get_emr()
522
523 list_items = []
524 list_data = []
525
526 issues = [
527 i for i in emr.get_health_issues()
528 if ((i['clinically_relevant'] is False) or (i['is_active'] is False))
529 ]
530 for issue in issues:
531 last_encounter = emr.get_last_encounter(issue_id = issue['pk_health_issue'])
532 if last_encounter is None:
533 last = issue['modified_when'].strftime('%m/%Y')
534 else:
535 last = last_encounter['last_affirmed'].strftime('%m/%Y')
536 list_items.append(u'%s %s' % (last, issue['description']))
537 list_data.append(issue)
538 del issues
539
540 fhxs = emr.get_family_history()
541 for fhx in fhxs:
542 list_items.append(u'%s: %s%s' % (
543 fhx['l10n_relation'],
544 fhx['condition'],
545 gmTools.coalesce(fhx['age_noted'], u'', u' (@ %s)')
546 ))
547 list_data.append(fhx)
548 del fhxs
549
550 stays = emr.get_hospital_stays()
551 for stay in stays:
552 if stay['discharge'] is not None:
553 discharge = u''
554 else:
555 discharge = gmTools.u_ellipsis
556 list_items.append(u'%s%s %s: %s' % (
557 gmDateTime.pydt_strftime(stay['admission'], format = '%Y %b %d'),
558 discharge,
559 stay['hospital'],
560 stay['episode']
561 ))
562 list_data.append(stay)
563 del stays
564
565 procs = emr.get_performed_procedures()
566 for proc in procs:
567 list_items.append(u'%s%s %s' % (
568 gmDateTime.pydt_strftime(proc['clin_when'], format = '%Y %b %d'),
569 gmTools.bool2subst(proc['is_ongoing'], gmTools.u_ellipsis, u'', u''),
570 proc['performed_procedure']
571 ))
572 list_data.append(proc)
573 del procs
574
575 vaccs = emr.get_latest_vaccinations()
576 for ind, tmp in vaccs.items():
577 tmp, vacc = tmp
578 list_items.append(_('%s Vacc: %s') % (
579 gmDateTime.pydt_strftime(vacc['date_given'], format = '%Y %b %d'),
580 ind
581 ))
582 list_data.append(vacc)
583 del vaccs
584
585 self._LCTRL_history.set_string_items(items = list_items)
586 self._LCTRL_history.set_data(data = list_data)
587
589
590 if isinstance(data, gmEMRStructItems.cHealthIssue):
591 return data.format (
592 patient = gmPerson.gmCurrentPatient(),
593 with_medications = False,
594 with_hospital_stays = False,
595 with_procedures = False,
596 with_family_history = False,
597 with_documents = False,
598 with_tests = False,
599 with_vaccinations = False
600 ).strip(u'\n')
601
602 if isinstance(data, gmFamilyHistory.cFamilyHistory):
603 return data.format(include_episode = True, include_comment = True)
604
605 if isinstance(data, gmEMRStructItems.cHospitalStay):
606 return data.format()
607
608 if isinstance(data, gmEMRStructItems.cPerformedProcedure):
609 return data.format(include_episode = True)
610
611 if isinstance(data, gmVaccination.cVaccination):
612 return u'\n'.join(data.format (
613 with_indications = True,
614 with_comment = True,
615 with_reaction = True,
616 date_format = '%Y %b %d'
617 ))
618
619 return None
620
622 data = self._LCTRL_history.get_selected_item_data(only_one = True)
623 if data is None:
624 return
625
626
627 if wx.GetKeyState(wx.WXK_CONTROL):
628 if isinstance(data, gmEMRStructItems.cHealthIssue):
629 gmEMRStructWidgets.edit_health_issue(parent = self, issue = data)
630 return
631 if isinstance(data, gmFamilyHistory.cFamilyHistory):
632 FamilyHistoryWidgets.edit_family_history(parent = self, family_history = data)
633 return
634 if isinstance(data, gmEMRStructItems.cHospitalStay):
635 gmEMRStructWidgets.edit_hospital_stay(parent = self, hospital_stay = data)
636 return
637 if isinstance(data, gmEMRStructItems.cPerformedProcedure):
638 gmEMRStructWidgets.edit_procedure(parent = self, procedure = data)
639 return
640 if isinstance(data, gmVaccination.cVaccination):
641 gmVaccWidgets.edit_vaccination(parent = self, vaccination = data, single_entry = True)
642 return
643 return
644
645 if isinstance(data, gmEMRStructItems.cHealthIssue):
646 wx.CallAfter(gmDispatcher.send, signal = 'display_widget', name = 'gmEMRBrowserPlugin')
647 return
648 if isinstance(data, gmFamilyHistory.cFamilyHistory):
649 FamilyHistoryWidgets.manage_family_history(parent = self)
650 return
651 if isinstance(data, gmEMRStructItems.cHospitalStay):
652 gmEMRStructWidgets.manage_hospital_stays(parent = self)
653 return
654 if isinstance(data, gmEMRStructItems.cPerformedProcedure):
655 gmEMRStructWidgets.manage_performed_procedures(parent = self)
656 return
657 if isinstance(data, gmVaccination.cVaccination):
658 gmVaccWidgets.manage_vaccinations(parent = self)
659 return
660
661 return
662
663
665
666 emr = patient.get_emr()
667 intakes = emr.get_current_substance_intake(include_inactive = False, include_unapproved = True, order_by = u'substance')
668
669 list_items = []
670 multi_brands_already_seen = []
671 for intake in intakes:
672 brand = intake.containing_drug
673 if brand is None or len(brand['pk_components']) == 1:
674 list_items.append(_('%s %s %s%s') % (
675 intake['substance'],
676 intake['amount'],
677 intake['unit'],
678 gmTools.coalesce (
679 intake['schedule'],
680 u'',
681 u': %s'
682 )
683 ))
684 else:
685 if intake['brand'] in multi_brands_already_seen:
686 continue
687 multi_brands_already_seen.append(intake['brand'])
688 list_items.append(_('%s %s%s') % (
689 intake['brand'],
690 brand['preparation'],
691 gmTools.coalesce (
692 intake['schedule'],
693 u'',
694 u': %s'
695 )
696 ))
697 self._LCTRL_meds.set_string_items(items = list_items)
698 self._LCTRL_meds.set_data(data = intakes)
699
712
722
723
768
817
837
838
840 emr = patient.get_emr()
841
842 problems = [
843 p for p in emr.get_problems(include_closed_episodes = False, include_irrelevant_issues = False)
844 if p['problem_active']
845 ]
846
847 list_items = []
848 for problem in problems:
849 if problem['type'] == 'issue':
850 issue = emr.problem2issue(problem)
851 last_encounter = emr.get_last_encounter(issue_id = issue['pk_health_issue'])
852 if last_encounter is None:
853 last = issue['modified_when'].strftime('%m/%Y')
854 else:
855 last = last_encounter['last_affirmed'].strftime('%m/%Y')
856 list_items.append(u'%s: %s' % (problem['problem'], last))
857
858 elif problem['type'] == 'episode':
859 epi = emr.problem2episode(problem)
860 last_encounter = emr.get_last_encounter(episode_id = epi['pk_episode'])
861 if last_encounter is None:
862 last = epi['episode_modified_when'].strftime('%m/%Y')
863 else:
864 last = last_encounter['last_affirmed'].strftime('%m/%Y')
865 list_items.append(u'%s: %s' % (problem['problem'], last))
866
867 self._LCTRL_problems.set_string_items(items = list_items)
868 self._LCTRL_problems.set_data(data = problems)
869
903
918
919
921
922 names = patient.get_names(exclude_active = True)
923 items = [
924 _('aka: %(last)s, %(first)s%(nick)s') % {
925 'last': n['lastnames'],
926 'first': n['firstnames'],
927 'nick': gmTools.coalesce(n['preferred'], u'', u" '%s'")
928 } for n in names
929 ]
930 data = names
931
932
933 ids = patient.external_ids
934 for i in ids:
935 items.append(u'%(name)s: %(value)s' % i)
936 data.append({'id': i})
937
938
939 jobs = patient.get_occupations()
940 for j in jobs:
941 items.append(_('job: %s') % j['l10n_occupation'])
942 data.append({'job': j})
943
944 self._LCTRL_identity.set_string_items(items = items)
945 self._LCTRL_identity.set_data(data = data)
946
964
966 data = self._LCTRL_identity.get_selected_item_data(only_one = True)
967 if data is not None:
968
969 if wx.GetKeyState(wx.WXK_CONTROL):
970 if isinstance(data, gmPerson.cPersonName):
971 ea = gmDemographicsWidgets.cPersonNameEAPnl(self, -1, name = data)
972 dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True)
973 dlg.SetTitle(_('Cloning name'))
974 dlg.ShowModal()
975 return
976 if isinstance(data, type({})):
977 key = data.keys()[0]
978 val = data[key]
979 if key == 'id':
980 ea = gmDemographicsWidgets.cExternalIDEditAreaPnl(self, -1, external_id = val)
981 ea.identity = gmPerson.gmCurrentPatient()
982 dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True)
983 dlg.SetTitle(_('Editing external ID'))
984 dlg.ShowModal()
985 return
986 if key == 'job':
987 gmDemographicsWidgets.edit_occupation()
988 return
989
990 wx.CallAfter(gmDispatcher.send, signal = 'display_widget', name = 'gmNotebookedPatientEditionPlugin')
991
992
993
994 if __name__ == "__main__":
995
996 if len(sys.argv) < 2:
997 sys.exit()
998
999 if sys.argv[1] != u'test':
1000 sys.exit()
1001
1002
1003
1004
1005
1006
1007
1008
1009