2 Czech-specific form helpers
5 from django
.core
.validators
import EMPTY_VALUES
6 from django
.forms
import ValidationError
7 from django
.forms
.fields
import Select
, RegexField
, Field
8 from django
.utils
.translation
import ugettext_lazy
as _
11 birth_number
= re
.compile(r
'^(?P<birth>\d{6})/?(?P<id>\d{3,4})$')
12 ic_number
= re
.compile(r
'^(?P<number>\d{7})(?P<check>\d)$')
14 class CZRegionSelect(Select
):
16 A select widget widget with list of Czech regions as choices.
18 def __init__(self
, attrs
=None):
19 from cz_regions
import REGION_CHOICES
20 super(CZRegionSelect
, self
).__init
__(attrs
, choices
=REGION_CHOICES
)
22 class CZPostalCodeField(RegexField
):
24 A form field that validates its input as Czech postal code.
25 Valid form is XXXXX or XXX XX, where X represents integer.
27 default_error_messages
= {
28 'invalid': _(u
'Enter a postal code in the format XXXXX or XXX XX.'),
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
)
35 def clean(self
, value
):
37 Validates the input and returns a string that contains only numbers.
38 Returns an empty string for empty values.
40 v
= super(CZPostalCodeField
, self
).clean(value
)
41 return v
.replace(' ', '')
43 class CZBirthNumberField(Field
):
45 Czech birth number field.
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.'),
53 def clean(self
, value
, gender
=None):
54 super(CZBirthNumberField
, self
).__init
__(value
)
56 if value
in EMPTY_VALUES
:
59 match
= re
.match(birth_number
, value
)
61 raise ValidationError(self
.error_messages
['invalid_format'])
63 birth
, id = match
.groupdict()['birth'], match
.groupdict()['id']
65 # Three digits for verificatin number were used until 1. january 1954
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:
78 raise ValidationError(self
.error_messages
['invalid_gender'])
80 month
= int(birth
[2:4]) - female_const
81 if (not 1 <= month
<= 12):
82 if (not 1 <= (month
- 20) <= 12):
83 raise ValidationError(self
.error_messages
['invalid'])
86 if not (1 <= day
<= 31):
87 raise ValidationError(self
.error_messages
['invalid'])
89 # Fourth digit has been added since 1. January 1954.
90 # It is modulo of dividing birth number and verification number by 11.
91 # If the modulo were 10, the last number was 0 (and therefore, the whole
92 # birth number wasn't divisable by 11. These number are no longer used (since 1985)
93 # and the condition 'modulo == 10' can be removed in 2085.
95 modulo
= int(birth
+ id[:3]) % 11
97 if (modulo
== int(id[-1])) or (modulo
== 10 and id[-1] == '0'):
100 raise ValidationError(self
.error_messages
['invalid'])
102 class CZICNumberField(Field
):
104 Czech IC number field.
106 default_error_messages
= {
107 'invalid': _(u
'Enter a valid IC number.'),
110 def clean(self
, value
):
111 super(CZICNumberField
, self
).__init
__(value
)
113 if value
in EMPTY_VALUES
:
116 match
= re
.match(ic_number
, value
)
118 raise ValidationError(self
.error_messages
['invalid'])
120 number
, check
= match
.groupdict()['number'], int(match
.groupdict()['check'])
125 sum += int(digit
)*weight
130 # remainder is equal:
131 # 0 or 10: last digit is 1
133 # in other case, last digin is 11 - remainder
135 if (not remainder
% 10 and check
== 1) or \
136 (remainder
== 1 and check
== 0) or \
137 (check
== (11 - remainder
)):
140 raise ValidationError(self
.error_messages
['invalid'])