MediaRouter / app /projects /schemas.py
basyx's picture
Upload 340 files
3493993 verified
Raw
History Blame Contribute Delete
4.86 kB
from __future__ import annotations
import json
import re
from datetime import datetime
from enum import Enum
from typing import Any, Literal
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
PROJECT_NAME_MAX_LENGTH = 200
PROJECT_DESCRIPTION_MAX_LENGTH = 4000
PROJECT_METADATA_MAX_BYTES = 16 * 1024
class ProjectStatus(str, Enum):
ACTIVE = "active"
ARCHIVED = "archived"
def normalize_project_name(value: str) -> str:
normalized = re.sub(r"\s+", " ", value).strip()
if not normalized:
raise ValueError("Project name cannot be blank")
if len(normalized) > PROJECT_NAME_MAX_LENGTH:
raise ValueError(f"Project name cannot exceed {PROJECT_NAME_MAX_LENGTH} characters")
return normalized
def validate_metadata_size(value: dict[str, Any]) -> dict[str, Any]:
try:
encoded = json.dumps(
value, ensure_ascii=False, separators=(",", ":"), allow_nan=False
).encode("utf-8")
except (TypeError, ValueError) as exc:
raise ValueError("Project metadata must be valid JSON") from exc
if len(encoded) > PROJECT_METADATA_MAX_BYTES:
raise ValueError(f"Project metadata cannot exceed {PROJECT_METADATA_MAX_BYTES} bytes")
return value
class ProjectCreate(BaseModel):
model_config = ConfigDict(extra="forbid")
name: str
description: str | None = Field(default=None, max_length=PROJECT_DESCRIPTION_MAX_LENGTH)
thumbnail_asset_id: str | None = Field(default=None, min_length=1, max_length=36)
metadata: dict[str, Any] = Field(default_factory=dict)
_normalize_name = field_validator("name")(normalize_project_name)
_validate_metadata = field_validator("metadata")(validate_metadata_size)
class ProjectUpdate(BaseModel):
model_config = ConfigDict(extra="forbid")
name: str | None = None
description: str | None = Field(default=None, max_length=PROJECT_DESCRIPTION_MAX_LENGTH)
thumbnail_asset_id: str | None = Field(default=None, min_length=1, max_length=36)
metadata: dict[str, Any] | None = None
status: ProjectStatus | None = None
@field_validator("name")
@classmethod
def normalize_name(cls, value: str | None) -> str | None:
if value is None:
return value
return normalize_project_name(value)
@field_validator("metadata")
@classmethod
def validate_metadata(cls, value: dict[str, Any] | None) -> dict[str, Any] | None:
return None if value is None else validate_metadata_size(value)
@model_validator(mode="after")
def validate_patch(self) -> ProjectUpdate:
if not self.model_fields_set:
raise ValueError("At least one project field must be supplied")
if "name" in self.model_fields_set and self.name is None:
raise ValueError("Project name cannot be null")
if "metadata" in self.model_fields_set and self.metadata is None:
raise ValueError("Project metadata cannot be null")
if "status" in self.model_fields_set and self.status is None:
raise ValueError("Project status cannot be null")
return self
class ProjectResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: str
workspace_id: str
name: str
description: str | None
status: ProjectStatus
thumbnail_asset_id: str | None
brand_kit_id: str | None = None
brand_kit_version_id: str | None = None
metadata: dict[str, Any]
created_by: str
created_at: datetime
updated_at: datetime
archived_at: datetime | None
class ProjectListResponse(BaseModel):
items: list[ProjectResponse]
next_cursor: str | None = None
limit: int = Field(ge=1, le=100)
class ProjectAssetAttach(BaseModel):
model_config = ConfigDict(extra="forbid")
asset_id: str = Field(min_length=1, max_length=36)
class ProjectAssetResponse(BaseModel):
id: str
project_id: str
request_id: str
filename: str
mime_type: str
file_size: int
metadata: dict[str, Any]
created_by_user_id: str | None
created_at: datetime
updated_at: datetime
class ProjectAssetListResponse(BaseModel):
items: list[ProjectAssetResponse]
class ProjectGenerationJobAttach(BaseModel):
model_config = ConfigDict(extra="forbid")
generation_job_id: str = Field(min_length=1, max_length=36)
class ProjectGenerationJobResponse(BaseModel):
id: str
project_id: str
job_type: Literal["generation"] = "generation"
generation_request_id: str
provider: str
status: str
output_asset_id: str | None
error_code: str | None
created_at: datetime
started_at: datetime | None
completed_at: datetime | None
updated_at: datetime
attached_at: datetime
class ProjectGenerationJobListResponse(BaseModel):
items: list[ProjectGenerationJobResponse]