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 gmPraxis
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 Details (first 250 characters):\n%s\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 'error_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
251 shutil.copy2(_logfile_name, new_name)
252
274
281
287
288 -def mail_log(parent=None, comment=None, helpdesk=None, sender=None):
289
290 if (comment is None) or (comment.strip() == u''):
291 comment = wx.GetTextFromUser (
292 message = _(
293 'Please enter a short note on what you\n'
294 'were about to do in GNUmed:'
295 ),
296 caption = _('Sending bug report'),
297 parent = parent
298 )
299 if comment.strip() == u'':
300 comment = u'<user did not comment on bug report>'
301
302 receivers = []
303 if helpdesk is not None:
304 receivers = regex.findall (
305 '[\S]+@[\S]+',
306 helpdesk.strip(),
307 flags = regex.UNICODE | regex.LOCALE
308 )
309 if len(receivers) == 0:
310 if _is_public_database:
311 receivers = [u'gnumed-bugs@gnu.org']
312
313 receiver_string = wx.GetTextFromUser (
314 message = _(
315 'Edit the list of email addresses to send the\n'
316 'bug report to (separate addresses by spaces).\n'
317 '\n'
318 'Note that <gnumed-bugs@gnu.org> refers to\n'
319 'the public (!) GNUmed bugs mailing list.'
320 ),
321 caption = _('Sending bug report'),
322 default_value = ','.join(receivers),
323 parent = parent
324 )
325 if receiver_string.strip() == u'':
326 return
327
328 receivers = regex.findall (
329 '[\S]+@[\S]+',
330 receiver_string,
331 flags = regex.UNICODE | regex.LOCALE
332 )
333
334 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
335 parent,
336 -1,
337 caption = _('Sending bug report'),
338 question = _(
339 'Your bug report will be sent to:\n'
340 '\n'
341 '%s\n'
342 '\n'
343 'Make sure you have reviewed the log file for potentially\n'
344 'sensitive information before sending out the bug report.\n'
345 '\n'
346 'Note that emailing the report may take a while depending\n'
347 'on the speed of your internet connection.\n'
348 ) % u'\n'.join(receivers),
349 button_defs = [
350 {'label': _('Send report'), 'tooltip': _('Yes, send the bug report.')},
351 {'label': _('Cancel'), 'tooltip': _('No, do not send the bug report.')}
352 ],
353 show_checkbox = True,
354 checkbox_msg = _('include log file in bug report')
355 )
356 dlg._CHBOX_dont_ask_again.SetValue(_is_public_database)
357 go_ahead = dlg.ShowModal()
358 if go_ahead == wx.ID_NO:
359 dlg.Destroy()
360 return
361
362 include_log = dlg._CHBOX_dont_ask_again.GetValue()
363 if not _is_public_database:
364 if include_log:
365 result = gmGuiHelpers.gm_show_question (
366 _(
367 'The database you are connected to is marked as\n'
368 '"in-production with controlled access".\n'
369 '\n'
370 'You indicated that you want to include the log\n'
371 'file in your bug report. While this is often\n'
372 'useful for debugging the log file might contain\n'
373 'bits of patient data which must not be sent out\n'
374 'without de-identification.\n'
375 '\n'
376 'Please confirm that you want to include the log !'
377 ),
378 _('Sending bug report')
379 )
380 include_log = (result is True)
381
382 if sender is None:
383 sender = _('<not supplied>')
384 else:
385 if sender.strip() == u'':
386 sender = _('<not supplied>')
387
388 msg = u"""\
389 Report sent via GNUmed's handler for unexpected exceptions.
390
391 user comment : %s
392
393 client version: %s
394
395 system account: %s
396 staff member : %s
397 sender email : %s
398
399 # enable Launchpad bug tracking
400 affects gnumed
401 tag automatic-report
402 importance medium
403
404 """ % (comment, _client_version, _local_account, _staff_name, sender)
405 if include_log:
406 _log2.error(comment)
407 _log2.warning('syncing log file for emailing')
408 gmLog2.flush()
409 attachments = [ [_logfile_name, 'text/plain', 'quoted-printable'] ]
410 else:
411 attachments = None
412
413 dlg.Destroy()
414
415 wx.BeginBusyCursor()
416 try:
417 gmNetworkTools.send_mail (
418 sender = '%s <%s>' % (_staff_name, gmNetworkTools.default_mail_sender),
419 receiver = receivers,
420 subject = u'<bug>: %s' % comment,
421 message = msg,
422 encoding = gmI18N.get_encoding(),
423 server = gmNetworkTools.default_mail_server,
424 auth = {'user': gmNetworkTools.default_mail_sender, 'password': u'gnumed-at-gmx-net'},
425 attachments = attachments
426 )
427 gmDispatcher.send(signal='statustext', msg = _('Bug report has been emailed.'))
428 except:
429 _log2.exception('cannot send bug report')
430 gmDispatcher.send(signal='statustext', msg = _('Bug report COULD NOT be emailed.'))
431 wx.EndBusyCursor()
432
433
434 from Gnumed.wxGladeWidgets import wxgUnhandledExceptionDlg
435
437
439
440 exception = kwargs['exception']
441 del kwargs['exception']
442 self.logfile = kwargs['logfile']
443 del kwargs['logfile']
444
445 wxgUnhandledExceptionDlg.wxgUnhandledExceptionDlg.__init__(self, *args, **kwargs)
446
447 if _sender_email is not None:
448 self._TCTRL_sender.SetValue(_sender_email)
449 self._TCTRL_helpdesk.SetValue(_helpdesk)
450 self._TCTRL_logfile.SetValue(self.logfile)
451 t, v, tb = exception
452 self._TCTRL_exc_type.SetValue(str(t))
453 self._TCTRL_exc_value.SetValue(str(v))
454 self._TCTRL_traceback.SetValue(''.join(traceback.format_tb(tb)))
455
456 self.Fit()
457
471
482
488
489