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