Skip to content

Commit

Permalink
Refactored complex methods #261
Browse files Browse the repository at this point in the history
- Complexity 9
- Moved function validators to Validator classes
  • Loading branch information
vladimirnani committed Nov 11, 2016
1 parent 2927a30 commit 0fa5cbd
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 52 deletions.
4 changes: 4 additions & 0 deletions localflavor.prospector.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ pep8:
max-line-length: 120
disable:
- N802

mccabe:
options:
max-complexity: 9
6 changes: 3 additions & 3 deletions localflavor/bg/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.db import models

from .validators import egn_validator, eik_validator
from .validators import EGNValidator, EIKValidator


class BGEGNField(models.CharField):
Expand All @@ -12,7 +12,7 @@ class BGEGNField(models.CharField):
models.CharField(max_length=10, validators=[localflavor.bg.validators.egn_validator])
"""

default_validators = models.CharField.default_validators + [egn_validator]
default_validators = models.CharField.default_validators + [EGNValidator()]

def __init__(self, *args, **kwargs):
kwargs['max_length'] = 10
Expand All @@ -28,7 +28,7 @@ class BGEIKField(models.CharField):
models.CharField(max_length=13, validators=[localflavor.bg.validators.eik_validator])
"""

default_validators = models.CharField.default_validators + [eik_validator]
default_validators = models.CharField.default_validators + [EIKValidator()]

def __init__(self, *args, **kwargs):
kwargs['max_length'] = 13
Expand Down
54 changes: 30 additions & 24 deletions localflavor/bg/validators.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,41 @@
from django.core.exceptions import ValidationError
from django.utils.deconstruct import deconstructible
from django.utils.translation import ugettext_lazy as _

from .utils import get_egn_birth_date


def egn_validator(egn):
@deconstructible
class EGNValidator(object):
"""
Check Bulgarian unique citizenship number (EGN) for validity.
More details https://en.wikipedia.org/wiki/Unique_citizenship_number
Full information in Bulgarian about algorithm is available here
http://www.grao.bg/esgraon.html#section2
"""
def check_checksum(egn):

def check_checksum(self, egn):
weights = (2, 4, 8, 5, 10, 9, 7, 3, 6)
try:
checksum = sum(weight * int(digit) for weight, digit in zip(weights, egn))
return int(egn[-1]) == checksum % 11 % 10
except ValueError:
return False

def check_valid_date(egn):
def check_valid_date(self, egn):
try:
return get_egn_birth_date(egn)
except ValueError:
return None

if not (len(egn) == 10 and check_checksum(egn) and check_valid_date(egn)):
raise ValidationError(_("The EGN is not valid"))
def __call__(self, egn):
if not (len(egn) == 10 and self.check_checksum(egn) and self.check_valid_date(egn)):
raise ValidationError(_("The EGN is not valid"))


def eik_validator(eik):
@deconstructible
class EIKValidator(object):
"""
Check Bulgarian EIK/BULSTAT codes for validity.
Expand All @@ -39,30 +44,31 @@ def eik_validator(eik):
"""
error_message = _('EIK/BULSTAT is not valid')

def get_checksum(weights, digits):
def __call__(self, value):
try:
value = list(map(int, value))
except ValueError:
raise ValidationError(self.error_message)

if not (len(value) in [9, 13] and self.check_eik_base(value)):
raise ValidationError(self.error_message)

if len(value) == 13 and not self.check_eik_extra(value):
raise ValidationError(self.error_message)

def get_checksum(self, weights, digits):
checksum = sum(weight * digit for weight, digit in zip(weights, digits))
return checksum % 11

def check_eik_base(eik):
checksum = get_checksum(range(1, 9), eik)
def check_eik_base(self, eik):
checksum = self.get_checksum(range(1, 9), eik)
if checksum == 10:
checksum = get_checksum(range(3, 11), eik)
checksum = self.get_checksum(range(3, 11), eik)
return eik[8] == checksum % 10

def check_eik_extra(eik):
def check_eik_extra(self, eik):
digits = eik[8:12]
checksum = get_checksum((2, 7, 3, 5), digits)
checksum = self.get_checksum((2, 7, 3, 5), digits)
if checksum == 10:
checksum = get_checksum((4, 9, 5, 7), digits)
checksum = self.get_checksum((4, 9, 5, 7), digits)
return eik[-1] == checksum % 10

try:
eik = list(map(int, eik))
except ValueError:
raise ValidationError(error_message)

if not (len(eik) in [9, 13] and check_eik_base(eik)):
raise ValidationError(error_message)

if len(eik) == 13 and not check_eik_extra(eik):
raise ValidationError(error_message)
52 changes: 28 additions & 24 deletions localflavor/si/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,39 @@ def clean(self, value):

value = value.strip()

m = self.regex_match(value)
day, month, year, nationality, gender, checksum = [int(i) for i in m.groups()]

self.validate_emso(checksum, value)
birthday = self.validate_birthday(day, month, year)

self.info = {
'gender': gender < 500 and 'male' or 'female',
'birthdate': birthday,
'nationality': nationality,
}
return value

def regex_match(self, value):
m = self.emso_regex.match(value)
if m is None:
raise ValidationError(self.error_messages['invalid'])
return m

# Extract information in the identification number.
day, month, year, nationality, gender, checksum = [int(i) for i in m.groups()]
def validate_birthday(self, day, month, year):
if year < 890:
year += 2000
else:
year += 1000
try:
birthday = datetime.date(year, month, day)
except ValueError:
raise ValidationError(self.error_messages['date'])
if datetime.date.today() < birthday:
raise ValidationError(self.error_messages['date'])
return birthday

# Validate EMSO
def validate_emso(self, checksum, value):
s = 0
int_values = [int(i) for i in value]
for a, b in zip(int_values, list(range(7, 1, -1)) * 2):
Expand All @@ -51,30 +76,9 @@ def clean(self, value):
k = 0
else:
k = 11 - chk

if k == 10 or checksum != k:
raise ValidationError(self.error_messages['checksum'])

# Validate birth date.
if year < 890:
year += 2000
else:
year += 1000

try:
birthday = datetime.date(year, month, day)
except ValueError:
raise ValidationError(self.error_messages['date'])
if datetime.date.today() < birthday:
raise ValidationError(self.error_messages['date'])

self.info = {
'gender': gender < 500 and 'male' or 'female',
'birthdate': birthday,
'nationality': nationality,
}
return value


class SITaxNumberField(CharField):
"""
Expand Down
5 changes: 4 additions & 1 deletion tests/test_bg.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

from localflavor.bg.models import BGEGNField, BGEIKField
from localflavor.bg.utils import get_egn_birth_date
from localflavor.bg.validators import egn_validator, eik_validator
from localflavor.bg.validators import EGNValidator, EIKValidator

VALID_EGNS = (
'7523169263',
Expand Down Expand Up @@ -60,6 +60,9 @@
'aaaaaaaaaa',
)

eik_validator = EIKValidator()
egn_validator = EGNValidator()


class BGLocalFlavorValidatorsTests(TestCase):

Expand Down

0 comments on commit 0fa5cbd

Please sign in to comment.