web/lib/django/contrib/localflavor/de/forms.py
changeset 0 0d40e90630ef
child 29 cc9b7e14412b
equal deleted inserted replaced
-1:000000000000 0:0d40e90630ef
       
     1 """
       
     2 DE-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.translation import ugettext_lazy as _
       
     8 import re
       
     9 
       
    10 id_re = re.compile(r"^(?P<residence>\d{10})(?P<origin>\w{1,3})[-\ ]?(?P<birthday>\d{7})[-\ ]?(?P<validity>\d{7})[-\ ]?(?P<checksum>\d{1})$")
       
    11 
       
    12 class DEZipCodeField(RegexField):
       
    13     default_error_messages = {
       
    14         'invalid': _('Enter a zip code in the format XXXXX.'),
       
    15     }
       
    16     def __init__(self, *args, **kwargs):
       
    17         super(DEZipCodeField, self).__init__(r'^\d{5}$',
       
    18             max_length=None, min_length=None, *args, **kwargs)
       
    19 
       
    20 class DEStateSelect(Select):
       
    21     """
       
    22     A Select widget that uses a list of DE states as its choices.
       
    23     """
       
    24     def __init__(self, attrs=None):
       
    25         from de_states import STATE_CHOICES
       
    26         super(DEStateSelect, self).__init__(attrs, choices=STATE_CHOICES)
       
    27 
       
    28 class DEIdentityCardNumberField(Field):
       
    29     """
       
    30     A German identity card number.
       
    31 
       
    32     Checks the following rules to determine whether the number is valid:
       
    33 
       
    34         * Conforms to the XXXXXXXXXXX-XXXXXXX-XXXXXXX-X format.
       
    35         * No group consists entirely of zeroes.
       
    36         * Included checksums match calculated checksums
       
    37 
       
    38     Algorithm is documented at http://de.wikipedia.org/wiki/Personalausweis
       
    39     """
       
    40     default_error_messages = {
       
    41         'invalid': _('Enter a valid German identity card number in XXXXXXXXXXX-XXXXXXX-XXXXXXX-X format.'),
       
    42     }
       
    43 
       
    44     def has_valid_checksum(self, number):
       
    45         given_number, given_checksum = number[:-1], number[-1]
       
    46         calculated_checksum = 0
       
    47         fragment = ""
       
    48         parameter = 7
       
    49 
       
    50         for i in range(len(given_number)):
       
    51             fragment = str(int(given_number[i]) * parameter)
       
    52             if fragment.isalnum():
       
    53                 calculated_checksum += int(fragment[-1])
       
    54             if parameter == 1:
       
    55                 parameter = 7
       
    56             elif parameter == 3:
       
    57                 parameter = 1
       
    58             elif parameter ==7:
       
    59                 parameter = 3
       
    60 
       
    61         return str(calculated_checksum)[-1] == given_checksum
       
    62 
       
    63     def clean(self, value):
       
    64         super(DEIdentityCardNumberField, self).clean(value)
       
    65         if value in EMPTY_VALUES:
       
    66             return u''
       
    67         match = re.match(id_re, value)
       
    68         if not match:
       
    69             raise ValidationError(self.error_messages['invalid'])
       
    70 
       
    71         gd = match.groupdict()
       
    72         residence, origin = gd['residence'], gd['origin']
       
    73         birthday, validity, checksum = gd['birthday'], gd['validity'], gd['checksum']
       
    74 
       
    75         if residence == '0000000000' or birthday == '0000000' or validity == '0000000':
       
    76             raise ValidationError(self.error_messages['invalid'])
       
    77 
       
    78         all_digits = u"%s%s%s%s" % (residence, birthday, validity, checksum)
       
    79         if not self.has_valid_checksum(residence) or not self.has_valid_checksum(birthday) or \
       
    80             not self.has_valid_checksum(validity) or not self.has_valid_checksum(all_digits):
       
    81                 raise ValidationError(self.error_messages['invalid'])
       
    82 
       
    83         return u'%s%s-%s-%s-%s' % (residence, origin, birthday, validity, checksum)