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