Skip to content

Commit

Permalink
Add support for birthday and gender to uk_UA ssn method (#1983)
Browse files Browse the repository at this point in the history
* feat(ssn)Add support uk_Ua ssn method birthday and gender optional params

* Fix unit test name capitalization

---------

Co-authored-by: Flavio Curella <[email protected]>
  • Loading branch information
lozik4 and fcurella authored Jan 29, 2024
1 parent c0de02c commit 6f59874
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 27 deletions.
77 changes: 50 additions & 27 deletions faker/providers/ssn/uk_UA/__init__.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,59 @@
from datetime import date
import random

from datetime import date, datetime
from typing import Optional

from ....typing import SexLiteral
from .. import Provider as SsnProvider


def select_gender(gender: SexLiteral) -> int:
"""Choose an even number for Female and odd number for Male."""
gender = 0 if gender.lower() == "f" else 1
return random.choice(range(gender, 10, 2))


def calculate_day_count(birthday: date) -> int:
"""Calculate the day count from reference date '31 December 1899'."""
ref_date = date(1899, 12, 31)
return (birthday - ref_date).days


def calculate_check_sum(val: str) -> int:
"""Calculate checksum using INN calculation method."""
weights = [-1, 5, 7, 9, 4, 6, 10, 5, 7]
checksum = sum(int(v) * w for v, w in zip(val, weights))

return checksum % 11 % 10


class Provider(SsnProvider):
def ssn(self) -> str:
def ssn(self, birthday: Optional[str] = None, gender: Optional[SexLiteral] = None) -> str:
"""
Ukrainian "Реєстраційний номер облікової картки платника податків"
also known as "Ідентифікаційний номер фізичної особи".
@params: birthday: "DD-MM-YYYY" format, default random date
@params: gender: "M" or "F" default: random gender
:sample:
:sample: birthday='22-06-1990', gender='F'
"""
digits = []

# Number of days between 1899-12-31 and a birth date
for digit in str((self.generator.date_object() - date(1899, 12, 31)).days):
digits.append(int(digit))

# Person's sequence number
for _ in range(4):
digits.append(self.random_int(0, 9))

checksum = (
digits[0] * -1
+ digits[1] * 5
+ digits[2] * 7
+ digits[3] * 9
+ digits[4] * 4
+ digits[5] * 6
+ digits[6] * 10
+ digits[7] * 5
+ digits[8] * 7
)
# Remainder of a checksum divided by 11 or 1 if it equals to 10
digits.append(checksum % 11 % 10)

return "".join(str(digit) for digit in digits)

try:
# generate day of birthday date object
if birthday:
dob = datetime.strptime(birthday, "%d-%m-%Y").date()
else:
dob = self.generator.date_object()
except Exception:
raise ValueError("Birthday format must be DD-MM-YYYY")

if gender and gender not in ("M", "F"):
raise ValueError('Gender must be "m" or "f" or None')

day_count = calculate_day_count(dob)
people_num = self.random_number(3, fix_len=True)
gender_ = select_gender(gender) if gender else random.randint(0, 1)
ssn_without_checksum = f"{day_count}{people_num}{gender_}"
checksum = calculate_check_sum(ssn_without_checksum)
return f"{ssn_without_checksum}{checksum}"
28 changes: 28 additions & 0 deletions tests/providers/test_ssn.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
from faker.providers.ssn.pt_BR import checksum as pt_checksum
from faker.providers.ssn.ro_RO import ssn_checksum as ro_ssn_checksum
from faker.providers.ssn.ro_RO import vat_checksum as ro_vat_checksum
from faker.providers.ssn.uk_UA import Provider as uk_Provider
from faker.providers.ssn.zh_TW import checksum as tw_checksum
from faker.utils.checksums import luhn_checksum

Expand Down Expand Up @@ -1364,3 +1365,30 @@ def test_gender(self):
def test_checksum(self):
for sample in self.samples:
assert tw_checksum(sample) % 10 == 0


class TestUkUA(unittest.TestCase):
def setUp(self):
self.fake = Faker("uk_Ua")
Faker.seed(0)
self.provider = uk_Provider

def test_ssn_len(self):
assert len(self.fake.ssn()) == 10

def test_start_ssn(self):
assert self.fake.ssn("21-06-1994")[:5] == "34505"

def test_ssn_gender(self):
m = self.fake.ssn(gender="M")
w = self.fake.ssn(gender="F")
assert int(m[8]) % 2 != 0, "Must be odd for men"
assert int(w[8]) % 2 == 0, "Must be even for women"

def test_incorrect_birthday(self):
with pytest.raises(ValueError):
self.fake.ssn(birthday="1994-06-01")

def test_incorrect_gender(self):
with pytest.raises(ValueError):
self.fake.ssn(gender="f")

0 comments on commit 6f59874

Please sign in to comment.