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

Source Code for Module Gnumed.wxpython.gmExceptionHandlingWidgets

  1  """GNUmed exception handling widgets.""" 
  2  # ======================================================================== 
  3  # $Source: /cvsroot/gnumed/gnumed/gnumed/client/wxpython/gmExceptionHandlingWidgets.py,v $ 
  4  # $Id: gmExceptionHandlingWidgets.py,v 1.17 2010/01/31 18:15:55 ncq Exp $ 
  5  __version__ = "$Revision: 1.17 $" 
  6  __author__  = "K. Hilbert <Karsten.Hilbert@gmx.net>" 
  7  __license__ = "GPL (details at http://www.gnu.org)" 
  8   
  9  import logging, exceptions, traceback, re as regex, sys, os, shutil, datetime as pyDT, codecs 
 10   
 11   
 12  import wx 
 13   
 14   
 15  from Gnumed.business import gmSurgery 
 16  from Gnumed.pycommon import gmDispatcher, gmTools, gmCfg2, gmI18N, gmLog2 
 17  from Gnumed.wxpython import gmGuiHelpers 
 18  from Gnumed.wxGladeWidgets import wxgUnhandledExceptionDlg 
 19   
 20   
 21  _log2 = logging.getLogger('gm.gui') 
 22  _log2.info(__version__) 
 23   
 24  _prev_excepthook = None 
 25  application_is_closing = False 
 26  #========================================================================= 
27 -def set_client_version(version):
28 global _client_version 29 _client_version = version
30 #-------------------------------------------------------------------------
31 -def set_sender_email(email):
32 global _sender_email 33 _sender_email = email
34 #-------------------------------------------------------------------------
35 -def set_helpdesk(helpdesk):
36 global _helpdesk 37 _helpdesk = helpdesk
38 #-------------------------------------------------------------------------
39 -def set_staff_name(staff_name):
40 global _staff_name 41 _staff_name = staff_name
42 #-------------------------------------------------------------------------
43 -def set_is_public_database(value):
44 global _is_public_database 45 _is_public_database = value
46 #-------------------------------------------------------------------------
47 -def handle_uncaught_exception_wx(t, v, tb):
48 49 _log2.debug('unhandled exception caught:', exc_info = (t, v, tb)) 50 51 # Strg-C ? 52 if t == KeyboardInterrupt: 53 print "<Ctrl-C>: Shutting down ..." 54 top_win = wx.GetApp().GetTopWindow() 55 wx.CallAfter(top_win.Close) 56 return 57 58 # careful: MSW does reference counting on Begin/End* :-( 59 try: wx.EndBusyCursor() 60 except: pass 61 62 # exception on shutdown ? 63 if application_is_closing: 64 # dead object error ? 65 if t == wx._core.PyDeadObjectError: 66 return 67 gmLog2.log_stack_trace() 68 return 69 70 # try to ignore those, they come about from async handling 71 if t == wx._core.PyDeadObjectError: 72 _log.warning('continuing and hoping for the best') 73 return 74 75 # failed import ? 76 if t == exceptions.ImportError: 77 gmGuiHelpers.gm_show_error ( 78 aTitle = _('Missing GNUmed module'), 79 aMessage = _( 80 'GNUmed detected that parts of it are not\n' 81 'properly installed. The following message\n' 82 'names the missing part:\n' 83 '\n' 84 ' "%s"\n' 85 '\n' 86 'Please make sure to get the missing\n' 87 'parts installed. Otherwise some of the\n' 88 'functionality will not be accessible.' 89 ) % v 90 ) 91 _log2.error('module [%s] not installed', v) 92 return 93 94 # other exceptions 95 _log2.error('enabling debug mode') 96 _cfg = gmCfg2.gmCfgData() 97 _cfg.set_option(option = 'debug', value = True) 98 root_logger = logging.getLogger() 99 root_logger.setLevel(logging.DEBUG) 100 gmLog2.log_stack_trace() 101 102 name = os.path.basename(_logfile_name) 103 name, ext = os.path.splitext(name) 104 new_name = os.path.expanduser(os.path.join ( 105 '~', 106 'gnumed', 107 'logs', 108 '%s_%s%s' % (name, pyDT.datetime.now().strftime('%Y-%m-%d_%H-%M-%S'), ext) 109 )) 110 111 dlg = cUnhandledExceptionDlg(parent = None, id = -1, exception = (t, v, tb), logfile = new_name) 112 dlg.ShowModal() 113 comment = dlg._TCTRL_comment.GetValue() 114 dlg.Destroy() 115 if (comment is not None) and (comment.strip() != u''): 116 _log2.error(u'user comment: %s', comment.strip()) 117 118 _log2.warning('syncing log file for backup to [%s]', new_name) 119 gmLog2.flush() 120 shutil.copy2(_logfile_name, new_name)
121 # ------------------------------------------------------------------------
122 -def install_wx_exception_handler():
123 124 global _logfile_name 125 _logfile_name = gmLog2._logfile_name 126 127 global _local_account 128 _local_account = os.path.basename(os.path.expanduser('~')) 129 130 set_helpdesk(gmSurgery.gmCurrentPractice().helpdesk) 131 set_staff_name(_local_account) 132 set_is_public_database(False) 133 set_sender_email(None) 134 set_client_version('gmExceptionHandlingWidgets.py %s' % __version__) 135 136 gmDispatcher.connect(signal = 'application_closing', receiver = _on_application_closing) 137 138 global _prev_excepthook 139 _prev_excepthook = sys.excepthook 140 sys.excepthook = handle_uncaught_exception_wx 141 142 return True
143 # ------------------------------------------------------------------------
144 -def uninstall_wx_exception_handler():
145 if _prev_excepthook is None: 146 sys.excepthook = sys.__excepthook__ 147 return True 148 sys.excepthook = _prev_excepthook 149 return True
150 # ------------------------------------------------------------------------
151 -def _on_application_closing():
152 global application_is_closing 153 # used to ignore a few exceptions, such as when the 154 # C++ object has been destroyed before the Python one 155 application_is_closing = True
156 # ========================================================================
157 -class cUnhandledExceptionDlg(wxgUnhandledExceptionDlg.wxgUnhandledExceptionDlg):
158
159 - def __init__(self, *args, **kwargs):
160 161 exception = kwargs['exception'] 162 del kwargs['exception'] 163 self.logfile = kwargs['logfile'] 164 del kwargs['logfile'] 165 166 wxgUnhandledExceptionDlg.wxgUnhandledExceptionDlg.__init__(self, *args, **kwargs) 167 168 if _sender_email is not None: 169 self._TCTRL_sender.SetValue(_sender_email) 170 self._TCTRL_helpdesk.SetValue(_helpdesk) 171 self._TCTRL_logfile.SetValue(self.logfile) 172 t, v, tb = exception 173 self._TCTRL_exc_type.SetValue(str(t)) 174 self._TCTRL_exc_value.SetValue(str(v)) 175 self._TCTRL_traceback.SetValue(''.join(traceback.format_tb(tb))) 176 177 self.Fit()
178 #------------------------------------------
179 - def _on_close_gnumed_button_pressed(self, evt):
180 comment = self._TCTRL_comment.GetValue() 181 if (comment is not None) and (comment.strip() != u''): 182 _log2.error(u'user comment: %s', comment.strip()) 183 _log2.warning('syncing log file for backup to [%s]', self.logfile) 184 gmLog2.flush() 185 shutil.copy2(_logfile_name, self.logfile) 186 top_win = wx.GetApp().GetTopWindow() 187 wx.CallAfter(top_win.Close) 188 evt.Skip()
189 #------------------------------------------
190 - def _on_mail_button_pressed(self, evt):
191 192 comment = self._TCTRL_comment.GetValue() 193 if (comment is None) or (comment.strip() == u''): 194 comment = wx.GetTextFromUser ( 195 message = _( 196 'Please enter a short note on what you\n' 197 'were about to do in GNUmed:' 198 ), 199 caption = _('Sending bug report'), 200 parent = self 201 ) 202 if comment.strip() == u'': 203 comment = u'<user did not comment on bug report>' 204 205 receivers = regex.findall ( 206 '[\S]+@[\S]+', 207 self._TCTRL_helpdesk.GetValue().strip(), 208 flags = regex.UNICODE | regex.LOCALE 209 ) 210 if len(receivers) == 0: 211 if _is_public_database: 212 receivers = [u'gnumed-bugs@gnu.org'] 213 214 receiver_string = wx.GetTextFromUser ( 215 message = _( 216 'Edit the list of email addresses to send the\n' 217 'bug report to (separate addresses by spaces).\n' 218 '\n' 219 'Note that <gnumed-bugs@gnu.org> refers to\n' 220 'the public (!) GNUmed bugs mailing list.' 221 ), 222 caption = _('Sending bug report'), 223 default_value = ','.join(receivers), 224 parent = self 225 ) 226 if receiver_string.strip() == u'': 227 evt.Skip() 228 return 229 230 receivers = regex.findall ( 231 '[\S]+@[\S]+', 232 receiver_string, 233 flags = regex.UNICODE | regex.LOCALE 234 ) 235 236 dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 237 self, 238 -1, 239 caption = _('Sending bug report'), 240 question = _( 241 'Your bug report will be sent to:\n' 242 '\n' 243 '%s\n' 244 '\n' 245 'Make sure you have reviewed the log file for potentially\n' 246 'sensitive information before sending out the bug report.\n' 247 '\n' 248 'Note that emailing the report may take a while depending\n' 249 'on the speed of your internet connection.\n' 250 ) % u'\n'.join(receivers), 251 button_defs = [ 252 {'label': _('Send report'), 'tooltip': _('Yes, send the bug report.')}, 253 {'label': _('Cancel'), 'tooltip': _('No, do not send the bug report.')} 254 ], 255 show_checkbox = True, 256 checkbox_msg = _('include log file in bug report') 257 ) 258 dlg._CHBOX_dont_ask_again.SetValue(_is_public_database) 259 go_ahead = dlg.ShowModal() 260 if go_ahead == wx.ID_NO: 261 dlg.Destroy() 262 evt.Skip() 263 return 264 265 include_log = dlg._CHBOX_dont_ask_again.GetValue() 266 if not _is_public_database: 267 if include_log: 268 result = gmGuiHelpers.gm_show_question ( 269 _( 270 'The database you are connected to is marked as\n' 271 '"in-production with controlled access".\n' 272 '\n' 273 'You indicated that you want to include the log\n' 274 'file in your bug report. While this is often\n' 275 'useful for debugging the log file might contain\n' 276 'bits of patient data which must not be sent out\n' 277 'without de-identification.\n' 278 '\n' 279 'Please confirm that you want to include the log !' 280 ), 281 _('Sending bug report') 282 ) 283 include_log = (result is True) 284 285 sender_email = gmTools.coalesce(self._TCTRL_sender.GetValue(), _('<not supplied>')) 286 msg = u"""\ 287 Report sent via GNUmed's handler for unexpected exceptions. 288 289 user comment : %s 290 291 client version: %s 292 293 system account: %s 294 staff member : %s 295 sender email : %s 296 297 # enable Launchpad bug tracking 298 affects gnumed 299 tag automatic-report 300 importance medium 301 302 """ % (comment, _client_version, _local_account, _staff_name, sender_email) 303 if include_log: 304 _log2.error(comment) 305 _log2.warning('syncing log file for emailing') 306 gmLog2.flush() 307 attachments = [ [_logfile_name, 'text/plain', 'quoted-printable'] ] 308 else: 309 attachments = None 310 311 dlg.Destroy() 312 313 wx.BeginBusyCursor() 314 try: 315 gmTools.send_mail ( 316 sender = '%s <%s>' % (_staff_name, gmTools.default_mail_sender), 317 receiver = receivers, 318 subject = u'<bug>: %s' % comment, 319 message = msg, 320 encoding = gmI18N.get_encoding(), 321 server = gmTools.default_mail_server, 322 auth = {'user': gmTools.default_mail_sender, 'password': u'gnumed-at-gmx-net'}, 323 attachments = attachments 324 ) 325 gmDispatcher.send(signal='statustext', msg = _('Bug report has been emailed.')) 326 except: 327 _log2.exception('cannot send bug report') 328 gmDispatcher.send(signal='statustext', msg = _('Bug report COULD NOT be emailed.')) 329 wx.EndBusyCursor() 330 331 evt.Skip()
332 #------------------------------------------
333 - def _on_view_log_button_pressed(self, evt):
334 from Gnumed.pycommon import gmMimeLib 335 gmLog2.flush() 336 gmMimeLib.call_viewer_on_file(_logfile_name, block = False) 337 evt.Skip()
338 # ======================================================================== 339 # $Log: gmExceptionHandlingWidgets.py,v $ 340 # Revision 1.17 2010/01/31 18:15:55 ncq 341 # - ignore one more PyDeadObjectError 342 # 343 # Revision 1.16 2009/12/21 15:06:05 ncq 344 # - better layout 345 # 346 # Revision 1.15 2009/07/30 12:04:06 ncq 347 # - better handle Ctrl-C 348 # 349 # Revision 1.14 2009/05/22 11:01:23 ncq 350 # - better catch exceptions on mailing log 351 # 352 # Revision 1.13 2009/05/08 07:59:55 ncq 353 # - improved default version 354 # 355 # Revision 1.12 2009/04/19 22:27:00 ncq 356 # - cleanup 357 # 358 # Revision 1.11 2009/04/03 12:30:16 ncq 359 # - attach log rather than include 360 # 361 # Revision 1.10 2009/02/24 10:13:02 ncq 362 # - -devel -> -bugs 363 # 364 # Revision 1.9 2009/02/20 15:43:05 ncq 365 # - typo fix 366 # 367 # Revision 1.8 2009/02/05 14:29:27 ncq 368 # - improved report mail reporting 369 # 370 # Revision 1.7 2008/12/25 23:31:51 ncq 371 # - ignore but log most exceptions during application shutdown 372 # 373 # Revision 1.6 2008/12/09 23:29:54 ncq 374 # - trap exceptions during smtp handling inside top-level exception handler 375 # 376 # Revision 1.5 2008/11/20 19:50:45 ncq 377 # - improved wording 378 # 379 # Revision 1.4 2008/10/12 16:17:57 ncq 380 # - include client version at top of bug email 381 # - improved launchpad tracking tags 382 # 383 # Revision 1.3 2008/07/28 20:26:49 ncq 384 # - fixed include_log logic 385 # 386 # Revision 1.2 2008/07/16 11:10:46 ncq 387 # - set_sender_email and use it 388 # - some cleanup and better docs 389 # 390 # Revision 1.1 2008/05/13 12:32:54 ncq 391 # - factor out exception handling widgets 392 # 393 # 394