From ceb04581c79f96dc96095b4cb8f6bc541f0af79c Mon Sep 17 00:00:00 2001
From: Thomas NOEL <tnoel@entrouvert.com>
Date: Fri, 7 Dec 2018 10:39:29 +0100
Subject: [PATCH] add new templatetags logic for date, datetime, time, decimal
 (#28738)

---
 tests/test_templates.py           | 101 ++++++++++++++++++++++++++++++
 wcs/qommon/templatetags/qommon.py |  54 +++++++++++++---
 2 files changed, 145 insertions(+), 10 deletions(-)

diff --git a/tests/test_templates.py b/tests/test_templates.py
index 7c66000d..864b4e4b 100644
--- a/tests/test_templates.py
+++ b/tests/test_templates.py
@@ -110,6 +110,94 @@ def test_template_encoding():
 
 
 def test_datetime_templatetags():
+    tmpl = Template('{{ plop|datetime }}')
+    assert tmpl.render({'plop': '2017-12-21 10:32'}) == 'Dec. 21, 2017, 10:32 a.m.'
+    assert tmpl.render({'plop': '21/12/2017 10h32'}) == 'Dec. 21, 2017, 10:32 a.m.'
+    assert tmpl.render({'plop': '2017-12-21'}) == 'Dec. 21, 2017, midnight'
+    assert tmpl.render({'plop': '21/12/2017'}) == 'Dec. 21, 2017, midnight'
+    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'}) == 'Dec. 21, 2017'
+    assert tmpl.render({'plop': '21/12/2017'}) == 'Dec. 21, 2017'
+    assert tmpl.render({'plop': '2017-12-21 10:32'}) == 'Dec. 21, 2017'
+    assert tmpl.render({'plop': '21/12/2017 10:32'}) == 'Dec. 21, 2017'
+    assert tmpl.render({'plop': '21/12/2017 10h32'}) == 'Dec. 21, 2017'
+    assert tmpl.render({'plop': '21/12/2017 10:32:42'}) == 'Dec. 21, 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('{% 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'
@@ -152,7 +240,20 @@ def test_decimal_templatetag():
     assert tmpl.render({'plop': 12345.678}) == '12345.678'
     assert tmpl.render({'plop': None}) == ''
 
+    tmpl = Template('{{ plop|decimal:3 }}')
+    assert tmpl.render({'plop': '3.14'}) == '3.140'
+    assert tmpl.render({'plop': None}) == ''
+    tmpl = Template('{{ plop|decimal:"3" }}')
+    assert tmpl.render({'plop': '3.14'}) == '3.140'
+    assert tmpl.render({'plop': None}) == ''
+
     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'}) == 'hello'
     assert tmpl.render({'plop': None}) == 'hello'  # will be str > int, always True.
diff --git a/wcs/qommon/templatetags/qommon.py b/wcs/qommon/templatetags/qommon.py
index 9ad0de7b..ff160d0c 100644
--- a/wcs/qommon/templatetags/qommon.py
+++ b/wcs/qommon/templatetags/qommon.py
@@ -51,6 +51,14 @@ def parse_date(date_string):
     except (ValueError, TypeError):
         return None
 
+@register.filter(expects_localtime=True, is_safe=False)
+def date(value, arg=None):
+    if arg is None:
+        return parse_date(value) or ''
+    if not isinstance(value, (datetime.datetime, datetime.date, datetime.time)):
+        value = parse_datetime(value) or parse_date(value)
+    return defaultfilters.date(value, arg=arg)
+
 @register.filter
 def parse_datetime(datetime_string):
     try:
@@ -63,30 +71,56 @@ def parse_datetime(datetime_string):
     except (ValueError, TypeError):
         return None
 
+@register.filter(name='datetime', expects_localtime=True, is_safe=False)
+def datetime_(value, arg=None):
+    if arg is None:
+        return parse_datetime(value) or ''
+    if not isinstance(value, (datetime.datetime, datetime.date, datetime.time)):
+        value = parse_datetime(value)
+    return defaultfilters.date(value, arg=arg)
+
 @register.filter
 def parse_time(time_string):
+    # if input is a datetime, extract its time
+    try:
+        dt = parse_datetime(time_string)
+        if dt:
+            return dt.time()
+    except (ValueError, TypeError):
+        pass
+    # fallback to Django function
     try:
         return dateparse.parse_time(time_string)
     except (ValueError, TypeError):
         return None
 
 @register.filter(expects_localtime=True, is_safe=False)
-def date(value, arg=None):
+def time(value, arg=None):
+    if arg is None:
+        parsed = parse_time(value)
+        return parsed if parsed is not None else ''  # because bool(midnigth) == False
     if not isinstance(value, (datetime.datetime, datetime.date, datetime.time)):
-        value = parse_date(value)
+        value = parse_time(value)
     return defaultfilters.date(value, arg=arg)
 
-@register.filter(is_safe=True)
-def decimal(value):
+def parse_decimal(value):
     if not value:
-        return ''
+        return None
+    if isinstance(value, basestring):
+        # replace , by . for French users comfort
+        value = value.replace(',', '.')
     try:
-        if isinstance(value, basestring):
-            # replace , by . for French users comfort
-            value = value.replace(',', '.')
         return Decimal(value).quantize(Decimal('1.0000')).normalize()
-    except ArithmeticError:
-        return ''
+    except (ArithmeticError, TypeError):
+        return None
+
+@register.filter(is_safe=True)
+def decimal(value, arg=None):
+    if arg is None:
+        return parse_decimal(value) or ''
+    if not isinstance(value, Decimal):
+        value = parse_decimal(value)
+    return defaultfilters.floatformat(value, arg=arg)
 
 @register.simple_tag
 def standard_text(text_id):
-- 
2.19.2

