refactor: Modularize attribute class definitions
- Split `definitions/attribute.py` into separate files under `definitions/attributes/`. - Added `__init__.py` for simplified imports. - Improved code organization and maintainability.
This commit is contained in:
		
							parent
							
								
									f398c9e35b
								
							
						
					
					
						commit
						3acbb85b08
					
				@ -1,4 +1,4 @@
 | 
			
		||||
from definitions.attribute import textAttribute, intAttribute, dateAttribute, selectAttribute
 | 
			
		||||
from definitions.attributes import *
 | 
			
		||||
 | 
			
		||||
app_secret_key = "test_phase_secret_key"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,219 +0,0 @@
 | 
			
		||||
from datetime import datetime
 | 
			
		||||
import re
 | 
			
		||||
from typing import List, Optional, Tuple, Set
 | 
			
		||||
from dataclasses import dataclass, field
 | 
			
		||||
 | 
			
		||||
ALLOWED_INPUT_TYPES = {"text", "number", "date", "select"}
 | 
			
		||||
 | 
			
		||||
@dataclass
 | 
			
		||||
class Attribute:
 | 
			
		||||
    """Base class for all attribute types."""
 | 
			
		||||
    attrib_name: str
 | 
			
		||||
    display_name: str
 | 
			
		||||
    html_input_type: Optional[str] = "text"
 | 
			
		||||
    required: bool = False
 | 
			
		||||
    unique: bool = False
 | 
			
		||||
    primary: bool = False
 | 
			
		||||
    default_val: Optional[str] = None
 | 
			
		||||
    compareto: Optional[List[Tuple[str, str]]] = None
 | 
			
		||||
    valid_comparisons: Optional[Set[str]] = None
 | 
			
		||||
 | 
			
		||||
    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 ALLOWED_INPUT_TYPES:
 | 
			
		||||
            return f"Invalid input type '{self.html_input_type}' for attribute '{self.attrib_name}'."
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
@dataclass
 | 
			
		||||
class textAttribute(Attribute):
 | 
			
		||||
    """Class for text attributes."""
 | 
			
		||||
    regex: Optional[str] = None
 | 
			
		||||
    min_length: Optional[int] = None
 | 
			
		||||
    max_length: Optional[int] = 50
 | 
			
		||||
    allowed_chars: Optional[str] = None
 | 
			
		||||
    
 | 
			
		||||
    def __post_init__(self):
 | 
			
		||||
        """Post-initialization to set the HTML input type."""
 | 
			
		||||
        self.html_input_type = "text"
 | 
			
		||||
        self.valid_comparisons = {"lt", "st", "eq", "ne"}, # longer than, shorter than, eq, or not eq, to the ref attrib
 | 
			
		||||
 | 
			
		||||
    def validate(self) -> Optional[str]:
 | 
			
		||||
        """Validate text-specific attributes."""
 | 
			
		||||
        error = super().validate()
 | 
			
		||||
        if error:
 | 
			
		||||
            return error
 | 
			
		||||
        if self.min_length is not None and self.max_length is not None:
 | 
			
		||||
            if not isinstance(self.min_length, int) or not isinstance(self.max_length, int):
 | 
			
		||||
                return f"Min and max lengths must be integers for '{self.attrib_name}'."
 | 
			
		||||
            if int(self.min_length) > int(self.max_length):
 | 
			
		||||
                return f"Max length must be greater than min length for '{self.attrib_name}'."
 | 
			
		||||
        if self.default_val is not None:
 | 
			
		||||
            if self.regex is not None:
 | 
			
		||||
                compiled_regex = re.compile(self.regex)
 | 
			
		||||
                if not compiled_regex.match(str(self.default_val)):
 | 
			
		||||
                    return f"Default value for '{self.attrib_name}' must match the pattern: {self.regex}"
 | 
			
		||||
            if self.allowed_chars is not None:
 | 
			
		||||
                for char in self.default_val:
 | 
			
		||||
                    if char not in self.allowed_chars:
 | 
			
		||||
                        return f"Invalid character '{char}' in default value for '{self.attrib_name}'. Allowed characters are: {self.allowed_chars}"
 | 
			
		||||
            if self.min_length is not None:
 | 
			
		||||
                if len(self.default_val) < int(self.min_length):
 | 
			
		||||
                    return f"Invalid default value for '{self.attrib_name}'. The minimum length is: {self.min_length}"
 | 
			
		||||
            if self.max_length is not None:
 | 
			
		||||
                if len(self.default_val) > int(self.max_length):
 | 
			
		||||
                    return f"Invalid default value for '{self.attrib_name}'. The maximum length is: {self.max_length}"
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
@dataclass
 | 
			
		||||
class numAttribute(Attribute):
 | 
			
		||||
    """Base class for numeric attributes (int and float)."""
 | 
			
		||||
    min_val: Optional[float] = None
 | 
			
		||||
    max_val: Optional[float] = None
 | 
			
		||||
    step: float = 1.0
 | 
			
		||||
 | 
			
		||||
    def __post_init__(self):
 | 
			
		||||
        """Post-initialization to set the HTML input type."""
 | 
			
		||||
        self.html_input_type = "number"
 | 
			
		||||
        self.valid_comparisons = {"lt", "gt", "le", "ge", "eq", "ne"}  # <, >, <=, >=, ==, !=
 | 
			
		||||
 | 
			
		||||
    def validate(self) -> Optional[str]:
 | 
			
		||||
        """Validate numeric-specific attributes."""
 | 
			
		||||
        error = super().validate()
 | 
			
		||||
        if error:
 | 
			
		||||
            return error
 | 
			
		||||
 | 
			
		||||
        # Validate min_val and max_val
 | 
			
		||||
        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}'."
 | 
			
		||||
 | 
			
		||||
        # Validate step
 | 
			
		||||
        if self.step is not None:
 | 
			
		||||
            if not isinstance(self.step, (int, float)):
 | 
			
		||||
                return f"Step must be a number for attribute '{self.attrib_name}'."
 | 
			
		||||
            if self.step <= 0:
 | 
			
		||||
                return f"Step must be a positive number for attribute '{self.attrib_name}'."
 | 
			
		||||
            if self.min_val is not None and self.max_val is not None:
 | 
			
		||||
                range_val = self.max_val - self.min_val
 | 
			
		||||
                if self.step > range_val:
 | 
			
		||||
                    return f"Step value is too large for attribute '{self.attrib_name}'."
 | 
			
		||||
 | 
			
		||||
        # Validate default_val
 | 
			
		||||
        if self.default_val is not None:
 | 
			
		||||
            if not isinstance(self.default_val, (int, float)):
 | 
			
		||||
                return f"default_val must be a number for attribute '{self.attrib_name}'."
 | 
			
		||||
            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
 | 
			
		||||
 | 
			
		||||
@dataclass
 | 
			
		||||
class intAttribute(numAttribute):
 | 
			
		||||
    """Class for integer attributes."""
 | 
			
		||||
 | 
			
		||||
    def validate(self) -> Optional[str]:
 | 
			
		||||
        """Validate integer-specific attributes."""
 | 
			
		||||
        error = super().validate()
 | 
			
		||||
        if error:
 | 
			
		||||
            return error
 | 
			
		||||
 | 
			
		||||
        # Ensure default_val is an integer
 | 
			
		||||
        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}'."
 | 
			
		||||
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
@dataclass
 | 
			
		||||
class floatAttribute(numAttribute):
 | 
			
		||||
    """Class for float attributes."""
 | 
			
		||||
 | 
			
		||||
    def validate(self) -> Optional[str]:
 | 
			
		||||
        """Validate float-specific attributes."""
 | 
			
		||||
        error = super().validate()
 | 
			
		||||
        if error:
 | 
			
		||||
            return error
 | 
			
		||||
 | 
			
		||||
        # Ensure default_val is a float
 | 
			
		||||
        if self.default_val is not None and not isinstance(self.default_val, float):
 | 
			
		||||
            return f"default_val must be a float for attribute '{self.attrib_name}'."
 | 
			
		||||
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
@dataclass
 | 
			
		||||
class dateAttribute(Attribute):
 | 
			
		||||
    """Class for date attributes."""
 | 
			
		||||
    min_val: Optional[str] = None
 | 
			
		||||
    max_val: Optional[str] = None
 | 
			
		||||
    
 | 
			
		||||
    def __post_init__(self):
 | 
			
		||||
        """Post-initialization to set the HTML input type."""
 | 
			
		||||
        self.html_input_type = "date"
 | 
			
		||||
        self.valid_comparisons = {"lt", "gt", "le", "ge", "eq", "ne"}, # <, >, <=, >=, ==, !=
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
@dataclass
 | 
			
		||||
class selectAttribute(Attribute):
 | 
			
		||||
    """Class for select attributes."""
 | 
			
		||||
    options: List[str] = None
 | 
			
		||||
    
 | 
			
		||||
    def __post_init__(self):
 | 
			
		||||
        """Post-initialization to set the HTML input type."""
 | 
			
		||||
        self.html_input_type = "select"
 | 
			
		||||
        self.valid_comparisons = {"eq", "ne"} # eq, or not eq, to the ref attrib
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
							
								
								
									
										29
									
								
								definitions/attributes/Attribute.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								definitions/attributes/Attribute.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
			
		||||
from typing import List, Optional, Tuple, Set
 | 
			
		||||
from dataclasses import dataclass, field
 | 
			
		||||
 | 
			
		||||
ALLOWED_INPUT_TYPES = {"text", "number", "date", "select"}
 | 
			
		||||
 | 
			
		||||
@dataclass
 | 
			
		||||
class Attribute:
 | 
			
		||||
    """Base class for all attribute types."""
 | 
			
		||||
    attrib_name: str
 | 
			
		||||
    display_name: str
 | 
			
		||||
    html_input_type: Optional[str] = "text"
 | 
			
		||||
    required: bool = False
 | 
			
		||||
    unique: bool = False
 | 
			
		||||
    primary: bool = False
 | 
			
		||||
    default_val: Optional[str] = None
 | 
			
		||||
    compareto: Optional[List[Tuple[str, str]]] = None
 | 
			
		||||
    valid_comparisons: Optional[Set[str]] = None
 | 
			
		||||
 | 
			
		||||
    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 ALLOWED_INPUT_TYPES:
 | 
			
		||||
            return f"Invalid input type '{self.html_input_type}' for attribute '{self.attrib_name}'."
 | 
			
		||||
        return None
 | 
			
		||||
							
								
								
									
										16
									
								
								definitions/attributes/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								definitions/attributes/__init__.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,16 @@
 | 
			
		||||
from .Attribute import Attribute
 | 
			
		||||
from .textAttribute import textAttribute
 | 
			
		||||
from .numAttribute import numAttribute, intAttribute, floatAttribute
 | 
			
		||||
from .dateAttribute import dateAttribute
 | 
			
		||||
from .selectAttribute import selectAttribute
 | 
			
		||||
 | 
			
		||||
# Export all classes in a list for easier introspection
 | 
			
		||||
__all__ = [
 | 
			
		||||
    "Attribute",
 | 
			
		||||
    "textAttribute",
 | 
			
		||||
    "numAttribute",
 | 
			
		||||
    "intAttribute",
 | 
			
		||||
    "floatAttribute",
 | 
			
		||||
    "dateAttribute",
 | 
			
		||||
    "selectAttribute",
 | 
			
		||||
]
 | 
			
		||||
							
								
								
									
										51
									
								
								definitions/attributes/dateAttribute.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								definitions/attributes/dateAttribute.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
			
		||||
from datetime import datetime
 | 
			
		||||
from typing import Optional
 | 
			
		||||
from dataclasses import dataclass, field
 | 
			
		||||
from .Attribute import Attribute
 | 
			
		||||
 | 
			
		||||
@dataclass
 | 
			
		||||
class dateAttribute(Attribute):
 | 
			
		||||
    """Class for date attributes."""
 | 
			
		||||
    min_val: Optional[str] = None
 | 
			
		||||
    max_val: Optional[str] = None
 | 
			
		||||
    
 | 
			
		||||
    def __post_init__(self):
 | 
			
		||||
        """Post-initialization to set the HTML input type."""
 | 
			
		||||
        self.html_input_type = "date"
 | 
			
		||||
        self.valid_comparisons = {"lt", "gt", "le", "ge", "eq", "ne"}, # <, >, <=, >=, ==, !=
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
							
								
								
									
										83
									
								
								definitions/attributes/numAttribute.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								definitions/attributes/numAttribute.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,83 @@
 | 
			
		||||
from typing import Optional
 | 
			
		||||
from dataclasses import dataclass, field
 | 
			
		||||
from .Attribute import Attribute
 | 
			
		||||
 | 
			
		||||
@dataclass
 | 
			
		||||
class numAttribute(Attribute):
 | 
			
		||||
    """Base class for numeric attributes (int and float)."""
 | 
			
		||||
    min_val: Optional[float] = None
 | 
			
		||||
    max_val: Optional[float] = None
 | 
			
		||||
    step: float = 1.0
 | 
			
		||||
 | 
			
		||||
    def __post_init__(self):
 | 
			
		||||
        """Post-initialization to set the HTML input type."""
 | 
			
		||||
        self.html_input_type = "number"
 | 
			
		||||
        self.valid_comparisons = {"lt", "gt", "le", "ge", "eq", "ne"}  # <, >, <=, >=, ==, !=
 | 
			
		||||
 | 
			
		||||
    def validate(self) -> Optional[str]:
 | 
			
		||||
        """Validate numeric-specific attributes."""
 | 
			
		||||
        error = super().validate()
 | 
			
		||||
        if error:
 | 
			
		||||
            return error
 | 
			
		||||
 | 
			
		||||
        # Validate min_val and max_val
 | 
			
		||||
        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}'."
 | 
			
		||||
 | 
			
		||||
        # Validate step
 | 
			
		||||
        if self.step is not None:
 | 
			
		||||
            if not isinstance(self.step, (int, float)):
 | 
			
		||||
                return f"Step must be a number for attribute '{self.attrib_name}'."
 | 
			
		||||
            if self.step <= 0:
 | 
			
		||||
                return f"Step must be a positive number for attribute '{self.attrib_name}'."
 | 
			
		||||
            if self.min_val is not None and self.max_val is not None:
 | 
			
		||||
                range_val = self.max_val - self.min_val
 | 
			
		||||
                if self.step > range_val:
 | 
			
		||||
                    return f"Step value is too large for attribute '{self.attrib_name}'."
 | 
			
		||||
 | 
			
		||||
        # Validate default_val
 | 
			
		||||
        if self.default_val is not None:
 | 
			
		||||
            if not isinstance(self.default_val, (int, float)):
 | 
			
		||||
                return f"default_val must be a number for attribute '{self.attrib_name}'."
 | 
			
		||||
            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
 | 
			
		||||
 | 
			
		||||
@dataclass
 | 
			
		||||
class intAttribute(numAttribute):
 | 
			
		||||
    """Class for integer attributes."""
 | 
			
		||||
 | 
			
		||||
    def validate(self) -> Optional[str]:
 | 
			
		||||
        """Validate integer-specific attributes."""
 | 
			
		||||
        error = super().validate()
 | 
			
		||||
        if error:
 | 
			
		||||
            return error
 | 
			
		||||
 | 
			
		||||
        # Ensure default_val is an integer
 | 
			
		||||
        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}'."
 | 
			
		||||
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
@dataclass
 | 
			
		||||
class floatAttribute(numAttribute):
 | 
			
		||||
    """Class for float attributes."""
 | 
			
		||||
 | 
			
		||||
    def validate(self) -> Optional[str]:
 | 
			
		||||
        """Validate float-specific attributes."""
 | 
			
		||||
        error = super().validate()
 | 
			
		||||
        if error:
 | 
			
		||||
            return error
 | 
			
		||||
 | 
			
		||||
        # Ensure default_val is a float
 | 
			
		||||
        if self.default_val is not None and not isinstance(self.default_val, float):
 | 
			
		||||
            return f"default_val must be a float for attribute '{self.attrib_name}'."
 | 
			
		||||
 | 
			
		||||
        return None
 | 
			
		||||
							
								
								
									
										24
									
								
								definitions/attributes/selectAttribute.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								definitions/attributes/selectAttribute.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
from typing import List, Optional
 | 
			
		||||
from dataclasses import dataclass, field
 | 
			
		||||
from .Attribute import Attribute
 | 
			
		||||
 | 
			
		||||
@dataclass
 | 
			
		||||
class selectAttribute(Attribute):
 | 
			
		||||
    """Class for select attributes."""
 | 
			
		||||
    options: List[str] = None
 | 
			
		||||
    
 | 
			
		||||
    def __post_init__(self):
 | 
			
		||||
        """Post-initialization to set the HTML input type."""
 | 
			
		||||
        self.html_input_type = "select"
 | 
			
		||||
        self.valid_comparisons = {"eq", "ne"} # eq, or not eq, to the ref attrib
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
							
								
								
									
										44
									
								
								definitions/attributes/textAttribute.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								definitions/attributes/textAttribute.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,44 @@
 | 
			
		||||
import re
 | 
			
		||||
from typing import Optional
 | 
			
		||||
from dataclasses import dataclass, field
 | 
			
		||||
from .Attribute import Attribute
 | 
			
		||||
 | 
			
		||||
@dataclass
 | 
			
		||||
class textAttribute(Attribute):
 | 
			
		||||
    """Class for text attributes."""
 | 
			
		||||
    regex: Optional[str] = None
 | 
			
		||||
    min_length: Optional[int] = None
 | 
			
		||||
    max_length: Optional[int] = 50
 | 
			
		||||
    allowed_chars: Optional[str] = None
 | 
			
		||||
    
 | 
			
		||||
    def __post_init__(self):
 | 
			
		||||
        """Post-initialization to set the HTML input type."""
 | 
			
		||||
        self.html_input_type = "text"
 | 
			
		||||
        self.valid_comparisons = {"lt", "st", "eq", "ne"}, # longer than, shorter than, eq, or not eq, to the ref attrib
 | 
			
		||||
 | 
			
		||||
    def validate(self) -> Optional[str]:
 | 
			
		||||
        """Validate text-specific attributes."""
 | 
			
		||||
        error = super().validate()
 | 
			
		||||
        if error:
 | 
			
		||||
            return error
 | 
			
		||||
        if self.min_length is not None and self.max_length is not None:
 | 
			
		||||
            if not isinstance(self.min_length, int) or not isinstance(self.max_length, int):
 | 
			
		||||
                return f"Min and max lengths must be integers for '{self.attrib_name}'."
 | 
			
		||||
            if int(self.min_length) > int(self.max_length):
 | 
			
		||||
                return f"Max length must be greater than min length for '{self.attrib_name}'."
 | 
			
		||||
        if self.default_val is not None:
 | 
			
		||||
            if self.regex is not None:
 | 
			
		||||
                compiled_regex = re.compile(self.regex)
 | 
			
		||||
                if not compiled_regex.match(str(self.default_val)):
 | 
			
		||||
                    return f"Default value for '{self.attrib_name}' must match the pattern: {self.regex}"
 | 
			
		||||
            if self.allowed_chars is not None:
 | 
			
		||||
                for char in self.default_val:
 | 
			
		||||
                    if char not in self.allowed_chars:
 | 
			
		||||
                        return f"Invalid character '{char}' in default value for '{self.attrib_name}'. Allowed characters are: {self.allowed_chars}"
 | 
			
		||||
            if self.min_length is not None:
 | 
			
		||||
                if len(self.default_val) < int(self.min_length):
 | 
			
		||||
                    return f"Invalid default value for '{self.attrib_name}'. The minimum length is: {self.min_length}"
 | 
			
		||||
            if self.max_length is not None:
 | 
			
		||||
                if len(self.default_val) > int(self.max_length):
 | 
			
		||||
                    return f"Invalid default value for '{self.attrib_name}'. The maximum length is: {self.max_length}"
 | 
			
		||||
        return None
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
from flask_sqlalchemy import SQLAlchemy
 | 
			
		||||
from sqlalchemy import Enum, Integer, Float, String, Date, Column, Boolean
 | 
			
		||||
from config import item_attributes, sql_conf
 | 
			
		||||
from definitions.attribute import textAttribute, intAttribute, floatAttribute, dateAttribute, selectAttribute
 | 
			
		||||
from definitions.attributes import *
 | 
			
		||||
 | 
			
		||||
# Initialize SQLAlchemy
 | 
			
		||||
db = SQLAlchemy()
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
from typing import List
 | 
			
		||||
from definitions.attribute import Attribute
 | 
			
		||||
from definitions.attributes import *
 | 
			
		||||
 | 
			
		||||
def validate_config(item_attributes: List[Attribute]) -> str:
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
@ -3,13 +3,7 @@ import re
 | 
			
		||||
from marshmallow import Schema, fields, ValidationError
 | 
			
		||||
from typing import Dict, Any, Optional, Type
 | 
			
		||||
from config import item_attributes
 | 
			
		||||
from definitions.attribute import (
 | 
			
		||||
    textAttribute,
 | 
			
		||||
    intAttribute,
 | 
			
		||||
    floatAttribute,
 | 
			
		||||
    dateAttribute,
 | 
			
		||||
    selectAttribute,
 | 
			
		||||
)
 | 
			
		||||
from definitions.attributes import *
 | 
			
		||||
 | 
			
		||||
def create_dynamic_schema() -> Type[Schema]:
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user