Ticket #19037: localflavor-sg.patch

File localflavor-sg.patch, 8.6 KB (added by James Rivett-Carnac, 12 years ago)

forms, validators and tests for localflavor.sg

  • new file django/contrib/localflavor/sg/forms.py

    diff --git a/django/contrib/localflavor/sg/__init__.py b/django/contrib/localflavor/sg/__init__.py
    new file mode 100644
    index 0000000..e69de29
    diff --git a/django/contrib/localflavor/sg/forms.py b/django/contrib/localflavor/sg/forms.py
    new file mode 100644
    index 0000000..5858506
    - +  
     1
     2"""
     3SG-specific Form helpers
     4"""
     5
     6from __future__ import absolute_import
     7
     8import re
     9import time
     10
     11from django.core.validators import EMPTY_VALUES
     12from django.forms import ValidationError
     13from django.core.validators import RegexValidator
     14from django.forms.fields import Field, Select
     15from django.utils.translation import ugettext_lazy as _
     16from django.utils.encoding import smart_unicode
     17
     18# from http://www.ura.gov.sg/realEstateWeb/resources/misc/list_of_postal_districts.htm
     19postcode_re = re.compile(r'^(?!(83|84|85|86|87|88|89))([0-8][0-9])\d{4}$')
     20# RE for both NRIC and FIN.  Check sum and validity of the numerical part
     21# are not calculated, due to branching rules and b/c the checksum isn't
     22# in the public domain.
     23# Format : @0000000#
     24# @ is centry marker, S,T for NRIC, F,G for FIN
     25# 0000000 is the serial no, with aditional constraints for birth year.
     26# # is Checksum.
     27nric_re = re.compile(r'([sftg])\d{7}[a-z]$',re.IGNORECASE)
     28mobile_phone_re = re.compile(r'^[(]?(?P<cc>\+65)[)]?(?P<num>[89]\d{7})')
     29phone_re = re.compile(r'^[(]?(?P<cc>\+65)[)]?(?P<num>[3689]\d{7})')
     30
     31class NRICValidator(RegexValidator):
     32    code = 'invalid'
     33    message = _('Enter a valid NRIC/FIN number')
     34    regex = nric_re
     35
     36    def __call__(self,value):
     37        super(NRICValidator,self).__call__(value)
     38
     39validate_nric = NRICValidator()
     40
     41class SGPhoneValidator(RegexValidator):
     42    code = 'invalid'
     43    message = _('Enter a valid phone number'),
     44    regex = phone_re
     45
     46    def __call__(self,value):
     47        super(SGPhoneValidator,self).__call__(value)
     48
     49validate_SG_phone = SGPhoneValidator()
     50validate_SG_mobile_phone = SGPhoneValidator(regex=mobile_phone_re)
     51
     52class SGPostCodeValidator(RegexValidator):
     53    code = 'invalid'
     54    message = _('Enter a valid post code'),
     55    regex = postcode_re
     56
     57    def __call__(self,value):
     58        super(SGPostCodeValidator,self).__call__(value)
     59
     60validate_SG_postcode = SGPostCodeValidator()
     61
     62class SGPostCodeField(Field):
     63    """
     64    A Singaporian post code field.
     65
     66    http://www.ura.gov.sg/realEstateWeb/resources/misc/list_of_postal_districts.htm
     67    """
     68    default_error_messages = {
     69            'invalid': _('Enter a valid post code'),
     70            'required': _('Post code is required'),
     71            }
     72    default_validators = [validate_SG_postcode]
     73
     74    def validate(self, value):
     75        if value in EMPTY_VALUES and self.required:
     76            raise ValidationError(self.error_messages['required'])
     77
     78    def to_python(self,value):
     79        if not value:
     80            return u''
     81        return value.strip()
     82
     83    def clean(self,value):
     84        value = self.to_python(value)
     85        self.validate(value)
     86        self.run_validators(value)
     87        return value
     88
     89class SGPhoneNumberField(Field):
     90    """
     91    Singaporian phone number field.
     92
     93    http://en.wikipedia.org/wiki/Telephone_numbers_in_Singapore
     94    """
     95    default_error_messages = {
     96            'invalid': _('Enter a valid phone number'),
     97            }
     98
     99    def __init__(self, mobile_phone_only=False, *args, **kwargs):
     100        if mobile_phone_only:
     101            validators = [validate_SG_mobile_phone,]
     102        else:
     103            validators = [validate_SG_phone,]
     104        super(SGPhoneNumberField, self).__init__(validators=validators, *args,
     105                **kwargs)
     106
     107    def to_python(self,value):
     108        if not value:
     109            return u''
     110        return value.strip()
     111
     112    def clean(self,value):
     113        value = self.to_python(value)
     114        self.validate(value)
     115        self.run_validators(value)
     116        v_search = phone_re.search(value)
     117        if value in EMPTY_VALUES and not self.required:
     118            return value
     119        elif not v_search:
     120            raise ValidationError(self.default_error_messages['invalid'])
     121
     122        return u"{0}{1}".format(v_search.group('cc'),
     123                                v_search.group('num'))
     124
     125class SGNationalRegistrationIdentityCard(Field):
     126    """
     127    Singaporian NRIC and FIN card
     128
     129    http://http://en.wikipedia.org/wiki/National_Registration_Identity_Card
     130    """
     131    default_error_messages = {
     132            'invalid': _('Enter a valid NRIC/FIN number'),
     133            'required': _('NRIC/FIN is required'),
     134                }
     135    default_validators = [RegexValidator(nric_re,code='invalid')]
     136
     137    def to_python(self,value):
     138        if not value:
     139            return u''
     140        return value.strip()
     141
     142    def clean(self,value):
     143        """Validates NRIC/FIN number, cleans, etc.
     144        raises ValidationError on any error"""
     145        value = super(SGNationalRegistrationIdentityCard, self).clean(value)
     146        return value.upper()
  • new file tests/regressiontests/localflavor/sg/tests.py

    diff --git a/tests/regressiontests/generic_views/.edit.py.swp b/tests/regressiontests/generic_views/.edit.py.swp
    new file mode 100644
    index 0000000..a498a68
    Binary files /dev/null and b/tests/regressiontests/generic_views/.edit.py.swp differ
    diff --git a/tests/regressiontests/localflavor/sg/__init__.py b/tests/regressiontests/localflavor/sg/__init__.py
    new file mode 100644
    index 0000000..e69de29
    diff --git a/tests/regressiontests/localflavor/sg/tests.py b/tests/regressiontests/localflavor/sg/tests.py
    new file mode 100644
    index 0000000..1f77cb8
    - +  
     1import warnings
     2
     3from django.contrib.localflavor.sg.forms import (SGPhoneNumberField,
     4        SGPostCodeField, SGNationalRegistrationIdentityCard)
     5
     6from django.test import SimpleTestCase
     7
     8class SGLocalFlavorTests(SimpleTestCase):
     9    """Test the form"""
     10    def test_phone(self):
     11        error_invalid = [u'Enter a valid phone number']
     12        valid = {
     13                '+6594761111':'+6594761111',
     14                '(+65)94761111':'+6594761111',
     15                '+6565521111':'+6565521111',
     16                }
     17        invalid = {
     18                '94761111':error_invalid,
     19                '(+65)44761111':error_invalid,
     20                '+6365521111':error_invalid,
     21                }
     22        self.assertFieldOutput(SGPhoneNumberField, valid, invalid)
     23
     24    def test_mobile(self):
     25        error_invalid = [u'Enter a valid phone number']
     26        valid = {
     27                '+6594761111':'+6594761111',
     28                '(+65)94761111':'+6594761111',
     29                '+6585521111':'+6585521111',
     30        }
     31        invalid = {
     32                '94761111':error_invalid,
     33                '(+65)44761111':error_invalid,
     34                '+6365521111':error_invalid,
     35                '+6565521111':error_invalid,
     36                }
     37        self.assertFieldOutput(SGPhoneNumberField, valid, invalid,
     38                field_kwargs={'mobile_phone_only':True,})
     39
     40    def test_postcode(self):
     41        error_invalid = [u'Enter a valid post code']
     42        error_required = [u'Post code is required']
     43        valid = {
     44                '570248':'570248',
     45                '010001':'010001',
     46                '580222':'580222',
     47                }
     48        invalid = {
     49                '852222':error_invalid,
     50                'm5d-2g4':error_invalid,
     51                '123':error_invalid,
     52                }
     53        self.assertFieldOutput(SGPostCodeField, valid, invalid,
     54                field_kwargs={'required':True})
     55
     56    def test_NRIC_num(self):
     57        error_invalid = [u'Enter a valid NRIC/FIN number']
     58        valid = {
     59                'S0850356G':'S0850356G',
     60                'T0850356C':'T0850356C',
     61                'F8554629P':'F8554629P',
     62                'G0854629R':'G0854629R',
     63                'g0854629R':'G0854629R',
     64                }
     65        invalid= {
     66                'S0123F':error_invalid,
     67                'R0850356G':error_invalid,
     68                'F8554629PP':error_invalid,
     69                }
     70        self.assertFieldOutput(SGNationalRegistrationIdentityCard, valid, invalid)
  • tests/regressiontests/localflavor/tests.py

    diff --git a/tests/regressiontests/localflavor/tests.py b/tests/regressiontests/localflavor/tests.py
    index 856e518..bab4b0f 100644
    a b from .py.tests import PYLocalFlavorTests  
    3737from .ro.tests import ROLocalFlavorTests
    3838from .ru.tests import RULocalFlavorTests
    3939from .se.tests import SELocalFlavorTests
     40from .sg.tests import SGLocalFlavorTests
    4041from .si.tests import SILocalFlavorTests
    4142from .sk.tests import SKLocalFlavorTests
    4243from .tr.tests import TRLocalFlavorTests
Back to Top