Person code generator
I had a task, where I needed to generate a bunch of fake person codes (to be more precise, Lithuanian national identification numbers) in tests. Also, I wanted to validate person code, so I need both, validator and generator. Generator should generate fake, but valid person code.
First idea is to check, maybe Faker library has person code generator already? I looked it up, and it looks, that Faker does not have this feature.
Then, I found a Wikipedia page, with per person code specs.
So according to Wikipedia, person code generator should look something like this:
import random
import datetime
def get_person_code_checksum(code: str) -> int:
numbers = list(map(int, code))
factors = [1, 2, 3, 4, 5, 6, 7, 8, 9, 1]
checksum = sum(x * i for x, i in zip(numbers, factors)) % 11
if checksum == 10:
factors = [3, 4, 5, 6, 7, 8, 9, 1, 2, 3]
checksum = sum(x * i for x, i in zip(numbers, factors)) % 10
return checksum
def generate_person_code(
*,
dob: datetime.datetime = None,
gender: int = None, # 0 - male, 1 - female
num: int = None, # between 1 and 999, including both ends
) -> str:
if dob is None:
now = datetime.datetime.now()
dob = random.randint(
int((now - datetime.timedelta(days=365) * 105).timestamp()),
int(now.timestamp()),
)
dob = datetime.date.fromtimestamp(dob)
if dob.year < 1801:
raise ValueError("dob year must be at least 1801")
if gender is None:
gender = random.randint(0, 1)
century = dob.year // 100 - 18
cender = century * 2 + gender + 1
if num is None:
num = random.randint(1, 999)
code = f'{cender}{dob:%y%m%d}{num:03}'
checksum = get_person_code_checksum(code)
return f'{code}{checksum}'
generate_person_code()
'61111244004'
Now the validator part, should look like this:
def validate_person_code(code: str) -> bool:
code = str(code)
if len(code) != 11:
return False
if not code.isdigit():
return False
if code[0] == '9':
# This is an exceptional case and might be a valid person code.
return True
year = int(code[1:3])
month = int(code[3:5])
day = int(code[5:7])
if year and month and day:
# year, month ant day can be 0 if person does not remember they birth
# date.
century, gender = divmod(int(code[0]), 2)
year = (17 + century) * 100 + year
try:
dob = datetime.datetime(year, month, day)
except ValueError:
# Invalid birth date.
return False
if dob > datetime.datetime.now():
# Person can not be born in future.
return False
if int(code[-1]) != get_person_code_checksum(code[:-1]):
# Invalid checksum.
return False
return True
validate_person_code(generate_person_code())
True
I learned interesting facts about Lithuanian national identification number. Only people who born at least on 1801 or later can have an identification number. That’s quite recent, I wonder if people were assigned an id before that?
Also, from person code you can learn persons full date of birth, gender and number, which child it was on that date. That is quite a lot of personal information. I think personal code should not have all that information. Person id should be just a random number, maybe with a checksum for validity.