####################################### InputValidation ####################################### from abc import ABC, abstractmethod from BreCal.schemas.model import Ship, Shipcall, Berth, User, Participant class InputValidation(): def __init__(self): self.build_supported_models_dictionary() return def build_supported_models_dictionary(self): self.supported_models = { Ship:ShipValidation(), Shipcall:ShipcallValidation(), Berth:BerthValidation(), User:UserValidation(), Participant:ParticipantValidation(), } return def assert_if_not_supported(self, dataclass_object): assert type(dataclass_object) in self.supported_models, f"unsupported model. Found: {type(dataclass_object)}" return def verify(self, dataclass_object): self.assert_if_not_supported(dataclass_object) # determine the type of the dataclass object. The internal dictionary 'supported_models' matches the dataclass object # to the respective validation protocol validator = self.supported_models.get(type(dataclass_object)) # check the object based on the rules within the matched validator input_validation_state = validator.check(dataclass_object) return input_validation_state class DataclassValidation(ABC): """parent class of dataclas validators, which determines the outline of every object""" def __init__(self): return def check(self, dataclass_object) -> (list, bool): """ the 'check' method provides a default style, how each dataclass object is validated. It returns a list of violations and a boolean, which determines, whether the check is passed successfully """ all_rules = self.apply_all_rules(dataclass_object) violations = self.filter_violations(all_rules) input_validation_state = self.evaluate(violations) return (violations, input_validation_state) @abstractmethod def apply_all_rules(self, dataclass_object) -> list: """ the 'apply_all_rules' method is mandatory for any dataclass validation object. It should execute all validation rules and return a list of tuples, where each element is (output_boolean, validation_name) """ all_rules = [(True, 'blank_validation_rule')] return all_rules def filter_violations(self, all_rules): """input: all_rules, a list of tuples, where each element is (output, validation_name), which are (bool, str). """ # if output is False, a violation is observed violations = [result[1] for result in all_rules if not result[0]] return violations def evaluate(self, violations) -> bool: input_validation_state = len(violations)==0 return input_validation_state class ShipcallValidation(DataclassValidation): """an object that validates a Shipcall dataclass object""" def __init__(self): super().__init__() return def apply_all_rules(self, dataclass_object) -> list: """apply all input validation rules to determine, whether there are violations. returns a list of tuples (output, validation_name)""" raise NotImplementedError() return all_rules from BreCal.validators.schema_validation import ship_bollard_pull_is_defined_or_is_not_tug, ship_bollard_pull_is_none_or_in_range, ship_callsign_len_is_seven_at_maximum, ship_eni_len_is_eight, ship_imo_len_is_seven, ship_length_in_range, ship_participant_id_is_defined_or_is_not_tug, ship_participant_id_is_none_or_int, ship_width_in_range # skip: ship_max_draft_is_defined_or_is_not_tug, ship_max_draft_is_none_or_in_range, class ShipValidation(DataclassValidation): """an object that validates a Ship dataclass object""" def __init__(self): super().__init__() return def apply_all_rules(self, dataclass_object) -> list: """apply all input validation rules to determine, whether there are violations. returns a list of tuples (output, validation_name)""" # skip: ship_max_draft_is_defined_or_is_not_tug, ship_max_draft_is_none_or_in_range, """ #TODO_ship_max_draft with pytest.raises(AttributeError, match="'Ship' object has no attribute 'max_draft'"): assert ship_max_draft_in_range(ship)[0], f"max draft of a ship must be between 0 and 20 meters" assert ship_max_draft_is_none_or_in_range(ship)[0], f"the max_draft should either be undefined or between 0 and 20 meters" """ # list comprehension: every function becomes part of the loop and will be executed. Each function is wrapped and provides (output, validation_name) all_rules = [ # tuple: (output, validation_name) check_rule(dataclass_object) for check_rule in [ ship_bollard_pull_is_defined_or_is_not_tug, ship_bollard_pull_is_none_or_in_range, ship_callsign_len_is_seven_at_maximum, ship_eni_len_is_eight, ship_imo_len_is_seven, ship_length_in_range, ship_participant_id_is_defined_or_is_not_tug, ship_participant_id_is_none_or_int, ship_width_in_range ] ] return all_rules class BerthValidation(DataclassValidation): """an object that validates a Berth dataclass object""" def __init__(self): super().__init__() return def apply_all_rules(self, dataclass_object) -> list: """apply all input validation rules to determine, whether there are violations. returns a list of tuples (output, validation_name)""" raise NotImplementedError() return all_rules class UserValidation(DataclassValidation): """an object that validates a User dataclass object""" def __init__(self): super().__init__() return def apply_all_rules(self, dataclass_object) -> list: """apply all input validation rules to determine, whether there are violations. returns a list of tuples (output, validation_name)""" raise NotImplementedError() return all_rules from BreCal.validators.schema_validation import participant_postal_code_len_is_five class ParticipantValidation(DataclassValidation): """an object that validates a Participant dataclass object""" def __init__(self): super().__init__() return def apply_all_rules(self, dataclass_object) -> list: """apply all input validation rules to determine, whether there are violations. returns a list of tuples (output, validation_name)""" # list comprehension: every function becomes part of the loop and will be executed. Each function is wrapped and provides (output, validation_name) all_rules = [ # tuple: (output, validation_name) check_rule(dataclass_object) for check_rule in [ participant_postal_code_len_is_five, ] ] return all_rules