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