import collections
import datetime
import pytest
import sys
import shutil
import time

from quixote import cleanup
from quixote.http_request import Upload
from wcs.qommon.template import Template
from wcs.qommon.form import PicklableUpload
from wcs.qommon.http_request import HTTPRequest
from wcs import fields, formdef
from wcs.categories import Category
from wcs.conditions import Condition
from wcs.formdef import FormDef
from wcs.formdata import Evolution
from wcs.roles import Role
from wcs import sessions
from wcs.variables import LazyFormData
from wcs.workflows import Workflow, WorkflowStatusItem, WorkflowCriticalityLevel, WorkflowBackofficeFieldsFormDef
from wcs.wf.anonymise import AnonymiseWorkflowStatusItem
from wcs.wf.wscall import JournalWsCallErrorPart

from wcs.wf.register_comment import JournalEvolutionPart

from utilities import create_temporary_pub, clean_temporary_pub

from test_api import local_user

def pytest_generate_tests(metafunc):
    if 'pub' in metafunc.fixturenames:
        metafunc.parametrize('pub', ['pickle', 'sql'], indirect=True)

@pytest.fixture
def pub(request):
    pub = create_temporary_pub(sql_mode=(request.param == 'sql'))

    req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'})
    pub.set_app_dir(req)
    pub._set_request(req)
    pub.cfg['identification'] = {'methods': ['password']}
    pub.cfg['language'] = {'language': 'en'}
    pub.write_cfg()

    FormDef.wipe()
    Category.wipe()

    cat = Category(name='test category')
    cat.store()

    global formdef
    formdef = FormDef()
    formdef.name = 'foobar'
    formdef.url_name = 'foobar'
    formdef.fields = []
    formdef.category_id = cat.id
    formdef.store()

    return pub


def teardown_module(module):
    clean_temporary_pub()


def test_basic(pub):
    formdata = formdef.data_class()()
    substvars = formdata.get_substitution_variables()
    assert substvars.get('form_status') == 'Unknown'
    assert substvars.get('form_name') == 'foobar'
    assert substvars.get('form_slug') == 'foobar'
    assert substvars.get('category_name') == 'test category'

def test_saved(pub):
    formdef.data_class().wipe()
    formdata = formdef.data_class()()
    formdata.store()
    substvars = formdata.get_substitution_variables()
    assert substvars.get('form_number') == '%s-1' % formdef.id
    assert substvars.get('form_number_raw') == '1'
    assert substvars.get('form_url').endswith('/foobar/1/')
    assert substvars.get('form_url_backoffice').endswith('/backoffice/management/foobar/1/')
    assert substvars.get('form_status_url').endswith('/foobar/1/status')

def test_auto_display_id(pub):
    formdef.data_class().wipe()
    formdata = formdef.data_class()()
    formdata.store()
    substvars = formdata.get_substitution_variables()
    assert substvars.get('form_number') == '%s-%s' % (formdef.id, formdata.id)
    assert substvars.get('form_number_raw') == str(formdata.id)

def test_manual_display_id(pub):
    formdef.data_class().wipe()
    formdata = formdef.data_class()()
    formdata.id_display = 'bar'
    formdata.store()
    substvars = formdata.get_substitution_variables()
    assert substvars.get('form_number') == 'bar'
    assert substvars.get('form_number_raw') == str(formdata.id)

def test_submission_context(pub):
    formdef.data_class().wipe()
    formdata = formdef.data_class()()
    formdata.backoffice_submission = True
    formdata.submission_channel = 'mail'
    formdata.submission_context = {
        'mail_url': 'http://www.example.com/test.pdf',
    }
    substvars = formdata.get_substitution_variables()
    assert substvars.get('form_submission_backoffice') is True
    assert substvars.get('form_submission_channel') == 'mail'
    assert substvars.get('form_submission_channel_label') == 'Mail'
    assert substvars.get('form_submission_context_mail_url') == 'http://www.example.com/test.pdf'

    formdata = formdef.data_class()()
    substvars = formdata.get_substitution_variables()
    assert substvars.get('form_submission_backoffice') is False
    assert substvars.get('form_submission_channel') is None
    assert substvars.get('form_submission_channel_label') == 'Web'

def test_just_created(pub):
    formdef.data_class().wipe()
    formdata = formdef.data_class()()
    formdata.just_created()
    formdata.store()
    substvars = formdata.get_substitution_variables()
    assert substvars.get('form_status') == 'Just Submitted'
    assert substvars.get('form_status_is_endpoint') == False
    assert substvars.get('form_receipt_date')
    assert substvars.get('form_receipt_time')
    assert substvars.get('form_receipt_datetime')
    assert substvars.get('form_evolution')

def test_field(pub):
    formdef.data_class().wipe()
    formdef.fields = [fields.StringField(id='0', label='string')]
    formdef.store()
    formdata = formdef.data_class()()
    substvars = formdata.get_substitution_variables()
    assert not substvars.get('form_f0')

    formdata.data = {'0': 'test'}
    substvars = formdata.get_substitution_variables()
    assert substvars.get('form_f0') == 'test'
    assert substvars.get('form_field_string') == 'test'

def test_field_varname(pub):
    formdef.data_class().wipe()
    formdef.fields = [fields.StringField(id='0', label='string', varname='foo')]
    formdef.store()
    formdata = formdef.data_class()()
    formdata.data = {'0': 'test'}
    substvars = formdata.get_substitution_variables()
    assert substvars.get('form_f0') == 'test'
    assert substvars.get('form_var_foo') == 'test'

def test_file_field(pub):
    formdef.data_class().wipe()
    formdef.fields = [fields.FileField(id='0', label='file', varname='foo')]
    formdef.store()
    formdata = formdef.data_class()()
    upload = Upload('test.txt', 'text/plain', 'ascii')
    upload.receive([b'first line', b'second line'])
    formdata.data = {'0': upload}
    formdata.id = 1
    substvars = formdata.get_substitution_variables()
    assert substvars.get('form_var_foo') == 'test.txt'
    assert substvars.get('form_var_foo_url').endswith('/foobar/1/download?f=0')
    assert isinstance(substvars.get('form_var_foo_raw'), Upload)

    formdata.data = {'0': None}
    substvars = formdata.get_substitution_variables()
    assert substvars['form_var_foo'] is None
    assert substvars['form_var_foo_raw'] is None
    assert substvars['form_var_foo_url'] is None

    formdata.data = {}
    substvars = formdata.get_substitution_variables()
    assert substvars['form_var_foo'] is None
    assert substvars['form_var_foo_raw'] is None
    assert substvars['form_var_foo_url'] is None

def test_get_submitter(pub):
    formdef.data_class().wipe()
    formdef.fields = [fields.StringField(id='0', label='email', varname='foo',
        prefill={'type': 'user', 'value': 'email'})]
    formdef.store()

    formdata = formdef.data_class()()
    assert formdef.get_submitter_email(formdata) is None
    formdata.data = {'0': 'foo@localhost'}
    assert formdef.get_submitter_email(formdata) == 'foo@localhost'

    user = pub.user_class()
    user.email = 'bar@localhost'
    user.store()

    formdata.user_id = user.id
    assert formdef.get_submitter_email(formdata) == 'foo@localhost'

    formdata.data = {}
    assert formdef.get_submitter_email(formdata) == 'bar@localhost'

def test_get_last_update_time(pub):
    formdef.data_class().wipe()
    formdef.store()

    formdata = formdef.data_class()()
    assert formdata.last_update_time is None

    formdata.just_created()
    assert formdata.last_update_time == formdata.evolution[-1].time

    time.sleep(1)
    evo = Evolution()
    evo.time = time.localtime()
    evo.status = formdata.status
    evo.comment = 'hello world'
    formdata.evolution.append(evo)
    assert formdata.last_update_time != formdata.receipt_time
    assert formdata.last_update_time == formdata.evolution[-1].time

    # check with missing 'evolution' values
    formdata.evolution = None
    assert formdata.last_update_time == formdata.receipt_time

def test_password_field(pub):
    formdef.data_class().wipe()
    formdef.fields = [fields.PasswordField(id='0', label='pwd')]
    formdef.store()
    formdata = formdef.data_class()()
    formdata.data = {'0': {'cleartext': 'foo'}}
    formdata.store()

    formdata2 = formdata.get(formdata.id)
    assert formdata2.data == {'0': {'cleartext': 'foo'}}

def test_date_field(pub):
    formdef.data_class().wipe()
    formdef.fields = [fields.DateField(id='0', label='date')]
    formdef.store()
    formdata = formdef.data_class()()
    value = time.strptime('2015-05-12', '%Y-%m-%d')
    formdata.data = {'0': value}
    formdata.store()

    formdata2 = formdata.get(formdata.id)
    assert formdata2.data == {'0': value}

    assert formdata2.get_substitution_variables()['form_field_date'] == '2015-05-12'
    pub.cfg['language'] = {'language': 'fr'}
    assert formdata2.get_substitution_variables()['form_field_date'] == '12/05/2015'
    pub.cfg['language'] = {'language': 'en'}

def test_clean_drafts(pub):
    formdef = FormDef()
    formdef.name = 'foo'
    formdef.fields = []
    formdef.store()
    formdef.data_class().wipe()

    d = formdef.data_class()()
    d.status = 'draft'
    d.receipt_time = time.localtime()
    d.store()
    d_id1 = d.id

    d = formdef.data_class()()
    d.status = 'draft'
    d.receipt_time = time.localtime(0) # epoch, 1970-01-01
    d.store()
    d_id2 = d.id

    assert formdef.data_class().count() == 2
    from wcs.formdef import clean_drafts
    clean_drafts(pub)
    assert formdef.data_class().count() == 1
    assert formdef.data_class().select()[0].id == d_id1

def test_criticality_levels(pub):
    workflow = Workflow(name='criticality')
    workflow.criticality_levels = [
        WorkflowCriticalityLevel(name='green'),
        WorkflowCriticalityLevel(name='yellow'),
        WorkflowCriticalityLevel(name='red'),
    ]
    workflow.store()

    formdef = FormDef()
    formdef.name = 'foo'
    formdef.fields = []
    formdef.workflow_id = workflow.id
    formdef.store()
    formdef.data_class().wipe()

    d = formdef.data_class()()
    assert d.get_criticality_level_object().name == 'green'
    d.increase_criticality_level()
    assert d.get_criticality_level_object().name == 'yellow'
    d.increase_criticality_level()
    assert d.get_criticality_level_object().name == 'red'
    d.increase_criticality_level()
    assert d.get_criticality_level_object().name == 'red'
    d.decrease_criticality_level()
    assert d.get_criticality_level_object().name == 'yellow'
    d.decrease_criticality_level()
    assert d.get_criticality_level_object().name == 'green'
    d.decrease_criticality_level()
    assert d.get_criticality_level_object().name == 'green'
    d.set_criticality_level(1)
    assert d.get_criticality_level_object().name == 'yellow'
    d.set_criticality_level(2)
    assert d.get_criticality_level_object().name == 'red'
    d.set_criticality_level(4)
    assert d.get_criticality_level_object().name == 'red'

    workflow.criticality_levels = [WorkflowCriticalityLevel(name='green')]
    workflow.store()
    formdef = FormDef.get(id=formdef.id) # reload formdef
    d = formdef.data_class()()
    assert d.get_criticality_level_object().name == 'green'
    d.increase_criticality_level()
    assert d.get_criticality_level_object().name == 'green'

    workflow.criticality_levels = [
        WorkflowCriticalityLevel(name='green'),
        WorkflowCriticalityLevel(name='yellow'),
    ]
    workflow.store()
    formdef = FormDef.get(id=formdef.id) # reload formdef
    d = formdef.data_class()()
    d.criticality_level = 104
    # set too high, this simulates a workflow being changed to have less
    # levels than before.
    assert d.get_criticality_level_object().name == 'yellow'
    d.increase_criticality_level()
    assert d.get_criticality_level_object().name == 'yellow'
    d.decrease_criticality_level()
    assert d.get_criticality_level_object().name == 'green'

    d.criticality_level = 104
    d.decrease_criticality_level()
    assert d.get_criticality_level_object().name == 'green'

    assert d.get_static_substitution_variables().get('form_criticality_label') == 'green'
    assert d.get_substitution_variables().get('form_criticality_label') == 'green'

def test_field_item_substvars(pub):
    ds = {
        'type': 'formula',
        'value': repr([('1', 'un'), ('2', 'deux')]),
    }

    formdef = FormDef()
    formdef.name = 'foobar'
    formdef.fields = [fields.ItemField(id='0', label='string', data_source=ds,
        varname='xxx')]
    formdef.store()

    formdata = formdef.data_class()()
    formdata.data = {'0': '1', '0_display': 'un'}

    variables = formdata.get_substitution_variables()
    assert variables.get('form_var_xxx') == 'un'
    assert variables.get('form_var_xxx_raw') == '1'

def test_get_json_export_dict_evolution(pub, local_user):
    Workflow.wipe()
    workflow = Workflow(name='test')
    st_new = workflow.add_status('New')
    st_finished = workflow.add_status('Finished')
    workflow.store()

    formdef = FormDef()
    formdef.workflow_id = workflow.id
    formdef.name = 'foo'
    formdef.fields = []
    formdef.store()
    formdef.data_class().wipe()

    d = formdef.data_class()()
    d.status = 'wf-%s' % st_new.id
    d.user_id = local_user.id
    d.receipt_time = time.localtime()
    evo = Evolution()
    evo.time = time.localtime()
    evo.status = 'wf-%s' % st_new.id
    evo.who = '_submitter'
    d.evolution = [evo]
    d.store()
    evo.add_part(JournalEvolutionPart(d, "ok"))
    evo.add_part(JournalWsCallErrorPart("summary", "label", "data"))
    evo = Evolution()
    evo.time = time.localtime()
    evo.status = 'wf-%s' % st_finished.id
    evo.who = '_submitter'
    d.evolution.append(evo)
    d.store()

    export = d.get_json_export_dict()
    assert 'evolution' in export
    assert len(export['evolution']) == 2
    assert export['evolution'][0]['status'] == st_new.id
    assert 'time' in export['evolution'][0]
    assert export['evolution'][0]['who']['id'] == local_user.id
    assert export['evolution'][0]['who']['email'] == local_user.email
    assert export['evolution'][0]['who']['NameID'] == local_user.name_identifiers
    assert 'parts' in export['evolution'][0]
    assert len(export['evolution'][0]['parts']) == 2
    assert export['evolution'][0]['parts'][0]['type'] == 'workflow-comment'
    assert export['evolution'][0]['parts'][0]['content'] == 'ok'
    assert export['evolution'][0]['parts'][1]['type'] == 'wscall-error'
    assert export['evolution'][0]['parts'][1]['summary'] == 'summary'
    assert export['evolution'][0]['parts'][1]['label'] == 'label'
    assert export['evolution'][0]['parts'][1]['data'] == 'data'
    assert export['evolution'][1]['status'] == st_finished.id
    assert 'time' in export['evolution'][1]
    assert export['evolution'][1]['who']['id'] == local_user.id
    assert export['evolution'][1]['who']['email'] == local_user.email
    assert export['evolution'][1]['who']['NameID'] == local_user.name_identifiers
    assert 'parts' not in export['evolution'][1]

    export = d.get_json_export_dict(anonymise=True)
    assert 'evolution' in export
    assert len(export['evolution']) == 2
    assert export['evolution'][0]['status'] == st_new.id
    assert 'time' in export['evolution'][0]
    assert 'who' not in export['evolution'][0]
    assert 'parts' in export['evolution'][0]
    assert len(export['evolution'][0]['parts']) == 2
    assert len(export['evolution'][0]['parts'][0]) == 1
    assert export['evolution'][0]['parts'][0]['type'] == 'workflow-comment'
    assert len(export['evolution'][0]['parts'][1]) == 1
    assert export['evolution'][0]['parts'][1]['type'] == 'wscall-error'
    assert export['evolution'][1]['status'] == st_finished.id
    assert 'time' in export['evolution'][1]
    assert 'who' not in export['evolution'][0]
    assert 'parts' not in export['evolution'][1]

def test_field_bool_substvars(pub):
    formdef = FormDef()
    formdef.name = 'foobar'
    formdef.fields = [fields.BoolField(id='0', label='checkbox', varname='xxx')]
    formdef.store()

    formdata = formdef.data_class()()

    formdata.data = {'0': False}
    variables = formdata.get_substitution_variables()
    assert variables.get('form_var_xxx') == 'False'
    assert variables.get('form_var_xxx_raw') is False

    formdata.data = {'0': True}
    variables = formdata.get_substitution_variables()
    assert variables.get('form_var_xxx') == 'True'
    assert variables.get('form_var_xxx_raw') is True

def test_backoffice_field_varname(pub):
    wf = Workflow(name='bo fields')
    wf.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(wf)
    wf.backoffice_fields_formdef.fields = [
        fields.StringField(id='bo1', label='1st backoffice field',
            type='string', varname='backoffice_blah'),
    ]
    st1 = wf.add_status('Status1')
    wf.store()

    formdef.workflow_id = wf.id
    formdef.data_class().wipe()
    formdef.fields = [fields.StringField(id='0', label='string', varname='foo')]
    formdef.store()
    formdata = formdef.data_class()()
    formdata.data = {'bo1': 'test'}
    substvars = formdata.get_substitution_variables()
    assert substvars.get('form_var_backoffice_blah') == 'test'

def test_workflow_data_file_url(pub):
    upload = PicklableUpload('test.txt', 'text/plain', 'ascii')
    upload.receive([b'first line', b'second line'])

    formdata = formdef.data_class()()
    formdata.store()
    # create workflow_data as ordered dict to be sure _url comes last, to
    # trigger #17233.
    formdata.workflow_data = collections.OrderedDict(
       foo_var_file='test.txt',
       foo_var_file_raw=upload,
       foo_var_file_url=None,
    )
    substvars = formdata.get_substitution_variables()
    assert substvars['foo_var_file_url']

def test_evolution_get_status(pub):
    Workflow.wipe()
    workflow = Workflow(name='test')
    st_new = workflow.add_status('New')
    st_finished = workflow.add_status('Finished')
    workflow.store()

    formdef = FormDef()
    formdef.workflow_id = workflow.id
    formdef.name = 'foo'
    formdef.fields = []
    formdef.store()
    formdef.data_class().wipe()

    d = formdef.data_class()()
    d.evolution = []

    evo = Evolution()
    evo.time = time.localtime()
    evo.status = 'wf-%s' % st_new.id
    d.evolution.append(evo)

    evo = Evolution()
    evo.time = time.localtime()
    d.evolution.append(evo)

    evo = Evolution()
    evo.time = time.localtime()
    d.evolution.append(evo)

    evo = Evolution()
    evo.time = time.localtime()
    evo.status = 'wf-%s' % st_finished.id
    d.evolution.append(evo)

    evo = Evolution()
    evo.time = time.localtime()
    d.evolution.append(evo)

    d.store()
    d = formdef.data_class().get(d.id)

    assert [x.get_status().id for x in d.evolution] == ['1', '1', '1', '2', '2']

@pytest.fixture
def variable_test_data(pub):
    pub.user_class.wipe()
    user = pub.user_class()
    user.email = 'bar@localhost'
    user.name_identifiers = ['....']
    user.store()

    role = Role(name='foobar')
    role.store()

    FormDef.wipe()
    formdef = FormDef()
    formdef.name = 'foobarlazy'
    formdef.fields = [
        fields.StringField(id='0', label='string', varname='foo_foo'),
        fields.BoolField(id='1', label='checkbox', varname='boolfield'),
        fields.BoolField(id='2', label='checkbox', varname='boolfield2'),
        fields.DateField(id='3', label='date', varname='datefield'),
        fields.ItemsField(id='4', label='items', items=['aa', 'ab', 'ac'], varname='itemsfield'),
        fields.FileField(id='5', label='file', varname='filefield'),
        fields.StringField(id='6', label='string2', varname='foo_foo_baz_baz'),
        fields.MapField(id='7', label='map', varname='map'),
        fields.DateField(id='8', label='date2', varname='datefield2'),
        fields.StringField(id='9', label='string2', varname='datestring'),
        fields.StringField(id='10', label='number1', varname='term1'),
        fields.StringField(id='11', label='number2', varname='term2'),
        fields.StringField(id='12', label='float1', varname='value'),
    ]
    formdef.workflow_roles = {'_receiver': role.id}
    formdef.geolocations = {'base': 'Base'}
    formdef.store()
    formdef.data_class().wipe()
    formdata = formdef.data_class()()
    formdata.just_created()
    formdata.user_id = user.id
    formdata.data = {
        '0': 'bar',
        '1': False,
        '2': True,
        '3': time.strptime('2018-07-31', '%Y-%m-%d'),
        '4': ['aa', 'ac'],
        '4_display': 'aa, ac',
        '5': PicklableUpload('test.txt', 'text/plain'),
        '6': 'other',
        '7': '2;4',  # map
        '8': time.strptime('2018-08-31', '%Y-%m-%d'),
        '9': '2018-07-31',
        '10': '3',
        '11': '4',
        '12': '3.14',
    }
    formdata.data['5'].receive([b'hello world'])
    formdata.geolocations = {'base': {'lat': 1, 'lon': 2}}
    formdata.store()
    pub.substitutions.feed(pub)
    pub.substitutions.feed(formdef)
    pub.substitutions.feed(formdata)
    return LazyFormData(formdata)

def test_lazy_formdata(pub, variable_test_data):
    formdata = FormDef.select()[0].data_class().select()[0]
    lazy_formdata = LazyFormData(formdata)
    assert lazy_formdata.receipt_date == time.strftime('%Y-%m-%d', formdata.receipt_time)
    assert lazy_formdata.receipt_time == time.strftime('%H:%M', formdata.receipt_time)
    assert lazy_formdata.internal_id == formdata.id
    assert lazy_formdata.name == 'foobarlazy'
    assert lazy_formdata.display_name == 'foobarlazy #%s' % formdata.get_display_id()
    assert lazy_formdata.page_no == 0
    assert lazy_formdata.url.endswith('/foobarlazy/%s/' % formdata.id)
    assert lazy_formdata.url_backoffice.endswith('/backoffice/management/foobarlazy/%s/' % formdata.id)
    assert lazy_formdata.backoffice_url == lazy_formdata.url_backoffice
    assert lazy_formdata.api_url == formdata.get_api_url()
    assert lazy_formdata.attachments
    assert lazy_formdata.geoloc['base'] == {'lat': 1, 'lon': 2}
    assert lazy_formdata.geoloc['base_lon'] == 2
    static_vars = formdata.get_static_substitution_variables()
    for attribute in ('name', 'receipt_date', 'receipt_time', 'previous_status',
            'uri', 'status_changed', 'comment', 'evolution', 'details',
            'criticality_level', 'digest'):
        assert getattr(lazy_formdata, attribute) == static_vars['form_' + attribute]

    assert lazy_formdata.user.email == 'bar@localhost'
    assert lazy_formdata.var.foo_foo == 'bar'
    assert lazy_formdata.var.boolfield == 'False'
    assert bool(lazy_formdata.var.boolfield) is False
    assert lazy_formdata.var.boolfield2 == 'True'
    assert bool(lazy_formdata.var.boolfield2) is True
    assert lazy_formdata.var.datefield.raw == time.strptime('2018-07-31', '%Y-%m-%d')
    assert lazy_formdata.var.datefield.tm_year == 2018
    assert lazy_formdata.var.datefield.tm_mon == 7
    assert lazy_formdata.var.datefield.tm_mday == 31
    for attr in ('tm_year', 'tm_mon', 'tm_mday', 'tm_hour', 'tm_min', 'tm_sec',
            'tm_wday', 'tm_yday'):
        getattr(lazy_formdata.var.datefield, attr)

    # flexible date comparison
    assert lazy_formdata.var.datefield == lazy_formdata.var.datefield
    assert not lazy_formdata.var.datefield == lazy_formdata.var.datefield2
    assert not lazy_formdata.var.datefield2 == lazy_formdata.var.datefield
    assert lazy_formdata.var.datefield != lazy_formdata.var.datefield2
    assert lazy_formdata.var.datefield2 != lazy_formdata.var.datefield
    assert not lazy_formdata.var.datefield != lazy_formdata.var.datefield
    assert lazy_formdata.var.datefield < lazy_formdata.var.datefield2
    assert lazy_formdata.var.datefield <= lazy_formdata.var.datefield2
    assert not lazy_formdata.var.datefield > lazy_formdata.var.datefield2
    assert not lazy_formdata.var.datefield >= lazy_formdata.var.datefield2
    assert lazy_formdata.var.datefield2 > lazy_formdata.var.datefield
    assert lazy_formdata.var.datefield2 >= lazy_formdata.var.datefield
    assert not lazy_formdata.var.datefield2 < lazy_formdata.var.datefield
    assert not lazy_formdata.var.datefield2 <= lazy_formdata.var.datefield

    assert lazy_formdata.var.datefield == lazy_formdata.var.datestring
    assert lazy_formdata.var.datestring == lazy_formdata.var.datefield
    assert lazy_formdata.var.datefield >= lazy_formdata.var.datestring
    assert lazy_formdata.var.datestring >= lazy_formdata.var.datefield
    assert lazy_formdata.var.datefield <= lazy_formdata.var.datestring
    assert lazy_formdata.var.datestring <= lazy_formdata.var.datefield
    assert not lazy_formdata.var.datefield != lazy_formdata.var.datestring
    assert not lazy_formdata.var.datestring != lazy_formdata.var.datefield
    assert not lazy_formdata.var.datefield < lazy_formdata.var.datestring
    assert not lazy_formdata.var.datestring < lazy_formdata.var.datefield
    assert not lazy_formdata.var.datefield > lazy_formdata.var.datestring
    assert not lazy_formdata.var.datestring > lazy_formdata.var.datefield

    for date in ('2018-07-31', '2018-07-31 00:00', '2018-07-31 00:00:00',
                 '31/07/2018', '31/07/2018 00h00', '31/07/2018 00:00:00',
                 datetime.date(2018, 7, 31),
                 datetime.datetime(2018, 7, 31, 0, 0),
                 time.strptime('2018-07-31', '%Y-%m-%d')):
        assert lazy_formdata.var.datefield == date
        assert lazy_formdata.var.datefield >= date
        assert lazy_formdata.var.datefield <= date
        assert date == lazy_formdata.var.datefield
        assert date <= lazy_formdata.var.datefield
        assert date >= lazy_formdata.var.datefield
        assert not lazy_formdata.var.datefield != date
        assert not date != lazy_formdata.var.datefield
        assert not lazy_formdata.var.datefield > date
        assert not lazy_formdata.var.datefield < date
        assert not date < lazy_formdata.var.datefield
        assert not date > lazy_formdata.var.datefield

    for date in ('2018-08-31', '2018-07-31 01:00', '2018-07-31 01:00:00',
                 '31/08/2018', '31/07/2018 01h00', '31/07/2018 01:00:00',
                 datetime.date(2018, 8, 31),
                 datetime.datetime(2018, 8, 31, 0, 0),
                 time.strptime('2018-08-31', '%Y-%m-%d')):
        assert lazy_formdata.var.datefield != date
        assert date != lazy_formdata.var.datefield
        assert not lazy_formdata.var.datefield == date
        assert not date == lazy_formdata.var.datefield
        assert lazy_formdata.var.datefield < date
        assert not lazy_formdata.var.datefield >= date
        assert lazy_formdata.var.datefield <= date
        assert not lazy_formdata.var.datefield > date
        assert date > lazy_formdata.var.datefield
        assert not date <= lazy_formdata.var.datefield
        assert date >= lazy_formdata.var.datefield
        assert not date < lazy_formdata.var.datefield

    assert lazy_formdata.var.itemsfield == 'aa, ac'
    assert 'aa' in lazy_formdata.var.itemsfield  # taken as a list
    assert 'aa,' not in lazy_formdata.var.itemsfield  # not as a string
    assert lazy_formdata.var.filefield == 'test.txt'
    assert lazy_formdata.var.filefield.raw.base_filename == 'test.txt'
    assert lazy_formdata.var.filefield.raw.content_type == 'text/plain'

    formdata = FormDef.select()[0].data_class()
    lazy_formdata = LazyFormData(formdata)
    assert lazy_formdata.tracking_code is None
    formdata.data = {'future_tracking_code': 'CDCBGWQX'}
    assert lazy_formdata.tracking_code == 'CDCBGWQX'

    formdata = FormDef.select()[0].data_class().select()[0]
    lazy_formdata = LazyFormData(formdata)
    assert lazy_formdata.tracking_code is None

    tracking_code = pub.tracking_code_class()
    tracking_code.formdata = formdata
    tracking_code.store()
    formdata = FormDef.select()[0].data_class().get(formdata.id)
    lazy_formdata = LazyFormData(formdata)
    assert lazy_formdata.tracking_code == tracking_code.id

def test_lazy_formdata_queryset(pub, variable_test_data):
    lazy_formdata = variable_test_data
    data_class = lazy_formdata._formdef.data_class()
    for i in range(6):
        formdata = data_class()
        formdata.just_created()
        formdata.store()
    for i in range(4):
        formdata = data_class()
        formdata.just_created()
        formdata.jump_status('finished')
        formdata.store()

    formdata = data_class()
    formdata.status = 'draft'
    formdata.store()

    assert lazy_formdata.objects.count == 11
    assert lazy_formdata.objects.drafts().count == 1
    assert lazy_formdata.objects.pending().count == 7
    assert lazy_formdata.objects.done().count == 4
    assert lazy_formdata.objects.drafts().count == 1
    # check __len__
    assert len(lazy_formdata.objects.drafts()) == 1

    assert lazy_formdata.objects.current_user().count == 0
    pub.get_request()._user = ()  # reset cache
    pub.get_request().session = sessions.BasicSession(id=1)
    pub.get_request().session.set_user(pub.user_class.select()[0].id)
    assert lazy_formdata.objects.current_user().count == 1

    qs = lazy_formdata.objects.drafts()
    # check __iter__
    for draft in qs:
        assert draft.internal_id == formdata.id
    # check __getitem__
    assert qs[0].internal_id == formdata.id
    # check against for cached resultset
    assert qs[0].internal_id == formdata.id
    # check __iter__ with cached resultset
    for draft in qs:
        assert draft.internal_id == formdata.id

    # check __iter__ creates a cached resultset
    qs = lazy_formdata.objects.drafts()
    for formdata1, formdata2 in zip(list(qs), list(qs)):
        assert formdata1 is formdata2

    # check ordering
    qs = lazy_formdata.objects.pending().order_by('id')
    assert [x.number for x in qs] == ['1-1', '1-2', '1-3', '1-4', '1-5', '1-6', '1-7']

    # Check accessing an non-numeric attribute doesn't try to cache things
    # (see code for explanation)
    manager = lazy_formdata.objects
    with pytest.raises(TypeError):
        manager['drafts']
    assert manager._cached_resultset is None

def test_lazy_formdata_queryset_distance(pub, variable_test_data):
    lazy_formdata = variable_test_data
    formdef = lazy_formdata._formdef
    formdef.geolocations = {'base': 'Base'}
    formdef.store()
    data_class = lazy_formdata._formdef.data_class()
    for i in range(6):
        formdata = data_class()
        formdata.geolocations = {'base': {'lat': i, 'lon': i}}
        formdata.just_created()
        formdata.store()
    for i in range(4):
        formdata = data_class()
        formdata.geolocations = {'base': {'lat': i + 0.5, 'lon': i + 0.5}}
        formdata.just_created()
        formdata.jump_status('finished')
        formdata.store()

    formdata = data_class()
    formdata.status = 'draft'
    formdata.store()

    # compute distance against map field of lazy formdata
    nearby = lazy_formdata.objects.distance_filter(200000)
    assert len(nearby) == 6
    assert set([x.number for x in nearby]) == set(['1-1', '1-3', '1-4', '1-8', '1-9', '1-10'])

    # compute distance against geolocation
    lazy_formdata._formdata.geolocations = {'base': {'lat': 2, 'lon': 2.5}}
    nearby = lazy_formdata.objects.distance_filter(200000)
    assert len(nearby) == 6
    assert set([x.number for x in nearby]) == set(['1-1', '1-4', '1-5', '1-9', '1-10', '1-11'])
    assert bool(nearby) is True

    lazy_formdata._formdata.geolocations = {'base': {'lat': 7, 'lon': 7.5}}
    nearby = lazy_formdata.objects.distance_filter(200000)
    assert bool(nearby) is False
    assert len(nearby) == 0

def test_lazy_variables(pub, variable_test_data):
    formdata = FormDef.select()[0].data_class().select()[0]
    for mode in (None, 'lazy'):
        context = pub.substitutions.get_context_variables(mode=mode)
        assert context['form_number'] == formdata.get_display_id()
        assert context['form_display_name'] == formdata.get_display_name()
        assert context['form_var_foo_foo'] == 'bar'
        with pytest.raises(KeyError):
            context['form_var_xxx']
        assert 'bar' in context['form_var_foo_foo']
        assert context['form_var_foo_foo'] + 'ab' == 'barab'
        for item in enumerate(context['form_var_foo_foo']):
            assert item in [(0, 'b'), (1, 'a'), (2, 'r')]
        assert context['form_var_foo_foo_baz_baz'] == 'other'

def test_lazy_variables_missing(pub, variable_test_data):
    formdef =  FormDef.select()[0]
    formdata = formdef.data_class()()
    formdata.just_created()
    formdata.data = {
        '0': 'bar',
    }
    pub.substitutions.reset()
    pub.substitutions.feed(formdef)
    pub.substitutions.feed(formdata)
    for mode in (None, 'lazy'):
        context = pub.substitutions.get_context_variables(mode=mode)
        assert context['form_var_foo_foo_baz_baz'] is None
        assert context['form_var_foo_foo'] == 'bar'
        with pytest.raises(KeyError):
            assert context['form_var_foo_foo_xxx'] == 'bar'

def test_lazy_map_variable(pub, variable_test_data):
    formdef = FormDef.select()[0]
    formdata = formdef.data_class().select()[0]
    for mode in (None, 'lazy'):
        pub.substitutions.reset()
        pub.substitutions.feed(formdef)
        with pub.substitutions.temporary_feed(formdata, force_mode=mode):
            assert WorkflowStatusItem.compute('=form_var_map', raises=True) == '2;4'
            assert WorkflowStatusItem.compute('{{ form_var_map }}', raises=True) == '2;4'
            assert WorkflowStatusItem.compute('=form_var_map.split(";")[0]', raises=True) == '2'
            assert WorkflowStatusItem.compute('{{ form_var_map|split:";"|first }}', raises=True) == '2'
            assert WorkflowStatusItem.compute('=form_var_map_lat', raises=True) == 2
            assert WorkflowStatusItem.compute('{{ form_var_map_lat }}', raises=True) == '2.0'
            assert WorkflowStatusItem.compute('=form_var_map_lon', raises=True) == 4
            assert WorkflowStatusItem.compute('{{ form_var_map_lon }}', raises=True) == '4.0'

            assert WorkflowStatusItem.compute('{{ form_var_map|distance:form_var_map|floatformat }}', raises=True) == '0'
            assert WorkflowStatusItem.compute('{{ form_var_map|distance:"2.1;4.1"|floatformat }}', raises=True) == '15685.4'
            assert WorkflowStatusItem.compute('{{ "2.1;4.1"|distance:form_var_map|floatformat }}', raises=True) == '15685.4'
            assert WorkflowStatusItem.compute('{{ form|distance:"1;2"|floatformat }}', raises=True) == '0'
            assert WorkflowStatusItem.compute('{{ form|distance:"1.1;2.1"|floatformat }}', raises=True) == '15689.1'
            assert WorkflowStatusItem.compute('{{ "1.1;2.1"|distance:form|floatformat }}', raises=True) == '15689.1'
            assert WorkflowStatusItem.compute('{{ form|distance:form_var_map|floatformat }}', raises=True) == '248515.5'
            assert WorkflowStatusItem.compute('{{ form_var_map|distance:form|floatformat }}', raises=True) == '248515.5'

    formdata.data['7'] = None
    formdata.store()
    pub.substitutions.reset()
    pub.substitutions.feed(formdef)
    pub.substitutions.feed(formdata)
    for mode in (None, 'lazy'):
        pub.substitutions.reset()
        pub.substitutions.feed(formdef)
        with pub.substitutions.temporary_feed(formdata, force_mode=mode):
            assert WorkflowStatusItem.compute('=form_var_map', raises=True) is None
            assert WorkflowStatusItem.compute('{{ form_var_map|distance:"1;2"|floatformat }}', raises=True) == ''
            assert WorkflowStatusItem.compute('{{ "1;2"|distance:form_var_map|floatformat }}', raises=True) == ''

def test_lazy_conditions(pub, variable_test_data):
    condition = Condition({'type': 'django', 'value': 'form_var_foo_foo == "bar"'})
    assert condition.evaluate() is True

    condition = Condition({'type': 'django', 'value': 'form_var_foo_foo|startswith:"ba"'})
    assert condition.evaluate() is True

    condition = Condition({'type': 'django', 'value': 'form_var_foo_foo|startswith:"fo"'})
    assert condition.evaluate() is False

    condition = Condition({'type': 'django', 'value': 'form_var_foo_foo|slice:":2" == "ba"'})
    assert condition.evaluate() is True

    condition = Condition({'type': 'django', 'value': 'form_var_foo_foo|slice:":2" == "fo"'})
    assert condition.evaluate() is False

    condition = Condition({'type': 'django', 'value': 'form.var.foo_foo == "bar"'})
    assert condition.evaluate() is True

    condition = Condition({'type': 'django', 'value': 'form_field_string == "bar"'})
    assert condition.evaluate() is True

    condition = Condition({'type': 'django', 'value': 'form_role_receiver_name == "foobar"'})
    assert condition.evaluate() is True

    condition = Condition({'type': 'django', 'value': 'form_user_email == "bar@localhost"'})
    assert condition.evaluate() is True

    condition = Condition({'type': 'django', 'value': 'form_var_filefield_raw_content_type == "text/plain"'})
    assert condition.evaluate() is True

    condition = Condition({'type': 'django', 'value': 'form_user_admin_access'})
    assert condition.evaluate() is False

    condition = Condition({'type': 'django', 'value': 'form_user_backoffice_access'})
    assert condition.evaluate() is False

    condition = Condition({'type': 'python', 'value':
        'vars().get("form_var_foo_foo_xxx", "") == ""'})
    assert condition.evaluate() is True

    user = pub.user_class.select()[0]
    user.is_admin = True
    user.store()

    condition = Condition({'type': 'django', 'value': 'form_user_admin_access'})
    assert condition.evaluate() is True

    condition = Condition({'type': 'django', 'value': 'form_user_backoffice_access'})
    assert condition.evaluate() is True

    condition = Condition({'type': 'django', 'value': 'form_var_datefield == "2018-07-31"'})
    assert condition.evaluate() is True
    condition = Condition({'type': 'django', 'value': 'form_var_datefield != "2018-07-31"'})
    assert condition.evaluate() is False
    condition = Condition({'type': 'django', 'value': 'form_var_datefield == "31/07/2018"'})
    assert condition.evaluate() is True
    condition = Condition({'type': 'django', 'value': 'form_var_datefield != "31/07/2018"'})
    assert condition.evaluate() is False
    condition = Condition({'type': 'django', 'value': 'form_var_datefield >= "31/07/2018"'})
    assert condition.evaluate() is True
    condition = Condition({'type': 'django', 'value': 'form_var_datefield <= "31/07/2018"'})
    assert condition.evaluate() is True
    condition = Condition({'type': 'django', 'value': 'form_var_datefield > "31/07/2018"'})
    assert condition.evaluate() is False
    condition = Condition({'type': 'django', 'value': 'form_var_datefield < "31/07/2018"'})
    assert condition.evaluate() is False
    condition = Condition({'type': 'django', 'value': '"2018-07-31" == form_var_datefield'})
    assert condition.evaluate() is True
    condition = Condition({'type': 'django', 'value': '"31/07/2018" == form_var_datefield'})
    assert condition.evaluate() is True
    condition = Condition({'type': 'django', 'value': 'form_var_datefield != form_var_datefield2'})
    assert condition.evaluate() is True
    condition = Condition({'type': 'django', 'value': 'form_var_datefield == form_var_datefield2'})
    assert condition.evaluate() is False
    condition = Condition({'type': 'django', 'value': 'form_var_datefield < form_var_datefield2'})
    assert condition.evaluate() is True
    condition = Condition({'type': 'django', 'value': 'form_var_datefield > form_var_datefield2'})
    assert condition.evaluate() is False
    condition = Condition({'type': 'django', 'value': 'form_var_datefield <= form_var_datefield2'})
    assert condition.evaluate() is True
    condition = Condition({'type': 'django', 'value': 'form_var_datefield >= form_var_datefield2'})
    assert condition.evaluate() is False
    # compare with string var representing a date
    condition = Condition({'type': 'django', 'value': 'form_var_datefield == form_var_datestring'})
    assert condition.evaluate() is True
    condition = Condition({'type': 'django', 'value': 'form_var_datestring == "2018-07-31"'})
    assert condition.evaluate() is True
    condition = Condition({'type': 'django', 'value': 'form_var_datestring == "31/07/2018"'})
    assert condition.evaluate() is False  # datestring is not a date !
    # non existing form_var
    condition = Condition({'type': 'django', 'value': 'form_var_datefield == form_var_barbarbar'})
    assert condition.evaluate() is False
    condition = Condition({'type': 'django', 'value': 'not form_var_datefield == form_var_barbarbar'})
    assert condition.evaluate() is True

def test_has_role_templatetag(pub, variable_test_data):
    condition = Condition({'type': 'django', 'value': 'form_user|has_role:"foobar"'})
    assert condition.evaluate() is False
    condition = Condition({'type': 'django', 'value': 'form_user|has_role:form_role_receiver_name'})
    assert condition.evaluate() is False

    role = Role.select()[0]
    user = pub.user_class.select()[0]
    user.roles = [role.id, '42']  # role.id 42 does not exist
    user.store()

    condition = Condition({'type': 'django', 'value': 'form_user|has_role:"foobar"'})
    assert condition.evaluate() is True
    condition = Condition({'type': 'django', 'value': 'form_user|has_role:form_role_receiver_name'})
    assert condition.evaluate() is True
    condition = Condition({'type': 'django', 'value': 'form_user|has_role:"barfoo"'})
    assert condition.evaluate() is False
    condition = Condition({'type': 'django', 'value': 'form_user|has_role:form_var_foo_foo'})
    assert condition.evaluate() is False

    # non-user object
    condition = Condition({'type': 'django', 'value': 'form_var_foo_foo|has_role:"foobar"'})
    assert condition.evaluate() is False
    condition = Condition({'type': 'django', 'value': 'xxx|has_role:"foobar"'})
    assert condition.evaluate() is False

def test_lazy_now_and_today(pub, variable_test_data):
    for condition_value in (
            'now > "1970-01-01"',
            '"1970-01-01" < now',
            'now < "2100-01-01"',
            '"2100-01-01" > now',
            'not now < "1970-01-01"',
            'not "1970-01-01" > now',
            'not now > "2100-01-01"',
            'not "2100-01-01" < now',
            # form_var_datefield is in 2018, we hope now is after 2019
            'form_var_datefield < now',
            'not form_var_datefield > now',
            'form_var_datefield <= now',
            'not form_var_datefield >= now',
            'form_var_datefield != now',
            'not form_var_datefield == now'):
        condition = Condition({'type': 'django', 'value': condition_value})
        assert condition.evaluate() is True
        condition = Condition({'type': 'django', 'value': condition_value.replace('now', 'today')})
        assert condition.evaluate() is True

def test_lazy_date_templatetags(pub, variable_test_data):
    for condition_value in (
            '"2017-10-10"|date == "2017-10-10"',
            '"2017-10-10"|date == "2017-10-10 00:00"',
            '"2017-10-10 00:00"|date == "2017-10-10 00:00"',
            '"2017-10-10"|date == "10/10/2017"',
            '"2017-10-10"|date == "10/10/2017 00:00"',
            '"2017-10-10"|date == "10/10/2017 00h00"',
            'not "2017-10-10"|date == "11/10/2017"',
            '"2017-10-10"|date != "11/10/2017"',
            'not "2017-10-10"|date != "10/10/2017"',
            '"2017-10-10"|date > "09/10/2017"',
            'not "2017-10-10"|date > "10/10/2017"',
            '"2017-10-10"|date < "11/10/2017"',
            'not "2017-10-10"|date < "10/10/2017"',
            '"2017-10-10"|date >= "09/10/2017"',
            '"2017-10-10"|date <= "10/10/2017"',
            '"2017-10-10"|date >= "10/10/2017"',
            ):
        condition = Condition({'type': 'django', 'value': condition_value})
        assert condition.evaluate() is True
        condition = Condition({'type': 'django', 'value':
                condition_value.replace('date', 'datetime')})
        assert condition.evaluate() is True
        # |date on the right member
        condition = Condition({'type': 'django', 'value':
                condition_value.replace('|date', '') + '|date'})
        assert condition.evaluate() is True
        condition = Condition({'type': 'django', 'value':
                condition_value.replace('|date', '') + '|datetime'})
        assert condition.evaluate() is True

    for condition_value in (
            '"2017-10-11 12:13"|datetime == "2017-10-11 12:13"',
            '"2017-10-11 12:13:14"|datetime == "2017-10-11 12:13:14"',
            '"2017-10-11 12:13"|datetime != "2017-10-11 12:13:14"',
            'not "2017-10-11 12:13"|datetime == "2017-10-11 12:13:14"',
            '"2017-10-11 12:13"|datetime < "2017-10-11 12:13:14"',
            '"2017-10-11 12:13"|datetime <= "2017-10-11 12:13:14"',
            '"2017-10-11 12:13:14"|datetime <= "2017-10-11 12:13:14"',
            '"2017-10-11 12:13:14"|datetime > "2017-10-11 12:13"',
            '"2017-10-11 12:13:14"|datetime >= "2017-10-11 12:13"',
            '"2017-10-11 12:13:14"|datetime >= "2017-10-11 12:13:14"',
            ):
        condition = Condition({'type': 'django', 'value': condition_value})
        assert condition.evaluate() is True
        # |datetime on the right member
        condition = Condition({'type': 'django', 'value':
                condition_value.replace('|datetime', '') + '|datetime'})
        assert condition.evaluate() is True

    condition = Condition({'type': 'django',
            'value': '"2017-10-11 00:00"|datetime == "2017-10-11"|date'})
    assert condition.evaluate() is True
    condition = Condition({'type': 'django',
            'value': '"2017-10-11 12:13"|date == "2017-10-11"'})
    assert condition.evaluate() is True
    condition = Condition({'type': 'django',
            'value': '"2017-10-11 12:13"|date == "2017-10-11 00:00"|datetime'})
    assert condition.evaluate() is True
    condition = Condition({'type': 'django',
            'value': '"2017-10-11 12:13"|date == "2017-10-11 14:15"|date'})
    assert condition.evaluate() is True
    condition = Condition({'type': 'django',
            'value': '"2017-10-11 01:00"|datetime|date == "2017-10-11"'})
    assert condition.evaluate() is True

    condition = Condition({'type': 'django', 'value': 'now|date == today'})
    assert condition.evaluate() is True
    condition = Condition({'type': 'django', 'value': 'today == now|date'})
    assert condition.evaluate() is True

def test_lazy_date_with_maths(pub, variable_test_data):
    # form_var_datefield  : 2018-07-31
    # form_var_datefield2 : 2018-08-31
    # form_var_datestring : "2018-07-31"
    for condition_value in (
            'form_var_datefield|add_days:0 <= "2019-01-01"',
            'form_var_datefield|add_days:0 >= "1980-01-01"',
            'form_var_datefield|add_days:0 == "2018-07-31"',
            'form_var_datefield|add_days:0 == "31/07/2018"',
            'form_var_datefield|add_days:5 == "2018-08-05"',
            'form_var_datefield|add_days:5 <= "2018-08-05"',
            'form_var_datefield|add_days:5 >= "2018-08-05"',
            'form_var_datefield|add_days:-5 == "2018-07-26"',
            'form_var_datefield|add_days:36500 > "2100-01-01"',   # 2118
            'form_var_datefield|add_days:-36500 < "1950-01-01"',  # 1918
            'form_var_datefield|add_days:31 == form_var_datefield2',
            'form_var_datefield|add_days:5|add_days:-5 == form_var_datestring',
            'form_var_datestring|add_days:31 == form_var_datefield2',
            'form_var_datestring|add_days:32 > form_var_datefield2',
            'form_var_datestring|add_days:30 < form_var_datefield2',
            ):
        condition = Condition({'type': 'django', 'value': condition_value})
        assert condition.evaluate() is True

    # form_var_datefield is in 2018, we hope today/now is after 2019
    for condition_value in (
            'form_var_datefield <= today',
            '"1970-01-01"|add_days:0 < today',
            '"2100-12-31"|add_days:0 > today',
            'form_var_datefield|add_days:0 <= today',
            'form_var_datefield|add_days:10 < today',
            'form_var_datefield|add_days:36500 > today',
            'form_var_datefield|add_days:36500 >= today',
            'form_var_datefield <= today|add_days:0',
            'form_var_datefield < today|add_days:-10',
            'form_var_datefield > today|add_days:-36500',
            'form_var_datefield >= today|add_days:-36500',
            ):
        condition = Condition({'type': 'django', 'value': condition_value})
        assert condition.evaluate() is True
        condition = Condition({'type': 'django', 'value': condition_value.replace('today', 'now')})
        assert condition.evaluate() is True

    for condition_value in (
            '"1970-01-01"|add_hours:0 < today',
            '"2100-12-31"|add_hours:0 > today',
            'form_var_datefield|add_hours:0 <= today',
            'form_var_datefield|add_hours:10 < today',
            'form_var_datefield|add_hours:876000 > today',  # + 100 years
            'form_var_datefield|add_hours:876000 >= today',
            ):
        condition = Condition({'type': 'django', 'value': condition_value})
        assert condition.evaluate() is True
        condition = Condition({'type': 'django', 'value': condition_value.replace('today', 'now')})
        assert condition.evaluate() is True

    for condition_value in (
            'today|add_days:0 == today',
            'today|add_hours:0 == today',
            'now|add_hours:0 == now',
            ):
        condition = Condition({'type': 'django', 'value': condition_value})
        assert condition.evaluate() is True

    for condition_value in (
            'today|add_days:2 >= today',
            'today|add_days:2 > today',
            'today|add_days:2 != today',
            'not today|add_days:2 == today',
            'not today|add_days:2 <= today',
            'not today|add_days:2 < today',
            'today|add_days:-2 <= today',
            'today|add_days:-2 < today',
            'today|add_days:-2 != today',
            'not today|add_days:-2 >= today',
            'not today|add_days:-2 > today',
            'not today|add_days:-2 == today',
            ):
        condition = Condition({'type': 'django', 'value': condition_value})
        assert condition.evaluate() is True
        condition = Condition({'type': 'django', 'value': condition_value.replace('today', 'now')})
        assert condition.evaluate() is True

def test_lazy_templates(pub, variable_test_data):
    context = pub.substitutions.get_context_variables(mode='lazy')
    tmpl = Template('{{form_var_foo_foo}}')
    assert tmpl.render(context) == 'bar'

    tmpl = Template('[form_var_foo_foo]')
    assert tmpl.render(context) == 'bar'

    tmpl = Template('[form_name]')
    assert tmpl.render(context) == 'foobarlazy'

    tmpl = Template('[form_user_email]')
    assert tmpl.render(context) == 'bar@localhost'

    tmpl = Template('{{form_user_name_identifier_0}}')
    assert tmpl.render(context) == pub.user_class.select()[0].name_identifiers[0]

    tmpl = Template('{% if form_user_email == "bar@localhost" %}HELLO{% endif %}')
    assert tmpl.render(context) == 'HELLO'

def test_lazy_ezt_templates(pub, variable_test_data):
    context = pub.substitutions.get_context_variables(mode='lazy')
    tmpl = Template('[form_var_foo_foo]')
    assert tmpl.render(context) == 'bar'

    tmpl = Template('[is form_var_foo_foo "bar"]HELLO[else]BYE[end]')
    assert tmpl.render(context) == 'HELLO'

def test_lazy_formdata_fields(pub):
    formdef = FormDef()
    formdef.name = 'foobar'
    formdef.url_name = 'foobar'
    formdef.fields = [
        fields.StringField(id='0', label='string', varname='string'),
        fields.ItemField(id='1', label='item', varname='item', items=['Foo', 'Bar'])
    ]
    formdef.store()

    formdata = formdef.data_class()()
    formdata.data = {'0': 'Foo', '1': 'Foo', '1_display': 'Foo'}
    formdata.store()

    pub.substitutions.feed(formdata)
    context = pub.substitutions.get_context_variables(mode='lazy')

    tmpl = Template('{% if form_var_string == "Foo" %}HELLO{% endif %}')
    assert tmpl.render(context) == 'HELLO'
    tmpl = Template('{% if form_var_item == "Foo" %}HELLO{% endif %}')
    assert tmpl.render(context) == 'HELLO'

    tmpl = Template('{% if form_var_string != "Foo" %}HELLO{% endif %}')
    assert tmpl.render(context) == ''
    tmpl = Template('{% if form_var_item != "Foo" %}HELLO{% endif %}')
    assert tmpl.render(context) == ''

def test_date_conditions_python(pub, variable_test_data):
    for pycondition in (
            'utils.age_in_days(form_var_datefield, form_var_datefield2) == 31',
            'utils.age_in_days(form_var_datefield, form_var_datefield) == 0',
            'utils.age_in_days(form_var_datefield, form_var_datestring) == 0',
            'utils.age_in_days(utils.add_days(today, -5)) == 5',
            'date(form_var_datefield) < today',
            'utils.add_days(form_var_datefield, 36500) > today',
            'date(form_var_datefield) + days(36500) > today',
            'utils.add_days(form_var_datefield, 31) == date(form_var_datefield2)',
            'date(form_var_datefield) + days(31) == date(form_var_datefield2)',
            'utils.add_days(form_var_datefield, 32) > date(form_var_datefield2)',
            'date(form_var_datefield) + days(32) > date(form_var_datefield2)',
            'utils.add_days(form_var_datefield, 30) < date(form_var_datefield2)',
            'date(form_var_datefield) + days(30) < date(form_var_datefield2)',
            'isinstance(now, datetime.datetime)',
            'isinstance(today, datetime.date)',
            'utils.age_in_days(today) == 0',
            'utils.age_in_days(now) == 0',
            'utils.age_in_years(today) == 0',
            'utils.age_in_years(now) == 0',
            'utils.age_in_years_and_months(today) == (0, 0)',
            'utils.age_in_years_and_months(now) == (0, 0)',
            'utils.age_in_years_and_months(form_var_datefield, form_var_datefield2) == (0, 1)',
            ):
        condition = Condition({'type': 'python', 'value': pycondition})
        assert condition.evaluate() is True

def test_date_conditions_django(pub, variable_test_data):
    for condition_value in (  # hope date is > 2018
            # age_in_days
            '"1970-01-01"|age_in_days > 0',
            '"01/01/1970"|age_in_days > 0',
            '"2500-01-01"|age_in_days < 0',
            '"01/01/2500"|age_in_days < 0',
            'form_var_datefield|age_in_days > 50',
            'form_var_datefield|age_in_days:form_var_datestring == 0',
            'form_var_datefield|age_in_days:form_var_datefield2 == 31',
            'form_var_datefield2|age_in_days:form_var_datefield == -31',
            'form_var_datefield|age_in_days:form_var_datefield == 0',
            'form_var_datestring|age_in_days:form_var_datefield == 0',
            'form_var_datestring|age_in_days:form_var_datestring == 0',
            'today|add_days:-5|age_in_days == 5',
            'today|add_days:5|age_in_days == -5',
            'today|age_in_days == 0',
            # with datetimes
            '"1970-01-01 02:03"|age_in_days > 0',
            '"01/01/1970 02h03"|age_in_days > 0',
            '"2500-01-01 02:03"|age_in_days < 0',
            '"01/01/2500 02h03"|age_in_days < 0',
            'now|age_in_days == 0',
            'now|add_hours:-24|age_in_days == 1',
            'now|add_hours:24|age_in_days == -1',
            '"2010-11-12 13:14"|age_in_days:"2010-11-12 13:14" == 0',
            '"2010-11-12 13:14"|age_in_days:"2010-11-12 12:14" == 0',
            '"2010-11-12 13:14"|age_in_days:"2010-11-12 14:14" == 0',
            '"2010-11-12 13:14"|age_in_days:"2010-11-13 13:13" == 1',
            '"2010-11-12 13:14"|age_in_days:"2010-11-13 13:15" == 1',

            # age_in_hours
            'now|add_hours:-5|age_in_hours == 5',
            'now|add_hours:25|age_in_hours == -24',
            'now|age_in_hours == 0',
            '"2010-11-12 13:14"|age_in_hours:"2010-11-12 13:14" == 0',
            '"2010-11-12 13:14"|age_in_hours:"2010-11-12 12:14" == -1',
            '"2010-11-12 13:14"|age_in_hours:"2010-11-12 14:14" == 1',
            '"2010-11-12 13:14"|age_in_hours:"2010-11-13 13:13" == 23',
            '"2010-11-12 13:14"|age_in_hours:"2010-11-13 13:15" == 24',
            '"1970-01-01 02:03"|age_in_hours > 0',
            '"01/01/1970 02h03"|age_in_hours > 0',
            '"2500-01-01 02:03"|age_in_hours < 0',
            '"01/01/2500 02h03"|age_in_hours < 0',
            # with dates
            '"1970-01-01"|age_in_hours > 0',
            '"01/01/1970"|age_in_hours > 0',
            '"2500-01-01"|age_in_hours < 0',
            '"01/01/2500"|age_in_hours < 0',
            'form_var_datefield|age_in_hours > 1200',
            'form_var_datefield|age_in_hours:form_var_datestring == 0',
            'form_var_datefield|age_in_hours:form_var_datefield2 == 744',  # 31*24
            'form_var_datefield2|age_in_hours:form_var_datefield == -744',
            'form_var_datefield|age_in_hours:form_var_datefield == 0',
            'form_var_datestring|age_in_hours:form_var_datefield == 0',
            'form_var_datestring|age_in_hours:form_var_datestring == 0',
            'today|add_days:-1|age_in_hours >= 24',
            'today|add_days:1|age_in_hours <= -0',
            'today|add_days:1|age_in_hours >= -24',
            'today|age_in_hours >= 0',

            # age_in_years
            '"1970-01-01"|age_in_years > 0',
            '"01/01/1970"|age_in_years > 0',
            '"2500-01-01"|age_in_years < 0',
            '"01/01/2500"|age_in_years < 0',
            'form_var_datefield|age_in_years:"2019-07-31" == 1',
            'form_var_datefield|age_in_years:"2019-09-20" == 1',
            'form_var_datefield|age_in_years:"2020-07-30" == 1',
            'form_var_datefield|age_in_years:"2020-07-31" == 2',
            'form_var_datestring|age_in_years:"2019-07-31" == 1',
            'today|age_in_years == 0',
            'today|add_days:-500|age_in_years == 1',
            'today|add_days:-300|age_in_years == 0',
            'today|add_days:300|age_in_years == -1',
            'now|age_in_years == 0',
            'now|add_days:-500|age_in_years == 1',
            'now|add_days:-300|age_in_years == 0',
            'now|add_days:300|age_in_years == -1',
            '"1970-01-01 02:03"|age_in_years > 0',
            '"2500-01-01 02:03"|age_in_years < 0',

            # age_in_months
            'form_var_datefield|age_in_months:form_var_datefield2 == 1',
            'form_var_datefield2|age_in_months:form_var_datefield == -1',
            'form_var_datefield|age_in_months:"2019-07-31" == 12',
            'form_var_datefield|age_in_months:"2019-08-20" == 12',
            'form_var_datefield|age_in_months:"2019-09-20" == 13',
            'form_var_datestring|age_in_months:"2019-09-20" == 13',
            '"1970-01-01"|age_in_months > 0',
            '"01/01/1970"|age_in_months > 0',
            '"2500-01-01"|age_in_months < 0',
            '"01/01/2500"|age_in_months < 0',
            '"1970-01-01 02:03"|age_in_months > 0',
            '"2500-01-01 02:03"|age_in_months < 0',

            # fail produce empty string
            'foobar|age_in_days == ""',
            '"foobar"|age_in_days == ""',
            '"1970-01-01"|age_in_days:"foobar" == ""',
            'foobar|age_in_hours == ""',
            '"foobar"|age_in_hours == ""',
            '"1970-01-01"|age_in_hours:"foobar" == ""',
            'foobar|age_in_years == ""',
            '"foobar"|age_in_years == ""',
            '"1970-01-01"|age_in_years:"foobar" == ""',
            'foobar|age_in_months == ""',
            '"foobar"|age_in_months == ""',
            '"1970-01-01"|age_in_months:"foobar" == ""',
            ):
        condition = Condition({'type': 'django', 'value': condition_value})
        assert condition.evaluate() is True

def test_form_digest_date(pub):
    formdef = FormDef()
    formdef.name = 'foobar'
    formdef.url_name = 'foobar'
    formdef.fields = [fields.DateField(id='0', label='date', varname='date')]
    formdef.digest_template = 'plop {{ form_var_date }} plop'
    formdef.store()

    formdata = formdef.data_class()()
    formdata.data = {'0': time.strptime('2015-05-12', '%Y-%m-%d')}
    formdata.store()
    assert formdef.data_class().get(formdata.id).digest == 'plop 2015-05-12 plop'

    pub.cfg['language'] = {'language': 'fr'}
    pub.write_cfg()
    formdata = formdef.data_class()()
    formdata.data = {'0': time.strptime('2015-05-12', '%Y-%m-%d')}
    formdata.store()
    assert formdef.data_class().get(formdata.id).digest == 'plop 12/05/2015 plop'

    formdef.digest_template = 'plop {{ form_var_date|date:"Y" }} plop'
    formdef.store()
    formdata = formdef.data_class()()
    formdata.data = {'0': time.strptime('2015-05-12', '%Y-%m-%d')}
    formdata.store()
    assert formdef.data_class().get(formdata.id).digest == 'plop 2015 plop'

    formdef.digest_template = 'plop {{ form_var_date_raw|date:"Y" }} plop'
    formdef.store()
    formdata = formdef.data_class()()
    formdata.data = {'0': time.strptime('2015-05-12', '%Y-%m-%d')}
    formdata.store()
    assert formdef.data_class().get(formdata.id).digest == 'plop 2015 plop'

    formdef.digest_template = 'plop {{ form_var_date|date:"Y" }} plop'
    formdef.store()
    formdata = formdef.data_class()()
    formdata.data = {'0': None}
    formdata.store()
    assert formdef.data_class().get(formdata.id).digest == 'plop  plop'

    # check there's no crash when an invaliad variable is given
    formdef.digest_template = 'plop {{ blah|date:"Y" }} plop'
    formdef.store()
    formdata = formdef.data_class()()
    formdata.data = {'0': time.strptime('2015-05-12', '%Y-%m-%d')}
    formdata.store()
    assert formdef.data_class().get(formdata.id).digest == 'plop  plop'

def test_lazy_formdata_decimal_filter(pub):
    formdef = FormDef()
    formdef.name = 'foobar'
    formdef.url_name = 'foobar'
    formdef.fields = [
        fields.StringField(id='0', label='value', varname='value'),
        fields.StringField(id='1', label='arg', varname='arg')
    ]
    formdef.store()
    formdata = formdef.data_class()()
    formdata.data = {'0': '3.14', '1': '3'}
    formdata.store()
    pub.substitutions.feed(formdata)
    for mode in (None, 'lazy'):
        context = pub.substitutions.get_context_variables(mode=mode)

        tmpl = Template('{{ form_var_value|decimal }}')
        assert tmpl.render(context) == '3.14'

        tmpl = Template('{{ form_var_value|decimal:1 }}')
        assert tmpl.render(context) == '3.1'

        tmpl = Template('{{ form_var_value|decimal:form_var_arg }}')
        assert tmpl.render(context) == '3.140'

        tmpl = Template('{{ 4.12|decimal:form_var_arg }}')
        assert tmpl.render(context) == '4.120'

def test_decimal_conditions_django(pub, variable_test_data):
    for condition_value in (
            'form_var_foo_foo|decimal == 0',
            'form_var_boolfield|decimal == 0',
            'form_var_boolfield2|decimal == 0',
            'form_var_datefield|decimal == 0',
            'form_var_datefield|decimal == 0',
            'form_var_filefield|decimal == 0',
            'form_var_foo_foo_baz_baz|decimal == 0',
            'form_var_map|decimal == 0',
            'form_var_datefield2|decimal == 0',
            'form_var_datestring|decimal == 0',
            'form_var_term1|decimal == 3',
            'form_var_term2|decimal == 4',
            ):
        condition = Condition({'type': 'django', 'value': condition_value})
        assert condition.evaluate() is True

def test_lazy_formdata_mathematics_filters(pub):
    formdef = FormDef()
    formdef.name = 'foobar'
    formdef.url_name = 'foobar'
    formdef.fields = [
        fields.StringField(id='0', label='term1', varname='term1'),
        fields.StringField(id='1', label='term2', varname='term2')
    ]
    formdef.store()
    formdata = formdef.data_class()()
    formdata.data = {'0': '3', '1': '4'}
    formdata.store()
    pub.substitutions.feed(formdata)
    for mode in (None, 'lazy'):
        context = pub.substitutions.get_context_variables(mode=mode)

        tmpl = Template('{{ form_var_term1|decimal }}')
        assert tmpl.render(context) == '3'

        tmpl = Template('{{ form_var_term1|add:form_var_term2 }}')
        assert tmpl.render(context) == '7'

        tmpl = Template('{{ form_var_term1|subtract:form_var_term2 }}')
        assert tmpl.render(context) == '-1'

        tmpl = Template('{{ form_var_term1|multiply:form_var_term2 }}')
        assert tmpl.render(context) == '12'

        tmpl = Template('{{ form_var_term1|divide:form_var_term2 }}')
        assert tmpl.render(context) == '0.75'

def test_mathematic_conditions_django(pub, variable_test_data):
    for true_condition_value in (
            # reminder
            'form_var_term1 == 3',
            'form_var_term2 == 4',

            # add
            'form_var_term1|add:form_var_term2 == 7',
            'form_var_term1|add:form_var_term2 == 7.0',
            'form_var_term1|add:form_var_term2 == "7"|decimal',
            'form_var_term1|add:form_var_term2 > 6',

            # subtract
            'form_var_term1|subtract:form_var_term2 == -1',
            'form_var_term1|subtract:form_var_term2 == -1.0',
            'form_var_term1|subtract:form_var_term2 == "-1"|decimal',
            'form_var_term1|subtract:form_var_term2 < 0',

            # multiply
            'form_var_term1|multiply:form_var_term2 == 12',
            'form_var_term1|multiply:form_var_term2 == 12.0',
            'form_var_term1|multiply:form_var_term2 == "12"|decimal',
            'form_var_term1|multiply:form_var_term2 > 10',

            # divide
            'form_var_term1|divide:form_var_term2 == 0.75',
            'form_var_term1|divide:form_var_term2 == 0.750',
            'form_var_term1|divide:form_var_term2 == "0.75"|decimal',
            'form_var_term1|divide:form_var_term2 > 0.5',
            ):
        condition = Condition({'type': 'django', 'value': true_condition_value})
        assert condition.evaluate() is True

    for false_condition_value in (
            'form_var_term1|add:form_var_term2 > 8',
            'form_var_term1|subtract:form_var_term2 > 0',
            'form_var_term1|multiply:form_var_term2 > 20',
            'form_var_term1|divide:form_var_term2 > 1',
            ):
        condition = Condition({'type': 'django', 'value': false_condition_value})
        assert condition.evaluate() is False

def test_lazy_formdata_ceil_filter(pub):
    formdef = FormDef()
    formdef.name = 'foobar'
    formdef.url_name = 'foobar'
    formdef.fields = [
        fields.StringField(id='0', label='value', varname='value'),
    ]
    formdef.store()
    formdata = formdef.data_class()()
    formdata.data = {'0': '3.14'}
    formdata.store()
    pub.substitutions.feed(formdata)
    for mode in (None, 'lazy'):
        context = pub.substitutions.get_context_variables(mode=mode)
        tmpl = Template('{{ form_var_value|ceil }}')
        assert tmpl.render(context) == '4'
        tmpl = Template('{{ form_var_value|floor }}')
        assert tmpl.render(context) == '3'
        tmpl = Template('{{ form_var_value|abs }}')
        assert tmpl.render(context) == '3.14'

def test_rounding_and_abs_conditions_django(pub, variable_test_data):
    for true_condition_value in (
            # reminder
            'form_var_value == 3.14',

            # ceil
            'form_var_value|ceil == 4',
            'form_var_value|ceil == 4.0',
            'form_var_value|ceil > 3',

            # floor
            'form_var_value|floor == 3',
            'form_var_value|floor == 3.0',
            'form_var_value|floor >= 3',

            # abs
            'form_var_value|abs == 3.14|decimal',
            'form_var_value|abs == 3.140|decimal',
            'form_var_value|abs >= 3',
            ):
        condition = Condition({'type': 'django', 'value': true_condition_value})
        assert condition.evaluate() is True

    for false_condition_value in (
            'form_var_value|ceil < 4',
            'form_var_value|ceil >= 5',
            'form_var_value|floor < 3',
            ):
        condition = Condition({'type': 'django', 'value': false_condition_value})
        assert condition.evaluate() is False

def test_formdata_user_field(pub, variable_test_data):
    local_user = variable_test_data._formdata.user

    from wcs.admin.settings import UserFieldsFormDef
    user_formdef = UserFieldsFormDef(pub)
    user_formdef.fields.append(fields.StringField(id='3', label='test', varname='test', type='string'))
    user_formdef.store()
    local_user.form_data = {'3': 'nono'}
    local_user.set_attributes_from_formdata(local_user.form_data)
    local_user.store()

    for mode in (None, 'lazy'):
        context = pub.substitutions.get_context_variables(mode=mode)
        tmpl = Template('{{ form_user_var_test }}')
        assert tmpl.render(context) == 'nono'

    condition = Condition({'type': 'django', 'value': 'form_user_var_test'})
    assert condition.evaluate() is True

    local_user.form_data = {'3': ''}
    local_user.set_attributes_from_formdata(local_user.form_data)
    local_user.store()
    condition = Condition({'type': 'django', 'value': 'form_user_var_test'})
    assert condition.evaluate() is False

def test_string_filters(pub, variable_test_data):
    tmpl = Template('{% with form_var_foo_foo|split:"a" as x %}{{x.0}}{% endwith %}', raises=True)
    for mode in (None, 'lazy'):
        context = pub.substitutions.get_context_variables(mode=mode)
        assert tmpl.render(context) == 'b'

    tmpl = Template('{% if form_var_foo_foo|startswith:"b" %}test{% endif %}', raises=True)
    for mode in (None, 'lazy'):
        context = pub.substitutions.get_context_variables(mode=mode)
        assert tmpl.render(context) == 'test'

    tmpl = Template('{% if form_var_foo_foo|startswith:form_var_term1 %}test{% endif %}', raises=True)
    for mode in (None, 'lazy'):
        context = pub.substitutions.get_context_variables(mode=mode)
        assert tmpl.render(context) == ''

def test_user_label(pub):
    from wcs.admin.settings import UserFieldsFormDef
    user_formdef = UserFieldsFormDef(pub)
    user_formdef.fields.append(fields.StringField(id='3', label='first_name', type='string'))
    user_formdef.fields.append(fields.StringField(id='4', label='last_name', type='string'))
    user_formdef.store()
    pub.cfg['users']['field_name'] = ['3', '4']
    pub.write_cfg()

    formdef = FormDef()
    formdef.name = 'foobar'
    formdef.url_name = 'foobar'
    formdef.fields = [
        fields.StringField(id='1', label='first_name',
        prefill={'type': 'user', 'value': '3'}),
        fields.StringField(id='2', label='last_name',
        prefill={'type': 'user', 'value': '4'}),
    ]
    formdef.store()

    user = pub.user_class()
    user.email = 'bar@localhost'
    user.store()

    formdata = formdef.data_class()()
    formdata.data = {}
    formdata.user_id = user.id
    formdata.store()

    assert str(formdef.data_class().get(formdata.id).user_id) == str(user.id)
    assert formdef.data_class().get(formdata.id).user_label is None
    assert formdef.data_class().get(formdata.id).get_user_label() == 'bar@localhost'

    formdata = formdef.data_class()()
    formdata.data = {}
    formdata.store()

    assert formdef.data_class().get(formdata.id).user_id is None
    assert formdef.data_class().get(formdata.id).user_label == ''
    assert formdef.data_class().get(formdata.id).get_user_label() == ''

    formdata = formdef.data_class()()
    formdata.data = {'1': 'blah'}
    formdata.store()

    assert formdef.data_class().get(formdata.id).user_id is None
    assert formdef.data_class().get(formdata.id).user_label == 'blah'
    assert formdef.data_class().get(formdata.id).get_user_label() == 'blah'

    formdata = formdef.data_class()()
    formdata.data = {'1': 'blah', '2': 'xxx'}
    formdata.store()

    assert formdef.data_class().get(formdata.id).user_id is None
    assert formdef.data_class().get(formdata.id).user_label == 'blah xxx'
    assert formdef.data_class().get(formdata.id).get_user_label() == 'blah xxx'
