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