~ubuntu-branches/ubuntu/jaunty/python-django/jaunty

« back to all changes in this revision

Viewing changes to django/contrib/localflavor/ch/forms.py

  • Committer: Bazaar Package Importer
  • Author(s): Scott James Remnant, Eddy Mulyono
  • Date: 2008-09-16 12:18:47 UTC
  • mfrom: (1.1.5 upstream) (4.1.1 lenny)
  • Revision ID: james.westby@ubuntu.com-20080916121847-mg225rg5mnsdqzr0
Tags: 1.0-1ubuntu1
* Merge from Debian (LP: #264191), remaining changes:
  - Run test suite on build.

[Eddy Mulyono]
* Update patch to workaround network test case failures.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""
 
2
Swiss-specific Form helpers
 
3
"""
 
4
 
 
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 _
 
9
import re
 
10
 
 
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}$')
 
13
 
 
14
class CHZipCodeField(RegexField):
 
15
    default_error_messages = {
 
16
        'invalid': _('Enter a zip code in the format XXXX.'),
 
17
    }
 
18
 
 
19
    def __init__(self, *args, **kwargs):
 
20
        super(CHZipCodeField, self).__init__(r'^\d{4}$',
 
21
        max_length=None, min_length=None, *args, **kwargs)
 
22
 
 
23
class CHPhoneNumberField(Field):
 
24
    """
 
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
 
28
    '0XX XXX XX XX'.
 
29
    """
 
30
    default_error_messages = {
 
31
        'invalid': 'Phone numbers must be in 0XX XXX XX XX format.',
 
32
    }
 
33
 
 
34
    def clean(self, value):
 
35
        super(CHPhoneNumberField, self).clean(value)
 
36
        if value in EMPTY_VALUES:
 
37
            return u''
 
38
        value = re.sub('(\.|\s|/|-)', '', smart_unicode(value))
 
39
        m = phone_digits_re.search(value)
 
40
        if m:
 
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'])
 
43
 
 
44
class CHStateSelect(Select):
 
45
    """
 
46
    A Select widget that uses a list of CH states as its choices.
 
47
    """
 
48
    def __init__(self, attrs=None):
 
49
        from ch_states import STATE_CHOICES # relative import
 
50
        super(CHStateSelect, self).__init__(attrs, choices=STATE_CHOICES)
 
51
 
 
52
class CHIdentityCardNumberField(Field):
 
53
    """
 
54
    A Swiss identity card number.
 
55
 
 
56
    Checks the following rules to determine whether the number is valid:
 
57
 
 
58
        * Conforms to the X1234567<0 or 1234567890 format.
 
59
        * Included checksums match calculated checksums
 
60
 
 
61
    Algorithm is documented at http://adi.kousz.ch/artikel/IDCHE.htm
 
62
    """
 
63
    default_error_messages = {
 
64
        'invalid': _('Enter a valid Swiss identity or passport card number in X1234567<0 or 1234567890 format.'),
 
65
    }
 
66
 
 
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
 
71
        fragment = ""
 
72
        parameter = 7
 
73
 
 
74
        first = str(number[:1])
 
75
        if first.isalpha():
 
76
            num = ord(first.upper()) - 65
 
77
            if num < 0 or num > 8:
 
78
                return False
 
79
            new_number = str(num) + new_number[1:]
 
80
            new_number = new_number[:8] + '0'
 
81
 
 
82
        if not new_number.isdigit():
 
83
            return False
 
84
 
 
85
        for i in range(len(new_number)):
 
86
          fragment = int(new_number[i])*parameter
 
87
          calculated_checksum += fragment
 
88
 
 
89
          if parameter == 1:
 
90
            parameter = 7
 
91
          elif parameter == 3:
 
92
            parameter = 1
 
93
          elif parameter ==7:
 
94
            parameter = 3
 
95
 
 
96
        return str(calculated_checksum)[-1] == given_checksum
 
97
 
 
98
    def clean(self, value):
 
99
        super(CHIdentityCardNumberField, self).clean(value)
 
100
        if value in EMPTY_VALUES:
 
101
            return u''
 
102
 
 
103
        match = re.match(id_re, value)
 
104
        if not match:
 
105
            raise ValidationError(self.error_messages['invalid'])
 
106
 
 
107
        idnumber, pos9, checksum = match.groupdict()['idnumber'], match.groupdict()['pos9'], match.groupdict()['checksum']
 
108
 
 
109
        if idnumber == '00000000' or \
 
110
           idnumber == 'A0000000':
 
111
            raise ValidationError(self.error_messages['invalid'])
 
112
 
 
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'])
 
116
 
 
117
        return u'%s%s%s' % (idnumber, pos9, checksum)
 
118