"""API /api/results — lista trainings/tests + compare multi-training.""" from __future__ import annotations import uuid from typing import Optional from fastapi import APIRouter, Depends, HTTPException, Query from core import db, influx_client from core.auth import require_auth from core.config import settings router = APIRouter(prefix="/api/results", tags=["results"]) def _row(r): if r is None: return None d = dict(r) for k in ("queued_at", "started_at", "finished_at", "started_at", "ended_at"): if d.get(k) is not None and hasattr(d[k], "isoformat"): d[k] = d[k].isoformat() return d @router.get("") async def list_results( model_id: Optional[str] = Query(None), user=Depends(require_auth), ): where = [] args: list = [] if model_id: args.append(uuid.UUID(model_id)) where.append(f"model_id = ${len(args)}") sql = "SELECT * FROM trainings" if where: sql += " WHERE " + " AND ".join(where) sql += " ORDER BY finished_at DESC NULLS LAST, queued_at DESC LIMIT 200" rows = await db.fetch(sql, *args) return {"count": len(rows), "trainings": [_row(r) for r in rows]} @router.get("/{training_id}") async def get_result(training_id: str, user=Depends(require_auth)): row = await db.fetchrow("SELECT * FROM trainings WHERE id = $1", uuid.UUID(training_id)) if not row: raise HTTPException(404, "not found") # timeseries via Influx: loss per iter + cpu/mem flux = ( f'from(bucket:"{settings.influx_bucket}") ' f'|> range(start:-90d) ' f'|> filter(fn: (r) => r._measurement == "ml_training" and r.training_id == "{training_id}")' ) try: ts = await influx_client.query_flux(flux) except Exception: ts = [] return {"training": _row(row), "timeseries": ts} @router.get("/compare") async def compare( trainings: str = Query(..., description="comma-separated training IDs"), user=Depends(require_auth), ): ids = [s.strip() for s in trainings.split(",") if s.strip()] if len(ids) < 2: raise HTTPException(400, "at least 2 training IDs required") out = [] for tid in ids: try: tid_uuid = uuid.UUID(tid) except ValueError: continue row = await db.fetchrow("SELECT * FROM trainings WHERE id = $1", tid_uuid) if not row: continue flux = ( f'from(bucket:"{settings.influx_bucket}") ' f'|> range(start:-90d) ' f'|> filter(fn: (r) => r._measurement == "ml_training" and r.training_id == "{tid}")' ) try: ts = await influx_client.query_flux(flux) except Exception: ts = [] out.append({"training": _row(row), "timeseries": ts}) return {"results": out}