Spaces:
Running
Running
| """ | |
| Data validation utilities | |
| """ | |
| import re | |
| from math import gcd | |
| from typing import Set | |
| # --- Aspect ratio validation --- | |
| _ASPECT_RATIO_PATTERN = re.compile(r"^\d+:\d+$") | |
| _ASPECT_RATIO_MIN = 0.2 | |
| _ASPECT_RATIO_MAX = 5.0 | |
| def normalize_aspect_ratio(raw_value) -> str: | |
| """ | |
| Normalize and validate aspect ratio input. | |
| - Accepts "W:H" where W/H are positive integers. | |
| - Reduces by gcd (e.g., "1920:1080" -> "16:9"). | |
| - Rejects obviously invalid or extreme ratios. | |
| - Returns the normalized string like "16:9". | |
| """ | |
| if raw_value is None: | |
| raise ValueError("Image aspect ratio is required") | |
| value = str(raw_value).strip() | |
| if value == "": | |
| raise ValueError("Image aspect ratio is required") | |
| if not _ASPECT_RATIO_PATTERN.fullmatch(value): | |
| raise ValueError( | |
| "Image aspect ratio must match \\d+:\\d+ (e.g., 16:9, 4:3, 1:1)" | |
| ) | |
| width, height = (int(part) for part in value.split(":", 1)) | |
| if width <= 0 or height <= 0: | |
| raise ValueError("Image aspect ratio must be positive integers (e.g., 16:9)") | |
| divisor = gcd(width, height) | |
| width //= divisor | |
| height //= divisor | |
| ratio_value = width / height | |
| if ratio_value < _ASPECT_RATIO_MIN or ratio_value > _ASPECT_RATIO_MAX: | |
| raise ValueError( | |
| f"Image aspect ratio must be between {_ASPECT_RATIO_MIN:.1f} and {_ASPECT_RATIO_MAX:.1f} (e.g., 16:9)" | |
| ) | |
| normalized = f"{width}:{height}" | |
| if len(normalized) > 10: | |
| raise ValueError("Image aspect ratio is too long") | |
| return normalized | |
| # Project status states | |
| PROJECT_STATUSES = { | |
| 'DRAFT', | |
| 'OUTLINE_GENERATED', | |
| 'DESCRIPTIONS_GENERATED', | |
| 'GENERATING_IMAGES', | |
| 'COMPLETED' | |
| } | |
| # Page status states | |
| PAGE_STATUSES = { | |
| 'DRAFT', | |
| 'DESCRIPTION_GENERATED', | |
| 'GENERATING', | |
| 'COMPLETED', | |
| 'FAILED' | |
| } | |
| # Task status states | |
| TASK_STATUSES = { | |
| 'PENDING', | |
| 'PROCESSING', | |
| 'COMPLETED', | |
| 'FAILED' | |
| } | |
| # Task types | |
| TASK_TYPES = { | |
| 'GENERATE_DESCRIPTIONS', | |
| 'GENERATE_IMAGES', | |
| 'EXPORT_EDITABLE_PPTX' | |
| } | |
| def validate_project_status(status: str) -> bool: | |
| """Validate project status""" | |
| return status in PROJECT_STATUSES | |
| def validate_page_status(status: str) -> bool: | |
| """Validate page status""" | |
| return status in PAGE_STATUSES | |
| def validate_task_status(status: str) -> bool: | |
| """Validate task status""" | |
| return status in TASK_STATUSES | |
| def validate_task_type(task_type: str) -> bool: | |
| """Validate task type""" | |
| return task_type in TASK_TYPES | |
| def allowed_file(filename: str, allowed_extensions: Set[str]) -> bool: | |
| """Check if file extension is allowed""" | |
| return '.' in filename and \ | |
| filename.rsplit('.', 1)[1].lower() in allowed_extensions | |