ConverTA / backend /api /run_export_routes.py
MikelWL's picture
Feat: persist sealed runs to SQLite
a7fd580
"""Export sealed runs using server-canonical persisted data."""
from __future__ import annotations
from datetime import datetime
from typing import Any, Dict, List
from fastapi import APIRouter, HTTPException
from fastapi.responses import Response
from .export_routes import ExportMessage, ExportRequest, _export_json_bytes, _export_xlsx_bytes, _filename
from .storage_service import get_run_store
router = APIRouter(prefix="", tags=["runs", "export"])
def _payload_from_run(*, run_id: str, messages: List[Dict[str, Any]], resources: Dict[str, Any]) -> ExportRequest:
exported_at = datetime.now().isoformat()
export_messages = [
ExportMessage(
role=str(m.get("role") or ""),
persona=m.get("persona"),
time=m.get("timestamp"),
text=str(m.get("content") or ""),
)
for m in (messages or [])
]
return ExportRequest(
conversation_id=run_id,
exported_at=exported_at,
messages=export_messages,
resources=resources or {},
)
@router.get("/runs/{run_id}/export/json")
async def export_run_json(run_id: str) -> Response:
store = get_run_store()
record = await store.get_run(run_id)
if record is None:
raise HTTPException(status_code=404, detail="Run not found")
resources = record.analyses.get("resource_agent_v2") or {}
payload = _payload_from_run(run_id=record.run_id, messages=record.messages, resources=resources)
data = _export_json_bytes(payload)
filename = _filename(payload.conversation_id, payload.exported_at or datetime.now().isoformat(), "json")
headers = {"Content-Disposition": f'attachment; filename="{filename}"'}
return Response(content=data, media_type="application/json", headers=headers)
@router.get("/runs/{run_id}/export/xlsx")
async def export_run_xlsx(run_id: str) -> Response:
store = get_run_store()
record = await store.get_run(run_id)
if record is None:
raise HTTPException(status_code=404, detail="Run not found")
resources = record.analyses.get("resource_agent_v2") or {}
payload = _payload_from_run(run_id=record.run_id, messages=record.messages, resources=resources)
try:
data = _export_xlsx_bytes(payload)
except Exception as e:
raise HTTPException(status_code=500, detail=f"openpyxl not available: {e}")
filename = _filename(payload.conversation_id, payload.exported_at or datetime.now().isoformat(), "xlsx")
headers = {"Content-Disposition": f'attachment; filename="{filename}"'}
return Response(
content=data,
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
headers=headers,
)