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  __version__ = "$Revision: 1.48 $" 
  5  __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>" 
  6   
  7  import sys, logging 
  8   
  9   
 10  import wx 
 11   
 12   
 13  if __name__ == '__main__': 
 14          sys.path.insert(0, '../../') 
 15  from Gnumed.pycommon import gmI18N, gmDispatcher, gmTools, gmCfg, gmPG2, gmExceptions 
 16  from Gnumed.business import gmPerson, gmSurgery 
 17  from Gnumed.wxpython import gmGuiHelpers, gmListWidgets, gmPlugin, gmRegetMixin, gmPhraseWheel, gmEditArea, gmAuthWidgets, gmPatSearchWidgets 
 18  from Gnumed.wxGladeWidgets import wxgProviderInboxPnl, wxgTextExpansionEditAreaPnl 
 19   
 20   
 21  _log = logging.getLogger('gm.ui') 
 22  _log.info(__version__) 
 23   
 24  _indicator = { 
 25          -1: '', 
 26          0: '', 
 27          1: '*!!*' 
 28  } 
 29  #============================================================ 
30 -class cTextExpansionEditAreaPnl(wxgTextExpansionEditAreaPnl.wxgTextExpansionEditAreaPnl):
31
32 - def __init__(self, *args, **kwds):
33 34 try: 35 self.__keyword = kwds['keyword'] 36 del kwds['keyword'] 37 except KeyError: 38 self.__keyword = None 39 40 wxgTextExpansionEditAreaPnl.wxgTextExpansionEditAreaPnl.__init__(self, *args, **kwds) 41 42 self.__init_ui() 43 self.__register_interests()
44 #--------------------------------------------------------
45 - def save(self):
46 if not self.__valid_for_save(): 47 return False 48 49 if self.__keyword is None: 50 result = gmPG2.add_text_expansion ( 51 keyword = self._TCTRL_keyword.GetValue().strip(), 52 expansion = self._TCTRL_expansion.GetValue(), 53 public = self._RBTN_public.GetValue() 54 ) 55 else: 56 gmPG2.edit_text_expansion ( 57 keyword = self._TCTRL_keyword.GetValue().strip(), 58 expansion = self._TCTRL_expansion.GetValue() 59 ) 60 result = True 61 62 return result
63 #--------------------------------------------------------
64 - def refresh(self):
65 self.__init_ui()
66 # if self.__keyword is not None: 67 # self._TCTRL_expansion.SetValue(u'') 68 #-------------------------------------------------------- 69 # event handling 70 #--------------------------------------------------------
71 - def __register_interests(self):
72 self._TCTRL_keyword.Bind(wx.EVT_TEXT, self._on_keyword_modified)
73 #--------------------------------------------------------
74 - def _on_keyword_modified(self, evt):
75 if self._TCTRL_keyword.GetValue().strip() == u'': 76 self._TCTRL_expansion.Enable(False) 77 else: 78 self._TCTRL_expansion.Enable(True)
79 #-------------------------------------------------------- 80 # internal API 81 #--------------------------------------------------------
82 - def __valid_for_save(self):
83 84 kwd = self._TCTRL_keyword.GetValue().strip() 85 if kwd == u'': 86 self._TCTRL_keyword.SetBackgroundColour('pink') 87 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save text expansion without keyword.'), beep = True) 88 return False 89 self._TCTRL_keyword.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 90 91 if self._TCTRL_expansion.GetValue().strip() == u'': 92 self._TCTRL_expansion.SetBackgroundColour('pink') 93 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save text expansion without expansion text.'), beep = True) 94 return False 95 self._TCTRL_expansion.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 96 97 return True
98 #--------------------------------------------------------
99 - def __init_ui(self, keyword=None):
100 101 if keyword is not None: 102 self.__keyword = keyword 103 104 if self.__keyword is None: 105 self._TCTRL_keyword.SetValue(u'') 106 self._TCTRL_keyword.Enable(True) 107 self._TCTRL_expansion.SetValue(u'') 108 self._TCTRL_expansion.Enable(False) 109 self._RBTN_public.Enable(True) 110 self._RBTN_private.Enable(True) 111 self._RBTN_public.SetValue(1) 112 else: 113 expansion = gmPG2.expand_keyword(keyword = self.__keyword) 114 self._TCTRL_keyword.SetValue(self.__keyword) 115 self._TCTRL_keyword.Enable(False) 116 self._TCTRL_expansion.SetValue(gmTools.coalesce(expansion, u'')) 117 self._TCTRL_expansion.Enable(True) 118 self._RBTN_public.Enable(False) 119 self._RBTN_private.Enable(False)
120 #============================================================
121 -def configure_keyword_text_expansion(parent=None):
122 123 if parent is None: 124 parent = wx.GetApp().GetTopWindow() 125 126 #---------------------- 127 def delete(keyword=None): 128 gmPG2.delete_text_expansion(keyword = keyword) 129 return True
130 #---------------------- 131 def edit(keyword=None): 132 # add new keyword 133 ea = cTextExpansionEditAreaPnl(parent, -1, keyword=keyword) 134 dlg = gmEditArea.cGenericEditAreaDlg(parent, -1, edit_area = ea) 135 dlg.SetTitle ( 136 gmTools.coalesce(keyword, _('Adding text expansion'), _('Editing text expansion "%s"')) 137 ) 138 if dlg.ShowModal() == wx.ID_OK: 139 return True 140 141 return False 142 #---------------------- 143 def refresh(lctrl=None): 144 kwds = [ [ 145 r[0], 146 gmTools.bool2subst(r[1], gmTools.u_checkmark_thick, u''), 147 gmTools.bool2subst(r[2], gmTools.u_checkmark_thick, u''), 148 r[3] 149 ] for r in gmPG2.get_text_expansion_keywords() 150 ] 151 data = [ r[0] for r in gmPG2.get_text_expansion_keywords() ] 152 lctrl.set_string_items(kwds) 153 lctrl.set_data(data) 154 #---------------------- 155 156 gmListWidgets.get_choices_from_list ( 157 parent = parent, 158 msg = _('\nSelect the keyword you want to edit !\n'), 159 caption = _('Editing keyword-based text expansions ...'), 160 columns = [_('Keyword'), _('Public'), _('Private'), _('Owner')], 161 single_selection = True, 162 edit_callback = edit, 163 new_callback = edit, 164 delete_callback = delete, 165 refresh_callback = refresh 166 ) 167 #============================================================
168 -class cProviderPhraseWheel(gmPhraseWheel.cPhraseWheel):
169
170 - def __init__(self, *args, **kwargs):
171 172 gmPhraseWheel.cPhraseWheel.__init__ ( 173 self, 174 *args, 175 **kwargs 176 ) 177 self.matcher = gmPerson.cMatchProvider_Provider() 178 self.SetToolTipString(_('Select a healthcare provider.')) 179 self.selection_only = True
180 #============================================================ 181 # practice related widgets 182 #============================================================
183 -def show_audit_trail(parent=None):
184 185 if parent is None: 186 parent = wx.GetApp().GetTopWindow() 187 188 #----------------------------------- 189 def refresh(lctrl): 190 191 cmd = u'SELECT * FROM audit.v_audit_trail ORDER BY audit_when_ts' 192 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = False) 193 194 195 196 lctrl.set_string_items ( 197 [ [ 198 r['event_when'], 199 r['event_by'], 200 u'%s %s %s' % ( 201 gmTools.coalesce(r['row_version_before'], gmTools.u_diameter), 202 gmTools.u_right_arrow, 203 gmTools.coalesce(r['row_version_after'], gmTools.u_diameter) 204 ), 205 r['event_table'], 206 r['event'], 207 r['pk_audit'] 208 ] for r in rows ] 209 )
210 #lctrl.set_selections(selections = sels) 211 #----------------------------------- 212 gmListWidgets.get_choices_from_list ( 213 parent = parent, 214 msg = u'', 215 caption = _('GNUmed database audit log ...'), 216 columns = [ _('When'), _('Who'), _('Revisions'), _('Table'), _('Event'), '#' ], 217 single_selection = True, 218 refresh_callback = refresh 219 ) 220 221 #============================================================ 222 # FIXME: this should be moved elsewhere !
223 -def configure_workplace_plugins(parent=None):
224 225 if parent is None: 226 parent = wx.GetApp().GetTopWindow() 227 228 #----------------------------------- 229 def delete(workplace): 230 231 curr_workplace = gmSurgery.gmCurrentPractice().active_workplace 232 if workplace == curr_workplace: 233 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete the active workplace.'), beep = True) 234 return False 235 236 dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 237 parent, 238 -1, 239 caption = _('Deleting workplace ...'), 240 question = _('Are you sure you want to delete this workplace ?\n\n "%s"\n') % workplace, 241 show_checkbox = True, 242 checkbox_msg = _('delete configuration, too'), 243 checkbox_tooltip = _( 244 'Check this if you want to delete all configuration items\n' 245 'for this workplace along with the workplace itself.' 246 ), 247 button_defs = [ 248 {'label': _('Delete'), 'tooltip': _('Yes, delete this workplace.'), 'default': True}, 249 {'label': _('Do NOT delete'), 'tooltip': _('No, do NOT delete this workplace'), 'default': False} 250 ] 251 ) 252 253 decision = dlg.ShowModal() 254 if decision != wx.ID_YES: 255 dlg.Destroy() 256 return False 257 258 include_cfg = dlg.checkbox_is_checked() 259 dlg.Destroy() 260 261 dbo_conn = gmAuthWidgets.get_dbowner_connection(procedure = _('delete workplace')) 262 if not dbo_conn: 263 return False 264 265 gmSurgery.delete_workplace(workplace = workplace, conn = dbo_conn, delete_config = include_cfg) 266 return True
267 #----------------------------------- 268 def edit(workplace=None): 269 270 available_plugins = gmPlugin.get_installed_plugins(plugin_dir='gui') 271 272 dbcfg = gmCfg.cCfgSQL() 273 274 if workplace is None: 275 dlg = wx.TextEntryDialog ( 276 parent = parent, 277 message = _('Enter a descriptive name for the new workplace:'), 278 caption = _('Configuring GNUmed workplaces ...'), 279 defaultValue = u'', 280 style = wx.OK | wx.CENTRE 281 ) 282 dlg.ShowModal() 283 workplace = dlg.GetValue().strip() 284 if workplace == u'': 285 gmGuiHelpers.gm_show_error(_('Cannot save a new workplace without a name.'), _('Configuring GNUmed workplaces ...')) 286 return False 287 curr_plugins = [] 288 choices = available_plugins 289 else: 290 curr_plugins = gmTools.coalesce(dbcfg.get2 ( 291 option = u'horstspace.notebook.plugin_load_order', 292 workplace = workplace, 293 bias = 'workplace' 294 ), [] 295 ) 296 choices = curr_plugins[:] 297 for p in available_plugins: 298 if p not in choices: 299 choices.append(p) 300 301 sels = range(len(curr_plugins)) 302 new_plugins = gmListWidgets.get_choices_from_list ( 303 parent = parent, 304 msg = _( 305 '\n' 306 'Select the plugin(s) to be loaded the next time\n' 307 'the client is restarted under the workplace:\n' 308 '\n' 309 ' [%s]' 310 '\n' 311 ) % workplace, 312 caption = _('Configuring GNUmed workplaces ...'), 313 choices = choices, 314 selections = sels, 315 columns = [_('Plugins')], 316 single_selection = False 317 ) 318 319 if new_plugins == curr_plugins: 320 return True 321 322 if new_plugins is None: 323 return True 324 325 dbcfg.set ( 326 option = u'horstspace.notebook.plugin_load_order', 327 value = new_plugins, 328 workplace = workplace 329 ) 330 331 return True 332 #----------------------------------- 333 def refresh(lctrl): 334 workplaces = gmSurgery.gmCurrentPractice().workplaces 335 curr_workplace = gmSurgery.gmCurrentPractice().active_workplace 336 try: 337 sels = [workplaces.index(curr_workplace)] 338 except ValueError: 339 sels = [] 340 341 lctrl.set_string_items(workplaces) 342 lctrl.set_selections(selections = sels) 343 #----------------------------------- 344 gmListWidgets.get_choices_from_list ( 345 parent = parent, 346 msg = _( 347 '\nSelect the workplace to configure below.\n' 348 '\n' 349 'The currently active workplace is preselected.\n' 350 ), 351 caption = _('Configuring GNUmed workplaces ...'), 352 columns = [_('Workplace')], 353 single_selection = True, 354 refresh_callback = refresh, 355 edit_callback = edit, 356 new_callback = edit, 357 delete_callback = delete 358 ) 359 #============================================================
360 -class cProviderInboxPnl(wxgProviderInboxPnl.wxgProviderInboxPnl, gmRegetMixin.cRegetOnPaintMixin):
361 362 _item_handlers = {} 363 _patient_msg_types = ['clinical.review docs', 'clinical.review results'] 364 #--------------------------------------------------------
365 - def __init__(self, *args, **kwds):
366 367 wxgProviderInboxPnl.wxgProviderInboxPnl.__init__(self, *args, **kwds) 368 gmRegetMixin.cRegetOnPaintMixin.__init__(self) 369 370 self.provider = gmPerson.gmCurrentProvider() 371 self.filter_mode = 'all' 372 self.__init_ui() 373 374 cProviderInboxPnl._item_handlers['clinical.review docs'] = self._goto_doc_review 375 cProviderInboxPnl._item_handlers['clinical.review results'] = self._goto_measurements_review 376 377 self.__register_interests()
378 #-------------------------------------------------------- 379 # reget-on-paint API 380 #--------------------------------------------------------
381 - def _populate_with_data(self):
382 self.__populate_inbox() 383 return True
384 #-------------------------------------------------------- 385 # internal helpers 386 #--------------------------------------------------------
387 - def __register_interests(self):
388 gmDispatcher.connect(signal = u'message_inbox_generic_mod_db', receiver = self._on_message_inbox_mod_db) 389 gmDispatcher.connect(signal = u'message_inbox_mod_db', receiver = self._on_message_inbox_mod_db) 390 # FIXME: listen for results insertion/deletion 391 gmDispatcher.connect(signal = u'reviewed_test_results_mod_db', receiver = self._on_message_inbox_mod_db) 392 # FIXME: listen for doc insertion/deletion 393 # FIXME: listen for doc reviews 394 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
395 #--------------------------------------------------------
396 - def __init_ui(self):
397 self._LCTRL_provider_inbox.set_columns([u'', _('date'), _('category'), _('type'), _('message')]) 398 399 msg = _('\n Inbox of %(title)s %(lname)s.\n') % { 400 'title': gmTools.coalesce ( 401 self.provider['title'], 402 gmPerson.map_gender2salutation(self.provider['gender']) 403 ), 404 'lname': self.provider['lastnames'] 405 } 406 407 self._msg_welcome.SetLabel(msg) 408 409 if gmPerson.gmCurrentPatient().connected: 410 self._RBTN_active_patient.Enable()
411 #--------------------------------------------------------
412 - def __populate_inbox(self):
413 414 """Fill UI with data.""" 415 self.__msgs = self.provider.inbox.messages 416 417 if self.filter_mode == 'active': 418 if gmPerson.gmCurrentPatient().connected: 419 curr_pat_id = gmPerson.gmCurrentPatient().ID 420 self.__msgs = [ m for m in self.__msgs if m['pk_patient'] == curr_pat_id ] 421 else: 422 self.__msgs = [] 423 424 items = [ 425 [ 426 _indicator[m['importance']], 427 m['received_when'].strftime('%Y-%m-%d'), 428 m['l10n_category'], 429 m['l10n_type'], 430 m['comment'] 431 ] for m in self.__msgs 432 ] 433 self._LCTRL_provider_inbox.set_string_items(items = items) 434 self._LCTRL_provider_inbox.set_data(data = self.__msgs) 435 self._LCTRL_provider_inbox.set_column_widths()
436 #-------------------------------------------------------- 437 # event handlers 438 #--------------------------------------------------------
439 - def _on_post_patient_selection(self):
440 wx.CallAfter(self._schedule_data_reget) 441 wx.CallAfter(self._RBTN_active_patient.Enable)
442 #--------------------------------------------------------
443 - def _on_message_inbox_mod_db(self, *args, **kwargs):
444 wx.CallAfter(self._schedule_data_reget) 445 gmDispatcher.send(signal = u'request_user_attention', msg = _('Please check your GNUmed Inbox !'))
446 #--------------------------------------------------------
447 - def _lst_item_activated(self, evt):
448 msg = self._LCTRL_provider_inbox.get_selected_item_data(only_one = True) 449 if msg is None: 450 return 451 452 handler_key = '%s.%s' % (msg['category'], msg['type']) 453 try: 454 handle_item = cProviderInboxPnl._item_handlers[handler_key] 455 except KeyError: 456 gmGuiHelpers.gm_show_warning ( 457 _( 458 """No double-click action pre-programmed into 459 GNUmed for message category and type: 460 461 [%s] 462 """ 463 ) % handler_key, 464 _('handling provider inbox item') 465 ) 466 return False 467 468 if not handle_item(pk_context = msg['pk_context'], pk_patient = msg['pk_patient']): 469 _log.error('item handler returned "false"') 470 _log.error('handler key: [%s]', handler_key) 471 _log.error('message: %s', str(msg)) 472 return False 473 474 return True
475 #--------------------------------------------------------
476 - def _lst_item_focused(self, evt):
477 pass
478 #--------------------------------------------------------
479 - def _lst_item_selected(self, evt):
480 msg = self._LCTRL_provider_inbox.get_selected_item_data(only_one = True) 481 if msg is None: 482 return 483 484 if msg['data'] is None: 485 tmp = _('Message: %s') % msg['comment'] 486 else: 487 tmp = _('Message: %s\nData: %s') % (msg['comment'], msg['data']) 488 489 self._TXT_inbox_item_comment.SetValue(tmp)
490 #--------------------------------------------------------
491 - def _lst_item_right_clicked(self, evt):
492 tmp = self._LCTRL_provider_inbox.get_selected_item_data(only_one = True) 493 if tmp is None: 494 return 495 self.__focussed_msg = tmp 496 497 # build menu 498 menu = wx.Menu(title = _('Inbox Message menu')) 499 # - delete message 500 if not self.__focussed_msg['is_virtual']: 501 ID = wx.NewId() 502 menu.AppendItem(wx.MenuItem(menu, ID, _('delete message'))) 503 wx.EVT_MENU(menu, ID, self._on_delete_focussed_msg) 504 505 # show menu 506 self.PopupMenu(menu, wx.DefaultPosition) 507 menu.Destroy()
508 #--------------------------------------------------------
510 self.filter_mode = 'all' 511 self._TXT_inbox_item_comment.SetValue(u'') 512 self.__populate_inbox()
513 #--------------------------------------------------------
515 self.filter_mode = 'active' 516 self._TXT_inbox_item_comment.SetValue(u'') 517 self.__populate_inbox()
518 #-------------------------------------------------------- 519 # item handlers 520 #--------------------------------------------------------
521 - def _on_delete_focussed_msg(self, evt):
522 if self.__focussed_msg['is_virtual']: 523 gmDispatcher.send(signal = 'statustext', msg = _('You must deal with the reason for this message to remove it from your inbox.'), beep = True) 524 return False 525 526 if not self.provider.inbox.delete_message(self.__focussed_msg['pk_message_inbox']): 527 gmDispatcher.send(signal='statustext', msg=_('Problem removing message from Inbox.')) 528 return False 529 return True
530 #--------------------------------------------------------
531 - def _goto_doc_review(self, pk_context=None, pk_patient=None):
532 wx.BeginBusyCursor() 533 534 try: 535 pat = gmPerson.cIdentity(aPK_obj = pk_patient) 536 except gmExceptions.ConstructorError: 537 wx.EndBusyCursor() 538 _log.exception('patient [%s] not found', pk_patient) 539 gmGuiHelpers.gm_show_error ( 540 _('Supposedly there are unreviewed documents\n' 541 'for patient [%s]. However, I cannot find\n' 542 'that patient in the GNUmed database.' 543 ) % pk_patient, 544 _('handling provider inbox item') 545 ) 546 return False 547 548 success = gmPatSearchWidgets.set_active_patient(patient = pat) 549 550 wx.EndBusyCursor() 551 552 if not success: 553 gmGuiHelpers.gm_show_error ( 554 _('Supposedly there are unreviewed documents\n' 555 'for patient [%s]. However, I cannot find\n' 556 'that patient in the GNUmed database.' 557 ) % pk_patient, 558 _('handling provider inbox item') 559 ) 560 return False 561 562 wx.CallAfter(gmDispatcher.send, signal = 'display_widget', name = 'gmShowMedDocs', sort_mode = 'review') 563 return True
564 #--------------------------------------------------------
565 - def _goto_measurements_review(self, pk_context=None, pk_patient=None):
566 wx.BeginBusyCursor() 567 success = gmPatSearchWidgets.set_active_patient(patient=gmPerson.cIdentity(aPK_obj=pk_patient)) 568 wx.EndBusyCursor() 569 if not success: 570 gmGuiHelpers.gm_show_error ( 571 _('Supposedly there are unreviewed results\n' 572 'for patient [%s]. However, I cannot find\n' 573 'that patient in the GNUmed database.' 574 ) % pk_patient, 575 _('handling provider inbox item') 576 ) 577 return False 578 579 wx.CallAfter(gmDispatcher.send, signal = 'display_widget', name = 'gmMeasurementsGridPlugin') 580 return True
581 #============================================================ 582 if __name__ == '__main__': 583 584 gmI18N.activate_locale() 585 gmI18N.install_domain(domain = 'gnumed') 586
587 - def test_configure_wp_plugins():
588 app = wx.PyWidgetTester(size = (400, 300)) 589 configure_workplace_plugins()
590
591 - def test_message_inbox():
592 app = wx.PyWidgetTester(size = (800, 600)) 593 app.SetWidget(cProviderInboxPnl, -1) 594 app.MainLoop()
595 596 if len(sys.argv) > 1 and sys.argv[1] == 'test': 597 #test_configure_wp_plugins() 598 test_message_inbox() 599 600 #============================================================ 601