Repository: joke2k/faker
Pull request #538 (2017-06-18 19:03:51)

Adding an implementation of `ssn` provider for the pl_PL locale.
Reference: https://en.wikipedia.org/wiki/National_identification_number#Poland


Patch:
diff --git a/faker/providers/ssn/pl_PL/__init__.py b/faker/providers/ssn/pl_PL/__init__.py
new file mode 100644
index 00000000..378519e9
--- /dev/null
+++ b/faker/providers/ssn/pl_PL/__init__.py
@@ -0,0 +1,66 @@
+# coding=utf-8
+
+from __future__ import unicode_literals
+from .. import Provider as SsnProvider
+from faker.providers.date_time import Provider as DateTimeProvider
+
+
+def checksum(digits):
+    """
+    Calculates and returns a control digit for given list of digits basing on PESEL standard.
+    """
+    weights_for_check_digit = [9, 7, 3, 1, 9, 7, 3, 1, 9, 7]
+    check_digit = 0
+
+    for i in range(0, 10):
+        check_digit += weights_for_check_digit[i] * digits[i]
+
+    check_digit %= 10
+
+    return check_digit
+
+
+def calculate_month(birth_date):
+    """
+    Calculates and returns a month number basing on PESEL standard.
+    """
+    year = int(birth_date.strftime('%Y'))
+    month = int(birth_date.strftime('%m')) + ((int(year / 100) - 14) % 5) * 20
+
+    return month
+
+
+class Provider(SsnProvider):
+
+    @classmethod
+    def ssn(cls):
+        """
+        Returns 11 character Polish national identity code (Public Electronic Census System,
+        Polish: Powszechny Elektroniczny System Ewidencji Ludności - PESEL).
+
+        It has the form YYMMDDZZZXQ, where YYMMDD is the date of birth (with century
+        encoded in month field), ZZZ is the personal identification number, X denotes sex
+        (even for females, odd for males) and Q is a parity number.
+
+        https://en.wikipedia.org/wiki/National_identification_number#Poland
+        """
+        birth_date = DateTimeProvider.date_time()
+
+        year_without_century = int(birth_date.strftime('%y'))
+        month = calculate_month(birth_date)
+        day = int(birth_date.strftime('%d'))
+
+        pesel_digits = [
+            int(year_without_century / 10),
+            year_without_century % 10,
+            int(month / 10),
+            month % 10,
+            int(day / 10), day % 10
+        ]
+
+        for _ in range(4):
+            pesel_digits.append(cls.random_digit())
+
+        pesel_digits.append(checksum(pesel_digits))
+
+        return ''.join(str(digit) for digit in pesel_digits)


Test patch:
diff --git a/tests/providers/ssn.py b/tests/providers/ssn.py
index fe559113..9e9e6627 100644
--- a/tests/providers/ssn.py
+++ b/tests/providers/ssn.py
@@ -4,11 +4,14 @@ from __future__ import unicode_literals
 
 import unittest
 import re
+from datetime import datetime
 
 from faker import Factory
 from faker.providers.ssn.et_EE import Provider as EtProvider, checksum as et_checksum
 from faker.providers.ssn.hr_HR import Provider as HrProvider, checksum as hr_checksum
 from faker.providers.ssn.pt_BR import Provider as PtProvider, checksum as pt_checksum
+from faker.providers.ssn.pl_PL import (Provider as PlProvider, checksum as pl_checksum,
+                                       calculate_month as pl_calculate_mouth)
 
 
 class TestEtEE(unittest.TestCase):
@@ -48,7 +51,6 @@ class TestHrHR(unittest.TestCase):
 
 
 class TestPtBR(unittest.TestCase):
-
     def setUp(self):
         self.factory = Factory.create('pt_BR')
 
@@ -64,3 +66,37 @@ class TestPtBR(unittest.TestCase):
         for _ in range(100):
             self.assertTrue(re.search(r'\d{3}\.\d{3}\.\d{3}\-\d{2}', PtProvider.cpf()))
 
+
+class TestPlPL(unittest.TestCase):
+    """ Tests SSN in the pl_PL locale """
+
+    def setUp(self):
+        self.factory = Factory.create('pl_PL')
+
+    def test_ssn_checksum(self):
+        self.assertEqual(pl_checksum([0, 5, 2, 6, 2, 8, 1, 2, 3, 6]), 5)
+        self.assertEqual(pl_checksum([8, 5, 0, 5, 0, 8, 1, 5, 5, 8]), 7)
+        self.assertEqual(pl_checksum([4, 5, 1, 1, 1, 0, 0, 2, 4, 3]), 3)
+        self.assertEqual(pl_checksum([9, 1, 0, 7, 2, 6, 1, 4, 8, 7]), 3)
+        self.assertEqual(pl_checksum([8, 1, 1, 2, 1, 4, 1, 1, 8, 7]), 6)
+
+    def test_calculate_month(self):
+        self.assertEqual(pl_calculate_mouth(datetime.strptime('1 1 1900', '%m %d %Y')), 1)
+        self.assertEqual(pl_calculate_mouth(datetime.strptime('12 1 1900', '%m %d %Y')), 12)
+        self.assertEqual(pl_calculate_mouth(datetime.strptime('1 1 1999', '%m %d %Y')), 1)
+
+        self.assertEqual(pl_calculate_mouth(datetime.strptime('1 1 2000', '%m %d %Y')), 21)
+        self.assertEqual(pl_calculate_mouth(datetime.strptime('12 1 2000', '%m %d %Y')), 32)
+        self.assertEqual(pl_calculate_mouth(datetime.strptime('1 1 2099', '%m %d %Y')), 21)
+
+        self.assertEqual(pl_calculate_mouth(datetime.strptime('1 1 2100', '%m %d %Y')), 41)
+        self.assertEqual(pl_calculate_mouth(datetime.strptime('12 1 2100', '%m %d %Y')), 52)
+        self.assertEqual(pl_calculate_mouth(datetime.strptime('1 1 2199', '%m %d %Y')), 41)
+
+        self.assertEqual(pl_calculate_mouth(datetime.strptime('1 1 2200', '%m %d %Y')), 61)
+        self.assertEqual(pl_calculate_mouth(datetime.strptime('12 1 2200', '%m %d %Y')), 72)
+        self.assertEqual(pl_calculate_mouth(datetime.strptime('1 1 2299', '%m %d %Y')), 61)
+
+    def test_ssn(self):
+        for i in range(100):
+            self.assertTrue(re.search(r'^\d{11}$', PlProvider.ssn()))