From a93d213d995be19b4c3bcbf18c3f0cc10b619305 Mon Sep 17 00:00:00 2001 From: Valentin Deniaud Date: Mon, 9 Jan 2023 17:55:44 +0100 Subject: [PATCH] testdef: add support for computed fields (#73330) --- tests/test_testdef.py | 94 +++++++++++++++++++++++++++++++++++++++++++ wcs/testdef.py | 29 +++++++++++-- 2 files changed, 120 insertions(+), 3 deletions(-) diff --git a/tests/test_testdef.py b/tests/test_testdef.py index 03c3136d5..2d6fcf882 100644 --- a/tests/test_testdef.py +++ b/tests/test_testdef.py @@ -437,3 +437,97 @@ def test_validation_file_field(pub): with pytest.raises(TestError) as excinfo: testdef.run(formdef) assert str(excinfo.value) == 'Invalid value "hop.pdf" for field "File": forbidden file type.' + + +def test_computed_field_support(pub): + formdef = FormDef() + formdef.name = 'test title' + formdef.fields = [ + fields.PageField( + id='0', + label='1st page', + type='page', + post_conditions=[ + { + 'condition': { + 'type': 'django', + 'value': 'form_var_foo == "xxx" and form_var_bar == "hop"', + }, + 'error_message': '', + } + ], + ), + fields.ComputedField( + id='1', + label='Computed (frozen)', + varname='foo', + value_template='{% firstof form_var_text "xxx" %}', + freeze_on_initial_value=True, + ), + fields.ComputedField( + id='2', label='Computed (live)', varname='bar', value_template='{% firstof form_var_text "xxx" %}' + ), + fields.StringField(id='3', label='Text', varname='text'), + ] + formdef.store() + + formdata = formdef.data_class()() + formdata.just_created() + formdata.receipt_time = datetime.datetime(2021, 1, 1, 0, 0).timetuple() + formdata.data['1'] = 'zzz' + formdata.data['3'] = 'hop' + + testdef = TestDef.create_from_formdata(formdef, formdata) + testdef.run(formdef) + + formdef.fields[1].value_template = '{% for %}' + testdef = TestDef.create_from_formdata(formdef, formdata) + with pytest.raises(TestError) as excinfo: + testdef.run(formdef) + assert ( + str(excinfo.value) + == 'Page 1 post condition was not met (form_var_foo == "xxx" and form_var_bar == "hop").' + ) + + +def test_computed_field_support_complex_data(pub): + formdef = FormDef() + formdef.name = 'test title' + formdef.fields = [ + fields.PageField( + id='0', + label='1st page', + type='page', + post_conditions=[ + {'condition': {'type': 'django', 'value': '"z" in form_var_foo'}, 'error_message': ''} + ], + ), + fields.ComputedField( + id='1', + label='Computed', + varname='foo', + value_template='{{form_objects|first|get:"form_var_items_raw"}}', + ), + fields.ItemsField(id='2', label='Items', varname='items', required=False), + ] + formdef.store() + + submitted_formdata = formdef.data_class()() + submitted_formdata.just_created() + submitted_formdata.receipt_time = datetime.datetime(2021, 1, 1, 0, 0).timetuple() + submitted_formdata.data['2'] = ['a', 'b'] + submitted_formdata.store() + + formdata = formdef.data_class()() + formdata.just_created() + formdata.receipt_time = datetime.datetime(2021, 1, 1, 0, 0).timetuple() + + testdef = TestDef.create_from_formdata(formdef, formdata) + with pytest.raises(TestError) as excinfo: + testdef.run(formdef) + assert str(excinfo.value) == 'Page 1 post condition was not met ("z" in form_var_foo).' + + submitted_formdata.data['2'] = ['a', 'b', 'z'] + submitted_formdata.store() + + testdef.run(formdef) diff --git a/wcs/testdef.py b/wcs/testdef.py index 4c677cfb7..5b523ec2c 100644 --- a/wcs/testdef.py +++ b/wcs/testdef.py @@ -24,6 +24,8 @@ from wcs import sql from wcs.compat import CompatHTTPRequest from wcs.fields import Field, PageField from wcs.qommon.form import FileWithPreviewWidget, Form, get_selection_error_text +from wcs.qommon.template import TemplateError +from wcs.workflows import WorkflowStatusItem from .qommon import _, misc from .qommon.storage import Equal @@ -158,6 +160,7 @@ class TestDef(sql.TestDef): self.evaluate_page_conditions(previous_page, formdata, objectdef) def fill_page_fields(self, fields, page, formdata, objectdef): + computed_fields = [] for field in fields: if field.type in ('subtitle', 'title', 'comment', 'computed'): continue @@ -170,13 +173,33 @@ class TestDef(sql.TestDef): ) continue - value = self.data['fields'].get(field.id) - value = field.from_json_value(value) - self.run_widget_validation(field, value) + if field.key == 'computed': + try: + value = WorkflowStatusItem.compute(field.value_template, raises=True, allow_complex=True) + except TemplateError: + continue + + if not field.freeze_on_initial_value: + computed_fields.append(field) + + formdata.data[field.id] = value + else: + value = self.data['fields'].get(field.id) + value = field.from_json_value(value) + self.run_widget_validation(field, value) formdata.data[field.id] = value get_publisher().substitutions.invalidate_cache() + for field in computed_fields: + try: + value = WorkflowStatusItem.compute(field.value_template, raises=True, allow_complex=True) + except TemplateError: + continue + + formdata.data[field.id] = value + get_publisher().substitutions.invalidate_cache() + def evaluate_page_conditions(self, page, formdata, objectdef): for post_condition in page.post_conditions or []: condition = post_condition.get('condition', {}) -- 2.39.0