From a6df63d2c3a99667d77c91fbd536309274da0ffa Mon Sep 17 00:00:00 2001 From: Camila Maia Date: Sat, 24 Jun 2023 13:41:07 +0200 Subject: [PATCH] Refatoracao do codigo dos utilitarios de cpf --- brutils/cpf.py | 117 +++++++++++++++++++--------------------------- tests/test_cpf.py | 27 ++++++----- 2 files changed, 62 insertions(+), 82 deletions(-) diff --git a/brutils/cpf.py b/brutils/cpf.py index 74aa83d..bb9c20c 100644 --- a/brutils/cpf.py +++ b/brutils/cpf.py @@ -7,33 +7,32 @@ def sieve(dirty): # type: (str) -> str """ - Filters out CPF formatting symbols. Symbols that are not used - in the CPF formatting are left unfiltered on purpose so that - if fails other tests, because their presence indicate that the - input was somehow corrupted. + Filters out CPF formatting symbols. + + Symbols that are not used in the CPF formatting are left + unfiltered on purpose so that if fails other tests, + because their presence indicate that the input was somehow corrupted. """ return "".join(filter(lambda char: char not in ".-", dirty)) def parse(dirty): # type: (str) -> str - """ - Filters out CPF formatting symbols. Symbols that are not used - in the CPF formatting are left unfiltered on purpose so that - if fails other tests, because their presence indicate that the - input was somehow corrupted. - """ + """ Alias to the function `sieve`. Better naming. """ return sieve(dirty) def display(cpf): # type: (str) -> str """ - Will format an adequately formatted numbers-only CPF string, + Format an adequately formatted numbers-only CPF string, adding in standard formatting visual aid symbols for display. + Backcompatibility for Version 1.0.1. """ + if not cpf.isdigit() or len(cpf) != 11 or len(set(cpf)) == 1: return None + return "{}.{}.{}-{}".format(cpf[:3], cpf[3:6], cpf[6:9], cpf[9:]) @@ -46,37 +45,8 @@ def format_cpf(cpf): # type: (str) -> str if not is_valid(cpf): return None - return "{}.{}.{}-{}".format(cpf[:3], cpf[3:6], cpf[6:9], cpf[9:]) - - -# CALCULATORS -############# - -def hashdigit(cpf, position): # type: (str, int) -> int - """ - Will compute the given `position` checksum digit for the `cpf` - input. The input needs to contain all elements previous to - `position` else computation will yield the wrong result. - """ - val = ( - sum( - int(digit) * weight - for digit, weight in zip(cpf, range(position, 1, -1)) - ) - % 11 - ) - return 0 if val < 2 else 11 - val - - -def checksum(basenum): # type: (str) -> str - """ - Will compute the checksum digits for a given CPF base number. - `basenum` needs to be a digit-string of adequate length. - """ - verifying_digits = str(hashdigit(basenum, 10)) - verifying_digits += str(hashdigit(basenum + verifying_digits, 11)) - return verifying_digits + return "{}.{}.{}-{}".format(cpf[:3], cpf[3:6], cpf[6:9], cpf[9:]) # OPERATIONS @@ -88,54 +58,61 @@ def validate(cpf): # type: (str) -> bool Returns whether or not the verifying checksum digits of the given `cpf` match it's base number. Input should be a digit string of proper length. + + Source: https://www.geradorcpf.com/algoritmo_do_cpf.htm + Backcompatibility for Version 1.0.1. """ + if not cpf.isdigit() or len(cpf) != 11 or len(set(cpf)) == 1: return False - return all(hashdigit(cpf, i + 10) == int(v) for i, v in enumerate(cpf[9:])) - -def generate(): # type: () -> str - """Generates a random valid CPF digit string.""" - base = str(randint(1, 999999998)).zfill(9) - while len(set(base)) == 1: - base = str(randint(1, 999999998)).zfill(9) - return base + checksum(base) + return all(_hashdigit(cpf, i + 10) == int(v) for i, v in enumerate(cpf[9:])) def is_valid(cpf): # type: (str) -> bool """ - Returns whether or not a cpf is_valid. - Source: https://www.geradorcpf.com/algoritmo_do_cpf.htm + Evaluates that cpf is String and calls validate. """ - is_syntax_valid = isinstance(cpf, str) and len(cpf) == 11 and cpf.isdigit() - return is_syntax_valid and _is_semantic_valid(cpf) + return isinstance(cpf, str) and validate(cpf) -def _is_semantic_valid(cpf): - cpf = [int(digit) for digit in cpf] +def generate(): # type: () -> str + """Generates a random valid CPF digit string.""" + + base = str(randint(1, 999999998)).zfill(9) - constants_tenth_digit = [10, 9, 8, 7, 6, 5, 4, 3, 2] - is_tenth_digit_valid = _is_digit_valid(cpf, constants_tenth_digit, 9) + while len(set(base)) == 1: + base = str(randint(1, 999999998)).zfill(9) - constants_eleventh_digit = [11, 10, 9, 8, 7, 6, 5, 4, 3, 2] - is_eleventh_digit_valid = _is_digit_valid(cpf, constants_eleventh_digit, 10) + return base + _checksum(base) - return is_tenth_digit_valid and is_eleventh_digit_valid +def _hashdigit(cpf, position): # type: (str, int) -> int + """ + Will compute the given `position` checksum digit for the `cpf` + input. The input needs to contain all elements previous to + `position` else computation will yield the wrong result. + """ -def _is_digit_valid(cpf, constants, digit_index): - sum = _multiply_and_sum_lists(cpf, constants, digit_index) - rest = sum % 11 - digit = cpf[digit_index] + val = ( + sum( + int(digit) * weight + for digit, weight in zip(cpf, range(position, 1, -1)) + ) + % 11 + ) - return (rest <= 2 and digit == 0) or (rest > 2 and digit == (11 - rest)) + return 0 if val < 2 else 11 - val -def _multiply_and_sum_lists(list_1, list_2, max_index): - sum = 0 +def _checksum(basenum): # type: (str) -> str + """ + Will compute the checksum digits for a given CPF base number. + `basenum` needs to be a digit-string of adequate length. + """ - for index in range(0, max_index): - sum += list_1[index] * list_2[index] + verifying_digits = str(_hashdigit(basenum, 10)) + verifying_digits += str(_hashdigit(basenum + verifying_digits, 11)) - return sum + return verifying_digits diff --git a/tests/test_cpf.py b/tests/test_cpf.py index f1e1550..5af9741 100644 --- a/tests/test_cpf.py +++ b/tests/test_cpf.py @@ -12,13 +12,13 @@ from brutils.cpf import ( sieve, display, - hashdigit, - checksum, validate, generate, is_valid, format_cpf, parse, + _hashdigit, + _checksum, ) from unittest import TestCase, main @@ -58,16 +58,6 @@ def test_format_cpf(self): # When cpf isn't valid, returns None assert format_cpf("11144477735") is None - def test_hashdigit(self): - assert hashdigit("000000000", 10) == 0 - assert hashdigit("0000000000", 11) == 0 - assert hashdigit("52513127765", 10) == 6 - assert hashdigit("52513127765", 11) == 5 - - def test_checksum(self): - assert checksum("000000000") == "00" - assert checksum("525131277") == "65" - def test_validate(self): assert validate("52513127765") assert validate("52599927765") @@ -83,6 +73,9 @@ def test_is_valid(self): # When cpf does not contain only digits, returns False assert not is_valid("1112223334-") + # When CPF has only the same digit, returns false + assert not is_valid("11111111111") + # When rest_1 is lt 2 and the 10th digit is not 0, returns False assert not is_valid("11111111215") @@ -104,6 +97,16 @@ def test_generate(self): assert validate(generate()) assert display(generate()) is not None + def test__hashdigit(self): + assert _hashdigit("000000000", 10) == 0 + assert _hashdigit("0000000000", 11) == 0 + assert _hashdigit("52513127765", 10) == 6 + assert _hashdigit("52513127765", 11) == 5 + + def test_checksum(self): + assert _checksum("000000000") == "00" + assert _checksum("525131277") == "65" + if __name__ == "__main__": main()