95 lines
3.5 KiB
Python
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 kazeia_central.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
|