Django

Code

root/django/trunk/django/contrib/localflavor/ca/forms.py

Revision 7971, 4.1 kB (checked in by jacob, 1 month ago)

Fixed #7741: django.newforms is now django.forms. This is obviously a backwards-incompatible change. There's a warning upon import of django.newforms itself, but deeper imports will raise errors.

  • Property svn:eol-style set to native
Line 
1 """
2 Canada-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.forms.util import smart_unicode
8 from django.utils.translation import ugettext_lazy as _
9 import re
10
11 phone_digits_re = re.compile(r'^(?:1-?)?(\d{3})[-\.]?(\d{3})[-\.]?(\d{4})$')
12 sin_re = re.compile(r"^(\d{3})-(\d{3})-(\d{3})$")
13
14 class CAPostalCodeField(RegexField):
15     """Canadian postal code field."""
16     default_error_messages = {
17         'invalid': _(u'Enter a postal code in the format XXX XXX.'),
18     }
19
20     def __init__(self, *args, **kwargs):
21         super(CAPostalCodeField, self).__init__(r'^[ABCEGHJKLMNPRSTVXYZ]\d[A-Z] \d[A-Z]\d$',
22             max_length=None, min_length=None, *args, **kwargs)
23
24 class CAPhoneNumberField(Field):
25     """Canadian phone number field."""
26     default_error_messages = {
27         'invalid': u'Phone numbers must be in XXX-XXX-XXXX format.',
28     }
29
30     def clean(self, value):
31         """Validate a phone number.
32         """
33         super(CAPhoneNumberField, self).clean(value)
34         if value in EMPTY_VALUES:
35             return u''
36         value = re.sub('(\(|\)|\s+)', '', smart_unicode(value))
37         m = phone_digits_re.search(value)
38         if m:
39             return u'%s-%s-%s' % (m.group(1), m.group(2), m.group(3))
40         raise ValidationError(self.error_messages['invalid'])
41
42 class CAProvinceField(Field):
43     """
44     A form field that validates its input is a Canadian province name or abbreviation.
45     It normalizes the input to the standard two-leter postal service
46     abbreviation for the given province.
47     """
48     default_error_messages = {
49         'invalid': u'Enter a Canadian province or territory.',
50     }
51
52     def clean(self, value):
53         from ca_provinces import PROVINCES_NORMALIZED
54         super(CAProvinceField, self).clean(value)
55         if value in EMPTY_VALUES:
56             return u''
57         try:
58             value = value.strip().lower()
59         except AttributeError:
60             pass
61         else:
62             try:
63                 return PROVINCES_NORMALIZED[value.strip().lower()].decode('ascii')
64             except KeyError:
65                 pass
66         raise ValidationError(self.error_messages['invalid'])
67
68 class CAProvinceSelect(Select):
69     """
70     A Select widget that uses a list of Canadian provinces and
71     territories as its choices.
72     """
73     def __init__(self, attrs=None):
74         from ca_provinces import PROVINCE_CHOICES # relative import
75         super(CAProvinceSelect, self).__init__(attrs, choices=PROVINCE_CHOICES)
76
77 class CASocialInsuranceNumberField(Field):
78     """
79     A Canadian Social Insurance Number (SIN).
80
81     Checks the following rules to determine whether the number is valid:
82
83         * Conforms to the XXX-XXX-XXX format.
84         * Passes the check digit process "Luhn Algorithm"
85              See: http://en.wikipedia.org/wiki/Social_Insurance_Number
86     """
87     default_error_messages = {
88         'invalid': _('Enter a valid Canadian Social Insurance number in XXX-XXX-XXX format.'),
89     }
90
91     def clean(self, value):
92         super(CASocialInsuranceNumberField, self).clean(value)
93         if value in EMPTY_VALUES:
94             return u''
95
96         match = re.match(sin_re, value)
97         if not match:
98             raise ValidationError(self.error_messages['invalid'])
99
100         number = u'%s-%s-%s' % (match.group(1), match.group(2), match.group(3))
101         check_number = u'%s%s%s' % (match.group(1), match.group(2), match.group(3))
102         if not self.luhn_checksum_is_valid(check_number):
103             raise ValidationError(self.error_messages['invalid'])
104         return number
105
106     def luhn_checksum_is_valid(self, number):
107         """
108         Checks to make sure that the SIN passes a luhn mod-10 checksum
109         See: http://en.wikipedia.org/wiki/Luhn_algorithm
110         """
111
112         sum = 0
113         num_digits = len(number)
114         oddeven = num_digits & 1
115
116         for count in range(0, num_digits):
117             digit = int(number[count])
118
119             if not (( count & 1 ) ^ oddeven ):
120                 digit = digit * 2
121             if digit > 9:
122                 digit = digit - 9
123
124             sum = sum + digit
125
126         return ( (sum % 10) == 0 )
Note: See TracBrowser for help on using the browser.