diff --git a/rrn.py b/rrn.py index c332644..73fe7d5 100644 --- a/rrn.py +++ b/rrn.py @@ -1,6 +1,7 @@ import itertools import re -from datetime import datetime +from datetime import date, datetime +from typing import Optional HYPHEN = re.compile('[-–]') @@ -10,6 +11,8 @@ MONTH_OF_BIRTH_FORMAT = '%y%m' DAY_OF_BIRTH_FORMAT = '%y%m%d' +SEX = 6 + LOC = 7, 9 MAX_LOC = 97 @@ -84,5 +87,61 @@ def is_valid_rrn(rrn: str) -> bool: return False -def is_corresponding_rrn(rrn: str) -> bool: - pass +def _is_birthday_corresponding(rrn: str, birthday: date) -> Optional[bool]: + try: + return datetime.strptime( + rrn[slice(*BIRTH)], + DAY_OF_BIRTH_FORMAT + ).date() == birthday + except (TypeError, ValueError): + return None + + +def _is_sex_corresponding(rrn: str, female: bool) -> Optional[bool]: + try: + return (int(rrn[SEX]) % 2 == 0) == female + except IndexError: + return None + + +def _is_foreignness_corresponding(rrn: str, foreign: bool) -> Optional[bool]: + try: + return (5 <= int(rrn[SEX]) <= 8) == foreign + except IndexError: + return None + + +def is_corresponding_rrn( + rrn: str, + *, + birthday: Optional[date]=None, + foreign: Optional[bool]=None, + female: Optional[bool]=None +) -> Optional[bool]: + """ + Check given RRN if it corresponds with given information or not. + It returns None if correspondence is undecidable. + For example, 6-digit RRN string does not contain any information about sex. + + :param rrn: RRN string + :param birthday: expected date of birth + :param foreign: expected to be foreigner or not + :param female: expected to be female or not + :return: correspondence (None if it's undecidable) + """ + try: + rrn = HYPHEN.sub('', rrn) + assert rrn.isdigit() + return ( + ( + birthday is None or _is_birthday_corresponding(rrn, birthday) + ) and + ( + foreign is None or _is_foreignness_corresponding(rrn, foreign) + ) and + ( + female is None or _is_sex_corresponding(rrn, female) + ) + ) + except (AssertionError, TypeError): + return None diff --git a/tests/test_rrn.py b/tests/test_rrn.py index 9916460..eb2fa10 100644 --- a/tests/test_rrn.py +++ b/tests/test_rrn.py @@ -1,4 +1,5 @@ import unittest +from datetime import date import rrn @@ -42,4 +43,27 @@ def test_is_valid_rrn(self): self.assertEqual(expected, rrn.is_valid_rrn(s)) def test_is_corresponding_rrn(self): - self.assertEqual(1 + 1, 2) + undecided, corresponding, not_corresponding = None, True, False + female, male = True, False + foreign, domestic = True, False + + for r, (b, s, f), expected in [ + ('RRN', (None, None, None), undecided), + ('940812', (None, None, None), corresponding), + ('940812', (None, male, None), undecided), + ('940812', (None, None, foreign), undecided), + ('940812', (None, female, foreign), undecided), + ('8808121', (None, male, domestic), corresponding), + ('6008122', (None, None, foreign), not_corresponding), + ('7403225', (None, female, None), not_corresponding), + ('9408131', (date(1994, 8, 13), None, None), corresponding), + ('9408121', (date(1994, 8, 12), None, domestic), corresponding), + ('0408127', (date(2004, 8, 12), male, domestic), not_corresponding), + ('9408122', (date(1994, 8, 12), female, domestic), corresponding), + ('9802145', (date(1998, 2, 14), male, foreign), corresponding), + ('9103226', (date(1991, 3, 22), female, foreign), corresponding) + ]: + self.assertEqual( + expected, + rrn.is_corresponding_rrn(r, birthday=b, female=s, foreign=f) + )