assettrack/functions/validate_values.py
candifloss 3acbb85b08 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.
2025-03-14 11:32:03 +05:30

104 lines
4.5 KiB
Python

from datetime import datetime
import re
from marshmallow import Schema, fields, ValidationError
from typing import Dict, Any, Optional, Type
from config import item_attributes
from definitions.attributes import *
def create_dynamic_schema() -> Type[Schema]:
"""
Dynamically creates a Marshmallow Schema based on the configuration in item_attributes.
"""
schema_fields = {} # Dictionary to store dynamically created fields
for attrib in item_attributes:
if isinstance(attrib, textAttribute):
schema_fields[attrib.attrib_name] = fields.String(
required=attrib.required,
validate=[
lambda x, attrib=attrib: len(x) <= attrib.max_length if attrib.max_length else True,
lambda x, attrib=attrib: len(x) >= attrib.min_length if attrib.min_length else True,
lambda x, attrib=attrib: re.match(attrib.regex, x) if attrib.regex else True,
],
error_messages={
"required": f"{attrib.display_name} is required.",
"validator_failed": f"Invalid value for {attrib.display_name}.",
},
)
elif isinstance(attrib, intAttribute):
schema_fields[attrib.attrib_name] = fields.Integer(
required=attrib.required,
validate=[
lambda x, attrib=attrib: x >= attrib.min_val if attrib.min_val is not None else True,
lambda x, attrib=attrib: x <= attrib.max_val if attrib.max_val is not None else True,
],
error_messages={
"required": f"{attrib.display_name} is required.",
"validator_failed": f"Invalid value for {attrib.display_name}.",
},
)
elif isinstance(attrib, floatAttribute):
schema_fields[attrib.attrib_name] = fields.Float(
required=attrib.required,
validate=[
lambda x, attrib=attrib: x >= attrib.min_val if attrib.min_val is not None else True,
lambda x, attrib=attrib: x <= attrib.max_val if attrib.max_val is not None else True,
],
error_messages={
"required": f"{attrib.display_name} is required.",
"validator_failed": f"Invalid value for {attrib.display_name}.",
},
)
elif isinstance(attrib, dateAttribute):
schema_fields[attrib.attrib_name] = fields.Date(
required=attrib.required,
format="%Y-%m-%d",
validate=[
lambda x, attrib=attrib: x >= datetime.strptime(attrib.min_val, "%Y-%m-%d").date()
if attrib.min_val
else True,
lambda x, attrib=attrib: x <= datetime.strptime(attrib.max_val, "%Y-%m-%d").date()
if attrib.max_val
else True,
],
error_messages={
"required": f"{attrib.display_name} is required.",
"validator_failed": f"Invalid value for {attrib.display_name}.",
},
)
elif isinstance(attrib, selectAttribute):
schema_fields[attrib.attrib_name] = fields.String(
required=attrib.required,
validate=[lambda x, attrib=attrib: x in attrib.options],
error_messages={
"required": f"{attrib.display_name} is required.",
"validator_failed": f"Invalid value for {attrib.display_name}. Must be one of: {', '.join(attrib.options)}.",
},
)
# Dynamically create the schema class
DynamicSchema = type("DynamicSchema", (Schema,), schema_fields)
return DynamicSchema
def validate_values(form_data: Dict[str, Any]) -> Optional[str]:
"""
Validate form data against the configuration in item_attributes using Marshmallow.
Returns an error message if invalid, otherwise None.
"""
DynamicSchema = create_dynamic_schema()
schema = DynamicSchema()
try:
schema.load(form_data) # Validate the data
return None # No errors
except ValidationError as e:
# Format the error message for display
error_messages = []
for field, errors in e.messages.items():
for error in errors:
error_messages.append(f"{field}: {error}")
return "; ".join(error_messages)