chore: snapshot workflow migration progress

This commit is contained in:
2026-04-12 11:49:04 +02:00
parent 0cd02513d5
commit 3e810c74a3
163 changed files with 31774 additions and 2753 deletions
+34 -1
View File
@@ -1,17 +1,20 @@
"""Render Templates API — CRUD + .blend file upload/download + material library."""
import json
import uuid
import shutil
from datetime import datetime
from pathlib import Path
from typing import Any
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form, status
from fastapi.responses import FileResponse
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, update as sql_update, delete as sql_delete
from pydantic import BaseModel
from pydantic import BaseModel, TypeAdapter, ValidationError
from app.database import get_db
from app.config import settings as app_settings
from app.domains.rendering.workflow_node_registry import WorkflowNodeFieldDefinition
from app.models.user import User
from app.models.render_template import RenderTemplate
from app.models.output_type import OutputType
@@ -46,6 +49,7 @@ class RenderTemplateOut(BaseModel):
lighting_only: bool
shadow_catcher_enabled: bool
camera_orbit: bool
workflow_input_schema: list[WorkflowNodeFieldDefinition]
is_active: bool
created_at: str
updated_at: str
@@ -62,6 +66,7 @@ class RenderTemplateUpdate(BaseModel):
lighting_only: bool | None = None
shadow_catcher_enabled: bool | None = None
camera_orbit: bool | None = None
workflow_input_schema: list[WorkflowNodeFieldDefinition] | None = None
is_active: bool | None = None
@@ -72,6 +77,29 @@ class MaterialLibraryInfo(BaseModel):
path: str | None = None
_workflow_input_schema_adapter = TypeAdapter(list[WorkflowNodeFieldDefinition])
def _normalize_workflow_input_schema(schema: Any) -> list[dict[str, Any]]:
if schema in (None, "", "null"):
return []
try:
validated = _workflow_input_schema_adapter.validate_python(schema)
except ValidationError as exc:
raise HTTPException(status_code=422, detail={"workflow_input_schema": exc.errors()}) from exc
return [field.model_dump(mode="json") for field in validated]
def _parse_form_workflow_input_schema(raw_schema: str | None) -> list[dict[str, Any]]:
if raw_schema in (None, "", "null"):
return []
try:
payload = json.loads(raw_schema)
except json.JSONDecodeError as exc:
raise HTTPException(status_code=422, detail="workflow_input_schema must be valid JSON") from exc
return _normalize_workflow_input_schema(payload)
def _to_out(t: RenderTemplate) -> dict:
ot_name = None
if t.output_type:
@@ -94,6 +122,7 @@ def _to_out(t: RenderTemplate) -> dict:
"lighting_only": t.lighting_only,
"shadow_catcher_enabled": t.shadow_catcher_enabled,
"camera_orbit": t.camera_orbit,
"workflow_input_schema": t.workflow_input_schema or [],
"is_active": t.is_active,
"created_at": t.created_at.isoformat() if t.created_at else "",
"updated_at": t.updated_at.isoformat() if t.updated_at else "",
@@ -126,6 +155,7 @@ async def create_render_template(
lighting_only: bool = Form(False),
shadow_catcher_enabled: bool = Form(False),
camera_orbit: bool = Form(True),
workflow_input_schema: str | None = Form(None),
user: User = Depends(require_admin_or_pm),
db: AsyncSession = Depends(get_db),
):
@@ -182,6 +212,7 @@ async def create_render_template(
lighting_only=lighting_only,
shadow_catcher_enabled=shadow_catcher_enabled,
camera_orbit=camera_orbit,
workflow_input_schema=_parse_form_workflow_input_schema(workflow_input_schema),
)
db.add(tmpl)
await db.flush()
@@ -224,6 +255,8 @@ async def update_render_template(
# Normalise empty strings to None for nullable fields
if "category_key" in updates and updates["category_key"] in ("", "null"):
updates["category_key"] = None
if "workflow_input_schema" in updates:
updates["workflow_input_schema"] = _normalize_workflow_input_schema(updates["workflow_input_schema"])
# Handle M2M output_type_ids
new_ot_ids: list[str] | None = updates.pop("output_type_ids", None)