diff -r b758351d191f -r cc9b7e14412b web/lib/django/contrib/localflavor/se/forms.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/lib/django/contrib/localflavor/se/forms.py Tue May 25 02:43:45 2010 +0200 @@ -0,0 +1,157 @@ +# -*- coding: utf-8 -*- +""" +Swedish specific Form helpers +""" +import re +from django import forms +from django.utils.translation import ugettext_lazy as _ +from django.core.validators import EMPTY_VALUES +from django.contrib.localflavor.se.utils import (id_number_checksum, + validate_id_birthday, format_personal_id_number, valid_organisation, + format_organisation_number) + +__all__ = ('SECountySelect', 'SEOrganisationNumberField', + 'SEPersonalIdentityNumberField', 'SEPostalCodeField') + +SWEDISH_ID_NUMBER = re.compile(r'^(?P\d{2})?(?P\d{2})(?P\d{2})(?P\d{2})(?P[\-+])?(?P\d{3})(?P\d)$') +SE_POSTAL_CODE = re.compile(r'^[1-9]\d{2} ?\d{2}$') + +class SECountySelect(forms.Select): + """ + A Select form widget that uses a list of the Swedish counties (län) as its + choices. + + The cleaned value is the official county code -- see + http://en.wikipedia.org/wiki/Counties_of_Sweden for a list. + """ + + def __init__(self, attrs=None): + from se_counties import COUNTY_CHOICES + super(SECountySelect, self).__init__(attrs=attrs, + choices=COUNTY_CHOICES) + +class SEOrganisationNumberField(forms.CharField): + """ + A form field that validates input as a Swedish organisation number + (organisationsnummer). + + It accepts the same input as SEPersonalIdentityField (for sole + proprietorships (enskild firma). However, co-ordination numbers are not + accepted. + + It also accepts ordinary Swedish organisation numbers with the format + NNNNNNNNNN. + + The return value will be YYYYMMDDXXXX for sole proprietors, and NNNNNNNNNN + for other organisations. + """ + + default_error_messages = { + 'invalid': _('Enter a valid Swedish organisation number.'), + } + + def clean(self, value): + value = super(SEOrganisationNumberField, self).clean(value) + + if value in EMPTY_VALUES: + return u'' + + match = SWEDISH_ID_NUMBER.match(value) + if not match: + raise forms.ValidationError(self.error_messages['invalid']) + + gd = match.groupdict() + + # Compare the calculated value with the checksum + if id_number_checksum(gd) != int(gd['checksum']): + raise forms.ValidationError(self.error_messages['invalid']) + + # First: check if this is a real organisation_number + if valid_organisation(gd): + return format_organisation_number(gd) + + # Is this a single properitor (enskild firma)? + try: + birth_day = validate_id_birthday(gd, False) + return format_personal_id_number(birth_day, gd) + except ValueError: + raise forms.ValidationError(self.error_messages['invalid']) + + +class SEPersonalIdentityNumberField(forms.CharField): + """ + A form field that validates input as a Swedish personal identity number + (personnummer). + + The correct formats are YYYYMMDD-XXXX, YYYYMMDDXXXX, YYMMDD-XXXX, + YYMMDDXXXX and YYMMDD+XXXX. + + A + indicates that the person is older than 100 years, which will be taken + into consideration when the date is validated. + + The checksum will be calculated and checked. The birth date is checked to + be a valid date. + + By default, co-ordination numbers (samordningsnummer) will be accepted. To + only allow real personal identity numbers, pass the keyword argument + coordination_number=False to the constructor. + + The cleaned value will always have the format YYYYMMDDXXXX. + """ + + def __init__(self, coordination_number=True, *args, **kwargs): + self.coordination_number = coordination_number + super(SEPersonalIdentityNumberField, self).__init__(*args, **kwargs) + + default_error_messages = { + 'invalid': _('Enter a valid Swedish personal identity number.'), + 'coordination_number': _('Co-ordination numbers are not allowed.'), + } + + def clean(self, value): + value = super(SEPersonalIdentityNumberField, self).clean(value) + + if value in EMPTY_VALUES: + return u'' + + match = SWEDISH_ID_NUMBER.match(value) + if match is None: + raise forms.ValidationError(self.error_messages['invalid']) + + gd = match.groupdict() + + # compare the calculated value with the checksum + if id_number_checksum(gd) != int(gd['checksum']): + raise forms.ValidationError(self.error_messages['invalid']) + + # check for valid birthday + try: + birth_day = validate_id_birthday(gd) + except ValueError: + raise forms.ValidationError(self.error_messages['invalid']) + + # make sure that co-ordination numbers do not pass if not allowed + if not self.coordination_number and int(gd['day']) > 60: + raise forms.ValidationError(self.error_messages['coordination_number']) + + return format_personal_id_number(birth_day, gd) + + +class SEPostalCodeField(forms.RegexField): + """ + A form field that validates input as a Swedish postal code (postnummer). + Valid codes consist of five digits (XXXXX). The number can optionally be + formatted with a space after the third digit (XXX XX). + + The cleaned value will never contain the space. + """ + + default_error_messages = { + 'invalid': _('Enter a Swedish postal code in the format XXXXX.'), + } + + def __init__(self, *args, **kwargs): + super(SEPostalCodeField, self).__init__(SE_POSTAL_CODE, *args, **kwargs) + + def clean(self, value): + return super(SEPostalCodeField, self).clean(value).replace(' ', '')