Files

275 lines
6.3 KiB
Python

from __future__ import annotations
from datetime import datetime
from pathlib import Path
from typing import Any, Literal
from pydantic import BaseModel, Field
class ProductInput(BaseModel):
source: Literal["manual", "external"]
external_product_id: str | None = None
name: str
sku: str = ""
specification: str = ""
def __init__(
self,
source: Literal["manual", "external"] | None = None,
external_product_id: str | None = None,
name: str | None = None,
sku: str = "",
specification: str = "",
**data: Any,
) -> None:
if source is not None:
data["source"] = source
if external_product_id is not None or "external_product_id" not in data:
data["external_product_id"] = external_product_id
if name is not None:
data["name"] = name
if sku or "sku" not in data:
data["sku"] = sku
if specification or "specification" not in data:
data["specification"] = specification
super().__init__(**data)
class ProductRecord(BaseModel):
product_id: str
source: Literal["manual", "external"]
external_product_id: str | None = None
name: str
sku: str = ""
specification: str = ""
status: Literal["active", "pending_cleanup", "failed_cleanup", "purged"] = "active"
created_at: datetime
updated_at: datetime
purge_after: datetime | None = None
cover_image_id: str | None = None
class ProductVersionRecord(BaseModel):
version_id: str
product_id: str
version: str = ""
metadata: dict[str, Any] = Field(default_factory=dict)
created_at: datetime
class ProductImageRecord(BaseModel):
image_id: str
product_id: str
version_id: str
image_path: str = ""
image_type: str = ""
is_cover: bool = False
created_at: datetime
class ProductWordcloudArchiveRecord(BaseModel):
archive_id: str
product_id: str
version_id: str
archive_path: str = ""
source_job_id: str = ""
source_asset_id: str = ""
db_checksum: str = ""
created_at: datetime
class ProductWordcloudArchiveResponse(ProductWordcloudArchiveRecord):
db_path: str
class ProductVersionArchiveResponse(ProductVersionRecord):
design_preview_path: str
wordcloud_count: int
wordcloud_archives: list[ProductWordcloudArchiveResponse] = Field(default_factory=list)
class ProductImageResponse(ProductImageRecord):
image_url: str = ""
class ProductVersionDetailResponse(ProductVersionRecord):
design_preview_image_id: str | None = None
design_preview_url: str = ""
design_preview_path: str = ""
wordcloud_count: int = 0
wordcloud_archives: list[ProductWordcloudArchiveResponse] = Field(default_factory=list)
class ProductDetailResponse(ProductRecord):
images: list[ProductImageResponse] = Field(default_factory=list)
versions: list[ProductVersionDetailResponse] = Field(default_factory=list)
class JobCreateResponse(BaseModel):
job_id: str
class JobEvent(BaseModel):
type: Literal["log", "status"]
stage: str
progress_percent: int = Field(ge=0, le=100)
message: str
timestamp: datetime
elapsed_seconds: float | None = None
class JobStatus(BaseModel):
job_id: str
status: Literal["queued", "running", "success", "failed"]
stage: str
progress_percent: int = Field(ge=0, le=100)
message: str
created_at: datetime
updated_at: datetime
artifacts: dict[str, str]
error: str = ""
elapsed_seconds: float | None = None
class JobDetail(BaseModel):
status: JobStatus
recent_events: list[JobEvent]
class JobResult(BaseModel):
job_id: str
status: Literal["queued", "running", "success", "failed"]
image_url: str = ""
svg_url: str = ""
svg_stroke_url: str = ""
db_url: str = ""
metrics_url: str = ""
elapsed_seconds: float | None = None
class WordLocation(BaseModel):
id: int
name: str
x: int
y: int
font_size: int
color: str = ""
orientation: Literal["horizontal", "vertical"] = "horizontal"
box_x: int
box_y: int
box_width: int
box_height: int
class JobLocationSearchResult(BaseModel):
job_id: str
query: str = ""
mode: Literal["exact", "contains"] = "exact"
total: int = 0
canvas_width: int = 0
canvas_height: int = 0
matches: list[WordLocation] = Field(default_factory=list)
class JobPaths(BaseModel):
root: Path
input_dir: Path
output_dir: Path
mask_path: Path
excel_path: Path
config_path: Path
class Template(BaseModel):
id: str
name: str
width: int
height: int
aspect_ratio: str
description: str = ""
class Asset(BaseModel):
asset_id: str
name: str
type: str
mime_type: str
width: int
height: int
file_size: int
file_url: str
job_id: str = ""
created_at: datetime
class LineSpacingAnalysisRequest(BaseModel):
percentile: float = Field(default=3, ge=0, le=100)
elementWidth: float = Field(gt=0)
elementHeight: float = Field(gt=0)
sampleStep: float = Field(default=2, gt=0)
class LineSpacingClosestPoints(BaseModel):
ax: float
ay: float
bx: float
by: float
class LineSpacingAnalysisSummary(BaseModel):
percentile: float
spacingPx: float
spacingMm: float
minSpacingPx: float
minSpacingMm: float
sampleStep: float
curveCount: int
segmentCount: int
nearestCount: int
sourceWidth: float
sourceHeight: float
elementWidth: float
elementHeight: float
computedAt: str
closestPoints: LineSpacingClosestPoints | None = None
class DesignTemplate(BaseModel):
template_id: str
name: str
description: str = ""
document: dict
reference_asset_ids: list[str] = Field(default_factory=list)
cover_asset_id: str = ""
created_at: datetime
updated_at: datetime
class Project(BaseModel):
project_id: str
name: str
template_id: str
background_color: str
stickers: list[dict] = Field(default_factory=list)
created_at: datetime
updated_at: datetime
class ProjectSummary(BaseModel):
project_id: str
name: str
template_id: str
background_color: str
sticker_count: int = 0
created_at: datetime
updated_at: datetime
class Font(BaseModel):
font_id: str
name: str
filename: str
file_size: int
created_at: datetime