~ubuntu-branches/ubuntu/saucy/python-django/saucy-updates

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
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:])