web/lib/django/contrib/localflavor/cz/forms.py
changeset 0 0d40e90630ef
child 29 cc9b7e14412b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/lib/django/contrib/localflavor/cz/forms.py	Wed Jan 20 00:34:04 2010 +0100
@@ -0,0 +1,140 @@
+"""
+Czech-specific form helpers
+"""
+
+from django.forms import ValidationError
+from django.forms.fields import Select, RegexField, Field, EMPTY_VALUES
+from django.utils.translation import ugettext_lazy as _
+import re
+
+birth_number = re.compile(r'^(?P<birth>\d{6})/?(?P<id>\d{3,4})$')
+ic_number = re.compile(r'^(?P<number>\d{7})(?P<check>\d)$')
+
+class CZRegionSelect(Select):
+    """
+    A select widget widget with list of Czech regions as choices.
+    """
+    def __init__(self, attrs=None):
+        from cz_regions import REGION_CHOICES
+        super(CZRegionSelect, self).__init__(attrs, choices=REGION_CHOICES)
+
+class CZPostalCodeField(RegexField):
+    """
+    A form field that validates its input as Czech postal code.
+    Valid form is XXXXX or XXX XX, where X represents integer.
+    """
+    default_error_messages = {
+        'invalid': _(u'Enter a postal code in the format XXXXX or XXX XX.'),
+    }
+
+    def __init__(self, *args, **kwargs):
+        super(CZPostalCodeField, self).__init__(r'^\d{5}$|^\d{3} \d{2}$',
+            max_length=None, min_length=None, *args, **kwargs)
+
+    def clean(self, value):
+        """
+        Validates the input and returns a string that contains only numbers.
+        Returns an empty string for empty values.
+        """
+        v = super(CZPostalCodeField, self).clean(value)
+        return v.replace(' ', '')
+
+class CZBirthNumberField(Field):
+    """
+    Czech birth number field.
+    """
+    default_error_messages = {
+        'invalid_format': _(u'Enter a birth number in the format XXXXXX/XXXX or XXXXXXXXXX.'),
+        'invalid_gender': _(u'Invalid optional parameter Gender, valid values are \'f\' and \'m\''),
+        'invalid': _(u'Enter a valid birth number.'),
+    }
+
+    def clean(self, value, gender=None):
+        super(CZBirthNumberField, self).__init__(value)
+
+        if value in EMPTY_VALUES:
+            return u''
+
+        match = re.match(birth_number, value)
+        if not match:
+            raise ValidationError(self.error_messages['invalid_format'])
+
+        birth, id = match.groupdict()['birth'], match.groupdict()['id']
+
+        # Three digits for verificatin number were used until 1. january 1954
+        if len(id) == 3:
+            return u'%s' % value
+
+        # Birth number is in format YYMMDD. Females have month value raised by 50.
+        # In case that all possible number are already used (for given date),
+        #  the month field is raised by 20.
+        if gender is not None:
+            if gender == 'f':
+                female_const = 50
+            elif gender == 'm':
+                female_const = 0
+            else:
+                raise ValidationError(self.error_messages['invalid_gender'])
+
+            month = int(birth[2:4]) - female_const
+            if (not 1 <= month <= 12):
+                if (not 1 <= (month - 20) <= 12):
+                    raise ValidationError(self.error_messages['invalid'])
+
+        day = int(birth[4:6])
+        if not (1 <= day <= 31):
+            raise ValidationError(self.error_messages['invalid'])
+
+        # Fourth digit has been added since 1. January 1954.
+        # It is modulo of dividing birth number and verification number by 11.
+        # If the modulo were 10, the last number was 0 (and therefore, the whole
+        # birth number wasn't divisable by 11. These number are no longer used (since 1985)
+        # and the condition 'modulo == 10' can be removed in 2085.
+
+        modulo = int(birth + id[:3]) % 11
+
+        if (modulo == int(id[-1])) or (modulo == 10 and id[-1] == '0'):
+            return u'%s' % value
+        else:
+            raise ValidationError(self.error_messages['invalid'])
+
+class CZICNumberField(Field):
+    """
+    Czech IC number field.
+    """
+    default_error_messages = {
+        'invalid': _(u'Enter a valid IC number.'),
+    }
+
+    def clean(self, value):
+        super(CZICNumberField, self).__init__(value)
+
+        if value in EMPTY_VALUES:
+            return u''
+
+        match = re.match(ic_number, value)
+        if not match:
+            raise ValidationError(self.error_messages['invalid'])
+
+        number, check = match.groupdict()['number'], int(match.groupdict()['check'])
+
+        sum = 0
+        weight = 8
+        for digit in number:
+            sum += int(digit)*weight
+            weight -= 1
+
+        remainder = sum % 11
+
+        # remainder is equal:
+        #  0 or 10: last digit is 1
+        #  1: last digit is 0
+        # in other case, last digin is 11 - remainder
+
+        if (not remainder % 10 and check == 1) or \
+        (remainder == 1 and check == 0) or \
+        (check == (11 - remainder)):
+            return u'%s' % value
+
+        raise ValidationError(self.error_messages['invalid'])
+