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

Source Code for Module Gnumed.wxpython.gmExceptionHandlingWidgets

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