Skip to content

Commit

Permalink
Add local flavor for Ukraine. (#273)
Browse files Browse the repository at this point in the history
  • Loading branch information
illia-v authored and benkonrath committed Feb 7, 2017
1 parent 0f4aa82 commit ecbcff4
Show file tree
Hide file tree
Showing 12 changed files with 271 additions and 1 deletion.
1 change: 1 addition & 0 deletions docs/authors.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Authors
* Grzes Furga
* Honza Král
* Horst Gutmann
* Illia Volochii
* Ivan Fisun
* Jaap Roes
* Jacob Kaplan-Moss
Expand Down
3 changes: 2 additions & 1 deletion docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ Changelog

New flavors:

- None
- Added local flavor for Ukraine
(`gh-273 <https://github.com/django/django-localflavor/pull/273>`_)

New fields for existing flavors:

Expand Down
19 changes: 19 additions & 0 deletions docs/localflavor/ua.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Ukraine (``ua``)
================

Forms
-----

.. automodule:: localflavor.ua.forms
:members:

Models
------

.. automodule:: localflavor.ua.models
:members:

Data
----

.. autodata:: localflavor.ua.ua_regions.UA_REGION_CHOICES
Empty file added localflavor/ua/__init__.py
Empty file.
64 changes: 64 additions & 0 deletions localflavor/ua/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from django.forms.fields import RegexField, Select
from django.utils.translation import ugettext_lazy as _

from .ua_regions import UA_REGION_CHOICES


class UARegionSelect(Select):
"""
A Select widget that uses a list of Ukrainian regions as its choices.
.. versionadded:: 1.5
"""

def __init__(self, *args, **kwargs):
kwargs['choices'] = UA_REGION_CHOICES
super(UARegionSelect, self).__init__(*args, **kwargs)


class UAVatNumberField(RegexField):
"""
A form field that validates input as a Ukrainian analog of a VAT number.
Valid format is XXXXXXXXXX.
Whitespace around a VAT number is accepted and automatically trimmed.
.. versionadded:: 1.5
"""

default_error_messages = {
'invalid': _('Enter a valid VAT number.'),
}

def __init__(self, *args, **kwargs):
kwargs['max_length'] = kwargs['min_length'] = 10
super(UAVatNumberField, self).__init__(r'^\d{10}$', *args, **kwargs)

def to_python(self, value):
value = super(UAVatNumberField, self).to_python(value)
return value.strip()


class UAPostalCodeField(RegexField):
"""
A form field that validates input as a Ukrainian postal code.
Valid format is XXXXX. Note: first two numbers cannot be '00'.
Whitespace around a postal code is accepted and automatically trimmed.
.. versionadded:: 1.5
"""

default_error_messages = {
'invalid': _('Enter a valid postal code.'),
}

def __init__(self, *args, **kwargs):
kwargs['max_length'] = kwargs['min_length'] = 5
super(UAPostalCodeField, self).__init__(r'^(?!00)\d{5}$', *args, **kwargs)

def to_python(self, value):
value = super(UAPostalCodeField, self).to_python(value)
return value.strip()
64 changes: 64 additions & 0 deletions localflavor/ua/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from django.core.validators import RegexValidator
from django.db.models import CharField
from django.utils.translation import ugettext_lazy as _

from .ua_regions import UA_REGION_CHOICES


class UARegionField(CharField):
"""
A model field which stores a Ukrainian region.
This field is represented by forms as
a :class:`~localflavor.ua.forms.UARegionSelect` field.
.. versionadded:: 1.5
"""

description = _('Ukrainian region')

def __init__(self, *args, **kwargs):
kwargs['choices'] = UA_REGION_CHOICES
kwargs['max_length'] = 5
super(UARegionField, self).__init__(*args, **kwargs)

def deconstruct(self):
name, path, args, kwargs = super(UARegionField, self).deconstruct()
del kwargs['choices']
return name, path, args, kwargs


class UAVatNumberField(CharField):
"""
A model field which stores a Ukrainian analog of a VAT number.
This field is represented by forms as
a :class:`~localflavor.ua.forms.UAVatNumberField` field.
.. versionadded:: 1.5
"""

description = _('Ukrainian VAT number')
validators = [RegexValidator(r'^\d{10}$', 'Enter a valid VAT number.')]

def __init__(self, *args, **kwargs):
kwargs['max_length'] = 10
super(UAVatNumberField, self).__init__(*args, **kwargs)


class UAPostalCodeField(CharField):
"""
A model field which stores a Ukrainian postal code.
This field is represented by forms as
a :class:`~localflavor.ua.forms.UAPostalCodeField` field.
.. versionadded:: 1.5
"""

description = _('Ukrainian postal code')
validators = [RegexValidator(r'^(?!00)\d{5}$', 'Enter a valid postal code.')]

def __init__(self, *args, **kwargs):
kwargs['max_length'] = 5
super(UAPostalCodeField, self).__init__(*args, **kwargs)
33 changes: 33 additions & 0 deletions localflavor/ua/ua_regions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from django.utils.translation import ugettext_lazy as _

#: 24 oblasts, Avtonomna Respublika Krym and 2 cities with special status
# Codes were gotten from ISO 3166-2:UA
UA_REGION_CHOICES = (
('UA-71', _('Cherkasy Oblast')),
('UA-74', _('Chernihiv Oblast')),
('UA-77', _('Chernivtsi Oblast')),
('UA-12', _('Dnipropetrovsk Oblast')),
('UA-14', _('Donetsk Oblast')),
('UA-26', _('Ivano-Frankivsk Oblast')),
('UA-63', _('Kharkiv Oblast')),
('UA-65', _('Kherson Oblast')),
('UA-68', _('Khmelnytskyi Oblast')),
('UA-35', _('Kirovohrad Oblast')),
('UA-32', _('Kiev Oblast')),
('UA-09', _('Luhansk Oblast')),
('UA-46', _('Lviv Oblast')),
('UA-48', _('Mykolaiv Oblast')),
('UA-51', _('Odessa Oblast')),
('UA-53', _('Poltava Oblast')),
('UA-56', _('Rivne Oblast')),
('UA-59', _('Sumy Oblast')),
('UA-61', _('Ternopil Oblast')),
('UA-05', _('Vinnytsia Oblast')),
('UA-07', _('Volyn Oblast')),
('UA-21', _('Zakarpattia Oblast')),
('UA-23', _('Zaporizhia Oblast')),
('UA-18', _('Zhytomyr Oblast')),
('UA-43', _('Autonomous Republic of Crimea')),
('UA-30', _('Kiev')),
('UA-40', _('Sevastopol'))
)
1 change: 1 addition & 0 deletions tests/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
'tests.test_ec',
'tests.test_mk',
'tests.test_mx',
'tests.test_ua',
'tests.test_us',
'tests.test_pk',
'tests.test_generic',
Expand Down
Empty file added tests/test_ua/__init__.py
Empty file.
10 changes: 10 additions & 0 deletions tests/test_ua/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.forms import ModelForm

from .models import UAFlavorTestModel


class UAFlavorTestForm(ModelForm):

class Meta:
model = UAFlavorTestModel
fields = '__all__'
9 changes: 9 additions & 0 deletions tests/test_ua/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from django.db import models

from localflavor.ua.models import UAPostalCodeField, UARegionField, UAVatNumberField


class UAFlavorTestModel(models.Model):
postal_code = UAPostalCodeField()
region = UARegionField()
vat_number = UAVatNumberField()
68 changes: 68 additions & 0 deletions tests/test_ua/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from django.test import TestCase

from localflavor.ua.forms import UAPostalCodeField, UARegionSelect, UAVatNumberField
from localflavor.ua.ua_regions import UA_REGION_CHOICES
from .forms import UAFlavorTestForm


class UALocalFlavorTests(TestCase):
@classmethod
def setUpTestData(cls):
cls.form = UAFlavorTestForm({
'postal_code': '76000',
'region': 'UA-26',
'vat_number': '0000000000'
})

def test_get_display_methods(self):
flavor = self.form.save()
self.assertEqual(flavor.get_region_display(), 'Ivano-Frankivsk Oblast')

def test_errors(self):
form = UAFlavorTestForm({
'postal_code': False,
'region': False,
'vat_number': False
})
self.assertFalse(form.is_valid())
self.assertEqual(form.errors['postal_code'], ['Enter a valid postal code.'])
self.assertEqual(
form.errors['region'], ['Select a valid choice. False is not one of the available choices.']
)
self.assertEqual(form.errors['vat_number'], ['Enter a valid VAT number.'])

def test_UARegionSelect(self):
out = '<select name="region">\n'
for choice in UA_REGION_CHOICES:
out += '<option value="{}">{}</option>\n'.format(choice[0], choice[1])
out += '</select>'
self.assertHTMLEqual(UARegionSelect().render('region', None), out)

def test_UAVatNumberField(self):
error_format = ['Enter a valid VAT number.']
valid = {
'1111111111': '1111111111',
1234567890: '1234567890',
' 0987654321 ': '0987654321'
}
invalid = {
'abcdefghij': error_format,
'123': ['Ensure this value has at least 10 characters (it has 3).'] + error_format,
'98765432100': ['Ensure this value has at most 10 characters (it has 11).'] + error_format
}
self.assertFieldOutput(UAVatNumberField, valid, invalid)

def test_UAPostalCodeField(self):
error_format = ['Enter a valid postal code.']
valid = {
'76000': '76000',
10101: '10101',
' 09876 ': '09876'
}
invalid = {
'abcde': error_format,
'00000': error_format,
'123': ['Ensure this value has at least 5 characters (it has 3).'] + error_format,
'987654': ['Ensure this value has at most 5 characters (it has 6).'] + error_format
}
self.assertFieldOutput(UAPostalCodeField, valid, invalid)

0 comments on commit ecbcff4

Please sign in to comment.