From 6c15e9369be3339ee300d7eee626b2deebf13206 Mon Sep 17 00:00:00 2001 From: chmodx Date: Sun, 13 Sep 2020 10:14:06 +0300 Subject: [PATCH 01/21] chore: initialize KE localflavor --- localflavor/ke/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 localflavor/ke/__init__.py diff --git a/localflavor/ke/__init__.py b/localflavor/ke/__init__.py new file mode 100644 index 000000000..e69de29bb From 77ce9d113d224fcd9b07d238288ce9f71d4809bc Mon Sep 17 00:00:00 2001 From: chmodx Date: Sun, 13 Sep 2020 10:14:23 +0300 Subject: [PATCH 02/21] chore: initialize KE county choices --- localflavor/ke/ke_counties.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 localflavor/ke/ke_counties.py diff --git a/localflavor/ke/ke_counties.py b/localflavor/ke/ke_counties.py new file mode 100644 index 000000000..e69de29bb From 3ec98ec403237617134819e7fcbf4aa5a9eeef56 Mon Sep 17 00:00:00 2001 From: chmodx Date: Sun, 13 Sep 2020 10:46:49 +0300 Subject: [PATCH 03/21] chore: define county choices --- localflavor/ke/ke_counties.py | 55 +++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/localflavor/ke/ke_counties.py b/localflavor/ke/ke_counties.py index e69de29bb..e94e1ea25 100644 --- a/localflavor/ke/ke_counties.py +++ b/localflavor/ke/ke_counties.py @@ -0,0 +1,55 @@ +""" +Kenya Counties Data +""" + +from django.utils.translation import gettext_lazy as _ + +COUNTY_CHOICES= ( + ('MOMBASA', _('MOMBASA')), + ('KWALE', _('KWALE')), + ('KILIFI', _('KILIFI')), + ('TANA RIVER', _('TANA RIVER')), + ('LAMU', _('LAMU')), + ('TAITA-TAVETA', _('TAITA-TAVETA')), + ('GARISSA', _('GARISSA')), + ('WAJIR', _('WAJIR')), + ('MANDERA', _('MANDERA')), + ('MARSABIT', _('MARSABIT')), + ('ISIOLO', _('ISIOLO')), + ('MERU', _('MERU')), + ('THARAKA-NITHI', _('THARAKA-NITHI')), + ('EMBU', _('EMBU')), + ('KITUI', _('KITUI')), + ('MACHAKOS', _('MACHAKOS')), + ('MAKUENI', _('MAKUENI')), + ('NYANDARUA', _('NYANDARUA')), + ('NYERI', _('NYERI')), + ('KIRINYAGA', _('KIRINYAGA')), + ('MURANGA', _('MURANGA')), + ('KIAMBU', _('KIAMBU')), + ('TURKANA', _('TURKANA')), + ('WEST POKOT', _('WEST POKOT')), + ('SAMBURU', _('SAMBURU')), + ('TRANS-NZOIA', _('TRANS-NZOIA')), + ('UASIN GISHU', _('UASIN GISHU')), + ('ELGEYO-MARAKWET', _('ELGEYO-MARAKWET')), + ('NANDI', _('NANDI')), + ('BARINGO', _('BARINGO')), + ('LAIKIPIA', _('LAIKIPIA')), + ('NAKURU', _('NAKURU')), + ('NAROK', _('NAROK')), + ('KAJIADO', _('KAJIADO')), + ('KERICHO', _('KERICHO')), + ('BOMET', _('BOMET')), + ('KAKAMEGA', _('KAKAMEGA')), + ('VIHIGA', _('VIHIGA')), + ('BUNGOMA', _('BUNGOMA')), + ('BUSIA', _('BUSIA')), + ('SIAYA', _('SIAYA')), + ('KISUMU', _('KISUMU')), + ('HOMA BAY', _('HOMA BAY')), + ('MIGORI', _('MIGORI')), + ('KISII', _('KISII')), + ('NYAMIRA', _('NYAMIRA')), + ('NAIROBI', _('NAIROBI')), +) From 1f1ebb2a9d441a465dc57c05d964bbfed7e6d7d5 Mon Sep 17 00:00:00 2001 From: chmodx Date: Sun, 29 Nov 2020 21:56:10 +0300 Subject: [PATCH 04/21] chore: map out TODO forms and select widgets --- localflavor/ke/forms.py | 86 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 localflavor/ke/forms.py diff --git a/localflavor/ke/forms.py b/localflavor/ke/forms.py new file mode 100644 index 000000000..b8757c1cc --- /dev/null +++ b/localflavor/ke/forms.py @@ -0,0 +1,86 @@ +"""Kenya-specific Form Helpers""" + +from django.forms import ValidationError +from django.forms.fields import RegexField, Select +from django.utils.translation import gettext_lazy as _ + +from .ke_counties import COUNTY_CHOICES + + +class KEPostalCodeField(RegexField): + """ + A form field that validates its input as a Kenyan Postal Code. + + """ + default_error_messages = { + "invalid":_("Enter a valid Postal code in the format XXXXX") + } + + def __init__(self, **kwargs) -> None: + """ + TODO + """ + super().__init__(r'()', **kwargs) + + +class KEKraPinNumber(RegexField): + """ + TODO + + Kenya Revenue Authority PIN Number + + Validates 2 different formats: + + POXXXXXXXX - Company/Institutions + + AXXXXXXXXX - Individuals + """ + ... + + +class KEIDNumber(RegexField): + """ + TODO + + Kenya National ID Number + + """ + ... + +class KEPassportNumber(RegexField): + """ + TODO + + Kenya Passport Number + """ + ... + + +class KENSSFNumber(RegexField): + """ + TODO + + Kenya National Social Security Fund + """ + ... + + +class KENHIFNumber(RegexField): + """ + TODO + + Kenya National Hospital Insurance Fund + """ + ... + +class KECompanyRegNumber(RegexField): + """ + Kenya Companies Reg. Number + """ + +class KECountySelect(Select): + """ + A Select widget listing Kenyan Counties as the choices + """ + def __init__(self, attrs=None) -> None: + super().__init__(attrs, choices=COUNTY_CHOICES) \ No newline at end of file From c278b2a40cb7ee9e35fa8e97c03632be58a5cb96 Mon Sep 17 00:00:00 2001 From: chmodx Date: Sun, 29 Nov 2020 22:08:46 +0300 Subject: [PATCH 05/21] chore: update KE form fields --- localflavor/ke/forms.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/localflavor/ke/forms.py b/localflavor/ke/forms.py index b8757c1cc..16cd5c47d 100644 --- a/localflavor/ke/forms.py +++ b/localflavor/ke/forms.py @@ -12,20 +12,21 @@ class KEPostalCodeField(RegexField): A form field that validates its input as a Kenyan Postal Code. """ + default_error_messages = { - "invalid":_("Enter a valid Postal code in the format XXXXX") + "invalid": _("Enter a valid Postal code in the format XXXXX") } def __init__(self, **kwargs) -> None: """ TODO """ - super().__init__(r'()', **kwargs) + super().__init__(r"()", **kwargs) class KEKraPinNumber(RegexField): """ - TODO + TODO Kenya Revenue Authority PIN Number @@ -35,6 +36,7 @@ class KEKraPinNumber(RegexField): AXXXXXXXXX - Individuals """ + ... @@ -45,14 +47,17 @@ class KEIDNumber(RegexField): Kenya National ID Number """ + ... + class KEPassportNumber(RegexField): """ TODO Kenya Passport Number """ + ... @@ -62,6 +67,7 @@ class KENSSFNumber(RegexField): Kenya National Social Security Fund """ + ... @@ -69,18 +75,22 @@ class KENHIFNumber(RegexField): """ TODO - Kenya National Hospital Insurance Fund + Kenya National Hospital Insurance Fund """ + ... + class KECompanyRegNumber(RegexField): """ Kenya Companies Reg. Number """ + class KECountySelect(Select): """ A Select widget listing Kenyan Counties as the choices """ + def __init__(self, attrs=None) -> None: super().__init__(attrs, choices=COUNTY_CHOICES) \ No newline at end of file From 4fa5cef3817f0a81b05c8f4b2bd3ca1692fa76e4 Mon Sep 17 00:00:00 2001 From: chmodx Date: Sun, 29 Nov 2020 22:09:24 +0300 Subject: [PATCH 06/21] chore: format KE Counties Info --- localflavor/ke/ke_counties.py | 96 +++++++++++++++++------------------ 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/localflavor/ke/ke_counties.py b/localflavor/ke/ke_counties.py index e94e1ea25..bfe326302 100644 --- a/localflavor/ke/ke_counties.py +++ b/localflavor/ke/ke_counties.py @@ -4,52 +4,52 @@ from django.utils.translation import gettext_lazy as _ -COUNTY_CHOICES= ( - ('MOMBASA', _('MOMBASA')), - ('KWALE', _('KWALE')), - ('KILIFI', _('KILIFI')), - ('TANA RIVER', _('TANA RIVER')), - ('LAMU', _('LAMU')), - ('TAITA-TAVETA', _('TAITA-TAVETA')), - ('GARISSA', _('GARISSA')), - ('WAJIR', _('WAJIR')), - ('MANDERA', _('MANDERA')), - ('MARSABIT', _('MARSABIT')), - ('ISIOLO', _('ISIOLO')), - ('MERU', _('MERU')), - ('THARAKA-NITHI', _('THARAKA-NITHI')), - ('EMBU', _('EMBU')), - ('KITUI', _('KITUI')), - ('MACHAKOS', _('MACHAKOS')), - ('MAKUENI', _('MAKUENI')), - ('NYANDARUA', _('NYANDARUA')), - ('NYERI', _('NYERI')), - ('KIRINYAGA', _('KIRINYAGA')), - ('MURANGA', _('MURANGA')), - ('KIAMBU', _('KIAMBU')), - ('TURKANA', _('TURKANA')), - ('WEST POKOT', _('WEST POKOT')), - ('SAMBURU', _('SAMBURU')), - ('TRANS-NZOIA', _('TRANS-NZOIA')), - ('UASIN GISHU', _('UASIN GISHU')), - ('ELGEYO-MARAKWET', _('ELGEYO-MARAKWET')), - ('NANDI', _('NANDI')), - ('BARINGO', _('BARINGO')), - ('LAIKIPIA', _('LAIKIPIA')), - ('NAKURU', _('NAKURU')), - ('NAROK', _('NAROK')), - ('KAJIADO', _('KAJIADO')), - ('KERICHO', _('KERICHO')), - ('BOMET', _('BOMET')), - ('KAKAMEGA', _('KAKAMEGA')), - ('VIHIGA', _('VIHIGA')), - ('BUNGOMA', _('BUNGOMA')), - ('BUSIA', _('BUSIA')), - ('SIAYA', _('SIAYA')), - ('KISUMU', _('KISUMU')), - ('HOMA BAY', _('HOMA BAY')), - ('MIGORI', _('MIGORI')), - ('KISII', _('KISII')), - ('NYAMIRA', _('NYAMIRA')), - ('NAIROBI', _('NAIROBI')), +COUNTY_CHOICES = ( + ("MOMBASA", _("MOMBASA")), + ("KWALE", _("KWALE")), + ("KILIFI", _("KILIFI")), + ("TANA RIVER", _("TANA RIVER")), + ("LAMU", _("LAMU")), + ("TAITA-TAVETA", _("TAITA-TAVETA")), + ("GARISSA", _("GARISSA")), + ("WAJIR", _("WAJIR")), + ("MANDERA", _("MANDERA")), + ("MARSABIT", _("MARSABIT")), + ("ISIOLO", _("ISIOLO")), + ("MERU", _("MERU")), + ("THARAKA-NITHI", _("THARAKA-NITHI")), + ("EMBU", _("EMBU")), + ("KITUI", _("KITUI")), + ("MACHAKOS", _("MACHAKOS")), + ("MAKUENI", _("MAKUENI")), + ("NYANDARUA", _("NYANDARUA")), + ("NYERI", _("NYERI")), + ("KIRINYAGA", _("KIRINYAGA")), + ("MURANGA", _("MURANGA")), + ("KIAMBU", _("KIAMBU")), + ("TURKANA", _("TURKANA")), + ("WEST POKOT", _("WEST POKOT")), + ("SAMBURU", _("SAMBURU")), + ("TRANS-NZOIA", _("TRANS-NZOIA")), + ("UASIN GISHU", _("UASIN GISHU")), + ("ELGEYO-MARAKWET", _("ELGEYO-MARAKWET")), + ("NANDI", _("NANDI")), + ("BARINGO", _("BARINGO")), + ("LAIKIPIA", _("LAIKIPIA")), + ("NAKURU", _("NAKURU")), + ("NAROK", _("NAROK")), + ("KAJIADO", _("KAJIADO")), + ("KERICHO", _("KERICHO")), + ("BOMET", _("BOMET")), + ("KAKAMEGA", _("KAKAMEGA")), + ("VIHIGA", _("VIHIGA")), + ("BUNGOMA", _("BUNGOMA")), + ("BUSIA", _("BUSIA")), + ("SIAYA", _("SIAYA")), + ("KISUMU", _("KISUMU")), + ("HOMA BAY", _("HOMA BAY")), + ("MIGORI", _("MIGORI")), + ("KISII", _("KISII")), + ("NYAMIRA", _("NYAMIRA")), + ("NAIROBI", _("NAIROBI")), ) From b1240506cb8be4efd4b113852426eded16a52657 Mon Sep 17 00:00:00 2001 From: chmodx Date: Sun, 29 Nov 2020 22:09:42 +0300 Subject: [PATCH 07/21] chore: add KE Models --- localflavor/ke/models.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 localflavor/ke/models.py diff --git a/localflavor/ke/models.py b/localflavor/ke/models.py new file mode 100644 index 000000000..2252822c0 --- /dev/null +++ b/localflavor/ke/models.py @@ -0,0 +1,33 @@ +from typing import Any +from django.db.models import CharField +from django.utils.translation import gettext_lazy as _ + +from .forms import ( + KECompanyRegNumber as KECompanyRegNumberFormField, + KECountySelect, + KEIDNumber, + KEKraPinNumber, + KENHIFNumber, + KENSSFNumber, + KEPassportNumber, + KEPostalCodeField as KEPostalCodeFormField, +) + + +class KEPostalCodeField(CharField): + """ + A model field that stores the Kenyan Postal Codes + """ + description = _("Kenya Postal Code") + + def __init__(self, *args, **kwargs) -> None: + kwargs.update(max_length=8) + super().__init__(*args, **kwargs) + + def formfield(self, **kwargs) -> Any: + defaults = {"form_class": KEPostalCodeFormField} + defaults.update(kwargs) + return super().formfield(**defaults) + + + \ No newline at end of file From dfb0a47b609a548b6b28ec7087d45782ece75ebe Mon Sep 17 00:00:00 2001 From: chmodx Date: Sun, 29 Nov 2020 22:15:07 +0300 Subject: [PATCH 08/21] chore: add Mpesa Paybill --- localflavor/ke/forms.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/localflavor/ke/forms.py b/localflavor/ke/forms.py index 16cd5c47d..fd6bca1b6 100644 --- a/localflavor/ke/forms.py +++ b/localflavor/ke/forms.py @@ -85,7 +85,13 @@ class KECompanyRegNumber(RegexField): """ Kenya Companies Reg. Number """ + ... +class KEPayBillNumber(RegexField): + """ + MPESA PayBill + """ + ... class KECountySelect(Select): """ From 8c25e12ed97977df97e0504c42eb646cb2d9db47 Mon Sep 17 00:00:00 2001 From: chmodx Date: Thu, 7 Jan 2021 09:20:57 +0300 Subject: [PATCH 09/21] feat: add regex to validate postal code and ID number --- localflavor/ke/forms.py | 53 +++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/localflavor/ke/forms.py b/localflavor/ke/forms.py index fd6bca1b6..c1518d2d9 100644 --- a/localflavor/ke/forms.py +++ b/localflavor/ke/forms.py @@ -1,11 +1,15 @@ """Kenya-specific Form Helpers""" +import re + from django.forms import ValidationError from django.forms.fields import RegexField, Select from django.utils.translation import gettext_lazy as _ from .ke_counties import COUNTY_CHOICES +ke_id_re = re.compile(r"^\d{7}(?:\d{1})?$") +ke_po_box_re = re.compile(r"\A\d{5,5}\Z") class KEPostalCodeField(RegexField): """ @@ -14,21 +18,29 @@ class KEPostalCodeField(RegexField): """ default_error_messages = { - "invalid": _("Enter a valid Postal code in the format XXXXX") + "invalid": _("Enter a valid Kenyan Postal code in the format 12345") } - def __init__(self, **kwargs) -> None: - """ - TODO - """ - super().__init__(r"()", **kwargs) + def clean(self, value): + value = super().clean(value) + if value in self.empty_values: + return self.empty_value + + # Strip out spaces and dashes + value = value.replace(" ", "").replace("-", "") + match = re.match(ke_po_box_re, value) + if not match: + raise ValidationError(self.error_messages.get("invalid")) + return value + + class KEKraPinNumber(RegexField): """ TODO - Kenya Revenue Authority PIN Number + A form field that validates input as a Kenya Revenue Authority PIN Number Validates 2 different formats: @@ -46,9 +58,26 @@ class KEIDNumber(RegexField): Kenya National ID Number + 8 Digits + """ - ... + default_error_messages = { + "invalid": _("Enter a valid Kenyan ID Number"), + } + + def clean(self, value): + value = super().clean(value) + if value in self.empty_values: + return self.empty_value + + # Strip out spaces and dashes + value = value.replace(" ", "").replace("-", "") + match = re.match(ke_id_re, value) + + if not match: + raise ValidationError(self.error_messages.get("invalid")) + return value class KEPassportNumber(RegexField): @@ -85,18 +114,22 @@ class KECompanyRegNumber(RegexField): """ Kenya Companies Reg. Number """ + ... + class KEPayBillNumber(RegexField): """ - MPESA PayBill + MPESA PayBill """ + ... + class KECountySelect(Select): """ A Select widget listing Kenyan Counties as the choices """ def __init__(self, attrs=None) -> None: - super().__init__(attrs, choices=COUNTY_CHOICES) \ No newline at end of file + super().__init__(attrs, choices=COUNTY_CHOICES) From c83b235d6eccc984496da36417de79f8087fdfc0 Mon Sep 17 00:00:00 2001 From: chmodx Date: Thu, 7 Jan 2021 09:21:21 +0300 Subject: [PATCH 10/21] chore: add deprecation notice --- localflavor/ke/deprecation.py | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 localflavor/ke/deprecation.py diff --git a/localflavor/ke/deprecation.py b/localflavor/ke/deprecation.py new file mode 100644 index 000000000..fb80ff5f3 --- /dev/null +++ b/localflavor/ke/deprecation.py @@ -0,0 +1,2 @@ +class RemovedInLocalflavor30Warning(PendingDeprecationWarning): + ... \ No newline at end of file From 9556f2ab198aaef158bb57df7d5b2b11a0fcaa2f Mon Sep 17 00:00:00 2001 From: chmodx Date: Fri, 8 Jan 2021 13:59:42 +0300 Subject: [PATCH 11/21] chore: update KE forms --- localflavor/ke/forms.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/localflavor/ke/forms.py b/localflavor/ke/forms.py index c1518d2d9..4cbdc2427 100644 --- a/localflavor/ke/forms.py +++ b/localflavor/ke/forms.py @@ -3,7 +3,7 @@ import re from django.forms import ValidationError -from django.forms.fields import RegexField, Select +from django.forms.fields import CharField, RegexField, Select from django.utils.translation import gettext_lazy as _ from .ke_counties import COUNTY_CHOICES @@ -11,7 +11,7 @@ ke_id_re = re.compile(r"^\d{7}(?:\d{1})?$") ke_po_box_re = re.compile(r"\A\d{5,5}\Z") -class KEPostalCodeField(RegexField): +class KEPostalCodeField(CharField): """ A form field that validates its input as a Kenyan Postal Code. @@ -36,7 +36,7 @@ def clean(self, value): -class KEKraPinNumber(RegexField): +class KEKraPinNumberField(CharField): """ TODO @@ -52,7 +52,7 @@ class KEKraPinNumber(RegexField): ... -class KEIDNumber(RegexField): +class KEIDNumberField(CharField): """ TODO @@ -80,7 +80,7 @@ def clean(self, value): return value -class KEPassportNumber(RegexField): +class KEPassportNumberField(RegexField): """ TODO @@ -90,7 +90,7 @@ class KEPassportNumber(RegexField): ... -class KENSSFNumber(RegexField): +class KENSSFNumberField(RegexField): """ TODO @@ -100,7 +100,7 @@ class KENSSFNumber(RegexField): ... -class KENHIFNumber(RegexField): +class KENHIFNumberField(RegexField): """ TODO @@ -110,7 +110,7 @@ class KENHIFNumber(RegexField): ... -class KECompanyRegNumber(RegexField): +class KECompanyRegNumberField(RegexField): """ Kenya Companies Reg. Number """ @@ -126,7 +126,7 @@ class KEPayBillNumber(RegexField): ... -class KECountySelect(Select): +class KECountySelectField(Select): """ A Select widget listing Kenyan Counties as the choices """ From cfa3713faf40e23c00f10834b9b2de8a8b72239a Mon Sep 17 00:00:00 2001 From: chmodx Date: Fri, 8 Jan 2021 13:59:59 +0300 Subject: [PATCH 12/21] chore: update ke model --- localflavor/ke/models.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/localflavor/ke/models.py b/localflavor/ke/models.py index 2252822c0..516776269 100644 --- a/localflavor/ke/models.py +++ b/localflavor/ke/models.py @@ -5,11 +5,11 @@ from .forms import ( KECompanyRegNumber as KECompanyRegNumberFormField, KECountySelect, - KEIDNumber, - KEKraPinNumber, - KENHIFNumber, - KENSSFNumber, - KEPassportNumber, + KEIDNumberField, + KEKraPinNumberField, + KENHIFNumberField, + KENSSFNumberField, + KEPassportNumberField, KEPostalCodeField as KEPostalCodeFormField, ) From c15ebd9d45a00d79e7debde3177d69e07d8954a2 Mon Sep 17 00:00:00 2001 From: chmod77 Date: Sun, 26 Mar 2023 09:04:58 +0300 Subject: [PATCH 13/21] feat: update KE KRA PIN validators --- localflavor/ke/forms.py | 59 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/localflavor/ke/forms.py b/localflavor/ke/forms.py index 4cbdc2427..f346a5b3c 100644 --- a/localflavor/ke/forms.py +++ b/localflavor/ke/forms.py @@ -11,6 +11,7 @@ ke_id_re = re.compile(r"^\d{7}(?:\d{1})?$") ke_po_box_re = re.compile(r"\A\d{5,5}\Z") + class KEPostalCodeField(CharField): """ A form field that validates its input as a Kenyan Postal Code. @@ -21,7 +22,18 @@ class KEPostalCodeField(CharField): "invalid": _("Enter a valid Kenyan Postal code in the format 12345") } - def clean(self, value): + def clean(self, value: str): + """Validates KE Postal Code + + Args: + value (_type_): _description_ + + Raises: + ValidationError: _description_ + + Returns: + _type_: _description_ + """ value = super().clean(value) if value in self.empty_values: return self.empty_value @@ -34,22 +46,55 @@ def clean(self, value): return value - - -class KEKraPinNumberField(CharField): +class KEKRAPINField(CharField): """ TODO - A form field that validates input as a Kenya Revenue Authority PIN Number + A form field that validates input as a Kenya Revenue Authority PIN + (Personal Identification Number) Number. + + A Kenyan KRA (Kenya Revenue Authority) PIN (Personal Identification Number) + + is typically 11 characters long, consisting of the letter 'A' or 'P' followed + + by 9 digits and ending with a letter (e.g., A123456789B or P987654321C). Validates 2 different formats: - POXXXXXXXX - Company/Institutions + POXXXXXXXX - Company/Institution AXXXXXXXXX - Individuals """ - ... + default_error_messages = { + "invalid": _( + "Enter a valid Kenyan KRA PIN Number in the format A123456789B or P987654321C" + ), + } + + def clean(self, value): + """Runs the validation checks + + Args: + value (_type_): _description_ + + Raises: + ValidationError: _description_ + + Returns: + _type_: _description_ + """ + value = super().clean(value) + if value in self.empty_values: + return self.empty_value + + # Strip out spaces and dashes + value = value.replace(" ", "").replace("-", "") + kra_pin_regex = r"^(A|P)\d{9}[A-Z]$" + match = re.match(kra_pin_regex, value) + if not match: + raise ValidationError(self.error_messages.get("invalid")) + return value.upper() class KEIDNumberField(CharField): From 8ad74a74adf87e7c0ea8f4bd08a97b18a4fc5815 Mon Sep 17 00:00:00 2001 From: chmod77 Date: Sun, 26 Mar 2023 09:05:37 +0300 Subject: [PATCH 14/21] chore: document the counties model --- localflavor/ke/ke_counties.py | 1 + 1 file changed, 1 insertion(+) diff --git a/localflavor/ke/ke_counties.py b/localflavor/ke/ke_counties.py index bfe326302..0536e632d 100644 --- a/localflavor/ke/ke_counties.py +++ b/localflavor/ke/ke_counties.py @@ -4,6 +4,7 @@ from django.utils.translation import gettext_lazy as _ +# The 47 counties of Kenya COUNTY_CHOICES = ( ("MOMBASA", _("MOMBASA")), ("KWALE", _("KWALE")), From 783576f93b1035c97c51edfedcc2af03bf26773e Mon Sep 17 00:00:00 2001 From: chmod77 Date: Sun, 26 Mar 2023 09:39:49 +0300 Subject: [PATCH 15/21] feat: update validators for National ID --- localflavor/ke/forms.py | 47 ++++++++++++++++++++++++++-------------- localflavor/ke/models.py | 6 ++--- 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/localflavor/ke/forms.py b/localflavor/ke/forms.py index f346a5b3c..2d1549fc0 100644 --- a/localflavor/ke/forms.py +++ b/localflavor/ke/forms.py @@ -8,8 +8,10 @@ from .ke_counties import COUNTY_CHOICES -ke_id_re = re.compile(r"^\d{7}(?:\d{1})?$") ke_po_box_re = re.compile(r"\A\d{5,5}\Z") +ke_kra_pin_regex = re.compile(r"^(A|P)\d{9}[A-Z]$") +ke_passport_regex = re.compile(r"^[A-Z]\d{6,7}$") +ke_national_id_regex = re.compile(r"^\d{7,8}$") class KEPostalCodeField(CharField): @@ -90,49 +92,62 @@ def clean(self, value): # Strip out spaces and dashes value = value.replace(" ", "").replace("-", "") - kra_pin_regex = r"^(A|P)\d{9}[A-Z]$" - match = re.match(kra_pin_regex, value) + match = re.match(ke_kra_pin_regex, value) if not match: raise ValidationError(self.error_messages.get("invalid")) return value.upper() -class KEIDNumberField(CharField): +class KENationalIDNumberField(CharField): """ - TODO - - Kenya National ID Number - - 8 Digits + A form field that validates its input as a Kenyan National ID Number. """ default_error_messages = { - "invalid": _("Enter a valid Kenyan ID Number"), + "invalid": _( + "Enter a valid Kenyan National ID Number in the format 1234567 or 12345678" + ) } def clean(self, value): + """Runs the validation checks for KE National ID Number""" value = super().clean(value) if value in self.empty_values: return self.empty_value # Strip out spaces and dashes value = value.replace(" ", "").replace("-", "") - match = re.match(ke_id_re, value) - + match = re.match(ke_national_id_regex, value) if not match: raise ValidationError(self.error_messages.get("invalid")) return value -class KEPassportNumberField(RegexField): +class KEPassportNumberField(CharField): """ - TODO + A form field that validates its input as a Kenyan Passport Number. - Kenya Passport Number """ - ... + default_error_messages = { + "invalid": _( + "Enter a valid Kenyan Passport Number in the format A123456 or B1234567" + ) + } + + def clean(self, value): + """Runs the validation checks for KE Passport Number""" + value = super().clean(value) + if value in self.empty_values: + return self.empty_value + + # Strip out spaces and dashes + value = value.replace(" ", "").replace("-", "") + match = re.match(ke_passport_regex, value) + if not match: + raise ValidationError(self.error_messages.get("invalid")) + return value.upper() class KENSSFNumberField(RegexField): diff --git a/localflavor/ke/models.py b/localflavor/ke/models.py index 516776269..ce609a9d4 100644 --- a/localflavor/ke/models.py +++ b/localflavor/ke/models.py @@ -3,10 +3,8 @@ from django.utils.translation import gettext_lazy as _ from .forms import ( - KECompanyRegNumber as KECompanyRegNumberFormField, - KECountySelect, - KEIDNumberField, - KEKraPinNumberField, + KENationalIDNumberField, + KEKRAPINField, KENHIFNumberField, KENSSFNumberField, KEPassportNumberField, From 47c477aabe68fa1a4227f60e4fbdd1cf4beea987 Mon Sep 17 00:00:00 2001 From: chmod77 Date: Thu, 12 Oct 2023 04:55:51 +0300 Subject: [PATCH 16/21] chore: auto update authors --- docs/authors.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/authors.rst b/docs/authors.rst index 8825321a6..0480f2957 100644 --- a/docs/authors.rst +++ b/docs/authors.rst @@ -125,3 +125,4 @@ Authors * Vishal Pandey * Vladimir Nani * Abhineet Tamrakar +* Shalom Nyende From 9359cee7cc127c9f0ec555264fd8c57ea2350ded Mon Sep 17 00:00:00 2001 From: chmod77 Date: Thu, 12 Oct 2023 04:56:59 +0300 Subject: [PATCH 17/21] chore: auto update changelog --- docs/changelog.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index a0a4db765..e5ebfb31b 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,6 +5,7 @@ Changelog ------------------ New flavors: +- Kenya localflavor - Nepal LocalFlavor: Support for Nepal added (`gh-451 `_). From f59b428cd7e54390baab38d6b9d084c8a5f614b6 Mon Sep 17 00:00:00 2001 From: chmod77 Date: Thu, 12 Oct 2023 04:57:15 +0300 Subject: [PATCH 18/21] feat: update KE forms --- localflavor/ke/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/localflavor/ke/forms.py b/localflavor/ke/forms.py index 2d1549fc0..0a29f4d69 100644 --- a/localflavor/ke/forms.py +++ b/localflavor/ke/forms.py @@ -17,7 +17,6 @@ class KEPostalCodeField(CharField): """ A form field that validates its input as a Kenyan Postal Code. - """ default_error_messages = { @@ -66,6 +65,7 @@ class KEKRAPINField(CharField): POXXXXXXXX - Company/Institution AXXXXXXXXX - Individuals + """ default_error_messages = { From 1afc94d0a1935513a29f592b9b75af376a03f261 Mon Sep 17 00:00:00 2001 From: chmod77 Date: Thu, 12 Oct 2023 04:57:34 +0300 Subject: [PATCH 19/21] feat: update KE models --- localflavor/ke/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/localflavor/ke/models.py b/localflavor/ke/models.py index ce609a9d4..e888e19a9 100644 --- a/localflavor/ke/models.py +++ b/localflavor/ke/models.py @@ -26,6 +26,8 @@ def formfield(self, **kwargs) -> Any: defaults = {"form_class": KEPostalCodeFormField} defaults.update(kwargs) return super().formfield(**defaults) + + \ No newline at end of file From d6762f7e85f2467b716e9436c6e63a03ea1108e8 Mon Sep 17 00:00:00 2001 From: chmod77 Date: Thu, 12 Oct 2023 04:57:45 +0300 Subject: [PATCH 20/21] feat: add KE tests --- tests/test_ke.py | 59 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 tests/test_ke.py diff --git a/tests/test_ke.py b/tests/test_ke.py new file mode 100644 index 000000000..4446e191c --- /dev/null +++ b/tests/test_ke.py @@ -0,0 +1,59 @@ +from django.test import SimpleTestCase +from django.forms import ValidationError +from .forms import KEPostalCodeField, KEKRAPINField, KENationalIDNumberField, KEPassportNumberField + +class KEPostalCodeFieldTest(SimpleTestCase): + def test_valid_postal_code(self): + field = KEPostalCodeField() + valid_postal_codes = ["12345", "54321"] + for postal_code in valid_postal_codes: + self.assertEqual(field.clean(postal_code), "12345") + + def test_invalid_postal_code(self): + field = KEPostalCodeField() + invalid_postal_codes = ["1234", "ABCDE", "12 345", "12-345"] + for postal_code in invalid_postal_codes: + with self.assertRaises(ValidationError): + field.clean(postal_code) + +class KEKRAPINFieldTest(SimpleTestCase): + def test_valid_kra_pin(self): + field = KEKRAPINField() + valid_pins = ["A123456789B", "P987654321C"] + for pin in valid_pins: + self.assertEqual(field.clean(pin), pin) + + def test_invalid_kra_pin(self): + field = KEKRAPINField() + invalid_pins = ["1234567890", "A123456789", "P987654321", "A12-3456789B"] + for pin in invalid_pins: + with self.assertRaises(ValidationError): + field.clean(pin) + +class KENationalIDNumberFieldTest(SimpleTestCase): + def test_valid_national_id(self): + field = KENationalIDNumberField() + valid_ids = ["1234567", "12345678"] + for id_number in valid_ids: + self.assertEqual(field.clean(id_number), id_number) + + def test_invalid_national_id(self): + field = KENationalIDNumberField() + invalid_ids = ["12345", "12345A", "12-34567", "123456789"] + for id_number in invalid_ids: + with self.assertRaises(ValidationError): + field.clean(id_number) + +class KEPassportNumberFieldTest(SimpleTestCase): + def test_valid_passport_number(self): + field = KEPassportNumberField() + valid_passports = ["A123456", "B1234567"] + for passport in valid_passports: + self.assertEqual(field.clean(passport), passport) + + def test_invalid_passport_number(self): + field = KEPassportNumberField() + invalid_passports = ["12345", "A1234567B", "AB-123456"] + for passport in invalid_passports: + with self.assertRaises(ValidationError): + field.clean(passport) From 84a1f2eca9df4f09cd40811d295d6c0dfbc4e395 Mon Sep 17 00:00:00 2001 From: chmod77 Date: Thu, 12 Oct 2023 05:09:34 +0300 Subject: [PATCH 21/21] feat: add versioning for new localflavor --- localflavor/ke/forms.py | 8 ++++++-- localflavor/ke/models.py | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/localflavor/ke/forms.py b/localflavor/ke/forms.py index 0a29f4d69..3c6f929be 100644 --- a/localflavor/ke/forms.py +++ b/localflavor/ke/forms.py @@ -17,6 +17,7 @@ class KEPostalCodeField(CharField): """ A form field that validates its input as a Kenyan Postal Code. + .. versionadded:: 4.0 """ default_error_messages = { @@ -65,6 +66,8 @@ class KEKRAPINField(CharField): POXXXXXXXX - Company/Institution AXXXXXXXXX - Individuals + + .. versionadded:: 4.0 """ @@ -101,7 +104,7 @@ def clean(self, value): class KENationalIDNumberField(CharField): """ A form field that validates its input as a Kenyan National ID Number. - + .. versionadded:: 4.0 """ default_error_messages = { @@ -127,7 +130,7 @@ def clean(self, value): class KEPassportNumberField(CharField): """ A form field that validates its input as a Kenyan Passport Number. - + .. versionadded:: 4.0 """ default_error_messages = { @@ -189,6 +192,7 @@ class KEPayBillNumber(RegexField): class KECountySelectField(Select): """ A Select widget listing Kenyan Counties as the choices + .. versionadded:: 4.0 """ def __init__(self, attrs=None) -> None: diff --git a/localflavor/ke/models.py b/localflavor/ke/models.py index e888e19a9..73667c6a5 100644 --- a/localflavor/ke/models.py +++ b/localflavor/ke/models.py @@ -15,6 +15,7 @@ class KEPostalCodeField(CharField): """ A model field that stores the Kenyan Postal Codes + .. versionadded:: 4.0 """ description = _("Kenya Postal Code")