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 wx.CallAfter(self._schedule_data_reget)
159
161 wx.CallAfter(self._schedule_data_reget)
162
164 wx.CallAfter(self._schedule_data_reget)
165
166
167
169 pat = gmPerson.gmCurrentPatient()
170 if not pat.connected:
171 self.__reset_ui_content()
172 return True
173
174 self.__refresh_identity(patient = pat)
175 self.__refresh_contacts(patient = pat)
176 self.__refresh_encounters(patient = pat)
177
178 self.__refresh_problems(patient = pat)
179 self.__refresh_meds(patient = pat)
180 self.__refresh_history(patient = pat)
181
182 self.__refresh_inbox(patient = pat)
183 self.__refresh_results(patient = pat)
184 self.__refresh_documents(patient = pat)
185
186 return True
187
188
189
191 list_items = []
192 list_data = []
193
194 emr = patient.get_emr()
195 most_recent = emr.get_most_recent_result()
196 if most_recent is None:
197 self._LCTRL_results.set_string_items(items = [])
198 self._LCTRL_results.set_data(data = [])
199 return
200
201 list_items.append(_('Latest: %s ago (%s %s %s %s%s)') % (
202 gmDateTime.format_interval_medically(gmDateTime.pydt_now_here() - most_recent['clin_when']),
203 most_recent['unified_abbrev'],
204 most_recent['unified_val'],
205 most_recent['val_unit'],
206 gmTools.coalesce(most_recent['abnormality_indicator'], u'', u' %s'),
207 gmTools.bool2subst(most_recent['reviewed'], u'', u' %s' % gmTools.u_writing_hand)
208 ))
209 list_data.append(most_recent)
210 most_recent_needs_red = False
211 if most_recent['is_technically_abnormal'] is True:
212 if most_recent['is_clinically_relevant']:
213 most_recent_needs_red = True
214 else:
215 if most_recent['abnormality_indicator'] not in [None, u'']:
216 most_recent_needs_red = True
217
218 unsigned = emr.get_unsigned_results(order_by = u"(trim(coalesce(abnormality_indicator), '') <> '') DESC NULLS LAST, unified_abbrev")
219 no_of_reds = 0
220 for result in unsigned:
221 if result['pk_test_result'] == most_recent['pk_test_result']:
222 continue
223 if result['abnormality_indicator'] is not None:
224 if result['abnormality_indicator'].strip() != u'':
225 no_of_reds += 1
226 list_items.append(_('%s %s %s %s (%s ago, %s)') % (
227 result['unified_abbrev'],
228 result['unified_val'],
229 result['val_unit'],
230 gmTools.coalesce(result['abnormality_indicator'], u'', u' %s'),
231 gmDateTime.format_interval_medically(gmDateTime.pydt_now_here() - result['clin_when']),
232 gmTools.u_writing_hand
233 ))
234 list_data.append(result)
235
236 self._LCTRL_results.set_string_items(items = list_items)
237 self._LCTRL_results.set_data(data = list_data)
238
239 if most_recent_needs_red:
240 self._LCTRL_results.SetItemTextColour(0, wx.NamedColour('RED'))
241 if no_of_reds > 0:
242 for idx in range(1, no_of_reds + 1):
243 self._LCTRL_results.SetItemTextColour(idx, wx.NamedColour('RED'))
244
247
249
250
251
252
253
254
255
256 wx.CallAfter(gmDispatcher.send, signal = 'display_widget', name = 'gmMeasurementsGridPlugin')
257 return
258
259
261 list_items = []
262 list_data = []
263
264 due_messages = patient.due_messages
265 no_of_dues = len(due_messages)
266 for msg in due_messages:
267 list_items.append(_('due %s: %s') % (
268 gmDateTime.format_interval_medically(msg['interval_due']),
269 gmTools.coalesce(msg['comment'], u'?')
270 ))
271 list_data.append(msg)
272
273 for msg in patient.messages:
274
275 if msg['is_due']:
276 continue
277
278 if msg['is_expired']:
279 continue
280 list_items.append(u'%s%s' % (
281 msg['l10n_type'],
282 gmTools.coalesce(msg['comment'], u'', u': %s')
283 ))
284 list_data.append(msg)
285
286 for hint in patient.dynamic_hints:
287 list_items.append(hint['title'])
288 list_data.append(hint)
289
290 self._LCTRL_inbox.set_string_items(items = list_items)
291 self._LCTRL_inbox.set_data(data = list_data)
292
293 if no_of_dues > 0:
294 for idx in range(no_of_dues):
295 self._LCTRL_inbox.SetItemTextColour(idx, wx.NamedColour('RED'))
296
310
342
343
345 doc_folder = patient.get_document_folder()
346
347 list_items = []
348 list_data = []
349
350 docs = doc_folder.get_unsigned_documents()
351 no_of_unsigned = len(docs)
352 for doc in docs:
353 list_items.append(u'%s %s (%s)' % (
354 gmDateTime.pydt_strftime(doc['clin_when'], format = '%m/%Y', accuracy = gmDateTime.acc_months),
355 doc['l10n_type'],
356 gmTools.u_writing_hand
357 ))
358 list_data.append(doc)
359
360 docs = doc_folder.get_documents(order_by = u'ORDER BY clin_when DESC', exclude_unsigned = True)
361 for doc in docs[:5]:
362 list_items.append(u'%s %s' % (
363 gmDateTime.pydt_strftime(doc['clin_when'], format = '%m/%Y', accuracy = gmDateTime.acc_months),
364 doc['l10n_type']
365 ))
366 list_data.append(doc)
367 if len(docs) > 5:
368 list_items.append(_('%s %s more not shown %s') % (
369 gmTools.u_ellipsis,
370 len(docs) - 5,
371 gmTools.u_ellipsis
372 ))
373 list_data.append(u'')
374
375 self._LCTRL_documents.set_string_items(items = list_items)
376 self._LCTRL_documents.set_data(data = list_data)
377
378 if no_of_unsigned > 0:
379 for idx in range(no_of_unsigned):
380 self._LCTRL_documents.SetItemTextColour(idx, wx.NamedColour('RED'))
381
389
405
406
408
409 cover_period = self._PRW_encounter_range.GetData()
410 if cover_period is None:
411 if self._PRW_encounter_range.GetValue().strip() != u'':
412 return
413
414 emr = patient.get_emr()
415
416 list_items = []
417 list_data = []
418
419 is_waiting = False
420 wlist = patient.get_waiting_list_entry()
421 if len(wlist) > 0:
422 is_waiting = True
423 w = wlist[0]
424 list_items.append(_('Currently in waiting list [%s]') % w['waiting_zone'])
425 list_data.append({'wlist': gmTools.coalesce(w['comment'], None)})
426
427 first = emr.get_first_encounter()
428 if first is not None:
429 list_items.append (
430 _('first: %s, %s') % (
431 gmDateTime.pydt_strftime (
432 first['started'],
433 format = '%Y %b %d',
434 accuracy = gmDateTime.acc_days
435 ),
436 first['l10n_type']
437 )
438 )
439 list_data.append(first)
440
441 last = emr.get_last_but_one_encounter()
442 if last is not None:
443 list_items.append (
444 _('last: %s, %s') % (
445 gmDateTime.pydt_strftime (
446 last['started'],
447 format = '%Y %b %d',
448 accuracy = gmDateTime.acc_days
449 ),
450 last['l10n_type']
451 )
452 )
453 list_data.append(last)
454
455 encs = emr.get_encounter_stats_by_type(cover_period = cover_period)
456 for enc in encs:
457 item = u'%s x %s' % (enc['frequency'], enc['l10n_type'])
458 list_items.append(item)
459 list_data.append(item)
460
461 stays = emr.get_hospital_stay_stats_by_hospital(cover_period = cover_period)
462 for stay in stays:
463 item = u'%s x %s' % (
464 stay['frequency'],
465 stay['hospital']
466 )
467 list_items.append(item)
468 list_data.append({'stay': item})
469
470 self._LCTRL_encounters.set_string_items(items = list_items)
471 self._LCTRL_encounters.set_data(data = list_data)
472 if is_waiting:
473 self._LCTRL_encounters.SetItemTextColour(0, wx.NamedColour('RED'))
474
495
515
516
517 - def __refresh_history(self, patient=None):
518 emr = patient.get_emr()
519
520 list_items = []
521 list_data = []
522
523 issues = [
524 i for i in emr.get_health_issues()
525 if ((i['clinically_relevant'] is False) or (i['is_active'] is False))
526 ]
527 for issue in issues:
528 last_encounter = emr.get_last_encounter(issue_id = issue['pk_health_issue'])
529 if last_encounter is None:
530 last = issue['modified_when'].strftime('%m/%Y')
531 else:
532 last = last_encounter['last_affirmed'].strftime('%m/%Y')
533 list_items.append(u'%s %s' % (last, issue['description']))
534 list_data.append(issue)
535 del issues
536
537 fhxs = emr.get_family_history()
538 for fhx in fhxs:
539 list_items.append(u'%s: %s%s' % (
540 fhx['l10n_relation'],
541 fhx['condition'],
542 gmTools.coalesce(fhx['age_noted'], u'', u' (@ %s)')
543 ))
544 list_data.append(fhx)
545 del fhxs
546
547 stays = emr.get_hospital_stays()
548 for stay in stays:
549 if stay['discharge'] is not None:
550 discharge = u''
551 else:
552 discharge = gmTools.u_ellipsis
553 list_items.append(u'%s%s %s: %s' % (
554 gmDateTime.pydt_strftime(stay['admission'], format = '%Y %b %d'),
555 discharge,
556 stay['hospital'],
557 stay['episode']
558 ))
559 list_data.append(stay)
560 del stays
561
562 procs = emr.get_performed_procedures()
563 for proc in procs:
564 list_items.append(u'%s%s %s' % (
565 gmDateTime.pydt_strftime(proc['clin_when'], format = '%Y %b %d'),
566 gmTools.bool2subst(proc['is_ongoing'], gmTools.u_ellipsis, u'', u''),
567 proc['performed_procedure']
568 ))
569 list_data.append(proc)
570 del procs
571
572 vaccs = emr.get_latest_vaccinations()
573 for ind, tmp in vaccs.items():
574 tmp, vacc = tmp
575 list_items.append(_('%s Vacc: %s') % (
576 gmDateTime.pydt_strftime(vacc['date_given'], format = '%Y %b %d'),
577 ind
578 ))
579 list_data.append(vacc)
580 del vaccs
581
582 self._LCTRL_history.set_string_items(items = list_items)
583 self._LCTRL_history.set_data(data = list_data)
584
586
587 if isinstance(data, gmEMRStructItems.cHealthIssue):
588 return data.format (
589 patient = gmPerson.gmCurrentPatient(),
590 with_medications = False,
591 with_hospital_stays = False,
592 with_procedures = False,
593 with_family_history = False,
594 with_documents = False,
595 with_tests = False,
596 with_vaccinations = False
597 ).strip(u'\n')
598
599 if isinstance(data, gmFamilyHistory.cFamilyHistory):
600 return data.format(include_episode = True, include_comment = True)
601
602 if isinstance(data, gmEMRStructItems.cHospitalStay):
603 return data.format()
604
605 if isinstance(data, gmEMRStructItems.cPerformedProcedure):
606 return data.format(include_episode = True)
607
608 if isinstance(data, gmVaccination.cVaccination):
609 return u'\n'.join(data.format (
610 with_indications = True,
611 with_comment = True,
612 with_reaction = True,
613 date_format = '%Y %b %d'
614 ))
615
616 return None
617
619 data = self._LCTRL_history.get_selected_item_data(only_one = True)
620 if data is None:
621 return
622
623
624 if wx.GetKeyState(wx.WXK_CONTROL):
625 if isinstance(data, gmEMRStructItems.cHealthIssue):
626 gmEMRStructWidgets.edit_health_issue(parent = self, issue = data)
627 return
628 if isinstance(data, gmFamilyHistory.cFamilyHistory):
629 FamilyHistoryWidgets.edit_family_history(parent = self, family_history = data)
630 return
631 if isinstance(data, gmEMRStructItems.cHospitalStay):
632 gmEMRStructWidgets.edit_hospital_stay(parent = self, hospital_stay = data)
633 return
634 if isinstance(data, gmEMRStructItems.cPerformedProcedure):
635 gmEMRStructWidgets.edit_procedure(parent = self, procedure = data)
636 return
637 if isinstance(data, gmVaccination.cVaccination):
638 gmVaccWidgets.edit_vaccination(parent = self, vaccination = data, single_entry = True)
639 return
640 return
641
642 if isinstance(data, gmEMRStructItems.cHealthIssue):
643 wx.CallAfter(gmDispatcher.send, signal = 'display_widget', name = 'gmEMRBrowserPlugin')
644 return
645 if isinstance(data, gmFamilyHistory.cFamilyHistory):
646 FamilyHistoryWidgets.manage_family_history(parent = self)
647 return
648 if isinstance(data, gmEMRStructItems.cHospitalStay):
649 gmEMRStructWidgets.manage_hospital_stays(parent = self)
650 return
651 if isinstance(data, gmEMRStructItems.cPerformedProcedure):
652 gmEMRStructWidgets.manage_performed_procedures(parent = self)
653 return
654 if isinstance(data, gmVaccination.cVaccination):
655 gmVaccWidgets.manage_vaccinations(parent = self)
656 return
657
658 return
659
660
662
663 emr = patient.get_emr()
664 intakes = emr.get_current_substance_intake(include_inactive = False, include_unapproved = True, order_by = u'substance')
665
666 list_items = []
667 multi_brands_already_seen = []
668 for intake in intakes:
669 brand = intake.containing_drug
670 if brand is None or len(brand['pk_components']) == 1:
671 list_items.append(_('%s %s %s%s') % (
672 intake['substance'],
673 intake['amount'],
674 intake['unit'],
675 gmTools.coalesce (
676 intake['schedule'],
677 u'',
678 u': %s'
679 )
680 ))
681 else:
682 if intake['brand'] in multi_brands_already_seen:
683 continue
684 multi_brands_already_seen.append(intake['brand'])
685 list_items.append(_('%s %s%s') % (
686 intake['brand'],
687 brand['preparation'],
688 gmTools.coalesce (
689 intake['schedule'],
690 u'',
691 u': %s'
692 )
693 ))
694 self._LCTRL_meds.set_string_items(items = list_items)
695 self._LCTRL_meds.set_data(data = intakes)
696
709
719
720
765
814
834
835
837 emr = patient.get_emr()
838
839 problems = [
840 p for p in emr.get_problems(include_closed_episodes = False, include_irrelevant_issues = False)
841 if p['problem_active']
842 ]
843
844 list_items = []
845 for problem in problems:
846 if problem['type'] == 'issue':
847 issue = emr.problem2issue(problem)
848 last_encounter = emr.get_last_encounter(issue_id = issue['pk_health_issue'])
849 if last_encounter is None:
850 last = issue['modified_when'].strftime('%m/%Y')
851 else:
852 last = last_encounter['last_affirmed'].strftime('%m/%Y')
853 list_items.append(u'%s: %s' % (problem['problem'], last))
854
855 elif problem['type'] == 'episode':
856 epi = emr.problem2episode(problem)
857 last_encounter = emr.get_last_encounter(episode_id = epi['pk_episode'])
858 if last_encounter is None:
859 last = epi['episode_modified_when'].strftime('%m/%Y')
860 else:
861 last = last_encounter['last_affirmed'].strftime('%m/%Y')
862 list_items.append(u'%s: %s' % (problem['problem'], last))
863
864 self._LCTRL_problems.set_string_items(items = list_items)
865 self._LCTRL_problems.set_data(data = problems)
866
900
915
916
918
919 names = patient.get_names(exclude_active = True)
920 items = [
921 _('aka: %(last)s, %(first)s%(nick)s') % {
922 'last': n['lastnames'],
923 'first': n['firstnames'],
924 'nick': gmTools.coalesce(n['preferred'], u'', u" '%s'")
925 } for n in names
926 ]
927 data = names
928
929
930 ids = patient.external_ids
931 for i in ids:
932 items.append(u'%(name)s: %(value)s' % i)
933 data.append({'id': i})
934
935
936 jobs = patient.get_occupations()
937 for j in jobs:
938 items.append(_('job: %s') % j['l10n_occupation'])
939 data.append({'job': j})
940
941 self._LCTRL_identity.set_string_items(items = items)
942 self._LCTRL_identity.set_data(data = data)
943
961
963 data = self._LCTRL_identity.get_selected_item_data(only_one = True)
964 if data is not None:
965
966 if wx.GetKeyState(wx.WXK_CONTROL):
967 if isinstance(data, gmPerson.cPersonName):
968 ea = gmDemographicsWidgets.cPersonNameEAPnl(self, -1, name = data)
969 dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True)
970 dlg.SetTitle(_('Cloning name'))
971 dlg.ShowModal()
972 return
973 if isinstance(data, type({})):
974 key = data.keys()[0]
975 val = data[key]
976 if key == 'id':
977 ea = gmDemographicsWidgets.cExternalIDEditAreaPnl(self, -1, external_id = val)
978 ea.identity = gmPerson.gmCurrentPatient()
979 dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True)
980 dlg.SetTitle(_('Editing external ID'))
981 dlg.ShowModal()
982 return
983 if key == 'job':
984 gmDemographicsWidgets.edit_occupation()
985 return
986
987 wx.CallAfter(gmDispatcher.send, signal = 'display_widget', name = 'gmNotebookedPatientEditionPlugin')
988
989
990
991 if __name__ == "__main__":
992
993 if len(sys.argv) < 2:
994 sys.exit()
995
996 if sys.argv[1] != u'test':
997 sys.exit()
998
999
1000
1001
1002
1003
1004
1005
1006