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

Source Code for Module Gnumed.wxpython.gmProviderInboxWidgets

  1  """GNUmed provider inbox handling widgets. 
  2  """ 
  3  #================================================================ 
  4  # $Source: /cvsroot/gnumed/gnumed/gnumed/client/wxpython/gmProviderInboxWidgets.py,v $ 
  5  # $Id: gmProviderInboxWidgets.py,v 1.47 2010/01/10 17:29:26 ncq Exp $ 
  6  __version__ = "$Revision: 1.47 $" 
  7  __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>" 
  8   
  9  import sys, logging 
 10   
 11   
 12  import wx 
 13   
 14   
 15  if __name__ == '__main__': 
 16          sys.path.insert(0, '../../') 
 17  from Gnumed.pycommon import gmI18N, gmDispatcher, gmTools, gmCfg, gmPG2, gmExceptions 
 18  from Gnumed.business import gmPerson, gmSurgery 
 19  from Gnumed.wxpython import gmGuiHelpers, gmListWidgets, gmPlugin, gmRegetMixin, gmPhraseWheel, gmEditArea, gmAuthWidgets, gmPatSearchWidgets 
 20  from Gnumed.wxGladeWidgets import wxgProviderInboxPnl, wxgTextExpansionEditAreaPnl 
 21   
 22   
 23  _log = logging.getLogger('gm.ui') 
 24  _log.info(__version__) 
 25   
 26  _indicator = { 
 27          -1: '', 
 28          0: '', 
 29          1: '!' 
 30  } 
 31  #============================================================ 
32 -class cTextExpansionEditAreaPnl(wxgTextExpansionEditAreaPnl.wxgTextExpansionEditAreaPnl):
33
34 - def __init__(self, *args, **kwds):
35 36 try: 37 self.__keyword = kwds['keyword'] 38 del kwds['keyword'] 39 except KeyError: 40 self.__keyword = None 41 42 wxgTextExpansionEditAreaPnl.wxgTextExpansionEditAreaPnl.__init__(self, *args, **kwds) 43 44 self.__init_ui() 45 self.__register_interests()
46 #--------------------------------------------------------
47 - def save(self):
48 if not self.__valid_for_save(): 49 return False 50 51 if self.__keyword is None: 52 result = gmPG2.add_text_expansion ( 53 keyword = self._TCTRL_keyword.GetValue().strip(), 54 expansion = self._TCTRL_expansion.GetValue(), 55 public = self._RBTN_public.GetValue() 56 ) 57 else: 58 gmPG2.edit_text_expansion ( 59 keyword = self._TCTRL_keyword.GetValue().strip(), 60 expansion = self._TCTRL_expansion.GetValue() 61 ) 62 result = True 63 64 return result
65 #--------------------------------------------------------
66 - def refresh(self):
67 self.__init_ui()
68 # if self.__keyword is not None: 69 # self._TCTRL_expansion.SetValue(u'') 70 #-------------------------------------------------------- 71 # event handling 72 #--------------------------------------------------------
73 - def __register_interests(self):
74 self._TCTRL_keyword.Bind(wx.EVT_TEXT, self._on_keyword_modified)
75 #--------------------------------------------------------
76 - def _on_keyword_modified(self, evt):
77 if self._TCTRL_keyword.GetValue().strip() == u'': 78 self._TCTRL_expansion.Enable(False) 79 else: 80 self._TCTRL_expansion.Enable(True)
81 #-------------------------------------------------------- 82 # internal API 83 #--------------------------------------------------------
84 - def __valid_for_save(self):
85 86 kwd = self._TCTRL_keyword.GetValue().strip() 87 if kwd == u'': 88 self._TCTRL_keyword.SetBackgroundColour('pink') 89 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save text expansion without keyword.'), beep = True) 90 return False 91 self._TCTRL_keyword.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 92 93 if self._TCTRL_expansion.GetValue().strip() == u'': 94 self._TCTRL_expansion.SetBackgroundColour('pink') 95 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save text expansion without expansion text.'), beep = True) 96 return False 97 self._TCTRL_expansion.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 98 99 return True
100 #--------------------------------------------------------
101 - def __init_ui(self, keyword=None):
102 103 if keyword is not None: 104 self.__keyword = keyword 105 106 if self.__keyword is None: 107 self._TCTRL_keyword.SetValue(u'') 108 self._TCTRL_keyword.Enable(True) 109 self._TCTRL_expansion.SetValue(u'') 110 self._TCTRL_expansion.Enable(False) 111 self._RBTN_public.Enable(True) 112 self._RBTN_private.Enable(True) 113 self._RBTN_public.SetValue(1) 114 else: 115 expansion = gmPG2.expand_keyword(keyword = self.__keyword) 116 self._TCTRL_keyword.SetValue(self.__keyword) 117 self._TCTRL_keyword.Enable(False) 118 self._TCTRL_expansion.SetValue(gmTools.coalesce(expansion, u'')) 119 self._TCTRL_expansion.Enable(True) 120 self._RBTN_public.Enable(False) 121 self._RBTN_private.Enable(False)
122 #============================================================
123 -def configure_keyword_text_expansion(parent=None):
124 125 if parent is None: 126 parent = wx.GetApp().GetTopWindow() 127 128 #---------------------- 129 def delete(keyword=None): 130 gmPG2.delete_text_expansion(keyword = keyword) 131 return True
132 #---------------------- 133 def edit(keyword=None): 134 # add new keyword 135 ea = cTextExpansionEditAreaPnl(parent, -1, keyword=keyword) 136 dlg = gmEditArea.cGenericEditAreaDlg(parent, -1, edit_area = ea) 137 dlg.SetTitle ( 138 gmTools.coalesce(keyword, _('Adding text espansion'), _('Editing text expansion "%s"')) 139 ) 140 if dlg.ShowModal() == wx.ID_OK: 141 return True 142 143 return False 144 #---------------------- 145 def refresh(lctrl=None): 146 kwds = [ [ 147 r[0], 148 gmTools.bool2subst(r[1], gmTools.u_checkmark_thick, u''), 149 gmTools.bool2subst(r[2], gmTools.u_checkmark_thick, u''), 150 r[3] 151 ] for r in gmPG2.get_text_expansion_keywords() 152 ] 153 data = [ r[0] for r in gmPG2.get_text_expansion_keywords() ] 154 lctrl.set_string_items(kwds) 155 lctrl.set_data(data) 156 #---------------------- 157 158 gmListWidgets.get_choices_from_list ( 159 parent = parent, 160 msg = _('\nSelect the keyword you want to edit !\n'), 161 caption = _('Editing keyword-based text expansions ...'), 162 columns = [_('Keyword'), _('Public'), _('Private'), _('Owner')], 163 single_selection = True, 164 edit_callback = edit, 165 new_callback = edit, 166 delete_callback = delete, 167 refresh_callback = refresh 168 ) 169 #============================================================
170 -class cProviderPhraseWheel(gmPhraseWheel.cPhraseWheel):
171
172 - def __init__(self, *args, **kwargs):
173 174 gmPhraseWheel.cPhraseWheel.__init__ ( 175 self, 176 *args, 177 **kwargs 178 ) 179 self.matcher = gmPerson.cMatchProvider_Provider() 180 self.SetToolTipString(_('Select a healthcare provider.')) 181 self.selection_only = True
182 #============================================================ 183 # practice related widgets 184 #============================================================ 185 # FIXME: this should be moved elsewhere !
186 -def configure_workplace_plugins(parent=None):
187 188 if parent is None: 189 parent = wx.GetApp().GetTopWindow() 190 191 #----------------------------------- 192 def delete(workplace): 193 194 curr_workplace = gmSurgery.gmCurrentPractice().active_workplace 195 if workplace == curr_workplace: 196 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete the active workplace.'), beep = True) 197 return False 198 199 dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 200 parent, 201 -1, 202 caption = _('Deleting workplace ...'), 203 question = _('Are you sure you want to delete this workplace ?\n\n "%s"\n') % workplace, 204 show_checkbox = True, 205 checkbox_msg = _('delete configuration, too'), 206 checkbox_tooltip = _( 207 'Check this if you want to delete all configuration items\n' 208 'for this workplace along with the workplace itself.' 209 ), 210 button_defs = [ 211 {'label': _('Delete'), 'tooltip': _('Yes, delete this workplace.'), 'default': True}, 212 {'label': _('Do NOT delete'), 'tooltip': _('No, do NOT delete this workplace'), 'default': False} 213 ] 214 ) 215 216 decision = dlg.ShowModal() 217 if decision != wx.ID_YES: 218 dlg.Destroy() 219 return False 220 221 include_cfg = dlg.checkbox_is_checked() 222 dlg.Destroy() 223 224 dbo_conn = gmAuthWidgets.get_dbowner_connection(procedure = _('delete workplace')) 225 if not dbo_conn: 226 return False 227 228 gmSurgery.delete_workplace(workplace = workplace, conn = dbo_conn, delete_config = include_cfg) 229 return True
230 #----------------------------------- 231 def edit(workplace=None): 232 233 available_plugins = gmPlugin.get_installed_plugins(plugin_dir='gui') 234 235 dbcfg = gmCfg.cCfgSQL() 236 237 if workplace is None: 238 dlg = wx.TextEntryDialog ( 239 parent = parent, 240 message = _('Enter a descriptive name for the new workplace:'), 241 caption = _('Configuring GNUmed workplaces ...'), 242 defaultValue = u'', 243 style = wx.OK | wx.CENTRE 244 ) 245 dlg.ShowModal() 246 workplace = dlg.GetValue().strip() 247 if workplace == u'': 248 gmGuiHelpers.gm_show_error(_('Cannot save a new workplace without a name.'), _('Configuring GNUmed workplaces ...')) 249 return False 250 curr_plugins = [] 251 choices = available_plugins 252 else: 253 curr_plugins = gmTools.coalesce(dbcfg.get2 ( 254 option = u'horstspace.notebook.plugin_load_order', 255 workplace = workplace, 256 bias = 'workplace' 257 ), [] 258 ) 259 choices = curr_plugins[:] 260 for p in available_plugins: 261 if p not in choices: 262 choices.append(p) 263 264 sels = range(len(curr_plugins)) 265 new_plugins = gmListWidgets.get_choices_from_list ( 266 parent = parent, 267 msg = _( 268 '\n' 269 'Select the plugin(s) to be loaded the next time\n' 270 'the client is restarted under the workplace:\n' 271 '\n' 272 ' [%s]' 273 '\n' 274 ) % workplace, 275 caption = _('Configuring GNUmed workplaces ...'), 276 choices = choices, 277 selections = sels, 278 columns = [_('Plugins')], 279 single_selection = False 280 ) 281 282 if new_plugins == curr_plugins: 283 return True 284 285 if new_plugins is None: 286 return True 287 288 dbcfg.set ( 289 option = u'horstspace.notebook.plugin_load_order', 290 value = new_plugins, 291 workplace = workplace 292 ) 293 294 return True 295 #----------------------------------- 296 def refresh(lctrl): 297 workplaces = gmSurgery.gmCurrentPractice().workplaces 298 curr_workplace = gmSurgery.gmCurrentPractice().active_workplace 299 try: 300 sels = [workplaces.index(curr_workplace)] 301 except ValueError: 302 sels = [] 303 304 lctrl.set_string_items(workplaces) 305 lctrl.set_selections(selections = sels) 306 #----------------------------------- 307 gmListWidgets.get_choices_from_list ( 308 parent = parent, 309 msg = _( 310 '\nSelect the workplace to configure below.\n' 311 '\n' 312 'The currently active workplace is preselected.\n' 313 ), 314 caption = _('Configuring GNUmed workplaces ...'), 315 columns = [_('Workplace')], 316 single_selection = True, 317 refresh_callback = refresh, 318 edit_callback = edit, 319 new_callback = edit, 320 delete_callback = delete 321 ) 322 #============================================================
323 -class cProviderInboxPnl(wxgProviderInboxPnl.wxgProviderInboxPnl, gmRegetMixin.cRegetOnPaintMixin):
324 325 _item_handlers = {} 326 _patient_msg_types = ['clinical.review docs', 'clinical.review results'] 327 #--------------------------------------------------------
328 - def __init__(self, *args, **kwds):
329 330 wxgProviderInboxPnl.wxgProviderInboxPnl.__init__(self, *args, **kwds) 331 gmRegetMixin.cRegetOnPaintMixin.__init__(self) 332 333 self.provider = gmPerson.gmCurrentProvider() 334 self.filter_mode = 'all' 335 self.__init_ui() 336 337 cProviderInboxPnl._item_handlers['clinical.review docs'] = self._goto_doc_review 338 cProviderInboxPnl._item_handlers['clinical.review results'] = self._goto_measurements_review 339 340 self.__register_interests()
341 #-------------------------------------------------------- 342 # reget-on-paint API 343 #--------------------------------------------------------
344 - def _populate_with_data(self):
345 self.__populate_inbox() 346 return True
347 #-------------------------------------------------------- 348 # internal helpers 349 #--------------------------------------------------------
350 - def __register_interests(self):
351 gmDispatcher.connect(signal = u'message_inbox_generic_mod_db', receiver = self._on_message_inbox_mod_db) 352 gmDispatcher.connect(signal = u'message_inbox_mod_db', receiver = self._on_message_inbox_mod_db) 353 # FIXME: listen for results insertion/deletion 354 gmDispatcher.connect(signal = u'reviewed_test_results_mod_db', receiver = self._on_message_inbox_mod_db) 355 # FIXME: listen for doc insertion/deletion 356 # FIXME: listen for doc reviews 357 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
358 #--------------------------------------------------------
359 - def __init_ui(self):
360 self._LCTRL_provider_inbox.set_columns([u'', _('date'), _('category'), _('type'), _('message')]) 361 362 msg = _('\n Inbox of %(title)s %(lname)s.\n') % { 363 'title': gmTools.coalesce ( 364 self.provider['title'], 365 gmPerson.map_gender2salutation(self.provider['gender']) 366 ), 367 'lname': self.provider['lastnames'] 368 } 369 370 self._msg_welcome.SetLabel(msg) 371 372 if gmPerson.gmCurrentPatient().connected: 373 self._RBTN_active_patient.Enable()
374 #--------------------------------------------------------
375 - def __populate_inbox(self):
376 377 """Fill UI with data.""" 378 self.__msgs = self.provider.inbox.messages 379 380 if self.filter_mode == 'active': 381 if gmPerson.gmCurrentPatient().connected: 382 curr_pat_id = gmPerson.gmCurrentPatient().ID 383 self.__msgs = [ m for m in self.__msgs if m['pk_patient'] == curr_pat_id ] 384 else: 385 self.__msgs = [] 386 387 items = [ 388 [ 389 _indicator[m['importance']], 390 m['received_when'].strftime('%Y-%m-%d'), 391 m['l10n_category'], 392 m['l10n_type'], 393 m['comment'] 394 ] for m in self.__msgs 395 ] 396 self._LCTRL_provider_inbox.set_string_items(items = items) 397 self._LCTRL_provider_inbox.set_data(data = self.__msgs) 398 self._LCTRL_provider_inbox.set_column_widths()
399 #-------------------------------------------------------- 400 # event handlers 401 #--------------------------------------------------------
402 - def _on_post_patient_selection(self):
403 wx.CallAfter(self._schedule_data_reget) 404 wx.CallAfter(self._RBTN_active_patient.Enable)
405 #--------------------------------------------------------
406 - def _on_message_inbox_mod_db(self, *args, **kwargs):
407 wx.CallAfter(self._schedule_data_reget) 408 gmDispatcher.send(signal = u'request_user_attention', msg = _('Please check your GNUmed Inbox !'))
409 #--------------------------------------------------------
410 - def _lst_item_activated(self, evt):
411 msg = self._LCTRL_provider_inbox.get_selected_item_data(only_one = True) 412 if msg is None: 413 return 414 415 handler_key = '%s.%s' % (msg['category'], msg['type']) 416 try: 417 handle_item = cProviderInboxPnl._item_handlers[handler_key] 418 except KeyError: 419 gmGuiHelpers.gm_show_warning ( 420 _( 421 """No double-click action pre-programmed into 422 GNUmed for message category and type: 423 424 [%s] 425 """ 426 ) % handler_key, 427 _('handling provider inbox item') 428 ) 429 return False 430 431 if not handle_item(pk_context = msg['pk_context'], pk_patient = msg['pk_patient']): 432 _log.error('item handler returned "false"') 433 _log.error('handler key: [%s]', handler_key) 434 _log.error('message: %s', str(msg)) 435 return False 436 437 return True
438 #--------------------------------------------------------
439 - def _lst_item_focused(self, evt):
440 pass
441 #--------------------------------------------------------
442 - def _lst_item_selected(self, evt):
443 msg = self._LCTRL_provider_inbox.get_selected_item_data(only_one = True) 444 if msg is None: 445 return 446 447 if msg['data'] is None: 448 tmp = _('Message: %s') % msg['comment'] 449 else: 450 tmp = _('Message: %s\nData: %s') % (msg['comment'], msg['data']) 451 452 self._TXT_inbox_item_comment.SetValue(tmp)
453 #--------------------------------------------------------
454 - def _lst_item_right_clicked(self, evt):
455 tmp = self._LCTRL_provider_inbox.get_selected_item_data(only_one = True) 456 if tmp is None: 457 return 458 self.__focussed_msg = tmp 459 460 # build menu 461 menu = wx.Menu(title = _('Inbox Message menu')) 462 # - delete message 463 if not self.__focussed_msg['is_virtual']: 464 ID = wx.NewId() 465 menu.AppendItem(wx.MenuItem(menu, ID, _('delete message'))) 466 wx.EVT_MENU(menu, ID, self._on_delete_focussed_msg) 467 468 # show menu 469 self.PopupMenu(menu, wx.DefaultPosition) 470 menu.Destroy()
471 #--------------------------------------------------------
473 self.filter_mode = 'all' 474 self._TXT_inbox_item_comment.SetValue(u'') 475 self.__populate_inbox()
476 #--------------------------------------------------------
478 self.filter_mode = 'active' 479 self._TXT_inbox_item_comment.SetValue(u'') 480 self.__populate_inbox()
481 #-------------------------------------------------------- 482 # item handlers 483 #--------------------------------------------------------
484 - def _on_delete_focussed_msg(self, evt):
485 if self.__focussed_msg['is_virtual']: 486 gmDispatcher.send(signal = 'statustext', msg = _('You must deal with the reason for this message to remove it from your inbox.'), beep = True) 487 return False 488 489 if not self.provider.inbox.delete_message(self.__focussed_msg['pk_message_inbox']): 490 gmDispatcher.send(signal='statustext', msg=_('Problem removing message from Inbox.')) 491 return False 492 return True
493 #--------------------------------------------------------
494 - def _goto_doc_review(self, pk_context=None, pk_patient=None):
495 wx.BeginBusyCursor() 496 497 try: 498 pat = gmPerson.cIdentity(aPK_obj = pk_patient) 499 except gmExceptions.ConstructorError: 500 wx.EndBusyCursor() 501 _log.exception('patient [%s] not found', pk_patient) 502 gmGuiHelpers.gm_show_error ( 503 _('Supposedly there are unreviewed documents\n' 504 'for patient [%s]. However, I cannot find\n' 505 'that patient in the GNUmed database.' 506 ) % pk_patient, 507 _('handling provider inbox item') 508 ) 509 return False 510 511 success = gmPatSearchWidgets.set_active_patient(patient = pat) 512 513 wx.EndBusyCursor() 514 515 if not success: 516 gmGuiHelpers.gm_show_error ( 517 _('Supposedly there are unreviewed documents\n' 518 'for patient [%s]. However, I cannot find\n' 519 'that patient in the GNUmed database.' 520 ) % pk_patient, 521 _('handling provider inbox item') 522 ) 523 return False 524 525 wx.CallAfter(gmDispatcher.send, signal = 'display_widget', name = 'gmShowMedDocs', sort_mode = 'review') 526 return True
527 #--------------------------------------------------------
528 - def _goto_measurements_review(self, pk_context=None, pk_patient=None):
529 wx.BeginBusyCursor() 530 success = gmPatSearchWidgets.set_active_patient(patient=gmPerson.cIdentity(aPK_obj=pk_patient)) 531 wx.EndBusyCursor() 532 if not success: 533 gmGuiHelpers.gm_show_error ( 534 _('Supposedly there are unreviewed results\n' 535 'for patient [%s]. However, I cannot find\n' 536 'that patient in the GNUmed database.' 537 ) % pk_patient, 538 _('handling provider inbox item') 539 ) 540 return False 541 542 wx.CallAfter(gmDispatcher.send, signal = 'display_widget', name = 'gmMeasurementsGridPlugin') 543 return True
544 #============================================================ 545 if __name__ == '__main__': 546 547 gmI18N.activate_locale() 548 gmI18N.install_domain(domain = 'gnumed') 549
550 - def test_configure_wp_plugins():
551 app = wx.PyWidgetTester(size = (400, 300)) 552 configure_workplace_plugins()
553
554 - def test_message_inbox():
555 app = wx.PyWidgetTester(size = (800, 600)) 556 app.SetWidget(cProviderInboxPnl, -1) 557 app.MainLoop()
558 559 if len(sys.argv) > 1 and sys.argv[1] == 'test': 560 #test_configure_wp_plugins() 561 test_message_inbox() 562 563 #============================================================ 564 # $Log: gmProviderInboxWidgets.py,v $ 565 # Revision 1.47 2010/01/10 17:29:26 ncq 566 # - slightly postpone plugin raising after message double-clicking 567 # to avoid async problems with patient activation plugin raising 568 # 569 # Revision 1.46 2010/01/06 14:43:55 ncq 570 # - inbox title flush left 571 # 572 # Revision 1.45 2009/11/30 22:27:57 ncq 573 # - adjust provider inbox filters 574 # 575 # Revision 1.44 2009/11/30 13:16:27 ncq 576 # - no deletion of .is_virtual messages 577 # - data update after patient activation 578 # - enabling active-patient filter on init, too 579 # 580 # Revision 1.43 2009/11/29 13:07:15 ncq 581 # - properly map messages to check boxes as per list 582 # 583 # Revision 1.42 2009/11/28 20:07:08 ncq 584 # - fix message detail display 585 # 586 # Revision 1.41 2009/08/24 20:11:27 ncq 587 # - bump db version 588 # - fix tag creation 589 # - provider inbox: 590 # enable filter-to-active-patient, 591 # listen to new signal, 592 # use cInboxMessage class 593 # - properly constrain LOINC phrasewheel SQL 594 # - include v12 scripts in release 595 # - install arriba jar to /usr/local/bin/ 596 # - check for table existence in audit schema generator 597 # - include dem.message inbox with additional generic signals 598 # 599 # Revision 1.40 2009/07/01 17:12:11 ncq 600 # - better wording 601 # 602 # Revision 1.39 2009/06/29 15:10:58 ncq 603 # - prevent deletion of the active workplace 604 # 605 # Revision 1.38 2009/06/22 09:28:21 ncq 606 # - improved wording as per list 607 # 608 # Revision 1.37 2009/06/20 12:47:38 ncq 609 # - test 610 # 611 # Revision 1.36 2009/06/11 12:37:25 ncq 612 # - much simplified initial setup of list ctrls 613 # 614 # Revision 1.35 2009/06/04 16:30:30 ncq 615 # - use set active patient from pat search widgets 616 # 617 # Revision 1.34 2009/05/18 15:32:42 ncq 618 # - add deleting workplaces 619 # 620 # Revision 1.33 2009/02/18 13:47:43 ncq 621 # - do not throw exception in _goto_doc_review if patient 622 # does not exist, rather report error 623 # 624 # Revision 1.32 2009/01/17 23:09:30 ncq 625 # - cleanup 626 # 627 # Revision 1.31 2008/10/22 12:21:57 ncq 628 # - use %x in strftime where appropriate 629 # 630 # Revision 1.30 2008/09/04 12:52:23 ncq 631 # - display received_when for incoming messages 632 # 633 # Revision 1.29 2008/07/13 16:14:59 ncq 634 # - implement keyword based text expansion widgets 635 # 636 # Revision 1.28 2008/06/09 15:36:58 ncq 637 # - provider phrasewheel 638 # 639 # Revision 1.27 2008/05/20 16:45:43 ncq 640 # - add filter for active patient messages 641 # - fix longstanding bug updating display on *every* PAINT event 642 # 643 # Revision 1.26 2008/04/22 21:19:22 ncq 644 # - signal busy-ness when activating patient 645 # 646 # Revision 1.25 2008/03/29 16:21:16 ncq 647 # - handle unreviewed tests messages 648 # - listen to review changes 649 # 650 # Revision 1.24 2008/03/05 22:30:14 ncq 651 # - new style logging 652 # 653 # Revision 1.23 2008/02/25 17:40:45 ncq 654 # - establish db cfg instance early enough 655 # 656 # Revision 1.22 2008/01/30 14:03:42 ncq 657 # - use signal names directly 658 # - switch to std lib logging 659 # 660 # Revision 1.21 2008/01/27 21:18:45 ncq 661 # - don't crash on trying to edit module-less workplace 662 # 663 # Revision 1.20 2008/01/05 16:41:27 ncq 664 # - remove logging from gm_show_*() 665 # 666 # Revision 1.19 2007/11/28 11:56:30 ncq 667 # - better logging 668 # 669 # Revision 1.18 2007/11/23 23:36:38 ncq 670 # - finish configure_workplace_plugins() 671 # 672 # Revision 1.17 2007/11/02 13:59:33 ncq 673 # - request user attention when new item arrives 674 # 675 # Revision 1.16 2007/10/30 12:51:45 ncq 676 # - make it a reget mixin child 677 # - cleanup 678 # - listen on backend changes 679 # 680 # Revision 1.15 2007/10/08 13:05:10 ncq 681 # - use gmListWidgets.cReportListCtrl 682 # - fix right-click on empty message list crashes 683 # - start test suite 684 # - start configure_workplace_plugins() 685 # 686 # Revision 1.14 2007/08/12 00:12:41 ncq 687 # - no more gmSignals.py 688 # 689 # Revision 1.13 2007/05/14 13:11:25 ncq 690 # - use statustext() signal 691 # 692 # Revision 1.12 2007/01/04 22:52:34 ncq 693 # - show proper salutation for people without title 694 # 695 # Revision 1.11 2006/12/17 20:46:24 ncq 696 # - cleanup 697 # 698 # Revision 1.10 2006/11/24 10:01:31 ncq 699 # - gm_beep_statustext() -> gm_statustext() 700 # 701 # Revision 1.9 2006/05/28 16:19:54 ncq 702 # - repopulate_ui() needed for receive_focus() from plugin base class 703 # 704 # Revision 1.8 2006/05/20 18:55:21 ncq 705 # - calculate handler via original category/type not i18ned one 706 # 707 # Revision 1.7 2006/05/16 15:56:03 ncq 708 # - properly resize columns 709 # 710 # Revision 1.6 2006/05/15 14:46:38 ncq 711 # - implement message deletion via context menu popup 712 # 713 # Revision 1.5 2006/05/15 13:39:31 ncq 714 # - cleanup 715 # 716 # Revision 1.4 2006/05/12 22:04:22 ncq 717 # - add _populate_with_data() 718 # - fully implement _goto_doc_review() 719 # 720 # Revision 1.3 2006/05/12 12:21:58 ncq 721 # - implement double-click item handling 722 # - use gmCurrentProvider 723 # - show message on item focused 724 # 725 # Revision 1.2 2006/01/22 18:10:52 ncq 726 # - now really display messages from backend 727 # 728 # Revision 1.1 2006/01/15 14:30:56 ncq 729 # - first crude cut at this 730 # 731 # 732