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

Source Code for Module Gnumed.wxpython.gmExceptionHandlingWidgets

  1  """GNUmed exception handling widgets.""" 
  2  # ======================================================================== 
  3  __version__ = "$Revision: 1.17 $" 
  4  __author__  = "K. Hilbert <Karsten.Hilbert@gmx.net>" 
  5  __license__ = "GPL (details at http://www.gnu.org)" 
  6   
  7  import logging, exceptions, traceback, re as regex, sys, os, shutil, datetime as pyDT, codecs 
  8   
  9   
 10  import wx 
 11   
 12   
 13  from Gnumed.business import gmSurgery 
 14  from Gnumed.pycommon import gmDispatcher, gmTools, gmCfg2, gmI18N, gmLog2, gmPG2 
 15  from Gnumed.wxpython import gmGuiHelpers 
 16   
 17   
 18  _log2 = logging.getLogger('gm.gui') 
 19  _log2.info(__version__) 
 20   
 21  _prev_excepthook = None 
 22  application_is_closing = False 
 23  #========================================================================= 
24 -def set_client_version(version):
25 global _client_version 26 _client_version = version
27 #-------------------------------------------------------------------------
28 -def set_sender_email(email):
29 global _sender_email 30 _sender_email = email
31 #-------------------------------------------------------------------------
32 -def set_helpdesk(helpdesk):
33 global _helpdesk 34 _helpdesk = helpdesk
35 #-------------------------------------------------------------------------
36 -def set_staff_name(staff_name):
37 global _staff_name 38 _staff_name = staff_name
39 #-------------------------------------------------------------------------
40 -def set_is_public_database(value):
41 global _is_public_database 42 _is_public_database = value
43 #-------------------------------------------------------------------------
44 -def handle_uncaught_exception_wx(t, v, tb):
45 46 _log2.debug('unhandled exception caught:', exc_info = (t, v, tb)) 47 48 # Strg-C ? 49 if t == KeyboardInterrupt: 50 print "<Ctrl-C>: Shutting down ..." 51 top_win = wx.GetApp().GetTopWindow() 52 wx.CallAfter(top_win.Close) 53 return 54 55 # careful: MSW does reference counting on Begin/End* :-( 56 try: wx.EndBusyCursor() 57 except: pass 58 59 # exception on shutdown ? 60 if application_is_closing: 61 # dead object error ? 62 if t == wx._core.PyDeadObjectError: 63 return 64 gmLog2.log_stack_trace() 65 return 66 67 # try to ignore those, they come about from async handling 68 # as Robin tells us 69 if t == wx._core.PyDeadObjectError: 70 _log2.warning('continuing and hoping for the best') 71 return 72 73 # failed import ? 74 if t == exceptions.ImportError: 75 _log2.error('module [%s] not installed', v) 76 gmGuiHelpers.gm_show_error ( 77 aTitle = _('Missing GNUmed module'), 78 aMessage = _( 79 'GNUmed detected that parts of it are not\n' 80 'properly installed. The following message\n' 81 'names the missing part:\n' 82 '\n' 83 ' "%s"\n' 84 '\n' 85 'Please make sure to get the missing\n' 86 'parts installed. Otherwise some of the\n' 87 'functionality will not be accessible.' 88 ) % v 89 ) 90 return 91 92 # other exceptions 93 _cfg = gmCfg2.gmCfgData() 94 if _cfg.get(option = 'debug') is False: 95 _log2.error('enabling debug mode') 96 _cfg.set_option(option = 'debug', value = True) 97 root_logger = logging.getLogger() 98 root_logger.setLevel(logging.DEBUG) 99 _log2.debug('unhandled exception caught:', exc_info = (t, v, tb)) 100 101 # lost connection ? 102 try: 103 msg = gmPG2.extract_msg_from_pg_exception(exc = v) 104 except: 105 msg = u'cannot extract message from PostgreSQL exception' 106 print msg 107 print v 108 109 conn_loss_on_operational_error = ( 110 (t == gmPG2.dbapi.OperationalError) 111 and 112 ('erver' in msg) 113 and 114 (('term' in msg) or ('abnorm' in msg) or ('end' in msg)) 115 ) 116 conn_loss_on_interface_error = ( 117 (t == gmPG2.dbapi.InterfaceError) 118 and 119 ('onnect' in msg) 120 and 121 (('close' in msg) or ('end' in msg)) 122 ) 123 if (conn_loss_on_operational_error or conn_loss_on_interface_error): 124 _log2.error('lost connection') 125 gmLog2.log_stack_trace() 126 gmLog2.flush() 127 gmGuiHelpers.gm_show_error ( 128 aTitle = _('Lost connection'), 129 aMessage = _( 130 'Since you were last working in GNUmed,\n' 131 'your database connection timed out.\n' 132 '\n' 133 'This GNUmed session is now expired.\n' 134 '\n' 135 'You will have to close this client and\n' 136 'restart a new GNUmed session.' 137 ) 138 ) 139 return 140 141 gmLog2.log_stack_trace() 142 143 name = os.path.basename(_logfile_name) 144 name, ext = os.path.splitext(name) 145 new_name = os.path.expanduser(os.path.join ( 146 '~', 147 'gnumed', 148 'logs', 149 '%s_%s%s' % (name, pyDT.datetime.now().strftime('%Y-%m-%d_%H-%M-%S'), ext) 150 )) 151 152 dlg = cUnhandledExceptionDlg(parent = None, id = -1, exception = (t, v, tb), logfile = new_name) 153 dlg.ShowModal() 154 comment = dlg._TCTRL_comment.GetValue() 155 dlg.Destroy() 156 if (comment is not None) and (comment.strip() != u''): 157 _log2.error(u'user comment: %s', comment.strip()) 158 159 _log2.warning('syncing log file for backup to [%s]', new_name) 160 gmLog2.flush() 161 shutil.copy2(_logfile_name, new_name)
162 # ------------------------------------------------------------------------
163 -def install_wx_exception_handler():
164 165 global _logfile_name 166 _logfile_name = gmLog2._logfile_name 167 168 global _local_account 169 _local_account = os.path.basename(os.path.expanduser('~')) 170 171 set_helpdesk(gmSurgery.gmCurrentPractice().helpdesk) 172 set_staff_name(_local_account) 173 set_is_public_database(False) 174 set_sender_email(None) 175 set_client_version('gmExceptionHandlingWidgets.py %s' % __version__) 176 177 gmDispatcher.connect(signal = 'application_closing', receiver = _on_application_closing) 178 179 global _prev_excepthook 180 _prev_excepthook = sys.excepthook 181 sys.excepthook = handle_uncaught_exception_wx 182 183 return True
184 # ------------------------------------------------------------------------
185 -def uninstall_wx_exception_handler():
186 if _prev_excepthook is None: 187 sys.excepthook = sys.__excepthook__ 188 return True 189 sys.excepthook = _prev_excepthook 190 return True
191 # ------------------------------------------------------------------------
192 -def _on_application_closing():
193 global application_is_closing 194 # used to ignore a few exceptions, such as when the 195 # C++ object has been destroyed before the Python one 196 application_is_closing = True
197 # ========================================================================
198 -def mail_log(parent=None, comment=None, helpdesk=None, sender=None):
199 200 if (comment is None) or (comment.strip() == u''): 201 comment = wx.GetTextFromUser ( 202 message = _( 203 'Please enter a short note on what you\n' 204 'were about to do in GNUmed:' 205 ), 206 caption = _('Sending bug report'), 207 parent = parent 208 ) 209 if comment.strip() == u'': 210 comment = u'<user did not comment on bug report>' 211 212 receivers = [] 213 if helpdesk is not None: 214 receivers = regex.findall ( 215 '[\S]+@[\S]+', 216 helpdesk.strip(), 217 flags = regex.UNICODE | regex.LOCALE 218 ) 219 if len(receivers) == 0: 220 if _is_public_database: 221 receivers = [u'gnumed-bugs@gnu.org'] 222 223 receiver_string = wx.GetTextFromUser ( 224 message = _( 225 'Edit the list of email addresses to send the\n' 226 'bug report to (separate addresses by spaces).\n' 227 '\n' 228 'Note that <gnumed-bugs@gnu.org> refers to\n' 229 'the public (!) GNUmed bugs mailing list.' 230 ), 231 caption = _('Sending bug report'), 232 default_value = ','.join(receivers), 233 parent = parent 234 ) 235 if receiver_string.strip() == u'': 236 return 237 238 receivers = regex.findall ( 239 '[\S]+@[\S]+', 240 receiver_string, 241 flags = regex.UNICODE | regex.LOCALE 242 ) 243 244 dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 245 parent, 246 -1, 247 caption = _('Sending bug report'), 248 question = _( 249 'Your bug report will be sent to:\n' 250 '\n' 251 '%s\n' 252 '\n' 253 'Make sure you have reviewed the log file for potentially\n' 254 'sensitive information before sending out the bug report.\n' 255 '\n' 256 'Note that emailing the report may take a while depending\n' 257 'on the speed of your internet connection.\n' 258 ) % u'\n'.join(receivers), 259 button_defs = [ 260 {'label': _('Send report'), 'tooltip': _('Yes, send the bug report.')}, 261 {'label': _('Cancel'), 'tooltip': _('No, do not send the bug report.')} 262 ], 263 show_checkbox = True, 264 checkbox_msg = _('include log file in bug report') 265 ) 266 dlg._CHBOX_dont_ask_again.SetValue(_is_public_database) 267 go_ahead = dlg.ShowModal() 268 if go_ahead == wx.ID_NO: 269 dlg.Destroy() 270 return 271 272 include_log = dlg._CHBOX_dont_ask_again.GetValue() 273 if not _is_public_database: 274 if include_log: 275 result = gmGuiHelpers.gm_show_question ( 276 _( 277 'The database you are connected to is marked as\n' 278 '"in-production with controlled access".\n' 279 '\n' 280 'You indicated that you want to include the log\n' 281 'file in your bug report. While this is often\n' 282 'useful for debugging the log file might contain\n' 283 'bits of patient data which must not be sent out\n' 284 'without de-identification.\n' 285 '\n' 286 'Please confirm that you want to include the log !' 287 ), 288 _('Sending bug report') 289 ) 290 include_log = (result is True) 291 292 if sender is None: 293 sender = _('<not supplied>') 294 else: 295 if sender.strip() == u'': 296 sender = _('<not supplied>') 297 298 msg = u"""\ 299 Report sent via GNUmed's handler for unexpected exceptions. 300 301 user comment : %s 302 303 client version: %s 304 305 system account: %s 306 staff member : %s 307 sender email : %s 308 309 # enable Launchpad bug tracking 310 affects gnumed 311 tag automatic-report 312 importance medium 313 314 """ % (comment, _client_version, _local_account, _staff_name, sender) 315 if include_log: 316 _log2.error(comment) 317 _log2.warning('syncing log file for emailing') 318 gmLog2.flush() 319 attachments = [ [_logfile_name, 'text/plain', 'quoted-printable'] ] 320 else: 321 attachments = None 322 323 dlg.Destroy() 324 325 wx.BeginBusyCursor() 326 try: 327 gmTools.send_mail ( 328 sender = '%s <%s>' % (_staff_name, gmTools.default_mail_sender), 329 receiver = receivers, 330 subject = u'<bug>: %s' % comment, 331 message = msg, 332 encoding = gmI18N.get_encoding(), 333 server = gmTools.default_mail_server, 334 auth = {'user': gmTools.default_mail_sender, 'password': u'gnumed-at-gmx-net'}, 335 attachments = attachments 336 ) 337 gmDispatcher.send(signal='statustext', msg = _('Bug report has been emailed.')) 338 except: 339 _log2.exception('cannot send bug report') 340 gmDispatcher.send(signal='statustext', msg = _('Bug report COULD NOT be emailed.')) 341 wx.EndBusyCursor()
342 343 # ======================================================================== 344 from Gnumed.wxGladeWidgets import wxgUnhandledExceptionDlg 345
346 -class cUnhandledExceptionDlg(wxgUnhandledExceptionDlg.wxgUnhandledExceptionDlg):
347
348 - def __init__(self, *args, **kwargs):
349 350 exception = kwargs['exception'] 351 del kwargs['exception'] 352 self.logfile = kwargs['logfile'] 353 del kwargs['logfile'] 354 355 wxgUnhandledExceptionDlg.wxgUnhandledExceptionDlg.__init__(self, *args, **kwargs) 356 357 if _sender_email is not None: 358 self._TCTRL_sender.SetValue(_sender_email) 359 self._TCTRL_helpdesk.SetValue(_helpdesk) 360 self._TCTRL_logfile.SetValue(self.logfile) 361 t, v, tb = exception 362 self._TCTRL_exc_type.SetValue(str(t)) 363 self._TCTRL_exc_value.SetValue(str(v)) 364 self._TCTRL_traceback.SetValue(''.join(traceback.format_tb(tb))) 365 366 self.Fit()
367 #------------------------------------------
368 - def _on_close_gnumed_button_pressed(self, evt):
369 comment = self._TCTRL_comment.GetValue() 370 if (comment is not None) and (comment.strip() != u''): 371 _log2.error(u'user comment: %s', comment.strip()) 372 _log2.warning('syncing log file for backup to [%s]', self.logfile) 373 gmLog2.flush() 374 shutil.copy2(_logfile_name, self.logfile) 375 top_win = wx.GetApp().GetTopWindow() 376 wx.CallAfter(top_win.Close) 377 evt.Skip()
378 #------------------------------------------
379 - def _on_mail_button_pressed(self, evt):
380 381 mail_log ( 382 parent = self, 383 comment = self._TCTRL_comment.GetValue().strip(), 384 helpdesk = self._TCTRL_helpdesk.GetValue().strip(), 385 sender = self._TCTRL_sender.GetValue().strip() 386 ) 387 388 evt.Skip()
389 390 # comment = self._TCTRL_comment.GetValue() 391 # if (comment is None) or (comment.strip() == u''): 392 # comment = wx.GetTextFromUser ( 393 # message = _( 394 # 'Please enter a short note on what you\n' 395 # 'were about to do in GNUmed:' 396 # ), 397 # caption = _('Sending bug report'), 398 # parent = self 399 # ) 400 # if comment.strip() == u'': 401 # comment = u'<user did not comment on bug report>' 402 # 403 # receivers = regex.findall ( 404 # '[\S]+@[\S]+', 405 # self._TCTRL_helpdesk.GetValue().strip(), 406 # flags = regex.UNICODE | regex.LOCALE 407 # ) 408 # if len(receivers) == 0: 409 # if _is_public_database: 410 # receivers = [u'gnumed-bugs@gnu.org'] 411 # 412 # receiver_string = wx.GetTextFromUser ( 413 # message = _( 414 # 'Edit the list of email addresses to send the\n' 415 # 'bug report to (separate addresses by spaces).\n' 416 # '\n' 417 # 'Note that <gnumed-bugs@gnu.org> refers to\n' 418 # 'the public (!) GNUmed bugs mailing list.' 419 # ), 420 # caption = _('Sending bug report'), 421 # default_value = ','.join(receivers), 422 # parent = self 423 # ) 424 # if receiver_string.strip() == u'': 425 # evt.Skip() 426 # return 427 # 428 # receivers = regex.findall ( 429 # '[\S]+@[\S]+', 430 # receiver_string, 431 # flags = regex.UNICODE | regex.LOCALE 432 # ) 433 # 434 # dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 435 # self, 436 # -1, 437 # caption = _('Sending bug report'), 438 # question = _( 439 # 'Your bug report will be sent to:\n' 440 # '\n' 441 # '%s\n' 442 # '\n' 443 # 'Make sure you have reviewed the log file for potentially\n' 444 # 'sensitive information before sending out the bug report.\n' 445 # '\n' 446 # 'Note that emailing the report may take a while depending\n' 447 # 'on the speed of your internet connection.\n' 448 # ) % u'\n'.join(receivers), 449 # button_defs = [ 450 # {'label': _('Send report'), 'tooltip': _('Yes, send the bug report.')}, 451 # {'label': _('Cancel'), 'tooltip': _('No, do not send the bug report.')} 452 # ], 453 # show_checkbox = True, 454 # checkbox_msg = _('include log file in bug report') 455 # ) 456 # dlg._CHBOX_dont_ask_again.SetValue(_is_public_database) 457 # go_ahead = dlg.ShowModal() 458 # if go_ahead == wx.ID_NO: 459 # dlg.Destroy() 460 # evt.Skip() 461 # return 462 # 463 # include_log = dlg._CHBOX_dont_ask_again.GetValue() 464 # if not _is_public_database: 465 # if include_log: 466 # result = gmGuiHelpers.gm_show_question ( 467 # _( 468 # 'The database you are connected to is marked as\n' 469 # '"in-production with controlled access".\n' 470 # '\n' 471 # 'You indicated that you want to include the log\n' 472 # 'file in your bug report. While this is often\n' 473 # 'useful for debugging the log file might contain\n' 474 # 'bits of patient data which must not be sent out\n' 475 # 'without de-identification.\n' 476 # '\n' 477 # 'Please confirm that you want to include the log !' 478 # ), 479 # _('Sending bug report') 480 # ) 481 # include_log = (result is True) 482 # 483 # sender_email = gmTools.coalesce(self._TCTRL_sender.GetValue(), _('<not supplied>')) 484 # msg = u"""\ 485 #Report sent via GNUmed's handler for unexpected exceptions. 486 # 487 #user comment : %s 488 # 489 #client version: %s 490 # 491 #system account: %s 492 #staff member : %s 493 #sender email : %s 494 # 495 # # enable Launchpad bug tracking 496 # affects gnumed 497 # tag automatic-report 498 # importance medium 499 # 500 #""" % (comment, _client_version, _local_account, _staff_name, sender_email) 501 # if include_log: 502 # _log2.error(comment) 503 # _log2.warning('syncing log file for emailing') 504 # gmLog2.flush() 505 # attachments = [ [_logfile_name, 'text/plain', 'quoted-printable'] ] 506 # else: 507 # attachments = None 508 # 509 # dlg.Destroy() 510 # 511 # wx.BeginBusyCursor() 512 # try: 513 # gmTools.send_mail ( 514 # sender = '%s <%s>' % (_staff_name, gmTools.default_mail_sender), 515 # receiver = receivers, 516 # subject = u'<bug>: %s' % comment, 517 # message = msg, 518 # encoding = gmI18N.get_encoding(), 519 # server = gmTools.default_mail_server, 520 # auth = {'user': gmTools.default_mail_sender, 'password': u'gnumed-at-gmx-net'}, 521 # attachments = attachments 522 # ) 523 # gmDispatcher.send(signal='statustext', msg = _('Bug report has been emailed.')) 524 # except: 525 # _log2.exception('cannot send bug report') 526 # gmDispatcher.send(signal='statustext', msg = _('Bug report COULD NOT be emailed.')) 527 # wx.EndBusyCursor() 528 # 529 # evt.Skip() 530 #------------------------------------------
531 - def _on_view_log_button_pressed(self, evt):
532 from Gnumed.pycommon import gmMimeLib 533 gmLog2.flush() 534 gmMimeLib.call_viewer_on_file(_logfile_name, block = False) 535 evt.Skip()
536 # ======================================================================== 537