validator.py 7.74 KB
from .validation_required import ValidationRequired
from .validation_string import ValidationString
from .validation_email import ValidationEmail
from .validation_confirmed import ValidationConfirmed
from .validation_integer import ValidationInteger
from .validation_float import ValidationFloat
from .validation_digit import ValidationDigit
from .validation_min import ValidationMin
from .validation_max import ValidationMax
from .validation_list import ValidationList
from .validation_greater import ValidationGreater
from .validation_less import ValidationLess
from .validation_greater_field import ValidationGreaterField
from .validation_less_field import ValidationLessField
from .validation_datetime import ValidationDateTime
from .validation_db import (
    ValidationUniqueDB,
    ValidationExistsDB
)

class FormValidator(object):
    __invalid_key = 'VALIDATION ERROR'

    __map_validators    = {
        'required'      : ValidationRequired,
        'string'        : ValidationString,
        'integer'       : ValidationInteger,
        'float'         : ValidationFloat,
        'digit'         : ValidationDigit,
        'min'           : ValidationMin,
        'max'           : ValidationMax,
        'list'          : ValidationList,
        'greater'       : ValidationGreater,
        'less'          : ValidationLess,
        'greater_field' : ValidationGreaterField,
        'less_field'    : ValidationLessField,
        'email'         : ValidationEmail,
        'confirmed'     : ValidationConfirmed,
        'datetime'      : ValidationDateTime,
        'unique_db'     : ValidationUniqueDB,
        'exists_db'     : ValidationExistsDB
    }

    __map_rules_name    = {
        'required'      : 'required',
        'string'        : 'string',
        'str'           : 'string',
        'integer'       : 'integer',
        'int'           : 'integer',
        'float'         : 'float',
        'double'        : 'float',
        'decimal'       : 'float',
        'number'        : 'float',
        'digit'         : 'digit',
        'min'           : 'min',
        'max'           : 'max',
        'list'          : 'list',
        'greater'       : 'greater',
        'less'          : 'less',
        'greater_field' : 'greater_field',
        'less_field'    : 'less_field',
        'email'         : 'email',
        'confirmed'     : 'confirmed',
        'datetime'      : 'datetime',
        'date'          : 'datetime',
        'unique_db'     : 'unique_db',
        'unique'        : 'unique_db',
        'exists_db'     : 'exists_db',
        'exists'        : 'exists_db'
    }

    __boolean_rules = [
        'required',
        'string',
        'integer',
        'float',
        'email',
        'confirmed',
    ]

    def __init__(self, cldr, inputvalues, validations):
        self.__colander_invalid = cldr
        self.__validations      = validations
        self.__messages         = {}
        self.__values           = inputvalues
        self.__global_rules     = {}
        self.__validators       = []

        self.__todict_rules()
        self.__bind_validator()

    def __fix_rulename(self, rule_key):
        return (rule_key in self.__map_rules_name) and self.__map_rules_name[rule_key] or None

    def __is_boolean_rule(self, rule_name):
        return (rule_name in self.__boolean_rules)

    def __todict_rules(self):
        def fromstr(rs, cur_index = 0):
            result  = {}
            for x in list(rs.split('|')):
                rs = x.split(':')
                key = rs[0]
                val = (len(rs) > 1) and rs[1] or True
                result.update({key: val})
                result.update({cur_index: (key, val)})
                cur_index += 1

            return result

        def fromdict(rs, cur_index = 0):
            result = {}
            for s in rs:
                result.update({s: rs[s]})
                result.update({cur_index: (s, rs[s])})
                cur_index += 1

            return result

        def fromlist(rs, cur_index = 0):
            result = {}
            for rxs in rs:
                if not isinstance(rxs, (str, dict)):
                    continue
                if isinstance(rxs, str):
                    ds = fromstr(rxs, cur_index)
                else:
                    ds = fromdict(rxs, cur_index)
                result.update(ds)
                
            return result

        for inputname, validation in self.__validations.items():
            dict_rules  = None
            rule        = ('rules' in validation) and (validation['rules']) or None
            if not rule:
                continue

            if isinstance(rule, str):
                dict_rules = fromstr(rule)
            elif isinstance(rule, dict):
                dict_rules = fromdict(rule)
            elif isinstance(rule, list):
                dict_rules = fromlist(rule)

            if dict_rules:
                self.__global_rules[inputname]  = dict_rules
                self.__messages[inputname]      = ('messages' in validation) and validation['messages'] or {}

    def __get_validator(self, inputname, inputvalue, rule_key, rule_name, rule_value = None, isrequired = False, messages = {}):
        message     = (rule_key in messages) and messages[rule_key] or None
        if message is None:
            message = (rule_name in messages) and messages[rule_name] or None
        
        params = {
            'inputname'     : inputname,
            'inputvalue'    : inputvalue,
            'rule_value'    : rule_value,
            'required'      : isrequired,
            'message'       : message,
            'all_values'    : self.__values
        }

        validator   = None
        val_class   = (rule_name in self.__map_validators) and self.__map_validators[rule_name] or None
        
        if val_class:
            validator = val_class(cldr = self.__colander_invalid, params = params)
        else:
            raise Exception('Validation Error', 'validation rule "' + rule_key + '" tidak tersedia')

        return validator

    def __bind_validator(self):
        for inputname, rules in self.__global_rules.items():
            rules_validator = []
            inputvalue  = None
            if inputname in self.__values:
                inputvalue = self.__values[inputname]

            isrequired  = ('required' in rules and rules['required'] == True)
            messages    = (inputname in self.__messages) and self.__messages[inputname] or {}
            
            for rule_key, rule_value in rules.items():
                if not isinstance(rule_key, int):
                    continue

                rss     = rules[rule_key]
                key     = rss[0]
                value   = rss[1]

                rule_name   = self.__fix_rulename(key)

                if self.__is_boolean_rule(rule_name) and value != True:
                    continue

                validator   = self.__get_validator(
                    inputname   = inputname,
                    inputvalue  = inputvalue,
                    rule_key    = key,
                    rule_name   = rule_name,
                    rule_value  = value,
                    isrequired  = isrequired,
                    messages    = messages
                )
                rules_validator.append(validator)

            if rules_validator:
                self.__validators.append(rules_validator)

    def validate(self):
        if self.__validators:
            count_error = 0

            for validators in self.__validators:
                for validator in validators:
                    if not validator:
                        continue
                    
                    if not validator.validate():
                        count_error += 1
                        break

            if count_error > 0:
                raise self.__colander_invalid