web/lib/django/contrib/localflavor/cz/forms.py
changeset 38 77b6da96e6f1
equal deleted inserted replaced
37:8d941af65caf 38:77b6da96e6f1
       
     1 """
       
     2 Czech-specific form helpers
       
     3 """
       
     4 
       
     5 from django.core.validators import EMPTY_VALUES
       
     6 from django.forms import ValidationError
       
     7 from django.forms.fields import Select, RegexField, Field
       
     8 from django.utils.translation import ugettext_lazy as _
       
     9 import re
       
    10 
       
    11 birth_number = re.compile(r'^(?P<birth>\d{6})/?(?P<id>\d{3,4})$')
       
    12 ic_number = re.compile(r'^(?P<number>\d{7})(?P<check>\d)$')
       
    13 
       
    14 class CZRegionSelect(Select):
       
    15     """
       
    16     A select widget widget with list of Czech regions as choices.
       
    17     """
       
    18     def __init__(self, attrs=None):
       
    19         from cz_regions import REGION_CHOICES
       
    20         super(CZRegionSelect, self).__init__(attrs, choices=REGION_CHOICES)
       
    21 
       
    22 class CZPostalCodeField(RegexField):
       
    23     """
       
    24     A form field that validates its input as Czech postal code.
       
    25     Valid form is XXXXX or XXX XX, where X represents integer.
       
    26     """
       
    27     default_error_messages = {
       
    28         'invalid': _(u'Enter a postal code in the format XXXXX or XXX XX.'),
       
    29     }
       
    30 
       
    31     def __init__(self, *args, **kwargs):
       
    32         super(CZPostalCodeField, self).__init__(r'^\d{5}$|^\d{3} \d{2}$',
       
    33             max_length=None, min_length=None, *args, **kwargs)
       
    34 
       
    35     def clean(self, value):
       
    36         """
       
    37         Validates the input and returns a string that contains only numbers.
       
    38         Returns an empty string for empty values.
       
    39         """
       
    40         v = super(CZPostalCodeField, self).clean(value)
       
    41         return v.replace(' ', '')
       
    42 
       
    43 class CZBirthNumberField(Field):
       
    44     """
       
    45     Czech birth number field.
       
    46     """
       
    47     default_error_messages = {
       
    48         'invalid_format': _(u'Enter a birth number in the format XXXXXX/XXXX or XXXXXXXXXX.'),
       
    49         'invalid_gender': _(u'Invalid optional parameter Gender, valid values are \'f\' and \'m\''),
       
    50         'invalid': _(u'Enter a valid birth number.'),
       
    51     }
       
    52 
       
    53     def clean(self, value, gender=None):
       
    54         super(CZBirthNumberField, self).__init__(value)
       
    55 
       
    56         if value in EMPTY_VALUES:
       
    57             return u''
       
    58 
       
    59         match = re.match(birth_number, value)
       
    60         if not match:
       
    61             raise ValidationError(self.error_messages['invalid_format'])
       
    62 
       
    63         birth, id = match.groupdict()['birth'], match.groupdict()['id']
       
    64 
       
    65         # Three digits for verificatin number were used until 1. january 1954
       
    66         if len(id) == 3:
       
    67             return u'%s' % value
       
    68 
       
    69         # Birth number is in format YYMMDD. Females have month value raised by 50.
       
    70         # In case that all possible number are already used (for given date),
       
    71         #  the month field is raised by 20.
       
    72         if gender is not None:
       
    73             if gender == 'f':
       
    74                 female_const = 50
       
    75             elif gender == 'm':
       
    76                 female_const = 0
       
    77             else:
       
    78                 raise ValidationError(self.error_messages['invalid_gender'])
       
    79 
       
    80             month = int(birth[2:4]) - female_const
       
    81             if (not 1 <= month <= 12):
       
    82                 if (not 1 <= (month - 20) <= 12):
       
    83                     raise ValidationError(self.error_messages['invalid'])
       
    84 
       
    85         day = int(birth[4:6])
       
    86         if not (1 <= day <= 31):
       
    87             raise ValidationError(self.error_messages['invalid'])
       
    88 
       
    89         # Fourth digit has been added since 1. January 1954.
       
    90         # It is modulo of dividing birth number and verification number by 11.
       
    91         # If the modulo were 10, the last number was 0 (and therefore, the whole
       
    92         # birth number wasn't divisable by 11. These number are no longer used (since 1985)
       
    93         # and the condition 'modulo == 10' can be removed in 2085.
       
    94 
       
    95         modulo = int(birth + id[:3]) % 11
       
    96 
       
    97         if (modulo == int(id[-1])) or (modulo == 10 and id[-1] == '0'):
       
    98             return u'%s' % value
       
    99         else:
       
   100             raise ValidationError(self.error_messages['invalid'])
       
   101 
       
   102 class CZICNumberField(Field):
       
   103     """
       
   104     Czech IC number field.
       
   105     """
       
   106     default_error_messages = {
       
   107         'invalid': _(u'Enter a valid IC number.'),
       
   108     }
       
   109 
       
   110     def clean(self, value):
       
   111         super(CZICNumberField, self).__init__(value)
       
   112 
       
   113         if value in EMPTY_VALUES:
       
   114             return u''
       
   115 
       
   116         match = re.match(ic_number, value)
       
   117         if not match:
       
   118             raise ValidationError(self.error_messages['invalid'])
       
   119 
       
   120         number, check = match.groupdict()['number'], int(match.groupdict()['check'])
       
   121 
       
   122         sum = 0
       
   123         weight = 8
       
   124         for digit in number:
       
   125             sum += int(digit)*weight
       
   126             weight -= 1
       
   127 
       
   128         remainder = sum % 11
       
   129 
       
   130         # remainder is equal:
       
   131         #  0 or 10: last digit is 1
       
   132         #  1: last digit is 0
       
   133         # in other case, last digin is 11 - remainder
       
   134 
       
   135         if (not remainder % 10 and check == 1) or \
       
   136         (remainder == 1 and check == 0) or \
       
   137         (check == (11 - remainder)):
       
   138             return u'%s' % value
       
   139 
       
   140         raise ValidationError(self.error_messages['invalid'])
       
   141