Skip to content
Open
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
29 changes: 21 additions & 8 deletions codicefiscale.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
officially known as Italy's Codice Fiscale.

Copyright (C) 2009-2013 Emanuele Rocca
Copyright (C) 2014 Augusto Destrero (support for "Omocodie" [http://it.wikipedia.org/wiki/Omocodia])

Homepage: https://github.com/ema/pycodicefiscale

Expand All @@ -23,8 +24,8 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
"""

__version__ = '0.8'
__author__ = "Emanuele Rocca"
__version__ = '0.9'
__author__ = "Emanuele Rocca, Augusto Destrero"

import re
# pylint: disable=W0402
Expand All @@ -36,17 +37,24 @@
MONTHSCODE = [ 'A', 'B', 'C', 'D', 'E', 'H', 'L', 'M', 'P', 'R', 'S', 'T' ]

# pylint: disable=C0301
PATTERN = "^[A-Z]{6}[0-9]{2}([A-E]|[HLMPRST])[0-9]{2}[A-Z][0-9]([A-Z]|[0-9])[0-9][A-Z]$"
_pattern = re.compile("^[A-Z]{6}[0-9LMNPQRSTUV]{2}[ABCDEHLMPRST]{1}[0-9LMNPQRSTUV]{2}[A-Z]{1}[0-9LMNPQRSTUV]{3}[A-Z]{1}$")

def isvalid(code):
"""``isvalid(code) -> bool``

This function checks if the given fiscal code is syntactically valid.
This function checks if the given fiscal code is valid.

eg: isvalid('RCCMNL83S18D969H') -> True
isvalid('RCCMNL83S18D969') -> False
"""
return isinstance(code, basestring) and re.match(PATTERN, code) is not None
if not isinstance(code, basestring):
return False
if len(code) != 16:
return False
code = code.upper()
if _pattern.match(code) is None:
return False
return (control_code(code[0:15]) == code[15])

# Fiscal code calculation
def __common_triplet(input_string, consonants, vowels):
Expand Down Expand Up @@ -186,11 +194,16 @@ def get_birthday(code):
"""
assert isvalid(code)

day = int(code[9:11])
day = day < 32 and day or day - 40
day_year_charmap = {}
for idx, char in enumerate(string.digits):
day_year_charmap[char] = idx
for idx, char in enumerate('LMNPQRSTUV'):
day_year_charmap[char] = idx

day = day_year_charmap[code[9]] * 10 + day_year_charmap[code[10]]
day = day < 32 and day or day - 40
month = MONTHSCODE.index(code[8]) + 1
year = int(code[6:8])
year = day_year_charmap[code[6]] * 10 + day_year_charmap[code[7]]

return "%02d-%02d-%02d" % (day, month, year)

Expand Down
22 changes: 14 additions & 8 deletions tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,13 @@ def test_isvalid(self):
'RCCMNL83S18D969H',
'MRSMSR81D60Z611H',
'CNTCHR83T41D969D',
'XXXXXX77A01Z2P6B',
'FOXDRA26C24H872Y',
'MAILCU91A25F839D' )
'MAILCU91A25F839D',
'RSSMRA45C12F205C',
'RSSMRA45C12F20RX',
'RSSMRA45C12F2L5N',
'RSSMRA45C12F2LRI',
'RSSMRAQRCMNFNLRG',)

for cf in valid:
self.assertTrue(isvalid(cf))
Expand All @@ -34,7 +38,12 @@ def test_get_birthday(self):
'MRSMSR81D60Z611H': '20-04-81',
'CNTCHR83T41D969D': '01-12-83',
'FOXDRA26C24H872Y': '24-03-26',
'MAILCU91A25F839D': '25-01-91'
'MAILCU91A25F839D': '25-01-91',
'RSSMRA45C12F205C': '12-03-45',
'RSSMRA45C12F20RX': '12-03-45',
'RSSMRA45C12F2L5N': '12-03-45',
'RSSMRA45C12F2LRI': '12-03-45',
'RSSMRAQRCMNFNLRG': '12-03-45',
}

for cf, expected in inputs.items():
Expand Down Expand Up @@ -142,13 +151,10 @@ def test_01_locale_bug(self):
actual = build("mario", "rossi", datetime.datetime(1991, 1, 25), "M", "G693")
self.assertEquals(expected, actual)

def test_02_no_first_name_bug(self):
self.assertTrue(isvalid("XXXXXX77A01Z2P6B"))

def test_03_get_birthday_format(self):
def test_02_get_birthday_format(self):
self.assertEquals('02-08-23', get_birthday('MRTNTN23M02D969P'))

def test_04_unicode_handling_isvalid(self):
def test_03_unicode_handling_isvalid(self):
self.assertTrue(isvalid('MRTNTN23M02D969P'))
self.assertTrue(isvalid(u'MRTNTN23M02D969P'))

Expand Down