1 """GNUmed exception handling widgets."""
2
3 __version__ = "$Revision: 1.17 $"
4 __author__ = "K. Hilbert <Karsten.Hilbert@gmx.net>"
5 __license__ = "GPL v2 or later (details at http://www.gnu.org)"
6
7 import logging, exceptions, traceback, re as regex, sys, os, shutil, datetime as pyDT
8
9
10 import wx
11
12
13 from Gnumed.business import gmSurgery
14 from Gnumed.pycommon import gmDispatcher, gmCfg2, gmI18N, gmLog2, gmPG2
15 from Gnumed.pycommon import gmNetworkTools
16 from Gnumed.wxpython import gmGuiHelpers
17
18
19 _log2 = logging.getLogger('gm.gui')
20 _log2.info(__version__)
21
22 _prev_excepthook = None
23 application_is_closing = False
24
26 global _client_version
27 _client_version = version
28
30 global _sender_email
31 _sender_email = email
32
34 global _helpdesk
35 _helpdesk = helpdesk
36
38 global _staff_name
39 _staff_name = staff_name
40
42 global _is_public_database
43 _is_public_database = value
44
45
46
48
49 if t != wx._core.PyDeadObjectError:
50 return False
51
52 try: wx.EndBusyCursor()
53 except: pass
54
55
56
57 _log2.warning('continuing and hoping for the best')
58 return True
59
71
73
74 if t != exceptions.ImportError:
75 return False
76
77 try: wx.EndBusyCursor()
78 except: pass
79
80 _log2.error('module [%s] not installed', v)
81 gmGuiHelpers.gm_show_error (
82 aTitle = _('Missing GNUmed module'),
83 aMessage = _(
84 'GNUmed detected that parts of it are not\n'
85 'properly installed. The following message\n'
86 'names the missing part:\n'
87 '\n'
88 ' "%s"\n'
89 '\n'
90 'Please make sure to get the missing\n'
91 'parts installed. Otherwise some of the\n'
92 'functionality will not be accessible.'
93 ) % v
94 )
95 return True
96
98
99 if t != KeyboardInterrupt:
100 return False
101
102 print "<Ctrl-C>: Shutting down ..."
103 top_win = wx.GetApp().GetTopWindow()
104 wx.CallAfter(top_win.Close)
105 return True
106
108
109 if t not in [gmPG2.dbapi.OperationalError, gmPG2.dbapi.InterfaceError]:
110 return False
111
112 try:
113 msg = gmPG2.extract_msg_from_pg_exception(exc = v)
114 except:
115 msg = u'cannot extract message from PostgreSQL exception'
116 print msg
117 print v
118 return False
119
120 conn_lost = False
121
122 if t == gmPG2.dbapi.OperationalError:
123 conn_lost = (
124 ('erver' in msg)
125 and
126 (
127 ('term' in msg)
128 or
129 ('abnorm' in msg)
130 or
131 ('end' in msg)
132 or
133 ('oute' in msg)
134 )
135 )
136
137 if t == gmPG2.dbapi.InterfaceError:
138 conn_lost = (
139 ('onnect' in msg)
140 and
141 (('close' in msg) or ('end' in msg))
142 )
143
144 if not conn_lost:
145 return False
146
147 _log2.error('lost connection')
148 gmLog2.log_stack_trace()
149 try: wx.EndBusyCursor()
150 except: pass
151 gmLog2.flush()
152 gmGuiHelpers.gm_show_error (
153 aTitle = _('Lost connection'),
154 aMessage = _(
155 'Since you were last working in GNUmed,\n'
156 'your database connection timed out.\n'
157 '\n'
158 'This GNUmed session is now expired.\n'
159 '\n'
160 'You will have to close this client and\n'
161 'restart a new GNUmed session.'
162 )
163 )
164 return True
165
167
168 _log2.debug('unhandled exception caught:', exc_info = (t, v, tb))
169
170 if __handle_ctrl_c(t, v, tb):
171 return
172
173 if __handle_exceptions_on_shutdown(t, v, tb):
174 return
175
176 if __ignore_dead_objects_from_async(t, v, tb):
177 return
178
179 if __handle_import_error(t, v, tb):
180 return
181
182
183 _cfg = gmCfg2.gmCfgData()
184 if _cfg.get(option = 'debug') is False:
185 _log2.error('enabling debug mode')
186 _cfg.set_option(option = 'debug', value = True)
187 root_logger = logging.getLogger()
188 root_logger.setLevel(logging.DEBUG)
189 _log2.debug('unhandled exception caught:', exc_info = (t, v, tb))
190
191 if __handle_lost_db_connection(t, v, tb):
192 return
193
194 gmLog2.log_stack_trace()
195
196
197
198
199 try: wx.EndBusyCursor()
200 except: pass
201
202 name = os.path.basename(_logfile_name)
203 name, ext = os.path.splitext(name)
204 new_name = os.path.expanduser(os.path.join (
205 '~',
206 'gnumed',
207 'logs',
208 '%s_%s%s' % (name, pyDT.datetime.now().strftime('%Y-%m-%d_%H-%M-%S'), ext)
209 ))
210
211 dlg = cUnhandledExceptionDlg(parent = None, id = -1, exception = (t, v, tb), logfile = new_name)
212 dlg.ShowModal()
213 comment = dlg._TCTRL_comment.GetValue()
214 dlg.Destroy()
215 if (comment is not None) and (comment.strip() != u''):
216 _log2.error(u'user comment: %s', comment.strip())
217
218 _log2.warning('syncing log file for backup to [%s]', new_name)
219 gmLog2.flush()
220 shutil.copy2(_logfile_name, new_name)
221
243
250
256
257 -def mail_log(parent=None, comment=None, helpdesk=None, sender=None):
258
259 if (comment is None) or (comment.strip() == u''):
260 comment = wx.GetTextFromUser (
261 message = _(
262 'Please enter a short note on what you\n'
263 'were about to do in GNUmed:'
264 ),
265 caption = _('Sending bug report'),
266 parent = parent
267 )
268 if comment.strip() == u'':
269 comment = u'<user did not comment on bug report>'
270
271 receivers = []
272 if helpdesk is not None:
273 receivers = regex.findall (
274 '[\S]+@[\S]+',
275 helpdesk.strip(),
276 flags = regex.UNICODE | regex.LOCALE
277 )
278 if len(receivers) == 0:
279 if _is_public_database:
280 receivers = [u'gnumed-bugs@gnu.org']
281
282 receiver_string = wx.GetTextFromUser (
283 message = _(
284 'Edit the list of email addresses to send the\n'
285 'bug report to (separate addresses by spaces).\n'
286 '\n'
287 'Note that <gnumed-bugs@gnu.org> refers to\n'
288 'the public (!) GNUmed bugs mailing list.'
289 ),
290 caption = _('Sending bug report'),
291 default_value = ','.join(receivers),
292 parent = parent
293 )
294 if receiver_string.strip() == u'':
295 return
296
297 receivers = regex.findall (
298 '[\S]+@[\S]+',
299 receiver_string,
300 flags = regex.UNICODE | regex.LOCALE
301 )
302
303 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
304 parent,
305 -1,
306 caption = _('Sending bug report'),
307 question = _(
308 'Your bug report will be sent to:\n'
309 '\n'
310 '%s\n'
311 '\n'
312 'Make sure you have reviewed the log file for potentially\n'
313 'sensitive information before sending out the bug report.\n'
314 '\n'
315 'Note that emailing the report may take a while depending\n'
316 'on the speed of your internet connection.\n'
317 ) % u'\n'.join(receivers),
318 button_defs = [
319 {'label': _('Send report'), 'tooltip': _('Yes, send the bug report.')},
320 {'label': _('Cancel'), 'tooltip': _('No, do not send the bug report.')}
321 ],
322 show_checkbox = True,
323 checkbox_msg = _('include log file in bug report')
324 )
325 dlg._CHBOX_dont_ask_again.SetValue(_is_public_database)
326 go_ahead = dlg.ShowModal()
327 if go_ahead == wx.ID_NO:
328 dlg.Destroy()
329 return
330
331 include_log = dlg._CHBOX_dont_ask_again.GetValue()
332 if not _is_public_database:
333 if include_log:
334 result = gmGuiHelpers.gm_show_question (
335 _(
336 'The database you are connected to is marked as\n'
337 '"in-production with controlled access".\n'
338 '\n'
339 'You indicated that you want to include the log\n'
340 'file in your bug report. While this is often\n'
341 'useful for debugging the log file might contain\n'
342 'bits of patient data which must not be sent out\n'
343 'without de-identification.\n'
344 '\n'
345 'Please confirm that you want to include the log !'
346 ),
347 _('Sending bug report')
348 )
349 include_log = (result is True)
350
351 if sender is None:
352 sender = _('<not supplied>')
353 else:
354 if sender.strip() == u'':
355 sender = _('<not supplied>')
356
357 msg = u"""\
358 Report sent via GNUmed's handler for unexpected exceptions.
359
360 user comment : %s
361
362 client version: %s
363
364 system account: %s
365 staff member : %s
366 sender email : %s
367
368 # enable Launchpad bug tracking
369 affects gnumed
370 tag automatic-report
371 importance medium
372
373 """ % (comment, _client_version, _local_account, _staff_name, sender)
374 if include_log:
375 _log2.error(comment)
376 _log2.warning('syncing log file for emailing')
377 gmLog2.flush()
378 attachments = [ [_logfile_name, 'text/plain', 'quoted-printable'] ]
379 else:
380 attachments = None
381
382 dlg.Destroy()
383
384 wx.BeginBusyCursor()
385 try:
386 gmNetworkTools.send_mail (
387 sender = '%s <%s>' % (_staff_name, gmNetworkTools.default_mail_sender),
388 receiver = receivers,
389 subject = u'<bug>: %s' % comment,
390 message = msg,
391 encoding = gmI18N.get_encoding(),
392 server = gmNetworkTools.default_mail_server,
393 auth = {'user': gmNetworkTools.default_mail_sender, 'password': u'gnumed-at-gmx-net'},
394 attachments = attachments
395 )
396 gmDispatcher.send(signal='statustext', msg = _('Bug report has been emailed.'))
397 except:
398 _log2.exception('cannot send bug report')
399 gmDispatcher.send(signal='statustext', msg = _('Bug report COULD NOT be emailed.'))
400 wx.EndBusyCursor()
401
402
403 from Gnumed.wxGladeWidgets import wxgUnhandledExceptionDlg
404
406
408
409 exception = kwargs['exception']
410 del kwargs['exception']
411 self.logfile = kwargs['logfile']
412 del kwargs['logfile']
413
414 wxgUnhandledExceptionDlg.wxgUnhandledExceptionDlg.__init__(self, *args, **kwargs)
415
416 if _sender_email is not None:
417 self._TCTRL_sender.SetValue(_sender_email)
418 self._TCTRL_helpdesk.SetValue(_helpdesk)
419 self._TCTRL_logfile.SetValue(self.logfile)
420 t, v, tb = exception
421 self._TCTRL_exc_type.SetValue(str(t))
422 self._TCTRL_exc_value.SetValue(str(v))
423 self._TCTRL_traceback.SetValue(''.join(traceback.format_tb(tb)))
424
425 self.Fit()
426
437
448
454
455