| 1 |
""" |
|---|
| 2 |
Norwegian-specific Form helpers |
|---|
| 3 |
""" |
|---|
| 4 |
|
|---|
| 5 |
import re, datetime |
|---|
| 6 |
from django.forms import ValidationError |
|---|
| 7 |
from django.forms.fields import Field, RegexField, Select, EMPTY_VALUES |
|---|
| 8 |
from django.utils.translation import ugettext_lazy as _ |
|---|
| 9 |
|
|---|
| 10 |
class NOZipCodeField(RegexField): |
|---|
| 11 |
default_error_messages = { |
|---|
| 12 |
'invalid': _('Enter a zip code in the format XXXX.'), |
|---|
| 13 |
} |
|---|
| 14 |
|
|---|
| 15 |
def __init__(self, *args, **kwargs): |
|---|
| 16 |
super(NOZipCodeField, self).__init__(r'^\d{4}$', |
|---|
| 17 |
max_length=None, min_length=None, *args, **kwargs) |
|---|
| 18 |
|
|---|
| 19 |
class NOMunicipalitySelect(Select): |
|---|
| 20 |
""" |
|---|
| 21 |
A Select widget that uses a list of Norwegian municipalities (fylker) |
|---|
| 22 |
as its choices. |
|---|
| 23 |
""" |
|---|
| 24 |
def __init__(self, attrs=None): |
|---|
| 25 |
from no_municipalities import MUNICIPALITY_CHOICES |
|---|
| 26 |
super(NOMunicipalitySelect, self).__init__(attrs, choices=MUNICIPALITY_CHOICES) |
|---|
| 27 |
|
|---|
| 28 |
class NOSocialSecurityNumber(Field): |
|---|
| 29 |
""" |
|---|
| 30 |
Algorithm is documented at http://no.wikipedia.org/wiki/Personnummer |
|---|
| 31 |
""" |
|---|
| 32 |
default_error_messages = { |
|---|
| 33 |
'invalid': _(u'Enter a valid Norwegian social security number.'), |
|---|
| 34 |
} |
|---|
| 35 |
|
|---|
| 36 |
def clean(self, value): |
|---|
| 37 |
super(NOSocialSecurityNumber, self).clean(value) |
|---|
| 38 |
if value in EMPTY_VALUES: |
|---|
| 39 |
return u'' |
|---|
| 40 |
|
|---|
| 41 |
if not re.match(r'^\d{11}$', value): |
|---|
| 42 |
raise ValidationError(self.error_messages['invalid']) |
|---|
| 43 |
|
|---|
| 44 |
day = int(value[:2]) |
|---|
| 45 |
month = int(value[2:4]) |
|---|
| 46 |
year2 = int(value[4:6]) |
|---|
| 47 |
|
|---|
| 48 |
inum = int(value[6:9]) |
|---|
| 49 |
self.birthday = None |
|---|
| 50 |
try: |
|---|
| 51 |
if 000 <= inum < 500: |
|---|
| 52 |
self.birthday = datetime.date(1900+year2, month, day) |
|---|
| 53 |
if 500 <= inum < 750 and year2 > 54: |
|---|
| 54 |
self.birthday = datetime.date(1800+year2, month, day) |
|---|
| 55 |
if 500 <= inum < 1000 and year2 < 40: |
|---|
| 56 |
self.birthday = datetime.date(2000+year2, month, day) |
|---|
| 57 |
if 900 <= inum < 1000 and year2 > 39: |
|---|
| 58 |
self.birthday = datetime.date(1900+year2, month, day) |
|---|
| 59 |
except ValueError: |
|---|
| 60 |
raise ValidationError(self.error_messages['invalid']) |
|---|
| 61 |
|
|---|
| 62 |
sexnum = int(value[8]) |
|---|
| 63 |
if sexnum % 2 == 0: |
|---|
| 64 |
self.gender = 'F' |
|---|
| 65 |
else: |
|---|
| 66 |
self.gender = 'M' |
|---|
| 67 |
|
|---|
| 68 |
digits = map(int, list(value)) |
|---|
| 69 |
weight_1 = [3, 7, 6, 1, 8, 9, 4, 5, 2, 1, 0] |
|---|
| 70 |
weight_2 = [5, 4, 3, 2, 7, 6, 5, 4, 3, 2, 1] |
|---|
| 71 |
|
|---|
| 72 |
def multiply_reduce(aval, bval): |
|---|
| 73 |
return sum([(a * b) for (a, b) in zip(aval, bval)]) |
|---|
| 74 |
|
|---|
| 75 |
if multiply_reduce(digits, weight_1) % 11 != 0: |
|---|
| 76 |
raise ValidationError(self.error_messages['invalid']) |
|---|
| 77 |
if multiply_reduce(digits, weight_2) % 11 != 0: |
|---|
| 78 |
raise ValidationError(self.error_messages['invalid']) |
|---|
| 79 |
|
|---|
| 80 |
return value |
|---|