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 (('term' in msg) or ('abnorm' in msg) or ('end' in msg))
127 )
128
129 if t == gmPG2.dbapi.InterfaceError:
130 conn_lost = (
131 ('onnect' in msg)
132 and
133 (('close' in msg) or ('end' in msg))
134 )
135
136 if not conn_lost:
137 return False
138
139 _log2.error('lost connection')
140 gmLog2.log_stack_trace()
141 try: wx.EndBusyCursor()
142 except: pass
143 gmLog2.flush()
144 gmGuiHelpers.gm_show_error (
145 aTitle = _('Lost connection'),
146 aMessage = _(
147 'Since you were last working in GNUmed,\n'
148 'your database connection timed out.\n'
149 '\n'
150 'This GNUmed session is now expired.\n'
151 '\n'
152 'You will have to close this client and\n'
153 'restart a new GNUmed session.'
154 )
155 )
156 return True
157
159
160 _log2.debug('unhandled exception caught:', exc_info = (t, v, tb))
161
162 if __handle_ctrl_c(t, v, tb):
163 return
164
165 if __handle_exceptions_on_shutdown(t, v, tb):
166 return
167
168 if __ignore_dead_objects_from_async(t, v, tb):
169 return
170
171 if __handle_import_error(t, v, tb):
172 return
173
174
175 _cfg = gmCfg2.gmCfgData()
176 if _cfg.get(option = 'debug') is False:
177 _log2.error('enabling debug mode')
178 _cfg.set_option(option = 'debug', value = True)
179 root_logger = logging.getLogger()
180 root_logger.setLevel(logging.DEBUG)
181 _log2.debug('unhandled exception caught:', exc_info = (t, v, tb))
182
183 if __handle_lost_db_connection(t, v, tb):
184 return
185
186 gmLog2.log_stack_trace()
187
188
189
190
191 try: wx.EndBusyCursor()
192 except: pass
193
194 name = os.path.basename(_logfile_name)
195 name, ext = os.path.splitext(name)
196 new_name = os.path.expanduser(os.path.join (
197 '~',
198 'gnumed',
199 'logs',
200 '%s_%s%s' % (name, pyDT.datetime.now().strftime('%Y-%m-%d_%H-%M-%S'), ext)
201 ))
202
203 dlg = cUnhandledExceptionDlg(parent = None, id = -1, exception = (t, v, tb), logfile = new_name)
204 dlg.ShowModal()
205 comment = dlg._TCTRL_comment.GetValue()
206 dlg.Destroy()
207 if (comment is not None) and (comment.strip() != u''):
208 _log2.error(u'user comment: %s', comment.strip())
209
210 _log2.warning('syncing log file for backup to [%s]', new_name)
211 gmLog2.flush()
212 shutil.copy2(_logfile_name, new_name)
213
235
242
248
249 -def mail_log(parent=None, comment=None, helpdesk=None, sender=None):
250
251 if (comment is None) or (comment.strip() == u''):
252 comment = wx.GetTextFromUser (
253 message = _(
254 'Please enter a short note on what you\n'
255 'were about to do in GNUmed:'
256 ),
257 caption = _('Sending bug report'),
258 parent = parent
259 )
260 if comment.strip() == u'':
261 comment = u'<user did not comment on bug report>'
262
263 receivers = []
264 if helpdesk is not None:
265 receivers = regex.findall (
266 '[\S]+@[\S]+',
267 helpdesk.strip(),
268 flags = regex.UNICODE | regex.LOCALE
269 )
270 if len(receivers) == 0:
271 if _is_public_database:
272 receivers = [u'gnumed-bugs@gnu.org']
273
274 receiver_string = wx.GetTextFromUser (
275 message = _(
276 'Edit the list of email addresses to send the\n'
277 'bug report to (separate addresses by spaces).\n'
278 '\n'
279 'Note that <gnumed-bugs@gnu.org> refers to\n'
280 'the public (!) GNUmed bugs mailing list.'
281 ),
282 caption = _('Sending bug report'),
283 default_value = ','.join(receivers),
284 parent = parent
285 )
286 if receiver_string.strip() == u'':
287 return
288
289 receivers = regex.findall (
290 '[\S]+@[\S]+',
291 receiver_string,
292 flags = regex.UNICODE | regex.LOCALE
293 )
294
295 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
296 parent,
297 -1,
298 caption = _('Sending bug report'),
299 question = _(
300 'Your bug report will be sent to:\n'
301 '\n'
302 '%s\n'
303 '\n'
304 'Make sure you have reviewed the log file for potentially\n'
305 'sensitive information before sending out the bug report.\n'
306 '\n'
307 'Note that emailing the report may take a while depending\n'
308 'on the speed of your internet connection.\n'
309 ) % u'\n'.join(receivers),
310 button_defs = [
311 {'label': _('Send report'), 'tooltip': _('Yes, send the bug report.')},
312 {'label': _('Cancel'), 'tooltip': _('No, do not send the bug report.')}
313 ],
314 show_checkbox = True,
315 checkbox_msg = _('include log file in bug report')
316 )
317 dlg._CHBOX_dont_ask_again.SetValue(_is_public_database)
318 go_ahead = dlg.ShowModal()
319 if go_ahead == wx.ID_NO:
320 dlg.Destroy()
321 return
322
323 include_log = dlg._CHBOX_dont_ask_again.GetValue()
324 if not _is_public_database:
325 if include_log:
326 result = gmGuiHelpers.gm_show_question (
327 _(
328 'The database you are connected to is marked as\n'
329 '"in-production with controlled access".\n'
330 '\n'
331 'You indicated that you want to include the log\n'
332 'file in your bug report. While this is often\n'
333 'useful for debugging the log file might contain\n'
334 'bits of patient data which must not be sent out\n'
335 'without de-identification.\n'
336 '\n'
337 'Please confirm that you want to include the log !'
338 ),
339 _('Sending bug report')
340 )
341 include_log = (result is True)
342
343 if sender is None:
344 sender = _('<not supplied>')
345 else:
346 if sender.strip() == u'':
347 sender = _('<not supplied>')
348
349 msg = u"""\
350 Report sent via GNUmed's handler for unexpected exceptions.
351
352 user comment : %s
353
354 client version: %s
355
356 system account: %s
357 staff member : %s
358 sender email : %s
359
360 # enable Launchpad bug tracking
361 affects gnumed
362 tag automatic-report
363 importance medium
364
365 """ % (comment, _client_version, _local_account, _staff_name, sender)
366 if include_log:
367 _log2.error(comment)
368 _log2.warning('syncing log file for emailing')
369 gmLog2.flush()
370 attachments = [ [_logfile_name, 'text/plain', 'quoted-printable'] ]
371 else:
372 attachments = None
373
374 dlg.Destroy()
375
376 wx.BeginBusyCursor()
377 try:
378 gmNetworkTools.send_mail (
379 sender = '%s <%s>' % (_staff_name, gmNetworkTools.default_mail_sender),
380 receiver = receivers,
381 subject = u'<bug>: %s' % comment,
382 message = msg,
383 encoding = gmI18N.get_encoding(),
384 server = gmNetworkTools.default_mail_server,
385 auth = {'user': gmNetworkTools.default_mail_sender, 'password': u'gnumed-at-gmx-net'},
386 attachments = attachments
387 )
388 gmDispatcher.send(signal='statustext', msg = _('Bug report has been emailed.'))
389 except:
390 _log2.exception('cannot send bug report')
391 gmDispatcher.send(signal='statustext', msg = _('Bug report COULD NOT be emailed.'))
392 wx.EndBusyCursor()
393
394
395 from Gnumed.wxGladeWidgets import wxgUnhandledExceptionDlg
396
398
400
401 exception = kwargs['exception']
402 del kwargs['exception']
403 self.logfile = kwargs['logfile']
404 del kwargs['logfile']
405
406 wxgUnhandledExceptionDlg.wxgUnhandledExceptionDlg.__init__(self, *args, **kwargs)
407
408 if _sender_email is not None:
409 self._TCTRL_sender.SetValue(_sender_email)
410 self._TCTRL_helpdesk.SetValue(_helpdesk)
411 self._TCTRL_logfile.SetValue(self.logfile)
412 t, v, tb = exception
413 self._TCTRL_exc_type.SetValue(str(t))
414 self._TCTRL_exc_value.SetValue(str(v))
415 self._TCTRL_traceback.SetValue(''.join(traceback.format_tb(tb)))
416
417 self.Fit()
418
429
440
446
447