254 lines
5.6 KiB
Python
254 lines
5.6 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", "purged"] = "active"
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
purge_after: datetime | 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 = ""
|
|
created_at: datetime
|
|
|
|
|
|
class ProductWordcloudArchiveRecord(BaseModel):
|
|
archive_id: str
|
|
product_id: str
|
|
version_id: str
|
|
archive_path: str = ""
|
|
created_at: datetime
|
|
|
|
|
|
class ProductWordcloudArchiveResponse(ProductWordcloudArchiveRecord):
|
|
source_job_id: str
|
|
db_path: str
|
|
|
|
|
|
class ProductVersionArchiveResponse(ProductVersionRecord):
|
|
design_preview_path: str
|
|
wordcloud_count: int
|
|
wordcloud_archives: list[ProductWordcloudArchiveResponse] = 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
|