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