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
25 global _client_version
26 _client_version = version
27
29 global _sender_email
30 _sender_email = email
31
33 global _helpdesk
34 _helpdesk = helpdesk
35
37 global _staff_name
38 _staff_name = staff_name
39
41 global _is_public_database
42 _is_public_database = value
43
45
46 _log2.debug('unhandled exception caught:', exc_info = (t, v, tb))
47
48
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
56 try: wx.EndBusyCursor()
57 except: pass
58
59
60 if application_is_closing:
61
62 if t == wx._core.PyDeadObjectError:
63 return
64 gmLog2.log_stack_trace()
65 return
66
67
68
69 if t == wx._core.PyDeadObjectError:
70 _log2.warning('continuing and hoping for the best')
71 return
72
73
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
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
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
184
191
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
347
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
378
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
536
537