0001-misc-store-LoggedErrors-in-SQL-48925.patch
tests/admin_pages/test_workflow.py | ||
---|---|---|
12 | 12 |
from webtest import Upload |
13 | 13 | |
14 | 14 |
from wcs.qommon.http_request import HTTPRequest |
15 |
from wcs.logged_errors import LoggedError |
|
16 | 15 |
from wcs.roles import Role |
17 | 16 |
from wcs.workflows import ( |
18 | 17 |
Workflow, WorkflowCriticalityLevel, DisplayMessageWorkflowStatusItem, |
... | ... | |
1779 | 1778 | |
1780 | 1779 | |
1781 | 1780 |
def test_workflows_wscall_status_error(pub): |
1781 |
if not pub.is_using_postgresql(): |
|
1782 |
pytest.skip('this requires SQL') |
|
1783 |
return |
|
1784 | ||
1782 | 1785 |
create_superuser(pub) |
1783 | 1786 | |
1784 |
LoggedError.wipe()
|
|
1787 |
pub.loggederror_class.wipe()
|
|
1785 | 1788 |
Workflow.wipe() |
1786 | 1789 |
workflow = Workflow(name='foo') |
1787 | 1790 |
baz_status = workflow.add_status(name='baz') |
... | ... | |
1798 | 1801 | |
1799 | 1802 |
app = login(get_app(pub)) |
1800 | 1803 |
app.get('/backoffice/workflows/%s/' % workflow.id) |
1801 |
assert LoggedError.count() == 0
|
|
1804 |
assert pub.loggederror_class.count() == 0
|
|
1802 | 1805 | |
1803 | 1806 |
# delete foo status |
1804 | 1807 |
del workflow.possible_status[1] |
1805 | 1808 |
workflow.store() |
1806 | 1809 |
app.get('/backoffice/workflows/%s/' % workflow.id) |
1807 |
assert LoggedError.count() == 1
|
|
1808 |
error = LoggedError.select()[0]
|
|
1810 |
assert pub.loggederror_class.count() == 1
|
|
1811 |
error = pub.loggederror_class.select()[0]
|
|
1809 | 1812 |
assert error.tech_id == '%s-reference-to-invalid-status-in-workflow-foo-status-baz-item-webservice' % workflow.id |
1810 | 1813 |
assert error.formdef_id is None |
1811 | 1814 |
assert error.workflow_id == workflow.id |
tests/backoffice_pages/test_all.py | ||
---|---|---|
39 | 39 |
from wcs.carddef import CardDef |
40 | 40 |
from wcs.categories import Category |
41 | 41 |
from wcs.formdef import FormDef |
42 |
from wcs.logged_errors import LoggedError |
|
43 | 42 |
from wcs import fields |
44 | 43 |
from wcs.wscalls import NamedWsCall |
45 | 44 | |
... | ... | |
2257 | 2256 |
@pytest.mark.parametrize('notify_on_errors', [True, False]) |
2258 | 2257 |
@pytest.mark.parametrize('record_on_errors', [True, False]) |
2259 | 2258 |
def test_backoffice_wscall_on_error(http_requests, pub, emails, notify_on_errors, record_on_errors): |
2259 |
if not pub.is_using_postgresql(): |
|
2260 |
pytest.skip('this requires SQL') |
|
2261 |
return |
|
2262 | ||
2260 | 2263 |
pub.cfg['debug'] = {'error_email': 'errors@localhost.invalid'} |
2261 | 2264 |
pub.write_cfg() |
2262 | 2265 | |
2263 | 2266 |
create_user(pub) |
2264 | 2267 |
create_environment(pub) |
2268 |
pub.loggederror_class.wipe() |
|
2265 | 2269 |
formdef = FormDef.get_by_urlname('form-title') |
2266 | 2270 |
form_class = formdef.data_class() |
2267 | 2271 | |
... | ... | |
2314 | 2318 |
else: |
2315 | 2319 |
assert emails.count() == 0 |
2316 | 2320 | |
2317 |
# check LoggedError
|
|
2321 |
# check pub.loggederror_class
|
|
2318 | 2322 |
if record_on_errors: |
2319 |
assert LoggedError.count() == 1
|
|
2320 |
LoggedError.wipe()
|
|
2323 |
assert pub.loggederror_class.count() == 1
|
|
2324 |
pub.loggederror_class.wipe()
|
|
2321 | 2325 |
else: |
2322 |
assert LoggedError.count() == 0
|
|
2326 |
assert pub.loggederror_class.count() == 0
|
|
2323 | 2327 | |
2324 | 2328 | |
2325 | 2329 |
def test_backoffice_wscall_attachment(http_requests, pub): |
... | ... | |
4709 | 4713 | |
4710 | 4714 | |
4711 | 4715 |
def test_backoffice_logged_errors(pub): |
4716 |
if not pub.is_using_postgresql(): |
|
4717 |
pytest.skip('this requires SQL') |
|
4718 |
return |
|
4719 | ||
4712 | 4720 |
Workflow.wipe() |
4713 | 4721 |
workflow = Workflow.get_default_workflow() |
4714 | 4722 |
workflow.id = '12' |
... | ... | |
4738 | 4746 |
carddef.fields = [] |
4739 | 4747 |
carddef.store() |
4740 | 4748 | |
4741 |
LoggedError.wipe()
|
|
4749 |
pub.loggederror_class.wipe()
|
|
4742 | 4750 | |
4743 | 4751 |
create_superuser(pub) |
4744 | 4752 |
app = login(get_app(pub)) |
... | ... | |
4753 | 4761 |
resp = app.get('/test/') |
4754 | 4762 |
resp = resp.form.submit('submit').follow() |
4755 | 4763 |
resp = resp.form.submit('submit') |
4756 |
assert LoggedError.count() == 1
|
|
4764 |
assert pub.loggederror_class.count() == 1
|
|
4757 | 4765 | |
4758 | 4766 |
app = login(get_app(pub)) |
4759 | 4767 |
resp = app.get('/backoffice/forms/%s/' % formdef.id) |
... | ... | |
4773 | 4781 |
assert not 'Acked' in resp.text |
4774 | 4782 |
resp = resp.click('Ack').follow() |
4775 | 4783 |
assert 'Acked' in resp.text |
4776 |
assert LoggedError.select()[0].acked is True
|
|
4784 |
assert pub.loggederror_class.select()[0].acked is True
|
|
4777 | 4785 |
resp = resp.click('Delete').follow() |
4778 |
assert LoggedError.count() == 0
|
|
4786 |
assert pub.loggederror_class.count() == 0
|
|
4779 | 4787 | |
4780 | 4788 |
pub.cfg.update({'debug': {'error_email': None}}) |
4781 | 4789 |
pub.write_cfg() |
... | ... | |
4784 | 4792 |
resp = app.get('/test/') |
4785 | 4793 |
resp = resp.form.submit('submit').follow() |
4786 | 4794 |
resp = resp.form.submit('submit') |
4787 |
assert LoggedError.count() == 1
|
|
4795 |
assert pub.loggederror_class.count() == 1
|
|
4788 | 4796 | |
4789 | 4797 |
app = login(get_app(pub)) |
4790 | 4798 |
resp = app.get('/backoffice/workflows/%s/' % workflow.id) |
... | ... | |
4802 | 4810 |
resp = app.get('/test/') |
4803 | 4811 |
resp = resp.form.submit('submit').follow() |
4804 | 4812 |
resp = resp.form.submit('submit') |
4805 |
assert LoggedError.count() == 2
|
|
4813 |
assert pub.loggederror_class.count() == 2
|
|
4806 | 4814 | |
4807 | 4815 |
app = login(get_app(pub)) |
4808 | 4816 |
resp = app.get('/backoffice/workflows/%s/' % workflow.id) |
... | ... | |
5338 | 5346 |
source_formdef.store() |
5339 | 5347 |
source_formdef.data_class().wipe() |
5340 | 5348 |
target_formdef.data_class().wipe() |
5341 |
LoggedError.wipe() |
|
5349 |
if pub.is_using_postgresql(): |
|
5350 |
pub.loggederror_class.wipe() |
|
5342 | 5351 |
return locals() |
5343 | 5352 | |
5344 | 5353 | |
5345 |
def test_backoffice_create_formdata_backoffice_submission(create_formdata): |
|
5354 |
def test_backoffice_create_formdata_backoffice_submission(pub, create_formdata):
|
|
5346 | 5355 |
# create submitting user |
5347 | 5356 |
user = create_formdata['pub'].user_class() |
5348 | 5357 |
user.name = 'Jean Darmette' |
... | ... | |
5373 | 5382 |
assert target_data_class.count() == 0 |
5374 | 5383 |
# resubmit it through backoffice submission |
5375 | 5384 |
resp = resp.form.submit(name='button_resubmit') |
5376 |
assert LoggedError.count() == 0 |
|
5385 |
if pub.is_using_postgresql(): |
|
5386 |
assert pub.loggederror_class.count() == 0 |
|
5377 | 5387 |
assert target_data_class.count() == 1 |
5378 | 5388 |
target_formdata = target_data_class.select()[0] |
5379 | 5389 | |
... | ... | |
5402 | 5412 |
assert pq('.field-type-file .value').text() == 'bar' |
5403 | 5413 | |
5404 | 5414 | |
5405 |
def test_linked_forms_variables(create_formdata): |
|
5415 |
def test_linked_forms_variables(pub, create_formdata):
|
|
5406 | 5416 |
# create source formdata |
5407 | 5417 |
formdata = create_formdata['source_formdef'].data_class()() |
5408 | 5418 |
upload = PicklableUpload('/foo/bar', content_type='text/plain') |
... | ... | |
5419 | 5429 |
formdata.perform_workflow() |
5420 | 5430 |
formdata.store() |
5421 | 5431 | |
5422 |
get_publisher().substitutions.reset()
|
|
5423 |
get_publisher().substitutions.feed(formdata)
|
|
5424 |
substvars = get_publisher().substitutions.get_context_variables(mode='lazy')
|
|
5432 |
pub.substitutions.reset()
|
|
5433 |
pub.substitutions.feed(formdata)
|
|
5434 |
substvars = pub.substitutions.get_context_variables(mode='lazy')
|
|
5425 | 5435 |
assert str(substvars['form_links_resubmitted_form_var_foo_string']) == 'coucou' |
5426 | 5436 |
assert 'form_links_resubmitted_form_var_foo_string' in substvars.get_flat_keys() |
5427 | 5437 | |
... | ... | |
5440 | 5450 |
assert 'form_links_resubmitted_form_var_foo_string' not in resp |
5441 | 5451 | |
5442 | 5452 | |
5443 |
def test_backoffice_create_formdata_map_fields_by_varname(create_formdata): |
|
5453 |
def test_backoffice_create_formdata_map_fields_by_varname(pub, create_formdata):
|
|
5444 | 5454 |
create_formdata['create_formdata'].map_fields_by_varname = True |
5445 | 5455 |
create_formdata['create_formdata'].mappings = [] |
5446 | 5456 |
create_formdata['wf'].store() |
... | ... | |
5490 | 5500 |
assert target_data_class.count() == 0 |
5491 | 5501 |
# resubmit it through backoffice submission |
5492 | 5502 |
resp = resp.form.submit(name='button_resubmit') |
5493 |
assert LoggedError.count() == 0 |
|
5503 |
if pub.is_using_postgresql(): |
|
5504 |
assert pub.loggederror_class.count() == 0 |
|
5494 | 5505 |
assert target_data_class.count() == 1 |
5495 | 5506 |
target_formdata = target_data_class.select()[0] |
5496 | 5507 |
tests/backoffice_pages/test_carddata.py | ||
---|---|---|
10 | 10 |
from wcs.carddef import CardDef |
11 | 11 |
from wcs.categories import CardDefCategory |
12 | 12 |
from wcs.formdef import FormDef |
13 |
from wcs.logged_errors import LoggedError |
|
14 | 13 |
from wcs.qommon.http_request import HTTPRequest |
15 | 14 |
from wcs.wf.wscall import WebserviceCallStatusItem |
16 | 15 |
from wcs.workflows import ChoiceWorkflowStatusItem, Workflow |
... | ... | |
417 | 416 | |
418 | 417 | |
419 | 418 |
def test_backoffice_cards_wscall_failure_display(http_requests, pub, studio): |
420 |
LoggedError.wipe() |
|
419 |
if not pub.is_using_postgresql(): |
|
420 |
pytest.skip('this requires SQL') |
|
421 |
return |
|
422 | ||
423 |
pub.loggederror_class.wipe() |
|
421 | 424 |
user = create_user(pub) |
422 | 425 | |
423 | 426 |
Workflow.wipe() |
... | ... | |
473 | 476 |
resp = resp.follow() |
474 | 477 |
assert 'Error during webservice call' in resp.text |
475 | 478 | |
476 |
assert LoggedError.count() == 1
|
|
477 |
assert LoggedError.select()[0].get_formdata().data == {'1': 'plop'}
|
|
479 |
assert pub.loggederror_class.count() == 1
|
|
480 |
assert pub.loggederror_class.select()[0].get_formdata().data == {'1': 'plop'}
|
|
478 | 481 | |
479 | 482 | |
480 | 483 |
def test_block_card_item_link(pub, studio, blocks_feature): |
tests/form_pages/test_all.py | ||
---|---|---|
50 | 50 |
from wcs.data_sources import NamedDataSource |
51 | 51 |
from wcs.wscalls import NamedWsCall |
52 | 52 |
from wcs import fields |
53 |
from wcs.logged_errors import LoggedError |
|
54 | 53 |
from wcs.forms.root import PublicFormStatusPage |
55 | 54 | |
56 | 55 |
from utilities import get_app, login, create_temporary_pub, clean_temporary_pub |
... | ... | |
2351 | 2350 |
assert formdef.data_class().get(data_id).status == 'wf-%s' % st2.id |
2352 | 2351 | |
2353 | 2352 |
# jump to a nonexistent status == do not jump, but add a LoggedError |
2354 |
LoggedError.wipe() |
|
2355 |
assert LoggedError.count() == 0 |
|
2353 |
if pub.is_using_postgresql(): |
|
2354 |
pub.loggederror_class.wipe() |
|
2355 |
assert pub.loggederror_class.count() == 0 |
|
2356 | 2356 |
editable.status = 'deleted_status_id' |
2357 | 2357 |
workflow.store() |
2358 | 2358 |
# go back to st1 |
... | ... | |
2368 | 2368 |
resp = resp.forms[0].submit('submit') |
2369 | 2369 |
resp = resp.follow() |
2370 | 2370 |
assert formdef.data_class().get(data_id).status == 'wf-%s' % st1.id # stay on st1 |
2371 |
assert LoggedError.count() == 1 |
|
2372 |
logged_error = LoggedError.select()[0] |
|
2373 |
assert logged_error.formdata_id == str(formdata.id) |
|
2374 |
assert logged_error.formdef_id == formdef.id |
|
2375 |
assert logged_error.workflow_id == workflow.id |
|
2376 |
assert logged_error.status_id == st1.id |
|
2377 |
assert logged_error.status_item_id == editable.id |
|
2378 |
assert logged_error.occurences_count == 1 |
|
2371 |
if pub.is_using_postgresql(): |
|
2372 |
assert pub.loggederror_class.count() == 1 |
|
2373 |
logged_error = pub.loggederror_class.select()[0] |
|
2374 |
assert logged_error.formdata_id == str(formdata.id) |
|
2375 |
assert logged_error.formdef_id == formdef.id |
|
2376 |
assert logged_error.workflow_id == workflow.id |
|
2377 |
assert logged_error.status_id == st1.id |
|
2378 |
assert logged_error.status_item_id == editable.id |
|
2379 |
assert logged_error.occurences_count == 1 |
|
2379 | 2380 | |
2380 | 2381 |
# do it again: increment logged_error.occurences_count |
2381 | 2382 |
page = login(get_app(pub), username='foo', password='foo').get('/test/%s/' % data_id) |
... | ... | |
2386 | 2387 |
resp = resp.forms[0].submit('submit') |
2387 | 2388 |
resp = resp.follow() |
2388 | 2389 |
assert formdef.data_class().get(data_id).status == 'wf-%s' % st1.id # stay on st1 |
2389 |
assert LoggedError.count() == 1 |
|
2390 |
logged_error = LoggedError.select()[0] |
|
2391 |
assert logged_error.occurences_count == 2 |
|
2390 |
if pub.is_using_postgresql(): |
|
2391 |
assert pub.loggederror_class.count() == 1 |
|
2392 |
logged_error = pub.loggederror_class.select()[0] |
|
2393 |
assert logged_error.occurences_count == 2 |
|
2392 | 2394 | |
2393 | 2395 | |
2394 | 2396 |
def test_form_edit_autocomplete_list(pub): |
... | ... | |
6785 | 6787 | |
6786 | 6788 | |
6787 | 6789 |
def test_logged_errors(pub): |
6790 |
if not pub.is_using_postgresql(): |
|
6791 |
pytest.skip('this requires SQL') |
|
6792 |
return |
|
6793 | ||
6788 | 6794 |
Workflow.wipe() |
6789 | 6795 |
workflow = Workflow.get_default_workflow() |
6790 | 6796 |
workflow.id = '12' |
... | ... | |
6806 | 6812 |
formdef.fields = [] |
6807 | 6813 |
formdef.store() |
6808 | 6814 | |
6809 |
LoggedError.wipe()
|
|
6815 |
pub.loggederror_class.wipe()
|
|
6810 | 6816 | |
6811 | 6817 |
app = get_app(pub) |
6812 | 6818 |
resp = app.get('/test/') |
6813 | 6819 |
resp = resp.form.submit('submit').follow() |
6814 | 6820 |
resp = resp.form.submit('submit') |
6815 |
assert LoggedError.count() == 1
|
|
6821 |
assert pub.loggederror_class.count() == 1
|
|
6816 | 6822 | |
6817 | 6823 |
resp = app.get('/test/') |
6818 | 6824 |
resp = resp.form.submit('submit').follow() |
6819 | 6825 |
resp = resp.form.submit('submit') |
6820 |
assert LoggedError.count() == 1
|
|
6826 |
assert pub.loggederror_class.count() == 1
|
|
6821 | 6827 | |
6822 |
error = LoggedError.get_on_index(
|
|
6823 |
'34-12-just_submitted-_jump-failed-to-evaluate-condition-ZeroDivisionError-integer-division-or-modulo-by-zero',
|
|
6824 |
'tech_id')
|
|
6828 |
error = list(pub.loggederror_class.get_with_indexed_value(
|
|
6829 |
'tech_id',
|
|
6830 |
'34-12-just_submitted-_jump-failed-to-evaluate-condition-ZeroDivisionError-integer-division-or-modulo-by-zero'))[0]
|
|
6825 | 6831 |
assert error.occurences_count == 2 |
6826 | 6832 | |
6827 |
assert len(LoggedError.get_ids_with_indexed_value('formdef_id', '34')) == 1
|
|
6828 |
assert len(LoggedError.get_ids_with_indexed_value('formdef_id', 'X')) == 0
|
|
6833 |
assert len(list(pub.loggederror_class.get_with_indexed_value('formdef_id', '34'))) == 1
|
|
6834 |
assert len(list(pub.loggederror_class.get_with_indexed_value('formdef_id', 'X'))) == 0
|
|
6829 | 6835 | |
6830 |
assert len(LoggedError.get_ids_with_indexed_value('workflow_id', '12')) == 1
|
|
6831 |
assert len(LoggedError.get_ids_with_indexed_value('workflow_id', 'X')) == 0
|
|
6836 |
assert len(list(pub.loggederror_class.get_with_indexed_value('workflow_id', '12'))) == 1
|
|
6837 |
assert len(list(pub.loggederror_class.get_with_indexed_value('workflow_id', 'X'))) == 0
|
|
6832 | 6838 | |
6833 | 6839 | |
6834 | 6840 |
def test_formdata_named_wscall(http_requests, pub): |
... | ... | |
8876 | 8882 |
assert pq('.linked .foo_string').text() == 'zob' |
8877 | 8883 | |
8878 | 8884 | |
8879 |
def test_create_formdata_empty_item_ds_with_id_parameter(create_formdata): |
|
8880 |
LoggedError.wipe() |
|
8885 |
def test_create_formdata_empty_item_ds_with_id_parameter(pub, create_formdata): |
|
8886 |
if pub.is_using_postgresql(): |
|
8887 |
pub.loggederror_class.wipe() |
|
8881 | 8888 |
NamedDataSource.wipe() |
8882 | 8889 |
data_source = NamedDataSource(name='foobar') |
8883 | 8890 |
data_source.data_source = { |
... | ... | |
8905 | 8912 |
resp = resp.form.submit('submit') # -> submission |
8906 | 8913 |
resp = resp.follow() |
8907 | 8914 |
assert create_formdata['target_formdef'].data_class().count() == 0 |
8908 |
assert LoggedError.count() == 0 |
|
8915 |
if pub.is_using_postgresql(): |
|
8916 |
assert pub.loggederror_class.count() == 0 |
|
8909 | 8917 |
resp = resp.form.submit('button_resubmit') |
8910 |
assert LoggedError.count() == 0 |
|
8918 |
if pub.is_using_postgresql(): |
|
8919 |
assert pub.loggederror_class.count() == 0 |
|
8911 | 8920 | |
8912 | 8921 | |
8913 | 8922 |
def test_js_libraries(pub): |
tests/test_formdata.py | ||
---|---|---|
18 | 18 |
from wcs.conditions import Condition |
19 | 19 |
from wcs.formdef import FormDef |
20 | 20 |
from wcs.formdata import Evolution |
21 |
from wcs.logged_errors import LoggedError |
|
22 | 21 |
from wcs.roles import Role |
23 | 22 |
from wcs import sessions |
24 | 23 |
from wcs.variables import LazyFormData |
... | ... | |
1058 | 1057 |
assert queryset.count == 4 |
1059 | 1058 |
queryset = lazy_formdata.objects.filter_by('foo_foo').apply_filter_value('X') |
1060 | 1059 |
assert queryset.count == 0 |
1061 |
LoggedError.wipe() |
|
1060 |
if pub.is_using_postgresql(): |
|
1061 |
pub.loggederror_class.wipe() |
|
1062 | 1062 |
queryset = lazy_formdata.objects.filter_by('unknown').apply_filter_value('X') |
1063 | 1063 |
assert queryset.count == 0 |
1064 |
assert LoggedError.count() == 1 |
|
1065 |
logged_error = LoggedError.select()[0] |
|
1066 |
assert logged_error.summary == 'Invalid filter "unknown"' |
|
1064 |
if pub.is_using_postgresql(): |
|
1065 |
assert pub.loggederror_class.count() == 1 |
|
1066 |
logged_error = pub.loggederror_class.select()[0] |
|
1067 |
assert logged_error.summary == 'Invalid filter "unknown"' |
|
1067 | 1068 | |
1068 | 1069 |
# filter function on backoffice field |
1069 | 1070 |
queryset = lazy_formdata.objects.filter_by('backoffice_blah').apply_filter_value('plop1') |
tests/test_mail_templates.py | ||
---|---|---|
8 | 8 |
from quixote import cleanup |
9 | 9 |
from wcs.formdef import FormDef |
10 | 10 |
from wcs.fields import FileField |
11 |
from wcs.logged_errors import LoggedError |
|
12 | 11 |
from wcs.mail_templates import MailTemplate |
13 | 12 |
from wcs.workflows import Workflow, SendmailWorkflowStatusItem |
14 | 13 |
from wcs.qommon.http_request import HTTPRequest |
... | ... | |
193 | 192 |
assert workflow.possible_status[0].items[0].mail_template == 'test-mail-template' |
194 | 193 | |
195 | 194 | |
196 |
def test_workflow_send_mail_template(pub, superuser, mail_templates_option, emails): |
|
195 |
def test_workflow_send_mail_template_with_sql(superuser, mail_templates_option, emails): |
|
196 |
pub = create_temporary_pub(sql_mode=True) |
|
197 |
req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'}) |
|
198 |
pub.set_app_dir(req) |
|
199 |
pub._set_request(req) |
|
200 | ||
197 | 201 |
Workflow.wipe() |
198 | 202 |
MailTemplate.wipe() |
199 | 203 | |
... | ... | |
231 | 235 |
# check nothing is sent and an error is logged if the mail template is |
232 | 236 |
# missing |
233 | 237 |
emails.empty() |
234 |
LoggedError.wipe()
|
|
238 |
pub.loggederror_class.wipe()
|
|
235 | 239 |
MailTemplate.wipe() |
236 | 240 |
item.perform(formdata) |
237 | 241 |
pub.get_request().response.process_after_jobs() |
238 | 242 |
assert emails.count() == 0 |
239 |
assert LoggedError.count() == 1
|
|
240 |
logged_error = LoggedError.select()[0]
|
|
243 |
assert pub.loggederror_class.count() == 1
|
|
244 |
logged_error = pub.loggederror_class.select()[0]
|
|
241 | 245 |
assert logged_error.summary == 'reference to invalid mail template test-mail-template in status Status1' |
242 | 246 | |
243 | 247 |
tests/test_workflows.py | ||
---|---|---|
32 | 32 |
ItemsField, CommentField, EmailField, PageField, TitleField, |
33 | 33 |
SubtitleField, TextField, BoolField, TableField) |
34 | 34 |
from wcs.formdata import Evolution |
35 |
from wcs.logged_errors import LoggedError |
|
36 | 35 |
from wcs.roles import Role |
37 | 36 |
from wcs.workflows import (Workflow, WorkflowStatusItem, |
38 | 37 |
SendmailWorkflowStatusItem, SendSMSWorkflowStatusItem, |
... | ... | |
314 | 313 |
assert item.must_jump(formdata) is False |
315 | 314 | |
316 | 315 | |
317 |
def test_jump_bad_python_condition(pub): |
|
316 |
def test_jump_bad_python_condition(two_pubs): |
|
317 |
if not two_pubs.is_using_postgresql(): |
|
318 |
pytest.skip('this requires SQL') |
|
319 |
return |
|
320 | ||
318 | 321 |
FormDef.wipe() |
319 | 322 |
formdef = FormDef() |
320 | 323 |
formdef.name = 'foobar' |
321 | 324 |
formdef.store() |
322 |
pub.substitutions.feed(formdef)
|
|
325 |
two_pubs.substitutions.feed(formdef)
|
|
323 | 326 |
formdef.data_class().wipe() |
324 | 327 |
formdata = formdef.data_class()() |
325 | 328 |
item = JumpWorkflowStatusItem() |
326 | 329 | |
327 |
LoggedError.wipe()
|
|
330 |
two_pubs.loggederror_class.wipe()
|
|
328 | 331 |
item.condition = {'type': 'python', 'value': 'form_var_foobar == 0'} |
329 | 332 |
assert item.must_jump(formdata) is False |
330 |
assert LoggedError.count() == 1
|
|
331 |
logged_error = LoggedError.select()[0]
|
|
333 |
assert two_pubs.loggederror_class.count() == 1
|
|
334 |
logged_error = two_pubs.loggederror_class.select()[0]
|
|
332 | 335 |
assert logged_error.summary == 'Failed to evaluate condition' |
333 | 336 |
assert logged_error.exception_class == 'NameError' |
334 | 337 |
assert logged_error.exception_message == "name 'form_var_foobar' is not defined" |
335 | 338 |
assert logged_error.expression == 'form_var_foobar == 0' |
336 | 339 |
assert logged_error.expression_type == 'python' |
337 | 340 | |
338 |
LoggedError.wipe()
|
|
341 |
two_pubs.loggederror_class.wipe()
|
|
339 | 342 |
item.condition = {'type': 'python', 'value': '~ invalid ~'} |
340 | 343 |
assert item.must_jump(formdata) is False |
341 |
assert LoggedError.count() == 1
|
|
342 |
logged_error = LoggedError.select()[0]
|
|
344 |
assert two_pubs.loggederror_class.count() == 1
|
|
345 |
logged_error = two_pubs.loggederror_class.select()[0]
|
|
343 | 346 |
assert logged_error.summary == 'Failed to evaluate condition' |
344 | 347 |
assert logged_error.exception_class == 'SyntaxError' |
345 | 348 |
assert logged_error.exception_message == 'unexpected EOF while parsing (<string>, line 1)' |
... | ... | |
347 | 350 |
assert logged_error.expression_type == 'python' |
348 | 351 | |
349 | 352 | |
350 |
def test_jump_django_conditions(pub):
|
|
353 |
def test_jump_django_conditions(two_pubs):
|
|
351 | 354 |
FormDef.wipe() |
352 | 355 |
formdef = FormDef() |
353 | 356 |
formdef.name = 'foobar' |
... | ... | |
355 | 358 |
formdef.store() |
356 | 359 |
formdata = formdef.data_class()() |
357 | 360 |
formdata.data = {'1': 'hello'} |
358 |
pub.substitutions.feed(formdata)
|
|
361 |
two_pubs.substitutions.feed(formdata)
|
|
359 | 362 |
item = JumpWorkflowStatusItem() |
360 | 363 | |
361 |
LoggedError.wipe() |
|
364 |
if two_pubs.is_using_postgresql(): |
|
365 |
two_pubs.loggederror_class.wipe() |
|
362 | 366 |
item.condition = {'type': 'django', 'value': '1 < 2'} |
363 | 367 |
assert item.must_jump(formdata) is True |
364 | 368 | |
... | ... | |
371 | 375 |
item.condition = {'type': 'django', 'value': 'form_var_foo|first|upper == "X"'} |
372 | 376 |
assert item.must_jump(formdata) is False |
373 | 377 | |
374 |
assert LoggedError.count() == 0 |
|
378 |
if two_pubs.is_using_postgresql(): |
|
379 |
assert two_pubs.loggederror_class.count() == 0 |
|
375 | 380 | |
376 | 381 |
item.condition = {'type': 'django', 'value': '~ invalid ~'} |
377 | 382 |
assert item.must_jump(formdata) is False |
378 |
assert LoggedError.count() == 1 |
|
379 |
logged_error = LoggedError.select()[0] |
|
380 |
assert logged_error.summary == 'Failed to evaluate condition' |
|
381 |
assert logged_error.exception_class == 'TemplateSyntaxError' |
|
382 |
assert logged_error.exception_message == "Could not parse the remainder: '~' from '~'" |
|
383 |
assert logged_error.expression == '~ invalid ~' |
|
384 |
assert logged_error.expression_type == 'django' |
|
383 |
if two_pubs.is_using_postgresql(): |
|
384 |
assert two_pubs.loggederror_class.count() == 1 |
|
385 |
logged_error = two_pubs.loggederror_class.select()[0] |
|
386 |
assert logged_error.summary == 'Failed to evaluate condition' |
|
387 |
assert logged_error.exception_class == 'TemplateSyntaxError' |
|
388 |
assert logged_error.exception_message == "Could not parse the remainder: '~' from '~'" |
|
389 |
assert logged_error.expression == '~ invalid ~' |
|
390 |
assert logged_error.expression_type == 'django' |
|
385 | 391 | |
386 | 392 | |
387 | 393 |
def test_check_auth(pub): |
... | ... | |
455 | 461 | |
456 | 462 | |
457 | 463 |
def test_dispatch_auto(pub): |
458 |
LoggedError.wipe() |
|
464 |
if pub.is_using_postgresql(): |
|
465 |
pub.loggederror_class.wipe() |
|
459 | 466 | |
460 | 467 |
formdef = FormDef() |
461 | 468 |
formdef.name = 'baz' |
... | ... | |
526 | 533 |
pub.substitutions.feed(formdata) |
527 | 534 |
item.perform(formdata) |
528 | 535 |
assert not formdata.workflow_roles |
529 |
assert LoggedError.count() == 1 |
|
530 |
error = LoggedError.select()[0] |
|
531 |
assert error.tech_id == '%s-_default-error-in-dispatch-missing-role-foobar' % formdef.id |
|
532 |
assert error.formdef_id == formdef.id |
|
533 |
assert error.workflow_id == '_default' |
|
534 |
assert error.summary == 'error in dispatch, missing role (foobar)' |
|
535 |
assert error.occurences_count == 1 |
|
536 |
if pub.is_using_postgresql(): |
|
537 |
assert pub.loggederror_class.count() == 1 |
|
538 |
error = pub.loggederror_class.select()[0] |
|
539 |
assert error.tech_id == '%s-_default-error-in-dispatch-missing-role-foobar' % formdef.id |
|
540 |
assert error.formdef_id == formdef.id |
|
541 |
assert error.workflow_id == '_default' |
|
542 |
assert error.summary == 'error in dispatch, missing role (foobar)' |
|
543 |
assert error.occurences_count == 1 |
|
536 | 544 | |
537 | 545 | |
538 | 546 |
def test_dispatch_computed(pub): |
539 |
LoggedError.wipe() |
|
547 |
if pub.is_using_postgresql(): |
|
548 |
pub.loggederror_class.wipe() |
|
540 | 549 | |
541 | 550 |
formdef = FormDef() |
542 | 551 |
formdef.name = 'baz' |
... | ... | |
571 | 580 |
item.role_id = '="foobar"' |
572 | 581 |
item.perform(formdata) |
573 | 582 |
assert not formdata.workflow_roles |
574 |
assert LoggedError.count() == 1 |
|
575 |
error = LoggedError.select()[0] |
|
576 |
assert error.tech_id == '%s-_default-error-in-dispatch-missing-role-foobar' % formdef.id |
|
577 |
assert error.formdef_id == formdef.id |
|
578 |
assert error.workflow_id == '_default' |
|
579 |
assert error.summary == 'error in dispatch, missing role (="foobar")' |
|
580 |
assert error.occurences_count == 1 |
|
583 |
if pub.is_using_postgresql(): |
|
584 |
assert pub.loggederror_class.count() == 1 |
|
585 |
error = pub.loggederror_class.select()[0] |
|
586 |
assert error.tech_id == '%s-_default-error-in-dispatch-missing-role-foobar' % formdef.id |
|
587 |
assert error.formdef_id == formdef.id |
|
588 |
assert error.workflow_id == '_default' |
|
589 |
assert error.summary == 'error in dispatch, missing role (="foobar")' |
|
590 |
assert error.occurences_count == 1 |
|
581 | 591 | |
582 | 592 | |
583 | 593 |
def test_roles(pub): |
... | ... | |
2293 | 2303 | |
2294 | 2304 | |
2295 | 2305 |
def test_jump_missing_previous_mark(two_pubs): |
2306 |
if not two_pubs.is_using_postgresql(): |
|
2307 |
pytest.skip('this requires SQL') |
|
2308 |
return |
|
2309 | ||
2296 | 2310 |
workflow = Workflow(name='jump-mark') |
2297 | 2311 |
st1 = workflow.add_status('Status1', 'st1') |
2298 | 2312 | |
... | ... | |
2317 | 2331 |
formdata.store() |
2318 | 2332 | |
2319 | 2333 |
time.sleep(0.3) |
2320 |
LoggedError.wipe()
|
|
2334 |
two_pubs.loggederror_class.wipe()
|
|
2321 | 2335 |
_apply_timeouts(two_pubs) |
2322 |
assert LoggedError.count() == 1
|
|
2336 |
assert two_pubs.loggederror_class.count() == 1
|
|
2323 | 2337 | |
2324 | 2338 | |
2325 | 2339 |
def test_sms(pub, sms_mocking): |
... | ... | |
3745 | 3759 |
def test_set_backoffice_field(http_requests, two_pubs): |
3746 | 3760 |
Workflow.wipe() |
3747 | 3761 |
FormDef.wipe() |
3748 |
LoggedError.wipe() |
|
3762 |
if two_pubs.is_using_postgresql(): |
|
3763 |
two_pubs.loggederror_class.wipe() |
|
3749 | 3764 |
wf = Workflow(name='xxx') |
3750 | 3765 |
wf.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(wf) |
3751 | 3766 |
wf.backoffice_fields_formdef.fields = [ |
... | ... | |
3844 | 3859 |
formdata = formdef.data_class().get(formdata.id) |
3845 | 3860 |
assert formdata.data['bo1'] == '' |
3846 | 3861 | |
3847 |
assert LoggedError.count() == 0 |
|
3862 |
if two_pubs.is_using_postgresql(): |
|
3863 |
assert two_pubs.loggederror_class.count() == 0 |
|
3848 | 3864 | |
3849 | 3865 |
item.fields = [{'field_id': 'bo1', 'value': '= ~ invalid python ~'}] |
3850 | 3866 |
item.perform(formdata) |
3851 | 3867 |
formdata = formdef.data_class().get(formdata.id) |
3852 |
assert LoggedError.count() == 1 |
|
3853 |
logged_error = LoggedError.select()[0] |
|
3854 |
assert logged_error.summary == 'Failed to compute Python expression' |
|
3855 |
assert logged_error.formdata_id == str(formdata.id) |
|
3856 |
assert logged_error.expression == ' ~ invalid python ~' |
|
3857 |
assert logged_error.expression_type == 'python' |
|
3858 |
assert logged_error.exception_class == 'SyntaxError' |
|
3859 |
assert logged_error.exception_message == 'invalid syntax (<string>, line 1)' |
|
3868 |
if two_pubs.is_using_postgresql(): |
|
3869 |
assert two_pubs.loggederror_class.count() == 1 |
|
3870 |
logged_error = two_pubs.loggederror_class.select()[0] |
|
3871 |
assert logged_error.summary == 'Failed to compute Python expression' |
|
3872 |
assert logged_error.formdata_id == str(formdata.id) |
|
3873 |
assert logged_error.expression == ' ~ invalid python ~' |
|
3874 |
assert logged_error.expression_type == 'python' |
|
3875 |
assert logged_error.exception_class == 'SyntaxError' |
|
3876 |
assert logged_error.exception_message == 'invalid syntax (<string>, line 1)' |
|
3860 | 3877 | |
3861 |
LoggedError.wipe() |
|
3878 |
if two_pubs.is_using_postgresql(): |
|
3879 |
two_pubs.loggederror_class.wipe() |
|
3862 | 3880 |
item.fields = [{'field_id': 'bo1', 'value': '{% if bad django %}'}] |
3863 | 3881 |
item.perform(formdata) |
3864 | 3882 |
formdata = formdef.data_class().get(formdata.id) |
3865 |
assert LoggedError.count() == 1 |
|
3866 |
logged_error = LoggedError.select()[0] |
|
3867 |
assert logged_error.summary == 'Failed to compute template' |
|
3868 |
assert logged_error.formdata_id == str(formdata.id) |
|
3869 |
assert logged_error.expression == '{% if bad django %}' |
|
3870 |
assert logged_error.expression_type == 'template' |
|
3871 |
assert logged_error.exception_class == 'TemplateError' |
|
3872 |
assert logged_error.exception_message.startswith('syntax error in Django template') |
|
3883 |
if two_pubs.is_using_postgresql(): |
|
3884 |
assert two_pubs.loggederror_class.count() == 1 |
|
3885 |
logged_error = two_pubs.loggederror_class.select()[0] |
|
3886 |
assert logged_error.summary == 'Failed to compute template' |
|
3887 |
assert logged_error.formdata_id == str(formdata.id) |
|
3888 |
assert logged_error.expression == '{% if bad django %}' |
|
3889 |
assert logged_error.expression_type == 'template' |
|
3890 |
assert logged_error.exception_class == 'TemplateError' |
|
3891 |
assert logged_error.exception_message.startswith('syntax error in Django template') |
|
3873 | 3892 | |
3874 | 3893 | |
3875 | 3894 |
def test_set_backoffice_field_file(http_requests, two_pubs): |
... | ... | |
4036 | 4055 |
item.parent = st1 |
4037 | 4056 |
item.fields = [{'field_id': 'bo1', 'value': value}] |
4038 | 4057 | |
4039 |
LoggedError.wipe() |
|
4058 |
if two_pubs.is_using_postgresql(): |
|
4059 |
two_pubs.loggederror_class.wipe() |
|
4040 | 4060 |
item.perform(formdata) |
4041 | 4061 | |
4042 | 4062 |
formdata = formdef.data_class().get(formdata.id) |
4043 | 4063 |
assert formdata.data['bo1'].base_filename == 'hello.txt' |
4044 | 4064 |
assert formdata.data['bo1'].get_content() == b'HELLO WORLD' |
4045 |
assert LoggedError.count() == 1 |
|
4046 |
logged_error = LoggedError.select()[0] |
|
4047 |
assert logged_error.summary.startswith('Failed to convert') |
|
4048 |
assert logged_error.formdata_id == str(formdata.id) |
|
4049 |
assert logged_error.exception_class == 'ValueError' |
|
4065 |
if two_pubs.is_using_postgresql(): |
|
4066 |
assert two_pubs.loggederror_class.count() == 1 |
|
4067 |
logged_error = two_pubs.loggederror_class.select()[0] |
|
4068 |
assert logged_error.summary.startswith('Failed to convert') |
|
4069 |
assert logged_error.formdata_id == str(formdata.id) |
|
4070 |
assert logged_error.exception_class == 'ValueError' |
|
4050 | 4071 | |
4051 | 4072 |
# check wrong field |
4052 | 4073 |
item = SetBackofficeFieldsWorkflowStatusItem() |
... | ... | |
4343 | 4364 |
def test_set_backoffice_field_date(two_pubs): |
4344 | 4365 |
Workflow.wipe() |
4345 | 4366 |
FormDef.wipe() |
4346 |
LoggedError.wipe() |
|
4367 |
if two_pubs.is_using_postgresql(): |
|
4368 |
two_pubs.loggederror_class.wipe() |
|
4347 | 4369 |
wf = Workflow(name='xxx') |
4348 | 4370 |
wf.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(wf) |
4349 | 4371 |
st1 = wf.add_status('Status1') |
... | ... | |
4391 | 4413 |
assert datetime.date(*formdata.data['bo1'][:3]) == datetime.date(2017, 3, 23) |
4392 | 4414 | |
4393 | 4415 |
# invalid values => do nothing |
4394 |
assert LoggedError.count() == 0 |
|
4416 |
if two_pubs.is_using_postgresql(): |
|
4417 |
assert two_pubs.loggederror_class.count() == 0 |
|
4395 | 4418 |
for value in ('plop', '={}', '=[]'): |
4396 | 4419 |
item = SetBackofficeFieldsWorkflowStatusItem() |
4397 | 4420 |
item.parent = st1 |
4398 | 4421 |
item.fields = [{'field_id': 'bo1', 'value': value}] |
4399 | 4422 | |
4400 |
LoggedError.wipe() |
|
4423 |
if two_pubs.is_using_postgresql(): |
|
4424 |
two_pubs.loggederror_class.wipe() |
|
4401 | 4425 |
item.perform(formdata) |
4402 | 4426 |
formdata = formdef.data_class().get(formdata.id) |
4403 | 4427 |
assert datetime.date(*formdata.data['bo1'][:3]) == datetime.date(2017, 3, 23) |
4404 |
assert LoggedError.count() == 1 |
|
4405 |
assert LoggedError.select()[0].summary.startswith('Failed to convert') |
|
4428 |
if two_pubs.is_using_postgresql(): |
|
4429 |
assert two_pubs.loggederror_class.count() == 1 |
|
4430 |
assert two_pubs.loggederror_class.select()[0].summary.startswith('Failed to convert') |
|
4406 | 4431 | |
4407 | 4432 |
# None : empty date |
4408 | 4433 |
item = SetBackofficeFieldsWorkflowStatusItem() |
... | ... | |
4417 | 4442 |
def test_set_backoffice_field_boolean(two_pubs): |
4418 | 4443 |
Workflow.wipe() |
4419 | 4444 |
FormDef.wipe() |
4420 |
LoggedError.wipe() |
|
4421 | 4445 |
wf = Workflow(name='xxx') |
4422 | 4446 |
wf.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(wf) |
4423 | 4447 |
st1 = wf.add_status('Status1') |
... | ... | |
4572 | 4596 | |
4573 | 4597 | |
4574 | 4598 |
def test_workflow_action_condition(two_pubs): |
4575 |
pub = two_pubs |
|
4576 |
pub._set_request(None) # to avoid after jobs |
|
4599 |
two_pubs._set_request(None) # to avoid after jobs |
|
4577 | 4600 |
workflow = Workflow(name='jump condition migration') |
4578 | 4601 |
st1 = workflow.add_status('Status1', 'st1') |
4579 | 4602 |
workflow.store() |
... | ... | |
4581 | 4604 |
role = Role(name='bar1') |
4582 | 4605 |
role.store() |
4583 | 4606 | |
4584 |
user = pub.user_class()
|
|
4607 |
user = two_pubs.user_class()
|
|
4585 | 4608 |
user.roles = [role.id] |
4586 | 4609 |
user.store() |
4587 | 4610 | |
... | ... | |
4616 | 4639 |
choice.condition = {'type': 'python', 'value': 'form_var_foo == "foo"'} |
4617 | 4640 |
workflow.store() |
4618 | 4641 | |
4619 |
with pub.substitutions.temporary_feed(formdata1):
|
|
4642 |
with two_pubs.substitutions.temporary_feed(formdata1):
|
|
4620 | 4643 |
assert FormDef.get(formdef.id).data_class().get(formdata1.id).get_actions_roles() == set([role.id]) |
4621 |
with pub.substitutions.temporary_feed(formdata2):
|
|
4644 |
with two_pubs.substitutions.temporary_feed(formdata2):
|
|
4622 | 4645 |
assert FormDef.get(formdef.id).data_class().get(formdata2.id).get_actions_roles() == set() |
4623 | 4646 | |
4624 | 4647 |
assert len(FormDef.get(formdef.id).data_class().get_actionable_ids([role.id])) == 1 |
... | ... | |
4633 | 4656 |
assert len(FormDef.get(formdef.id).data_class().get_actionable_ids([role.id])) == 2 |
4634 | 4657 | |
4635 | 4658 |
# bad condition |
4636 |
LoggedError.wipe() |
|
4659 |
if two_pubs.is_using_postgresql(): |
|
4660 |
two_pubs.loggederror_class.wipe() |
|
4637 | 4661 |
choice.condition = {'type': 'python', 'value': 'foobar == barfoo'} |
4638 | 4662 |
workflow.store() |
4639 | 4663 |
assert len(FormDef.get(formdef.id).data_class().get_actionable_ids([role.id])) == 0 |
4640 |
assert LoggedError.count() == 1 |
|
4641 |
logged_error = LoggedError.select()[0] |
|
4642 |
assert logged_error.occurences_count > 1 # should be 2... == 12 with pickle, 4 with sql |
|
4643 |
assert logged_error.summary == 'Failed to evaluate condition' |
|
4644 |
assert logged_error.exception_class == 'NameError' |
|
4645 |
assert logged_error.exception_message == "name 'foobar' is not defined" |
|
4646 |
assert logged_error.expression == 'foobar == barfoo' |
|
4647 |
assert logged_error.expression_type == 'python' |
|
4664 |
if two_pubs.is_using_postgresql(): |
|
4665 |
assert two_pubs.loggederror_class.count() == 1 |
|
4666 |
logged_error = two_pubs.loggederror_class.select()[0] |
|
4667 |
assert logged_error.occurences_count > 1 # should be 2... == 12 with pickle, 4 with sql |
|
4668 |
assert logged_error.summary == 'Failed to evaluate condition' |
|
4669 |
assert logged_error.exception_class == 'NameError' |
|
4670 |
assert logged_error.exception_message == "name 'foobar' is not defined" |
|
4671 |
assert logged_error.expression == 'foobar == barfoo' |
|
4672 |
assert logged_error.expression_type == 'python' |
|
4648 | 4673 | |
4649 | 4674 | |
4650 | 4675 |
def test_notifications(pub, http_requests): |
... | ... | |
4774 | 4799 |
formdef.fields = [] |
4775 | 4800 |
formdef.workflow_id = workflow.id |
4776 | 4801 |
formdef.store() |
4802 |
formdef.data_class().wipe() |
|
4777 | 4803 | |
4778 | 4804 |
for i in range(5): |
4779 | 4805 |
formdata = formdef.data_class()() |
... | ... | |
4811 | 4837 |
assert 'http://example.net/foobar/%s/status (New)' % formdata.id in emails.emails['New arrivals']['payload'] |
4812 | 4838 | |
4813 | 4839 | |
4814 |
def test_create_formdata(pub):
|
|
4840 |
def test_create_formdata(two_pubs):
|
|
4815 | 4841 |
FormDef.wipe() |
4816 |
LoggedError.wipe() |
|
4817 |
pub.tracking_code_class.wipe() |
|
4842 |
if two_pubs.is_using_postgresql(): |
|
4843 |
two_pubs.loggederror_class.wipe() |
|
4844 |
two_pubs.tracking_code_class.wipe() |
|
4818 | 4845 | |
4819 | 4846 |
target_formdef = FormDef() |
4820 | 4847 |
target_formdef.name = 'target form' |
... | ... | |
4849 | 4876 |
formdata.just_created() |
4850 | 4877 | |
4851 | 4878 |
assert target_formdef.data_class().count() == 0 |
4852 |
assert LoggedError.count() == 0 |
|
4879 |
if two_pubs.is_using_postgresql(): |
|
4880 |
assert two_pubs.loggederror_class.count() == 0 |
|
4853 | 4881 |
# check unconfigure action do nothing |
4854 | 4882 |
formdata.perform_workflow() |
4855 | 4883 |
assert target_formdef.data_class().count() == 0 |
... | ... | |
4860 | 4888 |
formdata.perform_workflow() |
4861 | 4889 |
assert target_formdef.data_class().count() == 1 |
4862 | 4890 | |
4863 |
errors = LoggedError.select() |
|
4864 |
assert len(errors) == 2 |
|
4865 |
assert any('form_var_toto_string' in (error.exception_message or '') for error in errors) |
|
4866 |
assert any('Missing field' in error.summary for error in errors) |
|
4891 |
if two_pubs.is_using_postgresql(): |
|
4892 |
errors = two_pubs.loggederror_class.select() |
|
4893 |
assert len(errors) == 2 |
|
4894 |
assert any('form_var_toto_string' in (error.exception_message or '') for error in errors) |
|
4895 |
assert any('Missing field' in error.summary for error in errors) |
|
4867 | 4896 | |
4868 | 4897 |
# no tracking code has been created |
4869 | 4898 |
created_formdata = target_formdef.data_class().select()[0] |
4870 | 4899 |
assert created_formdata.tracking_code is None |
4871 |
assert pub.tracking_code_class.count() == 0
|
|
4900 |
assert two_pubs.tracking_code_class.count() == 0
|
|
4872 | 4901 |
# now we want one |
4873 | 4902 |
target_formdef.enable_tracking_codes = True |
4874 | 4903 |
target_formdef.store() |
... | ... | |
4878 | 4907 |
assert target_formdef.data_class().count() == 1 |
4879 | 4908 |
created_formdata = target_formdef.data_class().select()[0] |
4880 | 4909 |
assert created_formdata.tracking_code is not None |
4881 |
assert pub.tracking_code_class.count() == 1
|
|
4882 |
assert pub.tracking_code_class.select()[0].formdef_id == target_formdef.id
|
|
4883 |
assert pub.tracking_code_class.select()[0].formdata_id == created_formdata.id
|
|
4910 |
assert two_pubs.tracking_code_class.count() == 1
|
|
4911 |
assert two_pubs.tracking_code_class.select()[0].formdef_id == target_formdef.id
|
|
4912 |
assert two_pubs.tracking_code_class.select()[0].formdata_id == str(created_formdata.id)
|
|
4884 | 4913 | |
4885 | 4914 |
create.condition = {'type': 'python', 'value': '1 == 2'} |
4886 | 4915 |
wf.store() |
... | ... | |
4891 | 4920 |
assert target_formdef.data_class().count() == 0 |
4892 | 4921 | |
4893 | 4922 | |
4894 |
def test_create_carddata(pub):
|
|
4923 |
def test_create_carddata(two_pubs):
|
|
4895 | 4924 |
CardDef.wipe() |
4896 | 4925 |
FormDef.wipe() |
4897 |
LoggedError.wipe() |
|
4926 |
if two_pubs.is_using_postgresql(): |
|
4927 |
two_pubs.loggederror_class.wipe() |
|
4898 | 4928 | |
4899 | 4929 |
carddef = CardDef() |
4900 | 4930 |
carddef.name = 'My card' |
... | ... | |
4946 | 4976 | |
4947 | 4977 |
assert carddef.data_class().count() == 1 |
4948 | 4978 | |
4949 |
errors = LoggedError.select() |
|
4950 |
assert len(errors) == 2 |
|
4951 |
assert any('form_var_undefined' in (error.exception_message or '') for error in errors) |
|
4952 |
assert any('invalid date value' in (error.exception_message or '') for error in errors) |
|
4979 |
if two_pubs.is_using_postgresql(): |
|
4980 |
errors = two_pubs.loggederror_class.select() |
|
4981 |
assert len(errors) == 2 |
|
4982 |
assert any('form_var_undefined' in (error.exception_message or '') for error in errors) |
|
4983 |
assert any('invalid date value' in (error.exception_message or '') for error in errors) |
|
4953 | 4984 | |
4954 | 4985 |
formdata = formdef.data_class()() |
4955 | 4986 |
today = datetime.date.today() |
4956 | 4987 | |
4957 | 4988 |
formdata.data = {'1': 'item1', |
4958 | 4989 |
'1_display': 'item1', |
4959 |
'2': today} |
|
4990 |
'2': today.timetuple()}
|
|
4960 | 4991 |
formdata.just_created() |
4961 | 4992 |
formdata.perform_workflow() |
4962 | 4993 | |
... | ... | |
4974 | 5005 |
assert carddef.data_class().count() == 0 |
4975 | 5006 | |
4976 | 5007 | |
4977 |
def test_call_external_workflow_with_evolution_linked_object(pub):
|
|
5008 |
def test_call_external_workflow_with_evolution_linked_object(two_pubs):
|
|
4978 | 5009 |
FormDef.wipe() |
4979 | 5010 |
CardDef.wipe() |
4980 |
LoggedError.wipe() |
|
5011 |
if two_pubs.is_using_postgresql(): |
|
5012 |
two_pubs.loggederror_class.wipe() |
|
4981 | 5013 | |
4982 | 5014 |
external_wf = Workflow(name='External Workflow') |
4983 | 5015 |
st1 = external_wf.add_status(name='New') |
... | ... | |
5056 | 5088 | |
5057 | 5089 |
# remove external formdata |
5058 | 5090 |
perform_items([action], formdata) |
5059 |
assert LoggedError.count() == 0 |
|
5091 |
if two_pubs.is_using_postgresql(): |
|
5092 |
assert two_pubs.loggederror_class.count() == 0 |
|
5060 | 5093 |
assert external_formdef.data_class().count() == 0 |
5061 | 5094 |
assert external_carddef.data_class().count() == 1 |
5062 | 5095 | |
5063 | 5096 |
# formdata is already deleted: cannot find it again |
5064 | 5097 |
perform_items([action], formdata) |
5065 |
assert LoggedError.count() == 1 |
|
5066 |
logged_error = LoggedError.select()[0] |
|
5067 |
assert logged_error.summary == 'Could not find linked "External Form" object by id %s' % external_formdata.id |
|
5068 |
assert logged_error.exception_class == 'KeyError' |
|
5069 |
assert logged_error.status_item_id == action.id |
|
5098 |
if two_pubs.is_using_postgresql(): |
|
5099 |
assert two_pubs.loggederror_class.count() == 1 |
|
5100 |
logged_error = two_pubs.loggederror_class.select()[0] |
|
5101 |
assert logged_error.summary == 'Could not find linked "External Form" object by id %s' % external_formdata.id |
|
5102 |
assert logged_error.exception_class == 'KeyError' |
|
5103 |
assert logged_error.status_item_id == action.id |
|
5070 | 5104 | |
5071 | 5105 |
# try remove an unexisting carddef: do nothing |
5072 | 5106 |
unused_carddef = CardDef() |
... | ... | |
5074 | 5108 |
unused_carddef.fields = [] |
5075 | 5109 |
unused_carddef.workflow = external_wf |
5076 | 5110 |
unused_carddef.store() |
5077 |
LoggedError.wipe() |
|
5111 |
if two_pubs.is_using_postgresql(): |
|
5112 |
two_pubs.loggederror_class.wipe() |
|
5078 | 5113 |
action.slug = 'carddef:%s' % unused_carddef.url_name |
5079 | 5114 |
wf.store() |
5080 | 5115 |
perform_items([action], formdata) |
5081 |
assert LoggedError.count() == 0 |
|
5116 |
if two_pubs.is_using_postgresql(): |
|
5117 |
assert two_pubs.loggederror_class.count() == 0 |
|
5082 | 5118 |
assert external_formdef.data_class().count() == 0 |
5083 | 5119 |
assert external_carddef.data_class().count() == 1 |
5084 | 5120 |
# remove the right carddef |
5085 | 5121 |
action.slug = 'carddef:%s' % external_carddef.url_name |
5086 | 5122 |
wf.store() |
5087 | 5123 |
perform_items([action], formdata) |
5088 |
assert LoggedError.count() == 0 |
|
5124 |
if two_pubs.is_using_postgresql(): |
|
5125 |
assert two_pubs.loggederror_class.count() == 0 |
|
5089 | 5126 |
assert external_formdef.data_class().count() == 0 |
5090 | 5127 |
assert external_carddef.data_class().count() == 0 |
5091 | 5128 | |
5092 | 5129 | |
5093 |
def test_call_external_workflow_with_data_sourced_object(pub):
|
|
5130 |
def test_call_external_workflow_with_data_sourced_object(two_pubs):
|
|
5094 | 5131 |
FormDef.wipe() |
5095 | 5132 |
CardDef.wipe() |
5096 |
LoggedError.wipe() |
|
5133 |
if two_pubs.is_using_postgresql(): |
|
5134 |
two_pubs.loggederror_class.wipe() |
|
5097 | 5135 | |
5098 | 5136 |
carddef_wf = Workflow(name='Carddef Workflow') |
5099 | 5137 |
carddef_wf.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(carddef_wf) |
... | ... | |
5154 | 5192 |
formdef.workflow = wf |
5155 | 5193 |
formdef.store() |
5156 | 5194 | |
5157 |
assert LoggedError.count() == 0 |
|
5195 |
if two_pubs.is_using_postgresql(): |
|
5196 |
assert two_pubs.loggederror_class.count() == 0 |
|
5158 | 5197 |
assert carddef.data_class().count() == 1 |
5159 | 5198 | |
5160 | 5199 |
formdata = formdef.data_class()() |
... | ... | |
5164 | 5203 |
formdata.perform_workflow() |
5165 | 5204 | |
5166 | 5205 |
perform_items([update_action], formdata) |
5167 |
assert LoggedError.count() == 0 |
|
5206 |
if two_pubs.is_using_postgresql(): |
|
5207 |
assert two_pubs.loggederror_class.count() == 0 |
|
5168 | 5208 |
assert carddef.data_class().count() == 1 |
5169 | 5209 |
data = carddef.data_class().select()[0] |
5170 | 5210 |
assert data.data['bo0'] == '1' |
... | ... | |
5174 | 5214 |
assert data.data['bo0'] == '2' |
5175 | 5215 | |
5176 | 5216 |
perform_items([delete_action], formdata) |
5177 |
assert LoggedError.count() == 0 |
|
5217 |
if two_pubs.is_using_postgresql(): |
|
5218 |
assert two_pubs.loggederror_class.count() == 0 |
|
5178 | 5219 |
assert carddef.data_class().count() == 0 |
5179 | 5220 | |
5180 | 5221 |
perform_items([delete_action], formdata) |
5181 |
assert LoggedError.count() == 1 |
|
5182 |
logged_error = LoggedError.select()[0] |
|
5183 |
assert logged_error.summary == 'Could not find linked "Data" object by id %s' % carddata.id |
|
5184 |
assert logged_error.exception_class == 'KeyError' |
|
5222 |
if two_pubs.is_using_postgresql(): |
|
5223 |
assert two_pubs.loggederror_class.count() == 1 |
|
5224 |
logged_error = two_pubs.loggederror_class.select()[0] |
|
5225 |
assert logged_error.summary == 'Could not find linked "Data" object by id %s' % carddata.id |
|
5226 |
assert logged_error.exception_class == 'KeyError' |
|
5185 | 5227 | |
5186 | 5228 | |
5187 | 5229 |
def test_call_external_workflow_with_parent_object(pub): |
5188 | 5230 |
FormDef.wipe() |
5189 | 5231 |
CardDef.wipe() |
5190 |
LoggedError.wipe() |
|
5191 | 5232 | |
5192 | 5233 |
# carddef workflow, with global action to increment a counter in its |
5193 | 5234 |
# backoffice fields. |
... | ... | |
5274 | 5315 |
def test_call_external_workflow_use_caller_variable(pub): |
5275 | 5316 |
FormDef.wipe() |
5276 | 5317 |
CardDef.wipe() |
5277 |
LoggedError.wipe() |
|
5278 | 5318 | |
5279 | 5319 |
# carddef workflow, with global action to set a value in a backoffice field |
5280 | 5320 |
carddef_wf = Workflow(name='Carddef Workflow') |
... | ... | |
5349 | 5389 |
def test_edit_carddata_with_data_sourced_object(pub): |
5350 | 5390 |
FormDef.wipe() |
5351 | 5391 |
CardDef.wipe() |
5352 |
LoggedError.wipe() |
|
5353 | 5392 | |
5354 | 5393 |
datasource = { |
5355 | 5394 |
'type': 'formula', |
... | ... | |
5462 | 5501 |
def test_edit_carddata_with_linked_object(pub): |
5463 | 5502 |
FormDef.wipe() |
5464 | 5503 |
CardDef.wipe() |
5465 |
LoggedError.wipe() |
|
5466 | 5504 | |
5467 | 5505 |
carddef = CardDef() |
5468 | 5506 |
carddef.name = 'Parent' |
tests/test_wscall.py | ||
---|---|---|
3 | 3 | |
4 | 4 |
from wcs import fields |
5 | 5 |
from wcs.formdef import FormDef |
6 |
from wcs.logged_errors import LoggedError |
|
7 | 6 |
from wcs.qommon.http_request import HTTPRequest |
8 | 7 |
from wcs.qommon.template import Template |
9 | 8 |
from wcs.wscalls import NamedWsCall |
... | ... | |
27 | 26 | |
28 | 27 |
def test_named_wscall(pub): |
29 | 28 |
# create object |
29 |
NamedWsCall.wipe() |
|
30 | 30 |
wscall = NamedWsCall() |
31 | 31 |
wscall.name = 'Hello' |
32 | 32 |
wscall.request = {'url': 'http://example.net', 'qs_data': {'a': 'b'}} |
... | ... | |
172 | 172 | |
173 | 173 |
@pytest.mark.parametrize('notify_on_errors', [True, False]) |
174 | 174 |
@pytest.mark.parametrize('record_on_errors', [True, False]) |
175 |
def test_webservice_on_error(http_requests, pub, emails, notify_on_errors, record_on_errors): |
|
175 |
def test_webservice_on_error_with_sql(http_requests, emails, notify_on_errors, record_on_errors): |
|
176 |
pub = create_temporary_pub(sql_mode=True) |
|
176 | 177 |
pub.cfg['debug'] = {'error_email': 'errors@localhost.invalid'} |
177 | 178 |
pub.write_cfg() |
178 | 179 | |
179 | 180 |
NamedWsCall.wipe() |
180 |
LoggedError.wipe()
|
|
181 |
pub.loggederror_class.wipe()
|
|
181 | 182 |
FormDef.wipe() |
182 | 183 | |
183 | 184 |
wscall = NamedWsCall() |
... | ... | |
200 | 201 |
resp = get_app(pub).get('/foobar/') |
201 | 202 |
assert 'Foo Bar ' in resp.text |
202 | 203 |
assert emails.count() == 0 |
203 |
assert LoggedError.count() == 0
|
|
204 |
assert pub.loggederror_class.count() == 0
|
|
204 | 205 | |
205 | 206 |
for url_part in ['400', '400-json', '404', '404-json', '500', 'json-err1', 'json-errheader1']: |
206 | 207 |
status_code = 200 |
... | ... | |
217 | 218 |
else: |
218 | 219 |
assert emails.count() == 0 |
219 | 220 |
if record_on_errors: |
220 |
assert LoggedError.count() == 1
|
|
221 |
LoggedError.wipe()
|
|
221 |
assert pub.loggederror_class.count() == 1
|
|
222 |
pub.loggederror_class.wipe()
|
|
222 | 223 |
else: |
223 |
assert LoggedError.count() == 0 |
|
224 |
assert pub.loggederror_class.count() == 0 |
tests/utilities.py | ||
---|---|---|
4 | 4 |
import tempfile |
5 | 5 |
import random |
6 | 6 |
import psycopg2 |
7 |
import pytest |
|
8 | 7 |
import shutil |
9 | 8 |
import sys |
10 |
import threading |
|
11 | 9 | |
12 | 10 |
from wcs import sql, sessions, custom_views |
13 | 11 | |
... | ... | |
20 | 18 |
from wcs.qommon import force_str |
21 | 19 |
import wcs |
22 | 20 |
import wcs.wsgi |
23 |
from wcs import publisher, compat |
|
24 |
from wcs.qommon.http_request import HTTPRequest |
|
21 |
from wcs import compat |
|
25 | 22 |
from wcs.users import User |
26 | 23 |
from wcs.tracking_code import TrackingCode |
27 | 24 |
import wcs.qommon.emails |
... | ... | |
81 | 78 |
pub.session_class = sql.Session |
82 | 79 |
pub.custom_view_class = sql.CustomView |
83 | 80 |
pub.snapshot_class = sql.Snapshot |
81 |
pub.loggederror_class = sql.LoggedError |
|
84 | 82 |
pub.is_using_postgresql = lambda: True |
85 | 83 |
else: |
86 | 84 |
pub.user_class = User |
87 | 85 |
pub.tracking_code_class = TrackingCode |
88 | 86 |
pub.session_class = sessions.BasicSession |
89 | 87 |
pub.custom_view_class = custom_views.CustomView |
90 |
pub.snapshot_class = None |
|
91 | 88 |
pub.is_using_postgresql = lambda: False |
92 | 89 | |
93 | 90 |
pub.session_manager_class = sessions.StorageSessionManager |
... | ... | |
172 | 169 |
sql.do_session_table() |
173 | 170 |
sql.do_custom_views_table() |
174 | 171 |
sql.do_snapshots_table() |
172 |
sql.do_loggederrors_table() |
|
175 | 173 |
sql.do_meta_table() |
176 | 174 | |
177 | 175 |
conn.close() |
wcs/admin/logged_errors.py | ||
---|---|---|
24 | 24 |
from wcs.qommon import errors, get_cfg |
25 | 25 |
from wcs.qommon.misc import localstrftime |
26 | 26 | |
27 |
from wcs.logged_errors import LoggedError |
|
28 | ||
29 | 27 | |
30 | 28 |
class LoggedErrorDirectory(Directory): |
31 | 29 |
_q_exports = ['', 'delete', 'ack'] |
... | ... | |
107 | 105 |
return redirect('.') |
108 | 106 | |
109 | 107 |
def delete(self): |
110 |
self.error.remove_self()
|
|
108 |
get_publisher().loggederror_class.remove_object(self.error.id)
|
|
111 | 109 |
return redirect('..') |
112 | 110 | |
113 | 111 | |
... | ... | |
117 | 115 |
@classmethod |
118 | 116 |
def get_errors(cls, formdef_class=None, formdef_id=None, workflow_id=None): |
119 | 117 |
errors = [] |
118 |
if not get_publisher().loggederror_class: |
|
119 |
return errors |
|
120 | 120 |
if formdef_id and formdef_class: |
121 |
errors = [e for e in LoggedError.get_with_indexed_value('formdef_id', formdef_id) if e.formdef_class == formdef_class.__name__] |
|
121 |
errors = [ |
|
122 |
e for e in get_publisher().loggederror_class.get_with_indexed_value('formdef_id', formdef_id) |
|
123 |
if e.formdef_class == formdef_class.__name__] |
|
122 | 124 |
elif workflow_id: |
123 |
errors = LoggedError.get_with_indexed_value('workflow_id', workflow_id)
|
|
125 |
errors = get_publisher().loggederror_class.get_with_indexed_value('workflow_id', workflow_id)
|
|
124 | 126 |
return list(errors) |
125 | 127 | |
126 | 128 |
@classmethod |
... | ... | |
168 | 170 | |
169 | 171 |
def _q_lookup(self, component): |
170 | 172 |
try: |
171 |
error = LoggedError.get(component)
|
|
173 |
error = get_publisher().loggederror_class.get(component)
|
|
172 | 174 |
except KeyError: |
173 | 175 |
raise errors.TraversalError() |
174 | 176 |
get_response().breadcrumb.append(('logged-errors/', _('Logged Errors'))) |
wcs/conditions.py | ||
---|---|---|
56 | 56 |
if self.log_errors: |
57 | 57 |
get_logger().warning('failed to evaluate %r (%r)', self, e) |
58 | 58 |
if self.record_errors: |
59 |
from wcs.logged_errors import LoggedError |
|
60 | 59 |
summary = _('Failed to evaluate condition') |
61 |
LoggedError.record(summary, |
|
62 |
formdata=self.context.get('formdata'), |
|
63 |
status_item=self.context.get('status_item'), |
|
64 |
expression=self.value, expression_type=self.type, |
|
65 |
exception=e) |
|
60 |
get_publisher().record_error( |
|
61 |
summary, |
|
62 |
formdata=self.context.get('formdata'), |
|
63 |
status_item=self.context.get('status_item'), |
|
64 |
expression=self.value, expression_type=self.type, |
|
65 |
exception=e) |
|
66 | 66 |
raise RuntimeError() |
67 | 67 | |
68 | 68 |
def evaluate_python(self, local_variables): |
wcs/data_sources.py | ||
---|---|---|
512 | 512 |
url += param_name + '=' + urllib.quote(param_value) |
513 | 513 | |
514 | 514 |
def find_item(items, name, value): |
515 |
from wcs.logged_errors import LoggedError |
|
516 | 515 |
for item in items: |
517 | 516 |
if str(item.get(name)) == str(value): |
518 | 517 |
return item |
519 | 518 |
# not found |
520 |
LoggedError.record(_('Could not find element by id "%s"') % value)
|
|
519 |
get_publisher().record_error(_('Could not find element by id "%s"') % value)
|
|
521 | 520 |
return None |
522 | 521 | |
523 | 522 |
request = get_request() |
wcs/fields.py | ||
---|---|---|
387 | 387 |
except TemplateError: |
388 | 388 |
return ('', explicit_lock) |
389 | 389 |
except AttributeError as e: |
390 |
from wcs.logged_errors import LoggedError |
|
391 |
LoggedError.record(_('Failed to evaluate prefill on field "%s"') % self.label, formdef=getattr(self, 'formdef', None), exception=e) |
|
390 |
get_publisher().record_error( |
|
391 |
_('Failed to evaluate prefill on field "%s"') % self.label, |
|
392 |
formdef=getattr(self, 'formdef', None), |
|
393 |
exception=e) |
|
392 | 394 |
return ('', explicit_lock) |
393 | 395 | |
394 | 396 |
elif t == 'user' and user: |
wcs/formdata.py | ||
---|---|---|
601 | 601 |
if status_id == '_previous': |
602 | 602 |
previous_status = self.pop_previous_marked_status() |
603 | 603 |
if not previous_status: |
604 |
from wcs.logged_errors import LoggedError |
|
605 | 604 |
summary = _('Failed to compute previous status') |
606 |
LoggedError.record(summary, formdata=self)
|
|
605 |
get_publisher().record_error(summary, formdata=self)
|
|
607 | 606 |
return |
608 | 607 |
status_id = previous_status.id |
609 | 608 |
status = 'wf-%s' % status_id |
... | ... | |
1214 | 1213 |
def iter_target_datas(self, objectdef=None, object_type=None, status_item=None): |
1215 | 1214 |
# objectdef, object_type and status_item are provided when called from a workflow action |
1216 | 1215 |
from wcs.wf.create_formdata import LinkedFormdataEvolutionPart |
1217 |
from wcs.logged_errors import LoggedError |
|
1218 | 1216 |
from .carddef import CardDef |
1219 | 1217 |
from .formdef import FormDef |
1220 | 1218 | |
... | ... | |
1268 | 1266 |
yield objectdef.data_class().get(target_id) |
1269 | 1267 |
except KeyError as e: |
1270 | 1268 |
# use custom error message depending on target type |
1271 |
LoggedError.record(_('Could not find linked "%(object_name)s" object by id %(object_id)s') % { |
|
1272 |
'object_name': objectdef.name, 'object_id': target_id}, |
|
1269 |
get_publisher().record_error( |
|
1270 |
_('Could not find linked "%(object_name)s" object by id %(object_id)s') % { |
|
1271 |
'object_name': objectdef.name, 'object_id': target_id}, |
|
1273 | 1272 |
formdata=self, status_item=status_item, exception=e) |
1274 | 1273 |
else: |
1275 | 1274 |
# inspect page |
wcs/logged_errors.py | ||
---|---|---|
17 | 17 |
import datetime |
18 | 18 | |
19 | 19 |
from .qommon.misc import simplify |
20 |
from .qommon.xml_storage import XmlStorableObject |
|
21 | 20 |
from wcs.carddef import CardDef |
22 | 21 |
from wcs.formdef import FormDef |
23 | 22 |
from wcs.workflows import Workflow |
24 | 23 | |
25 | 24 | |
26 |
class LoggedError(XmlStorableObject):
|
|
25 |
class LoggedError(object):
|
|
27 | 26 |
_names = 'logged-errors' |
28 |
xml_root_node = 'error' |
|
29 |
_indexes = ['tech_id'] |
|
30 |
_hashed_indexes = ['formdef_id', 'workflow_id'] |
|
31 | 27 | |
28 |
id = None |
|
29 |
tech_id = None |
|
32 | 30 |
summary = None |
33 | 31 |
formdef_class = 'FormDef' |
34 | 32 |
formdata_id = None |
... | ... | |
46 | 44 |
latest_occurence_timestamp = None |
47 | 45 |
acked = False |
48 | 46 | |
49 |
# declarations for serialization |
|
50 |
XML_NODES = [ |
|
51 |
('summary', 'str'), ('traceback', 'str'), |
|
52 |
('exception_class', 'str'), ('exception_message', 'str'), |
|
53 |
('expression', 'str'), ('expression_type', 'str'), |
|
54 |
('formdata_id', 'str'), ('formdef_id', 'str'), ('workflow_id', 'str'), |
|
55 |
('formdef_class', 'str'), |
|
56 |
('status_id', 'str'), ('status_item_id', 'str'), |
|
57 |
('occurences_count', 'int'), |
|
58 |
('first_occurence_timestamp', 'datetime'), |
|
59 |
('latest_occurence_timestamp', 'datetime'), |
|
60 |
('acked', 'bool')] |
|
61 | ||
62 | 47 |
@classmethod |
63 | 48 |
def record(cls, error_summary, plain_error_msg=None, formdata=None, |
64 | 49 |
formdef=None, workflow=None, status=None, status_item=None, |
... | ... | |
96 | 81 |
error.status_id = status.id |
97 | 82 | |
98 | 83 |
error.first_occurence_timestamp = datetime.datetime.now() |
99 |
error.id = '%s-%s' % ( |
|
100 |
error.first_occurence_timestamp.strftime('%Y%m%d-%H%M%S'), |
|
101 |
error.tech_id, |
|
102 |
) |
|
103 |
existing_error = cls.get_on_index(error.tech_id, 'tech_id', ignore_errors=True) |
|
104 |
if existing_error: |
|
105 |
error = existing_error |
|
84 |
error.tech_id = error.build_tech_id() |
|
85 |
existing_errors = list(cls.get_with_indexed_value('tech_id', error.tech_id)) |
|
86 |
if existing_errors: |
|
87 |
error = existing_errors[0] |
|
106 | 88 |
error.occurences_count += 1 |
107 | 89 |
error.latest_occurence_timestamp = datetime.datetime.now() |
108 | 90 |
error.store() |
... | ... | |
112 | 94 |
def record_exception(cls, error_summary, plain_error_msg, publisher): |
113 | 95 |
try: |
114 | 96 |
context = publisher.substitutions.get_context_variables() |
115 |
except Exception as e:
|
|
97 |
except Exception: |
|
116 | 98 |
return |
117 | 99 |
formdata_id = context.get('form_number_raw') |
118 | 100 |
formdef_urlname = context.get('form_slug') |
... | ... | |
125 | 107 |
klass = CardDef |
126 | 108 |
formdef = klass.get_by_urlname(formdef_urlname) |
127 | 109 |
formdata = formdef.data_class().get(formdata_id, ignore_errors=True) |
128 |
return cls.record(error_summary, plain_error_msg, formdata=formdata, |
|
129 |
formdef=formdef, workflow=formdef.workflow) |
|
110 |
return cls.record( |
|
111 |
error_summary, plain_error_msg, formdata=formdata, |
|
112 |
formdef=formdef, workflow=formdef.workflow) |
|
130 | 113 | |
131 |
@property |
|
132 |
def tech_id(self): |
|
114 |
def build_tech_id(self): |
|
133 | 115 |
tech_id = '' |
134 | 116 |
if self.formdef_id: |
135 | 117 |
tech_id += '%s-' % self.formdef_id |
wcs/publisher.py | ||
---|---|---|
52 | 52 | |
53 | 53 |
from .users import User |
54 | 54 |
from .tracking_code import TrackingCode |
55 |
from .logged_errors import LoggedError |
|
56 | 55 | |
57 | 56 |
import pickle |
58 | 57 | |
... | ... | |
153 | 152 |
self.session_class = sql.Session |
154 | 153 |
self.custom_view_class = sql.CustomView |
155 | 154 |
self.snapshot_class = sql.Snapshot |
155 |
self.loggederror_class = sql.LoggedError |
|
156 | 156 |
sql.get_connection(new=True) |
157 | 157 |
else: |
158 | 158 |
self.user_class = User |
... | ... | |
160 | 160 |
self.session_class = sessions.BasicSession |
161 | 161 |
self.custom_view_class = custom_views.CustomView |
162 | 162 |
self.snapshot_class = None |
163 |
self.loggederror_class = None |
|
163 | 164 | |
164 | 165 |
self.session_manager_class = sessions.StorageSessionManager |
165 | 166 |
self.set_session_manager(self.session_manager_class(session_class=self.session_class)) |
... | ... | |
312 | 313 |
sql.do_tracking_code_table() |
313 | 314 |
sql.do_custom_views_table() |
314 | 315 |
sql.do_snapshots_table() |
316 |
sql.do_loggederrors_table() |
|
315 | 317 |
sql.do_meta_table() |
316 | 318 |
from .formdef import FormDef |
317 | 319 |
from .carddef import CardDef |
... | ... | |
340 | 342 | |
341 | 343 |
def log_internal_error(self, error_summary, plain_error_msg, record=False, notify=True): |
342 | 344 |
tech_id = None |
343 |
if record: |
|
344 |
logged_exception = LoggedError.record_exception(
|
|
345 |
if record and self.loggederror_class:
|
|
346 |
logged_exception = self.loggederror_class.record_exception(
|
|
345 | 347 |
error_summary, plain_error_msg, publisher=self) |
346 | 348 |
if logged_exception: |
347 | 349 |
tech_id = logged_exception.tech_id |
... | ... | |
357 | 359 |
# this could happen on file descriptor exhaustion |
358 | 360 |
pass |
359 | 361 | |
362 |
def record_error(self, *args, **kwargs): |
|
363 |
if self.loggederror_class: |
|
364 |
self.loggederror_class.record(*args, **kwargs) |
|
365 | ||
360 | 366 |
def apply_global_action_timeouts(self): |
361 | 367 |
from wcs.workflows import Workflow, WorkflowGlobalActionTimeoutTrigger |
362 | 368 |
for workflow in Workflow.select(): |
wcs/sql.py | ||
---|---|---|
45 | 45 |
import wcs.carddata |
46 | 46 |
import wcs.custom_views |
47 | 47 |
import wcs.formdata |
48 |
import wcs.logged_errors |
|
48 | 49 |
import wcs.snapshots |
49 | 50 |
import wcs.tracking_code |
50 | 51 |
import wcs.users |
... | ... | |
859 | 860 |
cur.close() |
860 | 861 | |
861 | 862 | |
863 |
def do_loggederrors_table(concurrently=False): |
|
864 |
conn, cur = get_connection_and_cursor() |
|
865 |
table_name = 'loggederrors' |
|
866 | ||
867 |
cur.execute('''SELECT COUNT(*) FROM information_schema.tables |
|
868 |
WHERE table_schema = 'public' |
|
869 |
AND table_name = %s''', (table_name,)) |
|
870 |
if cur.fetchone()[0] == 0: |
|
871 |
cur.execute('''CREATE TABLE %s (id SERIAL PRIMARY KEY, |
|
872 |
tech_id VARCHAR UNIQUE, |
|
873 |
summary VARCHAR, |
|
874 |
formdef_class VARCHAR, |
|
875 |
formdata_id VARCHAR, |
|
876 |
formdef_id VARCHAR, |
|
877 |
workflow_id VARCHAR, |
|
878 |
status_id VARCHAR, |
|
879 |
status_item_id VARCHAR, |
|
880 |
expression VARCHAR, |
|
881 |
expression_type VARCHAR, |
|
882 |
traceback TEXT, |
|
883 |
exception_class VARCHAR, |
|
884 |
exception_message VARCHAR, |
|
885 |
occurences_count INTEGER, |
|
886 |
first_occurence_timestamp TIMESTAMP WITH TIME ZONE, |
|
887 |
latest_occurence_timestamp TIMESTAMP WITH TIME ZONE, |
|
888 |
acked BOOLEAN |
|
889 |
)''' % table_name) |
|
890 |
cur.execute('''SELECT column_name FROM information_schema.columns |
|
891 |
WHERE table_schema = 'public' |
|
892 |
AND table_name = %s''', (table_name,)) |
|
893 |
existing_fields = set([x[0] for x in cur.fetchall()]) |
|
894 | ||
895 |
needed_fields = set([x[0] for x in LoggedError._table_static_fields]) |
|
896 | ||
897 |
# delete obsolete fields |
|
898 |
for field in (existing_fields - needed_fields): |
|
899 |
cur.execute('''ALTER TABLE %s DROP COLUMN %s''' % (table_name, field)) |
|
900 | ||
901 |
create_index = 'CREATE INDEX' |
|
902 |
if concurrently: |
|
903 |
create_index = 'CREATE INDEX CONCURRENTLY' |
|
904 | ||
905 |
# build indexes |
|
906 |
existing_indexes = set() |
|
907 |
cur.execute('''SELECT indexname |
|
908 |
FROM pg_indexes |
|
909 |
WHERE schemaname = 'public' |
|
910 |
AND tablename = %s''', (table_name,)) |
|
911 |
existing_indexes = set([x[0] for x in cur.fetchall()]) |
|
912 | ||
913 |
for attr in ('formdef_id', 'workflow_id'): |
|
914 |
if not table_name + '_' + attr + '_idx' in existing_indexes: |
|
915 |
cur.execute('%(create_index)s %(table_name)s_%(attr)s_idx ON %(table_name)s (%(attr)s)' % { |
|
916 |
'create_index': create_index, |
|
917 |
'table_name': table_name, |
|
918 |
'attr': attr}) |
|
919 | ||
920 |
conn.commit() |
|
921 |
cur.close() |
|
922 | ||
923 | ||
862 | 924 |
@guard_postgres |
863 | 925 |
def do_meta_table(conn=None, cur=None, insert_current_sql_level=True): |
864 | 926 |
own_conn = False |
... | ... | |
2458 | 2520 |
return cls.get(row[0]) |
2459 | 2521 | |
2460 | 2522 | |
2523 |
class LoggedError(SqlMixin, wcs.logged_errors.LoggedError): |
|
2524 |
_table_name = 'loggederrors' |
|
2525 |
_table_static_fields = [ |
|
2526 |
('id', 'serial'), |
|
2527 |
('tech_id', 'varchar'), |
|
2528 |
('summary', 'varchar'), |
|
2529 |
('formdef_class', 'varchar'), |
|
2530 |
('formdata_id', 'varchar'), |
|
2531 |
('formdef_id', 'varchar'), |
|
2532 |
('workflow_id', 'varchar'), |
|
2533 |
('status_id', 'varchar'), |
|
2534 |
('status_item_id', 'varchar'), |
|
2535 |
('expression', 'varchar'), |
|
2536 |
('expression_type', 'varchar'), |
|
2537 |
('traceback', 'text'), |
|
2538 |
('exception_class', 'varchar'), |
|
2539 |
('exception_message', 'varchar'), |
|
2540 |
('occurences_count', 'integer'), |
|
2541 |
('first_occurence_timestamp', 'timestamptz'), |
|
2542 |
('latest_occurence_timestamp', 'timestamptz'), |
|
2543 |
('acked', 'boolean'), |
|
2544 |
] |
|
2545 | ||
2546 |
_numerical_id = False |
|
2547 | ||
2548 |
@guard_postgres |
|
2549 |
@invalidate_substitution_cache |
|
2550 |
def store(self): |
|
2551 |
sql_dict = {x: getattr(self, x) for x, y in self._table_static_fields} |
|
2552 | ||
2553 |
conn, cur = get_connection_and_cursor() |
|
2554 |
if not self.id: |
|
2555 |
column_names = [x for x in sql_dict.keys() if x != 'id'] |
|
2556 |
sql_statement = '''INSERT INTO %s (%s) |
|
2557 |
VALUES (%s) |
|
2558 |
RETURNING id''' % ( |
|
2559 |
self._table_name, |
|
2560 |
', '.join(column_names), |
|
2561 |
', '.join(['%%(%s)s' % x for x in column_names])) |
|
2562 |
cur.execute(sql_statement, sql_dict) |
|
2563 |
self.id = cur.fetchone()[0] |
|
2564 |
else: |
|
2565 |
column_names = sql_dict.keys() |
|
2566 |
sql_statement = '''UPDATE %s SET %s WHERE id = %%(id)s RETURNING id''' % ( |
|
2567 |
self._table_name, |
|
2568 |
', '.join(['%s = %%(%s)s' % (x, x) for x in column_names])) |
|
2569 |
cur.execute(sql_statement, sql_dict) |
|
2570 |
if cur.fetchone() is None: |
|
2571 |
raise AssertionError() |
|
2572 | ||
2573 |
conn.commit() |
|
2574 |
cur.close() |
|
2575 | ||
2576 |
@classmethod |
|
2577 |
def _row2ob(cls, row, **kwargs): |
|
2578 |
o = cls() |
|
2579 |
for field, value in zip(cls._table_static_fields, tuple(row)): |
|
2580 |
if field[1] in ('varchar', 'text'): |
|
2581 |
setattr(o, field[0], str_encode(value)) |
|
2582 |
else: |
|
2583 |
setattr(o, field[0], value) |
|
2584 |
return o |
|
2585 | ||
2586 |
@classmethod |
|
2587 |
def get_data_fields(cls): |
|
2588 |
return [] |
|
2589 | ||
2590 |
@classmethod |
|
2591 |
@guard_postgres |
|
2592 |
def fix_sequences(cls): |
|
2593 |
conn, cur = get_connection_and_cursor() |
|
2594 | ||
2595 |
sql_statement = '''select max(id) from %s''' % cls._table_name |
|
2596 |
cur.execute(sql_statement) |
|
2597 |
max_id = cur.fetchone()[0] |
|
2598 |
if max_id is not None: |
|
2599 |
sql_statement = '''ALTER SEQUENCE %s_id_seq RESTART %s''' % ( |
|
2600 |
cls._table_name, max_id+1) |
|
2601 |
cur.execute(sql_statement) |
|
2602 | ||
2603 |
conn.commit() |
|
2604 |
cur.close() |
|
2605 | ||
2606 | ||
2461 | 2607 |
class classproperty(object): |
2462 | 2608 |
def __init__(self, f): |
2463 | 2609 |
self.f = f |
... | ... | |
2716 | 2862 |
# latest migration, number + description (description is not used |
2717 | 2863 |
# programmaticaly but will make sure git conflicts if two migrations are |
2718 | 2864 |
# separately added with the same number) |
2719 |
SQL_LEVEL = (46, 'add index on formdata(status) - fix')
|
|
2865 |
SQL_LEVEL = (47, 'use SQL to store LoggedError')
|
|
2720 | 2866 | |
2721 | 2867 | |
2722 | 2868 |
def migrate_global_views(conn, cur): |
... | ... | |
2871 | 3017 |
if sql_level < 42: |
2872 | 3018 |
# 42: create snapshots table |
2873 | 3019 |
do_snapshots_table() |
3020 |
if sql_level < 47: |
|
3021 |
# 47: store LoggedErrors in SQL |
|
3022 |
do_loggederrors_table() |
|
2874 | 3023 | |
2875 | 3024 |
cur.execute('''UPDATE wcs_meta SET value = %s WHERE key = %s''', ( |
2876 | 3025 |
str(SQL_LEVEL[0]), 'sql_level')) |
wcs/variables.py | ||
---|---|---|
24 | 24 | |
25 | 25 |
from pyproj import Geod |
26 | 26 | |
27 |
from .logged_errors import LoggedError |
|
28 | 27 |
from .qommon import misc, force_str, _ |
29 | 28 |
from .qommon.evalutils import make_datetime |
30 | 29 |
from .qommon.templatetags.qommon import parse_datetime |
... | ... | |
169 | 168 |
from wcs import sql |
170 | 169 |
criteria = Equal(sql.get_field_id(field), value) |
171 | 170 |
return self._clone(self._criterias + [criteria]) |
172 |
LoggedError.record(_('Invalid filter "%s"') % self.pending_attr, formdata=self._formdata)
|
|
171 |
get_publisher().record_error(_('Invalid filter "%s"') % self.pending_attr, formdata=self._formdata)
|
|
173 | 172 |
return self.none() |
174 | 173 | |
175 | 174 |
def __getattr__(self, attribute): |
wcs/wf/backoffice_fields.py | ||
---|---|---|
15 | 15 |
# along with this program; if not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 | 17 |
import xml.etree.ElementTree as ET |
18 | ||
19 |
from quixote import get_publisher |
|
18 | 20 |
from quixote.html import htmltext |
19 | 21 | |
20 | 22 |
from ..qommon import _, N_ |
... | ... | |
133 | 135 |
try: |
134 | 136 |
new_value = formdef_field.convert_value_from_anything(new_value) |
135 | 137 |
except ValueError as e: |
136 |
from wcs.logged_errors import LoggedError |
|
137 | 138 |
summary = _('Failed to convert %(class)s value to %(kind)s field (%(id)s)') % { |
138 | 139 |
'class': type(new_value), |
139 | 140 |
'kind': _(getattr(formdef_field, 'description', 'unknown')), |
140 | 141 |
'id': field['field_id'], |
141 | 142 |
} |
142 | 143 |
expression_dict = self.get_expression(field['value']) |
143 |
LoggedError.record(summary, formdata=formdata, status_item=self, |
|
144 |
expression=expression_dict['value'], |
|
145 |
expression_type=expression_dict['type'], |
|
146 |
exception=e) |
|
144 |
get_publisher().record_error( |
|
145 |
summary, formdata=formdata, status_item=self, |
|
146 |
expression=expression_dict['value'], |
|
147 |
expression_type=expression_dict['type'], |
|
148 |
exception=e) |
|
147 | 149 |
continue |
148 | 150 | |
149 | 151 |
formdata.data['%s' % field['field_id']] = new_value |
wcs/wf/create_formdata.py | ||
---|---|---|
27 | 27 |
SingleSelectWidget, ComputedExpressionWidget, |
28 | 28 |
CheckboxWidget, VarnameWidget, HtmlWidget) |
29 | 29 | |
30 |
from wcs.logged_errors import LoggedError |
|
31 | 30 |
from wcs.workflows import WorkflowStatusItem, register_item_class |
32 | 31 |
from wcs.formdef import FormDef |
33 | 32 | |
... | ... | |
391 | 390 |
field=dest_field, |
392 | 391 |
value=src.data.get(field.id)) |
393 | 392 |
except Exception as e: |
394 |
LoggedError.record(_('Could not copy field by varname for "%s"') % field.varname, |
|
395 |
formdata=src, status_item=self, exception=e) |
|
393 |
get_publisher().record_error( |
|
394 |
_('Could not copy field by varname for "%s"') % field.varname, |
|
395 |
formdata=src, status_item=self, exception=e) |
|
396 | 396 | |
397 | 397 |
# field.id can be serialized to xml, so we must always convert them to |
398 | 398 |
# str when matching |
... | ... | |
418 | 418 |
value=value) |
419 | 419 |
except Exception as e: |
420 | 420 |
expression = self.get_expression(mapping.expression) |
421 |
LoggedError.record(_('Could not assign value to field "%s"') % dest_field.label, |
|
422 |
formdata=src, status_item=self, |
|
423 |
expression=expression['value'], expression_type=expression['type'], |
|
424 |
exception=e) |
|
421 |
get_publisher().record_error( |
|
422 |
_('Could not assign value to field "%s"') % dest_field.label, |
|
423 |
formdata=src, status_item=self, |
|
424 |
expression=expression['value'], expression_type=expression['type'], |
|
425 |
exception=e) |
|
425 | 426 | |
426 | 427 |
if missing_fields: |
427 | 428 |
summary = _('Missing field %r') % missing_fields |
428 |
LoggedError.record(summary, formdata=src, status_item=self)
|
|
429 |
get_publisher().record_error(summary, formdata=src, status_item=self)
|
|
429 | 430 | |
430 | 431 |
def _set_value(self, formdata, field, value): |
431 | 432 |
if field.convert_value_from_anything: |
wcs/wf/dispatch.py | ||
---|---|---|
188 | 188 |
return htmltext('<ul class="rules">%s</ul>') % htmltext('').join(result) |
189 | 189 | |
190 | 190 |
def perform(self, formdata): |
191 |
from wcs.logged_errors import LoggedError |
|
192 | 191 |
if not formdata.workflow_roles: |
193 | 192 |
formdata.workflow_roles = {} |
194 | 193 | |
... | ... | |
199 | 198 |
return |
200 | 199 |
new_role_id = self.get_computed_role_id(self.role_id) |
201 | 200 |
if not new_role_id: |
202 |
LoggedError.record(_('error in dispatch, missing role (%s)') % self.role_id, formdata=formdata)
|
|
201 |
get_publisher().record_error(_('error in dispatch, missing role (%s)') % self.role_id, formdata=formdata)
|
|
203 | 202 |
elif self.dispatch_type == 'automatic': |
204 | 203 |
if not (self.role_key and self.variable and self.rules): |
205 | 204 |
return |
... | ... | |
226 | 225 | |
227 | 226 |
if new_role_id: |
228 | 227 |
if not Role.has_key(new_role_id): |
229 |
LoggedError.record(_('error in dispatch, missing role (%s)') % new_role_id, formdata=formdata)
|
|
228 |
get_publisher().record_error(_('error in dispatch, missing role (%s)') % new_role_id, formdata=formdata)
|
|
230 | 229 |
else: |
231 | 230 |
formdata.workflow_roles[self.role_key] = str(new_role_id) |
232 | 231 |
formdata.store() |
wcs/wf/external_workflow.py | ||
---|---|---|
19 | 19 |
from wcs.qommon import _ |
20 | 20 |
from wcs.qommon.form import SingleSelectWidget |
21 | 21 | |
22 |
from wcs.logged_errors import LoggedError |
|
23 | 22 |
from wcs.workflows import WorkflowStatusItem, perform_items, register_item_class |
24 | 23 |
from wcs.workflows import WorkflowGlobalActionWebserviceTrigger, Workflow |
25 | 24 |
from wcs.carddef import CardDef |
... | ... | |
126 | 125 | |
127 | 126 |
trigger = self.get_trigger(objectdef.workflow) |
128 | 127 |
if not trigger: |
129 |
LoggedError.record(_('No trigger with id "%s" found in workflow') % self.trigger_id, |
|
130 |
formdata=formdata, status_item=self) |
|
128 |
get_publisher().record_error( |
|
129 |
_('No trigger with id "%s" found in workflow') % self.trigger_id, |
|
130 |
formdata=formdata, status_item=self) |
|
131 | 131 |
return |
132 | 132 | |
133 | 133 |
class CallerSource: |
wcs/wf/wscall.py | ||
---|---|---|
447 | 447 |
try: |
448 | 448 |
target = self.parent.parent.get_status(value) |
449 | 449 |
except KeyError: |
450 |
from wcs.logged_errors import LoggedError |
|
451 | 450 |
message = _('reference to invalid status in workflow %(workflow)s, status %(status)s, item %(item)s') % { |
452 | 451 |
'workflow': self.parent.parent.name, |
453 | 452 |
'status': self.parent.name, |
454 | 453 |
'item': self.description, |
455 | 454 |
} |
456 |
LoggedError.record(message, workflow=self.parent.parent)
|
|
455 |
get_publisher().record_error(message, workflow=self.parent.parent)
|
|
457 | 456 |
continue |
458 | 457 |
targets.append(target) |
459 | 458 |
return targets |
wcs/workflows.py | ||
---|---|---|
1982 | 1982 |
vars.update(context or {}) |
1983 | 1983 | |
1984 | 1984 |
def log_exception(exception): |
1985 |
from wcs.logged_errors import LoggedError |
|
1986 | 1985 |
if expression['type'] == 'template': |
1987 | 1986 |
summary = _('Failed to compute template') |
1988 | 1987 |
else: |
1989 | 1988 |
summary = _('Failed to compute Python expression') |
1990 |
LoggedError.record(summary, formdata=formdata, status_item=status_item, |
|
1991 |
expression=expression['value'], |
|
1992 |
expression_type=expression['type'], |
|
1993 |
exception=exception) |
|
1989 |
get_publisher().record_error( |
|
1990 |
summary, formdata=formdata, status_item=status_item, |
|
1991 |
expression=expression['value'], |
|
1992 |
expression_type=expression['type'], |
|
1993 |
exception=exception) |
|
1994 | 1994 | |
1995 | 1995 |
if expression['type'] == 'template': |
1996 | 1996 |
try: |
... | ... | |
2043 | 2043 | |
2044 | 2044 |
targets = [x for x in self.parent.parent.possible_status if x.id == self.status] |
2045 | 2045 |
if not targets and formdata: # do not log in presentation context: formdata is needed |
2046 |
from wcs.logged_errors import LoggedError |
|
2047 | 2046 |
message = _('reference to invalid status %(target)s in status %(status)s, ' |
2048 | 2047 |
'action %(status_item)s') % { |
2049 | 2048 |
'target': self.status, |
2050 | 2049 |
'status': self.parent.name, |
2051 | 2050 |
'status_item': _(self.description) |
2052 | 2051 |
} |
2053 |
LoggedError.record(message, formdata=formdata, status_item=self)
|
|
2052 |
get_publisher().record_error(message, formdata=formdata, status_item=self)
|
|
2054 | 2053 | |
2055 | 2054 |
return targets |
2056 | 2055 | |
... | ... | |
2677 | 2676 |
subject = mail_template.subject |
2678 | 2677 |
extra_attachments = mail_template.attachments |
2679 | 2678 |
else: |
2680 |
from wcs.logged_errors import LoggedError |
|
2681 | 2679 |
message = _('reference to invalid mail template %(mail_template)s in status %(status)s') % { |
2682 | 2680 |
'status': self.parent.name, |
2683 | 2681 |
'mail_template': self.mail_template, |
2684 | 2682 |
} |
2685 |
LoggedError.record(message, formdata=formdata, status_item=self)
|
|
2683 |
get_publisher().record_error(message, formdata=formdata, status_item=self)
|
|
2686 | 2684 |
return |
2687 | 2685 | |
2688 | 2686 |
try: |
2689 |
- |