idnumbers.nationalid.vnm.national_id

 1import re
 2from math import floor
 3from types import SimpleNamespace
 4from typing import Optional, TypedDict
 5from ..constant import Gender
 6
 7
 8def normalize(id_number: str) -> str:
 9    """make all characters to upper case"""
10    return id_number.upper() if id_number else None
11
12
13class ParseResult(TypedDict):
14    """parse result for NationalID"""
15    province_country_code: str
16    """registration province/country code"""
17    yyyy: int
18    """year of birthday"""
19    sn: str
20    """serial number"""
21    gender: Gender
22    """gender, possible value: male, female"""
23
24
25class NationalID:
26    """
27    Vietnam ID card
28    https://vietnaminsider.vn/what-do-the-12-digits-on-the-citizen-id-card-with-chip-mean/
29    https://lawnet.vn/en/vb/Circular-07-2016-TT-BCA-detailing-Law-on-Citizen-Identification-137-2015-ND-CP-5CCC3.html
30    """
31    METADATA = SimpleNamespace(**{
32        'iso3166_alpha2': 'VN',
33        'min_length': 12,
34        'max_length': 12,
35        'parsable': True,
36        'checksum': False,
37        'regexp': re.compile(r'^(?P<province_country_code>\d{3})'
38                             r'(?P<gender>\d)'
39                             r'(?P<yy>\d{2})'
40                             r'(?P<sn>\d{6})$'),
41        'alias_of': None,
42        'names': ['National ID Number',
43                  'Thẻ căn cước công dân',
44                  ],
45        'links': ['https://en.wikipedia.org/wiki/National_identification_number#Vietnam',
46                  'https://vietnaminsider.vn/what-do-the-12-digits-on-the-citizen-id-card-with-chip-mean/',
47                  'https://lawnet.vn/en/vb/Circular-07-2016-TT-BCA-detailing-Law-on-Citizen-Identification'
48                  '-137-2015-ND-CP-5CCC3.html'],
49        'deprecated': False
50    })
51
52    @staticmethod
53    def validate(id_number: str) -> bool:
54        """
55        Validate the VNM id number
56        """
57        if not id_number:
58            return False
59
60        if not isinstance(id_number, str):
61            id_number = repr(id_number)
62        return NationalID.parse(id_number) is not None
63
64    @staticmethod
65    def parse(id_number: str) -> Optional[ParseResult]:
66        """parse the result"""
67        match_obj = NationalID.METADATA.regexp.match(id_number)
68        if not match_obj:
69            return None
70        province_country_code = match_obj.group('province_country_code')
71        century_gender = match_obj.group('gender')
72        yy = match_obj.group('yy')
73        sn = match_obj.group('sn')
74        yyyy = NationalID.get_birth_year(int(century_gender), int(yy))
75        return {
76            'province_country_code': province_country_code,
77            'yyyy': yyyy,
78            'sn': sn,
79            'gender': Gender.MALE if int(century_gender) % 2 == 0 else Gender.FEMALE
80        }
81
82    @staticmethod
83    def get_birth_year(century_gender: int, yy: int) -> int:
84        return 1900 + 100 * floor(century_gender / 2) + yy
def normalize(id_number: str) -> str:
 9def normalize(id_number: str) -> str:
10    """make all characters to upper case"""
11    return id_number.upper() if id_number else None

make all characters to upper case

class ParseResult(typing.TypedDict):
14class ParseResult(TypedDict):
15    """parse result for NationalID"""
16    province_country_code: str
17    """registration province/country code"""
18    yyyy: int
19    """year of birthday"""
20    sn: str
21    """serial number"""
22    gender: Gender
23    """gender, possible value: male, female"""

parse result for NationalID

province_country_code: str

registration province/country code

yyyy: int

year of birthday

sn: str

serial number

gender, possible value: male, female

class NationalID:
26class NationalID:
27    """
28    Vietnam ID card
29    https://vietnaminsider.vn/what-do-the-12-digits-on-the-citizen-id-card-with-chip-mean/
30    https://lawnet.vn/en/vb/Circular-07-2016-TT-BCA-detailing-Law-on-Citizen-Identification-137-2015-ND-CP-5CCC3.html
31    """
32    METADATA = SimpleNamespace(**{
33        'iso3166_alpha2': 'VN',
34        'min_length': 12,
35        'max_length': 12,
36        'parsable': True,
37        'checksum': False,
38        'regexp': re.compile(r'^(?P<province_country_code>\d{3})'
39                             r'(?P<gender>\d)'
40                             r'(?P<yy>\d{2})'
41                             r'(?P<sn>\d{6})$'),
42        'alias_of': None,
43        'names': ['National ID Number',
44                  'Thẻ căn cước công dân',
45                  ],
46        'links': ['https://en.wikipedia.org/wiki/National_identification_number#Vietnam',
47                  'https://vietnaminsider.vn/what-do-the-12-digits-on-the-citizen-id-card-with-chip-mean/',
48                  'https://lawnet.vn/en/vb/Circular-07-2016-TT-BCA-detailing-Law-on-Citizen-Identification'
49                  '-137-2015-ND-CP-5CCC3.html'],
50        'deprecated': False
51    })
52
53    @staticmethod
54    def validate(id_number: str) -> bool:
55        """
56        Validate the VNM id number
57        """
58        if not id_number:
59            return False
60
61        if not isinstance(id_number, str):
62            id_number = repr(id_number)
63        return NationalID.parse(id_number) is not None
64
65    @staticmethod
66    def parse(id_number: str) -> Optional[ParseResult]:
67        """parse the result"""
68        match_obj = NationalID.METADATA.regexp.match(id_number)
69        if not match_obj:
70            return None
71        province_country_code = match_obj.group('province_country_code')
72        century_gender = match_obj.group('gender')
73        yy = match_obj.group('yy')
74        sn = match_obj.group('sn')
75        yyyy = NationalID.get_birth_year(int(century_gender), int(yy))
76        return {
77            'province_country_code': province_country_code,
78            'yyyy': yyyy,
79            'sn': sn,
80            'gender': Gender.MALE if int(century_gender) % 2 == 0 else Gender.FEMALE
81        }
82
83    @staticmethod
84    def get_birth_year(century_gender: int, yy: int) -> int:
85        return 1900 + 100 * floor(century_gender / 2) + yy
@staticmethod
def validate(id_number: str) -> bool:
53    @staticmethod
54    def validate(id_number: str) -> bool:
55        """
56        Validate the VNM id number
57        """
58        if not id_number:
59            return False
60
61        if not isinstance(id_number, str):
62            id_number = repr(id_number)
63        return NationalID.parse(id_number) is not None

Validate the VNM id number

@staticmethod
def parse( id_number: str) -> Optional[idnumbers.nationalid.vnm.national_id.ParseResult]:
65    @staticmethod
66    def parse(id_number: str) -> Optional[ParseResult]:
67        """parse the result"""
68        match_obj = NationalID.METADATA.regexp.match(id_number)
69        if not match_obj:
70            return None
71        province_country_code = match_obj.group('province_country_code')
72        century_gender = match_obj.group('gender')
73        yy = match_obj.group('yy')
74        sn = match_obj.group('sn')
75        yyyy = NationalID.get_birth_year(int(century_gender), int(yy))
76        return {
77            'province_country_code': province_country_code,
78            'yyyy': yyyy,
79            'sn': sn,
80            'gender': Gender.MALE if int(century_gender) % 2 == 0 else Gender.FEMALE
81        }

parse the result

@staticmethod
def get_birth_year(century_gender: int, yy: int) -> int:
83    @staticmethod
84    def get_birth_year(century_gender: int, yy: int) -> int:
85        return 1900 + 100 * floor(century_gender / 2) + yy