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