web/lib/django/contrib/localflavor/se/forms.py
author ymh <ymh.work@gmail.com>
Tue, 25 May 2010 02:43:45 +0200
changeset 29 cc9b7e14412b
permissions -rw-r--r--
update django and lucene
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
29
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
     1
# -*- coding: utf-8 -*-
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
     2
"""
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
     3
Swedish specific Form helpers
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
     4
"""
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
     5
import re
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
     6
from django import forms
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
     7
from django.utils.translation import ugettext_lazy as _
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
     8
from django.core.validators import EMPTY_VALUES
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
     9
from django.contrib.localflavor.se.utils import (id_number_checksum,
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    10
    validate_id_birthday, format_personal_id_number, valid_organisation,
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    11
    format_organisation_number)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    12
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    13
__all__ = ('SECountySelect', 'SEOrganisationNumberField',
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    14
    'SEPersonalIdentityNumberField', 'SEPostalCodeField')
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    15
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    16
SWEDISH_ID_NUMBER = re.compile(r'^(?P<century>\d{2})?(?P<year>\d{2})(?P<month>\d{2})(?P<day>\d{2})(?P<sign>[\-+])?(?P<serial>\d{3})(?P<checksum>\d)$')
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    17
SE_POSTAL_CODE = re.compile(r'^[1-9]\d{2} ?\d{2}$')
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    18
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    19
class SECountySelect(forms.Select):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    20
    """
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    21
    A Select form widget that uses a list of the Swedish counties (län) as its
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    22
    choices.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    23
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    24
    The cleaned value is the official county code -- see
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    25
    http://en.wikipedia.org/wiki/Counties_of_Sweden for a list.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    26
    """
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    27
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    28
    def __init__(self, attrs=None):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    29
        from se_counties import COUNTY_CHOICES
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    30
        super(SECountySelect, self).__init__(attrs=attrs,
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    31
                                             choices=COUNTY_CHOICES)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    32
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    33
class SEOrganisationNumberField(forms.CharField):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    34
    """
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    35
    A form field that validates input as a Swedish organisation number
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    36
    (organisationsnummer).
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    37
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    38
    It accepts the same input as SEPersonalIdentityField (for sole
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    39
    proprietorships (enskild firma). However, co-ordination numbers are not
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    40
    accepted.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    41
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    42
    It also accepts ordinary Swedish organisation numbers with the format
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    43
    NNNNNNNNNN.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    44
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    45
    The return value will be YYYYMMDDXXXX for sole proprietors, and NNNNNNNNNN
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    46
    for other organisations.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    47
    """
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    48
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    49
    default_error_messages = {
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    50
        'invalid': _('Enter a valid Swedish organisation number.'),
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    51
    }
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    52
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    53
    def clean(self, value):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    54
        value = super(SEOrganisationNumberField, self).clean(value)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    55
        
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    56
        if value in EMPTY_VALUES:
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    57
            return u''
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    58
        
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    59
        match = SWEDISH_ID_NUMBER.match(value)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    60
        if not match:
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    61
            raise forms.ValidationError(self.error_messages['invalid'])
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    62
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    63
        gd = match.groupdict()
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    64
        
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    65
        # Compare the calculated value with the checksum 
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    66
        if id_number_checksum(gd) != int(gd['checksum']):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    67
            raise forms.ValidationError(self.error_messages['invalid'])
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    68
        
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    69
        # First: check if this is a real organisation_number
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    70
        if valid_organisation(gd):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    71
            return format_organisation_number(gd)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    72
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    73
        # Is this a single properitor (enskild firma)?
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    74
        try:
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    75
            birth_day = validate_id_birthday(gd, False)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    76
            return format_personal_id_number(birth_day, gd)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    77
        except ValueError:
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    78
            raise forms.ValidationError(self.error_messages['invalid'])
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    79
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    80
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    81
class SEPersonalIdentityNumberField(forms.CharField):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    82
    """
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    83
    A form field that validates input as a Swedish personal identity number
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    84
    (personnummer).
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    85
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    86
    The correct formats are YYYYMMDD-XXXX, YYYYMMDDXXXX, YYMMDD-XXXX,
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    87
    YYMMDDXXXX and YYMMDD+XXXX.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    88
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    89
    A + indicates that the person is older than 100 years, which will be taken
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    90
    into consideration when the date is validated.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    91
    
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    92
    The checksum will be calculated and checked. The birth date is checked to
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    93
    be a valid date.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    94
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    95
    By default, co-ordination numbers (samordningsnummer) will be accepted. To
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    96
    only allow real personal identity numbers, pass the keyword argument
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    97
    coordination_number=False to the constructor.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    98
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    99
    The cleaned value will always have the format YYYYMMDDXXXX.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   100
    """
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   101
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   102
    def __init__(self, coordination_number=True, *args, **kwargs):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   103
        self.coordination_number = coordination_number
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   104
        super(SEPersonalIdentityNumberField, self).__init__(*args, **kwargs)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   105
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   106
    default_error_messages = {
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   107
        'invalid': _('Enter a valid Swedish personal identity number.'),
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   108
        'coordination_number': _('Co-ordination numbers are not allowed.'),
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   109
    }
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   110
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   111
    def clean(self, value):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   112
        value = super(SEPersonalIdentityNumberField, self).clean(value)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   113
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   114
        if value in EMPTY_VALUES:
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   115
            return u''
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   116
 
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   117
        match = SWEDISH_ID_NUMBER.match(value)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   118
        if match is None:
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   119
            raise forms.ValidationError(self.error_messages['invalid'])
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   120
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   121
        gd = match.groupdict()
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   122
 
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   123
        # compare the calculated value with the checksum 
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   124
        if id_number_checksum(gd) != int(gd['checksum']):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   125
            raise forms.ValidationError(self.error_messages['invalid'])
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   126
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   127
        # check for valid birthday
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   128
        try:
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   129
            birth_day = validate_id_birthday(gd)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   130
        except ValueError:
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   131
            raise forms.ValidationError(self.error_messages['invalid'])
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   132
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   133
        # make sure that co-ordination numbers do not pass if not allowed 
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   134
        if not self.coordination_number and int(gd['day']) > 60:
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   135
            raise forms.ValidationError(self.error_messages['coordination_number'])
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   136
  
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   137
        return format_personal_id_number(birth_day, gd)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   138
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   139
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   140
class SEPostalCodeField(forms.RegexField):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   141
    """
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   142
    A form field that validates input as a Swedish postal code (postnummer).
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   143
    Valid codes consist of five digits (XXXXX). The number can optionally be
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   144
    formatted with a space after the third digit (XXX XX).
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   145
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   146
    The cleaned value will never contain the space. 
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   147
    """
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   148
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   149
    default_error_messages = {
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   150
        'invalid': _('Enter a Swedish postal code in the format XXXXX.'),
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   151
    }
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   152
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   153
    def __init__(self, *args, **kwargs):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   154
        super(SEPostalCodeField, self).__init__(SE_POSTAL_CODE, *args, **kwargs)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   155
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   156
    def clean(self, value):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   157
        return super(SEPostalCodeField, self).clean(value).replace(' ', '')