lists.arthurdejong.org
RSS feed

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



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&section=&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