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