import datetime import os import string import django import pytest from django.test import override_settings from django.utils import translation from django.utils.timezone import now try: import langdetect # pylint: disable=unused-import except ImportError: langdetect = None import PIL from pyzbar.pyzbar import ZBarSymbol from pyzbar.pyzbar import decode as zbar_decode_qrcode from wcs import fields from wcs.formdef import FormDef from wcs.qommon.http_request import HTTPRequest from wcs.qommon.substitution import CompatibilityNamesDict from wcs.qommon.template import Template, TemplateError from wcs.variables import LazyFormData from .utilities import clean_temporary_pub, create_temporary_pub @pytest.fixture def pub(): pub = create_temporary_pub() pub.substitutions.feed(pub) req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'}) pub.set_app_dir(req) pub.site_options.set('options', 'working_day_calendar', '') with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd: pub.site_options.write(fd) return pub def teardown_module(module): clean_temporary_pub() def test_template(): tmpl = Template('') assert tmpl.render() == '' assert tmpl.render({'foo': 'bar'}) == '' tmpl = Template('zoo') assert tmpl.render() == 'zoo' assert tmpl.render({'foo': 'bar'}) == 'zoo' # django tmpl = Template('{{ foo }}') assert tmpl.render() == '' assert tmpl.render({'foo': 'bar'}) == 'bar' tmpl = Template('{% if foo %}{{ foo }}{% endif %}') assert tmpl.render() == '' assert tmpl.render({'foo': 'bar'}) == 'bar' # ezt tmpl = Template('[foo]') assert tmpl.render() == '[foo]' assert tmpl.render({'foo': 'bar'}) == 'bar' tmpl = Template('[if-any foo][foo][end]') assert tmpl.render() == '' assert tmpl.render({'foo': 'bar'}) == 'bar' # mix Django/ezt: Django wins tmpl = Template('{% if foo %}{{ foo }}[foo]{% endif %}') assert tmpl.render({'foo': 'bar'}) == 'bar[foo]' # django syntax error with pytest.raises(TemplateError): tmpl = Template('{% if foo %}{{ foo }}{% end %}', raises=True) tmpl = Template('{% if foo %}{{ foo }}{% end %}') assert tmpl.render({'foo': 'bar'}) == '{% if foo %}{{ foo }}{% end %}' # ezt syntax error with pytest.raises(TemplateError): tmpl = Template('[if-any foo][foo][endif]', raises=True) tmpl = Template('[if-any foo][foo][endif]') assert tmpl.render({'foo': 'bar'}) == '[if-any foo][foo][endif]' def test_now_and_today_variables(pub): # create a today string, verify it contains the year, at least today = Template('{{d}}').render({'d': datetime.date.today()}) assert datetime.date.today().strftime('%Y') in today tmpl = Template('{{ today }}') for mode in (None, 'lazy'): context = pub.substitutions.get_context_variables(mode=mode) assert tmpl.render(context) == today tmpl = Template('{{ now }}') for mode in (None, 'lazy'): context = pub.substitutions.get_context_variables(mode=mode) assert today in tmpl.render(context) # contains the date, assert tmpl.render(context) != today # but not only # ezt templates (legacy) today = Template('[t]').render({'t': datetime.date.today()}) assert today == datetime.date.today().strftime('%Y-%m-%d') now = Template('[n]').render({'n': datetime.datetime.now()}) assert now == datetime.datetime.now().strftime('%Y-%m-%d %H:%M') tmpl = Template('[today]') for mode in (None, 'lazy'): context = pub.substitutions.get_context_variables(mode=mode) assert tmpl.render(context) == today tmpl = Template('[now]') for mode in (None, 'lazy'): context = pub.substitutions.get_context_variables(mode=mode) assert tmpl.render(context) == now def test_template_templatetag(): # check qommon templatetags are always loaded tmpl = Template('{{ date|parse_datetime|date:"Y" }}') assert tmpl.render({'date': '2018-06-06'}) == '2018' # check other templatetags can be loaded tmpl = Template('{% load i18n %}{% trans "hello" %}') assert tmpl.render() == 'hello' def test_startswith_templatetag(): tmpl = Template('{% if foo|startswith:"bar" %}hello{% endif %}') assert tmpl.render() == '' assert tmpl.render({'foo': 'bar-baz'}) == 'hello' assert tmpl.render({'foo': 'baz-bar'}) == '' def test_endswith_templatetag(): tmpl = Template('{% if foo|endswith:"bar" %}hello{% endif %}') assert tmpl.render() == '' assert tmpl.render({'foo': 'baz-bar'}) == 'hello' assert tmpl.render({'foo': 'bar-baz'}) == '' def test_split_templatetag(): tmpl = Template('{{ foo|split|last }}') assert tmpl.render() == '' assert tmpl.render({'foo': 'bar baz'}) == 'baz' assert tmpl.render({'foo': 'baz-bar'}) == 'baz-bar' tmpl = Template('{{ foo|split:"-"|last }}') assert tmpl.render() == '' assert tmpl.render({'foo': 'bar-baz'}) == 'baz' assert tmpl.render({'foo': 'baz-bar'}) == 'bar' def test_strip_templatetag(): tmpl = Template('{{ foo|strip }}') assert tmpl.render() == '' assert tmpl.render({'foo': None}) == '' assert tmpl.render({'foo': ' foo bar '}) == 'foo bar' assert tmpl.render({'foo': ' foo bar\t'}) == 'foo bar' assert tmpl.render({'foo': ' félé '}) == 'félé' tmpl = Template('{{ foo|strip:"XY" }}') assert tmpl.render({'foo': 'XXfoo barXYX'}) == 'foo bar' assert tmpl.render({'foo': ' foo barXX'}) == ' foo bar' def test_removeprefix_templatetag(): tmpl = Template('{{ foo|removeprefix }}') assert tmpl.render() == '' assert tmpl.render({'foo': None}) == '' assert tmpl.render({'foo': 'foo bar'}) == 'foo bar' tmpl = Template('{{ foo|removeprefix:"" }}') assert tmpl.render({'foo': 'foo bar'}) == 'foo bar' tmpl = Template('{{ foo|removeprefix:"XY" }}') assert tmpl.render({'foo': 'XYfoo barXY'}) == 'foo barXY' assert tmpl.render({'foo': 'foo bar'}) == 'foo bar' assert tmpl.render({'foo': 'xyfoo barXY'}) == 'xyfoo barXY' assert tmpl.render({'foo': ' XYfoo barXY'}) == ' XYfoo barXY' assert tmpl.render({'foo': 'XYXYfoo barXY'}) == 'XYfoo barXY' def test_removesuffix_templatetag(): tmpl = Template('{{ foo|removesuffix }}') assert tmpl.render() == '' assert tmpl.render({'foo': None}) == '' assert tmpl.render({'foo': 'foo bar'}) == 'foo bar' tmpl = Template('{{ foo|removesuffix:"" }}') assert tmpl.render({'foo': 'foo bar'}) == 'foo bar' tmpl = Template('{{ foo|removesuffix:"XY" }}') assert tmpl.render({'foo': 'XYfoo barXY'}) == 'XYfoo bar' assert tmpl.render({'foo': 'foo bar'}) == 'foo bar' assert tmpl.render({'foo': 'XYfoo barxy'}) == 'XYfoo barxy' assert tmpl.render({'foo': 'XYfoo barXY '}) == 'XYfoo barXY ' assert tmpl.render({'foo': 'XYfoo barXYXY'}) == 'XYfoo barXY' def test_urljoin_templatefilter(): tmpl = Template('{{ foo|urljoin }}') assert tmpl.render() == '' assert tmpl.render({'foo': None}) == '' assert tmpl.render({'foo': ''}) == '' assert tmpl.render({'foo': 'http://example.com'}) == 'http://example.com' assert tmpl.render({'foo': 'http://example.com/'}) == 'http://example.com/' tmpl = Template('{{ foo|urljoin:bar }}') assert tmpl.render({'foo': None, 'bar': None}) == '' assert tmpl.render({'foo': None, 'bar': 'some/url'}) == 'some/url' assert tmpl.render({'foo': 'http://example.com', 'bar': None}) == 'http://example.com' assert tmpl.render({'foo': 'http://example.com', 'bar': 'some/url'}) == 'http://example.com/some/url' assert tmpl.render({'foo': 'http://example.com/', 'bar': 'some/url'}) == 'http://example.com/some/url' assert tmpl.render({'foo': 'http://example.com', 'bar': '/some/url'}) == 'http://example.com/some/url' assert tmpl.render({'foo': 'http://example.com/', 'bar': '/some/url'}) == 'http://example.com/some/url' assert tmpl.render({'foo': 'http://example.com/', 'bar': '/some/url'}) == 'http://example.com/some/url' def test_unaccent_templatetag(): tmpl = Template('{{ foo|unaccent }}') assert tmpl.render() == '' assert tmpl.render({'foo': None}) == '' assert tmpl.render({'foo': 'FOO bar'}) == 'FOO bar' assert tmpl.render({'foo': 'félé'}) == 'fele' # lazy mode FormDef.wipe() formdef = FormDef() formdef.name = 'lazy' formdef.fields = [fields.StringField(id='0', label='string', varname='foo')] formdef.store() formdata = formdef.data_class()() formdata.data = {'0': 'félà'} formdata.store() context = CompatibilityNamesDict({'form': LazyFormData(formdata)}) tmpl = Template('{{ form_var_foo|unaccent }}') assert tmpl.render(context) == 'fela' formdata.data = {'0': None} formdata.store() assert tmpl.render(context) == '' def test_template_encoding(): # django tmpl = Template('{{ foo }} à vélo') assert tmpl.render() == ' à vélo' assert tmpl.render({'foo': 'fou'}) == 'fou à vélo' assert tmpl.render({'foo': 'félé'}) == 'félé à vélo' tmpl = Template("{% if foo == 'félé' %}à vélo{% endif %}") assert tmpl.render() == '' assert tmpl.render({'foo': 'fou'}) == '' assert tmpl.render({'foo': 'félé'}) == 'à vélo' tmpl = Template("{% if foo.bar == 'félé' %}à vélo{% endif %}") assert tmpl.render() == '' assert tmpl.render({'foo': {'bar': 'fou'}}) == '' assert tmpl.render({'foo': {'bar': 'félé'}}) == 'à vélo' # ezt tmpl = Template('[foo] à vélo') assert tmpl.render() == '[foo] à vélo' assert tmpl.render({'foo': 'fou'}) == 'fou à vélo' assert tmpl.render({'foo': 'félé'}) == 'félé à vélo' def test_datetime_templatetags(): tmpl = Template('{{ plop|datetime }}') assert tmpl.render({'plop': '2017-12-21 10:32'}) == '2017-12-21 10:32' assert tmpl.render({'plop': '21/12/2017 10h32'}) == '2017-12-21 10:32' assert tmpl.render({'plop': '2017-12-21'}) == '2017-12-21 00:00' assert tmpl.render({'plop': '21/12/2017'}) == '2017-12-21 00:00' with override_settings(LANGUAGE_CODE='fr-fr'): assert tmpl.render({'plop': '2017-12-21 10:32'}) == '21/12/2017 10:32' assert tmpl.render({'plop': '21/12/2017 10h32'}) == '21/12/2017 10:32' assert tmpl.render({'plop': '2017-12-21'}) == '21/12/2017 00:00' assert tmpl.render({'plop': '21/12/2017'}) == '21/12/2017 00:00' assert tmpl.render({'plop': '10h32'}) == '' assert tmpl.render({'plop': 'x'}) == '' assert tmpl.render({'plop': None}) == '' assert tmpl.render({'plop': 3}) == '' assert tmpl.render({'plop': {'foo': 'bar'}}) == '' assert tmpl.render() == '' tmpl = Template('{{ plop|datetime:"d i" }}') assert tmpl.render({'plop': '2017-12-21 10:32'}) == '21 32' assert tmpl.render({'plop': '2017-12-21 10:32:42'}) == '21 32' assert tmpl.render({'plop': '21/12/2017 10:32'}) == '21 32' assert tmpl.render({'plop': '21/12/2017 10:32:42'}) == '21 32' assert tmpl.render({'plop': '21/12/2017 10h32'}) == '21 32' assert tmpl.render({'plop': '21/12/2017'}) == '21 00' assert tmpl.render({'plop': '10h32'}) == '' assert tmpl.render({'plop': 'x'}) == '' assert tmpl.render({'plop': None}) == '' assert tmpl.render({'plop': 3}) == '' assert tmpl.render({'plop': {'foo': 'bar'}}) == '' assert tmpl.render() == '' tmpl = Template('{% if d1|datetime > d2|datetime %}d1>d2{% else %}d1<=d2{% endif %}') assert tmpl.render({'d1': '2017-12-22', 'd2': '2017-12-21'}) == 'd1>d2' assert tmpl.render({'d1': '2017-12-21', 'd2': '2017-12-21'}) == 'd1<=d2' assert tmpl.render({'d1': '2017-12-21 10:30', 'd2': '2017-12-21 09:00'}) == 'd1>d2' assert tmpl.render({'d1': '2017-12-21 10:30', 'd2': '2017-12-21'}) == 'd1>d2' assert tmpl.render({'d1': '2017-12-22'}) == 'd1<=d2' assert tmpl.render({'d2': '2017-12-22'}) == 'd1<=d2' tmpl = Template('{{ plop|date }}') assert tmpl.render({'plop': '2017-12-21'}) == '2017-12-21' assert tmpl.render({'plop': '21/12/2017'}) == '2017-12-21' assert tmpl.render({'plop': '2017-12-21 10:32'}) == '2017-12-21' assert tmpl.render({'plop': '21/12/2017 10:32'}) == '2017-12-21' assert tmpl.render({'plop': '21/12/2017 10h32'}) == '2017-12-21' assert tmpl.render({'plop': '21/12/2017 10:32:42'}) == '2017-12-21' with override_settings(LANGUAGE_CODE='fr-fr'): assert tmpl.render({'plop': '2017-12-21'}) == '21/12/2017' assert tmpl.render({'plop': '21/12/2017'}) == '21/12/2017' assert tmpl.render({'plop': '2017-12-21 10:32'}) == '21/12/2017' assert tmpl.render({'plop': '21/12/2017 10:32'}) == '21/12/2017' assert tmpl.render({'plop': '21/12/2017 10h32'}) == '21/12/2017' assert tmpl.render({'plop': '21/12/2017 10:32:42'}) == '21/12/2017' assert tmpl.render({'plop': '10:32'}) == '' assert tmpl.render({'plop': 'x'}) == '' assert tmpl.render({'plop': None}) == '' assert tmpl.render({'plop': 3}) == '' assert tmpl.render({'plop': {'foo': 'bar'}}) == '' assert tmpl.render() == '' tmpl = Template('{{ plop|date:"d" }}') assert tmpl.render({'plop': '2017-12-21'}) == '21' assert tmpl.render({'plop': '21/12/2017'}) == '21' assert tmpl.render({'plop': '2017-12-21 10:32'}) == '21' assert tmpl.render({'plop': '10:32'}) == '' assert tmpl.render({'plop': 'x'}) == '' assert tmpl.render({'plop': None}) == '' assert tmpl.render({'plop': 3}) == '' assert tmpl.render({'plop': {'foo': 'bar'}}) == '' assert tmpl.render() == '' tmpl = Template('{{ plop|date:"d B Y" }}') if django.VERSION < (3, 2): # B raises NotImplementedError assert tmpl.render({'plop': '2017-12-21'}) == '' else: # B is not considered a format character assert tmpl.render({'plop': '2017-12-21'}) == '21 B 2017' tmpl = Template('{% if d1|date > d2|date %}d1>d2{% else %}d1<=d2{% endif %}') assert tmpl.render({'d1': '2017-12-22', 'd2': '2017-12-21'}) == 'd1>d2' assert tmpl.render({'d1': '2017-12-21', 'd2': '2017-12-21'}) == 'd1<=d2' assert tmpl.render({'d1': '2017-12-22'}) == 'd1<=d2' assert tmpl.render({'d2': '2017-12-22'}) == 'd1<=d2' tmpl = Template('{{ plop|time }}') assert tmpl.render({'plop': '10:32'}) == '10:32 a.m.' assert tmpl.render({'plop': '2017-12-21 10:32'}) == '10:32 a.m.' assert tmpl.render({'plop': '21/12/2017 10h32'}) == '10:32 a.m.' assert tmpl.render({'plop': '21/12/2017'}) == 'midnight' assert tmpl.render({'plop': 'x'}) == '' assert tmpl.render({'plop': None}) == '' assert tmpl.render({'plop': 3}) == '' assert tmpl.render({'plop': {'foo': 'bar'}}) == '' assert tmpl.render() == '' tmpl = Template('{{ plop|time:"H i" }}') assert tmpl.render({'plop': '10:32'}) == '10 32' assert tmpl.render({'plop': '2017-12-21 10:32'}) == '10 32' assert tmpl.render({'plop': '21/12/2017 10h32'}) == '10 32' assert tmpl.render({'plop': '21/12/2017'}) == '00 00' assert tmpl.render({'plop': 'x'}) == '' assert tmpl.render({'plop': None}) == '' assert tmpl.render({'plop': 3}) == '' assert tmpl.render({'plop': {'foo': 'bar'}}) == '' assert tmpl.render() == '' # old fashion, with parse_* tmpl = Template('{{ plop|parse_datetime|date:"d i" }}') assert tmpl.render({'plop': '2017-12-21 10:32'}) == '21 32' assert tmpl.render({'plop': '2017-12-21 10:32:42'}) == '21 32' assert tmpl.render({'plop': '21/12/2017 10:32'}) == '21 32' assert tmpl.render({'plop': '21/12/2017 10:32:42'}) == '21 32' assert tmpl.render({'plop': '21/12/2017 10h32'}) == '21 32' assert tmpl.render({'plop': 'x'}) == '' assert tmpl.render({'plop': None}) == '' assert tmpl.render({'plop': 3}) == '' assert tmpl.render({'plop': {'foo': 'bar'}}) == '' assert tmpl.render() == '' tmpl = Template('{{ plop|parse_date|date:"d" }}') assert tmpl.render({'plop': '2017-12-21'}) == '21' assert tmpl.render({'plop': '21/12/2017'}) == '21' assert tmpl.render({'plop': 'x'}) == '' assert tmpl.render({'plop': None}) == '' assert tmpl.render({'plop': 3}) == '' assert tmpl.render({'plop': {'foo': 'bar'}}) == '' assert tmpl.render() == '' tmpl = Template('{{ plop|parse_time|date:"H i" }}') assert tmpl.render({'plop': '10:32'}) == '10 32' assert tmpl.render({'plop': 'x'}) == '' assert tmpl.render({'plop': None}) == '' assert tmpl.render({'plop': 3}) == '' assert tmpl.render({'plop': {'foo': 'bar'}}) == '' assert tmpl.render() == '' def test_date_maths(pub): tmpl = Template('{{ plop|add_days:4 }}') assert tmpl.render({'plop': '2017-12-21'}) == '2017-12-25' assert tmpl.render({'plop': '2017-12-21 18:00'}) == '2017-12-25' tmpl = Template('{{ plop|add_days:"-1" }}') assert tmpl.render({'plop': '2017-12-21'}) == '2017-12-20' assert tmpl.render({'plop': '2017-12-21 18:00'}) == '2017-12-20' tmpl = Template('{{ plop|add_days:1.5 }}') assert tmpl.render({'plop': '2017-12-21'}) == '2017-12-22' assert tmpl.render({'plop': '2017-12-21 18:00'}) == '2017-12-22' tmpl = Template('{{ plop|add_days:"1.5" }}') assert tmpl.render({'plop': '2017-12-21'}) == '2017-12-22' assert tmpl.render({'plop': '2017-12-21 18:00'}) == '2017-12-22' tmpl = Template('{{ plop|add_hours:24 }}') assert tmpl.render({'plop': '2017-12-21'}) == '2017-12-22 00:00' assert tmpl.render({'plop': '2017-12-21 18:00'}) == '2017-12-22 18:00' tmpl = Template('{{ plop|add_hours:"12.5" }}') assert tmpl.render({'plop': '2017-12-21'}) == '2017-12-21 12:30' assert tmpl.render({'plop': '2017-12-21 18:00'}) == '2017-12-22 06:30' tmpl = Template('{{ plop|add_minutes:30 }}') assert tmpl.render({'plop': '2017-12-21'}) == '2017-12-21 00:30' assert tmpl.render({'plop': '2017-12-21 18:00'}) == '2017-12-21 18:30' tmpl = Template('{{ plop|add_minutes:"12.5"|date:"Y-m-d H:m:s" }}') assert tmpl.render({'plop': '2017-12-21'}) == '2017-12-21 00:12:30' assert tmpl.render({'plop': '2017-12-21 18:00'}) == '2017-12-21 18:12:30' def test_variable_unicode_error_handling(): tmpl = Template('{{ form_var_éléphant }}') assert tmpl.render() == '' def test_decimal_templatetag(): tmpl = Template('{{ plop|decimal }}') assert tmpl.render({'plop': 'toto'}) == '0' assert tmpl.render({'plop': '3.14'}) == '3.14' assert tmpl.render({'plop': '3,14'}) == '3.14' assert tmpl.render({'plop': 3.14}) == '3.14' assert tmpl.render({'plop': 12345.678}) == '12345.678' assert tmpl.render({'plop': None}) == '0' assert tmpl.render({'plop': 0}) == '0' tmpl = Template('{{ plop|decimal:3 }}') assert tmpl.render({'plop': '3.14'}) == '3.140' assert tmpl.render({'plop': None}) == '0.000' tmpl = Template('{{ plop|decimal:"3" }}') assert tmpl.render({'plop': '3.14'}) == '3.140' assert tmpl.render({'plop': None}) == '0.000' tmpl = Template('{% if plop|decimal > 2 %}hello{% endif %}') assert tmpl.render({'plop': 3}) == 'hello' assert tmpl.render({'plop': '3'}) == 'hello' assert tmpl.render({'plop': 2.001}) == 'hello' assert tmpl.render({'plop': '2.001'}) == 'hello' assert tmpl.render({'plop': 1}) == '' assert tmpl.render({'plop': 1.99}) == '' assert tmpl.render({'plop': '1.99'}) == '' assert tmpl.render({'plop': 'x'}) == '' assert tmpl.render({'plop': None}) == '' assert tmpl.render({'plop': 0}) == '' tmpl = Template('{% if "3"|decimal == 3 %}hello{% endif %}') assert tmpl.render() == 'hello' tmpl = Template('{% if "3"|decimal == 3.0 %}hello{% endif %}') assert tmpl.render() == 'hello' tmpl = Template('{% if 3|decimal == 3 %}hello{% endif %}') assert tmpl.render() == 'hello' tmpl = Template('{% if 3.0|decimal == 3 %}hello{% endif %}') assert tmpl.render() == 'hello' tmpl = Template('{% if 3|decimal|decimal == 3 %}hello{% endif %}') assert tmpl.render() == 'hello' def test_mathematics_templatetag(): tmpl = Template('{{ term1|add:term2 }}') # using strings assert tmpl.render({'term1': '1.1', 'term2': 0}) == '1.1' assert tmpl.render({'term1': 'not a number', 'term2': 1.2}) == '' assert tmpl.render({'term1': 0.3, 'term2': "1"}) == '1.3' assert tmpl.render({'term1': 1.4, 'term2': "not a number"}) == '' # add assert tmpl.render({'term1': 4, 'term2': -0.9}) == '3.1' assert tmpl.render({'term1': '4', 'term2': -0.8}) == '3.2' assert tmpl.render({'term1': 4, 'term2': '-0.7'}) == '3.3' assert tmpl.render({'term1': '4', 'term2': '-0.6'}) == '3.4' assert tmpl.render({'term1': '', 'term2': 3.5}) == '3.5' assert tmpl.render({'term1': None, 'term2': 3.5}) == '3.5' assert tmpl.render({'term1': 3.6, 'term2': ''}) == '3.6' assert tmpl.render({'term1': '', 'term2': ''}) == '' assert tmpl.render({'term1': 3.6, 'term2': None}) == '3.6' assert tmpl.render({'term1': 0, 'term2': ''}) == '0' assert tmpl.render({'term1': '', 'term2': 0}) == '0' assert tmpl.render({'term1': 0, 'term2': 0}) == '0' # add using ',' instead of '.' decimal separator assert tmpl.render({'term1': '1,1', 'term2': '2,2'}) == '3.3' assert tmpl.render({'term1': '1,1', 'term2': '2.2'}) == '3.3' assert tmpl.render({'term1': '1,1', 'term2': 2.2}) == '3.3' assert tmpl.render({'term1': '1,1', 'term2': 0}) == '1.1' assert tmpl.render({'term1': '1,1', 'term2': ''}) == '1.1' assert tmpl.render({'term1': '1,1', 'term2': None}) == '1.1' assert tmpl.render({'term1': '1.1', 'term2': '2,2'}) == '3.3' assert tmpl.render({'term1': 1.1, 'term2': '2,2'}) == '3.3' assert tmpl.render({'term1': 0, 'term2': '2,2'}) == '2.2' assert tmpl.render({'term1': '', 'term2': '2,2'}) == '2.2' assert tmpl.render({'term1': None, 'term2': '2,2'}) == '2.2' # fallback to Django native add filter assert tmpl.render({'term1': 'foo', 'term2': 'bar'}) == 'foobar' assert tmpl.render({'term1': 'foo', 'term2': ''}) == 'foo' assert tmpl.render({'term1': 'foo', 'term2': None}) == 'foo' assert tmpl.render({'term1': '', 'term2': 'bar'}) == 'bar' assert tmpl.render({'term1': '', 'term2': None}) == '' assert tmpl.render({'term1': None, 'term2': 'bar'}) == 'bar' assert tmpl.render({'term1': None, 'term2': ''}) == '' assert tmpl.render({'term1': None, 'term2': None}) == '' assert tmpl.render({'term1': [1, 2], 'term2': [3, 4]}) == '[1, 2, 3, 4]' # subtract tmpl = Template('{{ term1|subtract:term2 }}') assert tmpl.render({'term1': 5.1, 'term2': 1}) == '4.1' assert tmpl.render({'term1': '5.2', 'term2': 1}) == '4.2' assert tmpl.render({'term1': 5.3, 'term2': '1'}) == '4.3' assert tmpl.render({'term1': '5.4', 'term2': '1'}) == '4.4' assert tmpl.render({'term1': '', 'term2': -4.5}) == '4.5' assert tmpl.render({'term1': 4.6, 'term2': ''}) == '4.6' assert tmpl.render({'term1': '', 'term2': ''}) == '0' assert tmpl.render({'term1': 0, 'term2': ''}) == '0' assert tmpl.render({'term1': '', 'term2': 0}) == '0' assert tmpl.render({'term1': 0, 'term2': 0}) == '0' # multiply tmpl = Template('{{ term1|multiply:term2 }}') assert tmpl.render({'term1': '3', 'term2': '2'}) == '6' assert tmpl.render({'term1': 2.5, 'term2': 2}) == '5.0' assert tmpl.render({'term1': '2.5', 'term2': 2}) == '5.0' assert tmpl.render({'term1': 2.5, 'term2': '2'}) == '5.0' assert tmpl.render({'term1': '2.5', 'term2': '2'}) == '5.0' assert tmpl.render({'term1': '', 'term2': '2'}) == '0' assert tmpl.render({'term1': 2.5, 'term2': ''}) == '0.0' assert tmpl.render({'term1': '', 'term2': ''}) == '0' assert tmpl.render({'term1': 0, 'term2': ''}) == '0' assert tmpl.render({'term1': '', 'term2': 0}) == '0' assert tmpl.render({'term1': 0, 'term2': 0}) == '0' assert tmpl.render({'term1': 780, 'term2': 0.000463}) == '0.36114' # divide tmpl = Template('{{ term1|divide:term2 }}') assert tmpl.render({'term1': 16, 'term2': 2}) == '8' assert tmpl.render({'term1': 6, 'term2': 0.75}) == '8' assert tmpl.render({'term1': '6', 'term2': 0.75}) == '8' assert tmpl.render({'term1': 6, 'term2': '0.75'}) == '8' assert tmpl.render({'term1': '6', 'term2': '0.75'}) == '8' assert tmpl.render({'term1': '', 'term2': '2'}) == '0' assert tmpl.render({'term1': 6, 'term2': ''}) == '' assert tmpl.render({'term1': '', 'term2': ''}) == '' assert tmpl.render({'term1': 0, 'term2': ''}) == '' assert tmpl.render({'term1': '', 'term2': 0}) == '' assert tmpl.render({'term1': 0, 'term2': 0}) == '' tmpl = Template('{{ term1|divide:term2|decimal:2 }}') assert tmpl.render({'term1': 2, 'term2': 3}) == '0.67' # modulo tmpl = Template('{{ term1|modulo:term2 }}') assert tmpl.render({'term1': 16, 'term2': 2}) == '0' assert tmpl.render({'term1': 16, 'term2': 3}) == '1' assert tmpl.render({'term1': 16, 'term2': 7}) == '2' assert tmpl.render({'term1': 6, 'term2': 0.75}) == '0.00' assert tmpl.render({'term1': 6, 'term2': 0.85}) == '0.05' assert tmpl.render({'term1': '6', 'term2': 0.75}) == '0.00' assert tmpl.render({'term1': 6, 'term2': '0.75'}) == '0.00' assert tmpl.render({'term1': '6', 'term2': '0.75'}) == '0.00' assert tmpl.render({'term1': '', 'term2': '2'}) == '0' assert tmpl.render({'term1': 6, 'term2': ''}) == '' assert tmpl.render({'term1': '', 'term2': ''}) == '' assert tmpl.render({'term1': 0, 'term2': ''}) == '' assert tmpl.render({'term1': '', 'term2': 0}) == '' assert tmpl.render({'term1': 0, 'term2': 0}) == '' assert tmpl.render({'term1': 'a', 'term2': 2}) == '0' assert tmpl.render({'term1': 2, 'term2': 'b'}) == '' tmpl = Template('{{ term1|modulo:term2|decimal:2 }}') assert tmpl.render({'term1': 2, 'term2': 3}) == '2.00' def test_rounding_templatetag(): # ceil tmpl = Template('{{ value|ceil }}') assert tmpl.render({'value': 3.14}) == '4' assert tmpl.render({'value': 3.99}) == '4' assert tmpl.render({'value': -3.14}) == '-3' assert tmpl.render({'value': -3.99}) == '-3' assert tmpl.render({'value': 0}) == '0' assert tmpl.render({'value': '3.14'}) == '4' assert tmpl.render({'value': '3.99'}) == '4' assert tmpl.render({'value': '-3.14'}) == '-3' assert tmpl.render({'value': '-3.99'}) == '-3' assert tmpl.render({'value': '0'}) == '0' assert tmpl.render({'value': 'not a number'}) == '0' assert tmpl.render({'value': ''}) == '0' assert tmpl.render({'value': None}) == '0' # floor tmpl = Template('{{ value|floor }}') assert tmpl.render({'value': 3.14}) == '3' assert tmpl.render({'value': 3.99}) == '3' assert tmpl.render({'value': -3.14}) == '-4' assert tmpl.render({'value': -3.99}) == '-4' assert tmpl.render({'value': 0}) == '0' assert tmpl.render({'value': '3.14'}) == '3' assert tmpl.render({'value': '3.99'}) == '3' assert tmpl.render({'value': '-3.14'}) == '-4' assert tmpl.render({'value': '-3.99'}) == '-4' assert tmpl.render({'value': '0'}) == '0' assert tmpl.render({'value': 'not a number'}) == '0' assert tmpl.render({'value': ''}) == '0' assert tmpl.render({'value': None}) == '0' def test_abs_templatetag(): tmpl = Template('{{ value|abs }}') assert tmpl.render({'value': 3.14}) == '3.14' assert tmpl.render({'value': -3.14}) == '3.14' assert tmpl.render({'value': 0}) == '0' assert tmpl.render({'value': '3.14'}) == '3.14' assert tmpl.render({'value': '-3.14'}) == '3.14' assert tmpl.render({'value': '0'}) == '0' assert tmpl.render({'value': 'not a number'}) == '0' assert tmpl.render({'value': ''}) == '0' assert tmpl.render({'value': None}) == '0' def test_token_decimal(): tokens = [Template('{% token_decimal 4 %}').render() for i in range(100)] assert all(len(token) == 4 for token in tokens) assert all(token.isdigit() for token in tokens) # check randomness, i.e. duplicates are rare assert len(set(tokens)) > 70 t = Template('{% if token1|token_check:token2 %}ok{% endif %}') assert t.render({'token1': tokens[0] + ' ', 'token2': tokens[0].lower()}) == 'ok' t = Template('{% if "é"|token_check:"è" %}ok{% endif %}') assert t.render({'token1': tokens[0] + ' ', 'token2': tokens[0].lower()}) == '' def test_token_alphanum(): tokens = [Template('{% token_alphanum 4 %}').render() for i in range(100)] assert all(len(token) == 4 for token in tokens) assert all(token.upper() == token for token in tokens) assert all(token.isalnum() for token in tokens) # check randomness, i.e. duplicates are rare assert len(set(tokens)) > 90 # check there are letters and digits assert any(set(token) & set(string.ascii_uppercase) for token in tokens) assert any(set(token) & set(string.digits) for token in tokens) # no look-alike characters assert not any(set(token) & set('01IiOo') for token in tokens) t = Template('{% if token1|token_check:token2 %}ok{% endif %}') assert t.render({'token1': tokens[0] + ' ', 'token2': tokens[0].lower()}) == 'ok' def test_distance(): t = Template( '{{ "48;2"|distance:"48.1;2.1"|floatformat }}', ) assert t.render() == '13387.2' t = Template( '{{ coords|distance:"48.1;2.1"|floatformat }}', ) assert t.render({'coords': '48;2'}) == '13387.2' t = Template( '{{ "48;2"|distance:coords|floatformat }}', ) assert t.render({'coords': '48.1;2.1'}) == '13387.2' t = Template( '{{ c1|distance:c2|floatformat }}', ) assert t.render({'c1': '48;2', 'c2': '48.1;2.1'}) == '13387.2' assert t.render({'c1': {'lat': '48', 'lon': '2'}, 'c2': {'lat': '48.1', 'lng': '2.1'}}) == '13387.2' assert t.render({'c1': {'lat': '48', 'lng': '2'}, 'c2': {'lat': '48.1', 'lng': '2.1'}}) == '13387.2' class MockFormData: formdef = None geolocations = {'base': {'lat': 48, 'lon': 2}} lazy_formdata = LazyFormData(MockFormData()) for tpl in ('{{ formdata|distance:coords|floatformat }}', '{{ coords|distance:formdata|floatformat }}'): t = Template( tpl, ) assert t.render({'formdata': lazy_formdata, 'coords': '48.1;2.1'}) == '13387.2' assert t.render({'formdata': lazy_formdata, 'coords': '49.1;3.1'}) == '146821.9' assert t.render({'formdata': lazy_formdata, 'coords': 'abc;def'}) == '' assert t.render({'formdata': lazy_formdata, 'coords': '42'}) == '' assert t.render({'formdata': lazy_formdata, 'coords': ''}) == '' MockFormData.geolocations = {} for tpl in ('{{ formdata|distance:coords|floatformat }}', '{{ coords|distance:formdata|floatformat }}'): t = Template( tpl, ) assert t.render({'formdata': lazy_formdata, 'coords': '49.1;3.1'}) == '' def test_get_filter(): tmpl = Template('{{ foo|get:"bar" }}') assert tmpl.render({'foo': {'bar': 'baz'}}) == 'baz' tmpl = Template('{{ foo|get:0 }}') assert tmpl.render({'foo': ['bar', 'baz']}) == 'bar' tmpl = Template('{{ foo|get:0|default_if_none:"" }}') assert tmpl.render({'foo': ""}) == '' assert tmpl.render({'foo': None}) == '' assert tmpl.render({'foo': 23}) == '' def test_get_on_lazy_var(): FormDef.wipe() formdef = FormDef() formdef.name = 'lazy' formdef.fields = [ fields.StringField(id='0', label='string', varname='foo'), fields.StringField(id='1', label='string', varname='bar'), ] formdef.store() formdef.data_class().wipe() formdata = formdef.data_class()() formdata.just_created() formdata.data = { '0': 'bar', '1': '1', } formdata.store() context = CompatibilityNamesDict( { 'form': LazyFormData(formdata), 'foo_dict': {'bar': 'baz'}, 'foo_array': ['bar', 'baz'], } ) tmpl = Template('{{ foo_dict|get:form_var_foo }}') assert tmpl.render(context) == 'baz' tmpl = Template('{{ foo_array|get:form_var_bar }}') assert tmpl.render(context) == 'baz' def test_reproj(): class MockFormData: formdef = None geolocations = {'base': {'lat': 48, 'lon': 2}} lazy_formdata = LazyFormData(MockFormData()) tmpl = Template('{% with form_geoloc_base|reproj:"EPSG:3946" as c %}{{c.0}}/{{c.1}}{% endwith %}') coords = tmpl.render(CompatibilityNamesDict({'form': lazy_formdata})).split('/') assert int(float(coords[0])) == 1625337 assert int(float(coords[1])) == 5422836 def test_phonenumber_fr(): t = Template('{{ number|phonenumber_fr }}') assert t.render({'number': '01 23 45 67 89'}) == '01 23 45 67 89' assert t.render({'number': '0 1 23 45 67 89'}) == '01 23 45 67 89' assert t.render({'number': '0123456789'}) == '01 23 45 67 89' assert t.render({'number': '01.23.45.67.89'}) == '01 23 45 67 89' assert t.render({'number': '01 23 45 67 89'}) == '01 23 45 67 89' assert t.render({'number': '00 33 1 23 45 67 89'}) == '00 33 1 23 45 67 89' assert t.render({'number': '00 33 1 23 45 67 89'}) == '00 33 1 23 45 67 89' assert t.render({'number': '+33 1 23 45 67 89'}) == '+33 1 23 45 67 89' assert t.render({'number': '+33 (0)1 23 45 67 89'}) == '+33 1 23 45 67 89' # drom assert t.render({'number': '02 62 11 22 33'}) == '02 62 11 22 33' assert t.render({'number': '00 262 11 22 33'}) == '00 262 11 22 33' assert t.render({'number': '+262 112233'}) == '+262 11 22 33' t = Template('{{ number|phonenumber_fr:"." }}') assert t.render({'number': '01 23 45 67 89'}) == '01.23.45.67.89' # unknown assert t.render({'number': '12 3'}) == '12 3' assert t.render({'number': 'bla bla'}) == 'bla bla' assert t.render({'number': None}) == 'None' t = Template('{{ number|decimal|phonenumber_fr }}') assert t.render({'number': '1,33'}) == '1.33' # lazy mode FormDef.wipe() formdef = FormDef() formdef.name = 'lazy' formdef.fields = [fields.StringField(id='0', label='string', varname='phone')] formdef.store() formdata = formdef.data_class()() formdata.data = {'0': '0143350135'} formdata.store() context = CompatibilityNamesDict({'form': LazyFormData(formdata)}) tmpl = Template('{{ form_var_phone|phonenumber_fr:"-" }}') assert tmpl.render(context) == '01-43-35-01-35' def test_is_french_mobile_phone_number(): t = Template('{{ number|is_french_mobile_phone_number }}') assert t.render({'number': '01 23 45 67 8989'}) == 'False' assert t.render({'number': '06 23 45 67 89 89'}) == 'False' assert t.render({'number': '06 23 45 89'}) == 'False' assert t.render({'number': '0 6 2 3 45 89'}) == 'False' assert t.render({'number': '06 23 45 67 89'}) == 'True' assert t.render({'number': '07 23 45 67 89'}) == 'True' assert t.render({'number': '06.23.45.67.89'}) == 'True' assert t.render({'number': '0 6 2 3 45 67 89'}) == 'True' @pytest.mark.skipif('langdetect is None') def test_language_detect(): t = Template('{{ plop|language_detect }}') assert t.render({'plop': 'Good morning world'}) == 'en' assert t.render({'plop': 'Bonjour tout le monde'}) == 'fr' assert t.render({'plop': '2132133'}) == '' @pytest.mark.parametrize( 'value, expected', [ (None, False), ('', False), ('foobar', False), (42, False), ('1970-06-15T12:01:03', True), ('2500-06-15T12:01:02', False), ('1970-01-01 02:03', True), ('2500-01-01 02:03', False), ('01/01/1970 02h03', True), ('01/01/2500 02h03', False), ('1970-01-01', True), ('2500-01-01', False), ('01/01/1970', True), ('01/01/2500', False), (datetime.datetime(1970, 6, 15, 12, 1, 3), True), (datetime.datetime(2500, 6, 15, 12, 1, 2), False), (datetime.date(1970, 6, 15), True), (datetime.date(2500, 6, 15), False), (datetime.datetime.now(), True), (datetime.datetime.now() + datetime.timedelta(hours=1), False), (now(), True), (now() + datetime.timedelta(hours=1), False), (datetime.date.today(), True), (datetime.date.today() + datetime.timedelta(days=1), False), ], ) def test_datetime_in_past(value, expected): t = Template('{{ value|datetime_in_past }}') assert t.render({'value': value}) == str(expected) def test_is_working_day_settings(settings, pub): settings.WORKING_DAY_CALENDAR = None t = Template('{{ value|is_working_day }}') assert t.render({'value': '2020-07-15'}) == 'False' t = Template('{{ value|is_working_day_with_saturday }}') assert t.render({'value': '2020-07-15'}) == 'False' settings.WORKING_DAY_CALENDAR = '' t = Template('{{ value|is_working_day }}') assert t.render({'value': '2020-07-15'}) == 'False' t = Template('{{ value|is_working_day_with_saturday }}') assert t.render({'value': '2020-07-15'}) == 'False' settings.WORKING_DAY_CALENDAR = 'foobar' t = Template('{{ value|is_working_day }}') assert t.render({'value': '2020-07-15'}) == 'False' t = Template('{{ value|is_working_day_with_saturday }}') assert t.render({'value': '2020-07-15'}) == 'False' settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France' t = Template('{{ value|is_working_day }}') assert t.render({'value': '2020-07-15'}) == 'True' t = Template('{{ value|is_working_day_with_saturday }}') assert t.render({'value': '2020-07-15'}) == 'True' pub.site_options.set('options', 'working_day_calendar', 'foobar') with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd: pub.site_options.write(fd) t = Template('{{ value|is_working_day }}') assert t.render({'value': '2020-07-15'}) == 'False' t = Template('{{ value|is_working_day_with_saturday }}') assert t.render({'value': '2020-07-15'}) == 'False' settings.WORKING_DAY_CALENDAR = 'foobar' pub.site_options.set('options', 'working_day_calendar', 'workalendar.europe.France') with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd: pub.site_options.write(fd) t = Template('{{ value|is_working_day }}') assert t.render({'value': '2020-07-15'}) == 'True' t = Template('{{ value|is_working_day_with_saturday }}') assert t.render({'value': '2020-07-15'}) == 'True' @pytest.mark.parametrize( 'value, expected', [ (None, False), ('', False), ('foobar', False), (42, False), ('2020-07-14T12:01:03', False), ('2020-07-15T12:01:03', True), ('2020-07-14 02:03', False), ('2020-07-15 02:03', True), ('14/07/2020 02h03', False), ('15/07/2020 02h03', True), ('2020-07-14', False), ('2020-07-15', True), ('14/07/2020', False), ('15/07/2020', True), (datetime.datetime(2020, 7, 14, 12, 1, 3), False), (datetime.datetime(2020, 7, 15, 12, 1, 3), True), (datetime.date(2020, 7, 14), False), (datetime.date(2020, 7, 15), True), ], ) def test_is_working_day(settings, pub, value, expected): settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France' t = Template('{{ value|is_working_day }}') assert t.render({'value': value}) == str(expected) t = Template('{{ value|is_working_day_with_saturday }}') assert t.render({'value': value}) == str(expected) def test_is_working_day_weekend(settings, pub): settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France' # check saturday t = Template('{{ value|is_working_day }}') assert t.render({'value': '2020-06-20'}) == 'False' t = Template('{{ value|is_working_day_with_saturday }}') assert t.render({'value': '2020-06-20'}) == 'True' # check sunday t = Template('{{ value|is_working_day }}') assert t.render({'value': '2020-06-21'}) == 'False' t = Template('{{ value|is_working_day_with_saturday }}') assert t.render({'value': '2020-06-21'}) == 'False' def test_add_working_days_settings(settings, pub): settings.WORKING_DAY_CALENDAR = None t = Template('{{ value|add_working_days:1 }}') assert t.render({'value': '2020-07-13'}) == '' t = Template('{{ value|add_working_days_with_saturday:1 }}') assert t.render({'value': '2020-07-13'}) == '' settings.WORKING_DAY_CALENDAR = '' t = Template('{{ value|add_working_days:1 }}') assert t.render({'value': '2020-07-13'}) == '' t = Template('{{ value|add_working_days_with_saturday:1 }}') assert t.render({'value': '2020-07-13'}) == '' settings.WORKING_DAY_CALENDAR = 'foobar' t = Template('{{ value|add_working_days:1 }}') assert t.render({'value': '2020-07-13'}) == '' t = Template('{{ value|add_working_days_with_saturday:1 }}') assert t.render({'value': '2020-07-13'}) == '' settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France' t = Template('{{ value|add_working_days:1 }}') assert t.render({'value': '2020-07-13'}) == '2020-07-15' t = Template('{{ value|add_working_days_with_saturday:1 }}') assert t.render({'value': '2020-07-13'}) == '2020-07-15' pub.site_options.set('options', 'working_day_calendar', 'foobar') with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd: pub.site_options.write(fd) t = Template('{{ value|add_working_days:1 }}') assert t.render({'value': '2020-07-13'}) == '' t = Template('{{ value|add_working_days_with_saturday:1 }}') assert t.render({'value': '2020-07-13'}) == '' settings.WORKING_DAY_CALENDAR = 'foobar' pub.site_options.set('options', 'working_day_calendar', 'workalendar.europe.France') with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd: pub.site_options.write(fd) t = Template('{{ value|add_working_days:1 }}') assert t.render({'value': '2020-07-13'}) == '2020-07-15' t = Template('{{ value|add_working_days_with_saturday:1 }}') assert t.render({'value': '2020-07-13'}) == '2020-07-15' def test_add_working_days_arg(settings, pub): settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France' t = Template('{{ value|add_working_days:"foobar" }}') assert t.render({'value': '2020-07-13'}) == '' t = Template('{{ value|add_working_days_with_saturday:"foobar" }}') assert t.render({'value': '2020-07-13'}) == '' t = Template('{{ value|add_working_days:2 }}') assert t.render({'value': '2020-07-13'}) == '2020-07-16' t = Template('{{ value|add_working_days_with_saturday:2 }}') assert t.render({'value': '2020-07-13'}) == '2020-07-16' @pytest.mark.parametrize( 'value, expected', [ (None, ''), ('', ''), ('foobar', ''), (42, ''), ('2020-07-13T12:01:03', '2020-07-15'), ('2020-07-13 02:03', '2020-07-15'), ('13/07/2020 02h03', '2020-07-15'), ('2020-07-13', '2020-07-15'), ('13/07/2020', '2020-07-15'), (datetime.datetime(2020, 7, 13, 12, 1, 3), '2020-07-15'), (datetime.date(2020, 7, 13), '2020-07-15'), ], ) def test_add_working_days(settings, pub, value, expected): settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France' t = Template('{{ value|add_working_days:1 }}') assert t.render({'value': value}) == str(expected) t = Template('{{ value|add_working_days_with_saturday:1 }}') assert t.render({'value': value}) == str(expected) def test_add_working_days_weekend(settings, pub): settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France' t = Template('{{ value|add_working_days:1 }}') assert t.render({'value': '2020-06-19'}) == '2020-06-22' t = Template('{{ value|add_working_days_with_saturday:1 }}') assert t.render({'value': '2020-06-19'}) == '2020-06-20' def test_adjust_to_working_day_settings(settings, pub): settings.WORKING_DAY_CALENDAR = None t = Template('{{ value|adjust_to_working_day }}') assert t.render({'value': '2020-07-13'}) == '' t = Template('{{ value|adjust_to_working_day_with_saturday }}') assert t.render({'value': '2020-07-13'}) == '' settings.WORKING_DAY_CALENDAR = '' t = Template('{{ value|adjust_to_working_day }}') assert t.render({'value': '2020-07-13'}) == '' t = Template('{{ value|adjust_to_working_day_with_saturday }}') assert t.render({'value': '2020-07-13'}) == '' settings.WORKING_DAY_CALENDAR = 'foobar' t = Template('{{ value|adjust_to_working_day }}') assert t.render({'value': '2020-07-13'}) == '' t = Template('{{ value|adjust_to_working_day_with_saturday }}') assert t.render({'value': '2020-07-13'}) == '' settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France' t = Template('{{ value|adjust_to_working_day }}') assert t.render({'value': '2020-07-14'}) == '2020-07-15' t = Template('{{ value|adjust_to_working_day_with_saturday }}') assert t.render({'value': '2020-07-14'}) == '2020-07-15' pub.site_options.set('options', 'working_day_calendar', 'foobar') with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd: pub.site_options.write(fd) t = Template('{{ value|adjust_to_working_day }}') assert t.render({'value': '2020-07-13'}) == '' t = Template('{{ value|adjust_to_working_day_with_saturday }}') assert t.render({'value': '2020-07-13'}) == '' settings.WORKING_DAY_CALENDAR = 'foobar' pub.site_options.set('options', 'working_day_calendar', 'workalendar.europe.France') with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd: pub.site_options.write(fd) t = Template('{{ value|adjust_to_working_day }}') assert t.render({'value': '2020-07-14'}) == '2020-07-15' t = Template('{{ value|adjust_to_working_day_with_saturday }}') assert t.render({'value': '2020-07-14'}) == '2020-07-15' @pytest.mark.parametrize( 'value, expected', [ (None, ''), ('', ''), ('foobar', ''), (42, ''), ('2020-07-14T12:01:03', '2020-07-15'), ('2020-07-14 02:03', '2020-07-15'), ('14/07/2020 02h03', '2020-07-15'), ('2020-07-14', '2020-07-15'), ('14/07/2020', '2020-07-15'), (datetime.datetime(2020, 7, 14, 12, 1, 3), '2020-07-15'), (datetime.date(2020, 7, 14), '2020-07-15'), (datetime.date(2020, 7, 15), '2020-07-15'), ], ) def test_adjust_to_working_day(settings, pub, value, expected): settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France' t = Template('{{ value|adjust_to_working_day }}') assert t.render({'value': value}) == str(expected) t = Template('{{ value|adjust_to_working_day_with_saturday }}') assert t.render({'value': value}) == str(expected) def test_adjust_to_working_day_weekend(settings, pub): settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France' t = Template('{{ value|adjust_to_working_day }}') assert t.render({'value': '2020-06-20'}) == '2020-06-22' t = Template('{{ value|adjust_to_working_day_with_saturday }}') assert t.render({'value': '2020-06-20'}) == '2020-06-20' def test_age_in_working_days_settings(settings, pub, freezer): freezer.move_to('2020-07-01T00:00:00') settings.WORKING_DAY_CALENDAR = None t = Template('{{ value|age_in_working_days:"2020-07-15" }}') assert t.render({'value': '2020-07-12'}) == '' t = Template('{{ value|age_in_working_days_with_saturday:"2020-07-15" }}') assert t.render({'value': '2020-07-12'}) == '' settings.WORKING_DAY_CALENDAR = '' t = Template('{{ value|age_in_working_days:"2020-07-15" }}') assert t.render({'value': '2020-07-12'}) == '' t = Template('{{ value|age_in_working_days_with_saturday:"2020-07-15" }}') assert t.render({'value': '2020-07-12'}) == '' settings.WORKING_DAY_CALENDAR = 'foobar' t = Template('{{ value|age_in_working_days:"2020-07-15" }}') assert t.render({'value': '2020-07-12'}) == '' t = Template('{{ value|age_in_working_days_with_saturday:"2020-07-15" }}') assert t.render({'value': '2020-07-12'}) == '' settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France' t = Template('{{ value|age_in_working_days:"2020-07-15" }}') assert t.render({'value': '2020-07-12'}) == '2' t = Template('{{ value|age_in_working_days_with_saturday:"2020-07-15" }}') assert t.render({'value': '2020-07-12'}) == '2' settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France' t = Template('{{ value|age_in_working_days }}') assert t.render({'value': '2020-07-12'}) == '7' t = Template('{{ value|age_in_working_days_with_saturday }}') assert t.render({'value': '2020-07-12'}) == '9' pub.site_options.set('options', 'working_day_calendar', 'foobar') with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd: pub.site_options.write(fd) t = Template('{{ value|age_in_working_days:"2020-07-15" }}') assert t.render({'value': '2020-07-12'}) == '' t = Template('{{ value|age_in_working_days_with_saturday:"2020-07-15" }}') assert t.render({'value': '2020-07-12'}) == '' settings.WORKING_DAY_CALENDAR = 'foobar' pub.site_options.set('options', 'working_day_calendar', 'workalendar.europe.France') with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd: pub.site_options.write(fd) t = Template('{{ value|age_in_working_days:"2020-07-15" }}') assert t.render({'value': '2020-07-12'}) == '2' t = Template('{{ value|age_in_working_days_with_saturday:"2020-07-15" }}') assert t.render({'value': '2020-07-12'}) == '2' def test_age_in_working_days_arg(settings, pub): settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France' t = Template('{{ value|age_in_working_days:"foobar" }}') assert t.render({'value': '2020-07-12'}) == '' t = Template('{{ value|age_in_working_days_with_saturday:"foobar" }}') assert t.render({'value': '2020-07-12'}) == '' t = Template('{{ value|age_in_working_days:"2020-07-15" }}') assert t.render({'value': '2020-07-12'}) == '2' t = Template('{{ value|age_in_working_days_with_saturday:"2020-07-15" }}') assert t.render({'value': '2020-07-12'}) == '2' @pytest.mark.parametrize( 'value, expected', [ (None, ''), ('', ''), ('foobar', ''), (42, ''), ('2020-07-14T12:01:03', '2'), ('2020-07-14 02:03', '2'), ('14/07/2020 02h03', '2'), ('2020-07-14', '2'), ('14/07/2020', '2'), (datetime.datetime(2020, 7, 14, 12, 1, 3), '2'), (datetime.date(2020, 7, 14), '2'), (datetime.date(2020, 7, 15), '1'), ], ) def test_age_in_working_days(settings, pub, value, expected): settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France' t = Template('{{ value|age_in_working_days:"2020-07-16" }}') assert t.render({'value': value}) == str(expected) t = Template('{{ value|age_in_working_days_with_saturday:"2020-07-16" }}') assert t.render({'value': value}) == str(expected) def test_age_in_working_days_weekend(settings, pub): settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France' t = Template('{{ value|age_in_working_days:"2020-06-22" }}') assert t.render({'value': '2020-06-19'}) == '1' t = Template('{{ value|age_in_working_days_with_saturday:"2020-06-22" }}') assert t.render({'value': '2020-06-19'}) == '2' def test_sum(): tmpl = Template('{{ "2 29.5 9,5 .5"|split|sum }}') assert tmpl.render({}) == '41.5' tmpl = Template('{{ list|sum }}') assert tmpl.render({'list': [1, 2, '3']}) == '6' assert tmpl.render({'list': [1, 2, 'x']}) == '3' assert tmpl.render({'list': [None, 2.0, 'x']}) == '2' assert tmpl.render({'list': []}) == '0' assert tmpl.render({'list': None}) == '' # list is not iterable assert tmpl.render({'list': '123'}) == '' # consider string as not iterable assert tmpl.render({}) == '' def test_getlist(): class FakeBlock: def getlist(self, key): return {'foo': ['foo1', 'foo2'], 'bar': ['bar1', 'bar2']}[key] tmpl = Template('{% for x in egg|getlist:coin %}{{x}}{% endfor %}') assert tmpl.render({'egg': FakeBlock(), 'coin': 'foo'}) == 'foo1foo2' assert tmpl.render({'egg': FakeBlock(), 'coin': 'bar'}) == 'bar1bar2' tmpl = Template('{{ egg|getlist:"foo"|length }}') assert tmpl.render({'egg': FakeBlock()}) == '2' assert tmpl.render({}) == '0' assert tmpl.render({'egg': None}) == '0' assert tmpl.render({'egg': 'spam'}) == '0' assert tmpl.render({'egg': 42}) == '0' def test_getlistdict(): class FakeBlock: def getlistdict(self, keys): data = [ {'foo': 'foo1', 'bar': 'bar1'}, {'foo': 'foo2', 'bar': 'bar2'}, ] return [{k: v for k, v in d.items() if k in keys} for d in data] tmpl = Template('{{ egg|getlistdict:coin }}', autoescape=False) assert tmpl.render({'egg': FakeBlock(), 'coin': 'foo'}) == "[{'foo': 'foo1'}, {'foo': 'foo2'}]" assert ( tmpl.render({'egg': FakeBlock(), 'coin': 'bar : test, foo:hop, ,,'}) == "[{'hop': 'foo1', 'test': 'bar1'}, {'hop': 'foo2', 'test': 'bar2'}]" ) tmpl = Template('{{ egg|getlistdict:"foo"|length }}') assert tmpl.render({'egg': FakeBlock()}) == '2' assert tmpl.render({}) == '0' assert tmpl.render({'egg': None}) == '0' assert tmpl.render({'egg': 'spam'}) == '0' assert tmpl.render({'egg': 42}) == '0' def test_django_contrib_humanize_filters(): tmpl = Template('{{ foo|intcomma }}') assert tmpl.render({'foo': 10000}) == '10,000' assert tmpl.render({'foo': '10000'}) == '10,000' with override_settings(LANGUAGE_CODE='fr-fr'): assert tmpl.render({'foo': 10000}) == '10 000' assert tmpl.render({'foo': '10000'}) == '10 000' def test_is_empty(): tmpl = Template('{{ foo|is_empty }}') assert tmpl.render({}) == 'True' assert tmpl.render({'foo': ''}) == 'True' assert tmpl.render({'foo': None}) == 'True' assert tmpl.render({'foo': 'foo'}) == 'False' assert tmpl.render({'foo': 42}) == 'False' assert tmpl.render({'foo': []}) == 'True' assert tmpl.render({'foo': ['foo']}) == 'False' assert tmpl.render({'foo': {}}) == 'True' assert tmpl.render({'foo': {'foo': 42}}) == 'False' def test_first(): tmpl = Template('{{ foo|first }}') assert tmpl.render({'foo': ['foo']}) == 'foo' assert tmpl.render({'foo': 'foo'}) == 'f' assert tmpl.render({'foo': ''}) == '' assert tmpl.render({'foo': None}) == '' def test_last(): tmpl = Template('{{ foo|last }}') assert tmpl.render({'foo': ['foo']}) == 'foo' assert tmpl.render({'foo': 'foo'}) == 'o' assert tmpl.render({'foo': ''}) == '' assert tmpl.render({'foo': None}) == '' def test_convert_as_list(): tmpl = Template('{{ foo|list|first }}') assert tmpl.render({'foo': ['foo']}) == 'foo' def list_generator(): yield from range(5) assert tmpl.render({'foo': list_generator}) == '0' def list_range(): return range(5) assert tmpl.render({'foo': list_range}) == '0' def test_adjust_to_week_monday(pub): t = Template('{{ value|adjust_to_week_monday }}') assert t.render({'value': '2021-06-13'}) == '2021-06-07' t = Template('{{ value|adjust_to_week_monday }}') assert t.render({'value': '2021-06-14'}) == '2021-06-14' t = Template('{{ value|adjust_to_week_monday }}') assert t.render({'value': datetime.datetime(2021, 6, 14, 0, 0)}) == '2021-06-14' def test_iterate_days_until(pub): t = Template( '{% for day in value|iterate_days_until:value2 %}{{ day }}{% if not forloop.last %}, {% endif %}{% endfor %}' ) assert ( t.render({'value': '2021-06-13', 'value2': '2021-06-16'}) == '2021-06-13, 2021-06-14, 2021-06-15, 2021-06-16' ) assert t.render({'value': 'error1', 'value2': 'error2'}) == '' def test_qrcode(pub): with pub.complex_data(): img = Template('{{ url|qrcode }}').render({'url': 'http://example.com/', 'allow_complex': True}) assert pub.has_cached_complex_data(img) value = pub.get_cached_complex_data(img) assert value.orig_filename == 'qrcode.png' assert value.content_type == 'image/png' with value.get_file_pointer() as fp: img = PIL.Image.open(fp) assert img.size == (330, 330) assert ( zbar_decode_qrcode(img, symbols=[ZBarSymbol.QRCODE])[0].data.decode() == 'http://example.com/' ) img = Template('{{ url|qrcode:"qrcode2.png" }}').render( {'url': 'http://example.com/', 'allow_complex': True} ) value = pub.get_cached_complex_data(img) assert value.orig_filename == 'qrcode2.png' img = Template('{{ url|qrcode }}').render({'url': 1, 'allow_complex': True}) assert img == '' def test_site_options_booleans(pub): if not pub.site_options.has_section('variables'): pub.site_options.add_section('variables') pub.site_options.set('variables', 'something_true', 'true') pub.site_options.set('variables', 'something_false', 'false') with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd: pub.site_options.write(fd) pub.load_site_options() context = pub.substitutions.get_context_variables() assert Template('{{ something_true }}').render(context) == 'true' assert Template('{% if something_true %}hello{% endif %}').render(context) == 'hello' assert Template('{% if something_true == "true" %}hello{% endif %}').render(context) == 'hello' assert Template('{% if something_true == True %}hello{% endif %}').render(context) == 'hello' assert Template('{% if something_true == "false" %}hello{% endif %}').render(context) == '' assert Template('{% if something_true == False %}hello{% endif %}').render(context) == '' assert Template('{{ something_false }}').render(context) == 'false' assert Template('{% if something_false %}hello{% endif %}').render(context) == '' assert Template('{% if something_false == False %}hello{% endif %}').render(context) == 'hello' assert Template('{% if something_false == "false" %}hello{% endif %}').render(context) == 'hello' assert Template('{% if something_false == True %}hello{% endif %}').render(context) == '' assert Template('{% if something_false == "true" %}hello{% endif %}').render(context) == '' def test_newline(pub): context = pub.substitutions.get_context_variables() assert Template('a{% newline %}b').render(context) == 'a\nb' assert Template('a{% newline windows=True %}b').render(context) == 'a\r\nb' def test_duration(pub): pub.ngettext = translation.ngettext context = {'value': 80} assert Template('{{ value|duration }}').render(context) == '1h20' assert Template('{{ value|duration:"long" }}').render(context) == '1 hour and 20 minutes' context = {'value': 40} assert Template('{{ value|duration }}').render(context) == '40min' assert Template('{{ value|duration:"long" }}').render(context) == '40 minutes' context = {'value': 120} assert Template('{{ value|duration }}').render(context) == '2h' assert Template('{{ value|duration:"long" }}').render(context) == '2 hours' context = {'value': 1510} assert Template('{{ value|duration }}').render(context) == '1 day and 1h10' assert Template('{{ value|duration:"long" }}').render(context) == '1 day, 1 hour and 10 minutes' context = {'value': 61} assert Template('{{ value|duration }}').render(context) == '1h01' context = {'value': 'xx'} assert Template('{{ value|duration }}').render(context) == '' assert Template('{{ value|duration:"long" }}').render(context) == '' def test_null_true_false(pub): for mode in (None, 'lazy'): context = pub.substitutions.get_context_variables(mode=mode) tmpl = Template('{{ null }}') assert tmpl.render(context) == 'None' assert tmpl.render({'null': 'bar'}) == 'bar' tmpl = Template('{% if foo is null %}foo is None{% endif %}') assert tmpl.render(context) == 'foo is None' assert tmpl.render({'foo': None}) == 'foo is None' assert tmpl.render({'foo': 42}) == '' tmpl = Template('{{ true }} {{ false }}') assert tmpl.render(context) == 'True False' tmpl = Template('{% if true %}OK{% endif %} {% if not false %}OK{% endif %}') assert tmpl.render(context) == 'OK OK' context['allow_complex'] = True with pub.complex_data(): img = Template('{{ null }}').render(context) assert pub.has_cached_complex_data(img) value = pub.get_cached_complex_data(img) assert value is None img = Template('{{ true }}').render(context) assert pub.has_cached_complex_data(img) value = pub.get_cached_complex_data(img) assert value is True img = Template('{{ false }}').render(context) assert pub.has_cached_complex_data(img) value = pub.get_cached_complex_data(img) assert value is False def test_as_template(pub): FormDef.wipe() formdef = FormDef() formdef.name = 'foobar' formdef.fields = [fields.StringField(id='1', label='Test', type='string', varname='foo')] formdef.store() formdata = formdef.data_class()() formdata.data = {'1': 'Foo Bar'} formdata.store() pub.substitutions.feed(formdata) context = {'template': 'Hello {{ form_var_foo }}'} assert Template('{{ template }}').render(context) == 'Hello {{ form_var_foo }}' assert Template('{{ template|as_template }}').render(context) == 'Hello Foo Bar'