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 for %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.get_messages(order_by = u'due_date NULLS LAST, importance DESC, received_when DESC'):
277
278 if msg['is_due']:
279 continue
280
281 if msg['is_expired']:
282 continue
283 if msg['due_date'] is None:
284 label = u'%s%s' % (
285 msg['l10n_type'],
286 gmTools.coalesce(msg['comment'], u'', u': %s')
287 )
288 else:
289 label = _('due in %s%s') % (
290 gmDateTime.format_interval_medically(msg['interval_due']),
291 gmTools.coalesce(msg['comment'], u'', u': %s')
292 )
293
294 list_items.append(label)
295 list_data.append(msg)
296
297 for hint in patient.dynamic_hints:
298 list_items.append(hint['title'])
299 list_data.append(hint)
300
301 self._LCTRL_inbox.set_string_items(items = list_items)
302 self._LCTRL_inbox.set_data(data = list_data)
303
304 if no_of_dues > 0:
305 for idx in range(no_of_dues):
306 self._LCTRL_inbox.SetItemTextColour(idx, wx.NamedColour('RED'))
307
321
353
354
356 doc_folder = patient.get_document_folder()
357
358 list_items = []
359 list_data = []
360
361 docs = doc_folder.get_unsigned_documents()
362 no_of_unsigned = len(docs)
363 for doc in docs:
364 list_items.append(u'%s %s (%s)' % (
365 gmDateTime.pydt_strftime(doc['clin_when'], format = '%m/%Y', accuracy = gmDateTime.acc_months),
366 doc['l10n_type'],
367 gmTools.u_writing_hand
368 ))
369 list_data.append(doc)
370
371 docs = doc_folder.get_documents(order_by = u'ORDER BY clin_when DESC', exclude_unsigned = True)
372 for doc in docs[:5]:
373 list_items.append(u'%s %s' % (
374 gmDateTime.pydt_strftime(doc['clin_when'], format = '%m/%Y', accuracy = gmDateTime.acc_months),
375 doc['l10n_type']
376 ))
377 list_data.append(doc)
378 if len(docs) > 5:
379 list_items.append(_('%s %s more not shown %s') % (
380 gmTools.u_ellipsis,
381 len(docs) - 5,
382 gmTools.u_ellipsis
383 ))
384 list_data.append(u'')
385
386 self._LCTRL_documents.set_string_items(items = list_items)
387 self._LCTRL_documents.set_data(data = list_data)
388
389 if no_of_unsigned > 0:
390 for idx in range(no_of_unsigned):
391 self._LCTRL_documents.SetItemTextColour(idx, wx.NamedColour('RED'))
392
400
416
417
419
420 cover_period = self._PRW_encounter_range.GetData()
421 if cover_period is None:
422 if self._PRW_encounter_range.GetValue().strip() != u'':
423 return
424
425 emr = patient.get_emr()
426
427 list_items = []
428 list_data = []
429
430 is_waiting = False
431 wlist = patient.get_waiting_list_entry()
432 if len(wlist) > 0:
433 is_waiting = True
434 w = wlist[0]
435 list_items.append(_('Currently in waiting list [%s]') % w['waiting_zone'])
436 list_data.append({'wlist': gmTools.coalesce(w['comment'], None)})
437
438 first = emr.get_first_encounter()
439 if first is not None:
440 list_items.append (
441 _('first: %s, %s') % (
442 gmDateTime.pydt_strftime (
443 first['started'],
444 format = '%Y %b %d',
445 accuracy = gmDateTime.acc_days
446 ),
447 first['l10n_type']
448 )
449 )
450 list_data.append(first)
451
452 last = emr.get_last_but_one_encounter()
453 if last is not None:
454 list_items.append (
455 _('last: %s, %s') % (
456 gmDateTime.pydt_strftime (
457 last['started'],
458 format = '%Y %b %d',
459 accuracy = gmDateTime.acc_days
460 ),
461 last['l10n_type']
462 )
463 )
464 list_data.append(last)
465
466 if cover_period is not None:
467 item = _('Last %s:') % self._PRW_encounter_range.GetValue().strip()
468 list_items.append(item)
469 list_data.append(_('Statistics cover period'))
470
471 encs = emr.get_encounter_stats_by_type(cover_period = cover_period)
472 for enc in encs:
473 item = u' %s x %s' % (enc['frequency'], enc['l10n_type'])
474 list_items.append(item)
475 list_data.append(item)
476
477 stays = emr.get_hospital_stay_stats_by_hospital(cover_period = cover_period)
478 for stay in stays:
479 item = u' %s x %s' % (
480 stay['frequency'],
481 stay['hospital']
482 )
483 list_items.append(item)
484 list_data.append({'stay': item})
485
486 self._LCTRL_encounters.set_string_items(items = list_items)
487 self._LCTRL_encounters.set_data(data = list_data)
488 if is_waiting:
489 self._LCTRL_encounters.SetItemTextColour(0, wx.NamedColour('RED'))
490
511
531
532
533 - def __refresh_history(self, patient=None):
534 emr = patient.get_emr()
535
536 list_items = []
537 list_data = []
538
539 issues = [
540 i for i in emr.get_health_issues()
541 if ((i['clinically_relevant'] is False) or (i['is_active'] is False))
542 ]
543 for issue in issues:
544 last_encounter = emr.get_last_encounter(issue_id = issue['pk_health_issue'])
545 if last_encounter is None:
546 last = issue['modified_when'].strftime('%m/%Y')
547 else:
548 last = last_encounter['last_affirmed'].strftime('%m/%Y')
549 list_items.append(u'%s %s' % (last, issue['description']))
550 list_data.append(issue)
551 del issues
552
553 fhxs = emr.get_family_history()
554 for fhx in fhxs:
555 list_items.append(u'%s: %s%s' % (
556 fhx['l10n_relation'],
557 fhx['condition'],
558 gmTools.coalesce(fhx['age_noted'], u'', u' (@ %s)')
559 ))
560 list_data.append(fhx)
561 del fhxs
562
563 stays = emr.get_hospital_stays()
564 for stay in stays:
565 if stay['discharge'] is not None:
566 discharge = u''
567 else:
568 discharge = gmTools.u_ellipsis
569 list_items.append(u'%s%s %s: %s' % (
570 gmDateTime.pydt_strftime(stay['admission'], format = '%Y %b %d'),
571 discharge,
572 stay['hospital'],
573 stay['episode']
574 ))
575 list_data.append(stay)
576 del stays
577
578 procs = emr.get_performed_procedures()
579 for proc in procs:
580 list_items.append(u'%s%s %s' % (
581 gmDateTime.pydt_strftime(proc['clin_when'], format = '%Y %b %d'),
582 gmTools.bool2subst(proc['is_ongoing'], gmTools.u_ellipsis, u'', u''),
583 proc['performed_procedure']
584 ))
585 list_data.append(proc)
586 del procs
587
588 vaccs = emr.get_latest_vaccinations()
589 for ind, tmp in vaccs.items():
590 tmp, vacc = tmp
591 list_items.append(_('%s Vacc: %s') % (
592 gmDateTime.pydt_strftime(vacc['date_given'], format = '%Y %b %d'),
593 ind
594 ))
595 list_data.append(vacc)
596 del vaccs
597
598 self._LCTRL_history.set_string_items(items = list_items)
599 self._LCTRL_history.set_data(data = list_data)
600
602
603 if isinstance(data, gmEMRStructItems.cHealthIssue):
604 return data.format (
605 patient = gmPerson.gmCurrentPatient(),
606 with_medications = False,
607 with_hospital_stays = False,
608 with_procedures = False,
609 with_family_history = False,
610 with_documents = False,
611 with_tests = False,
612 with_vaccinations = False
613 ).strip(u'\n')
614
615 if isinstance(data, gmFamilyHistory.cFamilyHistory):
616 return data.format(include_episode = True, include_comment = True)
617
618 if isinstance(data, gmEMRStructItems.cHospitalStay):
619 return data.format()
620
621 if isinstance(data, gmEMRStructItems.cPerformedProcedure):
622 return data.format(include_episode = True)
623
624 if isinstance(data, gmVaccination.cVaccination):
625 return u'\n'.join(data.format (
626 with_indications = True,
627 with_comment = True,
628 with_reaction = True,
629 date_format = '%Y %b %d'
630 ))
631
632 return None
633
635 data = self._LCTRL_history.get_selected_item_data(only_one = True)
636 if data is None:
637 return
638
639
640 if wx.GetKeyState(wx.WXK_CONTROL):
641 if isinstance(data, gmEMRStructItems.cHealthIssue):
642 gmEMRStructWidgets.edit_health_issue(parent = self, issue = data)
643 return
644 if isinstance(data, gmFamilyHistory.cFamilyHistory):
645 FamilyHistoryWidgets.edit_family_history(parent = self, family_history = data)
646 return
647 if isinstance(data, gmEMRStructItems.cHospitalStay):
648 gmEMRStructWidgets.edit_hospital_stay(parent = self, hospital_stay = data)
649 return
650 if isinstance(data, gmEMRStructItems.cPerformedProcedure):
651 gmEMRStructWidgets.edit_procedure(parent = self, procedure = data)
652 return
653 if isinstance(data, gmVaccination.cVaccination):
654 gmVaccWidgets.edit_vaccination(parent = self, vaccination = data, single_entry = True)
655 return
656 return
657
658 if isinstance(data, gmEMRStructItems.cHealthIssue):
659 wx.CallAfter(gmDispatcher.send, signal = 'display_widget', name = 'gmEMRBrowserPlugin')
660 return
661 if isinstance(data, gmFamilyHistory.cFamilyHistory):
662 FamilyHistoryWidgets.manage_family_history(parent = self)
663 return
664 if isinstance(data, gmEMRStructItems.cHospitalStay):
665 gmEMRStructWidgets.manage_hospital_stays(parent = self)
666 return
667 if isinstance(data, gmEMRStructItems.cPerformedProcedure):
668 gmEMRStructWidgets.manage_performed_procedures(parent = self)
669 return
670 if isinstance(data, gmVaccination.cVaccination):
671 gmVaccWidgets.manage_vaccinations(parent = self)
672 return
673
674 return
675
676
678
679 emr = patient.get_emr()
680 intakes = emr.get_current_substance_intake(include_inactive = False, include_unapproved = True, order_by = u'substance')
681
682 list_items = []
683 multi_brands_already_seen = []
684 data_items = []
685 for intake in intakes:
686 brand = intake.containing_drug
687 if brand is None or len(brand['pk_components']) == 1:
688 list_items.append(_('%s %s %s%s') % (
689 intake['substance'],
690 intake['amount'],
691 intake['unit'],
692 gmTools.coalesce (
693 intake['schedule'],
694 u'',
695 u': %s'
696 )
697 ))
698 data_items.append(intake)
699 else:
700 if intake['brand'] in multi_brands_already_seen:
701 continue
702 multi_brands_already_seen.append(intake['brand'])
703 list_items.append(_('%s %s%s') % (
704 intake['brand'],
705 brand['preparation'],
706 gmTools.coalesce (
707 intake['schedule'],
708 u'',
709 u': %s'
710 )
711 ))
712 data_items.append(intake)
713 self._LCTRL_meds.set_string_items(items = list_items)
714 self._LCTRL_meds.set_data(data = data_items)
715
728
738
739
785
835
855
856
887
921
936
937
967
986
988 data = self._LCTRL_identity.get_selected_item_data(only_one = True)
989 if data is not None:
990
991 if wx.GetKeyState(wx.WXK_CONTROL):
992 if isinstance(data, gmPerson.cPersonName):
993 ea = gmDemographicsWidgets.cPersonNameEAPnl(self, -1, name = data)
994 dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True)
995 dlg.SetTitle(_('Cloning name'))
996 dlg.ShowModal()
997 return
998 if isinstance(data, type({})):
999 key = data.keys()[0]
1000 val = data[key]
1001 if key == 'id':
1002 ea = gmDemographicsWidgets.cExternalIDEditAreaPnl(self, -1, external_id = val)
1003 ea.identity = gmPerson.gmCurrentPatient()
1004 dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True)
1005 dlg.SetTitle(_('Editing external ID'))
1006 dlg.ShowModal()
1007 return
1008 if key == 'job':
1009 gmDemographicsWidgets.edit_occupation()
1010 return
1011
1012 wx.CallAfter(gmDispatcher.send, signal = 'display_widget', name = 'gmNotebookedPatientEditionPlugin')
1013
1014
1015
1016 if __name__ == "__main__":
1017
1018 if len(sys.argv) < 2:
1019 sys.exit()
1020
1021 if sys.argv[1] != u'test':
1022 sys.exit()
1023
1024
1025
1026
1027
1028
1029
1030
1031