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
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
Vietnam ID card https://vietnaminsider.vn/what-do-the-12-digits-on-the-citizen-id-card-with-chip-mean/ https://lawnet.vn/en/vb/Circular-07-2016-TT-BCA-detailing-Law-on-Citizen-Identification-137-2015-ND-CP-5CCC3.html
@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