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
21 from Gnumed.business import gmPerson
22 from Gnumed.business import gmStaff
23 from Gnumed.business import gmDemographicRecord
24 from Gnumed.business import gmEMRStructItems
25 from Gnumed.business import gmFamilyHistory
26 from Gnumed.business import gmVaccination
27 from Gnumed.business import gmDocuments
28 from Gnumed.business import gmProviderInbox
29
30 from Gnumed.wxpython import gmRegetMixin
31 from Gnumed.wxpython import gmDemographicsWidgets
32 from Gnumed.wxpython import gmContactWidgets
33 from Gnumed.wxpython import gmMedicationWidgets
34 from Gnumed.wxpython import gmEditArea
35 from Gnumed.wxpython import gmEMRStructWidgets
36 from Gnumed.wxpython import gmFamilyHistoryWidgets
37 from Gnumed.wxpython import gmVaccWidgets
38 from Gnumed.wxpython import gmDocumentWidgets
39
40
41 _log = logging.getLogger('gm.patient')
42
43 from Gnumed.wxGladeWidgets import wxgPatientOverviewPnl
44
45 -class cPatientOverviewPnl(wxgPatientOverviewPnl.wxgPatientOverviewPnl, gmRegetMixin.cRegetOnPaintMixin):
46
53
54
55
95
97 self._LCTRL_identity.set_string_items()
98 self._LCTRL_contacts.set_string_items()
99 self._LCTRL_encounters.set_string_items()
100 self._PRW_encounter_range.SetText(value = u'', data = None)
101
102 self._LCTRL_problems.set_string_items()
103 self._LCTRL_meds.set_string_items()
104 self._LCTRL_history.set_string_items()
105
106 self._LCTRL_inbox.set_string_items()
107 self._LCTRL_results.set_string_items()
108 self._LCTRL_documents.set_string_items()
109
110
111
112
113
114
115
117
118 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
119 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
120
121
122 gmDispatcher.connect(signal = u'identity_mod_db', receiver = self._on_post_patient_selection)
123 gmDispatcher.connect(signal = u'name_mod_db', receiver = self._on_post_patient_selection)
124 gmDispatcher.connect(signal = u'comm_channel_mod_db', receiver = self._on_post_patient_selection)
125 gmDispatcher.connect(signal = u'job_mod_db', receiver = self._on_post_patient_selection)
126
127
128
129
130
131 gmDispatcher.connect(signal = u'episode_mod_db', receiver = self._on_episode_issue_mod_db)
132 gmDispatcher.connect(signal = u'health_issue_mod_db', receiver = self._on_episode_issue_mod_db)
133
134 gmDispatcher.connect(signal = u'substance_intake_mod_db', receiver = self._on_post_patient_selection)
135
136 gmDispatcher.connect(signal = u'hospital_stay_mod_db', receiver = self._on_post_patient_selection)
137 gmDispatcher.connect(signal = u'family_history_mod_db', receiver = self._on_post_patient_selection)
138 gmDispatcher.connect(signal = u'procedure_mod_db', receiver = self._on_post_patient_selection)
139 gmDispatcher.connect(signal = u'vacc_mod_db', receiver = self._on_post_patient_selection)
140
141 gmDispatcher.connect(signal = u'message_inbox_mod_db', receiver = self._on_post_patient_selection)
142 gmDispatcher.connect(signal = u'test_result_mod_db', receiver = self._on_post_patient_selection)
143 gmDispatcher.connect(signal = u'reviewed_test_results_mod_db', receiver = self._on_post_patient_selection)
144 gmDispatcher.connect(signal = u'doc_mod_db', receiver = self._on_post_patient_selection)
145
146
147
148
149
150 self._PRW_encounter_range.add_callback_on_selection(callback = self._on_encounter_range_selected)
151
154
156 wx.CallAfter(self._schedule_data_reget)
157
159 wx.CallAfter(self._schedule_data_reget)
160
162 wx.CallAfter(self._schedule_data_reget)
163
164
165
167 pat = gmPerson.gmCurrentPatient()
168 if not pat.connected:
169 self.__reset_ui_content()
170 return True
171
172 self.__refresh_identity(patient = pat)
173 self.__refresh_contacts(patient = pat)
174 self.__refresh_encounters(patient = pat)
175
176 self.__refresh_problems(patient = pat)
177 self.__refresh_meds(patient = pat)
178 self.__refresh_history(patient = pat)
179
180 self.__refresh_inbox(patient = pat)
181 self.__refresh_results(patient = pat)
182 self.__refresh_documents(patient = pat)
183
184 return True
185
186
187
189 list_items = []
190 list_data = []
191
192 emr = patient.get_emr()
193 most_recent = emr.get_most_recent_result()
194 if most_recent is None:
195 self._LCTRL_results.set_string_items(items = [])
196 self._LCTRL_results.set_data(data = [])
197 return
198
199 list_items.append(_('Latest: %s ago (%s %s %s %s%s)') % (
200 gmDateTime.format_interval_medically(gmDateTime.pydt_now_here() - most_recent['clin_when']),
201 most_recent['unified_abbrev'],
202 most_recent['unified_val'],
203 most_recent['val_unit'],
204 gmTools.coalesce(most_recent['abnormality_indicator'], u'', u' %s'),
205 gmTools.bool2subst(most_recent['reviewed'], u'', u' %s' % gmTools.u_writing_hand)
206 ))
207 list_data.append(most_recent)
208 most_recent_needs_red = False
209 if most_recent['is_technically_abnormal'] is True:
210 if most_recent['is_clinically_relevant']:
211 most_recent_needs_red = True
212 else:
213 if most_recent['abnormality_indicator'] not in [None, u'']:
214 most_recent_needs_red = True
215
216 unsigned = emr.get_unsigned_results(order_by = u"(trim(coalesce(abnormality_indicator), '') <> '') DESC NULLS LAST, unified_abbrev")
217 no_of_reds = 0
218 for result in unsigned:
219 if result['pk_test_result'] == most_recent['pk_test_result']:
220 continue
221 if result['abnormality_indicator'] is not None:
222 if result['abnormality_indicator'].strip() != u'':
223 no_of_reds += 1
224 list_items.append(_('%s %s %s %s (%s ago, %s)') % (
225 result['unified_abbrev'],
226 result['unified_val'],
227 result['val_unit'],
228 gmTools.coalesce(result['abnormality_indicator'], u'', u' %s'),
229 gmDateTime.format_interval_medically(gmDateTime.pydt_now_here() - result['clin_when']),
230 gmTools.u_writing_hand
231 ))
232 list_data.append(result)
233
234 self._LCTRL_results.set_string_items(items = list_items)
235 self._LCTRL_results.set_data(data = list_data)
236
237 if most_recent_needs_red:
238 self._LCTRL_results.SetItemTextColour(0, wx.NamedColour('RED'))
239 if no_of_reds > 0:
240 for idx in range(1, no_of_reds + 1):
241 self._LCTRL_results.SetItemTextColour(idx, wx.NamedColour('RED'))
242
245
247
248
249
250
251
252
253
254 wx.CallAfter(gmDispatcher.send, signal = 'display_widget', name = 'gmMeasurementsGridPlugin')
255 return
256
257
259 list_items = []
260 list_data = []
261
262 due_messages = patient.due_messages
263 no_of_dues = len(due_messages)
264 for msg in due_messages:
265 list_items.append(_('due %s: %s') % (
266 gmDateTime.format_interval_medically(msg['interval_due']),
267 gmTools.coalesce(msg['comment'], u'?')
268 ))
269 list_data.append(msg)
270
271 for msg in patient.messages:
272
273 if msg['is_due']:
274 continue
275
276 if msg['is_expired']:
277 continue
278 list_items.append(u'%s%s' % (
279 msg['l10n_type'],
280 gmTools.coalesce(msg['comment'], u'', u': %s')
281 ))
282 list_data.append(msg)
283
284 self._LCTRL_inbox.set_string_items(items = list_items)
285 self._LCTRL_inbox.set_data(data = list_data)
286
287 if no_of_dues > 0:
288 for idx in range(no_of_dues):
289 self._LCTRL_inbox.SetItemTextColour(idx, wx.NamedColour('RED'))
290
296
298
299
300
301
302
303
304
305 wx.CallAfter(gmDispatcher.send, signal = 'display_widget', name = 'gmProviderInboxPlugin')
306 return
307
308
310 doc_folder = patient.get_document_folder()
311
312 list_items = []
313 list_data = []
314
315 docs = doc_folder.get_unsigned_documents()
316 no_of_unsigned = len(docs)
317 for doc in docs:
318 list_items.append(u'%s %s (%s)' % (
319 gmDateTime.pydt_strftime(doc['clin_when'], format = '%m/%Y', accuracy = gmDateTime.acc_months),
320 doc['l10n_type'],
321 gmTools.u_writing_hand
322 ))
323 list_data.append(doc)
324
325 docs = doc_folder.get_documents(order_by = u'ORDER BY clin_when DESC', exclude_unsigned = True)
326 for doc in docs[:5]:
327 list_items.append(u'%s %s' % (
328 gmDateTime.pydt_strftime(doc['clin_when'], format = '%m/%Y', accuracy = gmDateTime.acc_months),
329 doc['l10n_type']
330 ))
331 list_data.append(doc)
332 if len(docs) > 5:
333 list_items.append(_('%s %s more not shown %s') % (
334 gmTools.u_ellipsis,
335 len(docs) - 5,
336 gmTools.u_ellipsis
337 ))
338 list_data.append(u'')
339
340 self._LCTRL_documents.set_string_items(items = list_items)
341 self._LCTRL_documents.set_data(data = list_data)
342
343 if no_of_unsigned > 0:
344 for idx in range(no_of_unsigned):
345 self._LCTRL_documents.SetItemTextColour(idx, wx.NamedColour('RED'))
346
354
370
371
373
374 cover_period = self._PRW_encounter_range.GetData()
375 if cover_period is None:
376 if self._PRW_encounter_range.GetValue().strip() != u'':
377 return
378
379 emr = patient.get_emr()
380
381 list_items = []
382 list_data = []
383
384 is_waiting = False
385 wlist = patient.get_waiting_list_entry()
386 if len(wlist) > 0:
387 is_waiting = True
388 w = wlist[0]
389 list_items.append(_('Currently in waiting list [%s]') % w['waiting_zone'])
390 list_data.append({'wlist': gmTools.coalesce(w['comment'], None)})
391
392 first = emr.get_first_encounter()
393 if first is not None:
394 list_items.append (
395 _('first: %s, %s') % (
396 gmDateTime.pydt_strftime (
397 first['started'],
398 format = '%Y %b %d',
399 accuracy = gmDateTime.acc_days
400 ),
401 first['l10n_type']
402 )
403 )
404 list_data.append(first)
405
406 last = emr.get_last_encounter()
407 if last is not None:
408 list_items.append (
409 _('last: %s, %s') % (
410 gmDateTime.pydt_strftime (
411 first['started'],
412 format = '%Y %b %d',
413 accuracy = gmDateTime.acc_days
414 ),
415 first['l10n_type']
416 )
417 )
418 list_data.append(last)
419
420 encs = emr.get_encounter_stats_by_type(cover_period = cover_period)
421 for enc in encs:
422 item = u'%s x %s' % (enc['frequency'], enc['l10n_type'])
423 list_items.append(item)
424 list_data.append(item)
425
426 stays = emr.get_hospital_stay_stats_by_hospital(cover_period = cover_period)
427 for stay in stays:
428 item = u'%s x %s' % (
429 stay['frequency'],
430 stay['hospital']
431 )
432 list_items.append(item)
433 list_data.append({'stay': item})
434
435 self._LCTRL_encounters.set_string_items(items = list_items)
436 self._LCTRL_encounters.set_data(data = list_data)
437 if is_waiting:
438 self._LCTRL_encounters.SetItemTextColour(0, wx.NamedColour('RED'))
439
460
480
481
482 - def __refresh_history(self, patient=None):
483 emr = patient.get_emr()
484
485 list_items = []
486 list_data = []
487
488 issues = [
489 i for i in emr.get_health_issues()
490 if ((i['clinically_relevant'] is False) or (i['is_active'] is False))
491 ]
492 for issue in issues:
493 last_encounter = emr.get_last_encounter(issue_id = issue['pk_health_issue'])
494 if last_encounter is None:
495 last = issue['modified_when'].strftime('%m/%Y')
496 else:
497 last = last_encounter['last_affirmed'].strftime('%m/%Y')
498 list_items.append(u'%s %s' % (last, issue['description']))
499 list_data.append(issue)
500 del issues
501
502 fhxs = emr.get_family_history()
503 for fhx in fhxs:
504 list_items.append(u'%s: %s%s' % (
505 fhx['l10n_relation'],
506 fhx['condition'],
507 gmTools.coalesce(fhx['age_noted'], u'', u' (@ %s)')
508 ))
509 list_data.append(fhx)
510 del fhxs
511
512 stays = emr.get_hospital_stays()
513 for stay in stays:
514 if stay['discharge'] is not None:
515 discharge = u''
516 else:
517 discharge = gmTools.u_ellipsis
518 list_items.append(u'%s%s %s: %s' % (
519 gmDateTime.pydt_strftime(stay['admission'], format = '%Y %b %d'),
520 discharge,
521 stay['hospital'],
522 stay['episode']
523 ))
524 list_data.append(stay)
525 del stays
526
527 procs = emr.get_performed_procedures()
528 for proc in procs:
529 list_items.append(u'%s%s %s' % (
530 gmDateTime.pydt_strftime(proc['clin_when'], format = '%Y %b %d'),
531 gmTools.bool2subst(proc['is_ongoing'], gmTools.u_ellipsis, u'', u''),
532 proc['performed_procedure']
533 ))
534 list_data.append(proc)
535 del procs
536
537 vaccs = emr.get_latest_vaccinations()
538 for ind, tmp in vaccs.items():
539 tmp, vacc = tmp
540 list_items.append(_('%s Vacc: %s') % (
541 gmDateTime.pydt_strftime(vacc['date_given'], format = '%Y %b %d'),
542 ind
543 ))
544 list_data.append(vacc)
545 del vaccs
546
547 self._LCTRL_history.set_string_items(items = list_items)
548 self._LCTRL_history.set_data(data = list_data)
549
551
552 if isinstance(data, gmEMRStructItems.cHealthIssue):
553 return data.format (
554 patient = gmPerson.gmCurrentPatient(),
555 with_medications = False,
556 with_hospital_stays = False,
557 with_procedures = False,
558 with_family_history = False,
559 with_documents = False,
560 with_tests = False,
561 with_vaccinations = False
562 ).strip(u'\n')
563
564 if isinstance(data, gmFamilyHistory.cFamilyHistory):
565 return data.format(include_episode = True, include_comment = True)
566
567 if isinstance(data, gmEMRStructItems.cHospitalStay):
568 return data.format()
569
570 if isinstance(data, gmEMRStructItems.cPerformedProcedure):
571 return data.format(include_episode = True)
572
573 if isinstance(data, gmVaccination.cVaccination):
574 return u'\n'.join(data.format (
575 with_indications = True,
576 with_comment = True,
577 with_reaction = True,
578 date_format = '%Y %b %d'
579 ))
580
581 return None
582
584 data = self._LCTRL_history.get_selected_item_data(only_one = True)
585 if data is None:
586 return
587
588
589 if wx.GetKeyState(wx.WXK_CONTROL):
590 if isinstance(data, gmEMRStructItems.cHealthIssue):
591 gmEMRStructWidgets.edit_health_issue(parent = self, issue = data)
592 return
593 if isinstance(data, gmFamilyHistory.cFamilyHistory):
594 FamilyHistoryWidgets.edit_family_history(parent = self, family_history = data)
595 return
596 if isinstance(data, gmEMRStructItems.cHospitalStay):
597 gmEMRStructWidgets.edit_hospital_stay(parent = self, hospital_stay = data)
598 return
599 if isinstance(data, gmEMRStructItems.cPerformedProcedure):
600 gmEMRStructWidgets.edit_procedure(parent = self, procedure = data)
601 return
602 if isinstance(data, gmVaccination.cVaccination):
603 gmVaccWidgets.edit_vaccination(parent = self, vaccination = data, single_entry = True)
604 return
605 return
606
607 if isinstance(data, gmEMRStructItems.cHealthIssue):
608 wx.CallAfter(gmDispatcher.send, signal = 'display_widget', name = 'gmEMRBrowserPlugin')
609 return
610 if isinstance(data, gmFamilyHistory.cFamilyHistory):
611 FamilyHistoryWidgets.manage_family_history(parent = self)
612 return
613 if isinstance(data, gmEMRStructItems.cHospitalStay):
614 gmEMRStructWidgets.manage_hospital_stays(parent = self)
615 return
616 if isinstance(data, gmEMRStructItems.cPerformedProcedure):
617 gmEMRStructWidgets.manage_performed_procedures(parent = self)
618 return
619 if isinstance(data, gmVaccination.cVaccination):
620 gmVaccWidgets.manage_vaccinations(parent = self)
621 return
622
623 return
624
625
627 emr = patient.get_emr()
628 list_items = []
629 meds = emr.get_current_substance_intake(include_inactive = False, include_unapproved = True, order_by = u'substance')
630 for med in meds:
631 list_items.append(_('%s %s %s%s') % (
632 med['substance'],
633 med['amount'],
634 med['unit'],
635 gmTools.coalesce (
636 med['schedule'],
637 u'',
638 u': %s'
639 )
640 ))
641 self._LCTRL_meds.set_string_items(items = list_items)
642 self._LCTRL_meds.set_data(data = meds)
643
656
666
667
712
761
781
782
784 emr = patient.get_emr()
785
786 problems = [
787 p for p in emr.get_problems(include_closed_episodes = False, include_irrelevant_issues = False)
788 if p['problem_active']
789 ]
790
791 list_items = []
792 for problem in problems:
793 if problem['type'] == 'issue':
794 issue = emr.problem2issue(problem)
795 last_encounter = emr.get_last_encounter(issue_id = issue['pk_health_issue'])
796 if last_encounter is None:
797 last = issue['modified_when'].strftime('%m/%Y')
798 else:
799 last = last_encounter['last_affirmed'].strftime('%m/%Y')
800 list_items.append(u'%s: %s' % (problem['problem'], last))
801
802 elif problem['type'] == 'episode':
803 epi = emr.problem2episode(problem)
804 last_encounter = emr.get_last_encounter(episode_id = epi['pk_episode'])
805 if last_encounter is None:
806 last = epi['episode_modified_when'].strftime('%m/%Y')
807 else:
808 last = last_encounter['last_affirmed'].strftime('%m/%Y')
809 list_items.append(u'%s: %s' % (problem['problem'], last))
810
811 self._LCTRL_problems.set_string_items(items = list_items)
812 self._LCTRL_problems.set_data(data = problems)
813
847
862
863
865
866 names = patient.get_names(exclude_active = True)
867 items = [
868 _('aka: %(last)s, %(first)s%(nick)s') % {
869 'last': n['lastnames'],
870 'first': n['firstnames'],
871 'nick': gmTools.coalesce(n['preferred'], u'', u" '%s'")
872 } for n in names
873 ]
874 data = names
875
876
877 ids = patient.external_ids
878 for i in ids:
879 items.append(u'%(name)s: %(value)s' % i)
880 data.append({'id': i})
881
882
883 jobs = patient.get_occupations()
884 for j in jobs:
885 items.append(_('job: %s') % j['l10n_occupation'])
886 data.append({'job': j})
887
888 self._LCTRL_identity.set_string_items(items = items)
889 self._LCTRL_identity.set_data(data = data)
890
908
910 data = self._LCTRL_identity.get_selected_item_data(only_one = True)
911 if data is not None:
912
913 if wx.GetKeyState(wx.WXK_CONTROL):
914 if isinstance(data, gmPerson.cPersonName):
915 ea = gmDemographicsWidgets.cPersonNameEAPnl(self, -1, name = data)
916 dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True)
917 dlg.SetTitle(_('Cloning name'))
918 dlg.ShowModal()
919 return
920 if isinstance(data, type({})):
921 key = data.keys()[0]
922 val = data[key]
923 if key == 'id':
924 ea = gmDemographicsWidgets.cExternalIDEditAreaPnl(self, -1, external_id = val)
925 ea.identity = gmPerson.gmCurrentPatient()
926 dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True)
927 dlg.SetTitle(_('Editing external ID'))
928 dlg.ShowModal()
929 return
930 if key == 'job':
931 gmDemographicsWidgets.edit_occupation()
932 return
933
934 wx.CallAfter(gmDispatcher.send, signal = 'display_widget', name = 'gmNotebookedPatientEditionPlugin')
935
936
937
938 if __name__ == "__main__":
939
940 if len(sys.argv) < 2:
941 sys.exit()
942
943 if sys.argv[1] != u'test':
944 sys.exit()
945
946
947
948
949
950
951
952
953