SPB Git forge

spb/api-ka

Public

API-KA — plateforme centrale : collecte quotidienne des 8 services KA, historisation append-only et API publique sur www.api-ka.com

48commits 1branches 0releases
5.9 MBsize
maindefault branch
19 days agolast push
Python 60.9% HTML 21% TypeScript 7.3% JavaScript 5.2% CSS 4.8% Shell 0.8%
2.5 KB · 79 lines python
Raw Blame History
1# ============================================2# Projet   : API-KA3# Fichier  : src/api/routes/runs.py4# Node     : m3u96b5# Author   : Simon-Pierre Boucher6# Contact  : contact@spboucher.ai7# Date     : 2026-08-168# ============================================9"""Route /api/v1/runs : historique des collectes, filtrable par service et statut."""1011from __future__ import annotations1213from typing import Any1415from fastapi import APIRouter, Depends, HTTPException, Query16from sqlalchemy import func, select17from sqlalchemy.orm import Session1819from src.api.routes import envelope20from src.config import SERVICES21from src.database.db import get_db22from src.database.models import CollectionRun2324router = APIRouter(prefix="/api/v1", tags=["runs"])2526VALID_STATUSES = ("success", "failed", "retried")272829@router.get("/runs")30def list_runs(31    service: str | None = Query(None, description="Filtrer par service KA"),32    status: str | None = Query(None, description="success / failed / retried"),33    page: int = Query(1, ge=1),34    limit: int = Query(100, ge=1, le=500),35    db: Session = Depends(get_db),36) -> dict[str, Any]:37    """Historique des runs de collecte, du plus récent au plus ancien."""38    if service is not None and service not in SERVICES:39        raise HTTPException(status_code=404, detail=f"Service inconnu : {service}")40    if status is not None and status not in VALID_STATUSES:41        raise HTTPException(42            status_code=422,43            detail=f"Statut invalide : {status}. Valides : {', '.join(VALID_STATUSES)}",44        )4546    base = select(CollectionRun)47    if service:48        base = base.where(CollectionRun.service == service)49    if status:50        base = base.where(CollectionRun.status == status)5152    total = db.execute(select(func.count()).select_from(base.subquery())).scalar() or 053    rows = (54        db.execute(55            base.order_by(CollectionRun.started_at.desc())56            .offset((page - 1) * limit)57            .limit(limit)58        )59        .scalars()60        .all()61    )6263    data = [64        {65            "id": run.id,66            "service": run.service,67            "date_key": run.date_key.isoformat(),68            "status": run.status,69            "records_count": run.records_count,70            "duration_seconds": run.duration_seconds,71            "error_message": run.error_message,72            "node": run.node,73            "started_at": run.started_at.isoformat(),74            "finished_at": run.finished_at.isoformat(),75        }76        for run in rows77    ]78    return envelope(data, page=page, limit=limit, total=total)79