Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions faker/providers/ssn/ar_DZ/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from .. import Provider as SsnProvider


class Provider(SsnProvider):
"""
Sources:

- https://fr.wikipedia.org/wiki/Num%C3%A9ro_d%27identification_national_(Alg%C3%A9rie)
- https://github.com/itshakim213/dz-nin-checker
"""

def _control_key(self, base: str) -> str:
"""Compute the 2-digit control key via modified Luhn over 16 digits."""
total, alternate = 0, False
for i in range(len(base) - 1, -1, -1):
d = int(base[i]) * (2 if alternate else 1)
total += d - 9 if d > 9 else d
alternate = not alternate
remainder = total % 10
return f"{(0 if remainder == 0 else 10 - remainder):02d}"

def ssn(self) -> str:
"""Generate an Algerian National Identification Number (NIN).

Structure (18 digits):

- 1 digit – nationality: ``1`` = Algerian, ``2`` = dual nationality
- 1 digit – sex: ``0`` = male, ``1`` = female
- 3 digits – last three digits of the birth-registration year
- 4 digits – commune code (wilaya 01–58 + commune 01–20)
- 5 digits – birth-certificate act number
- 2 digits – annual register serial number
- 2 digits – control key (modified Luhn)
"""
nationality = self.random_element(("1", "2"))
sex = self.random_element(("0", "1"))
year_code = f"{self.random_int(min=1950, max=2006) % 1000:03d}"
wilaya = self.random_int(min=1, max=58)
commune_code = f"{wilaya:02d}{self.random_int(min=1, max=20):02d}"
act_code = f"{self.random_int(min=1, max=99999):05d}"
register_code = f"{self.random_int(min=1, max=99):02d}"
base = nationality + sex + year_code + commune_code + act_code + register_code
return base + self._control_key(base)
5 changes: 5 additions & 0 deletions faker/providers/ssn/fr_DZ/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from ..ar_DZ import Provider as ArDzSsnProvider


class Provider(ArDzSsnProvider):
pass
31 changes: 31 additions & 0 deletions tests/providers/test_ssn.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,31 @@
from faker.utils.checksums import luhn_checksum


class TestArDz(unittest.TestCase):
def setUp(self):
self.fake = Faker("ar_DZ")
Faker.seed(0)

def test_ssn(self):
pattern = re.compile(r"[12][01]\d{3}(0[1-9]|[1-4]\d|5[0-8])\d{2}\d{5}\d{2}\d{2}")
for _ in range(100):
ssn = self.fake.ssn()
assert len(ssn) == 18
assert pattern.fullmatch(ssn) is not None

def test_ssn_control_key(self):
for _ in range(100):
ssn = self.fake.ssn()
base = ssn[:16]
total, alternate = 0, False
for i in range(len(base) - 1, -1, -1):
d = int(base[i]) * (2 if alternate else 1)
total += d - 9 if d > 9 else d
alternate = not alternate
remainder = total % 10
assert f"{(0 if remainder == 0 else 10 - remainder):02d}" == ssn[16:]


class TestAzAz(unittest.TestCase):
num_sample_runs = 10

Expand Down Expand Up @@ -851,6 +876,12 @@ def test_checksum(self, digits, expected):
assert result == expected


class TestFrDz(TestArDz):
def setUp(self):
self.fake = Faker("fr_DZ")
Faker.seed(0)


class TestFrFR(unittest.TestCase):
def setUp(self):
self.fake = Faker("fr_FR")
Expand Down
Loading