Repository: identique/idnumbers (language: python)

Issue:
id number for Hong_Kong
https://en.wikipedia.org/wiki/National_identification_number#Hong_Kong


Discussion / hints:

Fix patch:
diff --git a/idnumbers/nationalid/HKG.py b/idnumbers/nationalid/HKG.py
new file mode 100644
index 00000000..29cbb8a7
--- /dev/null
+++ b/idnumbers/nationalid/HKG.py
@@ -0,0 +1,53 @@
+import re
+from types import SimpleNamespace
+
+from .util import validate_regexp
+
+
+class NationalID:
+    """
+    Hong Kong national ID number format
+    https://en.wikipedia.org/wiki/National_identification_number#Hong_Kong
+    https://pinkylam.me/playground/hkid/
+    """
+    METADATA = SimpleNamespace(**{
+        'iso3166_alpha2': 'HK',
+        # length without insignificant chars
+        'min_length': 8,
+        'max_length': 9,
+        # has parse function
+        'parsable': False,
+        # has checksum function
+        'checksum': True,
+        # regular expression to validate the id
+        'regexp': re.compile(r"^[A-Z]{1,2}[0-9]{6}[0-9A]$")
+    })
+
+    @staticmethod
+    def validate(id_number: str) -> bool:
+        """
+        Validate HKG id number
+        """
+        if not validate_regexp(id_number, NationalID.METADATA.regexp):
+            return False
+        return NationalID.checksum(id_number)
+
+    @staticmethod
+    def checksum(id_number: str) -> bool:
+        """
+        https://github.com/hsyuen720/hkid-tools/blob/main/app/utils/validate.ts
+        """
+        arr = list(id_number[:-1])
+        MULTIPLIERS = [len(arr) + 1 - index for (index, _) in enumerate(arr)]
+        total = 0 if len(arr) % 2 == 0 else 36 * 9
+        total += sum([NationalID.get_number(arr[idx]) * mul for (idx, mul) in enumerate(MULTIPLIERS)])
+        rem = total % 11
+        check_digit: str = "A" if rem == 1 else '0' if rem == 0 else str(11 - rem)
+        return check_digit == id_number[-1]
+
+    @staticmethod
+    def get_number(digit: str) -> int:
+        if re.match('[A-Z]', digit):
+            return int(ord(digit)) - 55
+        else:
+            return int(digit)


Test patch:
diff --git a/idnumbers/nationalid/test_HKG.py b/idnumbers/nationalid/test_HKG.py
new file mode 100644
index 00000000..1ea9316a
--- /dev/null
+++ b/idnumbers/nationalid/test_HKG.py
@@ -0,0 +1,26 @@
+from unittest import TestCase, main
+from idnumbers.nationalid import HKG
+
+
+class TestHKGValidation(TestCase):
+    def test_normal_case(self):
+        self.assertTrue(HKG.NationalID.validate('TP1490258'))
+        self.assertTrue(HKG.NationalID.validate('D360546A'))
+
+    def test_error_case(self):
+        self.assertFalse(HKG.NationalID.validate('LL2557369A'))
+        self.assertFalse(HKG.NationalID.validate('TP1490253'))
+
+    def test_with_regex(self):
+        self.assertRegex('TP1490258', HKG.NationalID.METADATA.regexp)
+
+    def test_with_metadata(self):
+        self.assertIsNotNone(HKG.NationalID.METADATA)
+
+    def test_checksum(self):
+        self.assertTrue(HKG.NationalID.checksum("D360546A"))
+        self.assertFalse(HKG.NationalID.checksum("TP1490253"))
+
+
+if __name__ == '__main__':
+    main()