summaryrefslogtreecommitdiff
path: root/parts/django/django/contrib/localflavor/id/forms.py
diff options
context:
space:
mode:
Diffstat (limited to 'parts/django/django/contrib/localflavor/id/forms.py')
-rw-r--r--parts/django/django/contrib/localflavor/id/forms.py211
1 files changed, 211 insertions, 0 deletions
diff --git a/parts/django/django/contrib/localflavor/id/forms.py b/parts/django/django/contrib/localflavor/id/forms.py
new file mode 100644
index 0000000..834e588
--- /dev/null
+++ b/parts/django/django/contrib/localflavor/id/forms.py
@@ -0,0 +1,211 @@
+"""
+ID-specific Form helpers
+"""
+
+import re
+import time
+
+from django.core.validators import EMPTY_VALUES
+from django.forms import ValidationError
+from django.forms.fields import Field, Select
+from django.utils.translation import ugettext_lazy as _
+from django.utils.encoding import smart_unicode
+
+postcode_re = re.compile(r'^[1-9]\d{4}$')
+phone_re = re.compile(r'^(\+62|0)[2-9]\d{7,10}$')
+plate_re = re.compile(r'^(?P<prefix>[A-Z]{1,2}) ' + \
+ r'(?P<number>\d{1,5})( (?P<suffix>([A-Z]{1,3}|[1-9][0-9]{,2})))?$')
+nik_re = re.compile(r'^\d{16}$')
+
+
+class IDPostCodeField(Field):
+ """
+ An Indonesian post code field.
+
+ http://id.wikipedia.org/wiki/Kode_pos
+ """
+ default_error_messages = {
+ 'invalid': _('Enter a valid post code'),
+ }
+
+ def clean(self, value):
+ super(IDPostCodeField, self).clean(value)
+ if value in EMPTY_VALUES:
+ return u''
+
+ value = value.strip()
+ if not postcode_re.search(value):
+ raise ValidationError(self.error_messages['invalid'])
+
+ if int(value) < 10110:
+ raise ValidationError(self.error_messages['invalid'])
+
+ # 1xxx0
+ if value[0] == '1' and value[4] != '0':
+ raise ValidationError(self.error_messages['invalid'])
+
+ return u'%s' % (value, )
+
+
+class IDProvinceSelect(Select):
+ """
+ A Select widget that uses a list of provinces of Indonesia as its
+ choices.
+ """
+
+ def __init__(self, attrs=None):
+ from id_choices import PROVINCE_CHOICES
+ super(IDProvinceSelect, self).__init__(attrs, choices=PROVINCE_CHOICES)
+
+
+class IDPhoneNumberField(Field):
+ """
+ An Indonesian telephone number field.
+
+ http://id.wikipedia.org/wiki/Daftar_kode_telepon_di_Indonesia
+ """
+ default_error_messages = {
+ 'invalid': _('Enter a valid phone number'),
+ }
+
+ def clean(self, value):
+ super(IDPhoneNumberField, self).clean(value)
+ if value in EMPTY_VALUES:
+ return u''
+
+ phone_number = re.sub(r'[\-\s\(\)]', '', smart_unicode(value))
+
+ if phone_re.search(phone_number):
+ return smart_unicode(value)
+
+ raise ValidationError(self.error_messages['invalid'])
+
+
+class IDLicensePlatePrefixSelect(Select):
+ """
+ A Select widget that uses a list of vehicle license plate prefix code
+ of Indonesia as its choices.
+
+ http://id.wikipedia.org/wiki/Tanda_Nomor_Kendaraan_Bermotor
+ """
+
+ def __init__(self, attrs=None):
+ from id_choices import LICENSE_PLATE_PREFIX_CHOICES
+ super(IDLicensePlatePrefixSelect, self).__init__(attrs,
+ choices=LICENSE_PLATE_PREFIX_CHOICES)
+
+
+class IDLicensePlateField(Field):
+ """
+ An Indonesian vehicle license plate field.
+
+ http://id.wikipedia.org/wiki/Tanda_Nomor_Kendaraan_Bermotor
+
+ Plus: "B 12345 12"
+ """
+ default_error_messages = {
+ 'invalid': _('Enter a valid vehicle license plate number'),
+ }
+
+ def clean(self, value):
+ super(IDLicensePlateField, self).clean(value)
+ if value in EMPTY_VALUES:
+ return u''
+
+ plate_number = re.sub(r'\s+', ' ',
+ smart_unicode(value.strip())).upper()
+
+ matches = plate_re.search(plate_number)
+ if matches is None:
+ raise ValidationError(self.error_messages['invalid'])
+
+ # Make sure prefix is in the list of known codes.
+ from id_choices import LICENSE_PLATE_PREFIX_CHOICES
+ prefix = matches.group('prefix')
+ if prefix not in [choice[0] for choice in LICENSE_PLATE_PREFIX_CHOICES]:
+ raise ValidationError(self.error_messages['invalid'])
+
+ # Only Jakarta (prefix B) can have 3 letter suffix.
+ suffix = matches.group('suffix')
+ if suffix is not None and len(suffix) == 3 and prefix != 'B':
+ raise ValidationError(self.error_messages['invalid'])
+
+ # RI plates don't have suffix.
+ if prefix == 'RI' and suffix is not None and suffix != '':
+ raise ValidationError(self.error_messages['invalid'])
+
+ # Number can't be zero.
+ number = matches.group('number')
+ if number == '0':
+ raise ValidationError(self.error_messages['invalid'])
+
+ # CD, CC and B 12345 12
+ if len(number) == 5 or prefix in ('CD', 'CC'):
+ # suffix must be numeric and non-empty
+ if re.match(r'^\d+$', suffix) is None:
+ raise ValidationError(self.error_messages['invalid'])
+
+ # Known codes range is 12-124
+ if prefix in ('CD', 'CC') and not (12 <= int(number) <= 124):
+ raise ValidationError(self.error_messages['invalid'])
+ if len(number) == 5 and not (12 <= int(suffix) <= 124):
+ raise ValidationError(self.error_messages['invalid'])
+ else:
+ # suffix must be non-numeric
+ if suffix is not None and re.match(r'^[A-Z]{,3}$', suffix) is None:
+ raise ValidationError(self.error_messages['invalid'])
+
+ return plate_number
+
+
+class IDNationalIdentityNumberField(Field):
+ """
+ An Indonesian national identity number (NIK/KTP#) field.
+
+ http://id.wikipedia.org/wiki/Nomor_Induk_Kependudukan
+
+ xx.xxxx.ddmmyy.xxxx - 16 digits (excl. dots)
+ """
+ default_error_messages = {
+ 'invalid': _('Enter a valid NIK/KTP number'),
+ }
+
+ def clean(self, value):
+ super(IDNationalIdentityNumberField, self).clean(value)
+ if value in EMPTY_VALUES:
+ return u''
+
+ value = re.sub(r'[\s.]', '', smart_unicode(value))
+
+ if not nik_re.search(value):
+ raise ValidationError(self.error_messages['invalid'])
+
+ if int(value) == 0:
+ raise ValidationError(self.error_messages['invalid'])
+
+ def valid_nik_date(year, month, day):
+ try:
+ t1 = (int(year), int(month), int(day), 0, 0, 0, 0, 0, -1)
+ d = time.mktime(t1)
+ t2 = time.localtime(d)
+ if t1[:3] != t2[:3]:
+ return False
+ else:
+ return True
+ except (OverflowError, ValueError):
+ return False
+
+ year = int(value[10:12])
+ month = int(value[8:10])
+ day = int(value[6:8])
+ current_year = time.localtime().tm_year
+ if year < int(str(current_year)[-2:]):
+ if not valid_nik_date(2000 + int(year), month, day):
+ raise ValidationError(self.error_messages['invalid'])
+ elif not valid_nik_date(1900 + int(year), month, day):
+ raise ValidationError(self.error_messages['invalid'])
+
+ if value[:6] == '000000' or value[12:] == '0000':
+ raise ValidationError(self.error_messages['invalid'])
+
+ return '%s.%s.%s.%s' % (value[:2], value[2:6], value[6:12], value[12:])