diff --git a/src/os2datascanner/engine2/rules/experimental/__init__.py b/src/os2datascanner/engine2/rules/experimental/__init__.py index e14e757c5..ce7a14669 100644 --- a/src/os2datascanner/engine2/rules/experimental/__init__.py +++ b/src/os2datascanner/engine2/rules/experimental/__init__.py @@ -1,2 +1,3 @@ from . import cpr # noqa from . import health_rule # noqa +from . import credit_card # noqa \ No newline at end of file diff --git a/src/os2datascanner/engine2/rules/experimental/credit_card.py b/src/os2datascanner/engine2/rules/experimental/credit_card.py new file mode 100644 index 000000000..add4c5d7b --- /dev/null +++ b/src/os2datascanner/engine2/rules/experimental/credit_card.py @@ -0,0 +1,37 @@ +import re +from ..rule import SimpleRule +from ...conversions.types import OutputType + +def luhn_algorithm(num: str): + """Computes the Luhn check digit for a given string of digits. (The last + digit of virtually all credit and debit card numbers is a Luhn check + digit.)""" + double = (len(num) % 2 == 1) + tot = 0 + for ch in num: + v = int(ch) * (2 if double else 1) + tot += sum(int(c) for c in str(v)) + double = not double + return 10 - (tot % 10) + + +class CreditCardRule(SimpleRule): + operates_on = OutputType.Text + + def __init__(self): + self._expr = re.compile( + r"[0-9]{4}([- ]?[0-9]{4}){3}") + + def match(self, representation: str): + for mo in self._expr.finditer(representation): + # Canonicalise the joiners away + num = "".join(ch for ch in mo.group() if ch.isdigit()) + + # See if the check digit is what we expect + if str(luhn_algorithm(num[:-1])) == num[-1]: + yield { + "match": num + } + + def to_json_object(self): + return super().to_json_object() diff --git a/src/os2datascanner/projects/admin/adminapp/templates/miniscan.html b/src/os2datascanner/projects/admin/adminapp/templates/miniscan.html index 36c91fbde..53d7f6227 100644 --- a/src/os2datascanner/projects/admin/adminapp/templates/miniscan.html +++ b/src/os2datascanner/projects/admin/adminapp/templates/miniscan.html @@ -7,9 +7,10 @@ {% block scripts %} {{ block.super }} - - - + + + + {% endblock %} {% block body %} @@ -58,6 +59,7 @@

{% trans "File to scan" %}

type="button" onclick="clearFile()"> {% trans "Clear file" %} +