2
Swiss-specific Form helpers
5
from django.forms import ValidationError
6
from django.forms.fields import Field, RegexField, Select, EMPTY_VALUES
7
from django.utils.encoding import smart_unicode
8
from django.utils.translation import ugettext_lazy as _
11
id_re = re.compile(r"^(?P<idnumber>\w{8})(?P<pos9>(\d{1}|<))(?P<checksum>\d{1})$")
12
phone_digits_re = re.compile(r'^0([1-9]{1})\d{8}$')
14
class CHZipCodeField(RegexField):
15
default_error_messages = {
16
'invalid': _('Enter a zip code in the format XXXX.'),
19
def __init__(self, *args, **kwargs):
20
super(CHZipCodeField, self).__init__(r'^\d{4}$',
21
max_length=None, min_length=None, *args, **kwargs)
23
class CHPhoneNumberField(Field):
25
Validate local Swiss phone number (not international ones)
26
The correct format is '0XX XXX XX XX'.
27
'0XX.XXX.XX.XX' and '0XXXXXXXXX' validate but are corrected to
30
default_error_messages = {
31
'invalid': 'Phone numbers must be in 0XX XXX XX XX format.',
34
def clean(self, value):
35
super(CHPhoneNumberField, self).clean(value)
36
if value in EMPTY_VALUES:
38
value = re.sub('(\.|\s|/|-)', '', smart_unicode(value))
39
m = phone_digits_re.search(value)
41
return u'%s %s %s %s' % (value[0:3], value[3:6], value[6:8], value[8:10])
42
raise ValidationError(self.error_messages['invalid'])
44
class CHStateSelect(Select):
46
A Select widget that uses a list of CH states as its choices.
48
def __init__(self, attrs=None):
49
from ch_states import STATE_CHOICES # relative import
50
super(CHStateSelect, self).__init__(attrs, choices=STATE_CHOICES)
52
class CHIdentityCardNumberField(Field):
54
A Swiss identity card number.
56
Checks the following rules to determine whether the number is valid:
58
* Conforms to the X1234567<0 or 1234567890 format.
59
* Included checksums match calculated checksums
61
Algorithm is documented at http://adi.kousz.ch/artikel/IDCHE.htm
63
default_error_messages = {
64
'invalid': _('Enter a valid Swiss identity or passport card number in X1234567<0 or 1234567890 format.'),
67
def has_valid_checksum(self, number):
68
given_number, given_checksum = number[:-1], number[-1]
69
new_number = given_number
70
calculated_checksum = 0
74
first = str(number[:1])
76
num = ord(first.upper()) - 65
77
if num < 0 or num > 8:
79
new_number = str(num) + new_number[1:]
80
new_number = new_number[:8] + '0'
82
if not new_number.isdigit():
85
for i in range(len(new_number)):
86
fragment = int(new_number[i])*parameter
87
calculated_checksum += fragment
96
return str(calculated_checksum)[-1] == given_checksum
98
def clean(self, value):
99
super(CHIdentityCardNumberField, self).clean(value)
100
if value in EMPTY_VALUES:
103
match = re.match(id_re, value)
105
raise ValidationError(self.error_messages['invalid'])
107
idnumber, pos9, checksum = match.groupdict()['idnumber'], match.groupdict()['pos9'], match.groupdict()['checksum']
109
if idnumber == '00000000' or \
110
idnumber == 'A0000000':
111
raise ValidationError(self.error_messages['invalid'])
113
all_digits = "%s%s%s" % (idnumber, pos9, checksum)
114
if not self.has_valid_checksum(all_digits):
115
raise ValidationError(self.error_messages['invalid'])
117
return u'%s%s%s' % (idnumber, pos9, checksum)