wcs/wcs/admin/logged_errors.py

225 lines
9.2 KiB
Python

# w.c.s. - web application for online forms
# Copyright (C) 2005-2017 Entr'ouvert
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
import re
from quixote import get_response, get_publisher, redirect
from quixote.directory import Directory
from quixote.html import TemplateIO, htmltext
from wcs.qommon import _, ngettext
from wcs.qommon import errors, get_cfg
from wcs.qommon.misc import localstrftime
from wcs.logged_errors import LoggedError
class LoggedErrorDirectory(Directory):
_q_exports = ['', 'delete', 'ack']
def __init__(self, parent_dir, error):
self.parent_dir = parent_dir
self.error = error
def _q_index(self):
get_response().breadcrumb.append(('%s/' % self.error.id, self.error.summary))
self.parent_dir.html_top(_('Logged Errors - %s') % self.error.summary)
get_response().filter['sidebar'] = self.get_sidebar()
r = TemplateIO(html=True)
r += htmltext('<h2>%s</h2>') % self.error.summary
r += htmltext('<ul>')
r += htmltext(' <li>%s %s</li>') % (
_('First occurence:'),
localstrftime(self.error.first_occurence_timestamp))
r += htmltext(' <li>%s %s</li>') % (
_('Latest occurence:'),
localstrftime(self.error.latest_occurence_timestamp))
r += htmltext(' <li>%s %s</li>') % (_('Count:'), self.error.occurences_count)
formdef = self.error.get_formdef()
if formdef:
r += htmltext(' <li>%s <a href="/backoffice/forms/%s/">%s</a></li>') % (
_('Form:'), formdef.id, formdef.name)
workflow = self.error.get_workflow()
if workflow:
r += htmltext(' <li>%s <a href="/backoffice/workflows/%s/">%s</a></li>') % (
_('Workflow: '), workflow.id, workflow.name)
status = self.error.get_status()
if status:
r += htmltext('<ul>')
r += htmltext(
'<li>%s <a href="/backoffice/workflows/%s/status/%s/">%s</a></li>') % (
_('Status:'), workflow.id, status.id, status.name)
status_item = self.error.get_status_item()
if status_item:
r += htmltext(
'<li>%s <a href="/backoffice/workflows/%s/status/%s/items/%s/">%s</a></li>') % (
_('Action:'), workflow.id, status.id, status_item.id,
_(status_item.description))
r += htmltext('</ul>')
if self.error.expression or self.error.expression_type:
expression_title = {
'python': N_('Python Expression'),
'django': N_('Django Expression'),
'template': N_('Template'),
'text': N_('Text'),
}.get(self.error.expression_type, N_('Unknown'))
r += htmltext(' <li>%s <code>%s</code></li>') % (
_('%s:') % expression_title, self.error.expression)
if self.error.exception_class or self.error.exception_message:
r += htmltext(' <li>%s <code>%s: %s</code></li>') % (_('Error message:'),
self.error.exception_class,
self.error.exception_message)
if formdef:
formdata = self.error.get_formdata()
if formdata:
r += htmltext(' <li>%s <a href="%s">%s</a>') % (
_('Data: '), formdata.get_url(backoffice=True),
formdata.get_display_name())
r += htmltext(' (<a href="%sinspect">%s</a>)') % (
formdata.get_url(backoffice=True), _('inspector'))
r += htmltext('</li>')
r += htmltext('</ul>')
if not self.error.traceback:
return r.getvalue()
parts = (N_('Exception'), N_('Stack trace (most recent call first)'),
N_('Form'), N_('Cookies'), N_('Environment'))
current_part = None
for line in self.error.traceback.splitlines():
if line.endswith(':') and line.rstrip(':') in parts:
if current_part in parts[:2]:
r += htmltext('</pre>')
elif current_part:
r += htmltext('</table>')
current_part = line.rstrip(':')
r += htmltext('<h3>%s</h3>') % _(current_part)
if current_part in parts[:2]:
r += htmltext('<pre class="traceback">')
else:
r += htmltext('<table class="main compact code">')
continue
if current_part in parts[:2]:
r += line + '\n'
elif line:
r += htmltext('<tr><td>%s</td><td>%s</td></tr>') % tuple(
re.split(r'\s+', line, maxsplit=1))
if current_part in parts[:2]:
r += htmltext('</pre>')
elif current_part:
r += htmltext('</table>')
return r.getvalue()
def get_sidebar(self):
r = TemplateIO(html=True)
r += htmltext('<ul id="sidebar-actions">')
if not self.error.acked:
r += htmltext('<li><a href="ack">%s</a></li>') % ('Ack')
else:
r += htmltext('<li>%s</li>' % _('Acked'))
r += htmltext('<li><a href="delete">%s</a></li>') % _('Delete')
r += htmltext('</ul>')
return r.getvalue()
def ack(self):
self.error.acked = True
self.error.store()
return redirect('.')
def delete(self):
self.error.remove_self()
return redirect('..')
class LoggedErrorsDirectory(Directory):
_q_exports = ['']
@classmethod
def get_errors(cls, formdef_id=None, workflow_id=None):
errors = []
if formdef_id:
errors = LoggedError.get_with_indexed_value('formdef_id', formdef_id)
elif workflow_id:
errors = LoggedError.get_with_indexed_value('workflow_id', workflow_id)
return list(errors)
@classmethod
def errors_block(cls, formdef_id=None, workflow_id=None):
errors = cls.get_errors(formdef_id=formdef_id, workflow_id=workflow_id)
if not errors:
return ''
errors.sort(key=lambda x: x.id, reverse=True)
r = TemplateIO(html=True)
r += htmltext('<div class="bo-block logged-errors">')
r += htmltext('<h3><a href="logged-errors/">%s</a></h3>') % ngettext(
'%(count)d error', '%(count)d errors', len(errors)) % {'count': len(errors)}
r += htmltext('<ul>')
for error in errors[:3]:
r += htmltext('<li><a href="logged-errors/%s/">%s</a> ') % (error.id, error.summary)
if error.exception_class or error.exception_message:
r += htmltext(_('error %(class)s (%(message)s)')) % {
'class': error.exception_class,
'message': error.exception_message,
}
r += htmltext('</li>')
if len(errors) > 3:
r += htmltext('<li>...</li>')
r += htmltext('</ul>')
r += htmltext('</div>')
return r.getvalue()
def __init__(self, parent_dir, formdef_id=None, workflow_id=None):
self.parent_dir = parent_dir
self.formdef_id = formdef_id
self.workflow_id = workflow_id
def _q_index(self):
get_response().breadcrumb.append(('logged-errors/', _('Logged Errors')))
self.parent_dir.html_top(_('Logged Errors'))
r = TemplateIO(html=True)
r += htmltext('<div class="bo-block">')
r += htmltext('<h2>%s</h2>') % _('Logged Errors')
r += htmltext('<ul class="biglist">')
for error in self.get_errors(formdef_id=self.formdef_id, workflow_id=self.workflow_id):
r += htmltext('<li><strong><a href="%s/">%s</a></strong> ') % (error.id, error.summary)
if error.exception_class or error.exception_message:
r += htmltext(_('error %(class)s (%(message)s)')) % {
'class': error.exception_class,
'message': error.exception_message,
}
r += htmltext('<p class="details badge">%s</p>') % error.occurences_count
r += htmltext('</li>')
r += htmltext('</ul>')
r += htmltext('</div>')
return r.getvalue()
def _q_lookup(self, component):
try:
error = LoggedError.get(component)
except KeyError:
raise errors.TraversalError()
get_response().breadcrumb.append(('logged-errors/', _('Logged Errors')))
return LoggedErrorDirectory(self.parent_dir, error)