# -*- coding: utf-8 -*-
"""
HR-specific Form helpers
"""
import re
from django.forms.fields import Field, Select, RegexField
from django.core.validators import EMPTY_VALUES
from django.forms import ValidationError
from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import smart_unicode
jmbg_re = re.compile(r'^(?P
\d{2})(?P\d{2})(?P\d{3})' + \
r'(?P\d{2})(?P\d{3})(?P\d{1})$')
oib_re = re.compile(r'^\d{11}$')
plate_re = re.compile(ur'^(?P[A-ZČŠŽ]{2})' + \
ur'(?P\d{3,4})(?P[ABCDEFGHIJKLMNOPRSTUVZ]{1,2})$')
postal_code_re = re.compile(r'^\d{5}$')
phone_re = re.compile(r'^(\+385|00385|0)(?P\d{2})(?P\d{6,7})$')
jmbag_re = re.compile(r'^601983(?P\d{1})1(?P\d{10})(?P\d{1})$')
class HRCountySelect(Select):
"""
A Select widget that uses a list of counties of Croatia as its choices.
"""
def __init__(self, attrs=None):
from hr_choices import HR_COUNTY_CHOICES
super(HRCountySelect, self).__init__(attrs, choices=HR_COUNTY_CHOICES)
class HRLicensePlatePrefixSelect(Select):
"""
A Select widget that uses a list of vehicle license plate prefixes of
Croatia as its choices.
"""
def __init__(self, attrs=None):
from hr_choices import HR_LICENSE_PLATE_PREFIX_CHOICES
super(HRLicensePlatePrefixSelect, self).__init__(attrs,
choices=HR_LICENSE_PLATE_PREFIX_CHOICES)
class HRPhoneNumberPrefixSelect(Select):
"""
A Select widget that uses a list of phone number prefixes of Croatia as its
choices.
"""
def __init__(self, attrs=None):
from hr_choices import HR_PHONE_NUMBER_PREFIX_CHOICES
super(HRPhoneNumberPrefixSelect, self).__init__(attrs,
choices=HR_PHONE_NUMBER_PREFIX_CHOICES)
class HRJMBGField(Field):
"""
Unique Master Citizen Number (JMBG) field.
The number is still in use in Croatia, but it is being replaced by OIB.
Source: http://en.wikipedia.org/wiki/Unique_Master_Citizen_Number
For who might be reimplementing:
The "area" regular expression group is used to calculate the region where a
person was registered. Additional validation can be implemented in
accordance with it, however this could result in exclusion of legit
immigrated citizens. Therefore, this field works for any ex-Yugoslavia
country.
"""
default_error_messages = {
'invalid': _('Enter a valid 13 digit JMBG'),
'date': _('Error in date segment'),
}
def clean(self, value):
super(HRJMBGField, self).clean(value)
if value in EMPTY_VALUES:
return u''
value = value.strip()
matches = jmbg_re.search(value)
if matches is None:
raise ValidationError(self.error_messages['invalid'])
# Make sure the date part is correct.
dd = int(matches.group('dd'))
mm = int(matches.group('mm'))
yyy = int(matches.group('yyy'))
import datetime
try:
datetime.date(yyy,mm,dd)
except:
raise ValidationError(self.error_messages['date'])
# Validate checksum.
k = matches.group('k')
checksum = 0
for i,j in zip(range(7,1,-1),range(6)):
checksum+=i*(int(value[j])+int(value[13-i]))
m = 11 - checksum % 11
if m == 10:
raise ValidationError(self.error_messages['invalid'])
if m == 11 and k != '0':
raise ValidationError(self.error_messages['invalid'])
if not str(m) == k:
raise ValidationError(self.error_messages['invalid'])
return u'%s' % (value, )
class HROIBField(RegexField):
"""
Personal Identification Number of Croatia (OIB) field.
http://www.oib.hr/
"""
default_error_messages = {
'invalid': _('Enter a valid 11 digit OIB'),
}
def __init__(self, min_length=11, max_length=11, *args, **kwargs):
super(HROIBField, self).__init__(r'^\d{11}$',
min_length, max_length, *args, **kwargs)
def clean(self, value):
super(HROIBField, self).clean(value)
if value in EMPTY_VALUES:
return u''
return '%s' % (value, )
class HRLicensePlateField(Field):
"""
Vehicle license plate of Croatia field. Normalizes to the specific format
below. Suffix is constructed from the shared letters of the Croatian and
English alphabets.
Format examples:
SB 123-A
(but also supports more characters)
ZG 1234-AA
Used for standardized license plates only.
"""
default_error_messages = {
'invalid': _('Enter a valid vehicle license plate number'),
'area': _('Enter a valid location code'),
'number': _('Number part cannot be zero'),
}
def clean(self, value):
super(HRLicensePlateField, self).clean(value)
if value in EMPTY_VALUES:
return u''
value = re.sub(r'[\s\-]+', '', smart_unicode(value.strip())).upper()
matches = plate_re.search(value)
if matches is None:
raise ValidationError(self.error_messages['invalid'])
# Make sure the prefix is in the list of known codes.
from hr_choices import HR_LICENSE_PLATE_PREFIX_CHOICES
prefix = matches.group('prefix')
if prefix not in [choice[0] for choice in HR_LICENSE_PLATE_PREFIX_CHOICES]:
raise ValidationError(self.error_messages['area'])
# Make sure the number portion is not zero.
number = matches.group('number')
if int(number) == 0:
raise ValidationError(self.error_messages['number'])
return u'%s %s-%s' % (prefix,number,matches.group('suffix'), )
class HRPostalCodeField(Field):
"""
Postal code of Croatia field.
It consists of exactly five digits ranging from 10000 to possibly less than
60000.
http://www.posta.hr/main.aspx?id=66
"""
default_error_messages = {
'invalid': _('Enter a valid 5 digit postal code'),
}
def clean(self, value):
super(HRPostalCodeField, self).clean(value)
if value in EMPTY_VALUES:
return u''
value = value.strip()
if not postal_code_re.search(value):
raise ValidationError(self.error_messages['invalid'])
# Make sure the number is in valid range.
if not 9999