web/lib/django/contrib/localflavor/ch/forms.py
author ymh <ymh.work@gmail.com>
Wed, 02 Jun 2010 18:57:35 +0200
changeset 38 77b6da96e6f1
permissions -rw-r--r--
update django
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
38
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     1
"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     2
Swiss-specific Form helpers
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     3
"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     4
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     5
from django.core.validators import EMPTY_VALUES
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     6
from django.forms import ValidationError
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     7
from django.forms.fields import Field, RegexField, Select
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     8
from django.utils.encoding import smart_unicode
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     9
from django.utils.translation import ugettext_lazy as _
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    10
import re
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    11
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    12
id_re = re.compile(r"^(?P<idnumber>\w{8})(?P<pos9>(\d{1}|<))(?P<checksum>\d{1})$")
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    13
phone_digits_re = re.compile(r'^0([1-9]{1})\d{8}$')
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    14
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    15
class CHZipCodeField(RegexField):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    16
    default_error_messages = {
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    17
        'invalid': _('Enter a zip code in the format XXXX.'),
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    18
    }
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    19
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    20
    def __init__(self, *args, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    21
        super(CHZipCodeField, self).__init__(r'^\d{4}$',
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    22
        max_length=None, min_length=None, *args, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    23
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    24
class CHPhoneNumberField(Field):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    25
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    26
    Validate local Swiss phone number (not international ones)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    27
    The correct format is '0XX XXX XX XX'.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    28
    '0XX.XXX.XX.XX' and '0XXXXXXXXX' validate but are corrected to
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    29
    '0XX XXX XX XX'.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    30
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    31
    default_error_messages = {
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    32
        'invalid': 'Phone numbers must be in 0XX XXX XX XX format.',
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    33
    }
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    34
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    35
    def clean(self, value):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    36
        super(CHPhoneNumberField, self).clean(value)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    37
        if value in EMPTY_VALUES:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    38
            return u''
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    39
        value = re.sub('(\.|\s|/|-)', '', smart_unicode(value))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    40
        m = phone_digits_re.search(value)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    41
        if m:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    42
            return u'%s %s %s %s' % (value[0:3], value[3:6], value[6:8], value[8:10])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    43
        raise ValidationError(self.error_messages['invalid'])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    44
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    45
class CHStateSelect(Select):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    46
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    47
    A Select widget that uses a list of CH states as its choices.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    48
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    49
    def __init__(self, attrs=None):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    50
        from ch_states import STATE_CHOICES # relative import
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    51
        super(CHStateSelect, self).__init__(attrs, choices=STATE_CHOICES)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    52
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    53
class CHIdentityCardNumberField(Field):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    54
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    55
    A Swiss identity card number.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    56
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    57
    Checks the following rules to determine whether the number is valid:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    58
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    59
        * Conforms to the X1234567<0 or 1234567890 format.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    60
        * Included checksums match calculated checksums
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    61
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    62
    Algorithm is documented at http://adi.kousz.ch/artikel/IDCHE.htm
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    63
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    64
    default_error_messages = {
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    65
        'invalid': _('Enter a valid Swiss identity or passport card number in X1234567<0 or 1234567890 format.'),
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    66
    }
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    67
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    68
    def has_valid_checksum(self, number):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    69
        given_number, given_checksum = number[:-1], number[-1]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    70
        new_number = given_number
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    71
        calculated_checksum = 0
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    72
        fragment = ""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    73
        parameter = 7
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    74
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    75
        first = str(number[:1])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    76
        if first.isalpha():
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    77
            num = ord(first.upper()) - 65
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    78
            if num < 0 or num > 8:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    79
                return False
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    80
            new_number = str(num) + new_number[1:]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    81
            new_number = new_number[:8] + '0'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    82
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    83
        if not new_number.isdigit():
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    84
            return False
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    85
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    86
        for i in range(len(new_number)):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    87
          fragment = int(new_number[i])*parameter
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    88
          calculated_checksum += fragment
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    89
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    90
          if parameter == 1:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    91
            parameter = 7
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    92
          elif parameter == 3:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    93
            parameter = 1
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    94
          elif parameter ==7:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    95
            parameter = 3
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    96
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    97
        return str(calculated_checksum)[-1] == given_checksum
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    98
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    99
    def clean(self, value):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   100
        super(CHIdentityCardNumberField, self).clean(value)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   101
        if value in EMPTY_VALUES:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   102
            return u''
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   103
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   104
        match = re.match(id_re, value)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   105
        if not match:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   106
            raise ValidationError(self.error_messages['invalid'])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   107
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   108
        idnumber, pos9, checksum = match.groupdict()['idnumber'], match.groupdict()['pos9'], match.groupdict()['checksum']
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   109
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   110
        if idnumber == '00000000' or \
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   111
           idnumber == 'A0000000':
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   112
            raise ValidationError(self.error_messages['invalid'])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   113
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   114
        all_digits = "%s%s%s" % (idnumber, pos9, checksum)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   115
        if not self.has_valid_checksum(all_digits):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   116
            raise ValidationError(self.error_messages['invalid'])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   117
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   118
        return u'%s%s%s' % (idnumber, pos9, checksum)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   119