Model/backend/app/modelling/run_tasks.py
Khalim Conn-Kowlessar bf8647c9b5 The 409 guard and batch creation read as methods on ModellingRunTasks 🟪
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 12:16:42 +00:00

58 lines
2.2 KiB
Python

"""The distributor's view of the app-owned task's sub_tasks (ADR-0055).
Intention-revealing methods over parameterised SQL. SQL rather than the
SQLModel mirrors because the FastAPI app registers the legacy
``backend.app.db.models`` mirrors of ``sub_task``, and importing the
``infrastructure.postgres`` mirror of the same table into one process
double-registers it and crashes the app at import (contained here until the
DDD cut-over).
"""
import json
from datetime import datetime, timezone
from typing import Any
from uuid import UUID
from sqlalchemy import text
from sqlmodel import Session
class ModellingRunTasks:
def __init__(self, session: Session) -> None:
self._session = session
def already_distributed(self, task_id: UUID) -> bool:
"""Whether the task already has sub_tasks — i.e. a distributor has
already fanned it out (the 409 guard against double-submits)."""
row = self._session.connection().execute(
text("SELECT 1 FROM sub_task WHERE task_id = :task_id LIMIT 1"),
{"task_id": task_id},
).first()
return row is not None
def create_batch_subtasks(
self, task_id: UUID, messages: list[dict[str, Any]]
) -> None:
"""Pre-create one ``waiting`` sub_task per batch message under the
app-owned task. Each sub_task's inputs are its exact message payload
(minus the self-referential subtask_id) — the fixed progress
denominator and the batch's re-run recipe."""
now = datetime.now(timezone.utc)
self._session.connection().execute(
text(
"INSERT INTO sub_task (id, task_id, status, inputs, updated_at)"
" VALUES (:id, :task_id, 'waiting', :inputs, :updated_at)"
),
[
{
"id": message["subtask_id"],
"task_id": task_id,
"inputs": json.dumps(
{k: v for k, v in message.items() if k != "subtask_id"}
),
"updated_at": now,
}
for message in messages
],
)
self._session.commit()