This commit is contained in:
2022-12-28 13:24:37 +02:00
parent 9d48bdb3b2
commit 5078157a74

102
main.py Normal file
View File

@ -0,0 +1,102 @@
import datetime
from random import randint
from typing import Tuple, NamedTuple, Union, Iterable
START_LEGACY_DATE: datetime.date = datetime.date(1800, 1, 1)
FINAL_LEGACY_DATE: datetime.date = datetime.date(2017, 7, 1)
class PersonalCode(NamedTuple):
first_part: str
second_part: str
has_check_digit: bool = False
@classmethod
def generate_anonymous_personal_code(cls) -> "PersonalCode":
pc = f"3{randint(2 * 10 ** 9, 10 * 10 ** 9 - 1)}"
first_part = pc[:6]
second_part = pc[6:]
instance = cls(first_part, second_part)
return instance
@classmethod
def generate_anonymous_with_check_digit(cls) -> "PersonalCode":
tmp = cls.generate_anonymous_personal_code()
check_digit = tmp._get_checksum_digit()
if check_digit == 10:
return tmp.generate_anonymous_with_check_digit()
second_part = tmp.second_part[:-1]+str(check_digit)
instance = cls(tmp.first_part, second_part, True)
return instance
@classmethod
def generate_legacy_with_check_digit(cls, years: int = None) -> "PersonalCode":
if years is not None:
if years < (datetime.date.today()-FINAL_LEGACY_DATE).days//365:
raise ValueError(
f"Too young for legacy Personal Code! years < {(datetime.date.today()-FINAL_LEGACY_DATE).days//365}"
)
min_age_in_days = abs(years)*365
max_age_in_days = abs(years+1)*365
birthdate = datetime.date.today() - datetime.timedelta(days=randint(min_age_in_days, max_age_in_days))
else:
max_age_in_days = (FINAL_LEGACY_DATE-START_LEGACY_DATE).days
min_age_in_days = 1
birthdate = FINAL_LEGACY_DATE - datetime.timedelta(days=randint(min_age_in_days, max_age_in_days))
first_part = f"{birthdate.day:02d}{birthdate.month:02d}{str(birthdate.year)[2:]}"
century_digit = 2 if birthdate.year // 2000 else 1 if birthdate.year // 1900 else 0
second_part = f"{century_digit}{randint(0, 999):03}"
tmp = cls(first_part, second_part)
check_digit = tmp._get_checksum_digit()
if check_digit == 10:
return cls.generate_legacy_with_check_digit()
second_part = f"{second_part}{check_digit}"
return cls(first_part, second_part, True)
@classmethod
def generate_legacy_for_birthday(cls, *args: Union[Union[datetime.date], Union[int, int, int]]) -> "PersonalCode":
if len(args) == 1:
if not isinstance(args[0], datetime.date):
raise ValueError(
f"Calling method with one parameter, it must be of type `datetime.date` not `{type(args[0])}`"
)
birthday = args[0]
elif len(args) == 3:
if not all(isinstance(o, int) for o in args):
raise ValueError(
f"Calling method with three parameters, they all must be of type `int` not `{[type(arg) for arg in args]}`"
)
birthday = datetime.date(*args)
else:
raise ValueError(
"Method must be called with either one argument which is `datetime.date` or three int parameters "
f"which represent year, month, day representing date between {START_LEGACY_DATE} and {FINAL_LEGACY_DATE}"
)
if not START_LEGACY_DATE <= birthday < FINAL_LEGACY_DATE:
raise ValueError(
f"Legacy non-anonymous personal codes are generated for birthdays since {START_LEGACY_DATE} till {FINAL_LEGACY_DATE}!\n"
f"{START_LEGACY_DATE} <= {birthday} < {FINAL_LEGACY_DATE}"
)
first_part = f"{birthday.day:02d}{birthday.month:02d}{str(birthday.year)[2:]}"
century_digit = 2 if birthday.year // 2000 else 1 if birthday.year // 1900 else 0
second_part = f"{century_digit}{randint(0, 999):03}"
tmp = cls(first_part, second_part)
check_digit = tmp._get_checksum_digit()
if check_digit == 10:
return cls.generate_legacy_with_check_digit()
second_part = f"{second_part}{check_digit}"
return cls(first_part, second_part, True)
def as_tuple(self) -> Tuple[str, str]:
return self.first_part, self.second_part
def __str__(self):
return f"{self.first_part}{self.second_part}"
def _get_checksum_digit(self) -> int:
_factors = [1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
return (1101 - sum(map(lambda p, f: int(p) * f, str(self)[:10], _factors))) % 11
@property
def dashed(self):
return f"{self.first_part}-{self.second_part}"