From 0b2f9e89cdca3f4e05c160537119a7e7f390c187 Mon Sep 17 00:00:00 2001 From: Malcolm Tredinnick Date: Tue, 1 May 2007 03:10:10 +0000 Subject: Fixed #3946 -- Added Swiss localflavor. Thanks, charly.wilhelm@gmail.com. git-svn-id: http://code.djangoproject.com/svn/django/trunk@5132 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/contrib/localflavor/ch/__init__.py | 0 django/contrib/localflavor/ch/ch_states.py | 31 ++++++++ django/contrib/localflavor/ch/forms.py | 109 +++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+) create mode 100644 django/contrib/localflavor/ch/__init__.py create mode 100644 django/contrib/localflavor/ch/ch_states.py create mode 100644 django/contrib/localflavor/ch/forms.py (limited to 'django') diff --git a/django/contrib/localflavor/ch/__init__.py b/django/contrib/localflavor/ch/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/django/contrib/localflavor/ch/ch_states.py b/django/contrib/localflavor/ch/ch_states.py new file mode 100644 index 0000000000..e9bbcc6268 --- /dev/null +++ b/django/contrib/localflavor/ch/ch_states.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -* +from django.utils.translation import gettext_lazy as _ + +STATE_CHOICES = ( + ('AG', _('Aargau')), + ('AI', _('Appenzell Innerrhoden')), + ('AR', _('Appenzell Ausserrhoden')), + ('BS', _('Basel-Stadt')), + ('BL', _('Basel-Land')), + ('BE', _('Berne')), + ('FR', _('Fribourg')), + ('GE', _('Geneva')), + ('GL', _('Glarus')), + ('GR', _('Graubuenden')), + ('JU', _('Jura')), + ('LU', _('Lucerne')), + ('NE', _('Neuchatel')), + ('NW', _('Nidwalden')), + ('OW', _('Obwalden')), + ('SH', _('Schaffhausen')), + ('SZ', _('Schwyz')), + ('SO', _('Solothurn')), + ('SG', _('St. Gallen')), + ('TG', _('Thurgau')), + ('TI', _('Ticino')), + ('UR', _('Uri')), + ('VS', _('Valais')), + ('VD', _('Vaud')), + ('ZG', _('Zug')), + ('ZH', _('Zurich')) +) diff --git a/django/contrib/localflavor/ch/forms.py b/django/contrib/localflavor/ch/forms.py new file mode 100644 index 0000000000..51e52dc0e9 --- /dev/null +++ b/django/contrib/localflavor/ch/forms.py @@ -0,0 +1,109 @@ +""" +Swiss-specific Form helpers +""" + +from django.newforms import ValidationError +from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES +from django.utils.encoding import smart_unicode +from django.utils.translation import gettext +import re + +id_re = re.compile(r"^(?P\w{8})(?P(\d{1}|<))(?P\d{1})$") +phone_digits_re = re.compile(r'^0([1-9]{1})\d{8}$') + +class CHZipCodeField(RegexField): + def __init__(self, *args, **kwargs): + super(CHZipCodeField, self).__init__(r'^\d{4}$', + max_length=None, min_length=None, + error_message=gettext('Enter a zip code in the format XXXX.'), + *args, **kwargs) + +class CHPhoneNumberField(Field): + """ + Validate local Swiss phone number (not international ones) + The correct format is '0XX XXX XX XX'. + '0XX.XXX.XX.XX' and '0XXXXXXXXX' validate but are corrected to + '0XX XXX XX XX'. + """ + def clean(self, value): + super(CHPhoneNumberField, self).clean(value) + if value in EMPTY_VALUES: + return u'' + value = re.sub('(\.|\s|/|-)', '', smart_unicode(value)) + m = phone_digits_re.search(value) + if m: + return u'%s %s %s %s' % (value[0:3], value[3:6], value[6:8], value[8:10]) + raise ValidationError('Phone numbers must be in 0XX XXX XX XX format.') + +class CHStateSelect(Select): + """ + A Select widget that uses a list of CH states as its choices. + """ + def __init__(self, attrs=None): + from ch_states import STATE_CHOICES # relative import + super(CHStateSelect, self).__init__(attrs, choices=STATE_CHOICES) + +class CHIdentityCardNumberField(Field): + """ + A Swiss identity card number. + + Checks the following rules to determine whether the number is valid: + + * Conforms to the X1234567<0 or 1234567890 format. + * Included checksums match calculated checksums + + Algorithm is documented at http://adi.kousz.ch/artikel/IDCHE.htm + """ + def has_valid_checksum(self, number): + given_number, given_checksum = number[:-1], number[-1] + new_number = given_number + calculated_checksum = 0 + fragment = "" + parameter = 7 + + first = str(number[:1]) + if first.isalpha(): + num = ord(first.upper()) - 65 + if num < 0 or num > 8: + return False + new_number = str(num) + new_number[1:] + new_number = new_number[:8] + '0' + + if not new_number.isdigit(): + return False + + for i in range(len(new_number)): + fragment = int(new_number[i])*parameter + calculated_checksum += fragment + + if parameter == 1: + parameter = 7 + elif parameter == 3: + parameter = 1 + elif parameter ==7: + parameter = 3 + + return str(calculated_checksum)[-1] == given_checksum + + def clean(self, value): + super(CHIdentityCardNumberField, self).clean(value) + error_msg = gettext('Enter a valid Swiss identity or passport card number in X1234567<0 or 1234567890 format.') + if value in EMPTY_VALUES: + return u'' + + match = re.match(id_re, value) + if not match: + raise ValidationError(error_msg) + + idnumber, pos9, checksum = match.groupdict()['idnumber'], match.groupdict()['pos9'], match.groupdict()['checksum'] + + if idnumber == '00000000' or \ + idnumber == 'A0000000': + raise ValidationError(error_msg) + + all_digits = "%s%s%s" % (idnumber, pos9, checksum) + if not self.has_valid_checksum(all_digits): + raise ValidationError(error_msg) + + return u'%s%s%s' % (idnumber, pos9, checksum) + -- cgit v1.3