Skip to content

Commit

Permalink
Merge branch 'master' of github.com:personnummer/python
Browse files Browse the repository at this point in the history
* 'master' of github.com:personnummer/python:
  Add get age and is coordination number tests
  Small fixes
  Remove include_coordination_number
  Raise exception in constructor and use only get parts once
  • Loading branch information
frozzare committed Apr 17, 2020
2 parents f05049b + eb339d8 commit b5ae0ba
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 68 deletions.
105 changes: 37 additions & 68 deletions personnummer/personnummer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import datetime
import math
import numbers
import re

string_types = str
Expand All @@ -22,11 +21,12 @@ def __init__(self, ssn, options=None):

if options is None:
options = {}
self.ssn = str(ssn)

self.options = options
self.parts = self.get_parts(ssn)

if self.valid() is False:
raise PersonnummerException(self.ssn + ' Not a valid Swedish social security number!')
raise PersonnummerException(str(ssn) + ' Not a valid Swedish social security number!')

def format(self, long_format=False):
"""
Expand All @@ -43,44 +43,29 @@ def format(self, long_format=False):
:return:
"""

if not self.valid():
raise ValueError(
'The social security number "{}" is not valid '
'and cannot be formatted.'.format(self.ssn)
)

if long_format:
ssn_format = '{century}{year}{month}{day}{num}{check}'
else:
ssn_format = '{year}{month}{day}{sep}{num}{check}'

parts = self.get_parts()
return ssn_format.format(**parts)
return ssn_format.format(**self.parts)

def get_age(self, include_coordination_number=True):
def get_age(self):
"""
Get the age of a person from a Swedish social security number
:param include_coordination_number: Set to False in order to exclude
coordination number (Samordningsnummer) from being considered as valid
:rtype: int
:return:
"""
if not self.valid(include_coordination_number=include_coordination_number):
raise PersonnummerException(
'The social security number "{}" is not valid '
'and cannot extra the age from it.'.format(self.ssn)
)
today = get_current_datetime()

parts = self.get_parts()
year = int('{century}{year}'.format(
century=parts['century'],
year=parts['year'])
century=self.parts['century'],
year=self.parts['year'])
)
month = int(parts['month'])
day = int(parts['day'])
if include_coordination_number and day > 60:
month = int(self.parts['month'])
day = int(self.parts['day'])
if self.is_coordination_number():
day -= 60

return today.year - year - ((today.month, today.day) < (month, day))
Expand All @@ -92,56 +77,17 @@ def is_female(self):
return True

def is_male(self):
gender_digit = self.get_parts()['num']
gender_digit = self.parts['num']

if int(gender_digit) % 2 == 0:
return False

return True

def is_coordination_number(self):
parts = self.get_parts()

return test_date(int(parts['year']), int(parts['month']), int(parts['day']))

def valid(self, include_coordination_number=True):
"""
Validate a Swedish social security number
:param include_coordination_number: Set to False in order to exclude
coordination number (Samordningsnummer) from validation
:type include_coordination_number: bool
:rtype: bool
:return:
"""

if isinstance(self.ssn, string_types) is False and isinstance(self.ssn, numbers.Integral) is False:
return False

try:
parts = self.get_parts()
except ValueError:
return False

year = parts['year']
month = parts['month']
day = parts['day']
num = parts['num']
check = parts['check']

if len(check) == 0:
return False

is_valid = luhn(year + month + day + num) == int(check)

if is_valid and test_date(int(year), int(month), int(day)):
return True

if not include_coordination_number:
return False

return is_valid and test_date(int(year), int(month), int(day) - 60)
return test_date(int(self.parts['year']), int(self.parts['month']), int(self.parts['day']) - 60)

def get_parts(self):
def get_parts(self, ssn):
"""
Get different parts of a Swedish social security number
:rtype: dict
Expand All @@ -150,7 +96,7 @@ def get_parts(self):
'century', 'year', 'month', 'day', 'sep', 'num', 'check'
"""
reg = r"^(\d{2}){0,1}(\d{2})(\d{2})(\d{2})([\-|\+]{0,1})?(\d{3})(\d{0,1})$"
match = re.match(reg, str(self.ssn))
match = re.match(reg, str(ssn))

if not match:
raise PersonnummerException(
Expand Down Expand Up @@ -188,6 +134,29 @@ def get_parts(self):
'check': check
}

def valid(self):
"""
Validate a Swedish social security number
:rtype: bool
:return:
"""

year = self.parts['year']
month = self.parts['month']
day = self.parts['day']
num = self.parts['num']
check = self.parts['check']

if len(check) == 0:
return False

is_valid = luhn(year + month + day + num) == int(check)

if is_valid and test_date(int(year), int(month), int(day)):
return True

return is_valid and test_date(int(year), int(month), int(day) - 60)


def luhn(data):
"""
Expand Down
18 changes: 18 additions & 0 deletions personnummer/tests/test_personnummer.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,22 @@ def test_is_female(self):
tmp = personnummer.parse(datum['long_format'])
self.assertEqual(tmp.is_female(), datum['isFemale'])

def test_get_age(self):
self.assertEqual(34, personnummer.parse('198507099805').get_age())
self.assertEqual(34, personnummer.parse('198507099813').get_age())
self.assertEqual(54, personnummer.parse('196411139808').get_age())
self.assertEqual(106, personnummer.parse('19121212+1212').get_age())

def test_coordination_numbers(self):
p1 = personnummer.parse('198507699810')
self.assertEqual(34, p1.get_age())
self.assertEqual(True, p1.is_coordination_number())

p2 = personnummer.parse('198507699802')
self.assertEqual(34, p2.get_age())
self.assertEqual(True, p2.is_coordination_number())

p3 = personnummer.parse('198507099805')
self.assertEqual(34, p3.get_age())
self.assertEqual(False, p3.is_coordination_number())

0 comments on commit b5ae0ba

Please sign in to comment.