| """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, |
| ) |
|
|
|
|