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.48 2010/02/02 13:56:42 ncq Exp $ 
  6  __version__ = "$Revision: 1.48 $" 
  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 expansion'), _('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.48 2010/02/02 13:56:42 ncq 566 # - text expansion management typo fix 567 # 568 # Revision 1.47 2010/01/10 17:29:26 ncq 569 # - slightly postpone plugin raising after message double-clicking 570 # to avoid async problems with patient activation plugin raising 571 # 572 # Revision 1.46 2010/01/06 14:43:55 ncq 573 # - inbox title flush left 574 # 575 # Revision 1.45 2009/11/30 22:27:57 ncq 576 # - adjust provider inbox filters 577 # 578 # Revision 1.44 2009/11/30 13:16:27 ncq 579 # - no deletion of .is_virtual messages 580 # - data update after patient activation 581 # - enabling active-patient filter on init, too 582 # 583 # Revision 1.43 2009/11/29 13:07:15 ncq 584 # - properly map messages to check boxes as per list 585 # 586 # Revision 1.42 2009/11/28 20:07:08 ncq 587 # - fix message detail display 588 # 589 # Revision 1.41 2009/08/24 20:11:27 ncq 590 # - bump db version 591 # - fix tag creation 592 # - provider inbox: 593 # enable filter-to-active-patient, 594 # listen to new signal, 595 # use cInboxMessage class 596 # - properly constrain LOINC phrasewheel SQL 597 # - include v12 scripts in release 598 # - install arriba jar to /usr/local/bin/ 599 # - check for table existence in audit schema generator 600 # - include dem.message inbox with additional generic signals 601 # 602 # Revision 1.40 2009/07/01 17:12:11 ncq 603 # - better wording 604 # 605 # Revision 1.39 2009/06/29 15:10:58 ncq 606 # - prevent deletion of the active workplace 607 # 608 # Revision 1.38 2009/06/22 09:28:21 ncq 609 # - improved wording as per list 610 # 611 # Revision 1.37 2009/06/20 12:47:38 ncq 612 # - test 613 # 614 # Revision 1.36 2009/06/11 12:37:25 ncq 615 # - much simplified initial setup of list ctrls 616 # 617 # Revision 1.35 2009/06/04 16:30:30 ncq 618 # - use set active patient from pat search widgets 619 # 620 # Revision 1.34 2009/05/18 15:32:42 ncq 621 # - add deleting workplaces 622 # 623 # Revision 1.33 2009/02/18 13:47:43 ncq 624 # - do not throw exception in _goto_doc_review if patient 625 # does not exist, rather report error 626 # 627 # Revision 1.32 2009/01/17 23:09:30 ncq 628 # - cleanup 629 # 630 # Revision 1.31 2008/10/22 12:21:57 ncq 631 # - use %x in strftime where appropriate 632 # 633 # Revision 1.30 2008/09/04 12:52:23 ncq 634 # - display received_when for incoming messages 635 # 636 # Revision 1.29 2008/07/13 16:14:59 ncq 637 # - implement keyword based text expansion widgets 638 # 639 # Revision 1.28 2008/06/09 15:36:58 ncq 640 # - provider phrasewheel 641 # 642 # Revision 1.27 2008/05/20 16:45:43 ncq 643 # - add filter for active patient messages 644 # - fix longstanding bug updating display on *every* PAINT event 645 # 646 # Revision 1.26 2008/04/22 21:19:22 ncq 647 # - signal busy-ness when activating patient 648 # 649 # Revision 1.25 2008/03/29 16:21:16 ncq 650 # - handle unreviewed tests messages 651 # - listen to review changes 652 # 653 # Revision 1.24 2008/03/05 22:30:14 ncq 654 # - new style logging 655 # 656 # Revision 1.23 2008/02/25 17:40:45 ncq 657 # - establish db cfg instance early enough 658 # 659 # Revision 1.22 2008/01/30 14:03:42 ncq 660 # - use signal names directly 661 # - switch to std lib logging 662 # 663 # Revision 1.21 2008/01/27 21:18:45 ncq 664 # - don't crash on trying to edit module-less workplace 665 # 666 # Revision 1.20 2008/01/05 16:41:27 ncq 667 # - remove logging from gm_show_*() 668 # 669 # Revision 1.19 2007/11/28 11:56:30 ncq 670 # - better logging 671 # 672 # Revision 1.18 2007/11/23 23:36:38 ncq 673 # - finish configure_workplace_plugins() 674 # 675 # Revision 1.17 2007/11/02 13:59:33 ncq 676 # - request user attention when new item arrives 677 # 678 # Revision 1.16 2007/10/30 12:51:45 ncq 679 # - make it a reget mixin child 680 # - cleanup 681 # - listen on backend changes 682 # 683 # Revision 1.15 2007/10/08 13:05:10 ncq 684 # - use gmListWidgets.cReportListCtrl 685 # - fix right-click on empty message list crashes 686 # - start test suite 687 # - start configure_workplace_plugins() 688 # 689 # Revision 1.14 2007/08/12 00:12:41 ncq 690 # - no more gmSignals.py 691 # 692 # Revision 1.13 2007/05/14 13:11:25 ncq 693 # - use statustext() signal 694 # 695 # Revision 1.12 2007/01/04 22:52:34 ncq 696 # - show proper salutation for people without title 697 # 698 # Revision 1.11 2006/12/17 20:46:24 ncq 699 # - cleanup 700 # 701 # Revision 1.10 2006/11/24 10:01:31 ncq 702 # - gm_beep_statustext() -> gm_statustext() 703 # 704 # Revision 1.9 2006/05/28 16:19:54 ncq 705 # - repopulate_ui() needed for receive_focus() from plugin base class 706 # 707 # Revision 1.8 2006/05/20 18:55:21 ncq 708 # - calculate handler via original category/type not i18ned one 709 # 710 # Revision 1.7 2006/05/16 15:56:03 ncq 711 # - properly resize columns 712 # 713 # Revision 1.6 2006/05/15 14:46:38 ncq 714 # - implement message deletion via context menu popup 715 # 716 # Revision 1.5 2006/05/15 13:39:31 ncq 717 # - cleanup 718 # 719 # Revision 1.4 2006/05/12 22:04:22 ncq 720 # - add _populate_with_data() 721 # - fully implement _goto_doc_review() 722 # 723 # Revision 1.3 2006/05/12 12:21:58 ncq 724 # - implement double-click item handling 725 # - use gmCurrentProvider 726 # - show message on item focused 727 # 728 # Revision 1.2 2006/01/22 18:10:52 ncq 729 # - now really display messages from backend 730 # 731 # Revision 1.1 2006/01/15 14:30:56 ncq 732 # - first crude cut at this 733 # 734 # 735