Switch to python3

This commit is contained in:
j 2014-09-30 18:15:32 +02:00
commit 9ba4b6a91a
5286 changed files with 677347 additions and 576888 deletions

View file

@ -0,0 +1,106 @@
# __init__.py - main module
# coding: utf-8
#
# Copyright (C) 2010, 2011, 2012, 2013 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
"""Parse, validate and reformat standard numbers and codes.
This library offers functions for parsing, validating and reformatting
standard numbers and codes in various formats.
Currently this package supports the following formats:
* at.uid: UID (Umsatzsteuer-Identifikationsnummer, Austrian VAT number)
* be.vat: BTW, TVA, NWSt (Belgian VAT number)
* bg.egn: EGN (ЕГН, Единен граждански номер, Bulgarian personal identity codes)
* bg.pnf: PNF (ЛНЧ, Личен номер на чужденец, Bulgarian number of a foreigner)
* bg.vat: VAT (Идентификационен номер по ДДС, Bulgarian VAT number)
* br.cpf: CPF (Cadastro de Pessoas Físicas, Brazillian national identifier)
* cy.vat: Αριθμός Εγγραφής Φ.Π.Α. (Cypriot VAT number)
* cz.dic: DIČ (Daňové identifikační číslo, Czech VAT number)
* cz.rc: (Rodné číslo, the Czech birth number)
* de.vat: Ust ID Nr. (Umsatzsteur Identifikationnummer, German VAT number)
* dk.cpr: CPR (personnummer, the Danish citizen number)
* dk.cvr: CVR (Momsregistreringsnummer, Danish VAT number)
* ean: EAN (International Article Number)
* ee.kmkr: KMKR (Käibemaksukohuslase, Estonian VAT number)
* es.cif: CIF (Certificado de Identificación Fiscal, Spanish company tax number)
* es.dni: DNI (Documento nacional de identidad, Spanish personal identity codes)
* es.nie: NIE (Número de Identificación de Extranjeros, Spanish foreigner number)
* es.nif: NIF (Número de Identificación Fiscal, Spanish VAT number)
* eu.vat: VAT (European Union VAT number)
* fi.alv: ALV nro (Arvonlisäveronumero, Finnish VAT number)
* fi.hetu: HETU (Henkilötunnus, Finnish personal identity code)
* fr.siren: SIREN (a French company identification number)
* fr.tva: n° TVA (taxe sur la valeur ajoutée, French VAT number)
* gb.vat: VAT (United Kingdom (and Isle of Man) VAT registration number)
* gr.vat: FPA, ΦΠΑ (Foros Prostithemenis Aksias, the Greek VAT number)
* grid: GRid (Global Release Identifier)
* hr.oib: OIB (Osobni identifikacijski broj, Croatian identification number)
* hu.anum: ANUM (Közösségi adószám, Hungarian VAT number)
* iban: IBAN (International Bank Account Number)
* ie.pps: PPS No (Personal Public Service Number, Irish personal number)
* ie.vat: VAT (Irish VAT number)
* imei: IMEI (International Mobile Equipment Identity)
* imsi: IMSI (International Mobile Subscriber Identity)
* isan: ISAN (International Standard Audiovisual Number)
* isbn: ISBN (International Standard Book Number)
* isil: ISIL (International Standard Identifier for Libraries)
* ismn: ISMN (International Standard Music Number)
* issn: ISSN (International Standard Serial Number)
* it.iva: Partita IVA (Italian VAT number)
* lt.pvm: PVM (Pridėtinės vertės mokestis mokėtojo kodas, Lithuanian VAT number)
* lu.tva: TVA (taxe sur la valeur ajoutée, Luxembourgian VAT number)
* lv.pvn: PVN (Pievienotās vērtības nodokļa, Latvian VAT number)
* meid: MEID (Mobile Equipment Identifier)
* mt.vat: VAT (Maltese VAT number)
* my.nric: NRIC No. (Malaysian National Registration Identity Card Number)
* nl.brin: Brin number (Dutch number for schools)
* nl.bsn: BSN (Burgerservicenummer, Dutch national identification number)
* nl.btw: BTW-nummer (Omzetbelastingnummer, the Dutch VAT number)
* nl.onderwijsnummer: Onderwijsnummer (Dutch student school number)
* nl.postcode: Postcode (Dutch postal code)
* pl.nip: NIP (Numer Identyfikacji Podatkowej, Polish VAT number)
* pt.nif: NIF (Número de identificação fiscal, Portuguese VAT number)
* ro.cf: CF (Cod de înregistrare în scopuri de TVA, Romanian VAT number)
* ro.cnp: CNP (Cod Numeric Personal, Romanian Numerical Personal Code)
* se.vat: VAT (Moms, Mervärdesskatt, Swedish VAT number)
* si.ddv: ID za DDV (Davčna številka, Slovenian VAT number)
* sk.dph: DPH ( pre daň z pridanej hodnoty, Slovak VAT number)
* sk.rc: (Rodné číslo, the Slovak birth number)
* us.atin: ATIN (U.S. Adoption Taxpayer Identification Number)
* us.ein: EIN (U.S. Employer Identification Number)
* us.itin: ITIN (U.S. Individual Taxpayer Identification Number)
* us.ptin: PTIN (U.S. Preparer Tax Identification Number)
* us.ssn: SSN (U.S. Social Security Number)
* us.tin: TIN (U.S. Taxpayer Identification Number)
Furthermore a number of generic check digit algorithms are available:
* iso7064.mod_11_10: The ISO 7064 Mod 11, 10 algorithm
* iso7064.mod_11_2: The ISO 7064 Mod 11, 2 algorithm
* iso7064.mod_37_2: The ISO 7064 Mod 37, 2 algorithm
* iso7064.mod_37_36: The ISO 7064 Mod 37, 36 algorithm
* iso7064.mod_97_10: The ISO 7064 Mod 97, 10 algorithm
* luhn: The Luhn and Luhn mod N algorithms
* verhoeff: The Verhoeff algorithm
"""
# the version number of the library
__version__ = '0.9'

View file

@ -0,0 +1,24 @@
# __init__.py - collection of Austrian numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of Austrian numbers."""
# provide vat as an alias
from stdnum.at import uid as vat

View file

@ -0,0 +1,74 @@
# vat.py - functions for handling Austrian VAT numbers
#
# Copyright (C) 2012, 2013 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
"""UID (Umsatzsteuer-Identifikationsnummer, Austrian VAT number).
The Austrian UID is a 9-digit number that starts with a U (optionally
preceded with AT). The last digit is a check digit.
>>> validate('AT U13585627')
'U13585627'
>>> calc_check_digit('U1358562')
'7'
>>> validate('U13585626') # incorrect check digit
Traceback (most recent call last):
...
InvalidChecksum: ...
"""
from stdnum import luhn
from stdnum.exceptions import *
from stdnum.util import clean
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' -./').upper().strip()
if number.startswith('AT'):
number = number[2:]
return number
def calc_check_digit(number):
"""Calculate the check digit. The number passed should not have the
check digit included."""
return str((6 - luhn.checksum(number[1:])) % 10)
def validate(number):
"""Checks to see if the number provided is a valid VAT number. This checks
the length, formatting and check digit."""
number = compact(number)
if number[:1] != 'U' or not number[1:].isdigit():
raise InvalidFormat()
if len(number) != 9:
raise InvalidLength()
if calc_check_digit(number[:-1]) != number[-1]:
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid VAT number. This checks
the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,21 @@
# __init__.py - collection of Belgian numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of Belgian numbers."""

View file

@ -0,0 +1,75 @@
# vat.py - functions for handling Belgian VAT numbers
#
# Copyright (C) 2012, 2013 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
"""BTW, TVA, NWSt (Belgian VAT number).
>>> compact('BE403019261')
'0403019261'
>>> compact('(0)403019261')
'0403019261'
>>> validate('BE 428759497')
'0428759497'
>>> validate('BE431150351')
Traceback (most recent call last):
...
InvalidChecksum: ...
"""
from stdnum.exceptions import *
from stdnum.util import clean
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' -./').upper().strip()
if number.startswith('BE'):
number = number[2:]
if number.startswith('(0)'):
number = '0' + number[3:]
if len(number) == 9:
number = '0' + number # old format had 9 digits
return number
def checksum(number):
"""Calculate the checksum."""
return (int(number[:-2]) + int(number[-2:])) % 97
def validate(number):
"""Checks to see if the number provided is a valid VAT number. This checks
the length, formatting and check digit."""
number = compact(number)
if not number.isdigit():
raise InvalidFormat()
if len(number) != 10:
raise InvalidLength()
if checksum(number) != 0:
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid VAT number. This checks
the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,21 @@
# __init__.py - collection of Bulgarian numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of Bulgarian numbers."""

View file

@ -0,0 +1,105 @@
# egn.py - functions for handling Bulgarian national identification numbers
# coding: utf-8
#
# Copyright (C) 2012, 2013 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
"""EGN (ЕГН, Единен граждански номер, Bulgarian personal identity codes).
It is a 10-digit number of which the first 6 digits denote the person's
birth date, the next three digits represent a birth order number from
which the person's gender can be determined and the last digit is a check
digit.
>>> compact('752316 926 3')
'7523169263'
>>> validate('8032056031')
'8032056031'
>>> get_birth_date('7542011030')
datetime.date(2075, 2, 1)
>>> validate('7552A10004') # invalid digit
Traceback (most recent call last):
...
InvalidFormat: ...
>>> validate('8019010008') # invalid date
Traceback (most recent call last):
...
InvalidComponent: ...
"""
import datetime
from stdnum.exceptions import *
from stdnum.util import clean
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, ' -.').upper().strip()
def calc_check_digit(number):
"""Calculate the check digit. The number passed should not have the
check digit included."""
weights = (2, 4, 8, 5, 10, 9, 7, 3, 6)
return str(sum(weights[i] * int(n) for i, n in enumerate(number)) % 11 % 10)
def get_birth_date(number):
"""Split the date parts from the number and return the birth date."""
year = int(number[0:2]) + 1900
month = int(number[2:4])
day = int(number[4:6])
if month > 40:
year += 100
month -= 40
elif month > 20:
year -= 100
month -= 20
try:
return datetime.date(year, month, day)
except ValueError:
raise InvalidComponent()
def validate(number):
"""Checks to see if the number provided is a valid national
identification number. This checks the length, formatting, embedded
date and check digit."""
number = compact(number)
if not number.isdigit():
raise InvalidFormat()
if len(number) != 10:
raise InvalidLength()
# check if birth date is valid
birth_date = get_birth_date(number)
# TODO: check that the birth date is not in the future
# check the check digit
if calc_check_digit(number[:-1]) != number[-1]:
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid national
identification number. This checks the length, formatting, embedded
date and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,76 @@
# pnf.py - functions for handling Bulgarian personal number of a foreigner
# coding: utf-8
#
# Copyright (C) 2012, 2013 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
"""PNF (ЛНЧ, Личен номер на чужденец, Bulgarian number of a foreigner).
The personal number of a foreigner is a 10-digit number where the last digit
is the result of a weighted checksum.
>>> validate('7111 042 925')
'7111042925'
>>> validate('7111042922') # invalid check digit
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> validate('71110A2922') # invalid digit
Traceback (most recent call last):
...
InvalidFormat: ...
"""
from stdnum.exceptions import *
from stdnum.util import clean
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, ' -.').upper().strip()
def calc_check_digit(number):
"""Calculate the check digit. The number passed should not have the
check digit included."""
weights = (21, 19, 17, 13, 11, 9, 7, 3, 1)
return str(sum(weights[i] * int(n) for i, n in enumerate(number)) % 10)
def validate(number):
"""Checks to see if the number provided is a valid national
identification number. This checks the length, formatting, embedded
date and check digit."""
number = compact(number)
if not number.isdigit():
raise InvalidFormat()
if len(number) != 10:
raise InvalidLength()
if calc_check_digit(number[:-1]) != number[-1]:
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid national
identification number. This checks the length, formatting, embedded
date and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,93 @@
# vat.py - functions for handling Bulgarian VAT numbers
# coding: utf-8
#
# Copyright (C) 2012, 2013 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
"""VAT (Идентификационен номер по ДДС, Bulgarian VAT number).
The Bulgarian VAT (Данък върху добавената стойност) number is either 9
(for legal entities) or 10 digits (for physical persons, foreigners and
others) long. Each type of number has it's own check digit algorithm.
>>> compact('BG 175 074 752')
'175074752'
>>> validate('175074752')
'175074752'
>>> validate('175074751') # invalid check digit
Traceback (most recent call last):
...
InvalidChecksum: ...
"""
from stdnum.bg import egn, pnf
from stdnum.exceptions import *
from stdnum.util import clean
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' -.').upper().strip()
if number.startswith('BG'):
number = number[2:]
return number
def calc_check_digit_legal(number):
"""Calculate the check digit for legal entities. The number passed
should not have the check digit included."""
check = sum((i + 1) * int(n) for i, n in enumerate(number)) % 11
if check == 10:
check = sum((i + 3) * int(n) for i, n in enumerate(number)) % 11
return str(check % 10)
def calc_check_digit_other(number):
"""Calculate the check digit for others. The number passed should not
have the check digit included."""
weights = (4, 3, 2, 7, 6, 5, 4, 3, 2)
return str((11 - sum(weights[i] * int(n) for i, n in enumerate(number))) % 11)
def validate(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
number = compact(number)
if not number.isdigit():
raise InvalidFormat()
if len(number) == 9:
# 9 digit numbers are for legal entities
if number[-1] != calc_check_digit_legal(number[:-1]):
raise InvalidChecksum()
elif len(number) == 10:
# 10 digit numbers are for physical persons, foreigners and others
if not egn.is_valid(number) and not pnf.is_valid(number) and \
number[-1] != calc_check_digit_other(number[:-1]):
raise InvalidChecksum()
else:
raise InvalidLength()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,21 @@
# __init__.py - collection of Brazillian numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of Brazillian numbers."""

View file

@ -0,0 +1,81 @@
# cpf.py - functions for handling CPF numbers
# coding: utf-8
#
# Copyright (C) 2011, 2012, 2013 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
"""CPF (Cadastro de Pessoas Físicas, Brazillian national identifier).
>>> validate('390.533.447-05')
'39053344705'
>>> validate('231.002.999-00')
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> validate('390.533.447=0') # invalid delimiter
Traceback (most recent call last):
...
InvalidFormat: ...
>>> format('23100299900')
'231.002.999-00'
"""
from stdnum.exceptions import *
from stdnum.util import clean
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_digits(number):
"""Calculate the check digits for the number."""
d1 = sum((10 - i) * int(number[i]) for i in range(9))
d1 = (11 - d1) % 11 % 10
d2 = sum((11 - i) * int(number[i]) for i in range(9)) + 2 * d1
d2 = (11 - d2) % 11 % 10
return '%d%d' % (d1, d2)
def validate(number):
"""Checks to see if the number provided is a valid CPF. This checks
the length and whether the check digit is correct."""
number = compact(number)
if not number.isdigit() or int(number) <= 0:
raise InvalidFormat()
if len(number) != 11:
raise InvalidLength()
if _calc_check_digits(number) != number[-2:]:
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid CPF. This checks
the length and whether the check digit is correct."""
try:
return bool(validate(number))
except ValidationError:
return False
def format(number):
"""Reformat the passed number to the standard format."""
number = compact(number)
return number[:3] + '.' + number[3:6] + '.' + number[6:-2] + '-' + number[-2:]

View file

@ -0,0 +1,21 @@
# __init__.py - collection of Cypriot numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of Cypriot numbers."""

View file

@ -0,0 +1,83 @@
# vat.py - functions for handling Cypriot VAT numbers
# coding: utf-8
#
# Copyright (C) 2012, 2013 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
"""Αριθμός Εγγραφής Φ.Π.Α. (Cypriot VAT number).
The Cypriot Αριθμός Εγγραφής Φ.Π.Α. (VAT) number consists of 9 digits
where the last one is a is a letter and functions as a check digit.
>>> compact('CY-10259033P')
'10259033P'
>>> validate('CY-10259033P ')
'10259033P'
>>> validate('CY-10259033Z') # invalid check digit
Traceback (most recent call last):
...
InvalidChecksum: ...
"""
from stdnum.exceptions import *
from stdnum.util import clean
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' -').upper().strip()
if number.startswith('CY'):
number = number[2:]
return number
def calc_check_digit(number):
"""Calculate the check digit. The number passed should not have the
check digit included."""
translation = {
'0': 1, '1': 0, '2': 5, '3': 7, '4': 9,
'5': 13, '6': 15, '7': 17, '8': 19, '9': 21,
}
return 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[(
sum(translation[x] for x in number[::2]) +
sum(int(x) for x in number[1::2])
) % 26]
def validate(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
number = compact(number)
if not number[:-1].isdigit():
raise InvalidFormat()
if len(number) != 9:
raise InvalidLength()
if number[0:2] == '12':
raise InvalidComponent()
if number[-1] != calc_check_digit(number[:-1]):
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,24 @@
# __init__.py - collection of Czech numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of Czech numbers."""
# provide vat as an alias
from stdnum.cz import dic as vat

View file

@ -0,0 +1,101 @@
# dic.py - functions for handling Czech VAT numbers
# coding: utf-8
#
# Copyright (C) 2012, 2013 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
"""DIČ (Daňové identifikační číslo, Czech VAT number).
The number is an 8, 9 or 10 digit code that includes a check digit and is
used to uniquely identify taxpayers for VAT (DPH in Czech). The number can
refer to legal entities (8 digit numbers), individuals with a (the 9 or
10 digit Czech birth number) or individuals without a (9 digit numbers
that begin with a 6).
>>> compact('CZ 25123891')
'25123891'
>>> validate('25123891') # legal entity
'25123891'
>>> validate('25123890') # incorrect check digit
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> validate('7103192745') # RČ
'7103192745'
>>> validate('640903926') # special case
'640903926'
"""
from stdnum.cz import rc
from stdnum.exceptions import *
from stdnum.util import clean
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' /').upper().strip()
if number.startswith('CZ'):
number = number[2:]
return number
def calc_check_digit_legal(number):
"""Calculate the check digit for 8 digit legal entities. The number
passed should not have the check digit included."""
check = (11 - sum((8 - i) * int(n) for i, n in enumerate(number))) % 11
return str((check or 1) % 10)
def calc_check_digit_special(number):
"""Calculate the check digit for special cases. The number passed
should not have the first and last digits included."""
check = (11 - sum((8 - i) * int(n) for i, n in enumerate(number))) % 11
return str(9 - check % 10)
def validate(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
number = compact(number)
if not number.isdigit():
raise InvalidFormat()
if len(number) == 8:
# legal entities
if number.startswith('9'):
raise InvalidComponent()
if calc_check_digit_legal(number[:-1]) != number[-1]:
raise InvalidChecksum()
elif len(number) == 9 and number.startswith('6'):
# special cases (skip first digit in calculation)
if calc_check_digit_special(number[1:-1]) != number[-1]:
raise InvalidChecksum()
elif len(number) in (9, 10):
# 9 or 10 digit individual
rc.validate(number)
else:
raise InvalidLength()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,117 @@
# rc.py - functions for handling Czech birth numbers
# coding: utf-8
#
# Copyright (C) 2012, 2013 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
"""RČ (Rodné číslo, the Czech birth number).
The birth number (, Rodné číslo) is the Czech national identifier. The
number can be 9 or 10 digits long. Numbers given out after January 1st
1954 should have 10 digits. The number includes the birth date of the
person and their gender.
This number is identical to the Slovak counterpart.
>>> validate('710319/2745')
'7103192745'
>>> validate('991231123')
'991231123'
>>> validate('7103192746') # invalid check digit
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> validate('1103492745') # invalid date
Traceback (most recent call last):
...
InvalidComponent: ...
>>> validate('590312/123') # 9 digit number in 1959
Traceback (most recent call last):
...
InvalidLength: ...
>>> format('7103192745')
'710319/2745'
"""
import datetime
from stdnum.exceptions import *
from stdnum.util import clean
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, ' /').upper().strip()
def get_birth_date(number):
"""Split the date parts from the number and return the birth date."""
year = 1900 + int(number[0:2])
# females have 50 added to the month value, 20 is added when the serial
# overflows (since 2004)
month = int(number[2:4]) % 50 % 20
day = int(number[4:6])
# 9 digit numbers were used until January 1st 1954
if len(number) == 9:
if year >= 1980:
year -= 100
if year > 1953:
raise InvalidLength('No 9 digit birth numbers after 1953.')
elif year < 1954:
year += 100
try:
return datetime.date(year, month, day)
except ValueError:
raise InvalidComponent()
def validate(number):
"""Checks to see if the number provided is a valid birth number. This
checks the length, formatting, embedded date and check digit."""
number = compact(number)
if not number.isdigit():
raise InvalidFormat()
if len(number) not in (9, 10):
raise InvalidLength()
# check if birth date is valid
birth_date = get_birth_date(number)
# TODO: check that the birth date is not in the future
# check the check digit (10 digit numbers only)
if len(number) == 10:
check = int(number[:-1]) % 11
# before 1985 the checksum could be 0 or 10
if birth_date < datetime.date(1985, 1, 1):
check = check % 10
if number[-1] != str(check):
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid birth number. This
checks the length, formatting, embedded date and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False
def format(number):
"""Reformat the passed number to the standard format."""
number = compact(number)
return number[:6] + '/' + number[6:]

View file

@ -0,0 +1,21 @@
# __init__.py - collection of German numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of German numbers."""

View file

@ -0,0 +1,67 @@
# vat.py - functions for handling German VAT numbers
#
# Copyright (C) 2012, 2013 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
"""Ust ID Nr. (Umsatzsteur Identifikationnummer, German VAT number).
The number is 10 digits long and uses the ISO 7064 Mod 11, 10 check digit
algorithm.
>>> compact('DE 136,695 976')
'136695976'
>>> validate('DE136695976')
'136695976'
>>> validate('136695978')
Traceback (most recent call last):
...
InvalidChecksum: ...
"""
from stdnum.exceptions import *
from stdnum.iso7064 import mod_11_10
from stdnum.util import clean
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' -./,').upper().strip()
if number.startswith('DE'):
number = number[2:]
return number
def validate(number):
"""Checks to see if the number provided is a valid VAT number. This checks
the length, formatting and check digit."""
number = compact(number)
if not number.isdigit() or number[0] == '0':
raise InvalidFormat()
if len(number) != 9:
raise InvalidLength()
mod_11_10.validate(number)
return number
def is_valid(number):
"""Checks to see if the number provided is a valid VAT number. This checks
the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,24 @@
# __init__.py - collection of Danish numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of Danish numbers."""
# provide vat as an alias
from stdnum.dk import cvr as vat

View file

@ -0,0 +1,108 @@
# cpr.py - functions for handling Danish CPR numbers
#
# Copyright (C) 2012, 2013 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
"""CPR (personnummer, the Danish citizen number).
The CPR is the national number to identify Danish citizens. The number
consists of 10 digits in the format DDMMYY-SSSS where the first part
represents the birth date and the second a sequence number. The first
digit of the sequence number indicates the century.
The numbers used to validate using a checksum but since the sequence
numbers ran out this was abandoned in 2007.
>>> validate('211062-5629')
'2110625629'
>>> checksum('2110625629')
0
>>> validate('511062-5629') # invalid date
Traceback (most recent call last):
...
InvalidComponent: ...
>>> get_birth_date('2110620629')
datetime.date(1962, 10, 21)
>>> get_birth_date('2110525629')
datetime.date(2052, 10, 21)
>>> format('2110625629')
'211062-5629'
"""
import datetime
from stdnum.exceptions import *
from stdnum.util import clean
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 checksum(number):
"""Calculate the checksum. Note that the checksum isn't actually used
any more. Valid numbers used to have a checksum of 0."""
weights = (4, 3, 2, 7, 6, 5, 4, 3, 2, 1)
return sum(weights[i] * int(n) for i, n in enumerate(number)) % 11
def get_birth_date(number):
"""Split the date parts from the number and return the birth date."""
day = int(number[0:2])
month = int(number[2:4])
year = int(number[4:6])
if number[6] in '5678' and year >= 58:
year += 1800
elif number[6] in '0123' or (number[6] in '49' and year >= 37):
year += 1900
else:
year += 2000
try:
return datetime.date(year, month, day)
except ValueError:
raise InvalidComponent()
def validate(number):
"""Checks to see if the number provided is a valid CPR number. This
checks the length, formatting, embedded date and check digit."""
number = compact(number)
if not number.isdigit():
raise InvalidFormat()
if len(number) != 10:
raise InvalidLength()
# check if birth date is valid
birth_date = get_birth_date(number)
# TODO: check that the birth date is not in the future
return number
def is_valid(number):
"""Checks to see if the number provided is a valid CPR number. This
checks the length, formatting, embedded date and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False
def format(number):
"""Reformat the passed number to the standard format."""
number = compact(number)
return '-'.join((number[:6], number[6:]))

View file

@ -0,0 +1,71 @@
# cvr.py - functions for handling Danish CVR numbers
#
# Copyright (C) 2012, 2013 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
"""CVR (Momsregistreringsnummer, Danish VAT number).
The CVR (Momsregistreringsnummer, VAT) is an 8 digit number with a
straightforward check mechanism.
>>> validate('DK 13585628')
'13585628'
>>> validate('DK 13585627')
Traceback (most recent call last):
...
InvalidChecksum: ...
"""
from stdnum.exceptions import *
from stdnum.util import clean
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' -.,/:').upper().strip()
if number.startswith('DK'):
number = number[2:]
return number
def checksum(number):
"""Calculate the checksum."""
weights = (2, 7, 6, 5, 4, 3, 2, 1)
return sum(weights[i] * int(n) for i, n in enumerate(number)) % 11
def validate(number):
"""Checks to see if the number provided is a valid VAT number. This checks
the length, formatting and check digit."""
number = compact(number)
if not number.isdigit() or number[0] == '0':
raise InvalidFormat()
if len(number) != 8:
raise InvalidLength()
if checksum(number) != 0:
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid VAT number. This checks
the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,69 @@
# ean.py - functions for handling EANs
#
# Copyright (C) 2011, 2012, 2013 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
"""EAN (International Article Number).
Module for handling EAN (International Article Number) codes. This
module handles numbers EAN-13, EAN-8 and UPC (12-digit) format.
>>> validate('73513537')
'73513537'
>>> validate('978-0-471-11709-4') # EAN-13 format
'9780471117094'
"""
from stdnum.exceptions import *
from stdnum.util import clean
def compact(number):
"""Convert the EAN 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 EAN check digit for 13-digit numbers. The number passed
should not have the check bit included."""
return str((10 - sum((3 - 2 * (i % 2)) * int(n)
for i, n in enumerate(reversed(number)))) % 10)
def validate(number):
"""Checks to see if the number provided is a valid EAN-13. This checks
the length and the check bit but does not check whether a known GS1
Prefix and company identifier are referenced."""
number = compact(number)
if not number.isdigit():
raise InvalidFormat()
if len(number) not in (13, 12, 8):
raise InvalidLength()
if calc_check_digit(number[:-1]) != number[-1]:
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid EAN-13. This checks
the length and the check bit but does not check whether a known GS1
Prefix and company identifier are referenced."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,24 @@
# __init__.py - collection of Estonian numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of Estonian numbers."""
# provide vat as an alias
from stdnum.ee import kmkr as vat

View file

@ -0,0 +1,71 @@
# kmkr.py - functions for handling Estonian VAT numbers
# coding: utf-8
#
# Copyright (C) 2012, 2013 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
"""KMKR (Käibemaksukohuslase, Estonian VAT number).
>>> compact('EE 100 931 558')
'100931558'
>>> validate('100594102')
'100594102'
>>> validate('100594103') # incorrect check digit
Traceback (most recent call last):
...
InvalidChecksum: ...
"""
from stdnum.exceptions import *
from stdnum.util import clean
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' ').upper().strip()
if number.startswith('EE'):
number = number[2:]
return number
def checksum(number):
"""Calculate the checksum."""
weights = (3, 7, 1, 3, 7, 1, 3, 7, 1)
return sum(weights[i] * int(n) for i, n in enumerate(number)) % 10
def validate(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
number = compact(number)
if not number.isdigit():
raise InvalidFormat()
if len(number) != 9:
raise InvalidLength()
if checksum(number) != 0:
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,24 @@
# __init__.py - collection of Spanish numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of Spanish numbers."""
# provide vat as an alias
from stdnum.es import nif as vat

View file

@ -0,0 +1,108 @@
# cif.py - functions for handling Spanish fiscal numbers
# coding: utf-8
#
# Copyright (C) 2012, 2013 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
"""CIF (Certificado de Identificación Fiscal, Spanish company tax number).
The CIF is a tax identification number for legal entities. It has 9 digits
where the first digit is a letter (denoting the type of entity) and the
last is a check digit (which may also be a letter).
>>> validate('J99216582')
'J99216582'
>>> validate('J99216583') # invalid check digit
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> validate('J992165831') # too long
Traceback (most recent call last):
...
InvalidLength: ...
>>> validate('M-1234567-L')
'M1234567L'
>>> validate('O-1234567-L') # invalid first character
Traceback (most recent call last):
...
InvalidFormat: ...
>>> split('A13 585 625')
('A', '13', '58562', '5')
"""
from stdnum import luhn
from stdnum.es import dni
from stdnum.exceptions import *
__all__ = ['compact', 'validate', 'is_valid', 'split']
# use the same compact function as DNI
compact = dni.compact
def calc_check_digits(number):
"""Calculate the check digits for the specified number. The number
passed should not have the check digit included. This function returns
both the number and character check digit candidates."""
check = luhn.calc_check_digit(number[1:])
return check + 'JABCDEFGHI'[int(check)]
def validate(number):
"""Checks to see if the number provided is a valid DNI number. This
checks the length, formatting and check digit."""
number = compact(number)
if not number[1:-1].isdigit():
raise InvalidFormat()
if len(number) != 9:
raise InvalidLength()
if number[0] in 'KLM':
# K: Spanish younger than 14 year old
# L: Spanish living outside Spain without DNI
# M: granted the tax to foreigners who have no NIE
# these use the old checkdigit algorithm (the DNI one)
if number[-1] != dni.calc_check_digit(number[1:-1]):
raise InvalidChecksum()
elif number[0] in 'ABCDEFGHJNPQRSUVW':
# there seems to be conflicting information on which organisation types
# should have which type of check digit (alphabetic or numeric) so
# we support either here
if number[-1] not in calc_check_digits(number[:-1]):
raise InvalidChecksum()
else:
# anything else is invalid
raise InvalidFormat()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid DNI number. This
checks the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False
def split(number):
"""Split the provided number into a letter to define the type of
organisation, two digits that specify a province, a 5 digit sequence
number within the province and a check digit."""
number = compact(number)
return number[0], number[1:3], number[3:8], number[8:]

View file

@ -0,0 +1,76 @@
# dni.py - functions for handling Spanish personal identity codes
# coding: utf-8
#
# Copyright (C) 2012, 2013 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
"""DNI (Documento nacional de identidad, Spanish personal identity codes).
The DNI is a 9 digit number used to identify Spanish citizens. The last
digit is a checksum letter.
Foreign nationals, since 2010 are issued an NIE (Número de Identificación
de Extranjeros, Foreigner's Identity Number) instead.
>>> validate('54362315-K')
'54362315K'
>>> validate('54362315Z') # invalid check digit
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> validate('54362315') # digit missing
Traceback (most recent call last):
...
InvalidLength: ...
"""
from stdnum.exceptions import *
from stdnum.util import clean
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, ' -').upper().strip()
def calc_check_digit(number):
"""Calculate the check digit. The number passed should not have the
check digit included."""
return 'TRWAGMYFPDXBNJZSQVHLCKE'[int(number) % 23]
def validate(number):
"""Checks to see if the number provided is a valid DNI number. This
checks the length, formatting and check digit."""
number = compact(number)
if not number[:-1].isdigit():
raise InvalidFormat()
if len(number) != 9:
raise InvalidLength()
if calc_check_digit(number[:-1]) != number[-1]:
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid DNI number. This
checks the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,77 @@
# nie.py - functions for handling Spanish foreigner identity codes
# coding: utf-8
#
# Copyright (C) 2012, 2013 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
"""NIE (Número de Identificación de Extranjeros, Spanish foreigner number).
The NIE is an identification number for foreigners. It is a 9 digit number
where the first digit is either X, Y or Z and last digit is a checksum
letter.
>>> validate('x-2482300w')
'X2482300W'
>>> validate('x-2482300a') # invalid check digit
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> validate('X2482300') # digit missing
Traceback (most recent call last):
...
InvalidLength: ...
"""
from stdnum.es import dni
from stdnum.exceptions import *
__all__ = ['compact', 'is_valid']
# use the same compact function as DNI
compact = dni.compact
def calc_check_digit(number):
"""Calculate the check digit. The number passed should not have the
check digit included."""
# replace XYZ with 012
number = str('XYZ'.index(number[0])) + number[1:]
return dni.calc_check_digit(number)
def validate(number):
"""Checks to see if the number provided is a valid NIE. This checks
the length, formatting and check digit."""
number = compact(number)
if not number[1:-1].isdigit() or number[:1] not in 'XYZ':
raise InvalidFormat()
if len(number) != 9:
raise InvalidLength()
if calc_check_digit(number[:-1]) != number[-1]:
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid NIE. This checks
the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,85 @@
# nif.py - functions for handling Spanish NIF (VAT) numbers
# coding: utf-8
#
# Copyright (C) 2012, 2013 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
"""NIF (Número de Identificación Fiscal, Spanish VAT number).
The Spanish VAT number is a 9-digit number where either the first, last
digits or both can be letters.
The number is either a DNI (Documento nacional de identidad, for
Spaniards), a NIE (Número de Identificación de Extranjeros, for
foreigners) or a CIF (Certificado de Identificación Fiscal, for legal
entities and others).
>>> compact('ES B-58378431')
'B58378431'
>>> validate('B64717838')
'B64717838'
>>> validate('B64717839') # invalid check digit
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> validate('54362315K') # resident
'54362315K'
>>> validate('X-5253868-R') # foreign person
'X5253868R'
"""
from stdnum.es import dni, nie, cif
from stdnum.exceptions import *
from stdnum.util import clean
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' -').upper().strip()
if number.startswith('ES'):
number = number[2:]
return number
def validate(number):
"""Checks to see if the number provided is a valid VAT number. This checks
the length, formatting and check digit."""
number = compact(number)
if not number[1:-1].isdigit():
raise InvalidFormat()
if len(number) != 9:
raise InvalidLength()
if number[0].isdigit():
# natural resident
dni.validate(number)
elif number[0] in 'XYZ':
# foreign natural person
nie.validate(number)
else:
# otherwise it has to be a valid CIF
cif.validate(number)
return number
def is_valid(number):
"""Checks to see if the number provided is a valid VAT number. This checks
the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,21 @@
# __init__.py - collection of European Union numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of European Union numbers."""

View file

@ -0,0 +1,130 @@
# vat.py - functions for handling European VAT numbers
# coding: utf-8
#
# Copyright (C) 2012, 2013 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
"""VAT (European Union VAT number).
The European Union VAT number consists of a 2 letter country code (ISO
3166-1, except Greece which uses EL) followed by a number that is
allocated per country.
The exact format of the numbers varies per country and a country-specific
check is performed on the number using the VAT module that is relevant for
that country.
>>> compact('ATU 57194903')
'ATU57194903'
>>> validate('BE697449992')
'BE0697449992'
>>> validate('FR 61 954 506 077')
'FR61954506077'
>>> guess_country('00449544B01')
['nl']
"""
from stdnum.exceptions import *
from stdnum.util import clean
country_codes = set([
'at', 'be', 'bg', 'cy', 'cz', 'de', 'dk', 'ee', 'es', 'fi', 'fr', 'gb',
'gr', 'hu', 'ie', 'it', 'lt', 'lu', 'lv', 'mt', 'nl', 'pl', 'pt', 'ro',
'se', 'si', 'sk'
])
"""The collection of country codes that are queried. Greece is listed with
a country code of gr while for VAT purposes el is used instead."""
_country_modules = dict()
vies_wsdl = 'http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl'
"""The WSDL URL of the VAT Information Exchange System (VIES)."""
# a cached version of the suds client for VIES
_vies_client = None
def _get_cc_module(cc):
"""Get the VAT number module based on the country code."""
# Greece uses a "wrong" country code
cc = cc.lower()
if cc == 'el':
cc = 'gr'
if cc not in country_codes:
return
if cc not in _country_modules:
# do `from stdnum.CC import vat` instead of `import stdnum.CC.vat`
# to handle the case where vat is an alias
_country_modules[cc] = __import__(
'stdnum.%s' % cc, globals(), locals(), ['vat']).vat
return _country_modules[cc]
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, '').upper().strip()
module = _get_cc_module(number[:2])
if not module:
raise InvalidComponent()
return number[:2] + module.compact(number[2:])
def validate(number):
"""Checks to see if the number provided is a valid VAT number. This
performs the country-specific check for the number."""
number = clean(number, '').upper().strip()
module = _get_cc_module(number[:2])
if not module:
raise InvalidComponent()
return number[:2] + module.validate(number[2:])
def is_valid(number):
"""Checks to see if the number provided is a valid VAT number. This
performs the country-specific check for the number."""
try:
return bool(validate(number))
except ValidationError:
return False
def guess_country(number):
"""Guess the country code based on the provided number. This checks the
provided number against each of the validation routines and returns
the list of countries for which it is valid. This returns lower case
codes and returns gr (not el) for Greece."""
return [cc
for cc in country_codes
if _get_cc_module(cc).is_valid(number)]
def check_vies(number): # pragma: no cover (no tests for this function)
"""Queries the online European Commission VAT Information Exchange
System (VIES) for validity of the provided number. Note that the
service has usage limitations (see the VIES website for details).
This returns a dict-like object."""
# this function isn't automatically tested because it would require
# network access for the tests and unnecessarily load the VIES website
number = compact(number)
global _vies_client
if not _vies_client:
from suds.client import Client
from urllib import getproxies
_vies_client = Client(vies_wsdl, proxy=getproxies())
return _vies_client.service.checkVat(number[:2], number[2:])

View file

@ -0,0 +1,66 @@
# exceptions.py - collection of stdnum exceptions
# coding: utf-8
#
# Copyright (C) 2013 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
"""Collection of exceptions.
The validation functions of stdnum should raise one of the below exceptions
when validation of the number fails.
"""
class ValidationError(Exception):
"""Top-level error for validating numbers.
This exception should normally not be raised, only subclasses of this
exception."""
def __str__(self):
return getattr(self, 'message', '')
class InvalidFormat(ValidationError):
"""Something is wrong with the format of the number.
This generally means characters or delimiters that are not allowed are
part of the number or required parts are missing."""
message = 'The number has an invalid format.'
class InvalidChecksum(ValidationError):
"""The number's internal checksum or check digit does not match."""
message = "The number's checksum or check digit is invalid."
class InvalidLength(InvalidFormat):
"""The length of the number is wrong."""
message = 'The number has an invalid length.'
class InvalidComponent(ValidationError):
"""One of the parts of the number has an invalid reference.
Some part of the number refers to some external entity like a country
code, a date or a predefined collection of values. The number contains
some invalid reference."""
message = 'One of the parts of the number are invalid or unknown.'

View file

@ -0,0 +1,24 @@
# __init__.py - collection of Finnish numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of Finnish numbers."""
# provide vat as an alias
from stdnum.fi import alv as vat

View file

@ -0,0 +1,71 @@
# vat.py - functions for handling Finnish VAT numbers
# coding: utf-8
#
# Copyright (C) 2012, 2013 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
"""ALV nro (Arvonlisäveronumero, Finnish VAT number).
The number is an 8-digit code with a weighted checksum.
>>> validate('FI 20774740')
'20774740'
>>> validate('FI 20774741') # invalid check digit
Traceback (most recent call last):
...
InvalidChecksum: ...
"""
from stdnum.exceptions import *
from stdnum.util import clean
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' -').upper().strip()
if number.startswith('FI'):
number = number[2:]
return number
def checksum(number):
"""Calculate the checksum."""
weights = (7, 9, 10, 5, 8, 4, 2, 1)
return sum(weights[i] * int(n) for i, n in enumerate(number)) % 11
def validate(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
number = compact(number)
if not number.isdigit():
raise InvalidFormat()
if len(number) != 8:
raise InvalidLength()
if checksum(number) != 0:
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,113 @@
# hetu.py - functions for handling Finnish personal identity codes
# coding: utf-8
#
# Copyright (C) 2011 Jussi Judin
# Copyright (C) 2012, 2013 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
"""HETU (Henkilötunnus, Finnish personal identity code).
Module for handling Finnish personal identity codes (HETU, Henkilötunnus).
See http://www.vaestorekisterikeskus.fi/default.aspx?id=45 for checksum
calculation details and http://tarkistusmerkit.teppovuori.fi/tarkmerk.htm#hetu1
for historical details.
>>> validate('131052-308T')
'131052-308T'
>>> validate('131052-308U')
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> validate('310252-308Y')
Traceback (most recent call last):
...
InvalidComponent: ...
>>> compact('131052a308t')
'131052A308T'
"""
import re
import datetime
from stdnum.exceptions import *
from stdnum.util import clean
_century_codes = {
'+': 1800,
'-': 1900,
'A': 2000,
}
# Finnish personal identity codes are composed of date part, century
# indicating sign, individual number and control character.
# ddmmyyciiiC
_hetu_re = re.compile(r'^(?P<day>[0123]\d)(?P<month>[01]\d)(?P<year>\d\d)'
r'(?P<century>[-+A])(?P<individual>\d\d\d)'
r'(?P<control>[0-9ABCDEFHJKLMNPRSTUVWXY])$')
def compact(number):
"""Convert the HETU to the minimal representation. This strips
surrounding whitespace and converts it to upper case."""
return clean(number, '').upper().strip()
def _calc_checksum(number):
return '0123456789ABCDEFHJKLMNPRSTUVWXY'[int(number) % 31]
def validate(number):
"""Checks to see if the number provided is a valid HETU. It checks the
format, whether a valid date is given and whether the check digit is
correct."""
number = compact(number)
match = _hetu_re.search(number)
if not match:
raise InvalidFormat()
day = int(match.group('day'))
month = int(match.group('month'))
year = int(match.group('year'))
century = _century_codes[match.group('century')]
individual = int(match.group('individual'))
# check if birth date is valid
try:
datetime.date(century + year, month, day)
except ValueError:
raise InvalidComponent()
# for historical reasons individual IDs start from 002
if individual < 2:
raise InvalidComponent()
checkable_number = '%02d%02d%02d%03d' % (day, month, year, individual)
if match.group('control') != _calc_checksum(checkable_number):
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid HETU. It checks the
format, whether a valid date is given and whether the check digit is
correct."""
try:
return bool(validate(number))
except ValidationError:
return False
# This is here just for completeness as there are no different length forms
# of Finnish personal identity codes:
format = compact

View file

@ -0,0 +1,24 @@
# __init__.py - collection of French numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of French numbers."""
# provide vat as an alias
from stdnum.fr import tva as vat

View file

@ -0,0 +1,85 @@
# siren.py - functions for handling French SIREN numbers
# coding: utf-8
#
# Copyright (C) 2012, 2013 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
"""SIREN (a French company identification number).
The SIREN (Système d'Identification du Répertoire des Entreprises) is a 9
digit number used to identify French companies. The Luhn checksum is used
to validate the numbers.
>>> compact('552 008 443')
'552008443'
>>> validate('404833048')
'404833048'
>>> validate('404833047')
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> to_tva('443 121 975')
'46 443 121 975'
"""
from stdnum import luhn
from stdnum.exceptions import *
from stdnum.util import clean
# An online validation function is available but it does not provide an
# automated entry point, has usage restrictions and seems to require
# attribution to the service for any results used.
# http://avis-situation-sirene.insee.fr/
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 validate(number):
"""Checks to see if the number provided is a valid number. This checks
the length, formatting and check digit."""
number = compact(number)
if not number.isdigit():
raise InvalidFormat()
if len(number) != 9:
raise InvalidLength()
luhn.validate(number)
return number
def is_valid(number):
"""Checks to see if the number provided is a valid number. This checks
the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False
def to_tva(number):
"""Return a TVA that prepends the two extra check digits to the SIREN."""
# note that this always returns numeric check digits
# it is unclean when the alphabetic ones are used
return '%02d%s%s' % (
int(compact(number) + '12') % 97,
' ' if ' ' in number else '',
number
)

View file

@ -0,0 +1,94 @@
# tva.py - functions for handling French TVA numbers
# coding: utf-8
#
# Copyright (C) 2012, 2013 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
"""n° TVA (taxe sur la valeur ajoutée, French VAT number).
The n° TVA (Numéro d'identification à la taxe sur la valeur ajoutée) is the
SIREN (Système dIdentification du Répertoire des Entreprises) prefixed by
two digits. In old style numbers the two digits are numeric, with new
style numbers at least one is a alphabetic.
>>> compact('Fr 40 303 265 045')
'40303265045'
>>> validate('23334175221')
'23334175221'
>>> validate('84 323 140 391')
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> validate('K7399859412') # new-style number
'K7399859412'
>>> validate('4Z123456782') # new-style number starting with digit
'4Z123456782'
"""
from stdnum.exceptions import *
from stdnum.fr import siren
from stdnum.util import clean
# the valid characters for the first two digits (O and I are missing)
_alphabet = '0123456789ABCDEFGHJKLMNPQRSTUVWXYZ'
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' -.').upper().strip()
if number.startswith('FR'):
number = number[2:]
return number
def validate(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
number = compact(number)
if not all(x in _alphabet for x in number[:2]):
raise InvalidFormat()
if len(number) != 11:
raise InvalidLength()
siren.validate(number[2:])
if number.isdigit():
# all-numeric digits
if int(number[:2]) != (int(number[2:] + '12') % 97):
raise InvalidChecksum()
else:
# one of the first two digits isn't a number
if number[0].isdigit():
check = (
_alphabet.index(number[0]) * 24 +
_alphabet.index(number[1]) - 10)
else:
check = (
_alphabet.index(number[0]) * 34 +
_alphabet.index(number[1]) - 100)
if (int(number[2:]) + 1 + check // 11) % 11 != (check % 11):
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,21 @@
# __init__.py - collection of United Kingdom numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of United Kingdom numbers."""

View file

@ -0,0 +1,122 @@
# vat.py - functions for handling United Kingdom VAT numbers
#
# Copyright (C) 2012, 2013 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
"""VAT (United Kingdom (and Isle of Man) VAT registration number).
The VAT number can either be a 9-digit standard number, a 12-digit standard
number followed by a 3-digit branch identifier, a 5-digit number for
government departments (first two digits are GD) or a 5-digit number for
health authorities (first two digits are HA). The 9-digit variants use a
weighted checksum.
>>> validate('GB 980 7806 84')
'980780684'
>>> validate('802311781') # invalid check digit
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> format('980780684')
'980 7806 84'
"""
from stdnum.util import clean
from stdnum.exceptions import *
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' -.').upper().strip()
if number.startswith('GB'):
number = number[2:]
return number
def checksum(number):
"""Calculate the checksum. The checksum is only used for the 9 digits
of the number and the result can either be 0 or 42."""
weights = (8, 7, 6, 5, 4, 3, 2, 10, 1)
return sum(weights[i] * int(n) for i, n in enumerate(number)) % 97
def validate(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
number = compact(number)
if len(number) == 5:
if not number[2:].isdigit():
raise InvalidFormat()
if number.startswith('GD') and int(number[2:]) < 500:
# government department
pass
elif number.startswith('HA') and int(number[2:]) >= 500:
# health authority
pass
else:
raise InvalidComponent()
elif len(number) == 11 and number[0:6] in ('GD8888', 'HA8888'):
if not number[6:].isdigit():
raise InvalidFormat()
if number.startswith('GD') and int(number[6:9]) < 500:
# government department
pass
elif number.startswith('HA') and int(number[6:9]) >= 500:
# health authority
pass
else:
raise InvalidComponent()
if int(number[6:9]) % 97 != int(number[9:11]):
raise InvalidChecksum()
elif len(number) in (9, 12):
if not number.isdigit():
raise InvalidFormat()
# standard number: nnn nnnn nn
# branch trader: nnn nnnn nn nnn (ignore the last thee digits)
# restarting: 100 nnnn nn
if int(number[:3]) >= 100:
if checksum(number[:9]) not in (0, 42, 55):
raise InvalidChecksum()
else:
if checksum(number[:9]) != 0:
raise InvalidChecksum()
else:
raise InvalidLength()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False
def format(number):
"""Reformat the passed number to the standard format."""
number = compact(number)
if len(number) == 5:
# government department or health authority
return number
if len(number) == 12:
# includes branch number
return number[:3] + ' ' + number[3:7] + ' ' + number[7:9] + ' ' + number[9:]
# standard number: nnn nnnn nn
return number[:3] + ' ' + number[3:7] + ' ' + number[7:]

View file

@ -0,0 +1,21 @@
# __init__.py - collection of Greek numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of Greek numbers."""

View file

@ -0,0 +1,78 @@
# vat.py - functions for handling Greek VAT numbers
# coding: utf-8
#
# Copyright (C) 2012, 2013 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
"""FPA, ΦΠΑ (Foros Prostithemenis Aksias, the Greek VAT number).
The FPA is a 9-digit number with a simple checksum.
>>> compact('GR 23456783')
'023456783'
>>> validate('EL 094259216 ')
'094259216'
>>> validate('EL 123456781')
Traceback (most recent call last):
...
InvalidChecksum: ...
"""
from stdnum.exceptions import *
from stdnum.util import clean
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' -./:').upper().strip()
if number.startswith('EL') or number.startswith('GR'):
number = number[2:]
if len(number) == 8:
number = '0' + number # old format had 8 digits
return number
def calc_check_digit(number):
"""Calculate the check digit. The number passed should not have the
check digit included."""
checksum = 0
for n in number:
checksum = checksum * 2 + int(n)
return str(checksum * 2 % 11 % 10)
def validate(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
number = compact(number)
if not number.isdigit():
raise InvalidFormat()
if len(number) != 9:
raise InvalidLength()
if calc_check_digit(number[:-1]) != number[-1]:
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,74 @@
# grid.py - functions for handling Global Release Identifier (GRid) numbers
#
# Copyright (C) 2010, 2011, 2012, 2013 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
"""GRid (Global Release Identifier).
The Global Release Identifier is used to identify releases of digital
sound recordings and uses the ISO 7064 Mod 37, 36 algorithm to verify the
correctness of the number.
>>> validate('A12425GABC1234002M')
'A12425GABC1234002M'
>>> validate('Grid: A1-2425G-ABC1234002-M')
'A12425GABC1234002M'
>>> validate('A1-2425G-ABC1234002-Q') # incorrect check digit
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> compact('A1-2425G-ABC1234002-M')
'A12425GABC1234002M'
>>> format('A12425GABC1234002M')
'A1-2425G-ABC1234002-M'
"""
from stdnum.exceptions import *
from stdnum.util import clean
def compact(number):
"""Convert the GRid to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' -').strip().upper()
if number.startswith('GRID:'):
number = number[5:]
return number
def validate(number):
"""Checks to see if the number provided is a valid GRid."""
from stdnum.iso7064 import mod_37_36
number = compact(number)
if len(number) != 18:
raise InvalidLength()
return mod_37_36.validate(number)
def is_valid(number):
"""Checks to see if the number provided is a valid GRid."""
try:
return bool(validate(number))
except ValidationError:
return False
def format(number, separator='-'):
"""Reformat the passed number to the standard format."""
number = compact(number)
number = (number[0:2], number[2:7], number[7:17], number[17:])
return separator.join(x for x in number if x)

View file

@ -0,0 +1,21 @@
# __init__.py - collection of Croatian numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of Croatian numbers."""

View file

@ -0,0 +1,67 @@
# cnp.py - functions for handling Croatian OIB numbers
# coding: utf-8
#
# Copyright (C) 2012, 2013 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
"""OIB (Osobni identifikacijski broj, Croatian identification number).
The personal identification number is used to identify persons and legal
entities in Croatia. It has 11 digits (sometimes prefixed by HR), contains
no personal information and uses the ISO 7064 Mod 11, 10 checksum algorithm.
>>> validate('HR 33392005961')
'33392005961'
>>> validate('33392005962') # invalid check digit
Traceback (most recent call last):
...
InvalidChecksum: ...
"""
from stdnum.exceptions import *
from stdnum.iso7064 import mod_11_10
from stdnum.util import clean
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' -').upper().strip()
if number.startswith('HR'):
number = number[2:]
return number
def validate(number):
"""Checks to see if the number provided is a valid OIB number. This
checks the length, formatting and check digit."""
number = compact(number)
if not number.isdigit():
raise InvalidFormat()
if len(number) != 11:
raise InvalidLength()
mod_11_10.validate(number)
return number
def is_valid(number):
"""Checks to see if the number provided is a valid OIB number. This
checks the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,24 @@
# __init__.py - collection of Hungarian numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of Hungarian numbers."""
# provide vat as an alias
from stdnum.hu import anum as vat

View file

@ -0,0 +1,72 @@
# anum.py - functions for handling Hungarian VAT numbers
# coding: utf-8
#
# Copyright (C) 2012, 2013 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
"""ANUM (Közösségi adószám, Hungarian VAT number).
The ANUM is the Hungarian VAT (Közösségi adószám) number. It is an 8-digit
taxpayer registration number that includes a weighted checksum.
>>> validate('HU-12892312')
'12892312'
>>> validate('HU-12892313') # invalid check digit
Traceback (most recent call last):
...
InvalidChecksum: ...
"""
from stdnum.exceptions import *
from stdnum.util import clean
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' -').upper().strip()
if number.startswith('HU'):
number = number[2:]
return number
def checksum(number):
"""Calculate the checksum. Valid numbers should have a checksum of 0."""
weights = (9, 7, 3, 1, 9, 7, 3, 1)
return sum(weights[i] * int(n) for i, n in enumerate(number)) % 10
def validate(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
number = compact(number)
if not number.isdigit():
raise InvalidFormat()
if len(number) != 8:
raise InvalidLength()
if checksum(number) != 0:
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,67 @@
# generated from IBAN_Registry.txt, downloaded from
# http://www.swift.com/dsp/resources/documents/IBAN_Registry.txt
AL country="Albania" bban="8!n16!c"
AD country="Andorra" bban="4!n4!n12!c"
AT country="Austria" bban="5!n11!n"
AZ country="Republic of Azerbaijan" bban="4!a20!c"
BH country="Bahrain (Kingdom of)" bban="4!a14!c"
BE country="Belgium" bban="3!n7!n2!n"
BA country="Bosnia and Herzegovina" bban="3!n3!n8!n2!n"
BR country="Brazil" bban="8!n5!n10!n1!a1!c"
BG country="Bulgaria" bban="4!a4!n2!n8!c"
CR country="Costa Rica" bban="3!n14!n"
HR country="Croatia" bban="7!n10!n"
CY country="Cyprus" bban="3!n5!n16!c"
CZ country="Czech Republic" bban="4!n6!n10!n"
DK country="Denmark" bban="4!n9!n1!n"
FO country="Denmark" bban="4!n9!n1!n"
GL country="Denmark" bban="4!n9!n1!n"
DO country="Dominican Republic" bban="4!c20!n"
EE country="Estonia" bban="2!n2!n11!n1!n"
FI country="Finland" bban="6!n7!n1!n"
FR country="France" bban="5!n5!n11!c2!n"
GE country="Georgia" bban="2!a16!n"
DE country="Germany" bban="8!n10!n"
GI country="Gibraltar" bban="4!a15!c"
GR country="Greece" bban="3!n4!n16!c"
GT country="Guatemala" bban="4!c20!c"
HU country="Hungary" bban="3!n4!n1!n15!n1!n"
IS country="Iceland" bban="4!n2!n6!n10!n"
IE country="Ireland" bban="4!a6!n8!n"
IL country="Israel" bban="3!n3!n13!n"
IT country="Italy" bban="1!a5!n5!n12!c"
KW country="Kuwait" bban="4!a22!c"
KZ country="Kazakhstan" bban="3!n13!c"
LV country="Latvia" bban="4!a13!c"
LB country="Lebanon" bban="4!n20!c"
LI country="Liechtenstein (Principality of)" bban="5!n12!c"
LT country="Lithuania" bban="5!n11!n"
LU country="Luxembourg" bban="3!n13!c"
MK country="Macedonia, Former Yugoslav Republic of" bban="3!n10!c2!n"
MT country="Malta" bban="4!a5!n18!c"
MR country="Mauritania" bban="5!n5!n11!n2!n"
MU country="Mauritius" bban="4!a2!n2!n12!n3!n3!a"
MD country="Republic of Moldova" bban="2!c18!c"
MC country="Monaco" bban="5!n5!n11!c2!n"
ME country="Montenegro" bban="3!n13!n2!n"
NL country="The Netherlands" bban="4!a10!n"
NO country="Norway" bban="4!n6!n1!n"
PK country="Pakistan" bban="4!a16!c"
PS country="Palestine, State of" bban="4!a21!c"
PL country="Poland" bban="8!n16!n"
PT country="Portugal" bban="4!n4!n11!n2!n"
QA country="Qatar" bban="4!a21!c"
RO country="Romania" bban="4!a16!c"
SM country="San Marino" bban="1!a5!n5!n12!c"
SA country="Saudi Arabia" bban="2!n18!c"
RS country="Serbia" bban="3!n13!n2!n"
SK country="Slovak Republic" bban="4!n6!n10!n"
SI country="Slovenia" bban="5!n8!n2!n"
ES country="Spain" bban="4!n4!n1!n1!n10!n"
SE country="Sweden" bban="3!n16!n1!n"
CH country="Switzerland" bban="5!n12!c"
TN country="Tunisia" bban="2!n3!n13!n2!n"
TR country="Turkey" bban="5!n1!c16!c"
AE country="United Arab Emirates" bban="3!n16!n"
GB country="United Kingdom" bban="4!a6!n8!n"
VG country="Virgin Islands, British" bban="4!a16!n"

View file

@ -0,0 +1,115 @@
# iban.py - functions for handling International Bank Account Numbers (IBANs)
#
# Copyright (C) 2011, 2012, 2013 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
"""IBAN (International Bank Account Number).
The IBAN is used to identify bank accounts across national borders. The
first two letters are a country code. The next two digits are check digits
for the ISO 7064 Mod 97, 10 checksum. Each country uses it's own format
for the remainder of the number.
Some countries may also use checksum algorithms within their number but
this is currently not checked by this number.
>>> validate('GR16 0110 1050 0000 1054 7023 795')
'GR1601101050000010547023795'
>>> validate('BE31435411161155')
'BE31435411161155'
>>> compact('GR16 0110 1050 0000 1054 7023 795')
'GR1601101050000010547023795'
>>> format('GR1601101050000010547023795')
'GR16 0110 1050 0000 1054 7023 795'
"""
import re
from stdnum import numdb
from stdnum.exceptions import *
from stdnum.iso7064 import mod_97_10
from stdnum.util import clean
# our open copy of the IBAN database
_ibandb = numdb.get('iban')
# the valid characters we have
_alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
# regular expression to check IBAN structure
_struct_re = re.compile('([1-9][0-9]*)!([nac])')
def compact(number):
"""Convert the iban number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
return clean(number, ' -').strip().upper()
def _to_base10(number):
"""Prepare the number to it's base10 representation (also moving the
check digits to the end) so it can be checked with the ISO 7064
Mod 97, 10 algorithm."""
# TODO: find out whether this should be in the mod_97_10 module
return ''.join(str(_alphabet.index(x)) for x in number[4:] + number[:4])
def _struct_to_re(structure):
"""Convert an IBAN structure to a refular expression that can be used
to validate the number."""
def conv(match):
chars = {
'n': '[0-9]',
'a': '[A-Z]',
'c': '[A-Za-z0-9]',
}[match.group(2)]
return '%s{%s}' % (chars, match.group(1))
return re.compile('^%s$' % _struct_re.sub(conv, structure))
def validate(number):
"""Checks to see if the number provided is a valid IBAN."""
number = compact(number)
try:
test_number = _to_base10(number)
except:
raise InvalidFormat()
# ensure that checksum is valid
mod_97_10.validate(test_number)
# look up the number
info = _ibandb.info(number)
# check if the bban part of number has the correct structure
bban = number[4:]
if not _struct_to_re(info[0][1].get('bban', '')).match(bban):
raise InvalidFormat()
# return the compact representation
return number
def is_valid(number):
"""Checks to see if the number provided is a valid IBAN."""
try:
return bool(validate(number))
except ValidationError:
return False
def format(number, separator=' '):
"""Reformat the passed number to the space-separated format."""
number = compact(number)
return separator.join(number[i:i + 4] for i in range(0, len(number), 4))

View file

@ -0,0 +1,21 @@
# __init__.py - collection of Irish numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of Irish numbers."""

View file

@ -0,0 +1,69 @@
# pps.py - functions for handling Irish PPS numbers
#
# Copyright (C) 2012, 2013 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
"""PPS No (Personal Public Service Number, Irish personal number).
The Personal Public Service number consists of 8 digits. The first seven
are numeric and the last is the check character. The number is sometimes
be followed by an extra letter that can be a 'W', 'T' or an 'X' and is
ignored for the check algorithm.
>>> validate('6433435F')
'6433435F'
>>> validate('6433435E') # incorrect check digit
Traceback (most recent call last):
...
InvalidChecksum: ...
"""
import re
from stdnum.exceptions import *
from stdnum.ie import vat
from stdnum.util import clean
pps_re = re.compile('^\d{7}[A-W][WTX]?$')
"""Regular expression used to check syntax of PPS numbers."""
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, ' -').upper().strip()
def validate(number):
"""Checks to see if the number provided is a valid PPS number. This
checks the length, formatting and check digit."""
number = compact(number)
if not pps_re.match(number):
raise InvalidFormat()
if number[7] != vat.calc_check_digit(number[:7]):
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid PPS number. This
checks the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,87 @@
# vat.py - functions for handling Irish VAT numbers
#
# Copyright (C) 2012, 2013 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
"""VAT (Irish VAT number).
The Irish VAT number consists of 8 digits. The last digit is a check
letter, the second digit may be a number, a letter, "+" or "*".
>>> validate('IE 6433435F')
'6433435F'
>>> validate('6433435E') # incorrect check digit
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> validate('8D79739I') # old style number
'8D79739I'
>>> validate('8?79739J') # incorrect old style
Traceback (most recent call last):
...
InvalidFormat: ...
"""
from stdnum.exceptions import *
from stdnum.util import clean
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' -').upper().strip()
if number.startswith('IE'):
number = number[2:]
return number
def calc_check_digit(number):
"""Calculate the check digit. The number passed should not have the
check digit included."""
alphabet = 'WABCDEFGHIJKLMNOPQRSTUV'
number = (7 - len(number)) * '0' + number
return alphabet[sum((8 - i) * int(n) for i, n in enumerate(number)) % 23]
def validate(number):
"""Checks to see if the number provided is a valid VAT number. This checks
the length, formatting and check digit."""
number = compact(number)
if not number[:1].isdigit() or not number[2:7].isdigit():
raise InvalidFormat()
if len(number) != 8:
raise InvalidLength()
if number[:7].isdigit():
# new system
if number[-1] != calc_check_digit(number[:-1]):
raise InvalidChecksum()
elif number[1] in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ+*':
# old system
if number[-1] != calc_check_digit(number[2:-1] + number[0]):
raise InvalidChecksum()
else:
raise InvalidFormat()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid VAT number. This checks
the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,106 @@
# imei.py - functions for handling International Mobile Equipment Identity
# (IMEI) numbers
#
# Copyright (C) 2010, 2011, 2012, 2013 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
"""IMEI (International Mobile Equipment Identity).
The IMEI is used to identify mobile phones. The IMEI may optionally
include a check digit which is validated using the Luhn algorithm.
>>> validate('35686800-004141-20')
'3568680000414120'
>>> validate('35-417803-685978-1')
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> compact('35686800-004141-20')
'3568680000414120'
>>> format('354178036859789')
'35-417803-685978-9'
>>> format('35686800-004141', add_check_digit=True)
'35-686800-004141-8'
>>> imei_type('35686800-004141-20')
'IMEISV'
>>> split('35686800-004141')
('35686800', '004141', '')
"""
from stdnum import luhn
from stdnum.exceptions import *
from stdnum.util import clean
def compact(number):
"""Convert the IMEI number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
return clean(number, ' -').strip().upper()
def validate(number):
"""Checks to see if the number provided is a valid IMEI (or IMEISV)
number."""
number = compact(number)
if not number.isdigit():
raise InvalidFormat()
if len(number) == 15:
# only 15 digit IMEI has check digit
luhn.validate(number)
elif len(number) not in (14, 16):
# neither IMEI without check digit or IMEISV (which doesn't have one)
raise InvalidLength()
return number
def imei_type(number):
"""Check the passed number and returns 'IMEI', 'IMEISV' or None (for
invalid) for checking the type of number passed."""
try:
number = validate(number)
except:
return None
if len(number) in (14, 15):
return 'IMEI'
elif len(number) == 16:
return 'IMEISV'
def is_valid(number):
"""Checks to see if the number provided is a valid IMEI (or IMEISV)
number."""
try:
return bool(validate(number))
except ValidationError:
return False
def split(number):
"""Split the number into a Type Allocation Code (TAC), serial number
and either the checksum (for IMEI) or the software version number (for
IMEISV)."""
number = compact(number)
return (number[:8], number[8:14], number[14:])
def format(number, separator='-', add_check_digit=False):
"""Reformat the passed number to the standard format."""
number = compact(number)
if len(number) == 14 and add_check_digit:
number += luhn.calc_check_digit(number)
number = (number[:2], number[2:8], number[8:14], number[14:])
return separator.join(x for x in number if x)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,96 @@
# imsi.py - functions for handling International Mobile Subscriber Identity
# (IMSI) numbers
#
# Copyright (C) 2011, 2012, 2013 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
"""IMSI (International Mobile Subscriber Identity).
The IMSI (International Mobile Subscriber Identity) is used to identify
mobile phone users (the SIM).
>>> validate('429011234567890')
'429011234567890'
>>> validate('439011234567890') # unknown MCC
Traceback (most recent call last):
...
InvalidFormat: ...
>>> split('429011234567890')
('429', '01', '1234567890')
>>> split('310150123456789')
('310', '150', '123456789')
>>> info('460001234567890')['mcc']
'460'
>>> str(info('460001234567890')['country'])
'China'
"""
from stdnum.exceptions import *
from stdnum.util import clean
def compact(number):
"""Convert the IMSI number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
return clean(number, ' -').strip().upper()
def split(number):
"""Split the specified IMSI into a Mobile Country Code (MCC), a Mobile
Network Code (MNC), a Mobile Station Identification Number (MSIN)."""
from stdnum import numdb
# clean up number
number = compact(number)
# split the number
return tuple(numdb.get('imsi').split(number))
def validate(number):
"""Checks to see if the number provided is a valid IMSI."""
number = compact(number)
if not number.isdigit():
raise InvalidFormat()
if len(number) not in (14, 15):
raise InvalidLength()
if len(split(number)) != 3:
raise InvalidFormat()
return number
def info(number):
"""Return a dictionary of data about the supplied number."""
from stdnum import numdb
# clean up number
number = compact(number)
# split the number
info = dict(number=number)
mcc_info, mnc_info, msin_info = numdb.get('imsi').info(number)
info['mcc'] = mcc_info[0]
info.update(mcc_info[1])
info['mnc'] = mnc_info[0]
info.update(mnc_info[1])
info['msin'] = msin_info[0]
info.update(msin_info[1])
return info
def is_valid(number):
"""Checks to see if the number provided is a valid IMSI."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,158 @@
# isan.py - functions for handling International Standard Audiovisual Numbers
# (ISANs)
#
# Copyright (C) 2010, 2011, 2012, 2013 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
"""ISAN (International Standard Audiovisual Number).
The ISAN (International Standard Audiovisual Number) is used to identify
audiovisual works.
The number is hexadecimal and can consists of at least a root identifier,
and an episode or part. After that an optional check digit, optional
version and optionally another check digit can be provided. The check
digits are validated using the ISO 7064 Mod 37, 36 algorithm.
>>> validate('000000018947000000000000')
'000000018947000000000000'
>>> compact('0000-0000-D07A-0090-Q-0000-0000-X')
'00000000D07A009000000000'
>>> validate('0000-0001-8CFA-0000-I-0000-0000-K')
'000000018CFA0000I00000000K'
>>> validate('0000-0001-8CFA-0000-A-0000-0000-K')
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> format('000000018947000000000000')
'0000-0001-8947-0000-8-0000-0000-D'
>>> to_urn('00000000D07A009000000000')
'URN:ISAN:0000-0000-D07A-0090-Q-0000-0000-X'
>>> to_xml('1881-66C7-3420-6541-Y-9F3A-0245-O')
'<ISAN root="1881-66C7-3420" episode="6541" version="9F3A-0245" />'
"""
from stdnum.exceptions import *
from stdnum.iso7064 import mod_37_36
from stdnum.util import clean
def split(number):
"""Splits the number into a root, an episode or part, a check digit a
version and another check digit. If any of the parts are missing an
empty string is returned."""
number = clean(number, ' -').strip().upper()
if len(number) == 17 or len(number) == 26:
return number[0:12], number[12:16], number[16], number[17:25], number[25:]
elif len(number) > 16:
return number[0:12], number[12:16], '', number[16:24], number[24:]
else:
return number[0:12], number[12:16], number[16:], '', ''
def compact(number, strip_check_digits=True):
"""Convert the ISAN to the minimal representation. This strips the number
of any valid separators and removes surrounding whitespace. The check
digits are removed by default."""
number = list(split(number))
number[2] = number[4] = ''
return ''.join(number)
def validate(number, strip_check_digits=False, add_check_digits=False):
"""Checks to see if the number provided is a valid ISAN. If check digits
are present in the number they are validated. If strip_check_digits is
True any existing check digits will be removed (after checking). If
add_check_digits is True the check digit will be added if they are not
present yet."""
(root, episode, check1, version, check2) = split(number)
# check digits used
for x in root + episode + version:
if x not in '0123456789ABCDEF':
raise InvalidFormat()
# check length of all components
if len(root) != 12 or len(episode) != 4 or len(check1) not in (0, 1) or \
len(version) not in (0, 8) or len(check1) not in (0, 1):
raise InvalidLength()
# check check digits
if check1:
mod_37_36.validate(root + episode + check1)
if check2:
mod_37_36.validate(root + episode + version + check2)
# remove or add check digits
if strip_check_digits:
check1 = check2 = ''
if add_check_digits and not check1:
check1 = mod_37_36.calc_check_digit(root + episode)
if add_check_digits and not check2 and version:
check2 = mod_37_36.calc_check_digit(root + episode + version)
return root + episode + check1 + version + check2
def is_valid(number):
"""Checks to see if the number provided is a valid ISAN. If check digits
are present in the number they are validated."""
try:
return bool(validate(number))
except ValidationError:
return False
def format(number, separator='-', strip_check_digits=False, add_check_digits=True):
"""Reformat the passed number to the standard format. If
add_check_digits is True the check digit will be added if they are not
present yet. If both strip_check_digits and add_check_digits are True the
check digits will be recalculated."""
(root, episode, check1, version, check2) = split(number)
if strip_check_digits:
check1 = check2 = ''
if add_check_digits and not check1:
check1 = mod_37_36.calc_check_digit(root + episode)
if add_check_digits and not check2 and version:
check2 = mod_37_36.calc_check_digit(root + episode + version)
number = [root[i:i + 4] for i in range(0, 12, 4)] + [episode]
if check1:
number.append(check1)
if version:
number.extend((version[0:4], version[4:]))
if check2:
number.append(check2)
return separator.join(number)
def to_binary(number):
"""Convert the number to it's binary representation (without the check
digits)."""
import sys
number = compact(number, strip_check_digits=True)
if sys.version > '3': # pragma: no cover (Python 2/3 specific code)
return bytes.fromhex(number)
else: # pragma: no cover (Python 2/3 specific code)
return number.decode('hex')
def to_xml(number):
"""Returns the XML form of the ISAN as a string."""
number = format(number, strip_check_digits=True, add_check_digits=False)
return '<ISAN root="%s" episode="%s" version="%s" />' % (
number[0:14], number[15:19], number[20:])
def to_urn(number):
"""Returns the URN representation of the ISAN."""
return 'URN:ISAN:' + format(number, add_check_digits=True)

View file

@ -0,0 +1,467 @@
# generated from RangeMessage.xml, downloaded from
# http://www.isbn-international.org/agency?rmxml=1
# file serial be2e8e46-b368-41c0-8603-5938a5e7e0f7
# file date Mon, 25 Nov 2013 09:51:28 GMT
978
0-5,600-649,7-7,80-94,950-989,9900-9989,99900-99999
0 agency="English language"
00-19,200-699,7000-8499,85000-89999,900000-949999,9500000-9999999
1 agency="English language"
00-09,100-399,4000-5499,55000-86979,869800-998999,9990000-9999999
2 agency="French language"
00-19,200-349,35000-39999,400-699,7000-8399,84000-89999,900000-949999
9500000-9999999
3 agency="German language"
00-02,030-033,0340-0369,03700-03999,04-19,200-699,7000-8499,85000-89999
900000-949999,9500000-9539999,95400-96999,9700000-9899999,99000-99499
99500-99999
4 agency="Japan"
00-19,200-699,7000-8499,85000-89999,900000-949999,9500000-9999999
5 agency="Russian Federation and former USSR"
00000-00499,0050-0099,01-19,200-420,4210-4299,430-430,4310-4399,440-440
4410-4499,450-699,7000-8499,85000-89999,900000-909999,91000-91999
9200-9299,93000-94999,9500000-9500999,9501-9799,98000-98999
9900000-9909999,9910-9999
600 agency="Iran"
00-09,100-499,5000-8999,90000-99999
601 agency="Kazakhstan"
00-19,200-699,7000-7999,80000-84999,85-99
602 agency="Indonesia"
00-10,1100-1199,1200-1399,14000-14999,1500-1699,17000-17999,18000-18999
19000-19999,200-749,7500-7999,8000-9499,95000-99999
603 agency="Saudi Arabia"
00-04,05-49,500-799,8000-8999,90000-99999
604 agency="Vietnam"
0-4,50-89,900-979,9800-9999
605 agency="Turkey"
01-09,100-399,4000-5999,60000-89999,90-99
606 agency="Romania"
0-0,10-49,500-799,8000-9199,92000-99999
607 agency="Mexico"
00-39,400-749,7500-9499,95000-99999
608 agency="Macedonia"
0-0,10-19,200-449,4500-6499,65000-69999,7-9
609 agency="Lithuania"
00-39,400-799,8000-9499,95000-99999
611 agency="Thailand"
612 agency="Peru"
00-29,300-399,4000-4499,45000-49999,50-99
613 agency="Mauritius"
0-9
614 agency="Lebanon"
00-39,400-799,8000-9499,95000-99999
615 agency="Hungary"
00-09,100-499,5000-7999,80000-89999
616 agency="Thailand"
00-19,200-699,7000-8999,90000-99999
617 agency="Ukraine"
00-49,500-699,7000-8999,90000-99999
618 agency="Greece"
00-19,200-499,5000-7999,80000-99999
619 agency="Bulgaria"
00-14,150-699,7000-8999,90000-99999
620 agency="Mauritius"
0-9
621 agency="Philippines"
00-29,400-599,8000-8999,95000-99999
7 agency="China, People's Republic"
00-09,100-499,5000-7999,80000-89999,900000-999999
80 agency="Czech Republic and Slovakia"
00-19,200-699,7000-8499,85000-89999,900000-999999
81 agency="India"
00-19,200-699,7000-8499,85000-89999,900000-999999
82 agency="Norway"
00-19,200-699,7000-8999,90000-98999,990000-999999
83 agency="Poland"
00-19,200-599,60000-69999,7000-8499,85000-89999,900000-999999
84 agency="Spain"
00-13,140-149,15000-19999,200-699,7000-8499,85000-89999,9000-9199
920000-923999,92400-92999,930000-949999,95000-96999,9700-9999
85 agency="Brazil"
00-19,200-599,60000-69999,7000-8499,85000-89999,900000-979999,98000-99999
86 agency="Serbia (shared)"
00-29,300-599,6000-7999,80000-89999,900000-999999
87 agency="Denmark"
00-29,400-649,7000-7999,85000-94999,970000-999999
88 agency="Italy"
00-19,200-599,6000-8499,85000-89999,900000-909999,910-929,9300-9399
940000-949999,95000-99999
89 agency="Korea, Republic"
00-24,250-549,5500-8499,85000-94999,950000-969999,97000-98999,990-999
90 agency="Netherlands"
00-19,200-499,5000-6999,70000-79999,800000-849999,8500-8999,90-90,94-94
91 agency="Sweden"
0-1,20-49,500-649,7000-7999,85000-94999,970000-999999
92 agency="International NGO Publishers and EC Organizations"
0-5,60-79,800-899,9000-9499,95000-98999,990000-999999
93 agency="India"
00-09,100-499,5000-7999,80000-94999,950000-999999
94 agency="Netherlands"
000-599,6000-8999,90000-99999
950 agency="Argentina"
00-49,500-899,9000-9899,99000-99999
951 agency="Finland"
0-1,20-54,550-889,8900-9499,95000-99999
952 agency="Finland"
00-19,200-499,5000-5999,60-65,6600-6699,67000-69999,7000-7999,80-94
9500-9899,99000-99999
953 agency="Croatia"
0-0,10-14,150-509,51-54,55000-59999,6000-9499,95000-99999
954 agency="Bulgaria"
00-28,2900-2999,300-799,8000-8999,90000-92999,9300-9999
955 agency="Sri Lanka"
0000-1999,20-40,41000-43999,44000-44999,4500-4999,50000-54999,550-799
8000-9499,95000-99999
956 agency="Chile"
00-19,200-699,7000-9999
957 agency="Taiwan"
00-02,0300-0499,05-19,2000-2099,21-27,28000-30999,31-43,440-819
8200-9699,97000-99999
958 agency="Colombia"
00-56,57000-59999,600-799,8000-9499,95000-99999
959 agency="Cuba"
00-19,200-699,7000-8499,85000-99999
960 agency="Greece"
00-19,200-659,6600-6899,690-699,7000-8499,85000-92999,93-93,9400-9799
98000-99999
961 agency="Slovenia"
00-19,200-599,6000-8999,90000-94999
962 agency="Hong Kong, China"
00-19,200-699,7000-8499,85000-86999,8700-8999,900-999
963 agency="Hungary"
00-19,200-699,7000-8499,85000-89999,9000-9999
964 agency="Iran"
00-14,150-249,2500-2999,300-549,5500-8999,90000-96999,970-989,9900-9999
965 agency="Israel"
00-19,200-599,7000-7999,90000-99999
966 agency="Ukraine"
00-12,130-139,14-14,1500-1699,170-199,2000-2789,279-289,2900-2999
300-699,7000-8999,90000-90999,910-949,95000-97999,980-999
967 agency="Malaysia"
00-00,0100-0999,10000-19999,300-499,5000-5999,60-89,900-989,9900-9989
99900-99999
968 agency="Mexico"
01-39,400-499,5000-7999,800-899,9000-9999
969 agency="Pakistan"
0-1,20-39,400-799,8000-9999
970 agency="Mexico"
01-59,600-899,9000-9099,91000-96999,9700-9999
971 agency="Philippines"
000-015,0160-0199,02-02,0300-0599,06-49,500-849,8500-9099,91000-95999
9600-9699,97-98,9900-9999
972 agency="Portugal"
0-1,20-54,550-799,8000-9499,95000-99999
973 agency="Romania"
0-0,100-169,1700-1999,20-54,550-759,7600-8499,85000-88999,8900-9499
95000-99999
974 agency="Thailand"
00-19,200-699,7000-8499,85000-89999,90000-94999,9500-9999
975 agency="Turkey"
00000-01999,02-24,250-599,6000-9199,92000-98999,990-999
976 agency="Caribbean Community"
0-3,40-59,600-799,8000-9499,95000-99999
977 agency="Egypt"
00-19,200-499,5000-6999,700-849,85000-89999,90-99
978 agency="Nigeria"
000-199,2000-2999,30000-79999,8000-8999,900-999
979 agency="Indonesia"
000-099,1000-1499,15000-19999,20-29,3000-3999,400-799,8000-9499
95000-99999
980 agency="Venezuela"
00-19,200-599,6000-9999
981 agency="Singapore"
00-11,17000-19999,200-289,290-299,3000-3099,310-399,4000-9999
982 agency="South Pacific"
00-09,100-699,70-89,9000-9799,98000-99999
983 agency="Malaysia"
00-01,020-199,2000-3999,40000-44999,45-49,50-79,800-899,9000-9899
99000-99999
984 agency="Bangladesh"
00-39,400-799,8000-8999,90000-99999
985 agency="Belarus"
00-39,400-599,6000-8999,90000-99999
986 agency="Taiwan"
00-11,120-559,5600-7999,80000-99999
987 agency="Argentina"
00-09,1000-1999,20000-29999,30-35,3600-3999,40-44,45000-49999,500-899
9000-9499,95000-99999
988 agency="Hong Kong, China"
00-11,12000-14999,15000-16999,17000-19999,200-799,8000-9699,97000-99999
989 agency="Portugal"
0-1,20-54,550-799,8000-9499,95000-99999
9927 agency="Qatar"
00-09,100-399,4000-4999
9928 agency="Albania"
00-09,100-399,4000-4999
9929 agency="Guatemala"
0-3,40-54,550-799,8000-9999
9930 agency="Costa Rica"
00-49,500-939,9400-9999
9931 agency="Algeria"
00-29,300-899,9000-9999
9932 agency="Lao People's Democratic Republic"
00-39,400-849,8500-9999
9933 agency="Syria"
0-0,10-39,400-899,9000-9999
9934 agency="Latvia"
0-0,10-49,500-799,8000-9999
9935 agency="Iceland"
0-0,10-39,400-899,9000-9999
9936 agency="Afghanistan"
0-1,20-39,400-799,8000-9999
9937 agency="Nepal"
0-2,30-49,500-799,8000-9999
9938 agency="Tunisia"
00-79,800-949,9500-9999
9939 agency="Armenia"
0-4,50-79,800-899,9000-9999
9940 agency="Montenegro"
0-1,20-49,500-899,9000-9999
9941 agency="Georgia"
0-0,10-39,400-899,9000-9999
9942 agency="Ecuador"
00-84,8500-8999,900-984,9850-9999
9943 agency="Uzbekistan"
00-29,300-399,4000-9999
9944 agency="Turkey"
0000-0999,100-499,5000-5999,60-69,700-799,80-89,900-999
9945 agency="Dominican Republic"
00-00,010-079,08-39,400-569,57-57,580-849,8500-9999
9946 agency="Korea, P.D.R."
0-1,20-39,400-899,9000-9999
9947 agency="Algeria"
0-1,20-79,800-999
9948 agency="United Arab Emirates"
00-39,400-849,8500-9999
9949 agency="Estonia"
0-0,10-39,400-899,9000-9999
9950 agency="Palestine"
00-29,300-849,8500-9999
9951 agency="Kosova"
00-39,400-849,8500-9999
9952 agency="Azerbaijan"
0-1,20-39,400-799,8000-9999
9953 agency="Lebanon"
0-0,10-39,400-599,60-89,9000-9999
9954 agency="Morocco"
0-1,20-39,400-799,8000-9999
9955 agency="Lithuania"
00-39,400-929,9300-9999
9956 agency="Cameroon"
0-0,10-39,400-899,9000-9999
9957 agency="Jordan"
00-39,400-699,70-84,8500-8799,88-99
9958 agency="Bosnia and Herzegovina"
00-03,040-089,0900-0999,10-18,1900-1999,20-49,500-899,9000-9999
9959 agency="Libya"
0-1,20-79,800-949,9500-9699,970-979,98-99
9960 agency="Saudi Arabia"
00-59,600-899,9000-9999
9961 agency="Algeria"
0-2,30-69,700-949,9500-9999
9962 agency="Panama"
00-54,5500-5599,56-59,600-849,8500-9999
9963 agency="Cyprus"
0-1,2000-2499,250-279,2800-2999,30-54,550-734,7350-7499,7500-9999
9964 agency="Ghana"
0-6,70-94,950-999
9965 agency="Kazakhstan"
00-39,400-899,9000-9999
9966 agency="Kenya"
000-149,1500-1999,20-69,7000-7499,750-959,9600-9999
9967 agency="Kyrgyz Republic"
00-39,400-899,9000-9999
9968 agency="Costa Rica"
00-49,500-939,9400-9999
9970 agency="Uganda"
00-39,400-899,9000-9999
9971 agency="Singapore"
0-5,60-89,900-989,9900-9999
9972 agency="Peru"
00-09,1-1,200-249,2500-2999,30-59,600-899,9000-9999
9973 agency="Tunisia"
00-05,060-089,0900-0999,10-69,700-969,9700-9999
9974 agency="Uruguay"
0-2,30-54,550-749,7500-9499,95-99
9975 agency="Moldova"
0-0,100-399,4000-4499,45-89,900-949,9500-9999
9976 agency="Tanzania"
0-5,60-89,900-989,9900-9999
9977 agency="Costa Rica"
00-89,900-989,9900-9999
9978 agency="Ecuador"
00-29,300-399,40-94,950-989,9900-9999
9979 agency="Iceland"
0-4,50-64,650-659,66-75,760-899,9000-9999
9980 agency="Papua New Guinea"
0-3,40-89,900-989,9900-9999
9981 agency="Morocco"
00-09,100-159,1600-1999,20-79,800-949,9500-9999
9982 agency="Zambia"
00-79,800-989,9900-9999
9983 agency="Gambia"
80-94,950-989,9900-9999
9984 agency="Latvia"
00-49,500-899,9000-9999
9985 agency="Estonia"
0-4,50-79,800-899,9000-9999
9986 agency="Lithuania"
00-39,400-899,9000-9399,940-969,97-99
9987 agency="Tanzania"
00-39,400-879,8800-9999
9988 agency="Ghana"
0-2,30-54,550-749,7500-9999
9989 agency="Macedonia"
0-0,100-199,2000-2999,30-59,600-949,9500-9999
99901 agency="Bahrain"
00-49,500-799,80-99
99902 agency="Gabon (reserved)"
99903 agency="Mauritius"
0-1,20-89,900-999
99904 agency="Netherlands Antilles and Aruba"
0-5,60-89,900-999
99905 agency="Bolivia"
0-3,40-79,800-999
99906 agency="Kuwait"
0-2,30-59,600-699,70-89,90-94,950-999
99908 agency="Malawi"
0-0,10-89,900-999
99909 agency="Malta"
0-3,40-94,950-999
99910 agency="Sierra Leone"
0-2,30-89,900-999
99911 agency="Lesotho"
00-59,600-999
99912 agency="Botswana"
0-3,400-599,60-89,900-999
99913 agency="Andorra"
0-2,30-35,600-604
99914 agency="Suriname"
0-4,50-89,900-999
99915 agency="Maldives"
0-4,50-79,800-999
99916 agency="Namibia"
0-2,30-69,700-999
99917 agency="Brunei Darussalam"
0-2,30-89,900-999
99918 agency="Faroe Islands"
0-3,40-79,800-999
99919 agency="Benin"
0-2,300-399,40-69,70-79,800-849,850-899,900-999
99920 agency="Andorra"
0-4,50-89,900-999
99921 agency="Qatar"
0-1,20-69,700-799,8-8,90-99
99922 agency="Guatemala"
0-3,40-69,700-999
99923 agency="El Salvador"
0-1,20-79,800-999
99924 agency="Nicaragua"
0-1,20-79,800-999
99925 agency="Paraguay"
0-3,40-79,800-999
99926 agency="Honduras"
0-0,10-59,600-899,90-99
99927 agency="Albania"
0-2,30-59,600-999
99928 agency="Georgia"
0-0,10-79,800-999
99929 agency="Mongolia"
0-4,50-79,800-999
99930 agency="Armenia"
0-4,50-79,800-999
99931 agency="Seychelles"
0-4,50-79,800-999
99932 agency="Malta"
0-0,10-59,600-699,7-7,80-99
99933 agency="Nepal"
0-2,30-59,600-999
99934 agency="Dominican Republic"
0-1,20-79,800-999
99935 agency="Haiti"
0-2,30-59,600-699,7-8,90-99
99936 agency="Bhutan"
0-0,10-59,600-999
99937 agency="Macau"
0-1,20-59,600-999
99938 agency="Srpska, Republic of"
0-1,20-59,600-899,90-99
99939 agency="Guatemala"
0-5,60-89,900-999
99940 agency="Georgia"
0-0,10-69,700-999
99941 agency="Armenia"
0-2,30-79,800-999
99942 agency="Sudan"
0-4,50-79,800-999
99943 agency="Albania"
0-2,30-59,600-999
99944 agency="Ethiopia"
0-4,50-79,800-999
99945 agency="Namibia"
0-5,60-89,900-999
99946 agency="Nepal"
0-2,30-59,600-999
99947 agency="Tajikistan"
0-2,30-69,700-999
99948 agency="Eritrea"
0-4,50-79,800-999
99949 agency="Mauritius"
0-1,20-89,900-999
99950 agency="Cambodia"
0-4,50-79,800-999
99951 agency="Congo, The Democratic Republic"
99952 agency="Mali"
0-4,50-79,800-999
99953 agency="Paraguay"
0-2,30-79,800-939,94-99
99954 agency="Bolivia"
0-2,30-69,700-879,88-99
99955 agency="Srpska, Republic of"
0-1,20-59,600-799,80-99
99956 agency="Albania"
00-59,600-859,86-99
99957 agency="Malta"
0-1,20-79,800-999
99958 agency="Bahrain"
0-4,50-94,950-999
99959 agency="Luxembourg"
0-2,30-59,600-999
99960 agency="Malawi"
0-0,10-94,950-999
99961 agency="El Salvador"
0-3,40-89,900-999
99962 agency="Mongolia"
0-4,50-79,800-999
99963 agency="Cambodia"
00-49,500-999
99964 agency="Nicaragua"
0-1,20-79,800-999
99965 agency="Macau"
0-3,40-69,700-999
99966 agency="Kuwait"
0-2,30-69,700-799
99967 agency="Paraguay"
0-1,20-59,600-899
99968 agency="Botswana"
0-3,400-599,60-89,900-999
99969 agency="Oman"
0-4,50-79,800-999
99970 agency="Haiti"
0-4,50-89,900-999
99971 agency="Myanmar"
0-5,60-84,850-999
99972 agency="Faroe Islands"
0-4,50-89,900-999
99973 agency="Mongolia"
0-3,40-79,800-999
99974 agency="Bolivia"
40-79,800-999
99975 agency="Tajikistan"
0-3,40-79,800-999
979
10-11
10 agency="France"
00-19,200-699,7000-8999,90000-97599,976000-999999
11 agency="Korea, Republic"
00-24,250-549,5500-8499,85000-94999,950000-999999

View file

@ -0,0 +1,187 @@
# isbn.py - functions for handling ISBNs
#
# Copyright (C) 2010, 2011, 2012, 2013 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
"""ISBN (International Standard Book Number).
The ISBN is the International Standard Book Number, used to identify
publications. This module supports both numbers in ISBN-10 (10-digit) and
ISBN-13 (13-digit) format.
>>> validate('978-9024538270')
'9789024538270'
>>> validate('978-9024538271')
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> compact('1-85798-218-5')
'1857982185'
>>> format('9780471117094')
'978-0-471-11709-4'
>>> format('1857982185')
'1-85798-218-5'
>>> isbn_type('1-85798-218-5')
'ISBN10'
>>> isbn_type('978-0-471-11709-4')
'ISBN13'
>>> to_isbn13('1-85798-218-5')
'978-1-85798-218-3'
>>> to_isbn10('978-1-85798-218-3')
'1-85798-218-5'
"""
from stdnum import ean
from stdnum.exceptions import *
from stdnum.util import clean
def compact(number, convert=False):
"""Convert the ISBN to the minimal representation. This strips the number
of any valid ISBN separators and removes surrounding whitespace. If the
covert parameter is True the number is also converted to ISBN-13
format."""
number = clean(number, ' -').strip().upper()
if len(number) == 9:
number = '0' + number
if convert:
return to_isbn13(number)
return number
def _calc_isbn10_check_digit(number):
"""Calculate the ISBN check digit for 10-digit numbers. The number passed
should not have the check bit included."""
check = sum((i + 1) * int(n)
for i, n in enumerate(number)) % 11
return 'X' if check == 10 else str(check)
def validate(number, convert=False):
"""Checks to see if the number provided is a valid ISBN (either a legacy
10-digit one or a 13-digit one). This checks the length and the check
bit but does not check if the group and publisher are valid (use split()
for that)."""
number = compact(number, convert=False)
if not number[:-1].isdigit():
raise InvalidFormat()
if len(number) == 10:
if _calc_isbn10_check_digit(number[:-1]) != number[-1]:
raise InvalidChecksum()
elif len(number) == 13:
ean.validate(number)
else:
raise InvalidLength()
if convert:
number = to_isbn13(number)
return number
def isbn_type(number):
"""Check the passed number and returns 'ISBN13', 'ISBN10' or None (for
invalid) for checking the type of number passed."""
try:
number = validate(number, convert=False)
except ValidationError:
return None
if len(number) == 10:
return 'ISBN10'
elif len(number) == 13:
return 'ISBN13'
def is_valid(number):
"""Checks to see if the number provided is a valid ISBN (either a legacy
10-digit one or a 13-digit one). This checks the length and the check
bit but does not check if the group and publisher are valid (use split()
for that)."""
try:
return bool(validate(number))
except ValidationError:
return False
def to_isbn13(number):
"""Convert the number to ISBN-13 format."""
number = number.strip()
min_number = compact(number, convert=False)
if len(min_number) == 13:
return number # nothing to do, already ISBN-13
# put new check digit in place
number = number[:-1] + ean.calc_check_digit('978' + min_number[:-1])
# add prefix
if ' ' in number:
return '978 ' + number
elif '-' in number:
return '978-' + number
else:
return '978' + number
def to_isbn10(number):
"""Convert the number to ISBN-10 format."""
number = number.strip()
min_number = compact(number, convert=False)
if len(min_number) == 10:
return number # nothing to do, already ISBN-13
elif isbn_type(min_number) != 'ISBN13':
raise InvalidFormat('Not a valid ISBN13.')
elif not number.startswith('978'):
raise InvalidFormat('Does not use 978 Bookland prefix.')
# strip EAN prefix
number = number[3:-1].strip().strip('-')
digit = _calc_isbn10_check_digit(min_number[3:-1])
# append the new check digit
if ' ' in number:
return number + ' ' + digit
elif '-' in number:
return number + '-' + digit
else:
return number + digit
def split(number, convert=False):
"""Split the specified ISBN into an EAN.UCC prefix, a group prefix, a
registrant, an item number and a check-digit. If the number is in ISBN-10
format the returned EAN.UCC prefix is '978'. If the covert parameter is
True the number is converted to ISBN-13 format first."""
from stdnum import numdb
# clean up number
number = compact(number, convert)
# get Bookland prefix if any
delprefix = False
if len(number) == 10:
number = '978' + number
delprefix = True
# split the number
result = numdb.get('isbn').split(number[:-1])
itemnr = result.pop() if result else ''
prefix = result.pop(0) if result else ''
group = result.pop(0) if result else ''
publisher = result.pop(0) if result else ''
# return results
return ('' if delprefix else prefix, group, publisher, itemnr, number[-1])
def format(number, separator='-', convert=False):
"""Reformat the passed number to the standard format with the EAN.UCC
prefix (if any), the group prefix, the registrant, the item number and
the check-digit separated (if possible) by the specified separator.
Passing an empty separator should equal compact() though this is less
efficient. If the covert parameter is True the number is converted to
ISBN-13 format first."""
return separator.join(x for x in split(number, convert) if x)

View file

@ -0,0 +1,33 @@
# generated from ISIL Registration Authority, downloaded from
# http://biblstandard.dk/isil/
AR$ country="Argentine Republic" ra_url="http://www.iram.org.ar" ra="Argentine Standardization and Certification Institute (IRAM)"
AT$ country="Austria" ra_url="http://www.obvsg.at" ra="Die Österreichische Bibliothekenverbund und Service GmbH"
AU$ country="Australia" ra_url="http://www.nla.gov.au/ilrs" ra="National Library of Australia"
BE$ country="Belgium" ra_url="http://www.kbr.be" ra="Royal Library of Belgium"
BY$ country="Belarus" ra_url="http://www.nlb.by/portal/page/portal/index?lang=en" ra="National Library of Belarus"
CA$ country="Canada" ra_url="http://www.collectionscanada.ca/ill/s16-206-e.html#3.2.2" ra="Library and Archives Canada"
CH$ country="Switzerland" ra_url="http://www.nb.admin.ch/slb/slb_professionnel/01540/index.html?lang=en" ra="Swiss National Library"
CY$ country="Cyprus" ra_url="http://www.cut.ac.cy/library/english/isil_reg_agent.htm" ra="Cyprus University of Technology Library"
DE$ country="Germany" ra_url="http://sigel.staatsbibliothek-berlin.de/" ra="Staatsbibliothek zu Berlin"
DK$ country="Denmark" ra_url="http://www.bibliotekogmedier.dk/engelsk/library/national-solutions/standards/danish-library-number/" ra="Danish Agency for Culture"
EG$ country="Egypt" ra_url="http://www.sti.sci.eg/focal_point.htm" ra="Egyptian National Scientific and Technical Information Network (ENSTINET)"
FI$ country="Finland" ra_url="http://www.lib.helsinki.fi/english/libraries/standards/ISIL.htm" ra="The National Library of Finland"
FR$ country="France" ra_url="http://www.abes.fr" ra="Agence Bibliographique de l'Enseignement Superieur"
GB$ country="United Kingdom" ra_url="http://www.bl.uk/bibliographic/isilagency.html" ra="British Library"
GL$ country="Greenland" ra_url="http://www.katak.gl/ISIL/Greenlandic_library_identifiers.html" ra="Central and Public Library of Greenland"
IL$ country="Israel" ra_url="http://nli.org.il/eng" ra="National Library of Israel"
IR$ country="Islamic Republic of Iran" ra_url="http://www.nlai.ir/special_services/stds/isil.htm" ra="National Library and Archives of Islamic Republic of Iran of Iran"
IT$ country="Italy" ra_url="http://www.iccu.sbn.it/genera.jsp?id=78&l=en" ra="Istituto Centrale per il Catalogo Unico delle biblioteche italiane e per le informazioni bibliografiche"
JP$ country="Japan" ra_url="http://www.ndl.go.jp/en/library/isil/index.html" ra="National Diet Library"
KR$ country="Republic of Korea" ra_url="http://www.nl.go.kr/isil/" ra="The National Library of Korea"
LU$ country="Luxembourg" ra_url="http://www.anlux.lu" ra="Archives nationales de Luxembourg"
NL$ country="The Netherlands" ra_url="http://www.kb.nl/expertise/voor-bibliotheken/interbibliotheciar-leenverkeer/internationale-standard-identifier-for-libraries-isil" ra="Koninklijke Bibliotheek, National Library of the Netherlands"
NO$ country="Norway" ra_url="http://www.nb.no/html/tildeling_av_nasjonalt_bibliot.html" ra="National Library of Norway"
NZ$ country="New Zealand" ra_url="http://www.natlib.govt.nz/en/services/6docsupply.html#sect1" ra="National Library of New Zealand Te Puna Mātauranga o Aotearoa"
RU$ country="Russian Federation" ra_url="http://english.gpntb.ru/" ra="Russian National Public Library for Science and Technology"
SI$ country="The Republic of Slovenia" ra_url="http://www.nuk.uni-lj.si/nukeng3.asp?id=311364382" ra="National and University Library"
US$ country="United States of America" ra="Library of Congress - under registration"
M$ country="Library of Congress - outside US" ra="Library of Congress - under registration"
O$ ra="See OCLC"
OCLC$ country="WorldCat Symbol" ra_url="http://www.oclc.org" ra="OCLC"
ZDB$ country="Staatsbibliothek zu Berlin - Zeitschriftendatenbank" ra="Staatsbibliothek zu Berlin"

View file

@ -0,0 +1,94 @@
# isil.py - functions for handling identifiers for libraries and related
# organizations
#
# Copyright (C) 2011, 2012, 2013 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
"""ISIL (International Standard Identifier for Libraries).
The ISIL is the International Standard Identifier for
Libraries and Related Organizations.
>>> validate('IT-RM0267')
'IT-RM0267'
>>> validate('OCLC-DLC')
'OCLC-DLC'
>>> validate('WW-RM0267') # unregistered country code
Traceback (most recent call last):
...
InvalidComponent: ...
>>> validate('WW-RM026712423345334534512334534545') # too long
Traceback (most recent call last):
...
InvalidLength: ...
>>> format('it-RM0267')
'IT-RM0267'
"""
from stdnum.exceptions import *
from stdnum.util import clean
# the valid characters in an ISIL
_alphabet = set('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-:/')
def compact(number):
"""Convert the ISIL to the minimal representation. This strips
surrounding whitespace."""
return clean(number, '').strip()
def _known_agency(agency):
"""Checks whether the specified agency is valid."""
# look it up in the db
from stdnum import numdb
results = numdb.get('isil').info(agency.upper() + '$')
# there should be only one part and it should have properties
return len(results) == 1 and bool(results[0][1])
def validate(number):
"""Checks to see if the number provided is a valid isil (or isilSV)
number."""
number = compact(number)
for n in number:
if n not in _alphabet:
raise InvalidFormat()
if len(number) > 15:
raise InvalidLength()
if not _known_agency(number.split('-')[0]):
raise InvalidComponent()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid isil (or isilSV)
number."""
try:
return bool(validate(number))
except ValidationError:
return False
def format(number):
"""Reformat the passed number to the standard format."""
number = compact(number)
parts = number.split('-')
if len(parts) > 1 and _known_agency(parts[0]):
parts[0] = parts[0].upper()
return '-'.join(parts)

View file

@ -0,0 +1,132 @@
# ismn.py - functions for handling ISMNs
#
# Copyright (C) 2010, 2011, 2012, 2013 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
"""ISMN (International Standard Music Number).
The ISMN (International Standard Music Number) is used to identify sheet
music. This module handles both numbers in the 10-digit 13-digit format.
>>> validate('979-0-3452-4680-5')
'9790345246805'
>>> validate('9790060115615')
'9790060115615'
>>> ismn_type(' M-2306-7118-7')
'ISMN10'
>>> validate('9790060115614')
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> compact(' 979-0-3452-4680-5')
'9790345246805'
>>> format('9790060115615')
'979-0-060-11561-5'
>>> format('M230671187')
'979-0-2306-7118-7'
>>> to_ismn13('M230671187')
'9790230671187'
"""
from stdnum import ean
from stdnum.exceptions import *
from stdnum.util import clean
def compact(number):
"""Convert the ISMN to the minimal representation. This strips the number
of any valid ISMN separators and removes surrounding whitespace."""
return clean(number, ' -.').strip().upper()
def validate(number):
"""Checks to see if the number provided is a valid ISMN (either a legacy
10-digit one or a 13-digit one). This checks the length and the check
bit but does not check if the publisher is known."""
number = compact(number)
if len(number) == 10:
if number[0] != 'M':
raise InvalidFormat()
ean.validate('9790' + number[1:])
else:
ean.validate(number)
return number
def ismn_type(number):
"""Check the type of ISMN number passed and return 'ISMN13', 'ISMN10'
or None (for invalid)."""
try:
number = validate(number)
except ValidationError:
return None
if len(number) == 10:
return 'ISMN10'
elif len(number) == 13:
return 'ISMN13'
def is_valid(number):
"""Checks to see if the number provided is a valid ISMN (either a legacy
10-digit one or a 13-digit one). This checks the length and the check
bit but does not check if the publisher is known."""
try:
return bool(validate(number))
except ValidationError:
return False
def to_ismn13(number):
"""Convert the number to ISMN13 (EAN) format."""
number = number.strip()
min_number = compact(number)
if len(min_number) == 13:
return number # nothing to do, already 13 digit format
# add prefix and strip the M
if ' ' in number:
return '979 0' + number[1:]
elif '-' in number:
return '979-0' + number[1:]
else:
return '9790' + number[1:]
# these are the ranges allocated to publisher codes
_ranges = (
(3, '000', '099'), (4, '1000', '3999'), (5, '40000', '69999'),
(6, '700000', '899999'), (7, '9000000', '9999999'))
def split(number):
"""Split the specified ISMN into a bookland prefix (979), an ISMN
prefix (0), a publisher element (3 to 7 digits), an item element (2 to
6 digits) and a check digit."""
# clean up number
number = to_ismn13(compact(number))
# rind the correct range and split the number
for length, low, high in _ranges:
if low <= number[4:4 + length] <= high:
return (number[:3], number[3], number[4:4 + length],
number[4 + length:-1], number[-1])
def format(number, separator='-'):
"""Reformat the passed number to the standard format with the
prefixes, the publisher element, the item element and the check-digit
separated by the specified separator. The number is converted to the
13-digit format silently."""
return separator.join(x for x in split(number) if x)

View file

@ -0,0 +1,30 @@
# __init__.py - functions for performing the ISO 7064 algorithms
#
# Copyright (C) 2010, 2012 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
"""Collection of the ISO 7064 algorithms.
This package provides a number of modules for calculation and verification
of numbers using one of the ISO 7064 algorithms.
Note that these functions were not implemented using the ISO text itself
because the text is not available for free. These functions were
implemented based on information on the algorithms found online and some
reverse engineering. If anyone can provide a legal copy of the ISO/IEC
7064 standard these functions can be validated and perhaps improved.
"""

View file

@ -0,0 +1,71 @@
# mod_11_10.py - functions for performing the ISO 7064 Mod 11, 10 algorithm
#
# Copyright (C) 2010, 2011, 2012, 2013 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
"""The ISO 7064 Mod 11, 10 algorithm.
The Mod 11, 10 algorithm uses a number of calculations modulo 11 and 10 to
determine a checksum.
For a module that can do generic Mod x+1, x calculations see the
:mod:`stdnum.iso7064.mod_37_36` module.
>>> calc_check_digit('79462')
'3'
>>> validate('794623')
'794623'
>>> calc_check_digit('00200667308')
'5'
>>> validate('002006673085')
'002006673085'
"""
from stdnum.exceptions import *
def checksum(number):
"""Calculate the checksum. A valid number should have a checksum of 1."""
check = 5
for n in number:
check = (((check or 10) * 2) % 11 + int(n)) % 10
return check
def calc_check_digit(number):
"""With the provided number, calculate the extra digit that should be
appended to make it a valid number."""
return str((1 - ((checksum(number) or 10) * 2) % 11) % 10)
def validate(number):
"""Checks whether the check digit is valid."""
try:
valid = checksum(number) == 1
except:
raise InvalidFormat()
if not valid:
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks whether the check digit is valid."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,74 @@
# mod_11_2.py - functions for performing the ISO 7064 Mod 11, 2 algorithm
#
# Copyright (C) 2010, 2011, 2012, 2013 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
"""The ISO 7064 Mod 11, 2 algorithm.
The Mod 11, 2 algorithm is a simple module 11 checksum where the check
digit can be an X to make the number valid.
For a module that can do generic Mod x, 2 calculations see the
:mod:`stdnum.iso7064.mod_37_2` module.
>>> calc_check_digit('0794')
'0'
>>> validate('07940')
'07940'
>>> calc_check_digit('079')
'X'
>>> validate('079X')
'079X'
>>> checksum('079X')
1
"""
from stdnum.exceptions import *
def checksum(number):
"""Calculate the checksum. A valid number should have a checksum of 1."""
check = 0
for n in number:
check = (2 * check + int(10 if n == 'X' else n)) % 11
return check
def calc_check_digit(number):
"""With the provided number, calculate the extra digit that should be
appended to make it a valid number."""
c = (1 - 2 * checksum(number)) % 11
return 'X' if c == 10 else str(c)
def validate(number):
"""Checks whether the check digit is valid."""
try:
valid = checksum(number) == 1
except:
raise InvalidFormat()
if not valid:
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks whether the check digit is valid."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,78 @@
# mod_37_2.py - functions for performing the ISO 7064 Mod 37, 2 algorithm
#
# Copyright (C) 2010, 2011, 2012, 2013 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
"""The ISO 7064 Mod 37, 2 algorithm.
The Mod 37, 2 checksum can be used for alphanumeric numbers and the check
digit may also be numeric, a letter or '*'.
>>> calc_check_digit('G123489654321')
'Y'
>>> validate('G123489654321Y')
'G123489654321Y'
>>> checksum('G123489654321Y')
1
By changing the alphabet this can be turned into any Mod x, 2
algorithm. For example Mod 11, 2:
>>> calc_check_digit('079', alphabet='0123456789X')
'X'
>>> validate('079X', alphabet='0123456789X')
'079X'
>>> checksum('079X', alphabet='0123456789X')
1
"""
from stdnum.exceptions import *
def checksum(number, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ*'):
"""Calculate the checksum. A valid number should have a checksum of 1."""
modulus = len(alphabet)
check = 0
for n in number:
check = (2 * check + alphabet.index(n)) % modulus
return check
def calc_check_digit(number, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ*'):
"""With the provided number, calculate the extra digit that should be
appended to make it a valid number."""
modulus = len(alphabet)
return alphabet[(1 - 2 * checksum(number, alphabet)) % modulus]
def validate(number, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ*'):
"""Checks whether the check digit is valid."""
try:
valid = checksum(number, alphabet) == 1
except:
raise InvalidFormat()
if not valid:
raise InvalidChecksum()
return number
def is_valid(number, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ*'):
"""Checks whether the check digit is valid."""
try:
return bool(validate(number, alphabet))
except ValidationError:
return False

View file

@ -0,0 +1,76 @@
# mod_37_36.py - functions for performing the ISO 7064 Mod 37, 36 algorithm
#
# Copyright (C) 2010, 2011, 2012, 2013 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
"""The ISO 7064 Mod 37, 36 algorithm.
The Mod 37, 36 algorithm uses an alphanumeric check digit and the number
itself may also contain letters.
>>> checksum('A12425GABC1234002M')
1
>>> calc_check_digit('A12425GABC1234002')
'M'
>>> validate('A12425GABC1234002M')
'A12425GABC1234002M'
By changing the alphabet this can be turned into any Mod x+1, x
algorithm. For example Mod 11, 10:
>>> calc_check_digit('00200667308', alphabet='0123456789')
'5'
>>> validate('002006673085', alphabet='0123456789')
'002006673085'
"""
from stdnum.exceptions import *
def checksum(number, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
"""Calculate the checksum. A valid number should have a checksum of 1."""
modulus = len(alphabet)
check = modulus // 2
for n in number:
check = (((check or modulus) * 2) % (modulus + 1) + alphabet.index(n)) % modulus
return check
def calc_check_digit(number, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
"""With the provided number, calculate the extra digit that should be
appended to make it a valid number."""
modulus = len(alphabet)
return alphabet[(1 - ((checksum(number, alphabet) or modulus) * 2) % (modulus + 1)) % modulus]
def validate(number, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
"""Checks whether the check digit is valid."""
try:
valid = checksum(number, alphabet) == 1
except:
raise InvalidFormat()
if not valid:
raise InvalidChecksum()
return number
def is_valid(number, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
"""Checks whether the check digit is valid."""
try:
return bool(validate(number, alphabet))
except ValidationError:
return False

View file

@ -0,0 +1,67 @@
# mod_97_10.py - functions for performing the ISO 7064 Mod 97, 10 algorithm
#
# Copyright (C) 2010, 2011, 2012, 2013 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
"""The ISO 7064 Mod 97, 10 algorithm.
The Mod 97, 10 algorithm evaluates the whole number as an integer which is
valid if the number modulo 97 is 1. As such it has two check digits.
>>> calc_check_digits('99991234567890121414')
'90'
>>> validate('9999123456789012141490')
'9999123456789012141490'
>>> calc_check_digits('4354111611551114')
'31'
>>> validate('08686001256515001121751')
'08686001256515001121751'
>>> calc_check_digits('22181321402534321446701611')
'35'
"""
from stdnum.exceptions import *
def checksum(number):
"""Calculate the checksum. A valid number should have a checksum of 1."""
return int(number) % 97
def calc_check_digits(number):
"""With the provided number, calculate the extra digits that should be
appended to make it a valid number."""
return '%02d' % ((98 - 100 * checksum(number)) % 97)
def validate(number):
"""Checks whether the check digit is valid."""
try:
valid = checksum(number) == 1
except:
raise InvalidFormat()
if not valid:
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks whether the check digit is valid."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,93 @@
# issn.py - functions for handling ISSNs
#
# Copyright (C) 2010, 2011, 2012, 2013 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
"""ISSN (International Standard Serial Number).
The ISSN (International Standard Serial Number) is the standard code to
identify periodical publications. It has a checksum similar to ISBN-10.
>>> validate('0024-9319')
'00249319'
>>> validate('0032147X')
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> validate('003214712')
Traceback (most recent call last):
...
InvalidLength: ...
>>> compact('0032-1478')
'00321478'
>>> format('00249319')
'0024-9319'
>>> to_ean('0264-3596')
'9770264359008'
"""
from stdnum import ean
from stdnum.exceptions import *
from stdnum.util import clean
def compact(number):
"""Convert the ISSN to the minimal representation. This strips the number
of any valid ISSN separators and removes surrounding whitespace."""
return clean(number, ' -').strip().upper()
def calc_check_digit(number):
"""Calculate the ISSN check digit for 10-digit numbers. The number passed
should not have the check bit included."""
check = (11 - sum((8 - i) * int(n)
for i, n in enumerate(number))) % 11
return 'X' if check == 10 else str(check)
def validate(number):
"""Checks to see if the number provided is a valid ISSN. This checks
the length and whether the check digit is correct."""
number = compact(number)
if not number[:-1].isdigit():
raise InvalidFormat()
if len(number) != 8:
raise InvalidLength()
if calc_check_digit(number[:-1]) != number[-1]:
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid ISSN. This checks
the length and whether the check digit is correct."""
try:
return bool(validate(number))
except ValidationError:
return False
def format(number):
"""Reformat the passed number to the standard format."""
number = compact(number)
return number[:4] + '-' + number[4:]
def to_ean(number, issue_code='00'):
"""Convert the number to EAN-13 format."""
number = '977' + validate(number)[:-1] + issue_code
return number + ean.calc_check_digit(number)

View file

@ -0,0 +1,24 @@
# __init__.py - collection of Italian numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of Italian numbers."""
# provide vat as an alias
from stdnum.it import iva as vat

View file

@ -0,0 +1,73 @@
# iva.py - functions for handling Italian VAT numbers
#
# Copyright (C) 2012, 2013 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
"""Partita IVA (Italian VAT number).
The Partita IVA (Imposta sul valore aggiunto) consists of 11 digits. The
first 7 digits are a company identifier, the next 3 refer to the province
of residence and the last is a check digit.
The fiscal code for individuals is not accepted as valid code for
intracommunity VAT related operations so it is ignored here.
>>> validate('IT 00743110157')
'00743110157'
>>> validate('00743110158') # invalid check digit
Traceback (most recent call last):
...
InvalidChecksum: ...
"""
from stdnum import luhn
from stdnum.exceptions import *
from stdnum.util import clean
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' -:').upper().strip()
if number.startswith('IT'):
number = number[2:]
return number
def validate(number):
"""Checks to see if the number provided is a valid VAT number. This checks
the length, formatting and check digit."""
number = compact(number)
if not number.isdigit() or int(number[0:7]) == 0:
raise InvalidFormat()
if len(number) != 11:
raise InvalidLength()
# check the province of residence
if not('001' <= number[7:10] <= '100') and \
number[7:10] not in ('120', '121', '888', '999'):
raise InvalidComponent()
luhn.validate(number)
return number
def is_valid(number):
"""Checks to see if the number provided is a valid VAT number. This checks
the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,24 @@
# __init__.py - collection of Lithuanian numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of Lithuanian numbers."""
# provide vat as an alias
from stdnum.lt import pvm as vat

View file

@ -0,0 +1,89 @@
# pvm.py - functions for handling Lithuanian VAT numbers
# coding: utf-8
#
# Copyright (C) 2012, 2013 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
"""PVM (Pridėtinės vertės mokestis mokėtojo kodas, Lithuanian VAT number).
The PVM is used for VAT purposes in Lithuania. It is 9 digits (for legal
entities) or 12 digits long (for temporarily registered taxpayers). This
module does not check the format of Lithuanian personal codes (Asmens
kodas).
>>> validate('119511515') # organisation
'119511515'
>>> validate('LT 100001919017') # temporarily registered
'100001919017'
>>> validate('100001919018') # invalid check digit
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> validate('100004801610') # second step in check digit calculation
'100004801610'
"""
from stdnum.exceptions import *
from stdnum.util import clean
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' -').upper().strip()
if number.startswith('LT'):
number = number[2:]
return number
def calc_check_digit(number):
"""Calculate the check digit. The number passed should not have the
check digit included."""
check = sum((1 + i % 9) * int(n) for i, n in enumerate(number)) % 11
if check == 10:
check = sum((1 + (i + 2) % 9) * int(n) for i, n in enumerate(number))
return str(check % 11 % 10)
def validate(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
number = compact(number)
if not number.isdigit():
raise InvalidFormat()
if len(number) == 9:
# legal entities
if number[7] != '1':
raise InvalidComponent()
elif len(number) == 12:
# temporary tax payers and natural persons
if number[10] != '1':
raise InvalidComponent()
else:
raise InvalidLength()
if calc_check_digit(number[:-1]) != number[-1]:
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,24 @@
# __init__.py - collection of Luxembourgian numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of Luxembourgian numbers."""
# provide vat as an alias
from stdnum.lu import tva as vat

View file

@ -0,0 +1,72 @@
# tva.py - functions for handling Luxembourgian VAT numbers
# coding: utf-8
#
# Copyright (C) 2012, 2013 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
"""TVA (taxe sur la valeur ajoutée, Luxembourgian VAT number).
The n° TVA (Numéro d'identification à la taxe sur la valeur ajoutée) is
used for tax purposes in Luxembourg. The number consists of 8 digits of
which the last two are check digits.
>>> validate('LU 150 274 42')
'15027442'
>>> validate('150 274 43') # invalid check digits
Traceback (most recent call last):
...
InvalidChecksum: ...
"""
from stdnum.exceptions import *
from stdnum.util import clean
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' :.-').upper().strip()
if number.startswith('LU'):
number = number[2:]
return number
def calc_check_digits(number):
"""Calculate the check digits for the number."""
return '%02d' % (int(number) % 89)
def validate(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
number = compact(number)
if not number.isdigit():
raise InvalidFormat()
if len(number) != 8:
raise InvalidLength()
if calc_check_digits(number[:6]) != number[-2:]:
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,86 @@
# luhn.py - functions for performing the Luhn and Luhn mod N algorithms
#
# Copyright (C) 2010, 2011, 2012, 2013 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
"""The Luhn and Luhn mod N algorithms.
The Luhn algorithm is used to detect most accidental errors in various
identification numbers.
>>> validate('7894')
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> checksum('7894')
6
>>> calc_check_digit('7894')
'9'
>>> validate('78949')
'78949'
An alternative alphabet can be provided to use the Luhn mod N algorithm.
The default alphabet is '0123456789'.
>>> validate('1234', alphabet='0123456789abcdef')
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> checksum('1234', alphabet='0123456789abcdef')
14
"""
from stdnum.exceptions import *
def checksum(number, alphabet='0123456789'):
"""Calculate the Luhn checksum over the provided number. The checksum
is returned as an int. Valid numbers should have a checksum of 0."""
n = len(alphabet)
number = tuple(alphabet.index(i)
for i in reversed(str(number)))
return (sum(number[::2]) +
sum(sum(divmod(i * 2, n))
for i in number[1::2])) % n
def validate(number, alphabet='0123456789'):
"""Checks to see if the number provided passes the Luhn checksum."""
if not bool(number):
raise InvalidFormat()
try:
valid = checksum(number, alphabet) == 0
except:
raise InvalidFormat()
if not valid:
raise InvalidChecksum()
return number
def is_valid(number, alphabet='0123456789'):
"""Checks to see if the number provided passes the Luhn checksum."""
try:
return bool(validate(number, alphabet))
except ValidationError:
return False
def calc_check_digit(number, alphabet='0123456789'):
"""With the provided number, calculate the extra digit that should be
appended to make it pass the Luhn checksum."""
ck = checksum(str(number) + alphabet[0], alphabet)
return alphabet[-ck]

View file

@ -0,0 +1,24 @@
# __init__.py - collection of Latvian numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of Latvian numbers."""
# provide vat as an alias
from stdnum.lv import pvn as vat

View file

@ -0,0 +1,116 @@
# pvn.py - functions for handling Latvian PVN (VAT) numbers
# coding: utf-8
#
# Copyright (C) 2012, 2013 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
"""PVN (Pievienotās vērtības nodokļa, Latvian VAT number).
The PVN is a 11-digit number that can either be a reference to a legal
entity (in which case the first digit > 3) or a natural person (in which
case it should be the same as the personal code (personas kods)). Personal
codes start with 6 digits to denote the birth date in the form ddmmyy.
>>> validate('LV 4000 3521 600')
'40003521600'
>>> validate('40003521601') # invalid check digit
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> validate('161175-19997') # personal code
'16117519997'
>>> validate('161375-19997') # invalid date
Traceback (most recent call last):
...
InvalidComponent: ...
"""
import datetime
from stdnum.exceptions import *
from stdnum.util import clean
# validation functions are available on-line but it is not allowed
# to perform automated queries:
# http://www6.vid.gov.lv/VID_PDB?aspxerrorpath=/vid_pdb/pvn.asp
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' -').upper().strip()
if number.startswith('LV'):
number = number[2:]
return number
def checksum(number):
"""Calculate the checksum for legal entities."""
weights = (9, 1, 4, 8, 3, 10, 2, 5, 7, 6, 1)
return sum(weights[i] * int(n) for i, n in enumerate(number)) % 11
def calc_check_digit_pers(number):
"""Calculate the check digit for personal codes. The number passed
should not have the check digit included."""
# note that this algorithm has not been confirmed by an independent source
weights = (10, 5, 8, 4, 2, 1, 6, 3, 7, 9)
check = 1 + sum(weights[i] * int(n) for i, n in enumerate(number))
return str(check % 11 % 10)
def get_birth_date(number):
"""Split the date parts from the number and return the birth date."""
day = int(number[0:2])
month = int(number[2:4])
year = int(number[4:6])
year += 1800 + int(number[6]) * 100
try:
return datetime.date(year, month, day)
except ValueError:
raise InvalidComponent()
def validate(number):
"""Checks to see if the number provided is a valid VAT number. This checks
the length, formatting and check digit."""
number = compact(number)
if not number.isdigit():
raise InvalidFormat()
if len(number) != 11:
raise InvalidLength()
if number[0] > '3':
# legal entity
if checksum(number) != 3:
raise InvalidChecksum()
else:
# natural resident, check if birth date is valid
birth_date = get_birth_date(number)
# TODO: check that the birth date is not in the future
if calc_check_digit_pers(number[:-1]) != number[-1]:
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid VAT number. This checks
the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,189 @@
# meid.py - functions for handling Mobile Equipment Identifiers (MEIDs)
#
# Copyright (C) 2010, 2011, 2012, 2013 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
"""MEID (Mobile Equipment Identifier).
The Mobile Equipment Identifier is used to identify a physical piece of
CDMA mobile station equipment.
>>> validate('AF 01 23 45 0A BC DE C')
'AF0123450ABCDE'
>>> validate('29360 87365 0070 3710 0')
'AF0123450ABCDE'
>>> validate('29360 87365 0070 3710 1')
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> format('af0123450abcDEC', add_check_digit=True)
'AF 01 23 45 0A BC DE C'
>>> format('af0123450abcDEC', format='dec', add_check_digit=True)
'29360 87365 0070 3710 0'
"""
from stdnum.exceptions import *
from stdnum.util import clean
_hex_alphabet = '0123456789ABCDEF'
def _cleanup(number):
"""Remove any grouping information from the number and removes surrounding
whitespace."""
return clean(str(number), ' -').strip().upper()
def _ishex(number):
for x in number:
if x not in _hex_alphabet:
return False
return True
def _parse(number):
number = _cleanup(number)
if len(number) in (14, 15):
# 14 or 15 digit hex representation
if not _ishex(number):
raise InvalidFormat()
return number[0:14], number[14:]
elif len(number) in (18, 19):
# 18-digit decimal representation
if not number.isdigit():
raise InvalidFormat()
return number[0:18], number[18:]
else:
raise InvalidLength()
def calc_check_digit(number):
"""Calculate the check digit for the number. The number should not
already have a check digit."""
# both the 18-digit decimal format and the 14-digit hex format
# containing only decimal digits should use the decimal Luhn check
from stdnum import luhn
if number.isdigit():
return luhn.calc_check_digit(number)
else:
return luhn.calc_check_digit(number, alphabet=_hex_alphabet)
def compact(number, strip_check_digit=True):
"""Convert the MEID number to the minimal (hexadecimal) representation.
This strips grouping information, removes surrounding whitespace and
converts to hexadecimal if needed. If the check digit is to be preserved
and conversion is done a new check digit is recalculated."""
# first parse the number
number, cd = _parse(number)
# strip check digit if needed
if strip_check_digit:
cd = ''
# convert to hex if needed
if len(number) == 18:
number = '%08X%06X' % (int(number[0:10]), int(number[10:18]))
if cd:
cd = calc_check_digit(number)
# put parts back together again
return number + cd
def validate(number, strip_check_digit=True):
"""Checks to see if the number provided is a valid MEID number. This
converts the representation format of the number (if it is
decimal it is not converted to hexadecimal)."""
from stdnum import luhn
# first parse the number
number, cd = _parse(number)
if len(number) == 18:
# decimal format can be easily determined
if cd:
luhn.validate(number + cd)
# convert to hex
number = '%08X%06X' % (int(number[0:10]), int(number[10:18]))
cd = calc_check_digit(number)
elif number.isdigit():
# if the remaining hex format is fully decimal it is an IMEI number
from stdnum import imei
imei.validate(number + cd)
else:
# normal hex Luhn validation
if cd:
luhn.validate(number + cd, alphabet=_hex_alphabet)
if strip_check_digit:
cd = ''
return number + cd
def is_valid(number):
"""Checks to see if the number provided is a valid MEID number."""
try:
return bool(validate(number))
except ValidationError:
return False
def format(number, separator=' ', format=None, add_check_digit=False):
"""Reformat the passed number to the standard format. The separator
used can be provided. If the format is specified (either 'hex' or
'dec') the number is reformatted in that format, otherwise the current
representation is kept. If add_check_digit is True a check digit will
be added if it is not present yet."""
# first parse the number
number, cd = _parse(number)
# format conversions if needed
if format == 'dec' and len(number) == 14:
# convert to decimal
number = '%010d%08d' % (int(number[0:8], 16), int(number[8:14], 16))
if cd:
cd = calc_check_digit(number)
elif format == 'hex' and len(number) == 18:
# convert to hex
number = '%08X%06X' % (int(number[0:10]), int(number[10:18]))
if cd:
cd = calc_check_digit(number)
# see if we need to add a check digit
if add_check_digit and not cd:
cd = calc_check_digit(number)
# split number according to format
if len(number) == 14:
number = [number[i * 2:i * 2 + 2]
for i in range(7)] + [cd]
else:
number = (number[:5], number[5:10], number[10:14], number[14:], cd)
return separator.join(x for x in number if x)
def to_binary(number):
"""Convert the number to it's binary representation (without the check
digit)."""
import sys
number = compact(number, strip_check_digit=True)
if sys.version > '3': # pragma: no cover (Python 2/3 specific code)
return bytes.fromhex(number)
else: # pragma: no cover (Python 2/3 specific code)
return number.decode('hex')
def to_pseudo_esn(number):
"""Convert the provided MEID to a pseudo ESN (pESN). The ESN is returned
in compact hexadecimal representation."""
import hashlib
# return the last 6 digits of the SHA1 hash prefixed with the reserved
# manufacturer code
return '80' + hashlib.sha1(to_binary(number)).hexdigest()[-6:].upper()

View file

@ -0,0 +1,21 @@
# __init__.py - collection of Maltese numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of Maltese numbers."""

View file

@ -0,0 +1,71 @@
# vat.py - functions for handling Maltese VAT numbers
#
# Copyright (C) 2012, 2013 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
"""VAT (Maltese VAT number).
The Maltese VAT registration number contains 8 digits and uses a simple
weigted checksum.
>>> validate('MT 1167-9112')
'11679112'
>>> validate('1167-9113') # invalid check digits
Traceback (most recent call last):
...
InvalidChecksum: ...
"""
from stdnum.exceptions import *
from stdnum.util import clean
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' -').upper().strip()
if number.startswith('MT'):
number = number[2:]
return number
def checksum(number):
"""Calculate the checksum."""
weights = (3, 4, 6, 7, 8, 9, 10, 1)
return sum(weights[i] * int(n) for i, n in enumerate(number)) % 37
def validate(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
number = compact(number)
if not number.isdigit() or number[0] == '0':
raise InvalidFormat()
if len(number) != 8:
raise InvalidLength()
if checksum(number) != 0:
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,21 @@
# __init__.py - collection of Malaysian numbers
# coding: utf-8
#
# Copyright (C) 2013 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
"""Collection of Malaysian numbers."""

View file

@ -0,0 +1,86 @@
# generated from National Registration Department of Malaysia, downloaded from
# http://www.jpn.gov.my/en/informasi/states-code
# http://www.jpn.gov.my/en/informasi/country-code
01 state="Johor" country="Malaysia" countries="Malaysia"
02 state="Kedah" country="Malaysia" countries="Malaysia"
03 state="Kelantan" country="Malaysia" countries="Malaysia"
04 state="Melaka" country="Malaysia" countries="Malaysia"
05 state="Negeri Sembilan" country="Malaysia" countries="Malaysia"
06 state="Pahang" country="Malaysia" countries="Malaysia"
07 state="Pulau Pinang" country="Malaysia" countries="Malaysia"
08 state="Perak" country="Malaysia" countries="Malaysia"
09 state="Perlis" country="Malaysia" countries="Malaysia"
10 state="Selangor" country="Malaysia" countries="Malaysia"
11 state="Terengganu" country="Malaysia" countries="Malaysia"
12 state="Sabah" country="Malaysia" countries="Malaysia"
13 state="Sarawak" country="Malaysia" countries="Malaysia"
14 state="Wilayah Persekutuan (Kuala Lumpur)" country="Malaysia" countries="Malaysia"
15 state="Wilayah Persekutuan (Labuan)" country="Malaysia" countries="Malaysia"
16 state="Wilayah Persekutuan (Putrajaya)" country="Malaysia" countries="Malaysia"
21 state="Johor" country="Malaysia" countries="Malaysia"
22 state="Johor" country="Malaysia" countries="Malaysia"
23 state="Johor" country="Malaysia" countries="Malaysia"
24 state="Johor" country="Malaysia" countries="Malaysia"
25 state="Kedah" country="Malaysia" countries="Malaysia"
26 state="Kedah" country="Malaysia" countries="Malaysia"
27 state="Kedah" country="Malaysia" countries="Malaysia"
28 state="Kelantan" country="Malaysia" countries="Malaysia"
29 state="Kelantan" country="Malaysia" countries="Malaysia"
30 state="Melaka" country="Malaysia" countries="Malaysia"
31 state="Negeri Sembilan" country="Malaysia" countries="Malaysia"
32 state="Pahang" country="Malaysia" countries="Malaysia"
33 state="Pahang" country="Malaysia" countries="Malaysia"
34 state="Pulau Pinang" country="Malaysia" countries="Malaysia"
35 state="Pulau Pinang" country="Malaysia" countries="Malaysia"
36 state="Perak" country="Malaysia" countries="Malaysia"
37 state="Perak" country="Malaysia" countries="Malaysia"
38 state="Perak" country="Malaysia" countries="Malaysia"
39 state="Perak" country="Malaysia" countries="Malaysia"
40 state="Perlis" country="Malaysia" countries="Malaysia"
41 state="Selangor" country="Malaysia" countries="Malaysia"
42 state="Selangor" country="Malaysia" countries="Malaysia"
43 state="Selangor" country="Malaysia" countries="Malaysia"
44 state="Selangor" country="Malaysia" countries="Malaysia"
45 state="Terengganu" country="Malaysia" countries="Malaysia"
46 state="Terengganu" country="Malaysia" countries="Malaysia"
47 state="Sabah" country="Malaysia" countries="Malaysia"
48 state="Sabah" country="Malaysia" countries="Malaysia"
49 state="Sabah" country="Malaysia" countries="Malaysia"
50 state="Sarawak" country="Malaysia" countries="Malaysia"
51 state="Sarawak" country="Malaysia" countries="Malaysia"
52 state="Sarawak" country="Malaysia" countries="Malaysia"
53 state="Sarawak" country="Malaysia" countries="Malaysia"
54 state="Wilayah Persekutuan (Kuala Lumpur)" country="Malaysia" countries="Malaysia"
55 state="Wilayah Persekutuan (Kuala Lumpur)" country="Malaysia" countries="Malaysia"
56 state="Wilayah Persekutuan (Kuala Lumpur)" country="Malaysia" countries="Malaysia"
57 state="Wilayah Persekutuan (Kuala Lumpur)" country="Malaysia" countries="Malaysia"
58 state="Wilayah Persekutuan (Labuan)" country="Malaysia" countries="Malaysia"
59 state="Negeri Sembilan" country="Malaysia" countries="Malaysia"
60 country="Brunei" countries="Brunei"
61 country="Indonesia" countries="Indonesia"
62 countries="Cambodia, Kampuchea"
63 country="Laos" countries="Laos"
64 country="Mynmar" countries="Mynmar"
65 country="Filipina" countries="Filipina"
66 country="Singapura" countries="Singapura"
67 country="Thailand" countries="Thailand"
68 country="Vietnam" countries="Vietnam"
74 country="China" countries="China"
75 country="India" countries="India"
76 country="Pakistan" countries="Pakistan"
77 country="Arab Saudi" countries="Arab Saudi"
78 country="Sri Lanka" countries="Sri Lanka"
79 country="Bangladesh" countries="Bangladesh"
82 state="Negeri Tidak Diketahui" country="Malaysia" countries="Malaysia"
83 countries="Australia, American Samoa, Macedonia, New Zealand, New Caledonia, Papua New Gurney, Fiji, Timor Leste"
84 countries="Argentina, Anguilla, Aruba, Bolivia, Brazil, Paraguay, Peru, Chile, Colombia, Equador, Uruguay, Venezuela"
85 countries="Algeria, Angola, Kenya, Afrika Tengah, Liberia, Afrika Selatan, Mali, Mauritania, Morocco, Malawi, Botswana, Mozambique, Burundi, Nigeria, Namibia, Cameroon, Chad, Rwanda, Senegal, Sierra Leone, Somalia, Djibouti, Sudan, Egypt, Ethopia, Swaziland, Eritrea, Gambia, Ghana, Tunisia, Tanzania, Tonga, Togo, Uganda, Zaire, Zambia, Zimbabwe"
86 countries="Austria, Luxembourg, Armenia, Malta, Monaco, Belgium, Nitherlands, Norway, Cyprus, Portugal, Denmark, Sweeden, Spain, Switzerland, France, Finland, Slovakia, Slovenia, Greece, Germany, Holy See (Vatican City), Italy"
87 countries="Britain, Ireland"
88 countries="Jordan, Kuwait, Lebanon, Bahrain, Oman, Qatar, Syria, Turkey, United Arab Emirate, Iran, Iraq, Israel, Yemen"
89 countries="Japan, Korea Selatan, Korea Utara, Taiwan"
90 countries="Jamaica, Bahamas, Barbados, Belize, Mexico, Nicaragua, Panama, Puerto Rico, Costa Rica, Cuba, Dominica, El Salvador, Grenada, Guatemala, Trinidad&Tobado, Haiti, Honduras"
91 countries="Canada, Greenland, United State"
92 countries="Albania, Albania, Latvia, Lithuania, Bulgaria, Byelorussia, Bosnia, Belarus, Poland, Romania, Russia, Czechoslovakia, Crotia, Esthonia, Serbia, Georgia, Hungary, Ukraine"
93 countries="Afghanistan, Antigua & Barbuda, Kazakhstan, Andorra/Andora, Libya, Arzebaijan, Antartica, Maldives, Madagascar, Mauritius, Mongolia, Benin, Maghribi, Bhutan, Macau, Nepal, Bermuda, Burkina faso/Burkina, Bora-bora, Bouvet Island, Palestine, Cape Verde, Comoros, Seychelles, Soloman Islands, Samoa, San Marino, Guinea, Gibraltar, Tajikistan, Tukmenistan, Hong Kong, Uzbekistan, Ivory Coast, Vanuatu, Iceland, Yugoslavia"

View file

@ -0,0 +1,112 @@
# nric.py - functions for handling NRIC numbers
#
# Copyright (C) 2013 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
"""NRIC No. (Malaysian National Registration Identity Card Number).
The NRIC No. is the unique identifier for issued to Malaysian citizens and
permanent residents.
The number consist of 12 digits in three sections. The first 6 digits
represent the birth date, followed by two digits represeting the birth
place and finally four digits. The gender of a person can be derived from
the last digit: odd numbers for males and even numbers for females.
>>> validate('770305-02-1234')
'770305021234'
>>> validate('771305-02-1234') # invalid date
Traceback (most recent call last):
...
InvalidComponent: ...
>>> validate('770305-99-1234') # unknown birth place code
Traceback (most recent call last):
...
InvalidComponent: ...
>>> format('770305021234')
'770305-02-1234'
"""
import datetime
from stdnum.exceptions import *
from stdnum.util import clean
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 get_birth_date(number):
"""Split the date parts from the number and return the birth date.
Note that in some cases it may return the registration date instead of
the birth date and it may be a century off."""
number = compact(number)
year = int(number[0:2])
month = int(number[2:4])
day = int(number[4:6])
# this is a bit broken but it's easy
try:
return datetime.date(year + 1900, month, day)
except ValueError:
pass
try:
return datetime.date(year + 2000, month, day)
except ValueError:
raise InvalidComponent()
def get_birth_place(number):
"""Use the number to look up the place of birth of the person. This can
either be a state or federal territory within Malaysia or a country
outside of Malaysia."""
from stdnum import numdb
number = compact(number)
results = numdb.get('my/bp').info(number[6:8])[0][1]
if not results:
raise InvalidComponent()
return results
def validate(number):
"""Checks to see if the number provided is a valid NRIC numbers. This
checks the length, formatting and birth date and place."""
number = compact(number)
if len(number) != 12:
raise InvalidLength()
if not number.isdigit():
raise InvalidFormat()
get_birth_date(number)
get_birth_place(number)
return number
def is_valid(number):
"""Checks to see if the number provided is a valid NRIC numbers. This
checks the length, formatting and birth date and place."""
try:
return bool(validate(number))
except ValidationError:
return False
def format(number):
"""Reformat the passed number to the standard format."""
number = compact(number)
return number[:6] + '-' + number[6:8] + '-' + number[8:]

View file

@ -0,0 +1,25 @@
# __init__.py - collection of Dutch numbers
# coding: utf-8
#
# Copyright (C) 2012, 2013 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
"""Collection of Dutch numbers."""
# provide aliases
from stdnum.nl import btw as vat
from stdnum.nl import postcode as postcal_code

View file

@ -0,0 +1,80 @@
# brin.py - functions for handling Brin numbers
#
# Copyright (C) 2013 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
"""Brin number (Dutch number for schools).
The Brin (Basis Registratie Instellingen) is a number to identify schools
and related institutions. The number consists of four alphanumeric
characters, sometimes extended with two digits to indicate the site (this
complete code is called the vestigingsnummer).
The register of these numbers can be downloaded from:
http://www.duo.nl/organisatie/open_onderwijsdata/databestanden/default.asp
>>> validate('05 KO')
'05KO'
>>> validate('07NU 00')
'07NU00'
>>> validate('12KB1')
Traceback (most recent call last):
...
InvalidLength: ...
>>> validate('30AJ0A') # location code has letter
Traceback (most recent call last):
...
InvalidFormat: ...
"""
import re
from stdnum.exceptions import *
from stdnum.util import clean
# this regular expression is based on what was found in the online
# database: the first two digits are always numeric, followed by two
# letters and an optional two letter location identifier
_brin_re = re.compile(r'^(?P<brin>[0-9]{2}[A-Z]{2})(?P<location>[0-9]{2})?$')
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, ' -.').upper().strip()
def validate(number):
"""Checks to see if the number provided is in the correct format.
This currently does not check whether the number points to a
registered school."""
number = compact(number)
if len(number) not in (4, 6):
raise InvalidLength()
match = _brin_re.search(number)
if not match:
raise InvalidFormat()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid Brin number."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,83 @@
# bsn.py - functions for handling BSNs
#
# Copyright (C) 2010, 2011, 2012, 2013 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
"""BSN (Burgerservicenummer, Dutch national identification number).
The BSN is a number with up to 9 digits (the leading 0's are commonly left
out) which is used as the Dutch national identification number.
>>> validate('1112.22.333')
'111222333'
>>> validate('1112.52.333')
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> validate('1112223334')
Traceback (most recent call last):
...
InvalidLength: ...
>>> format('111222333')
'1112.22.333'
"""
from stdnum.exceptions import *
from stdnum.util import clean
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' -.').strip()
# pad with leading zeroes
return (9 - len(number)) * '0' + number
def checksum(number):
"""Calculate the checksum over the number. A valid number should have
a check digit of 0."""
return (sum((9 - i) * int(n) for i, n in enumerate(number[:-1])) -
int(number[-1])) % 11
def validate(number):
"""Checks to see if the number provided is a valid BSN. This checks
the length and whether the check digit is correct."""
number = compact(number)
if not number.isdigit() or int(number) <= 0:
raise InvalidFormat()
if len(number) != 9:
raise InvalidLength()
if checksum(number) != 0:
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid BSN. This checks
the length and whether the check digit is correct."""
try:
return bool(validate(number))
except ValidationError:
return False
def format(number):
"""Reformat the passed number to the standard format."""
number = compact(number)
return number[:4] + '.' + number[4:6] + '.' + number[6:]

View file

@ -0,0 +1,70 @@
# btw.py - functions for handling Dutch VAT numbers
#
# Copyright (C) 2012, 2013 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
"""BTW-nummer (Omzetbelastingnummer, the Dutch VAT number).
The BTW-nummer is the Dutch number for VAT. It consists of a RSIN or BSN
followed by a B and two digits that identify the unit within the
organisation (usually 01).
>>> validate('004495445B01')
'004495445B01'
>>> validate('NL4495445B01')
'004495445B01'
>>> validate('123456789B90')
Traceback (most recent call last):
...
InvalidChecksum: ...
"""
from stdnum.exceptions import *
from stdnum.nl import bsn
from stdnum.util import clean
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' -.').upper().strip()
if number.startswith('NL'):
number = number[2:]
return bsn.compact(number[:-3]) + number[-3:]
def validate(number):
"""Checks to see if the number provided is a valid BTW number. This checks
the length, formatting and check digit."""
number = compact(number)
if not number[10:].isdigit() or int(number[10:]) <= 0:
raise InvalidFormat()
if len(number) != 12:
raise InvalidLength()
if number[9] != 'B':
raise InvalidFormat()
bsn.validate(number[:9])
return number
def is_valid(number):
"""Checks to see if the number provided is a valid BTW number. This checks
the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,72 @@
# onderwijsnummer.py - functions for handling onderwijsnummers
#
# Copyright (C) 2012, 2013 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
"""Onderwijsnummer (Dutch student school number).
The onderwijsnummers (education number) is very similar to the BSN (Dutch
national identification number) for students without a BSN. It uses a
checksum mechanism similar to the BSN.
>>> validate('1012.22.331')
'101222331'
>>> validate('100252333')
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> validate('1012.22.3333')
Traceback (most recent call last):
...
InvalidLength: ...
>>> validate('2112.22.337') # number must start with 10
Traceback (most recent call last):
...
InvalidFormat: ...
"""
from stdnum.exceptions import *
from stdnum.nl.bsn import compact, checksum
__all__ = ['compact', 'validate', 'is_valid']
def validate(number):
"""Checks to see if the number provided is a valid onderwijsnummer.
This checks the length and whether the check digit is correct and
whether it starts with the right sequence."""
number = compact(number)
if not number.isdigit() or int(number) <= 0:
raise InvalidFormat()
if not number.startswith('10'):
raise InvalidFormat()
if len(number) != 9:
raise InvalidLength()
if checksum(number) != 5:
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid onderwijsnummer.
This checks the length and whether the check digit is correct and
whether it starts with the right sequence."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,77 @@
# postcode.py - functions for handling Dutch postal codes
#
# Copyright (C) 2013 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
"""Postcode (Dutch postal code).
The Dutch postal code consists of four numbers followed by two letters.
>>> validate('2601 DC')
'2601 DC'
>>> validate('NL-2611ET')
'2611 ET'
>>> validate('26112 ET')
Traceback (most recent call last):
...
InvalidFormat: ...
>>> validate('2611 SS') # a few letter combinations are banned
Traceback (most recent call last):
...
InvalidComponent: ...
"""
import re
from stdnum.exceptions import *
from stdnum.util import clean
_postcode_re = re.compile(r'^(?P<pt1>[1-9][0-9]{3})(?P<pt2>[A-Z]{2})$')
_postcode_blacklist = ('SA', 'SD' ,'SS')
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' -').upper().strip()
if number.startswith('NL'):
number = number[2:]
return number
def validate(number):
"""Checks to see if the number provided is in the correct format.
This currently does not check whether the code corresponds to a real
address."""
number = compact(number)
match = _postcode_re.search(number)
if not match:
raise InvalidFormat()
if match.group('pt2') in _postcode_blacklist:
raise InvalidComponent()
return '%s %s' % (match.group('pt1'), match.group('pt2'))
def is_valid(number):
"""Checks to see if the number provided is a valid postal code."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,199 @@
# numdb.py - module for handling hierarchically organised numbers
#
# Copyright (C) 2010, 2011, 2012, 2013 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
"""Query structured number format files with number properties.
This module contains functions for reading and querying a database that
stores numbers that use a hierarchical format (e.g. ISBN, IBAN, phone
numbers, etc).
To read a database from a file:
>>> dbfile = read(open('numdb-test.dat', 'r'))
To split a number:
>>> dbfile.split('01006')
['0', '100', '6']
>>> dbfile.split('902006')
['90', '20', '06']
>>> dbfile.split('909856')
['90', '985', '6']
To split the number and get properties for each part:
>>> dbfile.info('01006') == [
... ('0', {'prop1': 'foo'}),
... ('100', {'prop2': 'bar'}),
... ('6', {}),
... ]
True
>>> dbfile.info('02006') == [
... ('0', {'prop1': 'foo'}),
... ('200', {'prop2': 'bar', 'prop3': 'baz'}),
... ('6', {}),
... ]
True
>>> dbfile.info('03456') == [
... ('0', {'prop1': 'foo'}),
... ('345', {'prop2': 'bar', 'prop3': 'baz'}),
... ('6', {}),
... ]
True
>>> dbfile.info('902006') == [
... ('90', {'prop1': 'booz'}),
... ('20', {'prop2': 'foo'}),
... ('06', {}),
... ]
True
>>> dbfile.info('909856') == [
... ('90', {'prop1': 'booz'}),
... ('985', {'prop2': 'fooz'}),
... ('6', {}),
... ]
True
>>> dbfile.info('9889') == [
... ('98', {'prop1': 'booz'}),
... ('89', {'prop2': 'foo'}),
... ]
True
>>> dbfile.info('633322') == [
... ('6', {'prop1': 'boo'}),
... ('333', {'prop2': 'bar', 'prop3': 'baz'}),
... ('22', {}),
... ]
True
"""
import re
from pkg_resources import resource_stream
_line_re = re.compile('^(?P<indent> *)(?P<ranges>([^-,\s]+(-[^-,\s]+)?)(,[^-,\s]+(-[^-,\s]+)?)*)\s*(?P<props>.*)$')
_prop_re = re.compile('(?P<prop>[0-9a-zA-Z-_]+)="(?P<value>[^"]*)"')
# this is a cache of open databases
_open_databases = {}
# the prefixes attribute of NumDB is structured as follows:
# prefixes = [
# [ length, low, high, props, children ]
# ...
# ]
# where children is a prefixes structure in it's own right
# (there is no expected ordering within the list)
class NumDB(object):
def __init__(self):
self.prefixes = []
@staticmethod
def _merge(results):
"""Merge the provided list of possible results into a single result
list (this is a generator)."""
# expand the results to all have the same length
ml = max(len(x) for x in results)
results = [x + (ml - len(x)) * [None]
for x in results]
# go over each part
for parts in zip(*results):
# regroup parts into parts list and properties list
partlist, proplist = list(zip(*(x for x in parts if x)))
part = min(partlist, key=len)
props = {}
for p in proplist:
props.update(p)
yield part, props
@staticmethod
def _find(number, prefixes):
"""Lookup the specified number in the list of prefixes, this will
return basically what info() should return but works recursively."""
if not number:
return []
results = []
if prefixes:
for length, low, high, props, children in prefixes:
if low <= number[:length] <= high and len(number) >= length:
results.append([(number[:length], props)] +
NumDB._find(number[length:], children))
# not-found fallback
if not results:
return [(number, {})]
# merge the results into a single result
return list(NumDB._merge(results))
def info(self, number):
"""Split the provided number in components and associate properties
with each component. This returns a tuple of tuples. Each tuple
consists of a string (a part of the number) and a dict of properties.
"""
return NumDB._find(number, self.prefixes)
def split(self, number):
"""Split the provided number in components. This returns a tuple with
the number of components identified."""
return [part for part, props in self.info(number)]
def _parse(fp):
"""Read lines of text from the file pointer and generate indent, length,
low, high, properties tuples."""
for line in fp:
# ignore comments
if line[0] == '#' or line.strip() == '':
continue # pragma: no cover (optimisation takes it out)
# any other line should parse
match = _line_re.search(line)
indent = len(match.group('indent'))
ranges = match.group('ranges')
props = dict(_prop_re.findall(match.group('props')))
for rnge in ranges.split(','):
if '-' in rnge:
low, high = rnge.split('-')
else:
low, high = rnge, rnge
yield indent, len(low), low, high, props
def read(fp):
"""Return a new database with the data read from the specified file."""
last_indent = 0
db = NumDB()
stack = {0: db.prefixes}
for indent, length, low, high, props in _parse(fp):
if indent > last_indent:
# populate the children field of the last indent
if stack[last_indent][-1][4] is None:
stack[last_indent][-1][4] = []
stack[indent] = stack[last_indent][-1][4]
stack[indent].append([length, low, high, props, None])
last_indent = indent
return db
def get(name):
"""Opens a database with the specified name to perform queries on."""
if name not in _open_databases:
import codecs
reader = codecs.getreader('utf-8')
_open_databases[name] = read(reader(resource_stream(__name__, name + '.dat')))
return _open_databases[name]

View file

@ -0,0 +1,24 @@
# __init__.py - collection of Polish numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of Polish numbers."""
# provide vat as an alias
from stdnum.pl import nip as vat

View file

@ -0,0 +1,79 @@
# nip.py - functions for handling Polish VAT numbers
#
# Copyright (C) 2012, 2013 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
"""NIP (Numer Identyfikacji Podatkowej, Polish VAT number).
The NIP (Numer Identyfikacji Podatkowej) number consists of 10 digit with
a straightforward weighted checksum.
>>> validate('PL 8567346215')
'8567346215'
>>> validate('PL 8567346216') # invalid check digits
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> format('PL 8567346215')
'856-734-62-15'
"""
from stdnum.exceptions import *
from stdnum.util import clean
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' -').upper().strip()
if number.startswith('PL'):
number = number[2:]
return number
def checksum(number):
"""Calculate the checksum."""
weights = (6, 5, 7, 2, 3, 4, 5, 6, 7, -1)
return sum(weights[i] * int(n) for i, n in enumerate(number)) % 11
def validate(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
number = compact(number)
if not number.isdigit():
raise InvalidFormat()
if len(number) != 10:
raise InvalidLength()
if checksum(number) != 0:
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False
def format(number):
"""Reformat the passed number to the standard format."""
number = compact(number)
return '-'.join((number[0:3], number[3:6], number[6:8], number[8:]))

View file

@ -0,0 +1,24 @@
# __init__.py - collection of Portuguese numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of Portuguese numbers."""
# provide vat as an alias
from stdnum.pt import nif as vat

View file

@ -0,0 +1,73 @@
# nif.py - functions for handling Portuguese VAT numbers
# coding: utf-8
#
# Copyright (C) 2012, 2013 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
"""NIF (Número de identificação fiscal, Portuguese VAT number).
The NIF (Número de identificação fiscal, NIPC, Número de Identificação de
Pessoa Colectiva) is used for VAT purposes. It is a 9-digit number with a
simple checksum.
>>> validate('PT 501 964 843')
'501964843'
>>> validate('PT 501 964 842') # invalid check digits
Traceback (most recent call last):
...
InvalidChecksum: ...
"""
from stdnum.exceptions import *
from stdnum.util import clean
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' -.').upper().strip()
if number.startswith('PT'):
number = number[2:]
return number
def calc_check_digit(number):
"""Calculate the check digit. The number passed should not have the
check digit included."""
return str((11 - sum((9 - i) * int(n) for i, n in enumerate(number)) ) % 11 % 10)
def validate(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
number = compact(number)
if not number.isdigit() or number[0] == '0':
raise InvalidFormat()
if len(number) != 9:
raise InvalidLength()
if calc_check_digit(number[:-1]) != number[-1]:
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,24 @@
# __init__.py - collection of Romanian numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of Romanian numbers."""
# provide vat as an alias
from stdnum.ro import cf as vat

View file

@ -0,0 +1,81 @@
# cf.py - functions for handling Romanian CF (VAT) numbers
# coding: utf-8
#
# Copyright (C) 2012, 2013 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
"""CF (Cod de înregistrare în scopuri de TVA, Romanian VAT number).
The Romanian CF is used for VAT purposes and can be from 2 to 10 digits long.
>>> validate('RO 185 472 90')
'18547290'
>>> validate('185 472 91')
Traceback (most recent call last):
...
InvalidChecksum: ...
>>> validate('1630615123457') # personal code
'1630615123457'
"""
from stdnum.exceptions import *
from stdnum.ro import cnp
from stdnum.util import clean
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' -').upper().strip()
if number.startswith('RO'):
number = number[2:]
return number
def calc_check_digit(number):
"""Calculate the check digit for organisations. The number passed
should not have the check digit included."""
weights = (7, 5, 3, 2, 1, 7, 5, 3, 2)
number = (9 - len(number)) * '0' + number
check = 10 * sum(weights[i] * int(n) for i, n in enumerate(number))
return str(check % 11 % 10)
def validate(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
number = compact(number)
if not number.isdigit() or number[0] == '0':
raise InvalidFormat()
if len(number) == 13:
# apparently a CNP can also be used (however, not all sources agree)
cnp.validate(number)
elif 2 <= len(number) <= 10:
if calc_check_digit(number[:-1]) != number[-1]:
raise InvalidChecksum()
else:
raise InvalidLength()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,101 @@
# cnp.py - functions for handling Romanian CNP numbers
# coding: utf-8
#
# Copyright (C) 2012, 2013 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
"""CNP (Cod Numeric Personal, Romanian Numerical Personal Code).
The CNP is a 13 digit number that includes information on the person's
gender, birth date and country zone.
>>> validate('1630615123457')
'1630615123457'
>>> validate('8800101221144') # invalid first digit
Traceback (most recent call last):
...
InvalidFormat: ...
>>> validate('1632215123457') # invalid date
Traceback (most recent call last):
...
InvalidComponent: ...
>>> validate('1630615123458') # invalid check digit
Traceback (most recent call last):
...
InvalidChecksum: ...
"""
import datetime
from stdnum.exceptions import *
from stdnum.util import clean
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, ' -').upper().strip()
def calc_check_digit(number):
"""Calculate the check digit for personal codes. The number passed
should not have the check digit included."""
# note that this algorithm has not been confirmed by an independent source
weights = (2, 7, 9, 1, 4, 6, 3, 5, 8, 2, 7, 9)
check = sum(weights[i] * int(n) for i, n in enumerate(number)) % 11
return '1' if check == 10 else str(check)
def get_birth_date(number):
"""Split the date parts from the number and return the birth date."""
centuries = {
'1': 1900, '2': 1900, '3': 1800, '4': 1800, '5': 2000, '6': 2000,
} # we assume 1900 for the others in order to try to construct a date
year = int(number[1:3]) + centuries.get(number[0], 1900)
month = int(number[3:5])
day = int(number[5:7])
try:
return datetime.date(year, month, day)
except ValueError:
raise InvalidComponent()
def validate(number):
"""Checks to see if the number provided is a valid VAT number. This checks
the length, formatting and check digit."""
number = compact(number)
# first digit should be a known one (9=foreigner)
if not number.isdigit() or number[0] not in '1234569':
raise InvalidFormat()
if len(number) != 13:
raise InvalidLength()
# check if birth date is valid
birth_date = get_birth_date(number)
# TODO: check that the birth date is not in the future
# number[7:9] is the county, we ignore it for now, just check last digit
if calc_check_digit(number[:-1]) != number[-1]:
raise InvalidChecksum()
return number
def is_valid(number):
"""Checks to see if the number provided is a valid VAT number. This checks
the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,21 @@
# __init__.py - collection of Swedish numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of Swedish numbers."""

View file

@ -0,0 +1,67 @@
# vat.py - functions for handling Swedish VAT numbers
# coding: utf-8
#
# Copyright (C) 2012, 2013 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
"""VAT (Moms, Mervärdesskatt, Swedish VAT number).
The Momsregistreringsnummer is used for VAT (Moms, Mervärdesskatt)
purposes and consists of 12 digits of which the last two should be 01. The
first 10 digits should have a valid Luhn checksum.
>>> validate('SE 123456789701')
'123456789701'
>>> validate('123456789101') # invalid check digits
Traceback (most recent call last):
...
InvalidChecksum: ...
"""
from stdnum import luhn
from stdnum.exceptions import *
from stdnum.util import clean
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
number = clean(number, ' -.').upper().strip()
if number.startswith('SE'):
number = number[2:]
return number
def validate(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
number = compact(number)
if not number.isdigit() or number[-2:] != '01':
raise InvalidFormat()
if len(number) != 12:
raise InvalidLength()
luhn.validate(number[:-2])
return number
def is_valid(number):
"""Checks to see if the number provided is a valid VAT number. This
checks the length, formatting and check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

View file

@ -0,0 +1,24 @@
# __init__.py - collection of Slovenian numbers
# coding: utf-8
#
# Copyright (C) 2012 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
"""Collection of Slovenian numbers."""
# provide vat as an alias
from stdnum.si import ddv as vat

Some files were not shown because too many files have changed in this diff Show more