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

Source Code for Module Gnumed.wxpython.gmEMRBrowser

   1  """GNUmed patient EMR tree browser.""" 
   2  #================================================================ 
   3  __author__ = "cfmoro1976@yahoo.es, sjtan@swiftdsl.com.au, Karsten.Hilbert@gmx.net" 
   4  __license__ = "GPL v2 or later" 
   5   
   6  # std lib 
   7  import sys 
   8  import os.path 
   9  import io 
  10  import logging 
  11  import datetime as pydt 
  12   
  13   
  14  # 3rd party 
  15  import wx 
  16  import wx.lib.mixins.treemixin as treemixin 
  17   
  18   
  19  # GNUmed libs 
  20  from Gnumed.pycommon import gmI18N 
  21  from Gnumed.pycommon import gmDispatcher 
  22  from Gnumed.pycommon import gmExceptions 
  23  from Gnumed.pycommon import gmTools 
  24  from Gnumed.pycommon import gmDateTime 
  25  from Gnumed.pycommon import gmLog2 
  26   
  27  from Gnumed.exporters import gmPatientExporter 
  28   
  29  from Gnumed.business import gmGenericEMRItem 
  30  from Gnumed.business import gmEMRStructItems 
  31  from Gnumed.business import gmPerson 
  32  from Gnumed.business import gmSOAPimporter 
  33  from Gnumed.business import gmPersonSearch 
  34  from Gnumed.business import gmSoapDefs 
  35  from Gnumed.business import gmClinicalRecord 
  36   
  37  from Gnumed.wxpython import gmGuiHelpers 
  38  from Gnumed.wxpython import gmEMRStructWidgets 
  39  from Gnumed.wxpython import gmEncounterWidgets 
  40  from Gnumed.wxpython import gmSOAPWidgets 
  41  from Gnumed.wxpython import gmAllergyWidgets 
  42  from Gnumed.wxpython import gmDemographicsWidgets 
  43  from Gnumed.wxpython import gmNarrativeWidgets 
  44  from Gnumed.wxpython import gmNarrativeWorkflows 
  45  from Gnumed.wxpython import gmPatSearchWidgets 
  46  from Gnumed.wxpython import gmVaccWidgets 
  47  from Gnumed.wxpython import gmFamilyHistoryWidgets 
  48  from Gnumed.wxpython import gmFormWidgets 
  49  from Gnumed.wxpython import gmTimer 
  50  from Gnumed.wxpython import gmHospitalStayWidgets 
  51  from Gnumed.wxpython import gmProcedureWidgets 
  52  from Gnumed.wxpython import gmGenericEMRItemWorkflows 
  53   
  54   
  55  _log = logging.getLogger('gm.ui') 
  56   
  57  #============================================================ 
58 -def export_emr_to_ascii(parent=None):
59 """ 60 Dump the patient's EMR from GUI client 61 @param parent - The parent widget 62 @type parent - A wx.Window instance 63 """ 64 # sanity checks 65 if parent is None: 66 raise TypeError('expected wx.Window instance as parent, got <None>') 67 68 pat = gmPerson.gmCurrentPatient() 69 if not pat.connected: 70 gmDispatcher.send(signal='statustext', msg=_('Cannot export EMR. No active patient.')) 71 return False 72 73 # get file name 74 wc = "%s (*.txt)|*.txt|%s (*)|*" % (_("text files"), _("all files")) 75 defdir = os.path.abspath(os.path.expanduser(os.path.join('~', 'gnumed', pat.subdir_name))) 76 gmTools.mkdir(defdir) 77 fname = '%s-%s_%s.txt' % (_('emr-export'), pat['lastnames'], pat['firstnames']) 78 dlg = wx.FileDialog ( 79 parent = parent, 80 message = _("Save patient's EMR as..."), 81 defaultDir = defdir, 82 defaultFile = fname, 83 wildcard = wc, 84 style = wx.FD_SAVE 85 ) 86 choice = dlg.ShowModal() 87 fname = dlg.GetPath() 88 dlg.DestroyLater() 89 if choice != wx.ID_OK: 90 return None 91 92 _log.debug('exporting EMR to [%s]', fname) 93 94 output_file = io.open(fname, mode = 'wt', encoding = 'utf8', errors = 'replace') 95 exporter = gmPatientExporter.cEmrExport(patient = pat) 96 exporter.set_output_file(output_file) 97 exporter.dump_constraints() 98 exporter.dump_demographic_record(True) 99 exporter.dump_clinical_record() 100 exporter.dump_med_docs() 101 output_file.close() 102 103 gmDispatcher.send('statustext', msg = _('EMR successfully exported to file: %s') % fname, beep = False) 104 return fname
105 106 #============================================================
107 -class cEMRTree(wx.TreeCtrl, treemixin.ExpansionState):
108 """This wx.TreeCtrl derivative displays a tree view of a medical record.""" 109 110 #--------------------------------------------------------
111 - def __init__(self, parent, id, *args, **kwds):
112 """Set up our specialised tree. 113 """ 114 kwds['style'] = wx.TR_HAS_BUTTONS | wx.NO_BORDER | wx.TR_SINGLE 115 wx.TreeCtrl.__init__(self, parent, id, *args, **kwds) 116 117 self.__soap_display = None 118 self.__soap_display_mode = 'details' # "details" or "journal" or "revisions" 119 self.__img_display = None 120 self.__cb__enable_display_mode_selection = lambda x:x 121 self.__cb__select_edit_mode = lambda x:x 122 self.__cb__add_soap_editor = lambda x:x 123 self.__pat = None 124 self.__curr_node = None 125 self.__expanded_nodes = None 126 127 self.__make_popup_menus() 128 self.__register_events()
129 130 #-------------------------------------------------------- 131 # external API 132 #--------------------------------------------------------
133 - def _get_soap_display(self):
134 return self.__soap_display
135
136 - def _set_soap_display(self, soap_display=None):
137 self.__soap_display = soap_display 138 self.__soap_display_prop_font = soap_display.GetFont() 139 self.__soap_display_mono_font = wx.Font(self.__soap_display_prop_font.GetNativeFontInfo()) 140 self.__soap_display_mono_font.SetFamily(wx.FONTFAMILY_TELETYPE) 141 self.__soap_display_mono_font.SetPointSize(self.__soap_display_prop_font.GetPointSize() - 2)
142 143 soap_display = property(_get_soap_display, _set_soap_display) 144 145 #--------------------------------------------------------
146 - def _get_image_display(self):
147 return self.__img_display
148
149 - def _set_image_display(self, image_display=None):
150 self.__img_display = image_display
151 152 image_display = property(_get_image_display, _set_image_display) 153 154 #--------------------------------------------------------
156 if not callable(callback): 157 raise ValueError('callback [%s] not callable' % callback) 158 self.__cb__enable_display_mode_selection = callback
159 160 #--------------------------------------------------------
161 - def _set_edit_mode_selector(self, callback):
162 if callback is None: 163 callback = lambda x:x 164 if not callable(callback): 165 raise ValueError('edit mode selector [%s] not callable' % callback) 166 self.__cb__select_edit_mode = callback
167 168 edit_mode_selector = property(lambda x:x, _set_edit_mode_selector) 169 170 #--------------------------------------------------------
171 - def _set_soap_editor_adder(self, callback):
172 if callback is None: 173 callback = lambda x:x 174 if not callable(callback): 175 raise ValueError('soap editor adder [%s] not callable' % callback) 176 self.__cb__add_soap_editor = callback
177 178 soap_editor_adder = property(lambda x:x, _set_soap_editor_adder) 179 180 #-------------------------------------------------------- 181 # ExpansionState mixin API 182 #--------------------------------------------------------
183 - def GetItemIdentity(self, item):
184 if item is None: 185 return 'invalid item' 186 187 if not item.IsOk(): 188 return 'invalid item' 189 190 try: 191 node_data = self.GetItemData(item) 192 except wx.wxAssertionError: 193 _log.exception('unfathomable self.GetItemData() problem occurred, faking root node') 194 _log.error('real node: %s', item) 195 _log.error('node.IsOk(): %s', item.IsOk()) # already survived this further up 196 _log.error('is root node: %s', item == self.GetRootItem()) 197 _log.error('node attributes: %s', dir(item)) 198 gmLog2.log_stack_trace() 199 return 'invalid item' 200 201 if isinstance(node_data, gmEMRStructItems.cHealthIssue): 202 return 'issue::%s' % node_data['pk_health_issue'] 203 if isinstance(node_data, gmEMRStructItems.cEpisode): 204 return 'episode::%s' % node_data['pk_episode'] 205 if isinstance(node_data, gmEMRStructItems.cEncounter): 206 return 'encounter::%s' % node_data['pk_encounter'] 207 # unassociated episodes 208 if isinstance(node_data, dict): 209 return 'dummy node::%s' % self.__pat.ID 210 # root node == EMR level 211 return 'root node::%s' % self.__pat.ID
212 213 #-------------------------------------------------------- 214 # internal helpers 215 #--------------------------------------------------------
216 - def __register_events(self):
217 """Configures enabled event signals.""" 218 self.Bind(wx.EVT_TREE_SEL_CHANGED, self._on_tree_item_selected) 219 self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self._on_tree_item_activated) 220 self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self._on_tree_item_expanding) 221 self.Bind(wx.EVT_TREE_ITEM_MENU, self._on_tree_item_context_menu) 222 223 # handle tooltips 224 # self.Bind(wx.EVT_MOTION, self._on_mouse_motion) 225 self.Bind(wx.EVT_TREE_ITEM_GETTOOLTIP, self._on_tree_item_gettooltip) 226 227 # FIXME: xxxxx signal 228 gmDispatcher.connect(signal = 'narrative_mod_db', receiver = self._on_narrative_mod_db) 229 gmDispatcher.connect(signal = 'clin.episode_mod_db', receiver = self._on_episode_mod_db) 230 gmDispatcher.connect(signal = 'clin.health_issue_mod_db', receiver = self._on_issue_mod_db) 231 gmDispatcher.connect(signal = 'clin.family_history_mod_db', receiver = self._on_issue_mod_db)
232 233 #--------------------------------------------------------
234 - def clear_tree(self):
235 self.DeleteAllItems() 236 self.__expanded_nodes = None
237 238 #--------------------------------------------------------
239 - def __populate_tree(self):
240 """Updates EMR browser data.""" 241 # FIXME: auto select the previously self.__curr_node if not None 242 # FIXME: error handling 243 244 _log.debug('populating EMR tree') 245 246 wx.BeginBusyCursor() 247 248 if self.__pat is None: 249 self.clear_tree() 250 self.__expanded_nodes = None 251 wx.EndBusyCursor() 252 return True 253 254 # init new tree 255 root_item = self.__populate_root_node() 256 self.__curr_node = root_item 257 if self.__expanded_nodes is not None: 258 self.ExpansionState = self.__expanded_nodes 259 self.SelectItem(root_item) 260 self.Expand(root_item) 261 self.__update_text_for_selected_node() # this is fairly slow, too 262 263 wx.EndBusyCursor() 264 return True
265 266 #--------------------------------------------------------
267 - def __populate_root_node(self):
268 269 self.DeleteAllItems() 270 271 root_item = self.AddRoot(_('EMR of %(lastnames)s, %(firstnames)s') % self.__pat.get_active_name()) 272 self.SetItemData(root_item, None) 273 self.SetItemHasChildren(root_item, True) 274 275 self.__root_tooltip = self.__pat['description_gender'] + '\n' 276 if self.__pat['deceased'] is None: 277 self.__root_tooltip += ' %s (%s)\n\n' % ( 278 self.__pat.get_formatted_dob(format = '%d %b %Y'), 279 self.__pat['medical_age'] 280 ) 281 else: 282 template = ' %s - %s (%s)\n\n' 283 self.__root_tooltip += template % ( 284 self.__pat.get_formatted_dob(format = '%d.%b %Y'), 285 gmDateTime.pydt_strftime(self.__pat['deceased'], '%Y %b %d'), 286 self.__pat['medical_age'] 287 ) 288 self.__root_tooltip += gmTools.coalesce(self.__pat['comment'], '', '%s\n\n') 289 doc = self.__pat.primary_provider 290 if doc is not None: 291 self.__root_tooltip += '%s:\n' % _('Primary provider in this praxis') 292 self.__root_tooltip += ' %s %s %s (%s)%s\n\n' % ( 293 gmTools.coalesce(doc['title'], gmPerson.map_gender2salutation(gender = doc['gender'])), 294 doc['firstnames'], 295 doc['lastnames'], 296 doc['short_alias'], 297 gmTools.bool2subst(doc['is_active'], '', ' [%s]' % _('inactive')) 298 ) 299 if not ((self.__pat['emergency_contact'] is None) and (self.__pat['pk_emergency_contact'] is None)): 300 self.__root_tooltip += _('In case of emergency contact:') + '\n' 301 if self.__pat['emergency_contact'] is not None: 302 self.__root_tooltip += gmTools.wrap ( 303 text = '%s\n' % self.__pat['emergency_contact'], 304 width = 60, 305 initial_indent = ' ', 306 subsequent_indent = ' ' 307 ) 308 if self.__pat['pk_emergency_contact'] is not None: 309 contact = self.__pat.emergency_contact_in_database 310 self.__root_tooltip += ' %s\n' % contact['description_gender'] 311 self.__root_tooltip = self.__root_tooltip.strip('\n') 312 if self.__root_tooltip == '': 313 self.__root_tooltip = ' ' 314 315 return root_item
316 317 #--------------------------------------------------------
319 """Displays information for the selected tree node.""" 320 321 if self.__soap_display is None: 322 return 323 324 self.__soap_display.Clear() 325 self.__img_display.clear() 326 327 if self.__curr_node is None: 328 return 329 330 if not self.__curr_node.IsOk(): 331 return 332 333 try: 334 node_data = self.GetItemData(self.__curr_node) 335 except wx.wxAssertionError: 336 node_data = None # fake a root node 337 _log.exception('unfathomable self.GetItemData() problem occurred, faking root node') 338 _log.error('real node: %s', self.__curr_node) 339 _log.error('node.IsOk(): %s', self.__curr_node.IsOk()) # already survived this further up 340 _log.error('is root node: %s', self.__curr_node == self.GetRootItem()) 341 _log.error('node attributes: %s', dir(self.__curr_node)) 342 gmLog2.log_stack_trace() 343 344 if isinstance(node_data, gmEMRStructItems.cHealthIssue): 345 self.__update_text_for_issue_node(node_data) 346 return 347 348 # unassociated episodes # FIXME: turn into real dummy issue 349 if isinstance(node_data, dict): 350 self.__update_text_for_pseudo_issue_node(node_data) 351 return 352 353 if isinstance(node_data, gmEMRStructItems.cEpisode): 354 self.__update_text_for_episode_node(node_data) 355 return 356 357 if isinstance(node_data, gmEMRStructItems.cEncounter): 358 self.__update_text_for_encounter_node(node_data) 359 return 360 361 if isinstance(node_data, gmGenericEMRItem.cGenericEMRItem): 362 self.__update_text_for_generic_node(node_data) 363 return 364 365 # root node == EMR level 366 self.__update_text_for_root_node()
367 368 #--------------------------------------------------------
369 - def __make_popup_menus(self):
370 371 # - root node 372 self.__root_context_popup = wx.Menu(title = _('EMR Actions:')) 373 item = self.__root_context_popup.Append(-1, _('Print EMR')) 374 self.Bind(wx.EVT_MENU, self.__print_emr, item) 375 item = self.__root_context_popup.Append(-1, _('Create health issue')) 376 self.Bind(wx.EVT_MENU, self.__create_issue, item) 377 item = self.__root_context_popup.Append(-1, _('Create episode')) 378 self.Bind(wx.EVT_MENU, self.__create_episode, item) 379 item = self.__root_context_popup.Append(-1, _('Create progress note')) 380 self.Bind(wx.EVT_MENU, self.__create_soap_editor, item) 381 item = self.__root_context_popup.Append(-1, _('Manage allergies')) 382 self.Bind(wx.EVT_MENU, self.__document_allergy, item) 383 item = self.__root_context_popup.Append(-1, _('Manage family history')) 384 self.Bind(wx.EVT_MENU, self.__manage_family_history, item) 385 item = self.__root_context_popup.Append(-1, _('Manage hospitalizations')) 386 self.Bind(wx.EVT_MENU, self.__manage_hospital_stays, item) 387 item = self.__root_context_popup.Append(-1, _('Manage occupation')) 388 self.Bind(wx.EVT_MENU, self.__manage_occupation, item) 389 item = self.__root_context_popup.Append(-1, _('Manage procedures')) 390 self.Bind(wx.EVT_MENU, self.__manage_procedures, item) 391 item = self.__root_context_popup.Append(-1, _('Manage vaccinations')) 392 self.Bind(wx.EVT_MENU, self.__manage_vaccinations, item) 393 394 self.__root_context_popup.AppendSeparator() 395 396 # expand tree 397 expand_menu = wx.Menu() 398 self.__root_context_popup.Append(wx.NewId(), _('Open EMR to ...'), expand_menu) 399 item = expand_menu.Append(-1, _('... issue level')) 400 self.Bind(wx.EVT_MENU, self.__expand_to_issue_level, item) 401 item = expand_menu.Append(-1, _('... episode level')) 402 self.Bind(wx.EVT_MENU, self.__expand_to_episode_level, item) 403 item = expand_menu.Append(-1, _('... encounter level')) 404 self.Bind(wx.EVT_MENU, self.__expand_to_encounter_level, item) 405 406 # - health issues 407 self.__issue_context_popup = wx.Menu(title = _('Health Issue Actions:')) 408 item = self.__issue_context_popup.Append(-1, _('Edit details')) 409 self.Bind(wx.EVT_MENU, self.__edit_issue, item) 410 item = self.__issue_context_popup.Append(-1, _('Delete')) 411 self.Bind(wx.EVT_MENU, self.__delete_issue, item) 412 self.__issue_context_popup.AppendSeparator() 413 item = self.__issue_context_popup.Append(-1, _('Open to encounter level')) 414 self.Bind(wx.EVT_MENU, self.__expand_issue_to_encounter_level, item) 415 # print " attach issue to another patient" 416 # print " move all episodes to another issue" 417 item = self.__issue_context_popup.Append(-1, _('Create progress note')) 418 self.Bind(wx.EVT_MENU, self.__create_soap_editor, item) 419 420 # - episodes 421 self.__epi_context_popup = wx.Menu(title = _('Episode Actions:')) 422 item = self.__epi_context_popup.Append(-1, _('Toggle ongoing/closed')) 423 self.Bind(wx.EVT_MENU, self.__toggle_episode_open_close, item) 424 item = self.__epi_context_popup.Append(-1, _('Edit details')) 425 self.Bind(wx.EVT_MENU, self.__edit_episode, item) 426 item = self.__epi_context_popup.Append(-1, _('Delete')) 427 self.Bind(wx.EVT_MENU, self.__delete_episode, item) 428 item = self.__epi_context_popup.Append(-1, _('Promote')) 429 self.Bind(wx.EVT_MENU, self.__promote_episode_to_issue, item) 430 item = self.__epi_context_popup.Append(-1, _('Create progress note')) 431 self.Bind(wx.EVT_MENU, self.__create_soap_editor, item) 432 item = self.__epi_context_popup.Append(-1, _('Move encounters')) 433 self.Bind(wx.EVT_MENU, self.__move_encounters, item) 434 435 # - encounters 436 self.__enc_context_popup = wx.Menu(title = _('Encounter Actions:')) 437 item = self.__enc_context_popup.Append(-1, _('Move data to another episode')) 438 self.Bind(wx.EVT_MENU, self.__relink_encounter_data2episode, item) 439 item = self.__enc_context_popup.Append(-1, _('Edit details')) 440 self.Bind(wx.EVT_MENU, self.__edit_encounter_details, item) 441 # would require pre-configurable save-under which we don't have 442 #item = self.__enc_context_popup.Append(-1, _('Create progress note')) 443 #self.Bind(wx.EVT_MENU, self.__create_soap_editor, item) 444 #item = self.__enc_context_popup.Append(-1, _('Edit progress notes')) 445 #self.Bind(wx.EVT_MENU, self.__edit_progress_notes, item) 446 item = self.__enc_context_popup.Append(-1, _('Move progress notes')) 447 self.Bind(wx.EVT_MENU, self.__move_progress_notes, item) 448 item = self.__enc_context_popup.Append(-1, _('Export for Medistar')) 449 self.Bind(wx.EVT_MENU, self.__export_encounter_for_medistar, item) 450 451 # - generic EMR items 452 self.__generic_emr_item_context_popup = wx.Menu(title = _('Item Actions:')) 453 item = self.__generic_emr_item_context_popup.Append(-1, _('Edit item')) 454 self.Bind(wx.EVT_MENU, self.__on_edit_generic_emr_item, item)
455 456 #--------------------------------------------------------
457 - def __show_context_menu(self, pos=wx.DefaultPosition):
458 self.__curr_node_data = self.GetItemData(self.__curr_node) 459 460 if isinstance(self.__curr_node_data, gmEMRStructItems.cHealthIssue): 461 self.PopupMenu(self.__issue_context_popup, pos) 462 return True 463 464 if isinstance(self.__curr_node_data, gmEMRStructItems.cEpisode): 465 self.PopupMenu(self.__epi_context_popup, pos) 466 return True 467 468 if isinstance(self.__curr_node_data, gmEMRStructItems.cEncounter): 469 self.PopupMenu(self.__enc_context_popup, pos) 470 return True 471 472 if isinstance(self.__curr_node_data, gmGenericEMRItem.cGenericEMRItem): 473 self.PopupMenu(self.__generic_emr_item_context_popup, pos) 474 return True 475 476 if self.__curr_node == self.GetRootItem(): 477 self.PopupMenu(self.__root_context_popup, pos) 478 return True 479 480 return False
481 # ignore pseudo node "free-standing episodes" 482 # if isinstance(self.__curr_node_data, dict): 483 # pass 484 485 #-------------------------------------------------------- 486 # episode level 487 #--------------------------------------------------------
488 - def __move_encounters(self, event):
489 episode = self.GetItemData(self.__curr_node) 490 491 gmNarrativeWorkflows.move_progress_notes_to_another_encounter ( 492 parent = self, 493 episodes = [episode['pk_episode']], 494 move_all = True 495 )
496 497 #--------------------------------------------------------
498 - def __toggle_episode_open_close(self, event):
499 self.__curr_node_data['episode_open'] = not self.__curr_node_data['episode_open'] 500 self.__curr_node_data.save()
501 502 #--------------------------------------------------------
503 - def __edit_episode(self, event):
504 gmEMRStructWidgets.edit_episode(parent = self, episode = self.__curr_node_data)
505 506 #--------------------------------------------------------
507 - def __promote_episode_to_issue(self, evt):
508 gmEMRStructWidgets.promote_episode_to_issue(parent=self, episode = self.__curr_node_data, emr = self.__pat.emr)
509 510 #--------------------------------------------------------
511 - def __delete_episode(self, event):
512 dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 513 parent = self, 514 id = -1, 515 caption = _('Deleting episode'), 516 button_defs = [ 517 {'label': _('Yes, delete'), 'tooltip': _('Delete the episode if possible (it must be completely empty).')}, 518 {'label': _('No, cancel'), 'tooltip': _('Cancel and do NOT delete the episode.')} 519 ], 520 question = _( 521 'Are you sure you want to delete this episode ?\n' 522 '\n' 523 ' "%s"\n' 524 ) % self.__curr_node_data['description'] 525 ) 526 result = dlg.ShowModal() 527 if result != wx.ID_YES: 528 return 529 530 if not gmEMRStructItems.delete_episode(episode = self.__curr_node_data): 531 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete episode. There is still clinical data recorded for it.'))
532 533 #--------------------------------------------------------
534 - def __expand_episode_node(self, episode_node=None):
535 self.DeleteChildren(episode_node) 536 537 emr = self.__pat.emr 538 epi = self.GetItemData(episode_node) 539 encounters = emr.get_encounters(episodes = [epi['pk_episode']], skip_empty = True) 540 if len(encounters) == 0: 541 self.SetItemHasChildren(episode_node, False) 542 return 543 544 self.SetItemHasChildren(episode_node, True) 545 for enc in encounters: 546 label = '%s: %s' % ( 547 enc['started'].strftime('%Y %b %d'), 548 gmTools.unwrap ( 549 gmTools.coalesce ( 550 gmTools.coalesce ( 551 gmTools.coalesce ( 552 enc.get_latest_soap ( # soAp 553 soap_cat = 'a', 554 episode = epi['pk_episode'] 555 ), 556 enc['assessment_of_encounter'] # or AOE 557 ), 558 enc['reason_for_encounter'] # or RFE 559 ), 560 enc['l10n_type'] # or type 561 ), 562 max_length = 40 563 ) 564 ) 565 encounter_node = self.AppendItem(episode_node, label) 566 self.SetItemData(encounter_node, enc) 567 self.SetItemHasChildren(encounter_node, True) 568 569 self.SortChildren(episode_node)
570 571 #--------------------------------------------------------
572 - def __update_text_for_episode_node(self, episode):
573 self.__cb__enable_display_mode_selection(True) 574 if self.__soap_display_mode == 'details': 575 txt = episode.format(left_margin = 1, patient = self.__pat) 576 font = self.__soap_display_prop_font 577 elif self.__soap_display_mode == 'journal': 578 txt = episode.format_as_journal(left_margin = 1) 579 font = self.__soap_display_prop_font 580 elif self.__soap_display_mode == 'revisions': 581 txt = episode.formatted_revision_history 582 font = self.__soap_display_mono_font 583 else: 584 txt = 'unknown SOAP display mode [%s]' % self.__soap_display_mode 585 font = self.__soap_display_prop_font 586 doc_folder = self.__pat.get_document_folder() 587 self.__img_display.refresh ( 588 document_folder = doc_folder, 589 episodes = [ episode['pk_episode'] ] 590 ) 591 self.__soap_display.SetFont(font) 592 self.__soap_display.WriteText(txt) 593 self.__soap_display.ShowPosition(0)
594 595 #--------------------------------------------------------
596 - def __calc_episode_tooltip(self, episode):
597 tt = '' 598 tt += gmTools.bool2subst ( 599 (episode['diagnostic_certainty_classification'] is not None), 600 episode.diagnostic_certainty_description + '\n\n', 601 '' 602 ) 603 tt += gmTools.bool2subst ( 604 episode['episode_open'], 605 _('ongoing episode'), 606 _('closed episode'), 607 'error: episode state is None' 608 ) + '\n' 609 tt += gmTools.coalesce(episode['summary'], '', '\n%s') 610 if len(episode['pk_generic_codes']) > 0: 611 tt += '\n' 612 for code in episode.generic_codes: 613 tt += '%s: %s%s%s\n (%s %s)\n' % ( 614 code['code'], 615 gmTools.u_left_double_angle_quote, 616 code['term'], 617 gmTools.u_right_double_angle_quote, 618 code['name_short'], 619 code['version'] 620 ) 621 return tt
622 623 #-------------------------------------------------------- 624 # encounter level 625 #--------------------------------------------------------
626 - def __move_progress_notes(self, evt):
627 encounter = self.GetItemData(self.__curr_node) 628 node_parent = self.GetItemParent(self.__curr_node) 629 episode = self.GetItemData(node_parent) 630 631 gmNarrativeWorkflows.move_progress_notes_to_another_encounter ( 632 parent = self, 633 encounters = [encounter['pk_encounter']], 634 episodes = [episode['pk_episode']] 635 )
636 637 #--------------------------------------------------------
638 - def __edit_progress_notes(self, event):
639 encounter = self.GetItemData(self.__curr_node) 640 node_parent = self.GetItemParent(self.__curr_node) 641 episode = self.GetItemData(node_parent) 642 643 gmNarrativeWorkflows.manage_progress_notes ( 644 parent = self, 645 encounters = [encounter['pk_encounter']], 646 episodes = [episode['pk_episode']] 647 )
648 649 #--------------------------------------------------------
650 - def __edit_encounter_details(self, event):
651 node_data = self.GetItemData(self.__curr_node) 652 gmEncounterWidgets.edit_encounter(parent = self, encounter = node_data) 653 self.__populate_tree()
654 655 #-------------------------------------------------------- 673 674 #--------------------------------------------------------
675 - def __expand_encounter_node(self, encounter_node):
676 self.DeleteChildren(encounter_node) 677 encounter = self.GetItemData(encounter_node) 678 encounter_items = self.__pat.emr.get_generic_emr_items ( 679 pk_encounters = [encounter['pk_encounter']], 680 pk_episodes = [self.GetItemData(self.GetItemParent(encounter_node))['pk_episode']] 681 ) 682 if len(encounter_items) == 0: 683 self.SetItemHasChildren(encounter_node, False) 684 return 685 686 for enc_item in encounter_items: 687 if encounter['started'].year != enc_item['clin_when'].year: 688 when = enc_item['clin_when'].strftime('%Y') 689 elif encounter['started'].month != enc_item['clin_when'].month: 690 when = enc_item['clin_when'].strftime('%b') 691 elif encounter['started'].day != enc_item['clin_when'].day: 692 when = enc_item['clin_when'].strftime('%b %d') 693 else: 694 when = enc_item['clin_when'].strftime('%H:%M') 695 item_node = self.AppendItem(encounter_node, '[%s] %s: %s' % ( 696 enc_item.i18n_soap_cat, 697 when, 698 enc_item.item_type_str 699 )) 700 self.SetItemData(item_node, enc_item) 701 self.SetItemHasChildren(item_node, False)
702 703 # missing: 704 #self.SortChildren(encounter_node) 705 706 #--------------------------------------------------------
707 - def __update_text_for_encounter_node(self, encounter):
708 self.__cb__enable_display_mode_selection(True) 709 epi = self.GetItemData(self.GetItemParent(self.__curr_node)) 710 if self.__soap_display_mode == 'revisions': 711 txt = encounter.formatted_revision_history 712 font = self.__soap_display_mono_font 713 else: 714 txt = encounter.format ( 715 episodes = [epi['pk_episode']], 716 with_soap = True, 717 left_margin = 1, 718 patient = self.__pat, 719 with_co_encountlet_hints = True 720 ) 721 font = self.__soap_display_prop_font 722 self.__soap_display.SetFont(font) 723 self.__soap_display.WriteText(txt) 724 self.__soap_display.ShowPosition(0) 725 self.__img_display.refresh ( 726 document_folder = self.__pat.get_document_folder(), 727 episodes = [ epi['pk_episode'] ], 728 encounter = encounter['pk_encounter'] 729 )
730 731 #--------------------------------------------------------
732 - def __calc_encounter_tooltip(self, encounter):
733 tt = '%s %s %s - %s\n' % ( 734 gmDateTime.pydt_strftime(encounter['started'], '%Y %b %d'), 735 encounter['l10n_type'], 736 encounter['started'].strftime('%H:%M'), 737 encounter['last_affirmed'].strftime('%H:%M') 738 ) 739 if encounter['reason_for_encounter'] is not None: 740 tt += '\n' 741 tt += _('RFE: %s') % encounter['reason_for_encounter'] 742 if len(encounter['pk_generic_codes_rfe']) > 0: 743 for code in encounter.generic_codes_rfe: 744 tt += '\n %s: %s%s%s\n (%s %s)' % ( 745 code['code'], 746 gmTools.u_left_double_angle_quote, 747 code['term'], 748 gmTools.u_right_double_angle_quote, 749 code['name_short'], 750 code['version'] 751 ) 752 if encounter['assessment_of_encounter'] is not None: 753 tt += '\n' 754 tt += _('AOE: %s') % encounter['assessment_of_encounter'] 755 if len(encounter['pk_generic_codes_aoe']) > 0: 756 for code in encounter.generic_codes_aoe: 757 tt += '\n %s: %s%s%s\n (%s %s)' % ( 758 code['code'], 759 gmTools.u_left_double_angle_quote, 760 code['term'], 761 gmTools.u_right_double_angle_quote, 762 code['name_short'], 763 code['version'] 764 ) 765 return tt
766 767 #-------------------------------------------------------- 768 # generic EMR item level 769 #--------------------------------------------------------
770 - def __update_text_for_generic_node(self, generic_item):
771 self.__cb__enable_display_mode_selection(False) 772 txt = gmTools.list2text ( 773 generic_item.format(), 774 strip_leading_empty_lines = False, 775 strip_trailing_empty_lines = False, 776 strip_trailing_whitespace = True, 777 max_line_width = 85 778 ) 779 self.__soap_display.SetFont(self.__soap_display_prop_font) 780 self.__soap_display.WriteText(txt) 781 self.__soap_display.ShowPosition(0)
782 783 #--------------------------------------------------------
784 - def __on_edit_generic_emr_item(self, evt):
785 self.__edit_generic_emr_item()
786 787 #--------------------------------------------------------
788 - def __edit_generic_emr_item(self):
789 instance = self.__curr_node_data.specialized_item 790 if instance is None: 791 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit "%s".') % self.__curr_node_data.item_type_str, beep = True) 792 return False 793 gmGenericEMRItemWorkflows.edit_item_in_dlg(parent = self, item = instance) 794 return True
795 796 #-------------------------------------------------------- 797 # issue level 798 #--------------------------------------------------------
799 - def __edit_issue(self, event):
800 gmEMRStructWidgets.edit_health_issue(parent = self, issue = self.__curr_node_data)
801 802 #--------------------------------------------------------
803 - def __delete_issue(self, event):
804 dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 805 parent = self, 806 id = -1, 807 caption = _('Deleting health issue'), 808 button_defs = [ 809 {'label': _('Yes, delete'), 'tooltip': _('Delete the health issue if possible (it must be completely empty).')}, 810 {'label': _('No, cancel'), 'tooltip': _('Cancel and do NOT delete the health issue.')} 811 ], 812 question = _( 813 'Are you sure you want to delete this health issue ?\n' 814 '\n' 815 ' "%s"\n' 816 ) % self.__curr_node_data['description'] 817 ) 818 result = dlg.ShowModal() 819 if result != wx.ID_YES: 820 dlg.DestroyLater() 821 return 822 823 dlg.DestroyLater() 824 825 if not gmEMRStructItems.delete_health_issue(health_issue = self.__curr_node_data): 826 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete health issue. There is still clinical data recorded for it.'))
827 828 #--------------------------------------------------------
829 - def __expand_issue_to_encounter_level(self, evt):
830 831 if not self.__curr_node.IsOk(): 832 return 833 834 self.Expand(self.__curr_node) 835 836 epi, epi_cookie = self.GetFirstChild(self.__curr_node) 837 while epi.IsOk(): 838 self.Expand(epi) 839 epi, epi_cookie = self.GetNextChild(self.__curr_node, epi_cookie)
840 841 #--------------------------------------------------------
842 - def __expand_issue_node(self, issue_node=None):
843 self.DeleteChildren(issue_node) 844 845 issue = self.GetItemData(issue_node) 846 episodes = self.__pat.emr.get_episodes(issues = [issue['pk_health_issue']]) 847 if len(episodes) == 0: 848 self.SetItemHasChildren(issue_node, False) 849 return 850 851 self.SetItemHasChildren(issue_node, True) 852 853 for episode in episodes: 854 range_str, range_str_verb, duration_str = episode.formatted_clinical_duration 855 episode_node = self.AppendItem(issue_node, '%s (%s)' % ( 856 episode['description'], 857 range_str 858 )) 859 self.SetItemData(episode_node, episode) 860 # assume children so we can try to expand it 861 self.SetItemHasChildren(episode_node, True) 862 863 self.SortChildren(issue_node)
864 865 #--------------------------------------------------------
866 - def __update_text_for_issue_node(self, issue):
867 self.__cb__enable_display_mode_selection(True) 868 if self.__soap_display_mode == 'details': 869 txt = issue.format(left_margin = 1, patient = self.__pat) 870 font = self.__soap_display_prop_font 871 elif self.__soap_display_mode == 'journal': 872 txt = issue.format_as_journal(left_margin = 1) 873 font = self.__soap_display_prop_font 874 elif self.__soap_display_mode == 'revisions': 875 txt = issue.formatted_revision_history 876 font = self.__soap_display_mono_font 877 else: 878 txt = 'invalid SOAP display mode [%s]' % self.__soap_display_mode 879 font = self.__soap_display_prop_font 880 epis = issue.episodes 881 if len(epis) > 0: 882 doc_folder = self.__pat.get_document_folder() 883 self.__img_display.refresh ( 884 document_folder = doc_folder, 885 episodes = [ epi['pk_episode'] for epi in epis ], 886 do_async = True 887 ) 888 self.__soap_display.SetFont(font) 889 self.__soap_display.WriteText(txt) 890 self.__soap_display.ShowPosition(0)
891 892 #--------------------------------------------------------
893 - def __calc_issue_tooltip(self, issue):
894 tt = '' 895 tt += gmTools.bool2subst(issue['is_confidential'], _('*** CONFIDENTIAL ***\n\n'), '') 896 tt += gmTools.bool2subst ( 897 (issue['diagnostic_certainty_classification'] is not None), 898 issue.diagnostic_certainty_description + '\n', 899 '' 900 ) 901 tt += gmTools.bool2subst ( 902 (issue['laterality'] not in [None, 'na']), 903 issue.laterality_description + '\n', 904 '' 905 ) 906 # noted_at_age is too costly 907 tt += gmTools.bool2subst(issue['is_active'], _('active') + '\n', '') 908 tt += gmTools.bool2subst(issue['clinically_relevant'], _('clinically relevant') + '\n', '') 909 tt += gmTools.bool2subst(issue['is_cause_of_death'], _('contributed to death') + '\n', '') 910 tt += gmTools.coalesce(issue['grouping'], '\n', _('Grouping: %s') + '\n') 911 tt += gmTools.coalesce(issue['summary'], '', '\n%s') 912 if len(issue['pk_generic_codes']) > 0: 913 tt += '\n' 914 for code in issue.generic_codes: 915 tt += '%s: %s%s%s\n (%s %s)\n' % ( 916 code['code'], 917 gmTools.u_left_double_angle_quote, 918 code['term'], 919 gmTools.u_right_double_angle_quote, 920 code['name_short'], 921 code['version'] 922 ) 923 return tt
924 925 #--------------------------------------------------------
926 - def __expand_pseudo_issue_node(self, fake_issue_node=None):
927 self.DeleteChildren(fake_issue_node) 928 929 episodes = self.__pat.emr.unlinked_episodes 930 if len(episodes) == 0: 931 self.SetItemHasChildren(fake_issue_node, False) 932 return 933 934 self.SetItemHasChildren(fake_issue_node, True) 935 936 for episode in episodes: 937 range_str, range_str_verb, duration_str = episode.formatted_clinical_duration 938 episode_node = self.AppendItem(fake_issue_node, '%s (%s)' % ( 939 episode['description'], 940 range_str 941 )) 942 self.SetItemData(episode_node, episode) 943 if episode['episode_open']: 944 self.SetItemBold(fake_issue_node, True) 945 # assume children so we can try to expand it 946 self.SetItemHasChildren(episode_node, True) 947 948 self.SortChildren(fake_issue_node)
949 950 #--------------------------------------------------------
951 - def __update_text_for_pseudo_issue_node(self, pseudo_issue):
952 self.__cb__enable_display_mode_selection(True) 953 if self.__soap_display_mode == 'details': 954 txt = _('Pool of unassociated episodes "%s":\n') % pseudo_issue['description'] 955 epis = self.__pat.emr.get_episodes(unlinked_only = True, order_by = 'episode_open DESC, description') 956 if len(epis) > 0: 957 txt += '\n' 958 for epi in epis: 959 txt += epi.format ( 960 left_margin = 1, 961 patient = self.__pat, 962 with_summary = True, 963 with_codes = False, 964 with_encounters = False, 965 with_documents = False, 966 with_hospital_stays = False, 967 with_procedures = False, 968 with_family_history = False, 969 with_tests = False, 970 with_vaccinations = False, 971 with_health_issue = False 972 ) 973 txt += '\n' 974 else: 975 epis = self.__pat.emr.get_episodes(unlinked_only = True, order_by = 'episode_open DESC, description') 976 txt = '' 977 if len(epis) > 0: 978 txt += _(' Listing of unassociated episodes\n') 979 for epi in epis: 980 txt += ' %s\n' % (gmTools.u_box_horiz_4dashes * 60) 981 txt += epi.format ( 982 left_margin = 1, 983 patient = self.__pat, 984 with_summary = False, 985 with_codes = False, 986 with_encounters = False, 987 with_documents = False, 988 with_hospital_stays = False, 989 with_procedures = False, 990 with_family_history = False, 991 with_tests = False, 992 with_vaccinations = False, 993 with_health_issue = False 994 ) 995 txt += '\n' 996 txt += epi.format_as_journal(left_margin = 2) 997 self.__soap_display.SetFont(self.__soap_display_prop_font) 998 self.__soap_display.WriteText(txt) 999 self.__soap_display.ShowPosition(0)
1000 1001 #-------------------------------------------------------- 1002 # EMR level 1003 #--------------------------------------------------------
1004 - def __print_emr(self, event):
1006 1007 #--------------------------------------------------------
1008 - def __create_issue(self, event):
1009 gmEMRStructWidgets.edit_health_issue(parent = self, issue = None)
1010 1011 #--------------------------------------------------------
1012 - def __create_episode(self, event):
1013 gmEMRStructWidgets.edit_episode(parent = self, episode = None)
1014 1015 #--------------------------------------------------------
1016 - def __create_soap_editor(self, event):
1017 self.__cb__select_edit_mode(True) 1018 self.__cb__add_soap_editor(problem = self.__curr_node_data, allow_same_problem = False)
1019 1020 #--------------------------------------------------------
1021 - def __document_allergy(self, event):
1022 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent=self, id=-1) 1023 # FIXME: use signal and use node level update 1024 if dlg.ShowModal() == wx.ID_OK: 1025 self.__expanded_nodes = self.ExpansionState 1026 self.__populate_tree() 1027 dlg.DestroyLater() 1028 return
1029 1030 #--------------------------------------------------------
1031 - def __manage_procedures(self, event):
1033 1034 #--------------------------------------------------------
1035 - def __manage_family_history(self, event):
1037 1038 #--------------------------------------------------------
1039 - def __manage_hospital_stays(self, event):
1041 1042 #--------------------------------------------------------
1043 - def __manage_occupation(self, event):
1045 1046 #--------------------------------------------------------
1047 - def __manage_vaccinations(self, event):
1049 1050 #--------------------------------------------------------
1051 - def __expand_to_issue_level(self, evt):
1052 1053 root_item = self.GetRootItem() 1054 1055 if not root_item.IsOk(): 1056 return 1057 1058 self.Expand(root_item) 1059 1060 # collapse episodes and issues 1061 issue, issue_cookie = self.GetFirstChild(root_item) 1062 while issue.IsOk(): 1063 self.Collapse(issue) 1064 epi, epi_cookie = self.GetFirstChild(issue) 1065 while epi.IsOk(): 1066 self.Collapse(epi) 1067 epi, epi_cookie = self.GetNextChild(issue, epi_cookie) 1068 issue, issue_cookie = self.GetNextChild(root_item, issue_cookie)
1069 1070 #--------------------------------------------------------
1071 - def __expand_to_episode_level(self, evt):
1072 1073 root_item = self.GetRootItem() 1074 1075 if not root_item.IsOk(): 1076 return 1077 1078 self.Expand(root_item) 1079 1080 # collapse episodes, expand issues 1081 issue, issue_cookie = self.GetFirstChild(root_item) 1082 while issue.IsOk(): 1083 self.Expand(issue) 1084 epi, epi_cookie = self.GetFirstChild(issue) 1085 while epi.IsOk(): 1086 self.Collapse(epi) 1087 epi, epi_cookie = self.GetNextChild(issue, epi_cookie) 1088 issue, issue_cookie = self.GetNextChild(root_item, issue_cookie)
1089 1090 #--------------------------------------------------------
1091 - def __expand_to_encounter_level(self, evt):
1092 1093 root_item = self.GetRootItem() 1094 1095 if not root_item.IsOk(): 1096 return 1097 1098 self.Expand(root_item) 1099 1100 # collapse episodes, expand issues 1101 issue, issue_cookie = self.GetFirstChild(root_item) 1102 while issue.IsOk(): 1103 self.Expand(issue) 1104 epi, epi_cookie = self.GetFirstChild(issue) 1105 while epi.IsOk(): 1106 self.Expand(epi) 1107 epi, epi_cookie = self.GetNextChild(issue, epi_cookie) 1108 issue, issue_cookie = self.GetNextChild(root_item, issue_cookie)
1109 1110 #--------------------------------------------------------
1111 - def __export_encounter_for_medistar(self, evt):
1112 gmNarrativeWorkflows.export_narrative_for_medistar_import ( 1113 parent = self, 1114 soap_cats = 'soapu', 1115 encounter = self.__curr_node_data 1116 )
1117 1118 #--------------------------------------------------------
1119 - def __edit_tree_item(self):
1120 self.__curr_node_data = self.GetItemData(self.__curr_node) 1121 if isinstance(self.__curr_node_data, gmGenericEMRItem.cGenericEMRItem): 1122 self.__edit_generic_emr_item() 1123 return
1124 1125 #-------------------------------------------------------- 1126 # root node level 1127 #--------------------------------------------------------
1128 - def __expand_root_node(self):
1129 root_node = self.GetRootItem() 1130 self.DeleteChildren(root_node) 1131 1132 issues = [{ 1133 'description': _('Unattributed episodes'), 1134 'laterality': None, 1135 'diagnostic_certainty_classification': None, 1136 'has_open_episode': False, 1137 'pk_health_issue': None 1138 }] 1139 issues.extend(self.__pat.emr.health_issues) 1140 for issue in issues: 1141 issue_node = self.AppendItem(root_node, '%s%s%s' % ( 1142 issue['description'], 1143 gmTools.coalesce(issue['laterality'], '', ' [%s]', none_equivalents = [None, 'na']), 1144 gmTools.coalesce(issue['diagnostic_certainty_classification'], '', ' [%s]') 1145 )) 1146 self.SetItemBold(issue_node, issue['has_open_episode']) 1147 self.SetItemData(issue_node, issue) 1148 # fake it so we can expand it 1149 self.SetItemHasChildren(issue_node, True) 1150 1151 self.SetItemHasChildren(root_node, (len(issues) != 0)) 1152 self.SortChildren(root_node)
1153 1154 #--------------------------------------------------------
1156 self.__cb__enable_display_mode_selection(True) 1157 if self.__soap_display_mode == 'details': 1158 emr = self.__pat.emr 1159 txt = emr.format_summary() 1160 else: 1161 txt = self.__pat.emr.format_as_journal(left_margin = 1, patient = self.__pat) 1162 self.__soap_display.SetFont(self.__soap_display_prop_font) 1163 self.__soap_display.WriteText(txt) 1164 self.__soap_display.ShowPosition(0)
1165 1166 #-------------------------------------------------------- 1167 # event handlers 1168 #--------------------------------------------------------
1169 - def _on_narrative_mod_db(self, *args, **kwargs):
1170 self.__update_text_for_selected_node()
1171 1172 #--------------------------------------------------------
1173 - def _on_episode_mod_db(self, *args, **kwargs):
1174 self.__expanded_nodes = self.ExpansionState 1175 self.__populate_tree()
1176 1177 #--------------------------------------------------------
1178 - def _on_issue_mod_db(self, *args, **kwargs):
1179 self.__expanded_nodes = self.ExpansionState 1180 self.__populate_tree()
1181 1182 #--------------------------------------------------------
1183 - def _on_tree_item_expanding(self, event):
1184 event.Skip() 1185 1186 node = event.GetItem() 1187 if node == self.GetRootItem(): 1188 self.__expand_root_node() 1189 return 1190 1191 node_data = self.GetItemData(node) 1192 1193 if isinstance(node_data, gmEMRStructItems.cHealthIssue): 1194 self.__expand_issue_node(issue_node = node) 1195 return 1196 1197 if isinstance(node_data, gmEMRStructItems.cEpisode): 1198 self.__expand_episode_node(episode_node = node) 1199 return 1200 1201 # pseudo node "free-standing episodes" 1202 if type(node_data) == type({}): 1203 self.__expand_pseudo_issue_node(fake_issue_node = node) 1204 return 1205 1206 if isinstance(node_data, gmEMRStructItems.cEncounter): 1207 self.__expand_encounter_node(encounter_node = node) 1208 return
1209 1210 #--------------------------------------------------------
1211 - def _on_tree_item_activated(self, event):
1212 event.Skip() 1213 self.__curr_node = event.Item 1214 self.__edit_tree_item()
1215 1216 #--------------------------------------------------------
1217 - def _on_tree_item_selected(self, event):
1218 sel_item = event.GetItem() 1219 self.__curr_node = sel_item 1220 self.__update_text_for_selected_node() 1221 return True
1222 1223 # #-------------------------------------------------------- 1224 # def _on_mouse_motion(self, event): 1225 # 1226 # cursor_pos = (event.GetX(), event.GetY()) 1227 # 1228 # self.SetToolTip(u'') 1229 # 1230 # if cursor_pos != self._old_cursor_pos: 1231 # self._old_cursor_pos = cursor_pos 1232 # (item, flags) = self.HitTest(cursor_pos) 1233 # #if flags != wx.TREE_HITTEST_NOWHERE: 1234 # if flags == wx.TREE_HITTEST_ONITEMLABEL: 1235 # data = self.GetItemData(item) 1236 # 1237 # if not isinstance(data, gmEMRStructItems.cEncounter): 1238 # return 1239 # 1240 # self.SetToolTip(u'%s %s %s - %s\n\nRFE: %s\nAOE: %s' % ( 1241 # gmDateTime.pydt_strftime(data['started'], '%Y %b %d'), 1242 # data['l10n_type'], 1243 # data['started'].strftime('%H:%m'), 1244 # data['last_affirmed'].strftime('%H:%m'), 1245 # gmTools.coalesce(data['reason_for_encounter'], u''), 1246 # gmTools.coalesce(data['assessment_of_encounter'], u'') 1247 # )) 1248 #--------------------------------------------------------
1249 - def _on_tree_item_gettooltip(self, event):
1250 1251 item = event.GetItem() 1252 1253 if not item.IsOk(): 1254 event.SetToolTip(' ') 1255 return 1256 1257 data = self.GetItemData(item) 1258 if isinstance(data, gmEMRStructItems.cEncounter): 1259 tt = self.__calc_encounter_tooltip(data) 1260 elif isinstance(data, gmEMRStructItems.cEpisode): 1261 tt = self.__calc_episode_tooltip(data) 1262 elif isinstance(data, gmEMRStructItems.cHealthIssue): 1263 tt = self.__calc_issue_tooltip(data) 1264 else: 1265 tt = self.__root_tooltip 1266 tt = tt.strip('\n') 1267 if tt == '': 1268 tt = ' ' 1269 event.SetToolTip(tt)
1270 1271 # doing this prevents the tooltip from showing at all 1272 #event.Skip() 1273 1274 #widgetXY.GetToolTip().Enable(False) 1275 # 1276 #seems to work, supposing the tooltip is actually set for the widget, 1277 #otherwise a test would be needed 1278 #if widgetXY.GetToolTip(): 1279 # widgetXY.GetToolTip().Enable(False) 1280 1281 #--------------------------------------------------------
1282 - def _on_tree_item_context_menu(self, event):
1283 """Right button clicked: display the popup for the tree""" 1284 self.__curr_node = event.Item 1285 self.__show_context_menu(pos = event.Point) 1286 event.Skip()
1287 1288 #--------------------------------------------------------
1289 - def OnCompareItems (self, node1=None, node2=None):
1290 """Used in sorting items. 1291 1292 -1: 1 < 2 1293 0: 1 = 2 1294 1: 1 > 2 1295 """ 1296 # FIXME: implement sort modes, chron, reverse cron, by regex, etc 1297 1298 if not node1: 1299 _log.debug('invalid node 1') 1300 return 0 1301 if not node2: 1302 _log.debug('invalid node 2') 1303 return 0 1304 1305 if not node1.IsOk(): 1306 _log.debug('invalid node 1') 1307 return 0 1308 if not node2.IsOk(): 1309 _log.debug('invalid node 2') 1310 return 0 1311 1312 item1 = self.GetItemData(node1) 1313 item2 = self.GetItemData(node2) 1314 1315 # dummy health issue always on top 1316 if isinstance(item1, type({})): 1317 return -1 1318 if isinstance(item2, type({})): 1319 return 1 1320 1321 # encounters: reverse chronologically 1322 if isinstance(item1, gmEMRStructItems.cEncounter): 1323 if item1['started'] == item2['started']: 1324 return 0 1325 if item1['started'] > item2['started']: 1326 return -1 1327 return 1 1328 1329 # episodes: open, then reverse chronologically 1330 if isinstance(item1, gmEMRStructItems.cEpisode): 1331 # open episodes first 1332 if item1['episode_open']: 1333 return -1 1334 if item2['episode_open']: 1335 return 1 1336 start1 = item1.best_guess_clinical_start_date 1337 start2 = item2.best_guess_clinical_start_date 1338 if start1 == start2: 1339 return 0 1340 if start1 < start2: 1341 return 1 1342 return -1 1343 1344 # issues: alpha by grouping, no grouping at the bottom 1345 if isinstance(item1, gmEMRStructItems.cHealthIssue): 1346 1347 # no grouping below grouping 1348 if item1['grouping'] is None: 1349 if item2['grouping'] is not None: 1350 return 1 1351 1352 # grouping above no grouping 1353 if item1['grouping'] is not None: 1354 if item2['grouping'] is None: 1355 return -1 1356 1357 # both no grouping: alpha on description 1358 if (item1['grouping'] is None) and (item2['grouping'] is None): 1359 if item1['description'].lower() < item2['description'].lower(): 1360 return -1 1361 if item1['description'].lower() > item2['description'].lower(): 1362 return 1 1363 return 0 1364 1365 # both with grouping: alpha on grouping, then alpha on description 1366 if item1['grouping'] < item2['grouping']: 1367 return -1 1368 1369 if item1['grouping'] > item2['grouping']: 1370 return 1 1371 1372 if item1['description'].lower() < item2['description'].lower(): 1373 return -1 1374 1375 if item1['description'].lower() > item2['description'].lower(): 1376 return 1 1377 1378 return 0 1379 1380 _log.error('unknown item type during sorting EMR tree:') 1381 _log.error('item1: %s', type(item1)) 1382 _log.error('item2: %s', type(item2)) 1383 1384 return 0
1385 1386 #-------------------------------------------------------- 1387 # properties 1388 #--------------------------------------------------------
1389 - def _get_patient(self):
1390 return self.__pat
1391
1392 - def _set_patient(self, patient):
1393 if self.__pat == patient: 1394 return 1395 self.__pat = patient 1396 if patient is None: 1397 self.clear_tree() 1398 return 1399 return self.__populate_tree()
1400 1401 patient = property(_get_patient, _set_patient) 1402 1403 #--------------------------------------------------------
1404 - def _get_details_display_mode(self):
1405 return self.__soap_display_mode
1406
1407 - def _set_details_display_mode(self, mode):
1408 if mode not in ['details', 'journal', 'revisions']: 1409 raise ValueError('details display mode must be one of "details", "journal", "revisions"') 1410 if self.__soap_display_mode == mode: 1411 return 1412 self.__soap_display_mode = mode 1413 self.__update_text_for_selected_node()
1414 1415 details_display_mode = property(_get_details_display_mode, _set_details_display_mode)
1416 1417 #================================================================ 1418 # FIXME: still needed ? 1419 from Gnumed.wxGladeWidgets import wxgScrolledEMRTreePnl 1420
1421 -class cScrolledEMRTreePnl(wxgScrolledEMRTreePnl.wxgScrolledEMRTreePnl):
1422 """A scrollable panel holding an EMR tree. 1423 1424 Lacks a widget to display details for selected items. The 1425 tree data will be refetched - if necessary - whenever 1426 repopulate_ui() is called, e.g., when the patient is changed. 1427 """
1428 - def __init__(self, *args, **kwds):
1430 1431 #============================================================ 1432 from Gnumed.wxGladeWidgets import wxgSplittedEMRTreeBrowserPnl 1433
1434 -class cSplittedEMRTreeBrowserPnl(wxgSplittedEMRTreeBrowserPnl.wxgSplittedEMRTreeBrowserPnl):
1435 """A splitter window holding an EMR tree. 1436 1437 The left hand side displays a scrollable EMR tree while 1438 on the right details for selected items are displayed. 1439 1440 Expects to be put into a Notebook. 1441 """
1442 - def __init__(self, *args, **kwds):
1443 wxgSplittedEMRTreeBrowserPnl.wxgSplittedEMRTreeBrowserPnl.__init__(self, *args, **kwds) 1444 self._pnl_emr_tree._emr_tree.soap_display = self._TCTRL_item_details 1445 self._pnl_emr_tree._emr_tree.image_display = self._PNL_visual_soap 1446 self._pnl_emr_tree._emr_tree.set_enable_display_mode_selection_callback(self.enable_display_mode_selection) 1447 self._pnl_emr_tree._emr_tree.soap_editor_adder = self._add_soap_editor 1448 self._pnl_emr_tree._emr_tree.edit_mode_selector = self._select_edit_mode 1449 self.__register_events() 1450 1451 self.editing = False
1452 #--------------------------------------------------------
1453 - def __register_events(self):
1454 gmDispatcher.connect(signal = 'pre_patient_unselection', receiver = self._on_pre_patient_unselection) 1455 gmDispatcher.connect(signal = 'post_patient_selection', receiver = self._on_post_patient_selection) 1456 return True
1457 1458 #--------------------------------------------------------
1459 - def _get_editing(self):
1460 return self.__editing
1461
1462 - def _set_editing(self, editing):
1463 self.__editing = editing 1464 self.enable_display_mode_selection(enable = not self.__editing) 1465 if self.__editing: 1466 self._BTN_switch_browse_edit.SetLabel(_('&Browse %s') % gmTools.u_ellipsis) 1467 self._PNL_browse.Hide() 1468 self._PNL_visual_soap.Hide() 1469 self._PNL_edit.Show() 1470 else: 1471 self._BTN_switch_browse_edit.SetLabel(_('&New notes %s') % gmTools.u_ellipsis) 1472 self._PNL_edit.Hide() 1473 self._PNL_visual_soap.Show() 1474 self._PNL_browse.Show() 1475 self._PNL_right_side.GetSizer().Layout()
1476 1477 editing = property(_get_editing, _set_editing) 1478 1479 #-------------------------------------------------------- 1480 # event handler 1481 #--------------------------------------------------------
1483 self._pnl_emr_tree._emr_tree.patient = None 1484 self._PNL_edit.patient = None 1485 return True
1486 1487 #--------------------------------------------------------
1488 - def _on_post_patient_selection(self):
1489 if self.GetParent().GetCurrentPage() != self: 1490 return True 1491 self.repopulate_ui() 1492 return True
1493 1494 #--------------------------------------------------------
1495 - def _on_show_details_selected(self, event):
1496 self._pnl_emr_tree._emr_tree.details_display_mode = 'details'
1497 1498 #--------------------------------------------------------
1499 - def _on_show_journal_selected(self, event):
1500 self._pnl_emr_tree._emr_tree.details_display_mode = 'journal'
1501 1502 #--------------------------------------------------------
1503 - def _on_show_revisions_selected(self, event):
1504 self._pnl_emr_tree._emr_tree.details_display_mode = 'revisions'
1505 1506 #--------------------------------------------------------
1508 self.editing = not self.__editing
1509 1510 #-------------------------------------------------------- 1511 # external API 1512 #--------------------------------------------------------
1513 - def repopulate_ui(self):
1514 """Fills UI with data.""" 1515 pat = gmPerson.gmCurrentPatient() 1516 self._pnl_emr_tree._emr_tree.patient = pat 1517 self._PNL_edit.patient = pat 1518 self._splitter_browser.SetSashPosition(self._splitter_browser.GetSize()[0] // 3, True) 1519 1520 return True
1521 1522 #--------------------------------------------------------
1523 - def enable_display_mode_selection(self, enable):
1524 if self.editing: 1525 enable = False 1526 if enable: 1527 self._RBTN_details.Enable(True) 1528 self._RBTN_journal.Enable(True) 1529 self._RBTN_revisions.Enable(True) 1530 return 1531 self._RBTN_details.Enable(False) 1532 self._RBTN_journal.Enable(False) 1533 self._RBTN_revisions.Enable(False)
1534 1535 #--------------------------------------------------------
1536 - def _add_soap_editor(self, problem=None, allow_same_problem=False):
1537 self._PNL_edit._NB_soap_editors.add_editor(problem = problem, allow_same_problem = allow_same_problem)
1538 1539 #--------------------------------------------------------
1540 - def _select_edit_mode(self, edit=True):
1541 self.editing = edit
1542 1543 #================================================================ 1544 from Gnumed.wxGladeWidgets import wxgEMRJournalPluginPnl 1545
1546 -class cEMRJournalPluginPnl(wxgEMRJournalPluginPnl.wxgEMRJournalPluginPnl):
1547
1548 - def __init__(self, *args, **kwds):
1549 1550 wxgEMRJournalPluginPnl.wxgEMRJournalPluginPnl.__init__(self, *args, **kwds) 1551 self._TCTRL_journal.disable_keyword_expansions() 1552 self._TCTRL_journal.SetValue('')
1553 1554 #-------------------------------------------------------- 1555 # external API 1556 #--------------------------------------------------------
1557 - def repopulate_ui(self):
1558 self._TCTRL_journal.SetValue('') 1559 exporter = gmPatientExporter.cEMRJournalExporter() 1560 if self._RBTN_by_encounter.GetValue(): 1561 fname = exporter.save_to_file_by_encounter(patient = gmPerson.gmCurrentPatient()) 1562 else: 1563 fname = exporter.save_to_file_by_mod_time(patient = gmPerson.gmCurrentPatient()) 1564 1565 f = io.open(fname, mode = 'rt', encoding = 'utf8', errors = 'replace') 1566 for line in f: 1567 self._TCTRL_journal.AppendText(line) 1568 f.close() 1569 1570 self._TCTRL_journal.ShowPosition(self._TCTRL_journal.GetLastPosition()) 1571 return True
1572 #-------------------------------------------------------- 1573 # internal helpers 1574 #--------------------------------------------------------
1575 - def __register_events(self):
1576 gmDispatcher.connect(signal = 'pre_patient_unselection', receiver = self._on_pre_patient_unselection) 1577 gmDispatcher.connect(signal = 'post_patient_selection', receiver = self._on_post_patient_selection) 1578 return True
1579 1580 #-------------------------------------------------------- 1581 # event handlers 1582 #--------------------------------------------------------
1584 self._TCTRL_journal.SetValue('') 1585 return True
1586 1587 #--------------------------------------------------------
1588 - def _on_post_patient_selection(self):
1589 if self.GetParent().GetCurrentPage() != self: 1590 return True 1591 self.repopulate_ui() 1592 return True
1593 1594 #--------------------------------------------------------
1595 - def _on_order_by_encounter_selected(self, event):
1596 self.repopulate_ui()
1597 1598 #--------------------------------------------------------
1599 - def _on_order_by_last_mod_selected(self, event):
1600 self.repopulate_ui()
1601 1602 #--------------------------------------------------------
1603 - def _on_button_find_pressed(self, event):
1604 self._TCTRL_journal.show_find_dialog(title = _('Find text in EMR Journal'))
1605 1606 #================================================================ 1607 from Gnumed.wxGladeWidgets import wxgEMRListJournalPluginPnl 1608
1609 -class cEMRListJournalPluginPnl(wxgEMRListJournalPluginPnl.wxgEMRListJournalPluginPnl):
1610
1611 - def __init__(self, *args, **kwds):
1612 1613 wxgEMRListJournalPluginPnl.wxgEMRListJournalPluginPnl.__init__(self, *args, **kwds) 1614 1615 self._LCTRL_journal.select_callback = self._on_row_selected 1616 self._LCTRL_journal.activate_callback = self._on_row_activated 1617 self._TCTRL_details.SetValue('') 1618 1619 self.__load_timer = gmTimer.cTimer(callback = self._on_load_details, delay = 1000, cookie = 'EMRListJournalPluginDBLoadTimer') 1620 1621 self.__data = {}
1622 1623 #-------------------------------------------------------- 1624 # external API 1625 #--------------------------------------------------------
1626 - def repopulate_ui(self):
1627 self._LCTRL_journal.remove_items_safely() 1628 self._TCTRL_details.SetValue('') 1629 1630 # <pk_episode NULLS FIRST> ensures that health issues get sorted before their episodes 1631 if self._RBTN_by_encounter.Value: 1632 order_by = 'encounter_started, pk_health_issue, pk_episode NULLS FIRST, scr, src_table, modified_when' 1633 date_col_header = _('Encounter') 1634 date_fields = ['encounter_started', 'modified_when'] 1635 elif self._RBTN_by_last_modified.Value: 1636 order_by = 'modified_when, pk_health_issue, pk_episode NULLS FIRST, src_table, scr' 1637 date_col_header = _('Modified') 1638 date_fields = ['modified_when'] 1639 elif self._RBTN_by_item_time.Value: 1640 order_by = 'clin_when, pk_health_issue, pk_episode NULLS FIRST, scr, src_table, modified_when' 1641 date_col_header = _('Happened') 1642 date_fields = ['clin_when', 'modified_when'] 1643 else: 1644 raise ValueError('invalid EMR journal list sort state') 1645 1646 self._LCTRL_journal.set_columns([date_col_header, '', _('Entry'), _('Who / When')]) 1647 self._LCTRL_journal.set_resize_column(3) 1648 1649 # journal = gmPerson.gmCurrentPatient().emr.get_as_journal(order_by = order_by) 1650 journal = gmPerson.gmCurrentPatient().emr.get_generic_emr_items ( 1651 pk_encounters = None, 1652 pk_episodes = None, 1653 pk_health_issues = None, 1654 use_active_encounter = True, 1655 order_by = order_by 1656 ) 1657 1658 # self.__data = {} 1659 items = [] 1660 data = [] 1661 prev_date = None 1662 for entry in journal: 1663 if entry['narrative'].strip() == '': 1664 continue 1665 # self.__register_journal_entry(entry) 1666 soap_cat = gmSoapDefs.soap_cat2l10n[entry['soap_cat']] 1667 who = '%s (%s)' % (entry['modified_by'], entry['date_modified']) 1668 try: 1669 entry_date = gmDateTime.pydt_strftime(entry[date_fields[0]], '%Y-%m-%d') 1670 except KeyError: 1671 entry_date = gmDateTime.pydt_strftime(entry[date_fields[1]], '%Y-%m-%d') 1672 if entry_date == prev_date: 1673 date2show = '' 1674 else: 1675 date2show = entry_date 1676 prev_date = entry_date 1677 lines_of_journal_entry = entry['narrative'].strip().split('\n') 1678 first_line = lines_of_journal_entry[0] 1679 items.append([date2show, soap_cat, first_line.rstrip(), who]) 1680 # data.append ({ 1681 # 'table': entry['src_table'], 1682 # 'pk': entry['src_pk'] 1683 # }) 1684 data.append(entry) 1685 for line in lines_of_journal_entry[1:]: # skip first line 1686 if line.strip() == '': 1687 continue 1688 # only first line carries metadata 1689 items.append(['', '', line.rstrip(), '']) 1690 # data.append ({ 1691 # 'table': entry['src_table'], 1692 # 'pk': entry['src_pk'] 1693 # }) 1694 data.append(entry) 1695 1696 self._LCTRL_journal.set_string_items(items) 1697 # maybe add coloring per-entry ? 1698 #for item_idx in range(self._LCTRL_journal.ItemCount): 1699 # self._LCTRL_journal.SetItemBackgroundColour(item_idx, 'green') 1700 self._LCTRL_journal.set_column_widths([wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE_USEHEADER]) 1701 self._LCTRL_journal.set_data(data) 1702 1703 self._LCTRL_journal.SetFocus() 1704 return True
1705 1706 #-------------------------------------------------------- 1707 # internal helpers 1708 #--------------------------------------------------------
1709 - def __register_events(self):
1710 gmDispatcher.connect(signal = 'pre_patient_unselection', receiver = self._on_pre_patient_unselection) 1711 gmDispatcher.connect(signal = 'post_patient_selection', receiver = self._on_post_patient_selection) 1712 return True
1713 1714 #--------------------------------------------------------
1715 - def __register_journal_entry(self, entry):
1716 if entry['src_table'] in self.__data: 1717 if entry['src_pk'] in self.__data[entry['src_table']]: 1718 return 1719 1720 else: 1721 self.__data[entry['src_table']] = {} 1722 1723 self.__data[entry['src_table']][entry['src_pk']] = {} 1724 self.__data[entry['src_table']][entry['src_pk']]['entry'] = entry 1725 self.__data[entry['src_table']][entry['src_pk']]['formatted_instance'] = None 1726 if entry['encounter_started'] is None: 1727 enc_duration = gmTools.u_diameter 1728 else: 1729 enc_duration = '%s - %s' % ( 1730 gmDateTime.pydt_strftime(entry['encounter_started'], '%Y %b %d %H:%M'), 1731 gmDateTime.pydt_strftime(entry['encounter_last_affirmed'], '%H:%M') 1732 ) 1733 self.__data[entry['src_table']][entry['src_pk']]['formatted_header'] = _( 1734 'Chart entry: %s [#%s in %s]\n' 1735 ' Modified: %s by %s (%s rev %s)\n' 1736 '\n' 1737 'Health issue: %s%s\n' 1738 'Episode: %s%s\n' 1739 'Encounter: %s%s' 1740 ) % ( 1741 gmGenericEMRItem.generic_item_type_str(entry['src_table']), 1742 entry['src_pk'], 1743 entry['src_table'], 1744 entry['date_modified'], 1745 entry['modified_by'], 1746 gmTools.u_arrow2right, 1747 entry['row_version'], 1748 gmTools.coalesce(entry['health_issue'], gmTools.u_diameter, '%s'), 1749 gmTools.bool2subst(entry['issue_active'], ' (' + _('active') + ')', ' (' + _('inactive') + ')', ''), 1750 gmTools.coalesce(entry['episode'], gmTools.u_diameter, '%s'), 1751 gmTools.bool2subst(entry['episode_open'], ' (' + _('open') + ')', ' (' + _('closed') + ')', ''), 1752 enc_duration, 1753 gmTools.coalesce(entry['encounter_l10n_type'], '', ' (%s)'), 1754 ) 1755 self.__data[entry['src_table']][entry['src_pk']]['formatted_root_item'] = _( 1756 '%s\n' 1757 '\n' 1758 ' rev %s (%s) by %s in <%s>' 1759 ) % ( 1760 entry['narrative'].strip(), 1761 entry['row_version'], 1762 entry['date_modified'], 1763 entry['modified_by'], 1764 entry['src_table'] 1765 )
1766 1767 #-------------------------------------------------------- 1768 # event handlers 1769 #--------------------------------------------------------
1771 self._LCTRL_journal.remove_items_safely() 1772 self._TCTRL_details.SetValue('') 1773 self.__data = {} 1774 return True
1775 1776 #--------------------------------------------------------
1777 - def _on_post_patient_selection(self):
1778 if self.GetParent().GetCurrentPage() != self: 1779 return True 1780 self.repopulate_ui() 1781 return True
1782 1783 #--------------------------------------------------------
1784 - def _on_row_activated(self, evt):
1785 data = self._LCTRL_journal.get_item_data(item_idx = evt.Index) 1786 instance = data.specialized_item 1787 if instance is None: 1788 return 1789 if gmGenericEMRItemWorkflows.edit_item_in_dlg(parent = self, item = instance): 1790 self.repopulate_ui()
1791 1792 #--------------------------------------------------------
1793 - def _on_row_selected(self, evt):
1794 data = self._LCTRL_journal.get_item_data(item_idx = evt.Index) 1795 self._TCTRL_details.SetValue(data.format(eol = '\n')) 1796 # FIXME: fire off get-details 1797 return 1798 1799 data = self._LCTRL_journal.get_item_data(item_idx = evt.Index) 1800 if self.__data[data['table']][data['pk']]['formatted_instance'] is None: 1801 txt = _( 1802 '%s\n' 1803 '%s\n' 1804 '%s' 1805 ) % ( 1806 self.__data[data['table']][data['pk']]['formatted_header'], 1807 gmTools.u_box_horiz_4dashes * 40, 1808 self.__data[data['table']][data['pk']]['formatted_root_item'] 1809 ) 1810 self._TCTRL_details.SetValue(txt) 1811 self.__load_timer.Stop() 1812 self.__load_timer.Start(oneShot = True) 1813 return 1814 1815 txt = _( 1816 '%s\n' 1817 '%s\n' 1818 '%s' 1819 ) % ( 1820 self.__data[data['table']][data['pk']]['formatted_header'], 1821 gmTools.u_box_horiz_4dashes * 40, 1822 self.__data[data['table']][data['pk']]['formatted_instance'] 1823 ) 1824 self._TCTRL_details.SetValue(txt)
1825 1826 #--------------------------------------------------------
1827 - def _on_load_details(self, cookie):
1828 data = self._LCTRL_journal.get_selected_item_data(only_one = True) 1829 if self.__data[data['table']][data['pk']]['formatted_instance'] is None: 1830 self.__data[data['table']][data['pk']]['formatted_instance'] = gmClinicalRecord.format_clin_root_item(data['table'], data['pk'], patient = gmPerson.gmCurrentPatient()) 1831 txt = _( 1832 '%s\n' 1833 '%s\n' 1834 '%s' 1835 ) % ( 1836 self.__data[data['table']][data['pk']]['formatted_header'], 1837 gmTools.u_box_horiz_4dashes * 40, 1838 self.__data[data['table']][data['pk']]['formatted_instance'] 1839 ) 1840 wx.CallAfter(self._TCTRL_details.SetValue, txt)
1841 1842 #--------------------------------------------------------
1843 - def _on_order_by_encounter_selected(self, event):
1844 self.repopulate_ui()
1845 1846 #--------------------------------------------------------
1847 - def _on_order_by_last_mod_selected(self, event):
1848 self.repopulate_ui()
1849 1850 #--------------------------------------------------------
1851 - def _on_order_by_item_time_selected(self, event):
1852 self.repopulate_ui()
1853 1854 #--------------------------------------------------------
1855 - def _on_order_by_item_time_selected(self, event):
1856 event.Skip() 1857 self.repopulate_ui()
1858 1859 #--------------------------------------------------------
1860 - def _on_edit_button_pressed(self, event):
1861 event.Skip()
1862 1863 #--------------------------------------------------------
1864 - def _on_delete_button_pressed(self, event):
1865 event.Skip()
1866 1867 #-------------------------------------------------------- 1868 # def _on_button_find_pressed(self, event): 1869 # self._TCTRL_details.show_find_dialog(title = _('Find text in EMR Journal')) 1870 1871 #================================================================ 1872 # MAIN 1873 #---------------------------------------------------------------- 1874 if __name__ == '__main__': 1875 1876 _log.info("starting emr browser...") 1877 1878 # obtain patient 1879 patient = gmPersonSearch.ask_for_patient() 1880 if patient is None: 1881 print("No patient. Exiting gracefully...") 1882 sys.exit(0) 1883 gmPatSearchWidgets.set_active_patient(patient = patient) 1884 1885 # display standalone browser 1886 application = wx.PyWidgetTester(size=(800,600)) 1887 emr_browser = cEMRBrowserPanel(application.frame, -1) 1888 emr_browser.refresh_tree() 1889 1890 application.frame.Show(True) 1891 application.MainLoop() 1892 1893 # clean up 1894 if patient is not None: 1895 try: 1896 patient.cleanup() 1897 except Exception: 1898 print("error cleaning up patient") 1899 1900 _log.info("closing emr browser...") 1901 1902 #================================================================ 1903