From 4130d250afc49f1671406595a66b58cf401fbf73 Mon Sep 17 00:00:00 2001 From: Carlos Villar Date: Sun, 23 Oct 2022 19:37:41 +0200 Subject: [PATCH 1/4] Add Spain National ID validator (#7574) --- strings/spain_national_id_validator.py | 75 ++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 strings/spain_national_id_validator.py diff --git a/strings/spain_national_id_validator.py b/strings/spain_national_id_validator.py new file mode 100644 index 000000000000..8a2fa02117af --- /dev/null +++ b/strings/spain_national_id_validator.py @@ -0,0 +1,75 @@ +NUMBERS_PLUS_LETTER = "Input must be a string of 8 numbers plus letter" +LOOKUP_LETTERS = "TRWAGMYFPDXBNJZSQVHLCKE" + + +def validate(spanish_id: str) -> bool: + """ + Spain National Id is a string composed by 8 numbers plus a letter + The letter in fact is not part of the ID, it acts as a validator, + checking you didn't do a mistake when entering it on a system or + are giving a fake one. + + https://en.wikipedia.org/wiki/Documento_Nacional_de_Identidad_(Spain)#Number + + >>> validate("12345678Z") + True + >>> validate("12345678z") # It is case-insensitive + True + >>> validate("12345678x") + False + >>> validate("12345678I") + False + >>> validate("12345678-Z") # Some systems add a dash between number and letter + True + >>> validate("12345678") + Traceback (most recent call last): + ... + ValueError: Input must be a string of 8 numbers plus letter + >>> validate("123456709") + Traceback (most recent call last): + ... + ValueError: Input must be a string of 8 numbers plus letter + >>> validate("1234567--Z") + Traceback (most recent call last): + ... + ValueError: Input must be a string of 8 numbers plus letter + >>> validate("1234Z") + Traceback (most recent call last): + ... + ValueError: Input must be a string of 8 numbers plus letter + >>> validate("1234ZzZZ") + Traceback (most recent call last): + ... + ValueError: Input must be a string of 8 numbers plus letter + >>> validate(12345678) + Traceback (most recent call last): + ... + ValueError: Expected string as input, found int + """ + + if not isinstance(spanish_id, str): + raise ValueError(f"Expected string as input, found {type(spanish_id).__name__}") + + spanish_id_clean = spanish_id.replace("-", "").upper() + if len(spanish_id_clean) != 9: + raise ValueError(NUMBERS_PLUS_LETTER) + + try: + number = int(spanish_id_clean[0:8]) + letter = spanish_id_clean[8] + except ValueError as ex: + raise ValueError(NUMBERS_PLUS_LETTER) from ex + + if letter in "0123456789": + raise ValueError(NUMBERS_PLUS_LETTER) + + modulus = number % 23 + expected_letter = LOOKUP_LETTERS[modulus] + + return letter == expected_letter + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 14f99defdd508785a3004b34340e810b65236744 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Mon, 24 Oct 2022 13:13:01 +0200 Subject: [PATCH 2/4] is_spain_national_id() --- ...d_validator.py => is_spain_national_id.py} | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) rename strings/{spain_national_id_validator.py => is_spain_national_id.py} (66%) diff --git a/strings/spain_national_id_validator.py b/strings/is_spain_national_id.py similarity index 66% rename from strings/spain_national_id_validator.py rename to strings/is_spain_national_id.py index 8a2fa02117af..0e2d8a7b7e5c 100644 --- a/strings/spain_national_id_validator.py +++ b/strings/is_spain_national_id.py @@ -2,7 +2,7 @@ LOOKUP_LETTERS = "TRWAGMYFPDXBNJZSQVHLCKE" -def validate(spanish_id: str) -> bool: +def is_spain_national_id(spanish_id: str) -> bool: """ Spain National Id is a string composed by 8 numbers plus a letter The letter in fact is not part of the ID, it acts as a validator, @@ -11,44 +11,44 @@ def validate(spanish_id: str) -> bool: https://en.wikipedia.org/wiki/Documento_Nacional_de_Identidad_(Spain)#Number - >>> validate("12345678Z") + >>> is_spain_national_id("12345678Z") True - >>> validate("12345678z") # It is case-insensitive + >>> is_spain_national_id("12345678z") # It is case-insensitive True - >>> validate("12345678x") + >>> is_spain_national_id("12345678x") False - >>> validate("12345678I") + >>> is_spain_national_id("12345678I") False - >>> validate("12345678-Z") # Some systems add a dash between number and letter + >>> is_spain_national_id("12345678-Z") # Some systems add a dash between number and letter True - >>> validate("12345678") + >>> is_spain_national_id("12345678") Traceback (most recent call last): ... ValueError: Input must be a string of 8 numbers plus letter - >>> validate("123456709") + >>> is_spain_national_id("123456709") Traceback (most recent call last): ... ValueError: Input must be a string of 8 numbers plus letter - >>> validate("1234567--Z") + >>> is_spain_national_id("1234567--Z") Traceback (most recent call last): ... ValueError: Input must be a string of 8 numbers plus letter - >>> validate("1234Z") + >>> is_spain_national_id("1234Z") Traceback (most recent call last): ... ValueError: Input must be a string of 8 numbers plus letter - >>> validate("1234ZzZZ") + >>> is_spain_national_id("1234ZzZZ") Traceback (most recent call last): ... ValueError: Input must be a string of 8 numbers plus letter - >>> validate(12345678) + >>> is_spain_national_id(12345678) Traceback (most recent call last): ... - ValueError: Expected string as input, found int + TypeError: Expected string as input, found int """ if not isinstance(spanish_id, str): - raise ValueError(f"Expected string as input, found {type(spanish_id).__name__}") + raise TypeError(f"Expected string as input, found {type(spanish_id).__name__}") spanish_id_clean = spanish_id.replace("-", "").upper() if len(spanish_id_clean) != 9: @@ -60,13 +60,10 @@ def validate(spanish_id: str) -> bool: except ValueError as ex: raise ValueError(NUMBERS_PLUS_LETTER) from ex - if letter in "0123456789": + if letter.isdigit(): raise ValueError(NUMBERS_PLUS_LETTER) - modulus = number % 23 - expected_letter = LOOKUP_LETTERS[modulus] - - return letter == expected_letter + return letter == LOOKUP_LETTERS[number % 23] if __name__ == "__main__": From a98aaf59534896deb7e507b19175ca0c21240686 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Mon, 24 Oct 2022 13:25:58 +0200 Subject: [PATCH 3/4] Update is_spain_national_id.py --- strings/is_spain_national_id.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/strings/is_spain_national_id.py b/strings/is_spain_national_id.py index 0e2d8a7b7e5c..f39adb77e3f6 100644 --- a/strings/is_spain_national_id.py +++ b/strings/is_spain_national_id.py @@ -19,7 +19,7 @@ def is_spain_national_id(spanish_id: str) -> bool: False >>> is_spain_national_id("12345678I") False - >>> is_spain_national_id("12345678-Z") # Some systems add a dash between number and letter + >>> is_spain_national_id("12345678-Z") # Some systems add a dash between number & letter True >>> is_spain_national_id("12345678") Traceback (most recent call last): From 6870d916dbfa92e42025ad179ba553225d3bec45 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Mon, 24 Oct 2022 13:31:04 +0200 Subject: [PATCH 4/4] Some systems add a dash --- strings/is_spain_national_id.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/strings/is_spain_national_id.py b/strings/is_spain_national_id.py index f39adb77e3f6..67f49755f412 100644 --- a/strings/is_spain_national_id.py +++ b/strings/is_spain_national_id.py @@ -19,7 +19,7 @@ def is_spain_national_id(spanish_id: str) -> bool: False >>> is_spain_national_id("12345678I") False - >>> is_spain_national_id("12345678-Z") # Some systems add a dash between number & letter + >>> is_spain_national_id("12345678-Z") # Some systems add a dash True >>> is_spain_national_id("12345678") Traceback (most recent call last):