/django/contrib/localflavor/cz/forms.py
Python | 145 lines | 116 code | 11 blank | 18 comment | 8 complexity | 1aecdf1859fda0ca0a22f40787b0d352 MD5 | raw file
Possible License(s): BSD-3-Clause
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