Kazeia-central/kazeia_central/features/fleet/router.py

95 lines
3.5 KiB
Python

"""Router flotte : découverte tablettes, labels patients, télémétrie par device
(lecture provider) et opérations parc (fan-out OTA)."""
from __future__ import annotations
from fastapi import APIRouter
from pydantic import BaseModel
from ...core.context import Ctx
from .service import device_overview, fan_out
class _Label(BaseModel):
label: str
def make_router(ctx: Ctx) -> APIRouter:
r = APIRouter(prefix="/api", tags=["fleet"])
g, now = ctx.guard, ctx.now_ms
# ---- parc + labels ----------------------------------------------------
@r.get("/devices")
def devices():
devs = [d.__dict__ for d in g(ctx.adb.devices)]
if ctx.store.is_unlocked: # enrichit du label patient
labels = ctx.store.device_labels()
for d in devs:
d["label"] = labels.get(d["serial"])
return devs
@r.put("/devices/{serial}/label")
def set_label(serial: str, body: _Label):
g(lambda: ctx.store.set_device_label(serial, body.label, now=now()))
ctx.store.audit("set_label", actor=ctx.operator, target=serial, now=now()) # pas de PII en clair
return {"serial": serial, "label": body.label}
# ---- télémétrie par tablette (lecture provider) -----------------------
@r.get("/devices/{serial}/state")
def state(serial: str):
return g(lambda: ctx.client(serial).state()).model_dump()
@r.get("/devices/{serial}/turns")
def turns(serial: str):
return [t.model_dump() for t in g(lambda: ctx.client(serial).turns())]
@r.get("/devices/{serial}/crashes")
def crashes(serial: str):
return [c.model_dump() for c in g(lambda: ctx.client(serial).crashes())]
@r.get("/devices/{serial}/models")
def models(serial: str):
return [x.model_dump() for x in g(lambda: ctx.client(serial).models())]
@r.get("/devices/{serial}/rag/status")
def rag_status(serial: str):
return g(lambda: ctx.client(serial).rag_status()).model_dump()
@r.get("/devices/{serial}/rag/index")
def rag_index(serial: str):
return [d.model_dump() for d in g(lambda: ctx.client(serial).rag_index())]
@r.get("/devices/{serial}/updates")
def updates(serial: str):
return g(lambda: ctx.client(serial).updates()).model_dump()
@r.get("/devices/{serial}/profiles")
def profiles(serial: str):
return [p.model_dump() for p in g(lambda: ctx.client(serial).profiles())]
@r.get("/devices/{serial}/conversations")
def sessions(serial: str, profile_id: str | None = None):
return [s.model_dump() for s in g(lambda: ctx.client(serial).sessions(profile_id))]
# ---- opérations parc (fan-out, rapport par device §9) -----------------
@r.get("/fleet/overview")
def fleet_overview():
report = fan_out(ctx.adb, lambda s: device_overview(ctx.adb, s))
labels = ctx.store.device_labels() if ctx.store.is_unlocked else {}
for x in report:
x["label"] = labels.get(x["serial"])
return report
@r.post("/fleet/update-check")
def fleet_update_check():
return fan_out(ctx.adb, lambda s: ctx.client(s).update_check())
@r.post("/fleet/update-install")
def fleet_update_install():
report = fan_out(ctx.adb, lambda s: ctx.client(s).update_install())
ctx.store.audit("fleet_update_install", actor=ctx.operator,
detail={"devices": [x["serial"] for x in report if x["ok"]]}, now=now())
return report
return r