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