from datetime import datetime import re from typing import List, Optional, Tuple class Attribute: """Base class for all attribute types.""" def __init__( self, attrib_name: str, display_name: str, html_input_type: str, required: bool = False, unique: bool = False, primary: bool = False, default_val: Optional[str] = None, compareto: Optional[List[Tuple[str, str]]] = None, ): self.attrib_name = attrib_name self.display_name = display_name self.html_input_type = html_input_type self.required = required self.unique = unique self.primary = primary self.default_val = default_val self.compareto = compareto def validate(self) -> Optional[str]: """Validate common attributes. Returns an error message if invalid, otherwise None.""" if not self.attrib_name: return "Missing attribute name." if not self.display_name: return f"Missing display name for attribute '{self.attrib_name}'." if not self.html_input_type: return f"Missing input type for attribute '{self.attrib_name}'." if self.html_input_type not in ["text", "number", "date", "select"]: return f"Invalid input type for attribute '{self.attrib_name}'." return None class textAttribute(Attribute): """Class for text attributes.""" def __init__( self, attrib_name: str, display_name: str, regex: Optional[str] = None, **kwargs ): super().__init__(attrib_name, display_name, html_input_type="text", **kwargs) self.regex = regex def validate(self) -> Optional[str]: """Validate text-specific attributes.""" error = super().validate() if error: return error if self.default_val is not None and self.regex is not None: if not re.match(self.regex, str(self.default_val)): return f"default_val does not match the regex pattern for attribute '{self.attrib_name}'." return None class intAttribute(Attribute): """Class for integer attributes.""" def __init__( self, attrib_name: str, display_name: str, min_val: Optional[int] = None, max_val: Optional[int] = None, **kwargs ): super().__init__(attrib_name, display_name, html_input_type="number", **kwargs) self.min_val = min_val self.max_val = max_val def validate(self) -> Optional[str]: """Validate integer-specific attributes.""" error = super().validate() if error: return error if self.min_val is not None and not isinstance(self.min_val, int): return f"min_val must be an integer for attribute '{self.attrib_name}'." if self.max_val is not None and not isinstance(self.max_val, int): return f"max_val must be an integer for attribute '{self.attrib_name}'." if self.min_val is not None and self.max_val is not None and self.min_val > self.max_val: return f"min_val cannot be greater than max_val for attribute '{self.attrib_name}'." if self.default_val is not None and not isinstance(self.default_val, int): return f"default_val must be an integer for attribute '{self.attrib_name}'." if self.default_val is not None: if self.min_val is not None and self.default_val < self.min_val: return f"default_val cannot be less than min_val for attribute '{self.attrib_name}'." if self.max_val is not None and self.default_val > self.max_val: return f"default_val cannot be greater than max_val for attribute '{self.attrib_name}'." return None class floatAttribute(Attribute): """Class for float attributes.""" def __init__( self, attrib_name: str, display_name: str, min_val: Optional[float] = None, max_val: Optional[float] = None, **kwargs ): super().__init__(attrib_name, display_name, html_input_type="number", **kwargs) self.min_val = min_val self.max_val = max_val def validate(self) -> Optional[str]: """Validate float-specific attributes.""" error = super().validate() if error: return error if self.min_val is not None and not isinstance(self.min_val, (int, float)): return f"min_val must be a number for attribute '{self.attrib_name}'." if self.max_val is not None and not isinstance(self.max_val, (int, float)): return f"max_val must be a number for attribute '{self.attrib_name}'." if self.min_val is not None and self.max_val is not None and self.min_val > self.max_val: return f"min_val cannot be greater than max_val for attribute '{self.attrib_name}'." if self.default_val is not None and not isinstance(self.default_val, (int, float)): return f"default_val must be a number for attribute '{self.attrib_name}'." if self.default_val is not None: if self.min_val is not None and self.default_val < self.min_val: return f"default_val cannot be less than min_val for attribute '{self.attrib_name}'." if self.max_val is not None and self.default_val > self.max_val: return f"default_val cannot be greater than max_val for attribute '{self.attrib_name}'." return None class dateAttribute(Attribute): """Class for date attributes.""" def __init__( self, attrib_name: str, display_name: str, min_val: Optional[str] = None, max_val: Optional[str] = None, **kwargs ): super().__init__(attrib_name, display_name, html_input_type="date", **kwargs) self.min_val = min_val self.max_val = max_val def _is_date(self, value: str) -> bool: """Check if a value is a valid date in YYYY-MM-DD format.""" try: datetime.strptime(value, "%Y-%m-%d") return True except ValueError: return False def validate(self) -> Optional[str]: """Validate date-specific attributes.""" error = super().validate() if error: return error if self.min_val is not None and not self._is_date(self.min_val): return f"min_val must be a valid date (YYYY-MM-DD) for attribute '{self.attrib_name}'." if self.max_val is not None and not self._is_date(self.max_val): return f"max_val must be a valid date (YYYY-MM-DD) for attribute '{self.attrib_name}'." if self.min_val is not None and self.max_val is not None: min_date = datetime.strptime(self.min_val, "%Y-%m-%d") max_date = datetime.strptime(self.max_val, "%Y-%m-%d") if max_date < min_date: return f"max_val cannot be earlier than min_val for attribute '{self.attrib_name}'." if self.default_val is not None and not self._is_date(self.default_val): return f"default_val must be a valid date (YYYY-MM-DD) for attribute '{self.attrib_name}'." if self.default_val is not None: default_date = datetime.strptime(self.default_val, "%Y-%m-%d") if self.min_val is not None: min_date = datetime.strptime(self.min_val, "%Y-%m-%d") if default_date < min_date: return f"default_val cannot be earlier than min_val for attribute '{self.attrib_name}'." if self.max_val is not None: max_date = datetime.strptime(self.max_val, "%Y-%m-%d") if default_date > max_date: return f"default_val cannot be later than max_val for attribute '{self.attrib_name}'." return None class selectAttribute(Attribute): """Class for select attributes.""" def __init__( self, attrib_name: str, display_name: str, options: List[str], **kwargs ): super().__init__(attrib_name, display_name, html_input_type="select", **kwargs) self.options = options def validate(self) -> Optional[str]: """Validate select-specific attributes.""" error = super().validate() if error: return error if not self.options: return f"options cannot be empty for attribute '{self.attrib_name}'." if self.default_val is not None and self.default_val not in self.options: return f"default_val must be one of the options for attribute '{self.attrib_name}'." return None