PageRenderTime 48ms CodeModel.GetById 32ms app.highlight 13ms RepoModel.GetById 1ms app.codeStats 0ms

/django/contrib/localflavor/cz/forms.py

https://code.google.com/p/mango-py/
Python | 145 lines | 116 code | 11 blank | 18 comment | 8 complexity | 1aecdf1859fda0ca0a22f40787b0d352 MD5 | raw file
  1"""
  2Czech-specific form helpers
  3"""
  4
  5from django.core.validators import EMPTY_VALUES
  6from django.forms import ValidationError
  7from django.forms.fields import Select, RegexField, Field
  8from django.utils.translation import ugettext_lazy as _
  9import re
 10
 11birth_number = re.compile(r'^(?P<birth>\d{6})/?(?P<id>\d{3,4})$')
 12ic_number = re.compile(r'^(?P<number>\d{7})(?P<check>\d)$')
 13
 14class 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
 22class 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
 43class 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).clean(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 verification 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            import warnings
 74            warnings.warn(
 75                "Support for validating the gender of a CZ Birth number has been deprecated.",
 76                PendingDeprecationWarning)
 77            if gender == 'f':
 78                female_const = 50
 79            elif gender == 'm':
 80                female_const = 0
 81            else:
 82                raise ValidationError(self.error_messages['invalid_gender'])
 83
 84            month = int(birth[2:4]) - female_const
 85            if (not 1 <= month <= 12):
 86                if (not 1 <= (month - 20) <= 12):
 87                    raise ValidationError(self.error_messages['invalid'])
 88
 89        day = int(birth[4:6])
 90        if not (1 <= day <= 31):
 91            raise ValidationError(self.error_messages['invalid'])
 92
 93        # Fourth digit has been added since 1. January 1954.
 94        # It is modulo of dividing birth number and verification number by 11.
 95        # If the modulo were 10, the last number was 0 (and therefore, the whole
 96        # birth number wasn't divisable by 11. These number are no longer used (since 1985)
 97        # and the condition 'modulo == 10' can be removed in 2085.
 98
 99        modulo = int(birth + id[:3]) % 11
100
101        if (modulo == int(id[-1])) or (modulo == 10 and id[-1] == '0'):
102            return u'%s' % value
103        else:
104            raise ValidationError(self.error_messages['invalid'])
105
106class CZICNumberField(Field):
107    """
108    Czech IC number field.
109    """
110    default_error_messages = {
111        'invalid': _(u'Enter a valid IC number.'),
112    }
113
114    def clean(self, value):
115        super(CZICNumberField, self).clean(value)
116
117        if value in EMPTY_VALUES:
118            return u''
119
120        match = re.match(ic_number, value)
121        if not match:
122            raise ValidationError(self.error_messages['invalid'])
123
124        number, check = match.groupdict()['number'], int(match.groupdict()['check'])
125
126        sum = 0
127        weight = 8
128        for digit in number:
129            sum += int(digit)*weight
130            weight -= 1
131
132        remainder = sum % 11
133
134        # remainder is equal:
135        #  0 or 10: last digit is 1
136        #  1: last digit is 0
137        # in other case, last digin is 11 - remainder
138
139        if (not remainder % 10 and check == 1) or \
140        (remainder == 1 and check == 0) or \
141        (check == (11 - remainder)):
142            return u'%s' % value
143
144        raise ValidationError(self.error_messages['invalid'])
145