"""Celery Canvas workflow builder. Translates WorkflowDefinition config into a Celery Canvas (chain/group/chord). """ from __future__ import annotations import logging from celery import chain, group logger = logging.getLogger(__name__) def dispatch_workflow( workflow_type: str, order_line_id: str, params: dict | None = None, ) -> str: """Build and dispatch a Celery Canvas workflow. Returns the Celery task/group ID.""" params = params or {} builders = { "still": _build_still, "turntable": _build_turntable, "multi_angle": _build_multi_angle, "still_with_exports": _build_still_with_exports, } builder = builders.get(workflow_type) if not builder: raise ValueError(f"Unknown workflow type: {workflow_type!r}") canvas = builder(order_line_id, params) result = canvas.apply_async() return str(result.id) def _build_still(order_line_id: str, params: dict): """Still render: resolves STEP path from order_line DB record.""" from app.domains.rendering.tasks import render_order_line_still_task return chain( render_order_line_still_task.si(order_line_id, **params) ) def _build_turntable(order_line_id: str, params: dict): """Turntable animation: requires step_path + output_dir in params.""" from app.domains.rendering.tasks import render_turntable_task step_path = params.get("step_path") output_dir = params.get("output_dir") if not step_path or not output_dir: raise ValueError( "turntable workflow requires 'step_path' and 'output_dir' in params" ) remaining = {k: v for k, v in params.items() if k not in ("step_path", "output_dir")} return chain( render_turntable_task.si(step_path, output_dir, **remaining) ) def _build_multi_angle(order_line_id: str, params: dict): """Multi-angle stills: renders the same order_line from multiple rotation_z angles.""" from app.domains.rendering.tasks import render_order_line_still_task angles = params.pop("angles", [0, 45, 90]) return group( render_order_line_still_task.si(order_line_id, rotation_z=float(angle), **params) for angle in angles ) def _build_still_with_exports(order_line_id: str, params: dict): """Still render + .blend export. Pipeline: render_order_line_still_task → export_blend_for_order_line_task """ from app.domains.rendering.tasks import ( render_order_line_still_task, export_blend_for_order_line_task, ) return chain( render_order_line_still_task.si(order_line_id, **params), export_blend_for_order_line_task.si(order_line_id), )