diff --git a/django/contrib/localflavor/is_/__init__.py b/django/contrib/localflavor/is_/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/django/contrib/localflavor/is_/forms.py b/django/contrib/localflavor/is_/forms.py new file mode 100644 index 00000000000..3ee2d76937e --- /dev/null +++ b/django/contrib/localflavor/is_/forms.py @@ -0,0 +1,81 @@ +""" +Iceland specific form helpers. +""" +from django.newforms import ValidationError +from django.newforms.fields import RegexField, EMPTY_VALUES +from django.newforms.widgets import Select +from django.utils.translation import gettext + + +class ISIdNumberField(RegexField): + """ + Icelandic identification number (kennitala). This is a number every citizen + of Iceland has. + """ + def __init__(self, *args, **kwargs): + error_msg = gettext(u'Enter a valid Icelandic identification number. The format is XXXXXX-XXXX.') + kwargs['min_length'],kwargs['max_length'] = 10,11 + + super(ISIdNumberField, self).__init__(r'^\d{6}(-| )?\d{4}$', error_message=error_msg, *args, **kwargs) + + def clean(self, value): + value = super(ISIdNumberField, self).clean(value) + + if value in EMPTY_VALUES: + return u'' + + value = self._canonify(value) + if self._validate(value): + return self._format(value) + else: + raise ValidationError(gettext(u'The Icelandic identification number is not valid.')) + + + def _canonify(self, value): + """ + Returns the value as only digits. + """ + return value.replace('-', '').replace(' ', '') + + def _validate(self, value): + """ + Takes in the value in canonical form and checks the verifier digit. The + method is modulo 11. + """ + check = [3, 2, 7, 6, 5, 4, 3, 2, 1, 0] + return sum(int(value[i]) * check[i] for i in range(10)) % 11 == 0 + + def _format(self, value): + """ + Takes in the value in canonical form and returns it in the common + display format. + """ + return value[:6]+'-'+value[6:] + + +class ISPhoneNumberField(RegexField): + """ + Icelandic phone number. Seven digits with an optional hyphen or space after + the first three digits. + """ + def __init__(self, *args, **kwargs): + kwargs['min_length'], kwargs['max_length'] = 7,8 + super(ISPhoneNumberField, self).__init__(r'^\d{3}(-| )?\d{4}$', *args, **kwargs) + + def clean(self, value): + value = super(ISPhoneNumberField, self).clean(value) + + if value in EMPTY_VALUES: + return u'' + + return value.replace('-', '').replace(' ', '') + + +class ISPostalCodeSelect(Select): + """ + A Select widget that uses a list of Icelandic postal codes as its choices. + """ + def __init__(self, attrs=None): + from is_postalcodes import IS_POSTALCODES + super(ISPostalCodeSelect, self).__init__(attrs, choices=IS_POSTALCODES) + diff --git a/django/contrib/localflavor/is_/is_postalcodes.py b/django/contrib/localflavor/is_/is_postalcodes.py new file mode 100644 index 00000000000..4feca9c0131 --- /dev/null +++ b/django/contrib/localflavor/is_/is_postalcodes.py @@ -0,0 +1,151 @@ +# -*- coding: utf-8 -*- + +IS_POSTALCODES = ( + ('101', u'101 Reykjavík'), + ('103', u'103 Reykjavík'), + ('104', u'104 Reykjavík'), + ('105', u'105 Reykjavík'), + ('107', u'107 Reykjavík'), + ('108', u'108 Reykjavík'), + ('109', u'109 Reykjavík'), + ('110', u'110 Reykjavík'), + ('111', u'111 Reykjavík'), + ('112', u'112 Reykjavík'), + ('113', u'113 Reykjavík'), + ('116', u'116 Kjalarnes'), + ('121', u'121 Reykjavík'), + ('123', u'123 Reykjavík'), + ('124', u'124 Reykjavík'), + ('125', u'125 Reykjavík'), + ('127', u'127 Reykjavík'), + ('128', u'128 Reykjavík'), + ('129', u'129 Reykjavík'), + ('130', u'130 Reykjavík'), + ('132', u'132 Reykjavík'), + ('150', u'150 Reykjavík'), + ('155', u'155 Reykjavík'), + ('170', u'170 Seltjarnarnes'), + ('172', u'172 Seltjarnarnes'), + ('190', u'190 Vogar'), + ('200', u'200 Kópavogur'), + ('201', u'201 Kópavogur'), + ('202', u'202 Kópavogur'), + ('203', u'203 Kópavogur'), + ('210', u'210 Garðabær'), + ('212', u'212 Garðabær'), + ('220', u'220 Hafnarfjörður'), + ('221', u'221 Hafnarfjörður'), + ('222', u'222 Hafnarfjörður'), + ('225', u'225 Álftanes'), + ('230', u'230 Reykjanesbær'), + ('232', u'232 Reykjanesbær'), + ('233', u'233 Reykjanesbær'), + ('235', u'235 Keflavíkurflugvöllur'), + ('240', u'240 Grindavík'), + ('245', u'245 Sandgerði'), + ('250', u'250 Garður'), + ('260', u'260 Reykjanesbær'), + ('270', u'270 Mosfellsbær'), + ('300', u'300 Akranes'), + ('301', u'301 Akranes'), + ('302', u'302 Akranes'), + ('310', u'310 Borgarnes'), + ('311', u'311 Borgarnes'), + ('320', u'320 Reykholt í Borgarfirði'), + ('340', u'340 Stykkishólmur'), + ('345', u'345 Flatey á Breiðafirði'), + ('350', u'350 Grundarfjörður'), + ('355', u'355 Ólafsvík'), + ('356', u'356 Snæfellsbær'), + ('360', u'360 Hellissandur'), + ('370', u'370 Búðardalur'), + ('371', u'371 Búðardalur'), + ('380', u'380 Reykhólahreppur'), + ('400', u'400 Ísafjörður'), + ('401', u'401 Ísafjörður'), + ('410', u'410 Hnífsdalur'), + ('415', u'415 Bolungarvík'), + ('420', u'420 Súðavík'), + ('425', u'425 Flateyri'), + ('430', u'430 Suðureyri'), + ('450', u'450 Patreksfjörður'), + ('451', u'451 Patreksfjörður'), + ('460', u'460 Tálknafjörður'), + ('465', u'465 Bíldudalur'), + ('470', u'470 Þingeyri'), + ('471', u'471 Þingeyri'), + ('500', u'500 Staður'), + ('510', u'510 Hólmavík'), + ('512', u'512 Hólmavík'), + ('520', u'520 Drangsnes'), + ('522', u'522 Kjörvogur'), + ('523', u'523 Bær'), + ('524', u'524 Norðurfjörður'), + ('530', u'530 Hvammstangi'), + ('531', u'531 Hvammstangi'), + ('540', u'540 Blönduós'), + ('541', u'541 Blönduós'), + ('545', u'545 Skagaströnd'), + ('550', u'550 Sauðárkrókur'), + ('551', u'551 Sauðárkrókur'), + ('560', u'560 Varmahlíð'), + ('565', u'565 Hofsós'), + ('566', u'566 Hofsós'), + ('570', u'570 Fljót'), + ('580', u'580 Siglufjörður'), + ('600', u'600 Akureyri'), + ('601', u'601 Akureyri'), + ('602', u'602 Akureyri'), + ('603', u'603 Akureyri'), + ('610', u'610 Grenivík'), + ('611', u'611 Grímsey'), + ('620', u'620 Dalvík'), + ('621', u'621 Dalvík'), + ('625', u'625 Ólafsfjörður'), + ('630', u'630 Hrísey'), + ('640', u'640 Húsavík'), + ('641', u'641 Húsavík'), + ('645', u'645 Fosshóll'), + ('650', u'650 Laugar'), + ('660', u'660 Mývatn'), + ('670', u'670 Kópasker'), + ('671', u'671 Kópasker'), + ('675', u'675 Raufarhöfn'), + ('680', u'680 Þórshöfn'), + ('681', u'681 Þórshöfn'), + ('685', u'685 Bakkafjörður'), + ('690', u'690 Vopnafjörður'), + ('700', u'700 Egilsstaðir'), + ('701', u'701 Egilsstaðir'), + ('710', u'710 Seyðisfjörður'), + ('715', u'715 Mjóifjörður'), + ('720', u'720 Borgarfjörður eystri'), + ('730', u'730 Reyðarfjörður'), + ('735', u'735 Eskifjörður'), + ('740', u'740 Neskaupstaður'), + ('750', u'750 Fáskrúðsfjörður'), + ('755', u'755 Stöðvarfjörður'), + ('760', u'760 Breiðdalsvík'), + ('765', u'765 Djúpivogur'), + ('780', u'780 Höfn í Hornafirði'), + ('781', u'781 Höfn í Hornafirði'), + ('785', u'785 Öræfi'), + ('800', u'800 Selfoss'), + ('801', u'801 Selfoss'), + ('802', u'802 Selfoss'), + ('810', u'810 Hveragerði'), + ('815', u'815 Þorlákshöfn'), + ('820', u'820 Eyrarbakki'), + ('825', u'825 Stokkseyri'), + ('840', u'840 Laugarvatn'), + ('845', u'845 Flúðir'), + ('850', u'850 Hella'), + ('851', u'851 Hella'), + ('860', u'860 Hvolsvöllur'), + ('861', u'861 Hvolsvöllur'), + ('870', u'870 Vík'), + ('871', u'871 Vík'), + ('880', u'880 Kirkjubæjarklaustur'), + ('900', u'900 Vestmannaeyjar'), + ('902', u'902 Vestmannaeyjar') +) diff --git a/tests/regressiontests/forms/localflavor.py b/tests/regressiontests/forms/localflavor.py index 3f4f41ae8d4..e88228f2524 100644 --- a/tests/regressiontests/forms/localflavor.py +++ b/tests/regressiontests/forms/localflavor.py @@ -1052,4 +1052,102 @@ states/territories as its choices. + +## ISIdNumberField ############################################################# + +>>> from django.contrib.localflavor.is_.forms import * +>>> f = ISIdNumberField() +>>> f.clean('2308803449') +u'230880-3449' +>>> f.clean('230880-3449') +u'230880-3449' +>>> f.clean('230880 3449') +u'230880-3449' +>>> f.clean('230880343') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at least 10 characters.'] +>>> f.clean('230880343234') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at most 11 characters.'] +>>> f.clean('abcdefghijk') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid Icelandic identification number. The format is XXXXXX-XXXX.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('2308803439') +Traceback (most recent call last): +... +ValidationError: [u'The Icelandic identification number is not valid.'] +>>> f.clean('2308803440') +u'230880-3440' +>>> f = ISIdNumberField(required=False) +>>> f.clean(None) +u'' +>>> f.clean('') +u'' + +## ISPhoneNumberField ############################################################# + +>>> from django.contrib.localflavor.is_.forms import * +>>> f = ISPhoneNumberField() +>>> f.clean('1234567') +u'1234567' +>>> f.clean('123 4567') +u'1234567' +>>> f.clean('123-4567') +u'1234567' +>>> f.clean('123-456') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid value.'] +>>> f.clean('123456') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at least 7 characters.'] +>>> f.clean('123456555') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at most 8 characters.'] +>>> f.clean('abcdefg') +Traceback (most recent call last): +ValidationError: [u'Enter a valid value.'] +>>> f.clean(' 1234567 ') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at most 8 characters.'] +>>> f.clean(' 12367 ') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid value.'] + +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f = ISPhoneNumberField(required=False) +>>> f.clean(None) +u'' +>>> f.clean('') +u'' + +## ISPostalCodeSelect ############################################################# + +>>> from django.contrib.localflavor.is_.forms import * +>>> f = ISPostalCodeSelect() + +>>> f.render('foo', 'bar') +u'' """