From b927e1eaaa70c5bc0ede1a1736eafb37c2831fe1 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Tue, 23 Jul 2019 17:40:14 +0200 Subject: [PATCH] template filter for Luhn algorithm (#35013) --- tests/test_templates.py | 19 +++++++++++++++++++ wcs/qommon/templatetags/qommon.py | 30 ++++++++++++++++++++++++++---- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/tests/test_templates.py b/tests/test_templates.py index 18f0ea65..31dc78b6 100644 --- a/tests/test_templates.py +++ b/tests/test_templates.py @@ -522,3 +522,22 @@ def test_reproj(): lazy_formdata = LazyFormData(MockFormData()) tmpl = Template('{% with form_geoloc_base|reproj:"EPSG:3946" as c %}{{c.0}}/{{c.1}}{% endwith %}') assert tmpl.render(CompatibilityNamesDict({'form': lazy_formdata})) == '1625337.15483/5422836.71627' + +def test_luhn(): + # EO Siret + tmpl = Template('{% if "44317013900036"|luhn %}yes{% endif %}') + assert tmpl.render() == 'yes' + for i in range(10): + if i == 6: + continue + tmpl = Template('{% if "4431701390003' + str(i) + '"|luhn %}yes{% endif %}') + assert tmpl.render() == '' + + # EO Siren + tmpl = Template('{% if "443170139"|luhn %}yes{% endif %}') + assert tmpl.render() == 'yes' + for i in range(10): + if i == 9: + continue + tmpl = Template('{% if "44317013' + str(i) + '"|luhn %}yes{% endif %}') + assert tmpl.render() == '' diff --git a/wcs/qommon/templatetags/qommon.py b/wcs/qommon/templatetags/qommon.py index 5178392d..d7e5f92a 100644 --- a/wcs/qommon/templatetags/qommon.py +++ b/wcs/qommon/templatetags/qommon.py @@ -20,6 +20,7 @@ from decimal import InvalidOperation as DecimalInvalidOperation from decimal import DivisionByZero as DecimalDivisionByZero import hashlib import math +import re import string import random @@ -28,7 +29,7 @@ from pyproj import Geod from django import template from django.template import defaultfilters -from django.utils import dateparse +from django.utils import dateparse, six from django.utils.safestring import mark_safe from wcs.qommon import evalutils from wcs.qommon import tokens @@ -44,13 +45,13 @@ def get(mapping, key): @register.filter def startswith(string, substring): - return string and unicode(string).startswith(unicode(substring)) + return string and six.text_type(string).startswith(six.text_type(substring)) @register.filter def split(string, separator=' '): if not string: return [] - return unicode(string).split(unicode(separator)) + return six.text_type(string).split(six.text_type(separator)) @register.filter def parse_date(date_string): @@ -306,7 +307,7 @@ def token_alphanum(length=4): @register.filter def token_check(token1, token2): - return unicode(token1).strip().upper() == unicode(token2).strip().upper() + return six.text_type(token1).strip().upper() == six.text_type(token2).strip().upper() def get_latlon(obj): @@ -348,3 +349,24 @@ def reproj(coords, projection_name): proj = pyproj.Proj(init='EPSG:4326') target_proj = pyproj.Proj(init=projection_name) return pyproj.transform(proj, target_proj, coords['lon'], coords['lat']) + +@register.filter +def luhn(value): + '''Verify Luhn checksum on a string representing a number''' + if not value: + return False + string_value = six.text_type(value) + string_value = string_value.strip() + string_value = re.sub(r'\s', '', string_value) + if not string_value.isdigit(): + return False + + # take all digits couting from the right, double value for digits pair + # index (counting from 1), if double has 2 digits take their sum + checksum = 0 + for i, x in enumerate(reversed(string_value)): + if i % 2 == 0: + checksum += int(x) + else: + checksum += sum(int(y) for y in str(2 * int(x))) + return checksum % 10 == 0 -- 2.22.0