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 StringIO 
  10  import codecs 
  11  import logging 
  12   
  13   
  14  # 3rd party 
  15  import wx 
  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 gmEMRStructItems 
  29  from Gnumed.business import gmPerson 
  30  from Gnumed.business import gmSOAPimporter 
  31  from Gnumed.business import gmPersonSearch 
  32   
  33  from Gnumed.wxpython import gmGuiHelpers 
  34  from Gnumed.wxpython import gmEMRStructWidgets 
  35  from Gnumed.wxpython import gmSOAPWidgets 
  36  from Gnumed.wxpython import gmAllergyWidgets 
  37  from Gnumed.wxpython import gmDemographicsWidgets 
  38  from Gnumed.wxpython import gmNarrativeWidgets 
  39  from Gnumed.wxpython import gmPatSearchWidgets 
  40  from Gnumed.wxpython import gmVaccWidgets 
  41  from Gnumed.wxpython import gmFamilyHistoryWidgets 
  42   
  43   
  44  _log = logging.getLogger('gm.ui') 
  45   
  46  #============================================================ 
47 -def export_emr_to_ascii(parent=None):
48 """ 49 Dump the patient's EMR from GUI client 50 @param parent - The parent widget 51 @type parent - A wx.Window instance 52 """ 53 # sanity checks 54 if parent is None: 55 raise TypeError('expected wx.Window instance as parent, got <None>') 56 57 pat = gmPerson.gmCurrentPatient() 58 if not pat.connected: 59 gmDispatcher.send(signal='statustext', msg=_('Cannot export EMR. No active patient.')) 60 return False 61 62 # get file name 63 wc = "%s (*.txt)|*.txt|%s (*)|*" % (_("text files"), _("all files")) 64 defdir = os.path.abspath(os.path.expanduser(os.path.join('~', 'gnumed', pat['dirname']))) 65 gmTools.mkdir(defdir) 66 fname = '%s-%s_%s.txt' % (_('emr-export'), pat['lastnames'], pat['firstnames']) 67 dlg = wx.FileDialog ( 68 parent = parent, 69 message = _("Save patient's EMR as..."), 70 defaultDir = defdir, 71 defaultFile = fname, 72 wildcard = wc, 73 style = wx.SAVE 74 ) 75 choice = dlg.ShowModal() 76 fname = dlg.GetPath() 77 dlg.Destroy() 78 if choice != wx.ID_OK: 79 return None 80 81 _log.debug('exporting EMR to [%s]', fname) 82 83 output_file = codecs.open(fname, 'wb', encoding='utf8', errors='replace') 84 exporter = gmPatientExporter.cEmrExport(patient = pat) 85 exporter.set_output_file(output_file) 86 exporter.dump_constraints() 87 exporter.dump_demographic_record(True) 88 exporter.dump_clinical_record() 89 exporter.dump_med_docs() 90 output_file.close() 91 92 gmDispatcher.send('statustext', msg = _('EMR successfully exported to file: %s') % fname, beep = False) 93 return fname
94 #============================================================
95 -class cEMRTree(wx.TreeCtrl, gmGuiHelpers.cTreeExpansionHistoryMixin):
96 """This wx.TreeCtrl derivative displays a tree view of the medical record.""" 97 98 #--------------------------------------------------------
99 - def __init__(self, parent, id, *args, **kwds):
100 """Set up our specialised tree. 101 """ 102 kwds['style'] = wx.TR_HAS_BUTTONS | wx.NO_BORDER | wx.TR_SINGLE 103 wx.TreeCtrl.__init__(self, parent, id, *args, **kwds) 104 105 gmGuiHelpers.cTreeExpansionHistoryMixin.__init__(self) 106 107 self.__soap_display = None 108 self.__soap_display_mode = u'details' # "details" or "journal" 109 self.__img_display = None 110 self.__cb__enable_display_mode_selection = lambda x:x 111 self.__cb__select_edit_mode = lambda x:x 112 self.__cb__add_soap_editor = lambda x:x 113 self.__pat = gmPerson.gmCurrentPatient() 114 self.__curr_node = None 115 116 self._old_cursor_pos = None 117 118 self.__make_popup_menus() 119 self.__register_events()
120 #-------------------------------------------------------- 121 # external API 122 #--------------------------------------------------------
123 - def refresh(self):
124 if not self.__pat.connected: 125 gmDispatcher.send(signal='statustext', msg=_('Cannot load clinical narrative. No active patient.'),) 126 return False 127 128 if not self.__populate_tree(): 129 return False 130 131 return True
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 139 soap_display = property(_get_soap_display, _set_soap_display) 140 #--------------------------------------------------------
141 - def _get_image_display(self):
142 return self.__img_display
143
144 - def _set_image_display(self, image_display=None):
145 self.__img_display = image_display
146 147 image_display = property(_get_image_display, _set_image_display) 148 #--------------------------------------------------------
150 if not callable(callback): 151 raise ValueError('callback [%s] not callable' % callback) 152 self.__cb__enable_display_mode_selection = callback
153 #--------------------------------------------------------
154 - def _set_edit_mode_selector(self, callback):
155 if callback is None: 156 callback = lambda x:x 157 if not callable(callback): 158 raise ValueError('edit mode selector [%s] not callable' % callback) 159 self.__cb__select_edit_mode = callback
160 161 edit_mode_selector = property(lambda x:x, _set_edit_mode_selector) 162 #--------------------------------------------------------
163 - def _set_soap_editor_adder(self, callback):
164 if callback is None: 165 callback = lambda x:x 166 if not callable(callback): 167 raise ValueError('soap editor adder [%s] not callable' % callback) 168 self.__cb__add_soap_editor = callback
169 170 soap_editor_adder = property(lambda x:x, _set_soap_editor_adder) 171 #-------------------------------------------------------- 172 #-------------------------------------------------------- 173 # internal helpers 174 #--------------------------------------------------------
175 - def __register_events(self):
176 """Configures enabled event signals.""" 177 wx.EVT_TREE_SEL_CHANGED (self, self.GetId(), self._on_tree_item_selected) 178 wx.EVT_TREE_ITEM_RIGHT_CLICK (self, self.GetId(), self._on_tree_item_right_clicked) 179 self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self._on_tree_item_expanding) 180 181 # handle tooltips 182 # wx.EVT_MOTION(self, self._on_mouse_motion) 183 wx.EVT_TREE_ITEM_GETTOOLTIP(self, -1, self._on_tree_item_gettooltip) 184 185 gmDispatcher.connect(signal = 'narrative_mod_db', receiver = self._on_narrative_mod_db) 186 gmDispatcher.connect(signal = 'episode_mod_db', receiver = self._on_episode_mod_db) 187 gmDispatcher.connect(signal = 'health_issue_mod_db', receiver = self._on_issue_mod_db) 188 gmDispatcher.connect(signal = 'family_history_mod_db', receiver = self._on_issue_mod_db)
189 #--------------------------------------------------------
190 - def clear_tree(self):
191 self.DeleteAllItems()
192 #--------------------------------------------------------
193 - def __populate_tree(self):
194 """Updates EMR browser data.""" 195 # FIXME: auto select the previously self.__curr_node if not None 196 # FIXME: error handling 197 198 if not self.__pat.connected: 199 return 200 201 wx.BeginBusyCursor() 202 # self.snapshot_expansion() 203 # init new tree 204 root_item = self.__populate_root_node() 205 # self.__exporter.get_historical_tree(self) # this is slow 206 self.__curr_node = root_item 207 self.SelectItem(root_item) 208 self.Expand(root_item) 209 self.__update_text_for_selected_node() # this is fairly slow, too 210 # self.restore_expansion() 211 wx.EndBusyCursor() 212 return True
213 #--------------------------------------------------------
214 - def __populate_root_node(self):
215 216 self.DeleteAllItems() 217 218 root_item = self.AddRoot(_('EMR of %(lastnames)s, %(firstnames)s') % self.__pat.get_active_name()) 219 self.SetItemPyData(root_item, None) 220 self.SetItemHasChildren(root_item, True) 221 222 self.__root_tooltip = self.__pat['description_gender'] + u'\n' 223 if self.__pat['deceased'] is None: 224 self.__root_tooltip += u' %s (%s)\n\n' % ( 225 self.__pat.get_formatted_dob(format = '%d %b %Y', encoding = gmI18N.get_encoding()), 226 self.__pat['medical_age'] 227 ) 228 else: 229 template = u' %s - %s (%s)\n\n' 230 self.__root_tooltip += template % ( 231 self.__pat.get_formatted_dob(format = '%d.%b %Y', encoding = gmI18N.get_encoding()), 232 gmDateTime.pydt_strftime(self.__pat['deceased'], '%Y %b %d'), 233 self.__pat['medical_age'] 234 ) 235 self.__root_tooltip += gmTools.coalesce(self.__pat['comment'], u'', u'%s\n\n') 236 doc = self.__pat.primary_provider 237 if doc is not None: 238 self.__root_tooltip += u'%s:\n' % _('Primary provider in this praxis') 239 self.__root_tooltip += u' %s %s %s (%s)%s\n\n' % ( 240 gmTools.coalesce(doc['title'], gmPerson.map_gender2salutation(gender = doc['gender'])), 241 doc['firstnames'], 242 doc['lastnames'], 243 doc['short_alias'], 244 gmTools.bool2subst(doc['is_active'], u'', u' [%s]' % _('inactive')) 245 ) 246 if not ((self.__pat['emergency_contact'] is None) and (self.__pat['pk_emergency_contact'] is None)): 247 self.__root_tooltip += _('In case of emergency contact:') + u'\n' 248 if self.__pat['emergency_contact'] is not None: 249 self.__root_tooltip += gmTools.wrap ( 250 text = u'%s\n' % self.__pat['emergency_contact'], 251 width = 60, 252 initial_indent = u' ', 253 subsequent_indent = u' ' 254 ) 255 if self.__pat['pk_emergency_contact'] is not None: 256 contact = self.__pat.emergency_contact_in_database 257 self.__root_tooltip += u' %s\n' % contact['description_gender'] 258 self.__root_tooltip = self.__root_tooltip.strip('\n') 259 if self.__root_tooltip == u'': 260 self.__root_tooltip = u' ' 261 262 return root_item
263 #--------------------------------------------------------
265 """Displays information for the selected tree node.""" 266 267 if self.__soap_display is None: 268 self.__img_display.clear() 269 return 270 271 if self.__curr_node is None: 272 self.__img_display.clear() 273 return 274 275 if not self.__curr_node.IsOk(): 276 return 277 278 try: 279 node_data = self.GetPyData(self.__curr_node) 280 except wx.PyAssertionError: 281 node_data = None # fake a root node 282 _log.exception('unfathomable self.GetPyData() problem occurred, faking root node') 283 _log.error('real node: %s', self.__curr_node) 284 _log.error('node.IsOk(): %s', self.__curr_node.IsOk()) # already survived this further up 285 _log.error('is root node: %s', self.__curr_node == self.GetRootItem()) 286 _log.error('node.m_pItem: %s', getattr(self.__curr_node, 'm_pItem', '<NO SUCH ATTRIBUTE>')) 287 _log.error('node attributes: %s', dir(self.__curr_node)) 288 gmLog2.log_stack_trace() 289 doc_folder = self.__pat.get_document_folder() 290 291 if isinstance(node_data, gmEMRStructItems.cHealthIssue): 292 self.__cb__enable_display_mode_selection(True) 293 if self.__soap_display_mode == u'details': 294 txt = node_data.format(left_margin=1, patient = self.__pat) 295 else: 296 txt = node_data.format_as_journal(left_margin = 1) 297 298 self.__img_display.refresh ( 299 document_folder = doc_folder, 300 episodes = [ epi['pk_episode'] for epi in node_data.episodes ] 301 ) 302 303 elif isinstance(node_data, type({})): 304 self.__cb__enable_display_mode_selection(False) 305 # FIXME: turn into real dummy issue 306 txt = _('Pool of unassociated episodes:\n\n "%s"') % node_data['description'] 307 self.__img_display.clear() 308 309 elif isinstance(node_data, gmEMRStructItems.cEpisode): 310 self.__cb__enable_display_mode_selection(True) 311 if self.__soap_display_mode == u'details': 312 txt = node_data.format(left_margin = 1, patient = self.__pat) 313 else: 314 txt = node_data.format_as_journal(left_margin = 1) 315 self.__img_display.refresh ( 316 document_folder = doc_folder, 317 episodes = [node_data['pk_episode']] 318 ) 319 320 elif isinstance(node_data, gmEMRStructItems.cEncounter): 321 self.__cb__enable_display_mode_selection(False) 322 epi = self.GetPyData(self.GetItemParent(self.__curr_node)) 323 txt = node_data.format ( 324 episodes = [epi['pk_episode']], 325 with_soap = True, 326 left_margin = 1, 327 patient = self.__pat, 328 with_co_encountlet_hints = True 329 ) 330 self.__img_display.refresh ( 331 document_folder = doc_folder, 332 episodes = [epi['pk_episode']], 333 encounter = node_data['pk_encounter'] 334 ) 335 336 # root node == EMR level 337 else: 338 self.__cb__enable_display_mode_selection(True) 339 if self.__soap_display_mode == u'details': 340 emr = self.__pat.get_emr() 341 txt = emr.format_summary(dob = self.__pat['dob']) 342 else: 343 txt = self.__pat.emr.format_as_journal(left_margin = 1, patient = self.__pat) 344 self.__img_display.clear() 345 346 self.__soap_display.Clear() 347 self.__soap_display.WriteText(txt) 348 self.__soap_display.ShowPosition(0)
349 #--------------------------------------------------------
350 - def __make_popup_menus(self):
351 352 # - root node 353 self.__root_context_popup = wx.Menu(title = _('EMR Actions:')) 354 355 menu_id = wx.NewId() 356 self.__root_context_popup.AppendItem(wx.MenuItem(self.__root_context_popup, menu_id, _('Create health issue'))) 357 wx.EVT_MENU(self.__root_context_popup, menu_id, self.__create_issue) 358 359 item = self.__root_context_popup.Append(-1, _('Create episode')) 360 self.Bind(wx.EVT_MENU, self.__create_episode, item) 361 362 item = self.__root_context_popup.Append(-1, _('Create progress note')) 363 self.Bind(wx.EVT_MENU, self.__create_soap_editor, item) 364 365 menu_id = wx.NewId() 366 self.__root_context_popup.AppendItem(wx.MenuItem(self.__root_context_popup, menu_id, _('Manage allergies'))) 367 wx.EVT_MENU(self.__root_context_popup, menu_id, self.__document_allergy) 368 369 menu_id = wx.NewId() 370 self.__root_context_popup.AppendItem(wx.MenuItem(self.__root_context_popup, menu_id, _('Manage family history'))) 371 wx.EVT_MENU(self.__root_context_popup, menu_id, self.__manage_family_history) 372 373 menu_id = wx.NewId() 374 self.__root_context_popup.AppendItem(wx.MenuItem(self.__root_context_popup, menu_id, _('Manage hospitalizations'))) 375 wx.EVT_MENU(self.__root_context_popup, menu_id, self.__manage_hospital_stays) 376 377 menu_id = wx.NewId() 378 self.__root_context_popup.AppendItem(wx.MenuItem(self.__root_context_popup, menu_id, _('Manage occupation'))) 379 wx.EVT_MENU(self.__root_context_popup, menu_id, self.__manage_occupation) 380 381 menu_id = wx.NewId() 382 self.__root_context_popup.AppendItem(wx.MenuItem(self.__root_context_popup, menu_id, _('Manage procedures'))) 383 wx.EVT_MENU(self.__root_context_popup, menu_id, self.__manage_procedures) 384 385 menu_id = wx.NewId() 386 self.__root_context_popup.AppendItem(wx.MenuItem(self.__root_context_popup, menu_id, _('Manage vaccinations'))) 387 wx.EVT_MENU(self.__root_context_popup, menu_id, self.__manage_vaccinations) 388 389 self.__root_context_popup.AppendSeparator() 390 391 # expand tree 392 expand_menu = wx.Menu() 393 self.__root_context_popup.AppendMenu(wx.NewId(), _('Open EMR to ...'), expand_menu) 394 395 menu_id = wx.NewId() 396 expand_menu.AppendItem(wx.MenuItem(expand_menu, menu_id, _('... issue level'))) 397 wx.EVT_MENU(expand_menu, menu_id, self.__expand_to_issue_level) 398 399 menu_id = wx.NewId() 400 expand_menu.AppendItem(wx.MenuItem(expand_menu, menu_id, _('... episode level'))) 401 wx.EVT_MENU(expand_menu, menu_id, self.__expand_to_episode_level) 402 403 menu_id = wx.NewId() 404 expand_menu.AppendItem(wx.MenuItem(expand_menu, menu_id, _('... encounter level'))) 405 wx.EVT_MENU(expand_menu, menu_id, self.__expand_to_encounter_level) 406 407 # - health issues 408 self.__issue_context_popup = wx.Menu(title = _('Health Issue Actions:')) 409 410 menu_id = wx.NewId() 411 self.__issue_context_popup.AppendItem(wx.MenuItem(self.__issue_context_popup, menu_id, _('Edit details'))) 412 wx.EVT_MENU(self.__issue_context_popup, menu_id, self.__edit_issue) 413 414 menu_id = wx.NewId() 415 self.__issue_context_popup.AppendItem(wx.MenuItem(self.__issue_context_popup, menu_id, _('Delete'))) 416 wx.EVT_MENU(self.__issue_context_popup, menu_id, self.__delete_issue) 417 418 self.__issue_context_popup.AppendSeparator() 419 420 menu_id = wx.NewId() 421 self.__issue_context_popup.AppendItem(wx.MenuItem(self.__issue_context_popup, menu_id, _('Open to encounter level'))) 422 wx.EVT_MENU(self.__issue_context_popup, menu_id, self.__expand_issue_to_encounter_level) 423 # print " attach issue to another patient" 424 # print " move all episodes to another issue" 425 426 item = self.__issue_context_popup.Append(-1, _('Create progress note')) 427 self.Bind(wx.EVT_MENU, self.__create_soap_editor, item) 428 429 # - episodes 430 self.__epi_context_popup = wx.Menu(title = _('Episode Actions:')) 431 432 menu_id = wx.NewId() 433 self.__epi_context_popup.AppendItem(wx.MenuItem(self.__epi_context_popup, menu_id, _('Edit details'))) 434 wx.EVT_MENU(self.__epi_context_popup, menu_id, self.__edit_episode) 435 436 menu_id = wx.NewId() 437 self.__epi_context_popup.AppendItem(wx.MenuItem(self.__epi_context_popup, menu_id, _('Delete'))) 438 wx.EVT_MENU(self.__epi_context_popup, menu_id, self.__delete_episode) 439 440 menu_id = wx.NewId() 441 self.__epi_context_popup.AppendItem(wx.MenuItem(self.__epi_context_popup, menu_id, _('Promote'))) 442 wx.EVT_MENU(self.__epi_context_popup, menu_id, self.__promote_episode_to_issue) 443 444 item = self.__epi_context_popup.Append(-1, _('Create progress note')) 445 self.Bind(wx.EVT_MENU, self.__create_soap_editor, item) 446 447 menu_id = wx.NewId() 448 self.__epi_context_popup.AppendItem(wx.MenuItem(self.__epi_context_popup, menu_id, _('Move encounters'))) 449 wx.EVT_MENU(self.__epi_context_popup, menu_id, self.__move_encounters) 450 451 # - encounters 452 self.__enc_context_popup = wx.Menu(title = _('Encounter Actions:')) 453 # - move data 454 menu_id = wx.NewId() 455 self.__enc_context_popup.AppendItem(wx.MenuItem(self.__enc_context_popup, menu_id, _('Move data to another episode'))) 456 wx.EVT_MENU(self.__enc_context_popup, menu_id, self.__relink_encounter_data2episode) 457 # - edit encounter details 458 menu_id = wx.NewId() 459 self.__enc_context_popup.AppendItem(wx.MenuItem(self.__enc_context_popup, menu_id, _('Edit details'))) 460 wx.EVT_MENU(self.__enc_context_popup, menu_id, self.__edit_encounter_details) 461 462 # would require pre-configurable save-under which we don't have 463 #item = self.__enc_context_popup.Append(-1, _('Create progress note')) 464 #self.Bind(wx.EVT_MENU, self.__create_soap_editor, item) 465 466 item = self.__enc_context_popup.Append(-1, _('Edit progress notes')) 467 self.Bind(wx.EVT_MENU, self.__edit_progress_notes, item) 468 469 item = self.__enc_context_popup.Append(-1, _('Move progress notes')) 470 self.Bind(wx.EVT_MENU, self.__move_progress_notes, item) 471 472 item = self.__enc_context_popup.Append(-1, _('Export for Medistar')) 473 self.Bind(wx.EVT_MENU, self.__export_encounter_for_medistar, item)
474 #--------------------------------------------------------
475 - def __handle_root_context(self, pos=wx.DefaultPosition):
476 self.PopupMenu(self.__root_context_popup, pos)
477 #--------------------------------------------------------
478 - def __handle_issue_context(self, pos=wx.DefaultPosition):
479 # self.__issue_context_popup.SetTitle(_('Episode %s') % episode['description']) 480 self.PopupMenu(self.__issue_context_popup, pos)
481 #--------------------------------------------------------
482 - def __handle_episode_context(self, pos=wx.DefaultPosition):
483 # self.__epi_context_popup.SetTitle(_('Episode %s') % self.__curr_node_data['description']) 484 self.PopupMenu(self.__epi_context_popup, pos)
485 #--------------------------------------------------------
486 - def __handle_encounter_context(self, pos=wx.DefaultPosition):
487 self.PopupMenu(self.__enc_context_popup, pos)
488 #-------------------------------------------------------- 489 # episode level 490 #--------------------------------------------------------
491 - def __move_encounters(self, event):
492 episode = self.GetPyData(self.__curr_node) 493 494 gmNarrativeWidgets.move_progress_notes_to_another_encounter ( 495 parent = self, 496 episodes = [episode['pk_episode']], 497 move_all = True 498 )
499 #--------------------------------------------------------
500 - def __edit_episode(self, event):
501 gmEMRStructWidgets.edit_episode(parent = self, episode = self.__curr_node_data)
502 #--------------------------------------------------------
503 - def __promote_episode_to_issue(self, evt):
504 pat = gmPerson.gmCurrentPatient() 505 gmEMRStructWidgets.promote_episode_to_issue(parent=self, episode = self.__curr_node_data, emr = pat.get_emr())
506 #--------------------------------------------------------
507 - def __delete_episode(self, event):
508 dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 509 parent = self, 510 id = -1, 511 caption = _('Deleting episode'), 512 button_defs = [ 513 {'label': _('Yes, delete'), 'tooltip': _('Delete the episode if possible (it must be completely empty).')}, 514 {'label': _('No, cancel'), 'tooltip': _('Cancel and do NOT delete the episode.')} 515 ], 516 question = _( 517 'Are you sure you want to delete this episode ?\n' 518 '\n' 519 ' "%s"\n' 520 ) % self.__curr_node_data['description'] 521 ) 522 result = dlg.ShowModal() 523 if result != wx.ID_YES: 524 return 525 526 if not gmEMRStructItems.delete_episode(episode = self.__curr_node_data): 527 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete episode. There is still clinical data recorded for it.'))
528 #--------------------------------------------------------
529 - def __expand_episode_node(self, episode_node=None):
530 self.DeleteChildren(episode_node) 531 532 emr = self.__pat.emr 533 epi = self.GetPyData(episode_node) 534 encounters = emr.get_encounters(episodes = [epi['pk_episode']], skip_empty = True) 535 if len(encounters) == 0: 536 self.SetItemHasChildren(episode_node, False) 537 return 538 539 self.SetItemHasChildren(episode_node, True) 540 541 for enc in encounters: 542 label = u'%s: %s' % ( 543 enc['started'].strftime('%Y-%m-%d'), 544 gmTools.unwrap ( 545 gmTools.coalesce ( 546 gmTools.coalesce ( 547 gmTools.coalesce ( 548 enc.get_latest_soap ( # soAp 549 soap_cat = 'a', 550 episode = epi['pk_episode'] 551 ), 552 enc['assessment_of_encounter'] # or AOE 553 ), 554 enc['reason_for_encounter'] # or RFE 555 ), 556 enc['l10n_type'] # or type 557 ), 558 max_length = 40 559 ) 560 ) 561 encounter_node = self.AppendItem(episode_node, label) 562 self.SetItemPyData(encounter_node, enc) 563 # we don't expand encounter nodes (what for ?) 564 self.SetItemHasChildren(encounter_node, False) 565 566 self.SortChildren(episode_node)
567 #-------------------------------------------------------- 568 # encounter level 569 #--------------------------------------------------------
570 - def __move_progress_notes(self, evt):
571 encounter = self.GetPyData(self.__curr_node) 572 node_parent = self.GetItemParent(self.__curr_node) 573 episode = self.GetPyData(node_parent) 574 575 gmNarrativeWidgets.move_progress_notes_to_another_encounter ( 576 parent = self, 577 encounters = [encounter['pk_encounter']], 578 episodes = [episode['pk_episode']] 579 )
580 #--------------------------------------------------------
581 - def __edit_progress_notes(self, event):
582 encounter = self.GetPyData(self.__curr_node) 583 node_parent = self.GetItemParent(self.__curr_node) 584 episode = self.GetPyData(node_parent) 585 586 gmNarrativeWidgets.manage_progress_notes ( 587 parent = self, 588 encounters = [encounter['pk_encounter']], 589 episodes = [episode['pk_episode']] 590 )
591 #--------------------------------------------------------
592 - def __edit_encounter_details(self, event):
593 node_data = self.GetPyData(self.__curr_node) 594 gmEMRStructWidgets.edit_encounter(parent = self, encounter = node_data) 595 self.__populate_tree()
596 #-------------------------------------------------------- 614 #-------------------------------------------------------- 615 # issue level 616 #--------------------------------------------------------
617 - def __edit_issue(self, event):
618 gmEMRStructWidgets.edit_health_issue(parent = self, issue = self.__curr_node_data)
619 #--------------------------------------------------------
620 - def __delete_issue(self, event):
621 dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 622 parent = self, 623 id = -1, 624 caption = _('Deleting health issue'), 625 button_defs = [ 626 {'label': _('Yes, delete'), 'tooltip': _('Delete the health issue if possible (it must be completely empty).')}, 627 {'label': _('No, cancel'), 'tooltip': _('Cancel and do NOT delete the health issue.')} 628 ], 629 question = _( 630 'Are you sure you want to delete this health issue ?\n' 631 '\n' 632 ' "%s"\n' 633 ) % self.__curr_node_data['description'] 634 ) 635 result = dlg.ShowModal() 636 if result != wx.ID_YES: 637 dlg.Destroy() 638 return 639 640 dlg.Destroy() 641 642 try: 643 gmEMRStructItems.delete_health_issue(health_issue = self.__curr_node_data) 644 except gmExceptions.DatabaseObjectInUseError: 645 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete health issue. There is still clinical data recorded for it.'))
646 #--------------------------------------------------------
647 - def __expand_issue_to_encounter_level(self, evt):
648 649 if not self.__curr_node.IsOk(): 650 return 651 652 self.Expand(self.__curr_node) 653 654 epi, epi_cookie = self.GetFirstChild(self.__curr_node) 655 while epi.IsOk(): 656 self.Expand(epi) 657 epi, epi_cookie = self.GetNextChild(self.__curr_node, epi_cookie)
658 #--------------------------------------------------------
659 - def __expand_issue_node(self, issue_node=None):
660 self.DeleteChildren(issue_node) 661 662 emr = self.__pat.emr 663 issue = self.GetPyData(issue_node) 664 episodes = emr.get_episodes(issues = [issue['pk_health_issue']]) 665 if len(episodes) == 0: 666 self.SetItemHasChildren(issue_node, False) 667 return 668 669 self.SetItemHasChildren(issue_node, True) 670 671 for episode in episodes: 672 episode_node = self.AppendItem(issue_node, episode['description']) 673 self.SetItemPyData(episode_node, episode) 674 # fake it so we can expand it 675 self.SetItemHasChildren(episode_node, True) 676 677 self.SortChildren(issue_node)
678 #--------------------------------------------------------
679 - def __expand_pseudo_issue_node(self, fake_issue_node=None):
680 self.DeleteChildren(fake_issue_node) 681 682 emr = self.__pat.emr 683 episodes = emr.unlinked_episodes 684 if len(episodes) == 0: 685 self.SetItemHasChildren(fake_issue_node, False) 686 return 687 688 self.SetItemHasChildren(fake_issue_node, True) 689 690 for episode in episodes: 691 episode_node = self.AppendItem(fake_issue_node, episode['description']) 692 self.SetItemPyData(episode_node, episode) 693 if episode['episode_open']: 694 self.SetItemBold(fake_issue_node, True) 695 # fake it so we can expand it 696 self.SetItemHasChildren(episode_node, True) 697 698 self.SortChildren(fake_issue_node)
699 #-------------------------------------------------------- 700 # EMR level 701 #--------------------------------------------------------
702 - def __create_issue(self, event):
703 gmEMRStructWidgets.edit_health_issue(parent = self, issue = None)
704 #--------------------------------------------------------
705 - def __create_episode(self, event):
706 gmEMRStructWidgets.edit_episode(parent = self, episode = None)
707 #--------------------------------------------------------
708 - def __create_soap_editor(self, event):
709 self.__cb__select_edit_mode(True) 710 self.__cb__add_soap_editor(problem = self.__curr_node_data, allow_same_problem = False)
711 #--------------------------------------------------------
712 - def __document_allergy(self, event):
713 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent=self, id=-1) 714 # FIXME: use signal and use node level update 715 if dlg.ShowModal() == wx.ID_OK: 716 self.__populate_tree() 717 dlg.Destroy() 718 return
719 #--------------------------------------------------------
720 - def __manage_procedures(self, event):
722 #--------------------------------------------------------
723 - def __manage_family_history(self, event):
725 #--------------------------------------------------------
726 - def __manage_hospital_stays(self, event):
728 #--------------------------------------------------------
729 - def __manage_occupation(self, event):
731 #--------------------------------------------------------
732 - def __manage_vaccinations(self, event):
733 gmVaccWidgets.manage_vaccinations(parent = self)
734 #--------------------------------------------------------
735 - def __expand_to_issue_level(self, evt):
736 737 root_item = self.GetRootItem() 738 739 if not root_item.IsOk(): 740 return 741 742 self.Expand(root_item) 743 744 # collapse episodes and issues 745 issue, issue_cookie = self.GetFirstChild(root_item) 746 while issue.IsOk(): 747 self.Collapse(issue) 748 epi, epi_cookie = self.GetFirstChild(issue) 749 while epi.IsOk(): 750 self.Collapse(epi) 751 epi, epi_cookie = self.GetNextChild(issue, epi_cookie) 752 issue, issue_cookie = self.GetNextChild(root_item, issue_cookie)
753 #--------------------------------------------------------
754 - def __expand_to_episode_level(self, evt):
755 756 root_item = self.GetRootItem() 757 758 if not root_item.IsOk(): 759 return 760 761 self.Expand(root_item) 762 763 # collapse episodes, expand issues 764 issue, issue_cookie = self.GetFirstChild(root_item) 765 while issue.IsOk(): 766 self.Expand(issue) 767 epi, epi_cookie = self.GetFirstChild(issue) 768 while epi.IsOk(): 769 self.Collapse(epi) 770 epi, epi_cookie = self.GetNextChild(issue, epi_cookie) 771 issue, issue_cookie = self.GetNextChild(root_item, issue_cookie)
772 #--------------------------------------------------------
773 - def __expand_to_encounter_level(self, evt):
774 775 root_item = self.GetRootItem() 776 777 if not root_item.IsOk(): 778 return 779 780 self.Expand(root_item) 781 782 # collapse episodes, expand issues 783 issue, issue_cookie = self.GetFirstChild(root_item) 784 while issue.IsOk(): 785 self.Expand(issue) 786 epi, epi_cookie = self.GetFirstChild(issue) 787 while epi.IsOk(): 788 self.Expand(epi) 789 epi, epi_cookie = self.GetNextChild(issue, epi_cookie) 790 issue, issue_cookie = self.GetNextChild(root_item, issue_cookie)
791 #--------------------------------------------------------
792 - def __export_encounter_for_medistar(self, evt):
793 gmNarrativeWidgets.export_narrative_for_medistar_import ( 794 parent = self, 795 soap_cats = u'soapu', 796 encounter = self.__curr_node_data 797 )
798 #--------------------------------------------------------
799 - def __expand_root_node(self):
800 root_node = self.GetRootItem() 801 self.DeleteChildren(root_node) 802 803 issues = [{ 804 'description': _('Unattributed episodes'), 805 'has_open_episode': False, 806 'pk_health_issue': None 807 }] 808 809 emr = self.__pat.emr 810 issues.extend(emr.health_issues) 811 812 for issue in issues: 813 issue_node = self.AppendItem(root_node, issue['description']) 814 self.SetItemBold(issue_node, issue['has_open_episode']) 815 self.SetItemPyData(issue_node, issue) 816 # fake it so we can expand it 817 self.SetItemHasChildren(issue_node, True) 818 819 self.SetItemHasChildren(root_node, (len(issues) != 0)) 820 self.SortChildren(root_node)
821 #-------------------------------------------------------- 822 # event handlers 823 #--------------------------------------------------------
824 - def _on_narrative_mod_db(self, *args, **kwargs):
825 wx.CallAfter(self.__update_text_for_selected_node)
826 #--------------------------------------------------------
827 - def _on_episode_mod_db(self, *args, **kwargs):
828 wx.CallAfter(self.__populate_tree)
829 #--------------------------------------------------------
830 - def _on_issue_mod_db(self, *args, **kwargs):
831 wx.CallAfter(self.__populate_tree)
832 #--------------------------------------------------------
833 - def _on_tree_item_expanding(self, event):
834 if not self.__pat.connected: 835 return 836 837 event.Skip() 838 839 node = event.GetItem() 840 if node == self.GetRootItem(): 841 self.__expand_root_node() 842 return 843 844 node_data = self.GetPyData(node) 845 846 if isinstance(node_data, gmEMRStructItems.cHealthIssue): 847 self.__expand_issue_node(issue_node = node) 848 return 849 850 if isinstance(node_data, gmEMRStructItems.cEpisode): 851 self.__expand_episode_node(episode_node = node) 852 return 853 854 # pseudo node "free-standing episodes" 855 if type(node_data) == type({}): 856 self.__expand_pseudo_issue_node(fake_issue_node = node) 857 return
858 859 # encounter nodes do not need expanding 860 #if isinstance(node_data, gmEMRStructItems.cEncounter): 861 #--------------------------------------------------------
862 - def _on_tree_item_selected(self, event):
863 sel_item = event.GetItem() 864 self.__curr_node = sel_item 865 self.__update_text_for_selected_node() 866 return True
867 # #-------------------------------------------------------- 868 # def _on_mouse_motion(self, event): 869 # 870 # cursor_pos = (event.GetX(), event.GetY()) 871 # 872 # self.SetToolTipString(u'') 873 # 874 # if cursor_pos != self._old_cursor_pos: 875 # self._old_cursor_pos = cursor_pos 876 # (item, flags) = self.HitTest(cursor_pos) 877 # #if flags != wx.TREE_HITTEST_NOWHERE: 878 # if flags == wx.TREE_HITTEST_ONITEMLABEL: 879 # data = self.GetPyData(item) 880 # 881 # if not isinstance(data, gmEMRStructItems.cEncounter): 882 # return 883 # 884 # self.SetToolTip(u'%s %s %s - %s\n\nRFE: %s\nAOE: %s' % ( 885 # gmDateTime.pydt_strftime(data['started'], '%Y %b %d'), 886 # data['l10n_type'], 887 # data['started'].strftime('%H:%m'), 888 # data['last_affirmed'].strftime('%H:%m'), 889 # gmTools.coalesce(data['reason_for_encounter'], u''), 890 # gmTools.coalesce(data['assessment_of_encounter'], u'') 891 # )) 892 #--------------------------------------------------------
893 - def _on_tree_item_gettooltip(self, event):
894 895 item = event.GetItem() 896 897 if not item.IsOk(): 898 event.SetToolTip(u' ') 899 return 900 901 data = self.GetPyData(item) 902 903 if isinstance(data, gmEMRStructItems.cEncounter): 904 tt = u'%s %s %s - %s\n' % ( 905 gmDateTime.pydt_strftime(data['started'], '%Y %b %d'), 906 data['l10n_type'], 907 data['started'].strftime('%H:%M'), 908 data['last_affirmed'].strftime('%H:%M') 909 ) 910 if data['reason_for_encounter'] is not None: 911 tt += u'\n' 912 tt += _('RFE: %s') % data['reason_for_encounter'] 913 if len(data['pk_generic_codes_rfe']) > 0: 914 for code in data.generic_codes_rfe: 915 tt += u'\n %s: %s%s%s\n (%s %s)' % ( 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 if data['assessment_of_encounter'] is not None: 924 tt += u'\n' 925 tt += _('AOE: %s') % data['assessment_of_encounter'] 926 if len(data['pk_generic_codes_aoe']) > 0: 927 for code in data.generic_codes_aoe: 928 tt += u'\n %s: %s%s%s\n (%s %s)' % ( 929 code['code'], 930 gmTools.u_left_double_angle_quote, 931 code['term'], 932 gmTools.u_right_double_angle_quote, 933 code['name_short'], 934 code['version'] 935 ) 936 937 elif isinstance(data, gmEMRStructItems.cEpisode): 938 tt = u'' 939 tt += gmTools.bool2subst ( 940 (data['diagnostic_certainty_classification'] is not None), 941 data.diagnostic_certainty_description + u'\n\n', 942 u'' 943 ) 944 tt += gmTools.bool2subst ( 945 data['episode_open'], 946 _('ongoing episode'), 947 _('closed episode'), 948 'error: episode state is None' 949 ) + u'\n' 950 tt += gmTools.coalesce(data['summary'], u'', u'\n%s') 951 if len(data['pk_generic_codes']) > 0: 952 tt += u'\n' 953 for code in data.generic_codes: 954 tt += u'%s: %s%s%s\n (%s %s)\n' % ( 955 code['code'], 956 gmTools.u_left_double_angle_quote, 957 code['term'], 958 gmTools.u_right_double_angle_quote, 959 code['name_short'], 960 code['version'] 961 ) 962 963 tt = tt.strip(u'\n') 964 if tt == u'': 965 tt = u' ' 966 967 elif isinstance(data, gmEMRStructItems.cHealthIssue): 968 tt = u'' 969 tt += gmTools.bool2subst(data['is_confidential'], _('*** CONFIDENTIAL ***\n\n'), u'') 970 tt += gmTools.bool2subst ( 971 (data['diagnostic_certainty_classification'] is not None), 972 data.diagnostic_certainty_description + u'\n', 973 u'' 974 ) 975 tt += gmTools.bool2subst ( 976 (data['laterality'] not in [None, u'na']), 977 data.laterality_description + u'\n', 978 u'' 979 ) 980 # noted_at_age is too costly 981 tt += gmTools.bool2subst(data['is_active'], _('active') + u'\n', u'') 982 tt += gmTools.bool2subst(data['clinically_relevant'], _('clinically relevant') + u'\n', u'') 983 tt += gmTools.bool2subst(data['is_cause_of_death'], _('contributed to death') + u'\n', u'') 984 tt += gmTools.coalesce(data['grouping'], u'\n', _('Grouping: %s') + u'\n') 985 tt += gmTools.coalesce(data['summary'], u'', u'\n%s') 986 if len(data['pk_generic_codes']) > 0: 987 tt += u'\n' 988 for code in data.generic_codes: 989 tt += u'%s: %s%s%s\n (%s %s)\n' % ( 990 code['code'], 991 gmTools.u_left_double_angle_quote, 992 code['term'], 993 gmTools.u_right_double_angle_quote, 994 code['name_short'], 995 code['version'] 996 ) 997 998 tt = tt.strip(u'\n') 999 if tt == u'': 1000 tt = u' ' 1001 1002 else: 1003 tt = self.__root_tooltip 1004 1005 event.SetToolTip(tt)
1006 1007 # doing this prevents the tooltip from showing at all 1008 #event.Skip() 1009 1010 #widgetXY.GetToolTip().Enable(False) 1011 # 1012 #seems to work, supposing the tooltip is actually set for the widget, 1013 #otherwise a test would be needed 1014 #if widgetXY.GetToolTip(): 1015 # widgetXY.GetToolTip().Enable(False) 1016 #--------------------------------------------------------
1017 - def _on_tree_item_right_clicked(self, event):
1018 """Right button clicked: display the popup for the tree""" 1019 1020 node = event.GetItem() 1021 self.SelectItem(node) 1022 self.__curr_node_data = self.GetPyData(node) 1023 self.__curr_node = node 1024 1025 pos = wx.DefaultPosition 1026 if isinstance(self.__curr_node_data, gmEMRStructItems.cHealthIssue): 1027 self.__handle_issue_context(pos=pos) 1028 elif isinstance(self.__curr_node_data, gmEMRStructItems.cEpisode): 1029 self.__handle_episode_context(pos=pos) 1030 elif isinstance(self.__curr_node_data, gmEMRStructItems.cEncounter): 1031 self.__handle_encounter_context(pos=pos) 1032 elif node == self.GetRootItem(): 1033 self.__handle_root_context() 1034 elif type(self.__curr_node_data) == type({}): 1035 # ignore pseudo node "free-standing episodes" 1036 pass 1037 else: 1038 print "error: unknown node type, no popup menu" 1039 event.Skip()
1040 #--------------------------------------------------------
1041 - def OnCompareItems (self, node1=None, node2=None):
1042 """Used in sorting items. 1043 1044 -1: 1 < 2 1045 0: 1 = 2 1046 1: 1 > 2 1047 """ 1048 # FIXME: implement sort modes, chron, reverse cron, by regex, etc 1049 1050 if not node1: 1051 _log.debug('invalid node 1') 1052 return 0 1053 if not node2: 1054 _log.debug('invalid node 2') 1055 return 0 1056 1057 if not node1.IsOk(): 1058 _log.debug('invalid node 1') 1059 return 0 1060 if not node2.IsOk(): 1061 _log.debug('invalid node 2') 1062 return 0 1063 1064 item1 = self.GetPyData(node1) 1065 item2 = self.GetPyData(node2) 1066 1067 # dummy health issue always on top 1068 if isinstance(item1, type({})): 1069 return -1 1070 if isinstance(item2, type({})): 1071 return 1 1072 1073 # encounters: reverse chronologically 1074 if isinstance(item1, gmEMRStructItems.cEncounter): 1075 if item1['started'] == item2['started']: 1076 return 0 1077 if item1['started'] > item2['started']: 1078 return -1 1079 return 1 1080 1081 # episodes: chronologically 1082 if isinstance(item1, gmEMRStructItems.cEpisode): 1083 start1 = item1.best_guess_start_date 1084 start2 = item2.best_guess_start_date 1085 if start1 == start2: 1086 return 0 1087 if start1 < start2: 1088 return -1 1089 return 1 1090 1091 # issues: alpha by grouping, no grouping at the bottom 1092 if isinstance(item1, gmEMRStructItems.cHealthIssue): 1093 1094 # no grouping below grouping 1095 if item1['grouping'] is None: 1096 if item2['grouping'] is not None: 1097 return 1 1098 1099 # grouping above no grouping 1100 if item1['grouping'] is not None: 1101 if item2['grouping'] is None: 1102 return -1 1103 1104 # both no grouping: alpha on description 1105 if (item1['grouping'] is None) and (item2['grouping'] is None): 1106 if item1['description'].lower() < item2['description'].lower(): 1107 return -1 1108 if item1['description'].lower() > item2['description'].lower(): 1109 return 1 1110 return 0 1111 1112 # both with grouping: alpha on grouping, then alpha on description 1113 if item1['grouping'] < item2['grouping']: 1114 return -1 1115 1116 if item1['grouping'] > item2['grouping']: 1117 return 1 1118 1119 if item1['description'].lower() < item2['description'].lower(): 1120 return -1 1121 1122 if item1['description'].lower() > item2['description'].lower(): 1123 return 1 1124 1125 return 0 1126 1127 _log.error('unknown item type during sorting EMR tree:') 1128 _log.error('item1: %s', type(item1)) 1129 _log.error('item2: %s', type(item2)) 1130 1131 return 0
1132 #-------------------------------------------------------- 1133 # properties 1134 #--------------------------------------------------------
1135 - def _get_details_display_mode(self):
1136 return self.__soap_display_mode
1137
1138 - def _set_details_display_mode(self, mode):
1139 if mode not in [u'details', u'journal']: 1140 raise ValueError('details display mode must be one of "details", "journal"') 1141 if self.__soap_display_mode == mode: 1142 return 1143 self.__soap_display_mode = mode 1144 self.__update_text_for_selected_node()
1145 1146 details_display_mode = property(_get_details_display_mode, _set_details_display_mode)
1147 #================================================================ 1148 from Gnumed.wxGladeWidgets import wxgScrolledEMRTreePnl 1149
1150 -class cScrolledEMRTreePnl(wxgScrolledEMRTreePnl.wxgScrolledEMRTreePnl):
1151 """A scrollable panel holding an EMR tree. 1152 1153 Lacks a widget to display details for selected items. The 1154 tree data will be refetched - if necessary - whenever 1155 repopulate_ui() is called, e.g., when the patient is changed. 1156 """
1157 - def __init__(self, *args, **kwds):
1159 #--------------------------------------------------------
1160 - def repopulate_ui(self):
1161 self._emr_tree.refresh() 1162 return True
1163 #============================================================ 1164 from Gnumed.wxGladeWidgets import wxgSplittedEMRTreeBrowserPnl 1165
1166 -class cSplittedEMRTreeBrowserPnl(wxgSplittedEMRTreeBrowserPnl.wxgSplittedEMRTreeBrowserPnl):
1167 """A splitter window holding an EMR tree. 1168 1169 The left hand side displays a scrollable EMR tree while 1170 on the right details for selected items are displayed. 1171 1172 Expects to be put into a Notebook. 1173 """
1174 - def __init__(self, *args, **kwds):
1175 wxgSplittedEMRTreeBrowserPnl.wxgSplittedEMRTreeBrowserPnl.__init__(self, *args, **kwds) 1176 self._pnl_emr_tree._emr_tree.soap_display = self._TCTRL_item_details 1177 self._pnl_emr_tree._emr_tree.image_display = self._PNL_visual_soap 1178 self._pnl_emr_tree._emr_tree.set_enable_display_mode_selection_callback(self.enable_display_mode_selection) 1179 self._pnl_emr_tree._emr_tree.soap_editor_adder = self._add_soap_editor 1180 self._pnl_emr_tree._emr_tree.edit_mode_selector = self._select_edit_mode 1181 self.__register_events() 1182 1183 self.editing = False
1184 #--------------------------------------------------------
1185 - def __register_events(self):
1186 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection) 1187 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection) 1188 return True
1189 #--------------------------------------------------------
1190 - def _get_editing(self):
1191 return self.__editing
1192
1193 - def _set_editing(self, editing):
1194 self.__editing = editing 1195 self.enable_display_mode_selection(enable = not self.__editing) 1196 if self.__editing: 1197 self._BTN_switch_browse_edit.SetLabel(_('&Browse')) 1198 self._PNL_browse.Hide() 1199 self._PNL_visual_soap.Hide() 1200 self._PNL_edit.Show() 1201 else: 1202 self._BTN_switch_browse_edit.SetLabel(_('&Edit')) 1203 self._PNL_edit.Hide() 1204 self._PNL_visual_soap.Show() 1205 self._PNL_browse.Show() 1206 self._PNL_right_side.GetSizer().Layout()
1207 1208 editing = property(_get_editing, _set_editing) 1209 #-------------------------------------------------------- 1210 # event handler 1211 #--------------------------------------------------------
1212 - def _on_pre_patient_selection(self):
1213 self._pnl_emr_tree._emr_tree.clear_tree() 1214 self._PNL_edit.patient = None 1215 return True
1216 #--------------------------------------------------------
1217 - def _on_post_patient_selection(self):
1218 wx.CallAfter(self.__on_post_patient_selection) 1219 return True
1220 #--------------------------------------------------------
1221 - def __on_post_patient_selection(self):
1222 if self.GetParent().GetCurrentPage() != self: 1223 return True 1224 self.repopulate_ui()
1225 #--------------------------------------------------------
1226 - def _on_show_details_selected(self, event):
1227 self._pnl_emr_tree._emr_tree.details_display_mode = u'details'
1228 #--------------------------------------------------------
1229 - def _on_show_journal_selected(self, event):
1230 self._pnl_emr_tree._emr_tree.details_display_mode = u'journal'
1231 #--------------------------------------------------------
1233 self.editing = not self.__editing
1234 #-------------------------------------------------------- 1235 # external API 1236 #--------------------------------------------------------
1237 - def repopulate_ui(self):
1238 """Fills UI with data.""" 1239 self._pnl_emr_tree.repopulate_ui() 1240 self._PNL_edit.patient = gmPerson.gmCurrentPatient() 1241 self._splitter_browser.SetSashPosition(self._splitter_browser.GetSizeTuple()[0]/3, True) 1242 return True
1243 #--------------------------------------------------------
1244 - def enable_display_mode_selection(self, enable):
1245 if self.editing: 1246 enable = False 1247 if enable: 1248 self._RBTN_details.Enable(True) 1249 self._RBTN_journal.Enable(True) 1250 return 1251 self._RBTN_details.Enable(False) 1252 self._RBTN_journal.Enable(False)
1253 #--------------------------------------------------------
1254 - def _add_soap_editor(self, problem=None, allow_same_problem=False):
1255 self._PNL_edit._NB_soap_editors.add_editor(problem = problem, allow_same_problem = allow_same_problem)
1256 #--------------------------------------------------------
1257 - def _select_edit_mode(self, edit=True):
1258 self.editing = edit
1259 1260 #================================================================ 1261 from Gnumed.wxGladeWidgets import wxgEMRJournalPluginPnl 1262
1263 -class cEMRJournalPluginPnl(wxgEMRJournalPluginPnl.wxgEMRJournalPluginPnl):
1264
1265 - def __init__(self, *args, **kwds):
1266 1267 wxgEMRJournalPluginPnl.wxgEMRJournalPluginPnl.__init__(self, *args, **kwds) 1268 self._TCTRL_journal.SetValue(u'')
1269 #-------------------------------------------------------- 1270 # external API 1271 #--------------------------------------------------------
1272 - def repopulate_ui(self):
1273 self._TCTRL_journal.SetValue(u'') 1274 exporter = gmPatientExporter.cEMRJournalExporter() 1275 if self._RBTN_by_encounter.GetValue(): 1276 txt = StringIO.StringIO() 1277 # FIXME: if journal is large this will error out, use generator/yield etc 1278 # FIXME: turn into proper list 1279 try: 1280 exporter.export(txt) 1281 self._TCTRL_journal.SetValue(txt.getvalue()) 1282 except ValueError: 1283 _log.exception('cannot get EMR journal') 1284 self._TCTRL_journal.SetValue (_( 1285 'An error occurred while retrieving the EMR\n' 1286 'in journal form for the active patient.\n\n' 1287 'Please check the log file for details.' 1288 )) 1289 txt.close() 1290 else: 1291 fname = exporter.export_to_file_by_mod_time() 1292 f = codecs.open(filename = fname, mode = 'rU', encoding = 'utf8', errors = 'replace') 1293 for line in f: 1294 self._TCTRL_journal.AppendText(line) 1295 f.close() 1296 1297 self._TCTRL_journal.ShowPosition(self._TCTRL_journal.GetLastPosition()) 1298 return True
1299 #-------------------------------------------------------- 1300 # internal helpers 1301 #--------------------------------------------------------
1302 - def __register_events(self):
1303 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection) 1304 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection) 1305 return True
1306 #-------------------------------------------------------- 1307 # event handler 1308 #--------------------------------------------------------
1309 - def _on_pre_patient_selection(self):
1310 self._TCTRL_journal.SetValue(u'') 1311 return True
1312 #--------------------------------------------------------
1313 - def _on_post_patient_selection(self):
1314 wx.CallAfter(self.__on_post_patient_selection) 1315 return True
1316 #--------------------------------------------------------
1317 - def __on_post_patient_selection(self):
1318 if self.GetParent().GetCurrentPage() != self: 1319 return True 1320 self.repopulate_ui()
1321 #--------------------------------------------------------
1322 - def _on_order_by_encounter_selected(self, event):
1323 self.repopulate_ui()
1324 #--------------------------------------------------------
1325 - def _on_order_by_last_mod_selected(self, event):
1326 self.repopulate_ui()
1327 1328 #================================================================ 1329 # MAIN 1330 #---------------------------------------------------------------- 1331 if __name__ == '__main__': 1332 1333 _log.info("starting emr browser...") 1334 1335 try: 1336 # obtain patient 1337 patient = gmPersonSearch.ask_for_patient() 1338 if patient is None: 1339 print "No patient. Exiting gracefully..." 1340 sys.exit(0) 1341 gmPatSearchWidgets.set_active_patient(patient = patient) 1342 1343 # display standalone browser 1344 application = wx.PyWidgetTester(size=(800,600)) 1345 emr_browser = cEMRBrowserPanel(application.frame, -1) 1346 emr_browser.refresh_tree() 1347 1348 application.frame.Show(True) 1349 application.MainLoop() 1350 1351 # clean up 1352 if patient is not None: 1353 try: 1354 patient.cleanup() 1355 except: 1356 print "error cleaning up patient" 1357 except StandardError: 1358 _log.exception("unhandled exception caught !") 1359 # but re-raise them 1360 raise 1361 1362 _log.info("closing emr browser...") 1363 1364 #================================================================ 1365