Ticket #9066: cz_localflavour.patch

File cz_localflavour.patch, 12.8 KB (added by Tomáš Ehrlich, 12 years ago)

Bugs in CZBirthNumberField corrected and appropriate tests provided

  • django/contrib/localflavor/cz/cz_regions.py

     
     1"""
     2Czech regions, translations get from http://www.crwflags.com/fotw/Flags/cz-re.html
     3"""
     4
     5from django.utils.translation import ugettext_lazy as _
     6
     7REGION_CHOICES = (
     8    ('PR', _('Prague')),
     9    ('CE', _('Cenral Bohemian Region')),
     10    ('SO', _('South Bohemian Region')),
     11    ('PI', _('Pilsen Region')),
     12    ('CA', _('Carlsbad Region')),
     13    ('US', _('Usti Region')),
     14    ('LB', _('Liberec Region')),
     15    ('HK', _('Hradec Region')),
     16    ('PA', _('Pardubice Region')),
     17    ('VY', _('Vysocina Region')),
     18    ('SM', _('South Moravian Region')),
     19    ('OL', _('Olomouc Region')),
     20    ('ZL', _('Zlin Region')),
     21    ('MS', _('Moravian-Silesian Region')),
     22)
  • django/contrib/localflavor/cz/forms.py

     
     1"""
     2Czech-specific form helpers
     3"""
     4
     5from django.forms import ValidationError
     6from django.forms.fields import Select, RegexField, Field, EMPTY_VALUES
     7from django.utils.translation import ugettext_lazy as _
     8import re
     9
     10birth_number = re.compile(r'^(?P<birth>\d{6})/?(?P<id>\d{3,4})$')
     11ic_number = re.compile(r'^(?P<number>\d{7})(?P<check>\d)$')
     12
     13class 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
     21class 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
     42class 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 parametr 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 weren't dividable by 11. These number are no longer used (since 1985)
     92        # and 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
     101class 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
  • tests/regressiontests/forms/localflavor/cz.py

     
     1# -*- coding: utf-8 -*-
     2# Tests for the contrib/localflavor/ CZ Form Fields
     3
     4tests = r"""
     5# CZPostalCodeField #########################################################
     6
     7>>> from django.contrib.localflavor.cz.forms import CZPostalCodeField
     8>>> f = CZPostalCodeField()
     9>>> f.clean('84545x')
     10Traceback (most recent call last):
     11...
     12ValidationError: [u'Enter a postal code in the format XXXXX or XXX XX.']
     13>>> f.clean('91909')
     14u'91909'
     15>>> f.clean('917 01')
     16u'91701'
     17>>> f.clean('12345')
     18u'12345'
     19>>> f.clean('123456')
     20Traceback (most recent call last):
     21...
     22ValidationError: [u'Enter a postal code in the format XXXXX or XXX XX.']
     23>>> f.clean('1234')
     24Traceback (most recent call last):
     25...
     26ValidationError: [u'Enter a postal code in the format XXXXX or XXX XX.']
     27>>> f.clean('123 4')
     28Traceback (most recent call last):
     29...
     30ValidationError: [u'Enter a postal code in the format XXXXX or XXX XX.']
     31
     32# CZRegionSelect ############################################################
     33
     34>>> from django.contrib.localflavor.cz.forms import CZRegionSelect
     35>>> w = CZRegionSelect()
     36>>> w.render('regions', 'TT')
     37u'<select name="regions">\n<option value="PR">Prague</option>\n<option value="CE">Cenral Bohemian Region</option>\n<option value="SO">South Bohemian Region</option>\n<option value="PI">Pilsen Region</option>\n<option value="CA">Carlsbad Region</option>\n<option value="US">Usti Region</option>\n<option value="LB">Liberec Region</option>\n<option value="HK">Hradec Region</option>\n<option value="PA">Pardubice Region</option>\n<option value="VY">Vysocina Region</option>\n<option value="SM">South Moravian Region</option>\n<option value="OL">Olomouc Region</option>\n<option value="ZL">Zlin Region</option>\n<option value="MS">Moravian-Silesian Region</option>\n</select>'
     38
     39# CZBirthNumberField ########################################################
     40
     41>>> from django.contrib.localflavor.cz.forms import CZBirthNumberField
     42>>> f = CZBirthNumberField()
     43>>> f.clean('880523/1237')
     44u'880523/1237'
     45>>> f.clean('8805231237')
     46u'8805231237'
     47>>> f.clean('880523/000')
     48u'880523/000'
     49>>> f.clean('880523000')
     50u'880523000'
     51>>> f.clean('882101/0011')
     52u'882101/0011'
     53>>> f.clean('880523/1237', 'm')
     54u'880523/1237'
     55>>> f.clean('885523/1231', 'f')
     56u'885523/1231'
     57>>> f.clean('123456/12')
     58Traceback (most recent call last):
     59...
     60ValidationError: [u'Enter a birth number in the format XXXXXX/XXXX or XXXXXXXXXX.']
     61>>> f.clean('123456/12345')
     62Traceback (most recent call last):
     63...
     64ValidationError: [u'Enter a birth number in the format XXXXXX/XXXX or XXXXXXXXXX.']
     65>>> f.clean('12345612')
     66Traceback (most recent call last):
     67...
     68ValidationError: [u'Enter a birth number in the format XXXXXX/XXXX or XXXXXXXXXX.']
     69>>> f.clean('12345612345')
     70Traceback (most recent call last):
     71...
     72ValidationError: [u'Enter a birth number in the format XXXXXX/XXXX or XXXXXXXXXX.']
     73>>> f.clean('881523/0000', 'm')
     74Traceback (most recent call last):
     75...
     76ValidationError: [u'Enter a valid birth number.']
     77>>> f.clean('885223/0000', 'm')
     78Traceback (most recent call last):
     79...
     80ValidationError: [u'Enter a valid birth number.']
     81>>> f.clean('881223/0000', 'f')
     82Traceback (most recent call last):
     83...
     84ValidationError: [u'Enter a valid birth number.']
     85>>> f.clean('886523/0000', 'f')
     86Traceback (most recent call last):
     87...
     88ValidationError: [u'Enter a valid birth number.']
     89>>> f.clean('880523/1239')
     90Traceback (most recent call last):
     91...
     92ValidationError: [u'Enter a valid birth number.']
     93>>> f.clean('8805231239')
     94Traceback (most recent call last):
     95...
     96ValidationError: [u'Enter a valid birth number.']
     97>>> f.clean('990101/0011')
     98Traceback (most recent call last):
     99...
     100ValidationError: [u'Enter a valid birth number.']
     101
     102# CZICNumberField ########################################################
     103
     104>>> from django.contrib.localflavor.cz.forms import CZICNumberField
     105>>> f = CZICNumberField()
     106>>> f.clean('12345679')
     107u'12345679'
     108>>> f.clean('12345601')
     109u'12345601'
     110>>> f.clean('12345661')
     111u'12345661'
     112>>> f.clean('12345610')
     113u'12345610'
     114>>> f.clean('1234567')
     115Traceback (most recent call last):
     116...
     117ValidationError: [u'Enter a valid IC number.']
     118>>> f.clean('12345660')
     119Traceback (most recent call last):
     120...
     121ValidationError: [u'Enter a valid IC number.']
     122>>> f.clean('12345600')
     123Traceback (most recent call last):
     124...
     125ValidationError: [u'Enter a valid IC number.']
     126"""
  • tests/regressiontests/forms/tests.py

     
    1010from localflavor.ca import tests as localflavor_ca_tests
    1111from localflavor.ch import tests as localflavor_ch_tests
    1212from localflavor.cl import tests as localflavor_cl_tests
     13from localflavor.cz import tests as localflavor_cz_tests
    1314from localflavor.de import tests as localflavor_de_tests
    1415from localflavor.es import tests as localflavor_es_tests
    1516from localflavor.fi import tests as localflavor_fi_tests
     
    4344    'localflavor_ca_tests': localflavor_ca_tests,
    4445    'localflavor_ch_tests': localflavor_ch_tests,
    4546    'localflavor_cl_tests': localflavor_cl_tests,
     47    'localflavor_cz_tests': localflavor_cz_tests,
    4648    'localflavor_de_tests': localflavor_de_tests,
    4749    'localflavor_es_tests': localflavor_es_tests,
    4850    'localflavor_fi_tests': localflavor_fi_tests,
  • docs/ref/contrib/localflavor.txt

     
    4444    * Brazil_
    4545    * Canada_
    4646    * Chile_
     47    * Czech_
    4748    * Finland_
    4849    * France_
    4950    * Germany_
     
    8384.. _Brazil: `Brazil (br)`_
    8485.. _Canada: `Canada (ca)`_
    8586.. _Chile: `Chile (cl)`_
     87.. _Czech: `Czech (cz)`_
    8688.. _Finland: `Finland (fi)`_
    8789.. _France: `France (fr)`_
    8890.. _Germany: `Germany (de)`_
     
    231233    A ``Select`` widget that uses a list of Chilean regions (Regiones) as its
    232234    choices.
    233235
     236Czech (``cz``)
     237==============
     238
     239.. class:: cz.forms.CZPostalCodeField
     240
     241    A form field that validates input as a Czech postal code. Valid formats
     242    are XXXXX or XXX XX, where X is a digit.
     243
     244.. class:: cz.forms.CZBirthNumberField
     245
     246    A form field that validates input as a Czech Birth Number.
     247    A valid number must be in format XXXXXX/XXXX (slash is optional).
     248
     249.. class:: cz.forms.CZICNumberField
     250
     251    A form field that validates input as a Czech IC number field.
     252
     253.. class:: cz.forms.CZRegionSelect
     254
     255    A ``Select`` widget that uses a list of Czech regions as its choices.
     256
    234257Finland (``fi``)
    235258================
    236259
Back to Top