Package Gnumed :: Package wxpython :: Module gmPatOverviewWidgets
[frames] | no frames]

Source Code for Module Gnumed.wxpython.gmPatOverviewWidgets

   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  from Gnumed.business import gmExternalCare 
  31  from Gnumed.business import gmAutoHints 
  32  from Gnumed.business import gmMedication 
  33   
  34  from Gnumed.wxpython import gmRegetMixin 
  35  from Gnumed.wxpython import gmDemographicsWidgets 
  36  from Gnumed.wxpython import gmContactWidgets 
  37  from Gnumed.wxpython import gmMedicationWidgets 
  38  from Gnumed.wxpython import gmEditArea 
  39  from Gnumed.wxpython import gmEMRStructWidgets 
  40  from Gnumed.wxpython import gmEncounterWidgets 
  41  from Gnumed.wxpython import gmFamilyHistoryWidgets 
  42  from Gnumed.wxpython import gmVaccWidgets 
  43  from Gnumed.wxpython import gmDocumentWidgets 
  44  from Gnumed.wxpython import gmGuiHelpers 
  45  from Gnumed.wxpython import gmPregWidgets 
  46  from Gnumed.wxpython import gmHabitWidgets 
  47  from Gnumed.wxpython import gmHospitalStayWidgets 
  48  from Gnumed.wxpython import gmProcedureWidgets 
  49   
  50   
  51  _log = logging.getLogger('gm.patient') 
  52  #============================================================ 
  53  from Gnumed.wxGladeWidgets import wxgPatientOverviewPnl 
  54   
55 -class cPatientOverviewPnl(wxgPatientOverviewPnl.wxgPatientOverviewPnl, gmRegetMixin.cRegetOnPaintMixin):
56
57 - def __init__(self, *args, **kwargs):
58 wxgPatientOverviewPnl.wxgPatientOverviewPnl.__init__(self, *args, **kwargs) 59 gmRegetMixin.cRegetOnPaintMixin.__init__(self) 60 61 self.__init_ui() 62 self.__register_interests()
63 64 #-------------------------------------------------------- 65 # internal API 66 #--------------------------------------------------------
67 - def __init_ui(self):
68 69 #self._LCTRL_history.debug = u'LCTRL_history_sizing' 70 71 # left 72 self._LCTRL_identity.set_columns(columns = ['']) 73 self._LCTRL_identity.item_tooltip_callback = self._calc_identity_item_tooltip 74 self._LCTRL_identity.activate_callback = self._on_identity_item_activated 75 76 self._LCTRL_contacts.set_columns(columns = ['']) 77 self._LCTRL_contacts.item_tooltip_callback = self._calc_contacts_list_item_tooltip 78 self._LCTRL_contacts.activate_callback = self._on_contacts_item_activated 79 80 self._LCTRL_encounters.set_columns(columns = ['']) 81 self._LCTRL_encounters.item_tooltip_callback = self._calc_encounters_list_item_tooltip 82 self._LCTRL_encounters.activate_callback = self._on_encounter_activated 83 84 # middle 85 self._LCTRL_meds.set_columns(columns = ['']) 86 self._LCTRL_meds.item_tooltip_callback = self._calc_meds_list_item_tooltip 87 self._LCTRL_meds.activate_callback = self._on_meds_item_activated 88 89 self._LCTRL_problems.set_columns(columns = ['']) 90 self._LCTRL_problems.item_tooltip_callback = self._calc_problem_list_item_tooltip 91 self._LCTRL_problems.activate_callback = self._on_problem_activated 92 93 self._LCTRL_history.set_columns(columns = ['']) 94 self._LCTRL_history.item_tooltip_callback = self._calc_history_list_item_tooltip 95 self._LCTRL_history.activate_callback = self._on_history_item_activated 96 97 # right hand side 98 self._LCTRL_inbox.set_columns(columns = ['']) 99 self._LCTRL_inbox.item_tooltip_callback = self._calc_inbox_item_tooltip 100 self._LCTRL_inbox.activate_callback = self._on_inbox_item_activated 101 102 self._LCTRL_results.set_columns(columns = ['']) 103 self._LCTRL_results.item_tooltip_callback = self._calc_results_list_item_tooltip 104 self._LCTRL_results.activate_callback = self._on_result_activated 105 106 self._LCTRL_documents.set_columns(columns = ['']) 107 self._LCTRL_documents.item_tooltip_callback = self._calc_documents_list_item_tooltip 108 self._LCTRL_documents.activate_callback = self._on_document_activated
109 110 #--------------------------------------------------------
111 - def __reset_ui_content(self):
112 self._LCTRL_identity.set_string_items() 113 self._LCTRL_contacts.set_string_items() 114 self._LCTRL_encounters.set_string_items() 115 116 self._LCTRL_problems.set_string_items() 117 self._LCTRL_meds.set_string_items() 118 self._LCTRL_history.set_string_items() 119 120 self._LCTRL_inbox.set_string_items() 121 self._LCTRL_results.set_string_items() 122 self._LCTRL_documents.set_string_items()
123 124 #----------------------------------------------------- 125 # event handling 126 #-----------------------------------------------------
127 - def __register_interests(self):
128 # client internal signals 129 gmDispatcher.connect(signal = 'pre_patient_unselection', receiver = self._on_pre_patient_unselection) 130 gmDispatcher.connect(signal = 'post_patient_selection', receiver = self._on_post_patient_selection) 131 132 # generic database change signal 133 gmDispatcher.connect(signal = 'gm_table_mod', receiver = self._on_database_signal) 134 135 # database change signals 136 # no signal for external IDs yet 137 # no signal for address yet 138 ##gmDispatcher.connect(signal = u'current_encounter_modified', receiver = self._on_current_encounter_modified) 139 ##gmDispatcher.connect(signal = u'current_encounter_switched', receiver = self._on_current_encounter_switched) 140 141 # doesn't have pk_identity: 142 gmDispatcher.connect(signal = 'clin.reviewed_test_results_mod_db', receiver = self._on_post_patient_selection)
143 144 # synchronous signals 145 # self.__pat.register_before_switching_from_patient_callback(callback = self._before_switching_from_patient_callback) 146 # gmDispatcher.send(signal = u'register_pre_exit_callback', callback = self._pre_exit_callback) 147 148 #--------------------------------------------------------
150 # only empty out here, do NOT access the patient 151 # or else we will access the old patient while it 152 # may not be valid anymore ... 153 self.__reset_ui_content()
154 155 #--------------------------------------------------------
156 - def _on_post_patient_selection(self):
157 self._schedule_data_reget()
158 159 #--------------------------------------------------------
160 - def _on_database_signal(self, **kwds):
161 162 pat = gmPerson.gmCurrentPatient() 163 if not pat.connected: 164 return True 165 166 if kwds['pk_identity'] != pat.ID: 167 return True 168 169 if kwds['table'] == 'dem.identity': 170 if kwds['operation'] != 'UPDATE': 171 return True 172 173 if kwds['table'] in [ 174 'blobs.doc_med', 175 'clin.episode', 176 'clin.health_issue', 177 'clin.suppressed_hint', 178 'clin.substance_intake', 179 'clin.hospital_stay', 180 'clin.procedure', 181 'clin.vaccination', 182 'clin.family_history', 183 'clin.test_result', 184 'clin.export_item', 185 'clin.external_care', 186 'dem.identity', 187 'dem.names', 188 'dem.lnk_identity2comm', 189 'dem.lnk_job2person', 190 'dem.message_inbox', 191 'ref.auto_hint' 192 ]: 193 self._schedule_data_reget() 194 return True 195 196 return True
197 198 #----------------------------------------------------- 199 # reget-on-paint mixin API 200 #-----------------------------------------------------
201 - def _populate_with_data(self):
202 pat = gmPerson.gmCurrentPatient() 203 if not pat.connected: 204 self.__reset_ui_content() 205 return True 206 207 self.__refresh_identity(patient = pat) 208 self.__refresh_contacts(patient = pat) 209 self.__refresh_encounters(patient = pat) 210 211 self.__refresh_problems(patient = pat) 212 self.__refresh_meds(patient = pat) 213 self.__refresh_history(patient = pat) 214 215 self.__refresh_inbox(patient = pat) 216 self.__refresh_results(patient = pat) 217 self.__refresh_documents(patient = pat) 218 219 return True
220 221 #----------------------------------------------------- 222 # internal helpers 223 #-----------------------------------------------------
224 - def __refresh_results(self, patient=None):
225 226 emr = patient.emr 227 most_recent = emr.get_most_recent_results_for_patient() 228 if len(most_recent) == 0: 229 self._LCTRL_results.set_string_items(items = []) 230 self._LCTRL_results.set_data(data = []) 231 return 232 most_recent = most_recent[0] 233 234 list_items = [] 235 list_data = [] 236 now = gmDateTime.pydt_now_here() 237 238 list_items.append(_('Most recent lab work: %s ago (%s)') % ( 239 gmDateTime.format_interval_medically(now - most_recent['clin_when']), 240 gmDateTime.pydt_strftime(most_recent['clin_when'], format = '%Y %b %d') 241 )) 242 list_data.append(most_recent) 243 244 unsigned = emr.get_unsigned_results(order_by = "(trim(coalesce(abnormality_indicator), '') <> '') DESC NULLS LAST, unified_abbrev") 245 no_of_reds = 0 246 for result in unsigned: 247 if result['abnormality_indicator'] is not None: 248 if result['abnormality_indicator'].strip() != '': 249 no_of_reds += 1 250 list_items.append(_('%s %s%s%s (%s ago, %s)') % ( 251 result['unified_abbrev'], 252 result['unified_val'], 253 gmTools.coalesce(result['val_unit'], '', ' %s'), 254 gmTools.coalesce(result['abnormality_indicator'], '', ' %s'), 255 gmDateTime.format_interval_medically(gmDateTime.pydt_now_here() - result['clin_when']), 256 gmTools.u_writing_hand 257 )) 258 list_data.append(result) 259 260 self._LCTRL_results.set_string_items(items = list_items) 261 self._LCTRL_results.set_data(data = list_data) 262 263 if no_of_reds > 0: 264 for idx in range(1, no_of_reds + 1): 265 self._LCTRL_results.SetItemTextColour(idx, wx.Colour('RED'))
266 267 #-----------------------------------------------------
268 - def _calc_results_list_item_tooltip(self, data):
269 return data.format()
270 271 #-----------------------------------------------------
272 - def _on_result_activated(self, event):
273 # data = self._LCTRL_inbox.get_selected_item_data(only_one = True) 274 # 275 # if data is not None: 276 # # <ctrl> down ? 277 # if wx.GetKeyState(wx.WXK_CONTROL): 278 # if isinstance(data, gmProviderInbox.cInboxMessage): 279 # xxxxxxxxx 280 gmDispatcher.send(signal = 'display_widget', name = 'gmMeasurementsGridPlugin') 281 return
282 283 #----------------------------------------------------- 284 #-----------------------------------------------------
285 - def __refresh_inbox(self, patient=None):
286 list_items = [] 287 list_data = [] 288 highlight_list = [] 289 line_idx = -1 290 291 overdue_messages = patient.overdue_messages 292 if len(overdue_messages) > 0: 293 highlight_list.extend(range(len(overdue_messages))) 294 for msg in overdue_messages: 295 line_idx += 1 296 list_items.append(_('overdue %s: %s') % ( 297 gmDateTime.format_interval_medically(msg['interval_due']), 298 gmTools.coalesce(msg['comment'], '?') 299 )) 300 list_data.append(msg) 301 302 for msg in patient.get_messages(order_by = 'due_date NULLS LAST, importance DESC, received_when DESC'): 303 # already displayed above ? 304 if msg['is_overdue']: 305 continue 306 # not relevant anymore ? 307 if msg['is_expired']: 308 continue 309 line_idx += 1 310 if msg['due_date'] is None: 311 label = '%s%s' % ( 312 msg['l10n_type'], 313 gmTools.coalesce(msg['comment'], '', ': %s') 314 ) 315 else: 316 label = _('due in %s%s') % ( 317 gmDateTime.format_interval_medically(msg['interval_due']), 318 gmTools.coalesce(msg['comment'], '', ': %s') 319 ) 320 list_items.append(label) 321 list_data.append(msg) 322 323 pk_enc = patient.emr.active_encounter['pk_encounter'] 324 for hint in patient._get_dynamic_hints(pk_encounter = pk_enc): 325 line_idx += 1 326 list_items.append(hint['title']) 327 list_data.append(hint) 328 if hint['highlight_as_priority']: 329 highlight_list.append(line_idx) 330 331 hints = patient.suppressed_hints 332 if len(hints) > 0: 333 list_items.append((_("suppr'd (%s):") % len(hints)) + ' ' + ','.join([h['title'][:7] + gmTools.u_ellipsis for h in hints])) 334 list_data.append(_('Suppressed hints:\n') + '\n'.join(['%s: %s' % (hints.index(h) + 1, h['title']) for h in hints])) 335 336 self._LCTRL_inbox.set_string_items(items = list_items) 337 self._LCTRL_inbox.set_data(data = list_data) 338 339 for idx in highlight_list: 340 self._LCTRL_inbox.SetItemTextColour(idx, wx.Colour('RED'))
341 342 #-----------------------------------------------------
343 - def _calc_inbox_item_tooltip(self, data):
344 if isinstance(data, gmProviderInbox.cInboxMessage): 345 return data.format() 346 347 if isinstance(data, gmAutoHints.cDynamicHint): 348 return '%s\n\n%s%s\n\n%s %s' % ( 349 data['title'], 350 gmTools.wrap(data['hint'], width = 50), 351 gmTools.wrap(gmTools.coalesce(data['recommendation'], '', '\n\n%s'), width = 50), 352 gmTools.wrap(gmTools.coalesce(data['url'], '', '%s\n\n'), width = 50), 353 data['source'] 354 ) 355 356 if isinstance(data, type('')): 357 return data 358 359 return None
360 361 #-----------------------------------------------------
362 - def _on_inbox_item_activated(self, event):
363 364 data = self._LCTRL_inbox.get_selected_item_data(only_one = True) 365 366 # if it is a dynamic hint open the URL for that 367 if isinstance(data, gmAutoHints.cDynamicHint): 368 if data['url'] is not None: 369 gmNetworkTools.open_url_in_browser(data['url']) 370 return 371 372 # holding down <CTRL> when double-clicking an inbox 373 # item indicates the desire to delete it 374 # <ctrl> down ? 375 if wx.GetKeyState(wx.WXK_CONTROL): 376 # better safe than sorry: can only delete real inbox items 377 if data is None: 378 return 379 if not isinstance(data, gmProviderInbox.cInboxMessage): 380 return 381 delete_it = gmGuiHelpers.gm_show_question ( 382 question = _('Do you really want to\ndelete this inbox message ?'), 383 title = _('Deleting inbox message') 384 ) 385 if not delete_it: 386 return 387 gmProviderInbox.delete_inbox_message(inbox_message = data['pk_inbox_message']) 388 return 389 390 if data is None: 391 gmDispatcher.send(signal = 'display_widget', name = 'gmProviderInboxPlugin') 392 return 393 394 if not isinstance(data, gmProviderInbox.cInboxMessage): 395 gmDispatcher.send(signal = 'display_widget', name = 'gmProviderInboxPlugin') 396 return 397 398 gmDispatcher.send(signal = 'display_widget', name = 'gmProviderInboxPlugin', filter_by_active_patient = True) 399 return
400 #----------------------------------------------------- 401 #-----------------------------------------------------
402 - def __refresh_documents(self, patient=None):
403 404 list_items = [] 405 list_data = [] 406 407 # export area items 408 item_count = len(patient.export_area.items) 409 if item_count == 1: 410 list_items.append(_('Export area: 1 item')) 411 list_data.append('') 412 if item_count > 1: 413 list_items.append(_('Export area: %s items') % item_count) 414 list_data.append('') 415 416 doc_folder = patient.get_document_folder() 417 418 # unsigned docs first 419 docs = doc_folder.get_unsigned_documents() 420 no_of_unsigned = len(docs) 421 for doc in docs: 422 list_items.append('%s %s (%s)' % ( 423 gmDateTime.pydt_strftime(doc['clin_when'], format = '%m/%Y', accuracy = gmDateTime.acc_months), 424 doc['l10n_type'], 425 gmTools.u_writing_hand 426 )) 427 list_data.append(doc) 428 429 # other, signed docs second 430 docs = doc_folder.get_documents(order_by = 'ORDER BY clin_when DESC', exclude_unsigned = True) 431 for doc in docs[:5]: 432 list_items.append('%s %s' % ( 433 gmDateTime.pydt_strftime(doc['clin_when'], format = '%m/%Y', accuracy = gmDateTime.acc_months), 434 doc['l10n_type'] 435 )) 436 list_data.append(doc) 437 if len(docs) > 5: 438 list_items.append(_('%s %s more not shown %s') % ( 439 gmTools.u_ellipsis, 440 len(docs) - 5, 441 gmTools.u_ellipsis 442 )) 443 list_data.append('') 444 445 self._LCTRL_documents.set_string_items(items = list_items) 446 self._LCTRL_documents.set_data(data = list_data) 447 448 if no_of_unsigned > 0: 449 start_idx = 0 450 if item_count > 0: 451 start_idx = 1 452 end_idx = no_of_unsigned + start_idx 453 for idx in range(start_idx, end_idx): 454 self._LCTRL_documents.SetItemTextColour(idx, wx.Colour('RED'))
455 #-----------------------------------------------------
456 - def _calc_documents_list_item_tooltip(self, data):
457 emr = gmPerson.gmCurrentPatient().emr 458 459 if isinstance(data, gmDocuments.cDocument): 460 return data.format() 461 462 return None
463 #-----------------------------------------------------
464 - def _on_document_activated(self, event):
465 data = self._LCTRL_documents.get_selected_item_data(only_one = True) 466 467 if data is not None: 468 # <ctrl> down ? 469 if wx.GetKeyState(wx.WXK_CONTROL): 470 if isinstance(data, gmDocuments.cDocument): 471 if len(data.parts) > 0: 472 gmDocumentWidgets.display_document_part(parent = self, part = data.parts[0]) 473 else: 474 gmDocumentWidgets.review_document(parent = self, document = data) 475 return 476 477 gmDispatcher.send(signal = 'display_widget', name = 'gmShowMedDocs') 478 return
479 #----------------------------------------------------- 480 #-----------------------------------------------------
481 - def __refresh_encounters(self, patient=None):
482 483 emr = patient.emr 484 485 list_items = [] 486 list_data = [] 487 488 is_waiting = False 489 wlist = patient.get_waiting_list_entry() 490 if len(wlist) > 0: 491 is_waiting = True 492 list_items.append(_('Currently %s entries in waiting list') % len(wlist)) 493 tt = [] 494 for w in wlist: 495 tt.append('%s %s%s%s' % ( 496 gmTools.u_triangular_bullet, 497 gmDateTime.format_interval_medically(w['waiting_time']), 498 gmTools.coalesce(w['waiting_zone'], '', ' in "%s"'), 499 gmTools.coalesce(w['comment'], '', ': %s') 500 )) 501 if len(tt) > 0: 502 tt = '\n'.join(tt) 503 else: 504 tt = None 505 list_data.append({'wlist': tt}) 506 507 first = emr.get_first_encounter() 508 if first is not None: 509 list_items.append ( 510 _('first (in GMd): %s, %s') % ( 511 gmDateTime.pydt_strftime ( 512 first['started'], 513 format = '%Y %b %d', 514 accuracy = gmDateTime.acc_days 515 ), 516 first['l10n_type'] 517 ) 518 ) 519 list_data.append(first) 520 521 last = emr.get_last_but_one_encounter() 522 if last is not None: 523 list_items.append ( 524 _('last: %s, %s') % ( 525 gmDateTime.pydt_strftime ( 526 last['started'], 527 format = '%Y %b %d', 528 accuracy = gmDateTime.acc_days 529 ), 530 last['l10n_type'] 531 ) 532 ) 533 list_data.append(last) 534 535 encs = emr.get_encounter_stats_by_type() 536 for enc in encs: 537 item = ' %s x %s' % (enc['frequency'], enc['l10n_type']) 538 list_items.append(item) 539 list_data.append(item) 540 541 stays = emr.get_hospital_stay_stats_by_hospital() 542 for stay in stays: 543 item = ' %s x %s' % ( 544 stay['frequency'], 545 stay['hospital'] 546 ) 547 list_items.append(item) 548 list_data.append({'stay': item}) 549 550 self._LCTRL_encounters.set_string_items(items = list_items) 551 self._LCTRL_encounters.set_data(data = list_data) 552 if is_waiting: 553 self._LCTRL_encounters.SetItemTextColour(0, wx.Colour('RED'))
554 555 #-----------------------------------------------------
556 - def _calc_encounters_list_item_tooltip(self, data):
557 emr = gmPerson.gmCurrentPatient().emr 558 559 if isinstance(data, gmEMRStructItems.cEncounter): 560 return data.format ( 561 with_vaccinations = False, 562 with_tests = False, 563 with_docs = False, 564 with_co_encountlet_hints = True, 565 with_rfe_aoe = True 566 ) 567 568 if type(data) is dict: 569 key, val = list(data.items())[0] 570 if key == 'wlist': 571 return val 572 if key == 'stay': 573 return None 574 575 return data
576 #-----------------------------------------------------
577 - def _on_encounter_activated(self, event):
578 data = self._LCTRL_encounters.get_selected_item_data(only_one = True) 579 if data is not None: 580 # <ctrl> down ? 581 if wx.GetKeyState(wx.WXK_CONTROL): 582 if isinstance(data, gmEMRStructItems.cEncounter): 583 gmEncounterWidgets.edit_encounter(parent = self, encounter = data) 584 return 585 586 if type(data) is dict: 587 key, val = list(data.items())[0] 588 if key == 'wlist': 589 gmDispatcher.send(signal = 'display_widget', name = 'gmWaitingListPlugin') 590 return 591 if key == 'stay': 592 wx.CallAfter(gmHospitalStayWidgets.manage_hospital_stays, parent = self) 593 return 594 595 wx.CallAfter(gmEncounterWidgets.manage_encounters, parent = self, ignore_OK_button = False)
596 597 #----------------------------------------------------- 598 #-----------------------------------------------------
599 - def __refresh_history(self, patient=None):
600 emr = patient.emr 601 602 sort_key_list = [] 603 date_format4sorting = '%Y %m %d %H %M %S' 604 now = gmDateTime.pydt_now_here() 605 data = {} 606 607 # undated entries 608 # pregnancy 609 edc = emr.EDC 610 if edc is not None: 611 sort_key = '99999 edc' 612 if emr.EDC_is_fishy: 613 label = _('EDC (!?!): %s') % gmDateTime.pydt_strftime(edc, format = '%Y %b %d') 614 tt = _( 615 'The Expected Date of Confinement is rather questionable.\n' 616 '\n' 617 'Please check patient age, patient gender, time until/since EDC.' 618 ) 619 else: 620 label = _('EDC: %s') % gmDateTime.pydt_strftime(edc, format = '%Y %b %d') 621 tt = '' 622 sort_key_list.append(sort_key) 623 data[sort_key] = [label, tt] 624 625 # family history 626 fhxs = emr.get_family_history() 627 for fhx in fhxs: 628 sort_key = '99998 %s::%s' % (fhx['l10n_relation'], fhx['pk_family_history']) 629 sort_key_list.append(sort_key) 630 #gmDateTime.pydt_strftime(fhx['when_known_to_patient'], format = '%Y %m %d %H %M %S') 631 label = '%s%s: %s' % (fhx['l10n_relation'], gmTools.coalesce(fhx['age_noted'], '', ' (@ %s)'), fhx['condition']) 632 data[sort_key] = [label, fhx] 633 del fhxs 634 635 # dated entries 636 issues = [ 637 i for i in emr.get_health_issues() 638 if ((i['clinically_relevant'] is False) or (i['is_active'] is False)) 639 ] 640 for issue in issues: 641 last_encounter = emr.get_last_encounter(issue_id = issue['pk_health_issue']) 642 linked_encounter = gmEMRStructItems.cEncounter(issue['pk_encounter']) 643 when_candidates = [issue['modified_when'], linked_encounter['last_affirmed']] 644 if last_encounter is not None: 645 when_candidates.append(last_encounter['last_affirmed']) 646 if (patient['dob'] is not None) and (issue['age_noted'] is not None): 647 when_candidates.append(patient['dob'] + issue['age_noted']) 648 if issue['is_active']: 649 # sort active issues by time of most recent clinical access, which 650 # means the most recent of: 651 # issue.modified_when 652 # last_encounter.last_affirmed 653 # linked_encounter.last_affirmed 654 # dob + age 655 relevant_date = max(when_candidates) 656 else: 657 # sort IN-active issues by best guess of real clinical start 658 # means either: 659 # - dob + age 660 # or the earliest of: 661 # - issue.modified_when 662 # - last_encounter.last_affirmed 663 # - linked_encounter.last_affirmed 664 if (patient['dob'] is not None) and (issue['age_noted'] is not None): 665 relevant_date = patient['dob'] + issue['age_noted'] 666 else: 667 relevant_date = min(when_candidates) 668 sort_key = '%s::%s' % (gmDateTime.pydt_strftime(relevant_date, format = date_format4sorting), issue['pk_health_issue']) 669 relevant_date_str = gmDateTime.pydt_strftime(relevant_date, format = '%Y %b') 670 if issue['age_noted'] is None: 671 encounter = gmEMRStructItems.cEncounter(issue['pk_encounter']) 672 age = _(' (entered %s ago)') % gmDateTime.format_interval_medically(now - encounter['started']) 673 else: 674 age = ' (@ %s)' % gmDateTime.format_interval_medically(issue['age_noted']) 675 sort_key_list.append(sort_key) 676 data[sort_key] = ['%s %s%s' % (relevant_date_str, issue['description'], age), issue] 677 del issues 678 679 stays = emr.get_hospital_stays() 680 for stay in stays: 681 sort_key = '%s::%s' % (gmDateTime.pydt_strftime(stay['admission'], format = date_format4sorting), stay['pk_hospital_stay']) 682 label = '%s %s: %s (%s)' % ( 683 gmDateTime.pydt_strftime(stay['admission'], format = '%Y %b'), 684 stay['hospital'], 685 stay['episode'], 686 _('%s ago') % gmDateTime.format_interval_medically(now - stay['admission']) 687 ) 688 sort_key_list.append(sort_key) 689 data[sort_key] = [label, stay] 690 del stays 691 692 procs = emr.get_performed_procedures() 693 for proc in procs: 694 sort_key = '%s::%s' % (gmDateTime.pydt_strftime(proc['clin_when'], format = date_format4sorting), proc['pk_procedure']) 695 label = '%s%s %s (%s @ %s)' % ( 696 gmDateTime.pydt_strftime(proc['clin_when'], format = '%Y %b'), 697 gmTools.bool2subst(proc['is_ongoing'], gmTools.u_ellipsis, '', ''), 698 proc['performed_procedure'], 699 _('%s ago') % gmDateTime.format_interval_medically(now - proc['clin_when']), 700 gmDateTime.format_interval_medically(proc['clin_when'] - patient['dob']) 701 ) 702 sort_key_list.append(sort_key) 703 data[sort_key] = [label, proc] 704 del procs 705 706 vaccs = emr.get_latest_vaccinations() 707 for ind, tmp in vaccs.items(): 708 no_of_shots, vacc = tmp 709 sort_key = '%s::%s::%s' % (gmDateTime.pydt_strftime(vacc['date_given'], format = date_format4sorting), vacc['pk_vaccination'], ind) 710 label = _('%s Vacc: %s (latest of %s: %s ago)') % ( 711 gmDateTime.pydt_strftime(vacc['date_given'], format = '%Y %b'), 712 ind, 713 no_of_shots, 714 gmDateTime.format_interval_medically(now - vacc['date_given']) 715 ) 716 sort_key_list.append(sort_key) 717 data[sort_key] = [label, vacc] 718 del vaccs 719 720 for abuse in [ a for a in emr.abused_substances if a['harmful_use_type'] == 3 ]: 721 sort_key = '%s::%s' % (gmDateTime.pydt_strftime(abuse['last_checked_when'], format = date_format4sorting), abuse['substance']) 722 label = _('Hx of addiction: %s') % abuse['substance'] 723 sort_key_list.append(sort_key) 724 data[sort_key] = [label, abuse] 725 726 sort_key_list.sort() 727 sort_key_list.reverse() 728 list_items = [] 729 list_data = [] 730 for key in sort_key_list: 731 label, item = data[key] 732 list_items.append(label) 733 list_data.append(item) 734 735 self._LCTRL_history.set_string_items(items = list_items) 736 self._LCTRL_history.set_data(data = list_data)
737 738 #-----------------------------------------------------
739 - def _calc_history_list_item_tooltip(self, data):
740 741 if isinstance(data, gmEMRStructItems.cHealthIssue): 742 return data.format ( 743 patient = gmPerson.gmCurrentPatient(), 744 with_medications = False, 745 with_hospital_stays = False, 746 with_procedures = False, 747 with_family_history = False, 748 with_documents = False, 749 with_tests = False, 750 with_vaccinations = False 751 ).strip('\n') 752 753 if isinstance(data, gmMedication.cSubstanceIntakeEntry): 754 return data.format(single_line = False) 755 756 if isinstance(data, gmFamilyHistory.cFamilyHistory): 757 return data.format(include_episode = True, include_comment = True) 758 759 if isinstance(data, gmEMRStructItems.cHospitalStay): 760 return data.format() 761 762 if isinstance(data, gmEMRStructItems.cPerformedProcedure): 763 return data.format(include_episode = True, include_codes = False, include_address = True, include_comm = True) 764 765 if isinstance(data, gmVaccination.cVaccination): 766 return '\n'.join(data.format ( 767 with_indications = True, 768 with_comment = True, 769 with_reaction = True, 770 date_format = '%Y %b %d' 771 )) 772 773 # EDC 774 if isinstance(data, str): 775 if data == '': 776 return None 777 return data 778 779 return None
780 781 #-----------------------------------------------------
782 - def _on_history_item_activated(self, event):
783 data = self._LCTRL_history.get_selected_item_data(only_one = True) 784 if data is None: 785 return 786 787 if isinstance(data, str): 788 gmPregWidgets.calculate_edc(parent = self, patient = gmPerson.gmCurrentPatient()) 789 return 790 791 # <ctrl> down ? 792 if wx.GetKeyState(wx.WXK_CONTROL): 793 if isinstance(data, gmEMRStructItems.cHealthIssue): 794 gmEMRStructWidgets.edit_health_issue(parent = self, issue = data) 795 return 796 if isinstance(data, gmFamilyHistory.cFamilyHistory): 797 FamilyHistoryWidgets.edit_family_history(parent = self, family_history = data) 798 return 799 if isinstance(data, gmEMRStructItems.cHospitalStay): 800 gmHospitalStayWidgets.edit_hospital_stay(parent = self, hospital_stay = data) 801 return 802 if isinstance(data, gmEMRStructItems.cPerformedProcedure): 803 gmProcedureWidgets.edit_procedure(parent = self, procedure = data) 804 return 805 if isinstance(data, gmVaccination.cVaccination): 806 gmVaccWidgets.edit_vaccination(parent = self, vaccination = data, single_entry = True) 807 return 808 return 809 810 if isinstance(data, gmEMRStructItems.cHealthIssue): 811 gmDispatcher.send(signal = 'display_widget', name = 'gmEMRBrowserPlugin') 812 return 813 if isinstance(data, gmFamilyHistory.cFamilyHistory): 814 FamilyHistoryWidgets.manage_family_history(parent = self) 815 return 816 if isinstance(data, gmEMRStructItems.cHospitalStay): 817 gmHospitalStayWidgets.manage_hospital_stays(parent = self) 818 return 819 if isinstance(data, gmEMRStructItems.cPerformedProcedure): 820 gmProcedureWidgets.manage_performed_procedures(parent = self) 821 return 822 if isinstance(data, gmVaccination.cVaccination): 823 gmVaccWidgets.manage_vaccinations(parent = self) 824 return 825 826 return
827 #----------------------------------------------------- 828 #-----------------------------------------------------
829 - def __refresh_meds(self, patient=None):
830 831 emr = patient.emr 832 833 list_items = [] 834 data_items = [] 835 first_red = False 836 837 # harmful substance use ? 838 abuses = emr.abused_substances 839 if len([ a for a in abuses if a['harmful_use_type'] in [1, 2] ]) > 0: 840 list_items.append(_('active substance abuse')) 841 data_items.append('\n'.join([ a.format(left_margin=0, date_format='%Y %b %d', single_line=True) for a in abuses ])) 842 843 # list by product or substance: 844 intakes = emr.get_current_medications(include_inactive = False, include_unapproved = True, order_by = 'substance') 845 multi_products_already_seen = [] 846 for intake in intakes: 847 drug = intake.containing_drug 848 if len(drug['components']) == 1: 849 list_items.append(_('%s %s%s%s') % ( 850 intake['substance'], 851 intake['amount'], 852 intake.formatted_units, 853 gmTools.coalesce(intake['schedule'], '', ': %s') 854 )) 855 data_items.append(intake) 856 else: 857 if intake['product'] in multi_products_already_seen: 858 continue 859 multi_products_already_seen.append(intake['product']) 860 list_items.append(_('%s %s%s') % ( 861 intake['product'], 862 drug['l10n_preparation'], 863 gmTools.coalesce(intake['schedule'], '', ': %s') 864 )) 865 data_items.append(intake) 866 867 self._LCTRL_meds.set_string_items(items = list_items) 868 self._LCTRL_meds.set_data(data = data_items) 869 870 if first_red: 871 self._LCTRL_meds.SetItemTextColour(0, wx.Colour('RED'))
872 873 #-----------------------------------------------------
874 - def _calc_meds_list_item_tooltip(self, data):
875 if isinstance(data, str): 876 return data 877 emr = gmPerson.gmCurrentPatient().emr 878 atcs = [] 879 if data['atc_substance'] is not None: 880 atcs.append(data['atc_substance']) 881 # if data['atc_drug'] is not None: 882 # atcs.append(data['atc_drug']) 883 # allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (data['substance'],), drug = data['product']) 884 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (data['substance'],)) 885 if allg is False: 886 allg = None 887 return data.format(single_line = False, allergy = allg, show_all_product_components = True)
888 889 #-----------------------------------------------------
890 - def _on_meds_item_activated(self, event):
891 data = self._LCTRL_meds.get_selected_item_data(only_one = True) 892 893 if data is None: 894 return 895 896 # if isinstance(data, str): 897 # gmHabitWidgets.manage_substance_abuse(parent = self, patient = gmPerson.gmCurrentPatient()) 898 # return 899 900 # <ctrl> down ? -> edit 901 if wx.GetKeyState(wx.WXK_CONTROL): 902 wx.CallAfter(gmMedicationWidgets.edit_intake_of_substance, parent = self, substance = data) 903 return 904 905 gmDispatcher.send(signal = 'display_widget', name = 'gmCurrentSubstancesPlugin')
906 907 #----------------------------------------------------- 908 #-----------------------------------------------------
909 - def __refresh_contacts(self, patient=None):
910 emr = patient.emr 911 912 list_items = [] 913 list_data = [] 914 is_in_hospital = False 915 916 stays = emr.get_hospital_stays(ongoing_only = True) 917 if len(stays) > 0: 918 list_items.append(_('** Currently hospitalized: %s **') % stays[0]['hospital']) 919 list_data.append(stays[0]) 920 is_in_hospital = True 921 922 adrs = patient.get_addresses() 923 for adr in adrs: 924 list_items.append(adr.format(single_line = True, verbose = False, show_type = True)) 925 list_data.append(adr) 926 927 comms = patient.get_comm_channels() 928 for comm in comms: 929 list_items.append('%s: %s%s' % ( 930 comm['l10n_comm_type'], 931 comm['url'], 932 gmTools.coalesce(comm['comment'], '', ' (%s)') 933 )) 934 list_data.append(comm) 935 936 ident = patient.emergency_contact_in_database 937 if ident is not None: 938 list_items.append(_('emergency: %s') % ident['description_gender']) 939 list_data.append(ident) 940 941 if patient['emergency_contact'] is not None: 942 list_items.append(_('emergency: %s') % patient['emergency_contact'].split('\n')[0]) 943 list_data.append(patient['emergency_contact']) 944 945 provider = patient.primary_provider 946 if provider is not None: 947 list_items.append(_('in-praxis: %s') % patient.primary_provider_identity.get_description_gender(with_nickname = False)) 948 list_data.append(provider) 949 950 for item in emr.external_care_items: 951 list_items.append(_('care: %s%s@%s') % ( 952 gmTools.coalesce(item['provider'], '', '%s, '), 953 item['unit'], 954 item['organization'] 955 )) 956 list_data.append(item) 957 958 self._LCTRL_contacts.set_string_items(items = list_items) 959 self._LCTRL_contacts.set_data(data = list_data) 960 if is_in_hospital: 961 self._LCTRL_contacts.SetItemTextColour(0, wx.Colour('RED'))
962 963 #-----------------------------------------------------
964 - def _calc_contacts_list_item_tooltip(self, data):
965 966 if isinstance(data, gmEMRStructItems.cHospitalStay): 967 return data.format() 968 969 if isinstance(data, gmExternalCare.cExternalCareItem): 970 return '\n'.join(data.format ( 971 with_health_issue = True, 972 with_address = True, 973 with_comms = True 974 )) 975 976 if isinstance(data, gmDemographicRecord.cPatientAddress): 977 return '\n'.join(data.format()) 978 979 if isinstance(data, gmDemographicRecord.cCommChannel): 980 parts = [] 981 if data['is_confidential']: 982 parts.append(_('*** CONFIDENTIAL ***')) 983 if data['comment'] is not None: 984 parts.append(data['comment']) 985 return '\n'.join(parts) 986 987 if isinstance(data, gmPerson.cPerson): 988 return '%s\n\n%s' % ( 989 data['description_gender'], 990 '\n'.join([ 991 '%s: %s%s' % ( 992 c['l10n_comm_type'], 993 c['url'], 994 gmTools.bool2subst(c['is_confidential'], _(' (confidential !)'), '', '') 995 ) 996 for c in data.get_comm_channels() 997 ]) 998 ) 999 1000 if isinstance(data, str): 1001 return data 1002 1003 if isinstance(data, gmStaff.cStaff): 1004 ident = data.identity 1005 return '%s: %s\n\n%s%s' % ( 1006 data['short_alias'], 1007 ident['description_gender'], 1008 '\n'.join([ 1009 '%s: %s%s' % ( 1010 c['l10n_comm_type'], 1011 c['url'], 1012 gmTools.bool2subst(c['is_confidential'], _(' (confidential !)'), '', '') 1013 ) 1014 for c in ident.get_comm_channels() 1015 ]), 1016 gmTools.coalesce(data['comment'], '', '\n\n%s') 1017 ) 1018 1019 return None
1020 1021 #-----------------------------------------------------
1022 - def _on_contacts_item_activated(self, event):
1023 data = self._LCTRL_contacts.get_selected_item_data(only_one = True) 1024 if data is not None: 1025 # <ctrl> down ? 1026 if wx.GetKeyState(wx.WXK_CONTROL): 1027 if isinstance(data, gmEMRStructItems.cHospitalStay): 1028 gmHospitalStayWidgets.edit_hospital_stay(parent = self, hospital_stay = data) 1029 return 1030 if isinstance(data, gmDemographicRecord.cPatientAddress): 1031 pass 1032 if isinstance(data, gmDemographicRecord.cCommChannel): 1033 gmContactWidgets.edit_comm_channel(parent = self, comm_channel = data, channel_owner = gmPerson.gmCurrentPatient()) 1034 return 1035 if isinstance(data, gmPerson.cPerson): 1036 pass 1037 if isinstance(data, gmStaff.cStaff): 1038 pass 1039 1040 gmDispatcher.send(signal = 'display_widget', name = 'gmNotebookedPatientEditionPlugin')
1041 1042 #----------------------------------------------------- 1043 #-----------------------------------------------------
1044 - def __refresh_problems(self, patient=None):
1045 emr = patient.emr 1046 1047 problems = [ 1048 p for p in emr.get_problems(include_closed_episodes = False, include_irrelevant_issues = False) 1049 if p['problem_active'] 1050 ] 1051 1052 list_items = [] 1053 list_data = [] 1054 for problem in problems: 1055 if problem['type'] == 'issue': 1056 issue = emr.problem2issue(problem) 1057 last_encounter = emr.get_last_encounter(issue_id = issue['pk_health_issue']) 1058 if last_encounter is None: 1059 last = issue['modified_when'].strftime('%m/%Y') 1060 else: 1061 last = last_encounter['last_affirmed'].strftime('%m/%Y') 1062 list_items.append('%s: %s' % (problem['problem'], last)) 1063 elif problem['type'] == 'episode': 1064 epi = emr.problem2episode(problem) 1065 last_encounter = emr.get_last_encounter(episode_id = epi['pk_episode']) 1066 if last_encounter is None: 1067 last = epi['episode_modified_when'].strftime('%m/%Y') 1068 else: 1069 last = last_encounter['last_affirmed'].strftime('%m/%Y') 1070 list_items.append('%s: %s' % (problem['problem'], last)) 1071 list_data.append(problem) 1072 1073 care = emr.get_external_care_items(exclude_inactive = True) 1074 for item in care: 1075 # skip those already-shown(-or-not) 1076 if item['pk_health_issue'] is not None: 1077 continue 1078 list_items.append(_('extrnl: %s (%s@%s)') % ( 1079 item['issue'], 1080 item['unit'], 1081 item['organization'] 1082 )) 1083 list_data.append(item) 1084 1085 self._LCTRL_problems.set_string_items(items = list_items) 1086 self._LCTRL_problems.set_data(data = list_data)
1087 1088 #-----------------------------------------------------
1089 - def _calc_problem_list_item_tooltip(self, data):
1090 1091 if isinstance(data, gmExternalCare.cExternalCareItem): 1092 return '\n'.join(data.format ( 1093 with_health_issue = True, 1094 with_address = True, 1095 with_comms = True 1096 )) 1097 1098 emr = gmPerson.gmCurrentPatient().emr 1099 1100 if data['type'] == 'issue': 1101 issue = emr.problem2issue(data) 1102 tt = issue.format ( 1103 patient = gmPerson.gmCurrentPatient(), 1104 with_medications = False, 1105 with_hospital_stays = False, 1106 with_procedures = False, 1107 with_family_history = False, 1108 with_documents = False, 1109 with_tests = False, 1110 with_vaccinations = False 1111 ).strip('\n') 1112 return tt 1113 1114 if data['type'] == 'episode': 1115 epi = emr.problem2episode(data) 1116 tt = epi.format ( 1117 patient = gmPerson.gmCurrentPatient(), 1118 with_encounters = False, 1119 with_hospital_stays = False, 1120 with_procedures = False, 1121 with_family_history = False, 1122 with_documents = False, 1123 with_tests = False, 1124 with_vaccinations = False, 1125 with_health_issue = True 1126 ).strip('\n') 1127 return tt 1128 1129 return None
1130 1131 #-----------------------------------------------------
1132 - def _on_problem_activated(self, event):
1133 data = self._LCTRL_problems.get_selected_item_data(only_one = True) 1134 if data is not None: 1135 # <ctrl> down ? 1136 if wx.GetKeyState(wx.WXK_CONTROL): 1137 emr = gmPerson.gmCurrentPatient().emr 1138 if data['type'] == 'issue': 1139 gmEMRStructWidgets.edit_health_issue(parent = self, issue = emr.problem2issue(data)) 1140 return 1141 if data['type'] == 'episode': 1142 gmEMRStructWidgets.edit_episode(parent = self, episode = emr.problem2episode(data)) 1143 return 1144 1145 gmDispatcher.send(signal = 'display_widget', name = 'gmEMRBrowserPlugin')
1146 1147 #----------------------------------------------------- 1148 #-----------------------------------------------------
1149 - def __refresh_identity(self, patient=None):
1150 # names (.comment -> tooltip) 1151 names = patient.get_names(exclude_active = True) 1152 items = [ 1153 _('aka: %(last)s, %(first)s%(nick)s') % { 1154 'last': n['lastnames'], 1155 'first': n['firstnames'], 1156 'nick': gmTools.coalesce(n['preferred'], '', " '%s'") 1157 } for n in names 1158 ] 1159 data = names 1160 1161 # IDs (.issuer & .comment -> tooltip) 1162 ids = patient.external_ids 1163 for i in ids: 1164 items.append('%s: %s' % (i['name'], i['value'])) 1165 data.append({'id': i}) 1166 1167 # occupation 1168 jobs = patient.get_occupations() 1169 for j in jobs: 1170 items.append(_('job: %s (%s)') % ( 1171 j['l10n_occupation'], 1172 j['modified_when'].strftime('%m/%Y') 1173 )) 1174 data.append({'job': j}) 1175 1176 self._LCTRL_identity.set_string_items(items = items) 1177 self._LCTRL_identity.set_data(data = data)
1178 1179 #-----------------------------------------------------
1180 - def _calc_identity_item_tooltip(self, data):
1181 if isinstance(data, gmPerson.cPersonName): 1182 return data['comment'] 1183 if isinstance(data, dict): 1184 key = list(data)[0] 1185 val = data[key] 1186 if key == 'id': 1187 return _('issued by: %s%s') % ( 1188 val['issuer'], 1189 gmTools.coalesce(val['comment'], '', '\n\n%s') 1190 ) 1191 if key == 'job': 1192 tt = _('Last modified: %s') % val['modified_when'].strftime('%m/%Y') 1193 if val['activities'] is None: 1194 return tt 1195 return tt + ('\n\n' + _('Activities:\n\n%s') % val['activities']) 1196 1197 return None
1198 1199 #-----------------------------------------------------
1200 - def _on_identity_item_activated(self, event):
1201 data = self._LCTRL_identity.get_selected_item_data(only_one = True) 1202 if data is None: 1203 gmDispatcher.send(signal = 'display_widget', name = 'gmNotebookedPatientEditionPlugin') 1204 1205 # <ctrl> down ? 1206 if not wx.GetKeyState(wx.WXK_CONTROL): 1207 gmDispatcher.send(signal = 'display_widget', name = 'gmNotebookedPatientEditionPlugin') 1208 1209 # <ctrl> down ! 1210 if isinstance(data, gmPerson.cPersonName): 1211 ea = gmDemographicsWidgets.cPersonNameEAPnl(self, -1, name = data) 1212 dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True) 1213 dlg.SetTitle(_('Cloning name')) 1214 dlg.ShowModal() 1215 return 1216 1217 if isinstance(data, dict): 1218 key = list(data)[0] 1219 val = data[key] 1220 if key == 'id': 1221 ea = gmDemographicsWidgets.cExternalIDEditAreaPnl(self, -1, external_id = val) 1222 ea.id_holder = gmPerson.gmCurrentPatient() 1223 dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True) 1224 dlg.SetTitle(_('Editing external ID')) 1225 dlg.ShowModal() 1226 return 1227 if key == 'job': 1228 gmDemographicsWidgets.edit_occupation() 1229 return
1230 1231 #============================================================ 1232 # main 1233 #------------------------------------------------------------ 1234 if __name__ == "__main__": 1235 1236 if len(sys.argv) < 2: 1237 sys.exit() 1238 1239 if sys.argv[1] != 'test': 1240 sys.exit() 1241 1242 # from Gnumed.pycommon import gmPG2 1243 # from Gnumed.pycommon import gmI18N 1244 # gmI18N.activate_locale() 1245 # gmI18N.install_domain() 1246 1247 #-------------------------------------------------------- 1248 #test_org_unit_prw() 1249