8000 Add a Malaysian NRIC No. module · sharoonthomas/python-stdnum@46a7996 · GitHub
[go: up one dir, main page]

Skip to content

Commit 46a7996

Browse files
committed
Add a Malaysian NRIC No. module
NRIC No. (National Registration Identity Card Number) is the unique identifier for issued to Malaysian citizens and permanent residents.
1 parent 999f2c3 commit 46a7996

File tree

5 files changed

+430
-0
lines changed

5 files changed

+430
-0
lines changed

getmybp.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#!/usr/bin/env python
2+
3+
# getmybp.py - script to donwnload data from Malaysian government site
4+
#
5+
# Copyright (C) 2013 Arthur de Jong
6+
#
7+
# This library is free software; you can redistribute it and/or
8+
# modify it under the terms of the GNU Lesser General Public
9+
# License as published by the Free Software Foundation; either
10+
# version 2.1 of the License, or (at your option) any later version.
11+
#
12+
# This library is distributed in the hope that it will be useful,
13+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
# Lesser General Public License for more details.
16+
#
17+
# You should have received a copy of the GNU Lesser General Public
18+
# License along with this library; if not, write to the Free Software
19+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20+
# 02110-1301 USA
21+
22+
from collections import defaultdict
23+
import re
24+
import urllib
25+
26+
import BeautifulSoup
27+
28+
29+
# URLs that are downloaded
30+
state_list_url = 'http://www.jpn.gov.my/en/informasi/states-code'
31+
country_list_url = 'http://www.jpn.gov.my/en/informasi/country-code'
32+
33+
34+
spaces_re = re.compile('\s+', re.UNICODE)
35+
36+
37+
def clean(s):
38+
"""Cleans up the string removing unneeded stuff from it."""
39+
return spaces_re.sub(' ', s.replace(u'\u0096', '')).strip().encode('utf-8')
40+
41+
42+
def parse(f):
43+
"""Parse the specified file."""
44+
soup = BeautifulSoup.BeautifulSoup(f, convertEntities='html')
45+
# find all table rows
46+
for tr in soup.find('div', id='content').findAll('tr'):
47+
# find the rows with four columns of text
48+
tds = [
49+
clean(''.join(x.string for x in td.findAll(text=True)))
50+
for td in tr.findAll('td')
51+
]
52+
if len(tds) >= 2 and tds[0] and tds[1]:
53+
yield tds[0], tds[1]
54+
if len(tds) >= 4 and tds[2] and tds[3]:
55+
yield tds[2], tds[3]
56+
57+
58+
if __name__ == '__main__':
59+
results = defaultdict(lambda : defaultdict(list))
60+
# read the states
61+
#f = open('/tmp/states.html', 'r')
62+
f = urllib.urlopen(state_list_url)
63+
for state, bps in parse(f):
64+
for bp in bps.split(','):
65+
results[bp.strip()]['state'] = state
66+
results[bp.strip()]['countries'].append('Malaysia')
67+
# read the countries
68+
#f = open('/tmp/countries.html', 'r')
69+
f = urllib.urlopen(country_list_url)
70+
for country, bp in parse(f):
71+
results[bp]['countries'].append(country)
72+
# print the results
73+
print '# generated from National Registration Department of Malaysia, downloaded from'
74+
print '# %s' % state_list_url
75+
print '# %s' % country_list_url
76+
print
77+
for bp in sorted(results.iterkeys()):
78+
res = bp
79+
row = results[bp]
80+
if 'state' in row:
81+
res += ' state="%s"' % row['state']
82+
countries = row['countries']
83+
if len(countries) == 1:
84+
res += ' country="%s"' % countries[0]
85+
if len(countries) > 0:
86+
res += ' countries="%s"' % (', '.join(countries))
87+
print res

stdnum/my/__init__.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# __init__.py - collection of Malaysian numbers
2+
# coding: utf-8
3+
#
4+
# Copyright (C) 2013 Arthur de Jong
5+
#
6+
# This library is free software; you can redistribute it and/or
7+
# modify it under the terms of the GNU Lesser General Public
8+
# License as published by the Free Software Foundation; either
9+
# version 2.1 of the License, or (at your option) any later version.
10+
#
11+
# This library is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
# Lesser General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU Lesser General Public
17+
# License along with this library; if not, write to the Free Software
18+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19+
# 02110-1301 USA
20+
21+
"""Collection of Malaysian numbers."""

stdnum/my/bp.dat

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# generated from National Registration Department of Malaysia, downloaded from
2+
# http://www.jpn.gov.my/en/informasi/states-code
3+
# http://www.jpn.gov.my/en/informasi/country-code
4+
5+
01 state="Johor" country="Malaysia" countries="Malaysia"
6+
02 state="Kedah" country="Malaysia" countries="Malaysia"
7+
03 state="Kelantan" country="Malaysia" countries="Malaysia"
8+
04 state="Melaka" country="Malaysia" countries="Malaysia"
9+
05 state="Negeri Sembilan" country="Malaysia" countries="Malaysia"
10+
06 state="Pahang" country="Malaysia" countries="Malaysia"
11+
07 state="Pulau Pinang" country="Malaysia" countries="Malaysia"
12+
08 state="Perak" country="Malaysia" countries="Malaysia"
13+
09 state="Perlis" country="Malaysia" countries="Malaysia"
14+
10 state="Selangor" country="Malaysia" countries="Malaysia"
15+
11 state="Terengganu" country="Malaysia" countries="Malaysia"
16+
12 state="Sabah" country="Malaysia" countries="Malaysia"
17+
13 state="Sarawak" country="Malaysia" countries="Malaysia"
18+
14 state="Wilayah Persekutuan (Kuala Lumpur)" country="Malaysia" countries="Malaysia"
19+
15 state="Wilayah Persekutuan (Labuan)" country="Malaysia" countries="Malaysia"
20+
16 state="Wilayah Persekutuan (Putrajaya)" country="Malaysia" countries="Malaysia"
21+
21 state="Johor" country="Malaysia" countries="Malaysia"
22+
22 state="Johor" country="Malaysia" countries="Malaysia"
23+
23 state="Johor" country="Malaysia" countries="Malaysia"
24+
24 state="Johor" country="Malaysia" countries="Malaysia"
25+
25 state="Kedah" country="Malaysia" countries="Malaysia"
26+
26 state="Kedah" country="Malaysia" countries="Malaysia"
27+
27 state="Kedah" country="Malaysia" countries="Malaysia"
28+
28 state="Kelantan" country="Malaysia" countries="Malaysia"
29+
29 state="Kelantan" country="Malaysia" countries="Malaysia"
30+
30 state="Melaka" country="Malaysia" countries="Malaysia"
31+
31 state="Negeri Sembilan" country="Malaysia" countries="Malaysia"
32+
32 state="Pahang" country="Malaysia" countries="Malaysia"
33+
33 state="Pahang" country="Malaysia" countries="Malaysia"
34+
34 state="Pulau Pinang" country="Malaysia" countries="Malaysia"
35+
35 state="Pulau Pinang" country="Malaysia" countries="Malaysia"
36+
36 state="Perak" country="Malaysia" countries="Malaysia"
37+
37 state="Perak" country="Malaysia" countries="Malaysia"
38+
38 state="Perak" country="Malaysia" countries="Malaysia"
39+
39 state="Perak" country="Malaysia" countries="Malaysia"
40+
40 state="Perlis" country="Malaysia" countries="Malaysia"
41+
41 state="Selangor" country="Malaysia" countries="Malaysia"
42+
42 state="Selangor" country="Malaysia" countries="Malaysia"
43+
43 state="Selangor" country="Malaysia" countries="Malaysia"
44+
44 state="Selangor" country="Malaysia" countries="Malaysia"
45+
45 state="Terengganu" country="Malaysia" countries="Malaysia"
46+
46 state="Terengganu" country="Malaysia" countries="Malaysia"
47+
47 state="Sabah" country="Malaysia" countries="Malaysia"
48+
48 state="Sabah" country="Malaysia" countries="Malaysia"
49+
49 state="Sabah" country="Malaysia" countries="Malaysia"
50+
50 state="Sarawak" country="Malaysia" countries="Malaysia"
51+
51 state="Sarawak" country="Malaysia" countries="Malaysia"
52+
52 state="Sarawak" country="Malaysia" countries="Malaysia"
53+
53 state="Sarawak" country="Malaysia" countries="Malaysia"
54+
54 state="Wilayah Persekutuan (Kuala Lumpur)" country="Malaysia" countries="Malaysia"
55+
55 state="Wilayah Persekutuan (Kuala Lumpur)" country="Malaysia" countries="Malaysia"
56+
56 state="Wilayah Persekutuan (Kuala Lumpur)" country="Malaysia" countries="Malaysia"
57+
57 state="Wilayah Persekutuan (Kuala Lumpur)" country="Malaysia" countries="Malaysia"
58+
58 state="Wilayah Persekutuan (Labuan)" country="Malaysia" countries="Malaysia"
59+
59 state="Negeri Sembilan" country="Malaysia" countries="Malaysia"
60+
60 country="Brunei" countries="Brunei"
61+
61 country="Indonesia" countries="Indonesia"
62+
62 countries="Cambodia, Kampuchea"
63+
63 country="Laos" countries="Laos"
64+
64 country="Mynmar" countries="Mynmar"
65+
65 country="Filipina" countries="Filipina"
66+
66 country="Singapura" countries="Singapura"
67+
67 country="Thailand" countries="Thailand"
68+
68 country="Vietnam" countries="Vietnam"
69+
74 country="China" countries="China"
70+
75 country="India" countries="India"
71+
76 country="Pakistan" countries="Pakistan"
72+
77 country="Arab Saudi" countries="Arab Saudi"
73+
78 country="Sri Lanka" countries="Sri Lanka"
74+
79 country="Bangladesh" countries="Bangladesh"
75+
82 state="Negeri Tidak Diketahui" country="Malaysia" countries="Malaysia"
76+
83 countries="Australia, American Samoa, Macedonia, New Zealand, New Caledonia, Papua New Gurney, Fiji, Timor Leste"
77+
84 countries="Argentina, Anguilla, Aruba, Bolivia, Brazil, Paraguay, Peru, Chile, Colombia, Equador, Uruguay, Venezuela"
78+
85 countries="Algeria, Angola, Kenya, Afrika Tengah, Liberia, Afrika Selatan, Mali, Mauritania, Morocco, Malawi, Botswana, Mozambique, Burundi, Nigeria, Namibia, Cameroon, Chad, Rwanda, Senegal, Sierra Leone, Somalia, Djibouti, Sudan, Egypt, Ethopia, Swaziland, Eritrea, Gambia, Ghana, Tunisia, Tanzania, Tonga, Togo, Uganda, Zaire, Zambia, Zimbabwe"
79+
86 countries="Austria, Luxembourg, Armenia, Malta, Monaco, Belgium, Nitherlands, Norway, Cyprus, Portugal, Denmark, Sweeden, Spain, Switzerland, France, Finland, Slovakia, Slovenia, Greece, Germany, Holy See (Vatican City), Italy"
80+
87 countries="Britain, Ireland"
81+
88 countries="Jordan, Kuwait, Lebanon, Bahrain, Oman, Qatar, Syria, Turkey, United Arab Emirate, Iran, Iraq, Israel, Yemen"
82+
89 countries="Japan, Korea Selatan, Korea Utara, Taiwan"
83+
90 countries="Jamaica, Bahamas, Barbados, Belize, Mexico, Nicaragua, Panama, Puerto Rico, Costa Rica, Cuba, Dominica, El Salvador, Grenada, Guatemala, Trinidad&Tobado, Haiti, Honduras"
84+
91 countries="Canada, Greenland, United State"
85+
92 countries="Albania, Albania, Latvia, Lithuania, Bulgaria, Byelorussia, Bosnia, Belarus, Poland, Romania, Russia, Czechoslovakia, Crotia, Esthonia, Serbia, Georgia, Hungary, Ukraine"
86+
93 countries="Afghanistan, Antigua & Barbuda, Kazakhstan, Andorra/Andora, Libya, Arzebaijan, Antartica, Maldives, Madagascar, Mauritius, Mongolia, Benin, Maghribi, Bhutan, Macau, Nepal, Bermuda, Burkina faso/Burkina, Bora-bora, Bouvet Island, Palestine, Cape Verde, Comoros, Seychelles, Soloman Islands, Samoa, San Marino, Guinea, Gibraltar, Tajikistan, Tukmenistan, Hong Kong, Uzbekistan, Ivory Coast, Vanuatu, Iceland, Yugoslavia"

stdnum/my/nric.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# nric.py - functions for handling NRIC numbers
2+
#
3+
# Copyright (C) 2013 Arthur de Jong
4+
#
5+
# This library is free software; you can redistribute it and/or
6+
# modify it under the terms of the GNU Lesser General Public
7+
# License as published by the Free Software Foundation; either
8+
# version 2.1 of the License, or (at your option) any later version.
9+
#
10+
# This library is distributed in the hope that it will be useful,
11+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
# Lesser General Public License for more details.
14+
#
15+
# You should have received a copy of the GNU Lesser General Public
16+
# License along with this library; if not, write to the Free Software
17+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18+
# 02110-1301 USA
19+
20+
"""NRIC No. (National Registration Identity Card Number) is the unique
21+
identifier for issued to Malaysian citizens and permanent residents.
22+
23+
The number consist of 12 digits in three sections. The first 6 digits
24+
represent the birth date, followed by two digits represeting the birth
25+
place and finally four digits. The gender of a person can be derived from
26+
the last digit: odd numbers for males and even numbers for females.
27+
28+
>>> validate('770305-02-1234')
29+
'770305021234'
30+
>>> validate('771305-02-1234') # invalid date
31+
Traceback (most recent call last):
32+
...
33+
InvalidComponent: ...
34+
>>> validate('770305-99-1234') # unknown birth place code
35+
Traceback (most recent call last):
36+
...
37+
InvalidComponent: ...
38+
>>> format('770305021234')
39+
'770305-02-1234'
40+
"""
41+
42+
import datetime
43+
44+
from stdnum.exceptions import *
45+
from stdnum.util import clean
46+
47+
48+
def compact(number):
49+
"""Convert the number to the minimal representation. This strips the
50+
number of any valid separators and removes surrounding whitespace."""
51+
return clean(number, ' -*').strip()
52+
53+
54+
def get_birth_date(number):
55+
"""Split the date parts from the number and return the birth date.
56+
Note that in some cases it may return the registration date instead of
57+
the birth date and it may be a century off."""
58+
number = compact(number)
59+
year = int(number[0:2])
60+
month = int(number[2:4])
61+
day = int(number[4:6])
62+
# this is a bit broken but it's easy
63+
try:
64+
return datetime.date(year + 1900, month, day)
65+
except ValueError:
66+
pass
67+
try:
68+
return datetime.date(year + 2000, month, day)
69+
except ValueError:
70+
raise InvalidComponent()
71+
72+
73+
def get_birth_place(number):
74+
"""Use the number to look up the place of birth of the person. This can
75+
either be a state or federal territory within Malaysia or a country
76+
outside of Malaysia."""
77+
from stdnum import numdb
78+
number = compact(number)
79+
results = numdb.get('my/bp').info(number[6:8])[0][1]
80+
if not results:
81+
raise InvalidComponent()
82+
return results
83+
84+
85+
def validate(number):
86+
"""Checks to see if the number provided is a valid NRIC numbers. This
87+
checks the length, formatting and birth date and place."""
88+
number = compact(number)
89+
if len(number) != 12:
90+
raise InvalidLength()
91+
if not number.isdigit():
92+
raise InvalidFormat()
93+
get_birth_date(number)
94+
get_birth_place(number)
95+
return number
96+
97+
98+
def is_valid(number):
99+
"""Checks to see if the number provided is a valid NRIC numbers. This
100+
checks the length, formatting and birth date and place."""
101+
try:
102+
return bool(validate(number))
103+
except ValidationError:
104+
return False
105+
106+
107+
def format(number):
108+
"""Reformat the passed number to the standard format."""
109+
number = compact(number)
110+
return number[:6] + '-' + number[6:8] + '-' + number[8:]

0 commit comments

Comments
 (0)
0