templates: add filters for working days (#31851)
This commit is contained in:
parent
cedb3d2cf0
commit
2f030fd345
|
@ -33,6 +33,7 @@ Recommends: libreoffice-writer-nogui | libreoffice-writer,
|
|||
python3-langdetect,
|
||||
python3-magic,
|
||||
python3-qrcode,
|
||||
python3-workalendar,
|
||||
python3-xlwt
|
||||
Suggests: python3-libxml2
|
||||
Description: web application to design and set up online forms
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import pytest
|
||||
import string
|
||||
|
||||
|
@ -11,7 +12,8 @@ except ImportError:
|
|||
|
||||
from django.test import override_settings
|
||||
from django.utils.timezone import now
|
||||
from quixote import cleanup
|
||||
|
||||
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
|
||||
|
@ -21,11 +23,15 @@ from wcs import fields
|
|||
from utilities import create_temporary_pub, clean_temporary_pub
|
||||
|
||||
|
||||
def setup_module(module):
|
||||
cleanup()
|
||||
global 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', '')
|
||||
pub.site_options.write(open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w'))
|
||||
return pub
|
||||
|
||||
|
||||
def teardown_module(module):
|
||||
|
@ -73,7 +79,7 @@ def test_template():
|
|||
assert tmpl.render({'foo': 'bar'}) == '[if-any foo][foo][endif]'
|
||||
|
||||
|
||||
def test_now_and_today_variables():
|
||||
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
|
||||
|
@ -689,3 +695,239 @@ def test_language_detect():
|
|||
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')
|
||||
pub.site_options.write(open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w'))
|
||||
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')
|
||||
pub.site_options.write(open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w'))
|
||||
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, 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):
|
||||
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')
|
||||
pub.site_options.write(open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w'))
|
||||
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')
|
||||
pub.site_options.write(open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w'))
|
||||
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):
|
||||
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, 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):
|
||||
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')
|
||||
pub.site_options.write(open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w'))
|
||||
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')
|
||||
pub.site_options.write(open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w'))
|
||||
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, 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):
|
||||
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'
|
||||
|
|
1
tox.ini
1
tox.ini
|
@ -30,6 +30,7 @@ deps =
|
|||
vobject
|
||||
qrcode
|
||||
Pillow<7.2
|
||||
workalendar
|
||||
python-magic
|
||||
docutils
|
||||
langdetect
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
# w.c.s. - web application for online forms
|
||||
# Copyright (C) 2005-2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.utils.module_loading import import_string
|
||||
from quixote import get_publisher
|
||||
|
||||
try:
|
||||
from workalendar.core import SUN
|
||||
except ImportError:
|
||||
SUN = None
|
||||
|
||||
|
||||
def get_calendar(saturday_is_a_working_day=False):
|
||||
# get calendar from settings
|
||||
try:
|
||||
calendar_class = import_string(get_publisher().get_working_day_calendar())
|
||||
except (AttributeError, ImportError):
|
||||
return
|
||||
|
||||
# saturday is not a working day, return this calendar
|
||||
if not saturday_is_a_working_day:
|
||||
return calendar_class()
|
||||
|
||||
# saturday is a working day, build a custom calendar
|
||||
class CalendarWithSaturday(calendar_class):
|
||||
WEEKEND_DAYS = (SUN,)
|
||||
|
||||
return CalendarWithSaturday()
|
|
@ -892,6 +892,9 @@ class QommonPublisher(Publisher, object):
|
|||
url += '?key=%s' % key
|
||||
return url
|
||||
|
||||
def get_working_day_calendar(self):
|
||||
return self.get_site_option('working_day_calendar') or settings.WORKING_DAY_CALENDAR
|
||||
|
||||
def get_supported_authentication_contexts(self):
|
||||
contexts = collections.OrderedDict()
|
||||
labels = {
|
||||
|
|
|
@ -39,6 +39,7 @@ from django.utils import six
|
|||
from django.utils.encoding import force_bytes, force_text
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.timezone import is_naive, make_aware
|
||||
from wcs.qommon import calendar
|
||||
from wcs.qommon import evalutils
|
||||
from wcs.qommon import tokens
|
||||
from wcs.qommon.admin.texts import TextsDirectory
|
||||
|
@ -281,6 +282,66 @@ def datetime_in_past(value):
|
|||
return value <= date_now
|
||||
|
||||
|
||||
@register.filter(expects_localtime=True)
|
||||
def is_working_day(value, saturday_is_a_working_day=False):
|
||||
value = parse_date(value)
|
||||
if not value:
|
||||
return False
|
||||
|
||||
cal = calendar.get_calendar(saturday_is_a_working_day=saturday_is_a_working_day)
|
||||
if not cal:
|
||||
return False
|
||||
|
||||
return cal.is_working_day(value)
|
||||
|
||||
|
||||
@register.filter(expects_localtime=True)
|
||||
def is_working_day_with_saturday(value):
|
||||
return is_working_day(value, saturday_is_a_working_day=True)
|
||||
|
||||
|
||||
@register.filter(expects_localtime=True)
|
||||
def add_working_days(value, arg, saturday_is_a_working_day=False):
|
||||
value = parse_date(value)
|
||||
if not value:
|
||||
return ''
|
||||
|
||||
cal = calendar.get_calendar(saturday_is_a_working_day=saturday_is_a_working_day)
|
||||
if not cal:
|
||||
return ''
|
||||
|
||||
try:
|
||||
return cal.add_working_days(value, int(arg))
|
||||
except ValueError:
|
||||
return ''
|
||||
|
||||
|
||||
@register.filter(expects_localtime=True)
|
||||
def add_working_days_with_saturday(value, arg):
|
||||
return add_working_days(value, arg, saturday_is_a_working_day=True)
|
||||
|
||||
|
||||
@register.filter(expects_localtime=True)
|
||||
def adjust_to_working_day(value, saturday_is_a_working_day=False):
|
||||
value = parse_date(value)
|
||||
if not value:
|
||||
return ''
|
||||
|
||||
cal = calendar.get_calendar(saturday_is_a_working_day=saturday_is_a_working_day)
|
||||
if not cal:
|
||||
return ''
|
||||
|
||||
if cal.is_working_day(value):
|
||||
return value
|
||||
# return next working day
|
||||
return cal.add_working_days(value, 1)
|
||||
|
||||
|
||||
@register.filter(expects_localtime=True)
|
||||
def adjust_to_working_day_with_saturday(value):
|
||||
return adjust_to_working_day(value, saturday_is_a_working_day=True)
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def standard_text(text_id):
|
||||
return mark_safe(TextsDirectory.get_html_text(str(text_id)))
|
||||
|
|
|
@ -188,6 +188,9 @@ DISABLE_CRON_JOBS = False
|
|||
# w.c.s. can have very large forms, in backoffice and frontoffice
|
||||
DATA_UPLOAD_MAX_NUMBER_FIELDS = 2000 # Django default is 1000
|
||||
|
||||
# workalendar config
|
||||
WORKING_DAY_CALENDAR = 'workalendar.europe.France'
|
||||
|
||||
local_settings_file = os.environ.get('WCS_SETTINGS_FILE',
|
||||
os.path.join(os.path.dirname(__file__), 'local_settings.py'))
|
||||
if os.path.exists(local_settings_file):
|
||||
|
|
Loading…
Reference in New Issue