1 """GNUmed exception handling widgets."""
2
3 __author__ = "K. Hilbert <Karsten.Hilbert@gmx.net>"
4 __license__ = "GPL v2 or later (details at http://www.gnu.org)"
5
6 import logging, exceptions, traceback, re as regex, sys, os, shutil, datetime as pyDT
7
8
9 import wx
10
11
12 from Gnumed.pycommon import gmDispatcher, gmCfg2, gmI18N, gmLog2, gmPG2
13 from Gnumed.pycommon import gmExceptions
14 from Gnumed.pycommon import gmNetworkTools
15 from Gnumed.pycommon.gmTools import u_box_horiz_single
16
17 from Gnumed.business import gmSurgery
18
19 from Gnumed.wxpython import gmGuiHelpers
20
21
22 _log2 = logging.getLogger('gm.gui')
23
24 _prev_excepthook = None
25 application_is_closing = False
26
28 global _client_version
29 _client_version = version
30
32 global _sender_email
33 _sender_email = email
34
36 global _helpdesk
37 _helpdesk = helpdesk
38
40 global _staff_name
41 _staff_name = staff_name
42
44 global _is_public_database
45 _is_public_database = value
46
47
48
50
51 if t != wx._core.PyDeadObjectError:
52 return False
53
54 wx.EndBusyCursor()
55
56
57
58 _log2.warning('continuing and hoping for the best')
59 return True
60
72
74
75 if t != exceptions.ImportError:
76 return False
77
78 wx.EndBusyCursor()
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 != gmExceptions.AccessDenied:
110 return False
111
112 _log2.error('access permissions violation detected')
113 wx.EndBusyCursor()
114 gmLog2.flush()
115 txt = u' ' + v.errmsg
116 if v.source is not None:
117 txt += _('\n Source: %s') % v.source
118 if v.code is not None:
119 txt += _('\n Code: %s') % v.code
120 if v.details is not None:
121 txt += _('\n%s\n Details (250 chars max):\n%s\n%s') % (
122 u_box_horiz_single * 50,
123 v.details[:250],
124 u_box_horiz_single * 50
125 )
126 gmGuiHelpers.gm_show_error (
127 aTitle = _('Access violation'),
128 aMessage = _(
129 'You do not have access to this part of GNUmed.\n'
130 '\n'
131 '%s'
132 ) % txt
133 )
134 return True
135
137
138 if t not in [gmPG2.dbapi.OperationalError, gmPG2.dbapi.InterfaceError]:
139 return False
140
141 try:
142 msg = gmPG2.extract_msg_from_pg_exception(exc = v)
143 except:
144 msg = u'cannot extract message from PostgreSQL exception'
145 print msg
146 print v
147 return False
148
149 conn_lost = False
150
151 if t == gmPG2.dbapi.OperationalError:
152 conn_lost = (
153 ('erver' in msg)
154 and
155 (
156 ('term' in msg)
157 or
158 ('abnorm' in msg)
159 or
160 ('end' in msg)
161 or
162 ('oute' in msg)
163 )
164 )
165
166 if t == gmPG2.dbapi.InterfaceError:
167 conn_lost = (
168 ('onnect' in msg)
169 and
170 (('close' in msg) or ('end' in msg))
171 )
172
173 if not conn_lost:
174 return False
175
176 _log2.error('lost connection')
177 gmLog2.log_stack_trace()
178 wx.EndBusyCursor()
179 gmLog2.flush()
180 gmGuiHelpers.gm_show_error (
181 aTitle = _('Lost connection'),
182 aMessage = _(
183 'Since you were last working in GNUmed,\n'
184 'your database connection timed out.\n'
185 '\n'
186 'This GNUmed session is now expired.\n'
187 '\n'
188 'You will have to close this client and\n'
189 'restart a new GNUmed session.'
190 )
191 )
192 return True
193
195
196 _log2.debug('unhandled exception caught:', exc_info = (t, v, tb))
197
198 if __handle_ctrl_c(t, v, tb):
199 return
200
201 if __handle_exceptions_on_shutdown(t, v, tb):
202 return
203
204 if __ignore_dead_objects_from_async(t, v, tb):
205 return
206
207 if __handle_import_error(t, v, tb):
208 return
209
210 if __handle_access_violation(t, v, tb):
211 return
212
213
214 _cfg = gmCfg2.gmCfgData()
215 if _cfg.get(option = 'debug') is False:
216 _log2.error('enabling debug mode')
217 _cfg.set_option(option = 'debug', value = True)
218 root_logger = logging.getLogger()
219 root_logger.setLevel(logging.DEBUG)
220 _log2.debug('unhandled exception caught:', exc_info = (t, v, tb))
221
222 if __handle_lost_db_connection(t, v, tb):
223 return
224
225 gmLog2.log_stack_trace()
226
227
228
229
230 wx.EndBusyCursor()
231
232 name = os.path.basename(_logfile_name)
233 name, ext = os.path.splitext(name)
234 new_name = os.path.expanduser(os.path.join (
235 '~',
236 'gnumed',
237 'logs',
238 '%s_%s%s' % (name, pyDT.datetime.now().strftime('%Y-%m-%d_%H-%M-%S'), ext)
239 ))
240
241 dlg = cUnhandledExceptionDlg(parent = None, id = -1, exception = (t, v, tb), logfile = new_name)
242 dlg.ShowModal()
243 comment = dlg._TCTRL_comment.GetValue()
244 dlg.Destroy()
245 if (comment is not None) and (comment.strip() != u''):
246 _log2.error(u'user comment: %s', comment.strip())
247
248 _log2.warning('syncing log file for backup to [%s]', new_name)
249 gmLog2.flush()
250 shutil.copy2(_logfile_name, new_name)
251
273
280
286
287 -def mail_log(parent=None, comment=None, helpdesk=None, sender=None):
288
289 if (comment is None) or (comment.strip() == u''):
290 comment = wx.GetTextFromUser (
291 message = _(
292 'Please enter a short note on what you\n'
293 'were about to do in GNUmed:'
294 ),
295 caption = _('Sending bug report'),
296 parent = parent
297 )
298 if comment.strip() == u'':
299 comment = u'<user did not comment on bug report>'
300
301 receivers = []
302 if helpdesk is not None:
303 receivers = regex.findall (
304 '[\S]+@[\S]+',
305 helpdesk.strip(),
306 flags = regex.UNICODE | regex.LOCALE
307 )
308 if len(receivers) == 0:
309 if _is_public_database:
310 receivers = [u'gnumed-bugs@gnu.org']
311
312 receiver_string = wx.GetTextFromUser (
313 message = _(
314 'Edit the list of email addresses to send the\n'
315 'bug report to (separate addresses by spaces).\n'
316 '\n'
317 'Note that <gnumed-bugs@gnu.org> refers to\n'
318 'the public (!) GNUmed bugs mailing list.'
319 ),
320 caption = _('Sending bug report'),
321 default_value = ','.join(receivers),
322 parent = parent
323 )
324 if receiver_string.strip() == u'':
325 return
326
327 receivers = regex.findall (
328 '[\S]+@[\S]+',
329 receiver_string,
330 flags = regex.UNICODE | regex.LOCALE
331 )
332
333 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
334 parent,
335 -1,
336 caption = _('Sending bug report'),
337 question = _(
338 'Your bug report will be sent to:\n'
339 '\n'
340 '%s\n'
341 '\n'
342 'Make sure you have reviewed the log file for potentially\n'
343 'sensitive information before sending out the bug report.\n'
344 '\n'
345 'Note that emailing the report may take a while depending\n'
346 'on the speed of your internet connection.\n'
347 ) % u'\n'.join(receivers),
348 button_defs = [
349 {'label': _('Send report'), 'tooltip': _('Yes, send the bug report.')},
350 {'label': _('Cancel'), 'tooltip': _('No, do not send the bug report.')}
351 ],
352 show_checkbox = True,
353 checkbox_msg = _('include log file in bug report')
354 )
355 dlg._CHBOX_dont_ask_again.SetValue(_is_public_database)
356 go_ahead = dlg.ShowModal()
357 if go_ahead == wx.ID_NO:
358 dlg.Destroy()
359 return
360
361 include_log = dlg._CHBOX_dont_ask_again.GetValue()
362 if not _is_public_database:
363 if include_log:
364 result = gmGuiHelpers.gm_show_question (
365 _(
366 'The database you are connected to is marked as\n'
367 '"in-production with controlled access".\n'
368 '\n'
369 'You indicated that you want to include the log\n'
370 'file in your bug report. While this is often\n'
371 'useful for debugging the log file might contain\n'
372 'bits of patient data which must not be sent out\n'
373 'without de-identification.\n'
374 '\n'
375 'Please confirm that you want to include the log !'
376 ),
377 _('Sending bug report')
378 )
379 include_log = (result is True)
380
381 if sender is None:
382 sender = _('<not supplied>')
383 else:
384 if sender.strip() == u'':
385 sender = _('<not supplied>')
386
387 msg = u"""\
388 Report sent via GNUmed's handler for unexpected exceptions.
389
390 user comment : %s
391
392 client version: %s
393
394 system account: %s
395 staff member : %s
396 sender email : %s
397
398 # enable Launchpad bug tracking
399 affects gnumed
400 tag automatic-report
401 importance medium
402
403 """ % (comment, _client_version, _local_account, _staff_name, sender)
404 if include_log:
405 _log2.error(comment)
406 _log2.warning('syncing log file for emailing')
407 gmLog2.flush()
408 attachments = [ [_logfile_name, 'text/plain', 'quoted-printable'] ]
409 else:
410 attachments = None
411
412 dlg.Destroy()
413
414 wx.BeginBusyCursor()
415 try:
416 gmNetworkTools.send_mail (
417 sender = '%s <%s>' % (_staff_name, gmNetworkTools.default_mail_sender),
418 receiver = receivers,
419 subject = u'<bug>: %s' % comment,
420 message = msg,
421 encoding = gmI18N.get_encoding(),
422 server = gmNetworkTools.default_mail_server,
423 auth = {'user': gmNetworkTools.default_mail_sender, 'password': u'gnumed-at-gmx-net'},
424 attachments = attachments
425 )
426 gmDispatcher.send(signal='statustext', msg = _('Bug report has been emailed.'))
427 except:
428 _log2.exception('cannot send bug report')
429 gmDispatcher.send(signal='statustext', msg = _('Bug report COULD NOT be emailed.'))
430 wx.EndBusyCursor()
431
432
433 from Gnumed.wxGladeWidgets import wxgUnhandledExceptionDlg
434
436
438
439 exception = kwargs['exception']
440 del kwargs['exception']
441 self.logfile = kwargs['logfile']
442 del kwargs['logfile']
443
444 wxgUnhandledExceptionDlg.wxgUnhandledExceptionDlg.__init__(self, *args, **kwargs)
445
446 if _sender_email is not None:
447 self._TCTRL_sender.SetValue(_sender_email)
448 self._TCTRL_helpdesk.SetValue(_helpdesk)
449 self._TCTRL_logfile.SetValue(self.logfile)
450 t, v, tb = exception
451 self._TCTRL_exc_type.SetValue(str(t))
452 self._TCTRL_exc_value.SetValue(str(v))
453 self._TCTRL_traceback.SetValue(''.join(traceback.format_tb(tb)))
454
455 self.Fit()
456
467
478
484
485