"""Serialize tasks with vessel names for API responses.""" from __future__ import annotations from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.models.inventory import Vessel from app.models.task import Task from app.schemas import TaskDetail, TaskOut, TaskOverviewOut async def vessel_name_map(db: AsyncSession, vessel_ids: set[int]) -> dict[int, str]: if not vessel_ids: return {} res = await db.execute(select(Vessel).where(Vessel.id.in_(vessel_ids))) return {v.id: v.name for v in res.scalars().all()} def task_to_out(task: Task, *, vessel_name: str | None = None) -> TaskOut: base = TaskOut.model_validate(task) name = vessel_name if name is None and task.report: name = task.report.get("vessel_name") return base.model_copy(update={"vessel_name": name}) def task_to_detail(task: Task, *, vessel_name: str | None = None) -> TaskDetail: base = TaskDetail.model_validate(task) name = vessel_name if name is None and task.report: name = task.report.get("vessel_name") return base.model_copy(update={"vessel_name": name}) def _report_summary(report: dict | None) -> tuple[str | None, str | None]: if not report: return None, None summary = report.get("executive_summary") or report.get("summary") findings = report.get("findings") or [] key_finding = None if isinstance(findings, list) and findings: first = findings[0] if isinstance(first, dict): key_finding = first.get("summary") or first.get("description") else: key_finding = str(first) if not key_finding: key_finding = report.get("root_cause") or report.get("resolution") return summary, key_finding def task_to_overview(task: Task, *, vessel_name: str | None = None) -> TaskOverviewOut: report = task.report or {} name = vessel_name or report.get("vessel_name") summary, key_finding = _report_summary(report) return TaskOverviewOut( id=task.id, title=task.title, issue=task.issue, status=task.status, vessel_id=task.vessel_id, vessel_name=name, summary=summary, key_finding=key_finding, devices_checked=list(report.get("devices_checked") or []), resolved=report.get("resolved"), memory_id=task.memory_id or report.get("memory_id"), obsidian_path=task.obsidian_path or report.get("obsidian_path"), obsidian_push_error=report.get("obsidian_push_error"), run_cost_usd=report.get("run_cost_usd"), created_at=task.created_at, updated_at=task.updated_at, ) async def tasks_to_out_list(db: AsyncSession, tasks: list[Task]) -> list[TaskOut]: ids = {t.vessel_id for t in tasks if t.vessel_id} vmap = await vessel_name_map(db, ids) return [task_to_out(t, vessel_name=vmap.get(t.vessel_id) if t.vessel_id else None) for t in tasks] async def tasks_to_overview_list(db: AsyncSession, tasks: list[Task]) -> list[TaskOverviewOut]: ids = {t.vessel_id for t in tasks if t.vessel_id} vmap = await vessel_name_map(db, ids) return [task_to_overview(t, vessel_name=vmap.get(t.vessel_id) if t.vessel_id else None) for t in tasks]