- Remove |safe from |tojson in HTML attributes (x-data) - quotes must become " for browsers to parse correctly - Update LANG-002 and LANG-003 architecture rules to document correct |tojson usage patterns: - HTML attributes: |tojson (no |safe) - Script blocks: |tojson|safe - Fix validator to warn when |tojson|safe is used in x-data (breaks HTML attribute parsing) - Improve code quality across services, APIs, and tests 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
190 lines
5.2 KiB
Python
190 lines
5.2 KiB
Python
# models/schema/media.py
|
|
"""
|
|
Media/file management Pydantic schemas for API validation and responses.
|
|
|
|
This module provides schemas for:
|
|
- Media library listing
|
|
- File upload responses
|
|
- Media metadata operations
|
|
- Media usage tracking
|
|
"""
|
|
|
|
from datetime import datetime
|
|
from typing import Any
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
# ============================================================================
|
|
# SHARED RESPONSE SCHEMAS
|
|
# ============================================================================
|
|
|
|
|
|
class MessageResponse(BaseModel):
|
|
"""Generic message response for simple operations."""
|
|
|
|
message: str
|
|
|
|
|
|
# ============================================================================
|
|
# MEDIA ITEM SCHEMAS
|
|
# ============================================================================
|
|
|
|
|
|
class MediaItemResponse(BaseModel):
|
|
"""Single media item response."""
|
|
|
|
id: int
|
|
filename: str
|
|
original_filename: str | None = None
|
|
file_url: str
|
|
thumbnail_url: str | None = None
|
|
media_type: str # image, video, document
|
|
mime_type: str | None = None
|
|
file_size: int | None = None # bytes
|
|
width: int | None = None # for images/videos
|
|
height: int | None = None # for images/videos
|
|
alt_text: str | None = None
|
|
description: str | None = None
|
|
folder: str | None = None
|
|
metadata: dict[str, Any] | None = None
|
|
created_at: datetime
|
|
updated_at: datetime | None = None
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
class MediaListResponse(BaseModel):
|
|
"""Paginated list of media items."""
|
|
|
|
media: list[MediaItemResponse] = []
|
|
total: int = 0
|
|
skip: int = 0
|
|
limit: int = 100
|
|
message: str | None = None
|
|
|
|
|
|
# ============================================================================
|
|
# UPLOAD RESPONSE SCHEMAS
|
|
# ============================================================================
|
|
|
|
|
|
class MediaUploadResponse(BaseModel):
|
|
"""Response for single file upload."""
|
|
|
|
id: int | None = None
|
|
file_url: str | None = None
|
|
thumbnail_url: str | None = None
|
|
filename: str | None = None
|
|
file_size: int | None = None
|
|
media_type: str | None = None
|
|
message: str | None = None
|
|
|
|
|
|
class UploadedFileInfo(BaseModel):
|
|
"""Information about a successfully uploaded file."""
|
|
|
|
id: int
|
|
filename: str
|
|
file_url: str
|
|
thumbnail_url: str | None = None
|
|
|
|
|
|
class FailedFileInfo(BaseModel):
|
|
"""Information about a failed file upload."""
|
|
|
|
filename: str
|
|
error: str
|
|
|
|
|
|
class MultipleUploadResponse(BaseModel):
|
|
"""Response for multiple file upload."""
|
|
|
|
uploaded_files: list[UploadedFileInfo] = []
|
|
failed_files: list[FailedFileInfo] = []
|
|
total_uploaded: int = 0
|
|
total_failed: int = 0
|
|
message: str | None = None
|
|
|
|
|
|
# ============================================================================
|
|
# MEDIA DETAIL SCHEMAS
|
|
# ============================================================================
|
|
|
|
|
|
class MediaDetailResponse(BaseModel):
|
|
"""Detailed media item response with usage info."""
|
|
|
|
id: int | None = None
|
|
filename: str | None = None
|
|
original_filename: str | None = None
|
|
file_url: str | None = None
|
|
thumbnail_url: str | None = None
|
|
media_type: str | None = None
|
|
mime_type: str | None = None
|
|
file_size: int | None = None
|
|
width: int | None = None
|
|
height: int | None = None
|
|
alt_text: str | None = None
|
|
description: str | None = None
|
|
folder: str | None = None
|
|
metadata: dict[str, Any] | None = None
|
|
created_at: datetime | None = None
|
|
updated_at: datetime | None = None
|
|
message: str | None = None
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
# ============================================================================
|
|
# MEDIA UPDATE SCHEMAS
|
|
# ============================================================================
|
|
|
|
|
|
class MediaMetadataUpdate(BaseModel):
|
|
"""Request model for updating media metadata."""
|
|
|
|
filename: str | None = Field(None, max_length=255)
|
|
alt_text: str | None = Field(None, max_length=500)
|
|
description: str | None = None
|
|
folder: str | None = Field(None, max_length=100)
|
|
metadata: dict[str, Any] | None = None
|
|
|
|
|
|
# ============================================================================
|
|
# MEDIA USAGE SCHEMAS
|
|
# ============================================================================
|
|
|
|
|
|
class ProductUsageInfo(BaseModel):
|
|
"""Information about product using this media."""
|
|
|
|
product_id: int
|
|
product_name: str
|
|
usage_type: str # main_image, gallery, variant, etc.
|
|
|
|
|
|
class MediaUsageResponse(BaseModel):
|
|
"""Response showing where media is being used."""
|
|
|
|
media_id: int | None = None
|
|
products: list[ProductUsageInfo] = []
|
|
other_usage: list[dict[str, Any]] = []
|
|
total_usage_count: int = 0
|
|
message: str | None = None
|
|
|
|
|
|
# ============================================================================
|
|
# MEDIA OPTIMIZATION SCHEMAS
|
|
# ============================================================================
|
|
|
|
|
|
class OptimizationResultResponse(BaseModel):
|
|
"""Response for media optimization operation."""
|
|
|
|
media_id: int | None = None
|
|
original_size: int | None = None
|
|
optimized_size: int | None = None
|
|
savings_percent: float | None = None
|
|
optimized_url: str | None = None
|
|
message: str | None = None
|