PageRenderTime 392ms CodeModel.GetById 366ms app.highlight 20ms RepoModel.GetById 1ms app.codeStats 0ms

/django/contrib/localflavor/id/forms.py

https://code.google.com/p/mango-py/
Python | 211 lines | 180 code | 12 blank | 19 comment | 23 complexity | 650f1d4a09b2b612b34dc268bf97d9e1 MD5 | raw file
  1"""
  2ID-specific Form helpers
  3"""
  4
  5import re
  6import time
  7
  8from django.core.validators import EMPTY_VALUES
  9from django.forms import ValidationError
 10from django.forms.fields import Field, Select
 11from django.utils.translation import ugettext_lazy as _
 12from django.utils.encoding import smart_unicode
 13
 14postcode_re = re.compile(r'^[1-9]\d{4}$')
 15phone_re = re.compile(r'^(\+62|0)[2-9]\d{7,10}$')
 16plate_re = re.compile(r'^(?P<prefix>[A-Z]{1,2}) ' + \
 17            r'(?P<number>\d{1,5})( (?P<suffix>([A-Z]{1,3}|[1-9][0-9]{,2})))?$')
 18nik_re = re.compile(r'^\d{16}$')
 19
 20
 21class IDPostCodeField(Field):
 22    """
 23    An Indonesian post code field.
 24
 25    http://id.wikipedia.org/wiki/Kode_pos
 26    """
 27    default_error_messages = {
 28        'invalid': _('Enter a valid post code'),
 29    }
 30
 31    def clean(self, value):
 32        super(IDPostCodeField, self).clean(value)
 33        if value in EMPTY_VALUES:
 34            return u''
 35
 36        value = value.strip()
 37        if not postcode_re.search(value):
 38            raise ValidationError(self.error_messages['invalid'])
 39
 40        if int(value) < 10110:
 41            raise ValidationError(self.error_messages['invalid'])
 42
 43        # 1xxx0
 44        if value[0] == '1' and value[4] != '0':
 45            raise ValidationError(self.error_messages['invalid'])
 46
 47        return u'%s' % (value, )
 48
 49
 50class IDProvinceSelect(Select):
 51    """
 52    A Select widget that uses a list of provinces of Indonesia as its
 53    choices.
 54    """
 55
 56    def __init__(self, attrs=None):
 57        from id_choices import PROVINCE_CHOICES
 58        super(IDProvinceSelect, self).__init__(attrs, choices=PROVINCE_CHOICES)
 59
 60
 61class IDPhoneNumberField(Field):
 62    """
 63    An Indonesian telephone number field.
 64
 65    http://id.wikipedia.org/wiki/Daftar_kode_telepon_di_Indonesia
 66    """
 67    default_error_messages = {
 68        'invalid': _('Enter a valid phone number'),
 69    }
 70
 71    def clean(self, value):
 72        super(IDPhoneNumberField, self).clean(value)
 73        if value in EMPTY_VALUES:
 74            return u''
 75
 76        phone_number = re.sub(r'[\-\s\(\)]', '', smart_unicode(value))
 77
 78        if phone_re.search(phone_number):
 79            return smart_unicode(value)
 80
 81        raise ValidationError(self.error_messages['invalid'])
 82
 83
 84class IDLicensePlatePrefixSelect(Select):
 85    """
 86    A Select widget that uses a list of vehicle license plate prefix code
 87    of Indonesia as its choices.
 88
 89    http://id.wikipedia.org/wiki/Tanda_Nomor_Kendaraan_Bermotor
 90    """
 91
 92    def __init__(self, attrs=None):
 93        from id_choices import LICENSE_PLATE_PREFIX_CHOICES
 94        super(IDLicensePlatePrefixSelect, self).__init__(attrs,
 95            choices=LICENSE_PLATE_PREFIX_CHOICES)
 96
 97
 98class IDLicensePlateField(Field):
 99    """
100    An Indonesian vehicle license plate field.
101
102    http://id.wikipedia.org/wiki/Tanda_Nomor_Kendaraan_Bermotor
103
104    Plus: "B 12345 12"
105    """
106    default_error_messages = {
107        'invalid': _('Enter a valid vehicle license plate number'),
108    }
109
110    def clean(self, value):
111        super(IDLicensePlateField, self).clean(value)
112        if value in EMPTY_VALUES:
113            return u''
114
115        plate_number = re.sub(r'\s+', ' ',
116            smart_unicode(value.strip())).upper()
117
118        matches = plate_re.search(plate_number)
119        if matches is None:
120            raise ValidationError(self.error_messages['invalid'])
121
122        # Make sure prefix is in the list of known codes.
123        from id_choices import LICENSE_PLATE_PREFIX_CHOICES
124        prefix = matches.group('prefix')
125        if prefix not in [choice[0] for choice in LICENSE_PLATE_PREFIX_CHOICES]:
126            raise ValidationError(self.error_messages['invalid'])
127
128        # Only Jakarta (prefix B) can have 3 letter suffix.
129        suffix = matches.group('suffix')
130        if suffix is not None and len(suffix) == 3 and prefix != 'B':
131            raise ValidationError(self.error_messages['invalid'])
132
133        # RI plates don't have suffix.
134        if prefix == 'RI' and suffix is not None and suffix != '':
135            raise ValidationError(self.error_messages['invalid'])
136
137        # Number can't be zero.
138        number = matches.group('number')
139        if number == '0':
140            raise ValidationError(self.error_messages['invalid'])
141
142        # CD, CC and B 12345 12
143        if len(number) == 5 or prefix in ('CD', 'CC'):
144            # suffix must be numeric and non-empty
145            if re.match(r'^\d+$', suffix) is None:
146                raise ValidationError(self.error_messages['invalid'])
147
148            # Known codes range is 12-124
149            if prefix in ('CD', 'CC') and not (12 <= int(number) <= 124):
150                raise ValidationError(self.error_messages['invalid'])
151            if len(number) == 5 and not (12 <= int(suffix) <= 124):
152                raise ValidationError(self.error_messages['invalid'])
153        else:
154            # suffix must be non-numeric
155            if suffix is not None and re.match(r'^[A-Z]{,3}$', suffix) is None:
156                raise ValidationError(self.error_messages['invalid'])
157
158        return plate_number
159
160
161class IDNationalIdentityNumberField(Field):
162    """
163    An Indonesian national identity number (NIK/KTP#) field.
164
165    http://id.wikipedia.org/wiki/Nomor_Induk_Kependudukan
166
167    xx.xxxx.ddmmyy.xxxx - 16 digits (excl. dots)
168    """
169    default_error_messages = {
170        'invalid': _('Enter a valid NIK/KTP number'),
171    }
172
173    def clean(self, value):
174        super(IDNationalIdentityNumberField, self).clean(value)
175        if value in EMPTY_VALUES:
176            return u''
177
178        value = re.sub(r'[\s.]', '', smart_unicode(value))
179
180        if not nik_re.search(value):
181            raise ValidationError(self.error_messages['invalid'])
182
183        if int(value) == 0:
184            raise ValidationError(self.error_messages['invalid'])
185
186        def valid_nik_date(year, month, day):
187            try:
188                t1 = (int(year), int(month), int(day), 0, 0, 0, 0, 0, -1)
189                d = time.mktime(t1)
190                t2 = time.localtime(d)
191                if t1[:3] != t2[:3]:
192                    return False
193                else:
194                    return True
195            except (OverflowError, ValueError):
196                return False
197
198        year = int(value[10:12])
199        month = int(value[8:10])
200        day = int(value[6:8])
201        current_year = time.localtime().tm_year
202        if year < int(str(current_year)[-2:]):
203            if not valid_nik_date(2000 + int(year), month, day):
204                raise ValidationError(self.error_messages['invalid'])
205        elif not valid_nik_date(1900 + int(year), month, day):
206            raise ValidationError(self.error_messages['invalid'])
207
208        if value[:6] == '000000' or value[12:] == '0000':
209            raise ValidationError(self.error_messages['invalid'])
210
211        return '%s.%s.%s.%s' % (value[:2], value[2:6], value[6:12], value[12:])