from datetime import datetime import re from typing import List, Optional from definitions.attribute import textAttribute, intAttribute, dateAttribute, selectAttribute def _is_int(value) -> bool: """Check if a value is a valid integer.""" return isinstance(value, int) def _is_date(value) -> bool: """Check if a value is a valid date in YYYY-MM-DD format.""" if not isinstance(value, str): return False try: datetime.strptime(value, "%Y-%m-%d") return True except ValueError: return False def _validate_number(attrib: intAttribute) -> Optional[str]: """Validate number-specific attributes. Returns an error message if invalid, otherwise None.""" if attrib.min_val is not None and not _is_int(attrib.min_val): return f"min_val must be an integer for attribute '{attrib.attrib_name}'." if attrib.max_val is not None and not _is_int(attrib.max_val): return f"max_val must be an integer for attribute '{attrib.attrib_name}'." if attrib.min_val is not None and attrib.max_val is not None and attrib.min_val > attrib.max_val: return f"min_val cannot be greater than max_val for attribute '{attrib.attrib_name}'." if attrib.default_val is not None and not _is_int(attrib.default_val): return f"default_val must be an integer for attribute '{attrib.attrib_name}'." if attrib.default_val is not None: if attrib.min_val is not None and attrib.default_val < attrib.min_val: return f"default_val cannot be less than min_val for attribute '{attrib.attrib_name}'." if attrib.max_val is not None and attrib.default_val > attrib.max_val: return f"default_val cannot be greater than max_val for attribute '{attrib.attrib_name}'." return None def _validate_date(attrib: dateAttribute) -> Optional[str]: """Validate date-specific attributes. Returns an error message if invalid, otherwise None.""" if attrib.min_val is not None and not _is_date(attrib.min_val): return f"min_val must be a valid date (YYYY-MM-DD) for attribute '{attrib.attrib_name}'." if attrib.max_val is not None and not _is_date(attrib.max_val): return f"max_val must be a valid date (YYYY-MM-DD) for attribute '{attrib.attrib_name}'." if attrib.min_val is not None and attrib.max_val is not None: min_date = datetime.strptime(attrib.min_val, "%Y-%m-%d") max_date = datetime.strptime(attrib.max_val, "%Y-%m-%d") if max_date < min_date: return f"max_val cannot be earlier than min_val for attribute '{attrib.attrib_name}'." if attrib.default_val is not None and not _is_date(attrib.default_val): return f"default_val must be a valid date (YYYY-MM-DD) for attribute '{attrib.attrib_name}'." if attrib.default_val is not None: default_date = datetime.strptime(attrib.default_val, "%Y-%m-%d") if attrib.min_val is not None: min_date = datetime.strptime(attrib.min_val, "%Y-%m-%d") if default_date < min_date: return f"default_val cannot be earlier than min_val for attribute '{attrib.attrib_name}'." if attrib.max_val is not None: max_date = datetime.strptime(attrib.max_val, "%Y-%m-%d") if default_date > max_date: return f"default_val cannot be later than max_val for attribute '{attrib.attrib_name}'." return None def _validate_text(attrib: textAttribute) -> Optional[str]: """Validate text-specific attributes. Returns an error message if invalid, otherwise None.""" if attrib.default_val is not None and attrib.regex is not None: if not re.match(attrib.regex, str(attrib.default_val)): return f"default_val does not match the regex pattern for attribute '{attrib.attrib_name}'." return None def _validate_select(attrib: selectAttribute) -> Optional[str]: """Validate select-specific attributes. Returns an error message if invalid, otherwise None.""" if not attrib.options: return f"options cannot be empty for attribute '{attrib.attrib_name}'." if attrib.default_val is not None and attrib.default_val not in attrib.options: return f"default_val must be one of the options for attribute '{attrib.attrib_name}'." return None def validate_config(item_attributes: List) -> str: """ Validate the configuration file to ensure all attributes are properly defined. Returns "Ok" if everything is valid, otherwise returns an error message. """ for attrib in item_attributes: # Validate essential properties if not attrib.attrib_name: return f"Missing attribute name for the {item_attributes.index(attrib) + 1}th attribute." if not attrib.display_name: return f"Missing display name for attribute '{attrib.attrib_name}'." if not attrib.html_input_type: return f"Missing input type for attribute '{attrib.attrib_name}'." if attrib.html_input_type not in ["text", "number", "date", "select"]: return f"Invalid input type for attribute '{attrib.attrib_name}'." # Validate type-specific attributes if isinstance(attrib, intAttribute): error = _validate_number(attrib) if error: return error elif isinstance(attrib, dateAttribute): error = _validate_date(attrib) if error: return error elif isinstance(attrib, textAttribute): error = _validate_text(attrib) if error: return error elif isinstance(attrib, selectAttribute): error = _validate_select(attrib) if error: return error # Validate comparison (if applicable) if attrib.compareto: if not isinstance(attrib.compareto, list) or not all(isinstance(pair, tuple) and len(pair) == 2 for pair in attrib.compareto): return f"Invalid comparison format for attribute '{attrib.attrib_name}'. Expected a list of tuples." for cmp, ref_attr in attrib.compareto: if cmp not in ["lt", "gt", "le", "ge", "eq"]: return f"Invalid comparison operator '{cmp}' for attribute '{attrib.attrib_name}'." if ref_attr not in [a.attrib_name for a in item_attributes]: return f"Invalid reference attribute '{ref_attr}' for comparison in attribute '{attrib.attrib_name}'." return "Ok"