wcs/tests/form_pages/test_computed_field.py

636 lines
21 KiB
Python

import datetime
import decimal
import pytest
from django.utils.timezone import make_aware
from wcs import fields
from wcs.carddef import CardDef
from wcs.formdef import FormDef
from wcs.qommon.substitution import CompatibilityNamesDict
from wcs.workflows import EditableWorkflowStatusItem, Workflow
from wcs.wscalls import NamedWsCall
from ..utilities import clean_temporary_pub, create_temporary_pub, get_app, login
from .test_all import create_user
def pytest_generate_tests(metafunc):
if 'pub' in metafunc.fixturenames:
metafunc.parametrize('pub', ['pickle', 'sql'], indirect=True)
@pytest.fixture
def pub(request, emails):
pub = create_temporary_pub(
sql_mode=bool('sql' in request.param),
templates_mode=bool('templates' in request.param),
lazy_mode=bool('lazy' in request.param),
)
pub.cfg['identification'] = {'methods': ['password']}
pub.cfg['language'] = {'language': 'en'}
pub.write_cfg()
return pub
def teardown_module(module):
clean_temporary_pub()
def test_computed_field_simple(pub):
create_user(pub)
FormDef.wipe()
formdef = FormDef()
formdef.name = 'test'
formdef.fields = [
fields.ComputedField(id='1', label='computed', varname='computed', value_template='{{ "xxx" }}'),
]
formdef.store()
formdef.data_class().wipe()
resp = login(get_app(pub), username='foo', password='foo').get('/test/')
resp = resp.forms[0].submit('submit') # -> validation
resp = resp.forms[0].submit('submit').follow() # -> submit
assert 'The form has been recorded' in resp.text
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
assert formdata.data == {'1': 'xxx'}
def test_computed_field_used_in_prefill(pub):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'test'
formdef.fields = [
fields.ComputedField(id='1', label='computed', varname='computed', value_template='xxx'),
fields.StringField(
id='2', label='string', prefill={'type': 'string', 'value': '{{ form_var_computed }}'}
),
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
assert resp.forms[0]['f2'].value == 'xxx'
resp = resp.forms[0].submit('submit') # -> validation
resp = resp.forms[0].submit('submit').follow() # -> submit
assert 'The form has been recorded' in resp.text
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
assert formdata.data == {'1': 'xxx', '2': 'xxx'}
def test_computed_field_used_in_comment(pub):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'test'
formdef.fields = [
fields.ComputedField(id='1', label='computed', varname='computed', value_template='xxx'),
fields.CommentField(id='2', label='X{{ form_var_computed }}Y', type='comment'),
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
assert 'XxxxY' in resp.text
resp = resp.forms[0].submit('submit') # -> validation
resp = resp.forms[0].submit('submit').follow() # -> submit
assert 'The form has been recorded' in resp.text
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
assert formdata.data == {'1': 'xxx'}
def test_computed_field_freeze(pub, freezer):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'test'
formdef.fields = [
fields.ComputedField(
id='1',
label='computed',
varname='computed',
value_template='{% now "H:i" %}',
freeze_on_initial_value=False,
),
]
formdef.store()
formdef.data_class().wipe()
freezer.move_to(make_aware(datetime.datetime(2021, 4, 6, 10, 0)))
resp = get_app(pub).get('/test/')
freezer.move_to(make_aware(datetime.datetime(2021, 4, 6, 10, 5)))
resp = resp.forms[0].submit('submit') # -> validation
resp = resp.forms[0].submit('submit').follow() # -> submit
assert 'The form has been recorded' in resp.text
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
assert formdata.data == {'1': '10:05'}
formdef.data_class().wipe()
formdef.fields[0].freeze_on_initial_value = True
formdef.store()
freezer.move_to(make_aware(datetime.datetime(2021, 4, 6, 10, 0)))
resp = get_app(pub).get('/test/')
freezer.move_to(make_aware(datetime.datetime(2021, 4, 6, 10, 5)))
resp = resp.forms[0].submit('submit') # -> validation
resp = resp.forms[0].submit('submit').follow() # -> submit
assert 'The form has been recorded' in resp.text
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
assert formdata.data == {'1': '10:00'}
def test_computed_field_from_request_get(pub):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'test'
formdef.fields = [
fields.ComputedField(
id='1',
label='computed',
varname='computed',
value_template='{{ request.GET.param }}',
freeze_on_initial_value=True,
),
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/?param=value')
resp = resp.forms[0].submit('submit') # -> validation
resp = resp.forms[0].submit('submit').follow() # -> submit
assert 'The form has been recorded' in resp.text
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
assert formdata.data == {'1': 'value'}
def test_computed_field_usage_in_post_condition(pub):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'test'
formdef.fields = [
fields.PageField(
id='0',
label='1st page',
type='page',
post_conditions=[
{
'condition': {
'type': 'django',
'value': 'form_var_computed == "xxx"',
},
'error_message': 'You shall not pass.',
}
],
),
fields.ComputedField(
id='1',
label='computed',
varname='computed',
value_template='{{ request.GET.param }}',
freeze_on_initial_value=True,
),
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/?param=test')
resp = resp.forms[0].submit('submit') # -> validation
assert 'You shall not pass.' in resp.text
resp = get_app(pub).get('/test/?param=xxx')
resp = resp.forms[0].submit('submit') # -> validation
resp = resp.forms[0].submit('submit').follow() # -> submit
assert 'The form has been recorded' in resp.text
# change condition to test |is_empty
formdef.fields[0].post_conditions[0]['condition']['value'] = 'not form_var_computed|is_empty'
formdef.store()
resp = get_app(pub).get('/test/')
resp = resp.forms[0].submit('submit') # -> validation
assert 'You shall not pass.' in resp.text
resp = get_app(pub).get('/test/?param=test')
resp = resp.forms[0].submit('submit') # -> validation
assert 'You shall not pass.' not in resp.text
def test_computed_field_usage_updated_in_post_condition(pub):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'test'
formdef.fields = [
fields.PageField(
id='0',
label='1st page',
type='page',
post_conditions=[
{
'condition': {
'type': 'django',
'value': 'form_var_computed == "xxx"',
},
'error_message': 'You shall not pass.',
}
],
),
fields.ComputedField(
id='1',
label='computed',
varname='computed',
value_template='{{ form_var_field }}',
),
fields.StringField(id='2', label='string', varname='field'),
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
resp.forms[0]['f2'].value = 'test'
resp = resp.forms[0].submit('submit') # -> validation
assert 'You shall not pass.' in resp.text
resp.forms[0]['f2'].value = 'xxx'
resp = resp.forms[0].submit('submit') # -> validation
resp = resp.forms[0].submit('submit').follow() # -> submit
assert 'The form has been recorded' in resp.text
def test_computed_field_recall_draft(pub):
create_user(pub)
FormDef.wipe()
formdef = FormDef()
formdef.name = 'test'
formdef.fields = [
fields.ComputedField(
id='1',
label='computed',
varname='computed',
value_template='{{ request.GET.param }}',
freeze_on_initial_value=True,
),
]
formdef.store()
formdef.data_class().wipe()
resp = login(get_app(pub), username='foo', password='foo').get('/test/?param=value')
resp = resp.forms[0].submit('submit') # -> validation
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
assert formdata.is_draft()
# recall draft
resp = login(get_app(pub), username='foo', password='foo').get(formdata.get_url()).follow()
assert 'form-validation' in resp.text
resp = resp.forms[1].submit('submit').follow() # -> submit
assert 'The form has been recorded' in resp.text
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
assert formdata.data == {'1': 'value'}
# retry, moving back to first page
formdef.data_class().wipe()
resp = login(get_app(pub), username='foo', password='foo').get('/test/?param=value')
resp = resp.forms[0].submit('submit') # -> validation
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
assert formdata.is_draft()
resp = login(get_app(pub), username='foo', password='foo').get(formdata.get_url()).follow()
assert 'form-validation' in resp.text
resp = resp.forms[1].submit('previous') # -> first page
resp = resp.forms[1].submit('submit') # -> validation
resp = resp.forms[1].submit('submit').follow() # -> submit
assert 'The form has been recorded' in resp.text
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
assert formdata.data == {'1': 'value'}
def test_computed_field_complex_data(pub, http_requests):
FormDef.wipe()
wscall = NamedWsCall()
wscall.name = 'Hello world'
wscall.request = {'url': 'http://remote.example.net/json'}
wscall.store()
formdef = FormDef()
formdef.name = 'test'
formdef.fields = [
fields.ComputedField(
id='1',
label='computed',
varname='computed',
value_template='{{ webservice.hello_world }}',
freeze_on_initial_value=True,
),
fields.CommentField(id='2', label='X{{form_var_computed_foo}}Y', type='comment'),
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
assert 'XbarY' in resp.text
resp = resp.forms[0].submit('submit') # -> validation
resp = resp.forms[0].submit('submit').follow() # -> submit
assert 'The form has been recorded' in resp.text
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
assert formdata.data['1'] == {'foo': 'bar'}
def test_computed_field_decimal_data(pub, http_requests):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'test'
formdef.fields = [
fields.ComputedField(
id='1',
label='computed',
varname='computed',
value_template='{{ "123.45"|decimal }}',
freeze_on_initial_value=True,
),
fields.CommentField(id='2', label='X{{form_var_computed}}Y', type='comment'),
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
assert 'X123.45Y' in resp.text
resp = resp.forms[0].submit('submit') # -> validation
resp = resp.forms[0].submit('submit').follow() # -> submit
assert 'The form has been recorded' in resp.text
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
# pickle storage keeps the typed data but postgresql storage has to convert
# it to stringfor json storage, hence the casting here:
assert decimal.Decimal(formdata.data['1']) == decimal.Decimal('123.45')
def test_computed_field_usage_in_live_data(pub, http_requests):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'test'
formdef.fields = [
fields.ComputedField(
id='1',
label='computed',
varname='computed',
value_template='{{ "xxx" }}',
),
fields.StringField(id='0', label='string', varname='string'),
fields.CommentField(id='2', label='X{{form_var_computed}}Y{{form_var_string}}Z', type='comment'),
]
formdef.store()
formdef.data_class().wipe()
app = get_app(pub)
resp = app.get('/test/')
assert 'XxxxYNoneZ' in resp.text
resp.form['f0'] = 'hello'
live_resp = app.post('/test/live', params=resp.form.submit_fields())
assert live_resp.json['result']['2']['content'] == '<p>XxxxYhelloZ</p>'
def test_computed_field_inspect_keys(pub):
create_user(pub)
FormDef.wipe()
formdef = FormDef()
formdef.name = 'test'
formdef.fields = [
fields.ComputedField(id='1', label='computed', varname='computed', value_template='{{ "xxx" }}'),
]
formdef.store()
formdef.data_class().wipe()
formdata = formdef.data_class()()
formdata.data = {'1': 'xxx'}
substvars = CompatibilityNamesDict()
substvars.update(formdata.get_substitution_variables())
assert 'form_var_computed' in substvars.get_flat_keys()
assert substvars['form_var_computed'] == 'xxx'
formdata.data = {'1': {'foo': 'bar'}}
assert 'form_var_computed_foo' in substvars.get_flat_keys()
assert substvars['form_var_computed_foo'] == 'bar'
def test_computed_field_edit_action(pub):
create_user(pub)
workflow = Workflow.get_default_workflow()
workflow.id = '2'
editable = EditableWorkflowStatusItem()
editable.id = '_editable'
editable.by = ['_submitter']
st2 = workflow.get_status('new')
st2.items.append(editable)
editable.parent = st2
workflow.store()
FormDef.wipe()
formdef = FormDef()
formdef.name = 'test'
formdef.fields = [
fields.StringField(id='0', label='field', varname='plop'),
fields.ComputedField(
id='1', label='computed', varname='computed', value_template='{{ form_var_plop }}'
),
]
formdef.workflow_id = workflow.id
formdef.store()
formdef.data_class().wipe()
resp = login(get_app(pub), username='foo', password='foo').get('/test/')
resp.forms[0]['f0'] = 'foobar'
resp = resp.forms[0].submit('submit') # -> validation
resp = resp.forms[0].submit('submit').follow() # -> submit
assert 'The form has been recorded' in resp.text
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
assert formdata.data == {'0': 'foobar', '1': 'foobar'}
resp = resp.forms[0].submit('button_editable')
resp = resp.follow()
resp.forms[0]['f0'] = 'bar'
resp = resp.forms[0].submit('submit')
formdata.refresh_from_storage()
assert formdata.data == {'0': 'bar', '1': 'bar'}
# freeze value
formdef.data_class().wipe()
formdef.fields[1].value_template = '{{ request.GET.foo }}'
formdef.fields[1].freeze_on_initial_value = True
formdef.store()
resp = login(get_app(pub), username='foo', password='foo').get('/test/?foo=PLOP')
resp.forms[0]['f0'] = 'foobar'
resp = resp.forms[0].submit('submit') # -> validation
resp = resp.forms[0].submit('submit').follow() # -> submit
assert 'The form has been recorded' in resp.text
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
assert formdata.data == {'0': 'foobar', '1': 'PLOP'}
resp = resp.forms[0].submit('button_editable')
resp = resp.follow()
resp.forms[0]['f0'] = 'bar'
resp = resp.forms[0].submit('submit')
formdata.refresh_from_storage()
assert formdata.data == {'0': 'bar', '1': 'PLOP'}
def test_cascading_computed_fields(pub):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'test'
formdef.fields = [
fields.PageField(
id='0',
label='1st page',
type='page',
post_conditions=[
{
'condition': {
'type': 'django',
'value': 'form_var_dc == "0abc"',
},
'error_message': 'You shall not pass.',
}
],
),
fields.ComputedField(id='1', label='computed', varname='a', value_template='a'),
fields.ComputedField(id='2', label='computed', varname='b', value_template='{{form_var_a}}b'),
fields.ComputedField(id='3', label='computed', varname='c', value_template='{{form_var_b}}c'),
fields.CommentField(id='4', label='X{{ form_var_c }}Y', type='comment'),
fields.StringField(id='5', label='string', varname='d'),
fields.ComputedField(id='6', label='computed', varname='da', value_template='{{form_var_d}}a'),
fields.ComputedField(id='7', label='computed', varname='db', value_template='{{form_var_da}}b'),
fields.ComputedField(id='8', label='computed', varname='dc', value_template='{{form_var_db}}c'),
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
assert 'XabcY' in resp.text # check comment field has the correct value
# this should produce form_var_dc == "Xabc", and not pass the post condition
resp.forms[0]['f5'].value = 'X'
resp = resp.forms[0].submit('submit') # -> validation
assert 'You shall not pass.' in resp.text
# this should produce form_var_dc == "0abc", and be ok for the post condition
resp = get_app(pub).get('/test/')
resp.forms[0]['f5'].value = '0'
resp = resp.forms[0].submit('submit') # -> validation
assert 'You shall not pass.' not in resp.text
def test_computed_field_usage_in_criteria(pub):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'test'
formdef.fields = [
fields.PageField(
id='0',
label='1st page',
type='page',
),
fields.ComputedField(
id='1',
label='computed',
varname='computed',
value_template='{{ request.GET.param }}',
freeze_on_initial_value=True,
),
fields.PageField(
id='2',
label='2nd page',
type='page',
),
fields.CommentField(
id='3',
label='<p>count with this value: '
'{{form_objects|exclude_self|filter_by:"computed"|filter_value:form_var_computed|count}}</p>',
type='comment',
),
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/?param=test')
resp = resp.forms[0].submit('submit') # -> 2nd page
assert 'count with this value: 0' in resp.text
resp = resp.forms[0].submit('submit') # -> validation
resp = resp.forms[0].submit('submit').follow() # -> submit
assert 'The form has been recorded' in resp.text
resp = get_app(pub).get('/test/?param=test')
resp = resp.forms[0].submit('submit') # -> 2nd page
assert 'count with this value: 1' in resp.text
resp = resp.forms[0].submit('submit') # -> validation
resp = resp.forms[0].submit('submit').follow() # -> submit
assert 'The form has been recorded' in resp.text
resp = get_app(pub).get('/test/?param=plop')
resp = resp.forms[0].submit('submit') # -> 2nd page
assert 'count with this value: 0' in resp.text
def test_computed_field_with_data_source(pub):
CardDef.wipe()
FormDef.wipe()
carddef = CardDef()
carddef.name = 'items'
carddef.digest_templates = {'default': '{{form_var_name}}'}
carddef.fields = [
fields.StringField(id='0', label='string', varname='name'),
fields.StringField(id='1', label='string', varname='attr'),
]
carddef.store()
for i, value in enumerate(['foo', 'bar', 'baz']):
carddata = carddef.data_class()()
carddata.data = {
'0': value,
'1': 'attr%s' % i,
}
carddata.just_created()
carddata.store()
ds = {'type': 'carddef:%s' % carddef.url_name}
formdef = FormDef()
formdef.name = 'test'
formdef.fields = [
fields.ComputedField(
id='1',
label='computed',
varname='computed',
value_template='{{ request.GET.param }}',
freeze_on_initial_value=True,
data_source=ds,
),
fields.CommentField(id='2', label='X{{ form_var_computed_live_var_name }}Y', type='comment'),
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/?param=%s' % carddata.id)
assert 'XbazY' in resp.text
resp = get_app(pub).get('/test/?param=%s' % 'invalid')
assert 'XY' in resp.text