0001-can-notify-exceptions-on-formdata-only-in-UI-24327.patch
tests/test_form_pages.py | ||
---|---|---|
1694 | 1694 |
assert 'barXYZ' in resp.body # unchanged value is still there |
1695 | 1695 |
assert formdef.data_class().get(data_id).status == 'wf-%s' % st2.id |
1696 | 1696 | |
1697 |
# jump to a nonexistent status == do not jump |
|
1697 |
# jump to a nonexistent status == do not jump, but add a LoggedError |
|
1698 |
LoggedError.wipe() |
|
1699 |
assert LoggedError.count() == 0 |
|
1698 | 1700 |
editable.status = 'deleted_status_id' |
1699 | 1701 |
workflow.store() |
1700 | 1702 |
# go back to st1 |
... | ... | |
1710 | 1712 |
resp = resp.forms[0].submit('submit') |
1711 | 1713 |
resp = resp.follow() |
1712 | 1714 |
assert formdef.data_class().get(data_id).status == 'wf-%s' % st1.id # stay on st1 |
1715 |
assert LoggedError.count() == 1 |
|
1716 |
logged_error = LoggedError.select()[0] |
|
1717 |
assert logged_error.formdata_id == str(formdata.id) |
|
1718 |
assert logged_error.formdef_id == formdef.id |
|
1719 |
assert logged_error.workflow_id == workflow.id |
|
1720 |
assert logged_error.status_id == st1.id |
|
1721 |
assert logged_error.status_item_id == editable.id |
|
1713 | 1722 | |
1714 | 1723 |
def test_form_count_dispatching(pub): |
1715 | 1724 |
user = create_user(pub) |
wcs/admin/logged_errors.py | ||
---|---|---|
56 | 56 |
if workflow: |
57 | 57 |
r += htmltext(' <li>%s <a href="/backoffice/workflows/%s/">%s</a></li>') % ( |
58 | 58 |
_('Workflow: '), workflow.id, workflow.name) |
59 |
status = self.error.get_status() |
|
60 |
if status: |
|
61 |
r += htmltext('<ul>') |
|
62 |
r += htmltext( |
|
63 |
'<li>%s <a href="/backoffice/workflows/%s/status/%s/">%s</a></li>') % ( |
|
64 |
_('Status:'), workflow.id, status.id, status.name) |
|
65 |
status_item = self.error.get_status_item() |
|
66 |
if status_item: |
|
67 |
r += htmltext( |
|
68 |
'<li>%s <a href="/backoffice/workflows/%s/status/%s/items/%s/">%s</a></li>') % ( |
|
69 |
_('Action:'), workflow.id, status.id, status_item.id, |
|
70 |
_(status_item.description)) |
|
71 |
r += htmltext('</ul>') |
|
59 | 72 | |
60 | 73 |
if formdef: |
61 | 74 |
formdata = self.error.get_formdata() |
... | ... | |
68 | 81 |
r += htmltext('</li>') |
69 | 82 | |
70 | 83 |
r += htmltext('</ul>') |
84 | ||
85 |
if not self.error.traceback: |
|
86 |
return r.getvalue() |
|
87 | ||
71 | 88 |
parts = (N_('Exception'), N_('Stack trace (most recent call first)'), |
72 | 89 |
N_('Form'), N_('Cookies'), N_('Environment')) |
73 | 90 |
current_part = None |
wcs/logged_errors.py | ||
---|---|---|
31 | 31 |
formdata_id = None |
32 | 32 |
formdef_id = None |
33 | 33 |
workflow_id = None |
34 |
status_id = None |
|
35 |
status_item_id = None |
|
34 | 36 |
traceback = None |
35 | 37 |
occurences_count = 0 |
36 | 38 |
first_occurence_timestamp = None |
... | ... | |
41 | 43 |
XML_NODES = [ |
42 | 44 |
('summary', 'str'), ('traceback', 'str'), |
43 | 45 |
('formdata_id', 'str'), ('formdef_id', 'str'), ('workflow_id', 'str'), |
46 |
('status_id', 'str'), ('status_item_id', 'str'), |
|
44 | 47 |
('occurences_count', 'int'), |
45 | 48 |
('first_occurence_timestamp', 'datetime'), |
46 | 49 |
('latest_occurence_timestamp', 'datetime'), |
47 | 50 |
('acked', 'bool')] |
48 | 51 | |
49 | 52 |
@classmethod |
50 |
def record(cls, error_summary, plain_error_msg, publisher): |
|
53 |
def record(cls, error_summary, plain_error_msg=None, formdata=None, formdata_id=None, |
|
54 |
formdef=None, workflow=None, status=None, status_item=None): |
|
51 | 55 |
error = cls() |
52 | 56 |
error.summary = error_summary |
53 | 57 |
error.traceback = plain_error_msg |
54 |
try: |
|
55 |
context = publisher.substitutions.get_context_variables() |
|
56 |
except: |
|
57 |
return |
|
58 | 58 | |
59 |
formdef_urlname = context.get('form_slug') |
|
60 |
if not formdef_urlname: |
|
59 |
if formdata: |
|
60 |
error.formdata_id = str(formdata.id) |
|
61 |
error.formdef_id = formdata.formdef.id |
|
62 |
error.workflow_id = formdata.formdef.workflow.id |
|
63 |
else: |
|
64 |
error.formdata_id = str(formdata_id) |
|
65 |
if formdef: |
|
66 |
error.formdef_id = formdef.id |
|
67 |
error.workflow_id = formdef.workflow.id |
|
68 | ||
69 |
if not error.formdef_id: |
|
61 | 70 |
# cannot attach error to formdef, don't record in journal, it will |
62 | 71 |
# still be sent by email to administrators. |
63 | 72 |
return |
64 | 73 | |
65 |
error.formdata_id = context.get('form_number_raw') |
|
66 |
if formdef_urlname: |
|
67 |
formdef = FormDef.get_by_urlname(formdef_urlname) |
|
68 |
error.formdef_id = formdef.id |
|
69 |
error.workflow_id = formdef.workflow_id |
|
74 |
if not error.workflow_id: |
|
75 |
error.workflow_id = workflow.id |
|
76 | ||
77 |
if status_item: |
|
78 |
error.status_item_id = status_item.id |
|
79 |
if status_item.parent: |
|
80 |
error.status_id = status_item.parent.id |
|
81 |
if status: |
|
82 |
error.status_id = status.id |
|
70 | 83 | |
71 | 84 |
error.first_occurence_timestamp = datetime.datetime.now() |
72 | 85 |
error.id = '%s-%s' % ( |
... | ... | |
80 | 93 |
error.latest_occurence_timestamp = datetime.datetime.now() |
81 | 94 |
error.store() |
82 | 95 | |
96 |
@classmethod |
|
97 |
def record_exception(cls, error_summary, plain_error_msg, publisher): |
|
98 |
try: |
|
99 |
context = publisher.substitutions.get_context_variables() |
|
100 |
except Exception as e: |
|
101 |
return |
|
102 |
formdata_id = context.get('form_number_raw') |
|
103 |
formdef_urlname = context.get('form_slug') |
|
104 |
if not formdef_urlname: |
|
105 |
# cannot attach error to formdef, don't record in journal, it will |
|
106 |
# still be sent by email to administrators. |
|
107 |
return |
|
108 |
formdef = FormDef.get_by_urlname(formdef_urlname) |
|
109 |
cls.record(error_summary, plain_error_msg, formdata_id=formdata_id, |
|
110 |
formdef=formdef, workflow=formdef.workflow) |
|
111 | ||
83 | 112 |
@property |
84 | 113 |
def tech_id(self): |
85 |
return ('%s-%s-%s' % (self.formdef_id, self.workflow_id, simplify(self.summary)))[:200] |
|
114 |
return ('%s-%s-%s-%s-%s' % (self.formdef_id, self.workflow_id, self.status_id, |
|
115 |
self.status_item_id, simplify(self.summary)))[:200] |
|
86 | 116 | |
87 | 117 |
def get_formdef(self): |
88 | 118 |
return FormDef.get(self.formdef_id, ignore_errors=True) |
... | ... | |
91 | 121 |
return Workflow.get(self.workflow_id, ignore_errors=True) |
92 | 122 | |
93 | 123 |
def get_formdata(self): |
124 |
if not self.formdata_id: |
|
125 |
return None |
|
94 | 126 |
return self.get_formdef().data_class().get(self.formdata_id, ignore_errors=True) |
127 | ||
128 |
def get_status(self): |
|
129 |
if not self.status_id: |
|
130 |
return None |
|
131 |
workflow = self.get_workflow() |
|
132 |
if not workflow: |
|
133 |
return None |
|
134 |
for status in workflow.possible_status: |
|
135 |
if status.id == self.status_id: |
|
136 |
return status |
|
137 |
return None |
|
138 | ||
139 |
def get_status_item(self): |
|
140 |
status = self.get_status() |
|
141 |
if not status or not status.items: |
|
142 |
return None |
|
143 |
for status_item in status.items: |
|
144 |
if status_item.id == self.status_item_id: |
|
145 |
return status_item |
|
146 |
return None |
wcs/publisher.py | ||
---|---|---|
287 | 287 |
super(WcsPublisher, self).log_internal_error(error_summary, |
288 | 288 |
plain_error_msg, record=record) |
289 | 289 |
if record: |
290 |
LoggedError.record(error_summary, plain_error_msg, publisher=self) |
|
290 |
LoggedError.record_exception(error_summary, plain_error_msg, publisher=self)
|
|
291 | 291 | |
292 | 292 |
def apply_global_action_timeouts(self): |
293 | 293 |
from wcs.workflows import Workflow, WorkflowGlobalActionTimeoutTrigger |
wcs/workflows.py | ||
---|---|---|
1725 | 1725 |
return [] |
1726 | 1726 | |
1727 | 1727 |
targets = [x for x in self.parent.parent.possible_status if x.id == self.status] |
1728 |
if not targets:
|
|
1729 |
get_publisher().get_app_logger().error(
|
|
1730 |
'reference to invalid status in workflow %r, status %r, item %r' % (
|
|
1731 |
self.parent.parent.name,
|
|
1732 |
self.parent.name,
|
|
1733 |
self.description)) |
|
1728 |
if not targets and formdata: # do not log in presentation context: formdata is needed
|
|
1729 |
from wcs.logged_errors import LoggedError
|
|
1730 |
message = _('reference to invalid status %s in status %s, action %s') % (
|
|
1731 |
self.status, self.parent.name, _(self.description))
|
|
1732 |
LoggedError.record(message, formdata=formdata, status_item=self)
|
|
1733 | ||
1734 | 1734 |
return targets |
1735 | 1735 | |
1736 | 1736 |
def get_jump_label(self): |
1737 |
- |