python-stdnum branch master updated. 1.11-45-g790a052
[
Date Prev][
Date Next]
[
Thread Prev][
Thread Next]
python-stdnum branch master updated. 1.11-45-g790a052
- From: Commits of the python-stdnum project <python-stdnum-commits [at] lists.arthurdejong.org>
- To: python-stdnum-commits [at] lists.arthurdejong.org
- Reply-to: python-stdnum-users [at] lists.arthurdejong.org, python-stdnum-commits [at] lists.arthurdejong.org
- Subject: python-stdnum branch master updated. 1.11-45-g790a052
- Date: Mon, 14 Oct 2019 17:26:03 +0200 (CEST)
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "python-stdnum".
The branch, master has been updated
via 790a0526e66c006d16e148174c99f964c52e98d4 (commit)
from 63a643fcb2ef38d6e7cd1c24b7bf037b7145451b (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
https://arthurdejong.org/git/python-stdnum/commit/?id=790a0526e66c006d16e148174c99f964c52e98d4
commit 790a0526e66c006d16e148174c99f964c52e98d4
Author: Dimitri Papadopoulos
<3234522+DimitriPapadopoulos@users.noreply.github.com>
Date: Mon Oct 14 14:57:22 2019 +0200
Add South Korean Resident Registration Numbers
diff --git a/stdnum/kr/__init__.py b/stdnum/kr/__init__.py
new file mode 100644
index 0000000..03cbe0f
--- /dev/null
+++ b/stdnum/kr/__init__.py
@@ -0,0 +1,21 @@
+# __init__.py - collection of South Korean numbers
+# coding: utf-8
+#
+# Copyright (C) 2019 Dimitri Papadopoulos
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 USA
+
+"""Collection of South Korean numbers."""
diff --git a/stdnum/kr/rrn.py b/stdnum/kr/rrn.py
new file mode 100644
index 0000000..1b6abee
--- /dev/null
+++ b/stdnum/kr/rrn.py
@@ -0,0 +1,127 @@
+# rrn.py - functions for handling South Korean RRN numbers
+# coding: utf-8
+#
+# Copyright (C) 2019 Dimitri Papadopoulos
+# Copyright (C) 2019 Arthur de Jong
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 USA
+
+"""RRN (South Korean resident registration number).
+
+The RRN (resident registration number, 주민등록번호) is a 13-digit number
+issued to all residents of the Republic of Korea. Foreigners residing in the
+Republic of Korea receive an alien registration number (ARN) which follows
+the same encoding pattern.
+
+The first six digits code the date of birth. The seventh digit encodes the
+century and gender. The next four digits encode the place of birth for
+Koreans or the issuing agency for foreigners, followed by two digits for the
+community center number, one serial number and a check digit.
+
+More information:
+
+*
http://www.law.go.kr/lsSc.do?tabMenuId=tab18&p1=&subMenu=1&nwYn=1§ion=&tabNo=&query=%EA%B0%9C%EC%9D%B8%EC%A0%95%EB%B3%B4%20%EB%B3%B4%ED%98%B8%EB%B2%95
+* https://en.wikipedia.org/wiki/Resident_registration_number
+* https://techscience.org/a/2015092901/
+
+>>> validate('971013-9019902')
+'9710139019902'
+>>> validate('971013-9019903') # incorrect checksum
+Traceback (most recent call last):
+ ...
+InvalidChecksum: ...
+"""
+
+import datetime
+
+from stdnum.exceptions import *
+from stdnum.util import clean, isdigits
+
+
+def compact(number):
+ """Convert the number to the minimal representation. This strips the
+ number of any valid separators and removes surrounding whitespace."""
+ return clean(number, '-').strip()
+
+
+def calc_check_digit(number):
+ """Calculate the check digit."""
+ weights = (2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5)
+ check = sum(w * int(n) for w, n in zip(weights, number))
+ return str((11 - (check % 11)) % 10)
+
+
+def get_birth_date(number, allow_future=True):
+ """Split the date parts from the number and return the birth date. If
+ allow_future is True birth dates in the future are rejected."""
+ number = compact(number)
+ year = int(number[0:2])
+ month = int(number[2:4])
+ day = int(number[4:6])
+ century = int(number[6])
+ if century in {1, 2, 5, 6}: # born 1900-1999
+ year += 1900
+ elif century in {3, 4, 7, 8}: # born 2000-2099
+ year += 2000
+ else: # born 1800-1899
+ year += 1800
+ try:
+ date_of_birth = datetime.date(year, month, day)
+ except ValueError:
+ raise InvalidComponent()
+ else:
+ # The resident registration number is given to each Korean citizen
+ # at birth or by naturalization, although the resident registration
+ # card is issued upon the 17th birthday.
+ if not allow_future and date_of_birth >= datetime.date.today():
+ raise InvalidComponent()
+ return date_of_birth
+
+
+def validate(number, allow_future=True):
+ """Check if the number is a valid RNN. This checks the length, formatting
+ and check digit. If allow_future is True birth dates in the future are
+ rejected."""
+ number = compact(number)
+ if not isdigits(number):
+ raise InvalidFormat()
+ if len(number) != 13:
+ raise InvalidLength()
+ get_birth_date(number, allow_future)
+ place_of_birth = int(number[7:9])
+ if place_of_birth > 96:
+ raise InvalidComponent()
+ # We cannot check the community center (CC), any information on
+ # valid/invalid CC digits is welcome.
+ if number[-1] != calc_check_digit(number[:-1]):
+ raise InvalidChecksum()
+ return number
+
+
+def is_valid(number, allow_future=True):
+ """Check if the number provided is valid."""
+ try:
+ return bool(validate(number, allow_future))
+ except ValidationError:
+ return False
+
+
+def format(number):
+ """Reformat the number to the standard presentation format."""
+ number = compact(number)
+ if len(number) == 13:
+ number = number[:6] + '-' + number[6:]
+ return number
diff --git a/tests/test_kr_rrn.doctest b/tests/test_kr_rrn.doctest
new file mode 100644
index 0000000..09a1412
--- /dev/null
+++ b/tests/test_kr_rrn.doctest
@@ -0,0 +1,124 @@
+test_kr_rrn.doctest - more detailed doctests for stdnum.kr.rrn module
+
+Copyright (C) 2019 Dimitri Papadopoulos
+Copyright (C) 2019 Arthur de Jong
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA
+
+
+This file contains more detailed doctests for the stdnum.kr.rrn module. It
+tries to cover more corner cases and detailed functionality that is not
+really useful as module documentation.
+
+>>> from stdnum.kr import rrn
+
+
+Some basic tests for invalid numbers:
+
+>>> rrn.validate('971013901990') # less than 13 digits
+Traceback (most recent call last):
+ ...
+InvalidLength: ...
+>>> rrn.validate('971013-901990') # less than 13 digits
+Traceback (most recent call last):
+ ...
+InvalidLength: ...
+>>> rrn.validate('97101390199020') # more than 13 digits
+Traceback (most recent call last):
+ ...
+InvalidLength: ...
+>>> rrn.validate('971013-90199020') # more than 13 digits
+Traceback (most recent call last):
+ ...
+InvalidLength: ...
+>>> rrn.validate('acvbnmkjh')
+Traceback (most recent call last):
+ ...
+InvalidFormat: ...
+>>> rrn.validate('9710131019901') # date of birth is 1997-10-13
+'9710131019901'
+>>> rrn.validate('9710139019902') # date of birth is 1897-10-13
+'9710139019902'
+>>> rrn.validate('971013-9019902')
+'9710139019902'
+>>> rrn.validate('9710139019902', allow_future=False)
+'9710139019902'
+>>> rrn.validate('971013-9019903') # incorrect checksum
+Traceback (most recent call last):
+ ...
+InvalidChecksum: ...
+>>> rrn.validate('971310-9019908') # invalid date of birth 1897-13-10
+Traceback (most recent call last):
+ ...
+InvalidComponent: ...
+>>> rrn.validate('991231-4019906') # date of birth is 2099-12-31
+'9912314019906'
+>>> rrn.validate('991231-4019906', allow_future=False) # shall fail until
2099-12-31!
+Traceback (most recent call last):
+ ...
+InvalidComponent: ...
+>>> rrn.validate('971013-9999904') # invalid place of birth
+Traceback (most recent call last):
+ ...
+InvalidComponent: ...
+
+
+These have been randomly
+`generated online
<https://www.myfakeinfo.com/nationalidno/get-kr-resident-registration-number-with-name.php>`_
+and should all be valid numbers.
+
+>>> numbers = '''
+...
+... 880411-2238459
+... 760406-1679592
+... 861121-1967921
+... 751121-1356129
+... 970510-1931536
+... 771228-2514165
+... 931112-6271351
+... 850618-1811204
+... 870316-1692085
+... 750313-1852815
+... 940624-5149547
+... 751129-1803993
+... 860719-1668646
+... 910723-2859903
+... 881219-5460537
+... 741213-6670511
+... 810819-6901483
+... 930315-2140281
+... 861012-2690172
+... 900417-2806195
+...
+... '''
+>>> [x for x in numbers.splitlines() if x and not rrn.is_valid(x)]
+[]
+
+
+Formatting and compacting tests:
+
+>>> rrn.format('9710139019902')
+'971013-9019902'
+>>> rrn.format('971013-9019902')
+'971013-9019902'
+>>> rrn.compact('971013-9019902')
+'9710139019902'
+>>> rrn.compact('9710139-019902') # dash in the wrong place
+'9710139019902'
+>>> rrn.compact('971013901990') # less than 13 digits
+'971013901990'
+>>> rrn.format('123') # don't change invalid numbers
+'123'
-----------------------------------------------------------------------
Summary of changes:
stdnum/{ch => kr}/__init__.py | 6 +-
stdnum/kr/rrn.py | 127 ++++++++++++++++++++++++++++++++++++++++++
tests/test_kr_rrn.doctest | 124 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 254 insertions(+), 3 deletions(-)
copy stdnum/{ch => kr}/__init__.py (84%)
create mode 100644 stdnum/kr/rrn.py
create mode 100644 tests/test_kr_rrn.doctest
hooks/post-receive
--
python-stdnum
- python-stdnum branch master updated. 1.11-45-g790a052,
Commits of the python-stdnum project