feat(investigation): preserve cumulative reference graphs
Accumulate investigation paths and preserve failed attempts across rounds. Keep graph annotations separate from execution state, synchronize the API contract, and consolidate documentation.
This commit is contained in:
@@ -7,7 +7,9 @@ from uuid import UUID
|
||||
from psycopg import AsyncConnection
|
||||
from psycopg import sql
|
||||
from psycopg.rows import dict_row
|
||||
from psycopg.types.json import Jsonb
|
||||
from fastapi import HTTPException
|
||||
from app.domain.schemas.operational_task import LEGACY_INVESTIGATION_NODE_ANNOTATIONS
|
||||
from app.infra.db.postgresql.scada import _SCADA_VIEW_SELECT, _device
|
||||
from app.infra.db.timescaledb.repositories.realtime import RealtimeRepository
|
||||
from app.infra.db.timescaledb.repositories.analysis import AnalysisResultsRepository
|
||||
@@ -329,6 +331,29 @@ async def task_context_summary(conn: AsyncConnection, *, project_id: UUID, task_
|
||||
return result
|
||||
|
||||
|
||||
def _reference_diagram_projection(stored_sql: str, stored_params: tuple, section: str) -> tuple[str, tuple]:
|
||||
"""Normalize only saved diagram annotations before any data_path or page projection."""
|
||||
if section not in {"progress", "report"}:
|
||||
raise ValueError("reference diagrams only belong to progress or report sections")
|
||||
path = "{data," + section + ",diagram,node_refs}"
|
||||
statement = f"""
|
||||
WITH reference_documents AS (
|
||||
{stored_sql}
|
||||
), reference_aliases AS (
|
||||
SELECT %s::jsonb AS annotations
|
||||
)
|
||||
SELECT CASE WHEN jsonb_typeof(snapshot#>'{path}')='array' THEN
|
||||
jsonb_set(snapshot,'{path}',COALESCE((
|
||||
SELECT jsonb_agg(CASE WHEN annotations ? (node->>'status')
|
||||
THEN jsonb_set(node,'{{status}}',annotations->(node->>'status'))
|
||||
ELSE node END ORDER BY position)
|
||||
FROM jsonb_array_elements(snapshot#>'{path}') WITH ORDINALITY AS nodes(node,position)
|
||||
),'[]'::jsonb)) ELSE snapshot END AS snapshot
|
||||
FROM reference_documents CROSS JOIN reference_aliases
|
||||
"""
|
||||
return statement, (*stored_params, Jsonb(LEGACY_INVESTIGATION_NODE_ANNOTATIONS))
|
||||
|
||||
|
||||
async def task_context_page(
|
||||
conn: AsyncConnection, *, project_id: UUID, task_id: UUID, section: str,
|
||||
report_revision: int | None, data_path: list[str], limit: int, offset: int,
|
||||
@@ -386,11 +411,16 @@ async def task_context_page(
|
||||
stored = """SELECT jsonb_build_object('section','progress','data',p.document) AS snapshot
|
||||
FROM workbench.operational_task_progress p
|
||||
JOIN workbench.operational_task_runs r ON r.project_id=p.project_id AND r.task_id=p.task_id AND r.run_id=p.run_id
|
||||
JOIN workbench.operational_tasks t ON t.project_id=p.project_id AND t.task_id=p.task_id AND t.execution_generation=r.generation
|
||||
WHERE p.project_id=%s AND p.task_id=%s"""
|
||||
JOIN workbench.operational_tasks t ON t.project_id=p.project_id AND t.task_id=p.task_id
|
||||
WHERE p.project_id=%s AND p.task_id=%s AND r.generation<=t.execution_generation
|
||||
AND (t.execution_generation=r.generation
|
||||
OR jsonb_typeof(p.document#>'{progress,diagram,edges}')='array')
|
||||
ORDER BY (t.execution_generation=r.generation) DESC,r.generation DESC,p.revision DESC LIMIT 1"""
|
||||
params = (project_id, task_id)
|
||||
else:
|
||||
raise HTTPException(422, "Unknown task context section")
|
||||
if section in {"progress", "report"}:
|
||||
stored, params = _reference_diagram_projection(stored, params, section)
|
||||
return await _snapshot_page(conn, stored_sql=stored, stored_params=params,
|
||||
data_path=data_path, limit=limit, offset=projection_offset, mcp_sources=[], max_bytes=8000)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user