feat(dashboard): endpoint /dashboard/filtres restreint aux valeurs utilisées
VUE_CONSO ne rattache pas tous les services/catégories des référentiels SERVICE et CATEGORIE à un monitoring actif (ex. "Business Intelligence"). Les proposer tels quels dans les filtres du dashboard menait à un écran vide. Le nouvel endpoint renvoie un SELECT DISTINCT service, categorie sur VUE_CONSO, avec les combinaisons pour permettre un filtrage en cascade côté frontend. Ajoute un test dédié et régénère docs/openapi.json.
This commit is contained in:
+1
-1
File diff suppressed because one or more lines are too long
@@ -331,6 +331,33 @@ def get_dashboard(
|
|||||||
return rows_to_list(cursor, cursor.fetchall())
|
return rows_to_list(cursor, cursor.fetchall())
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/dashboard/filtres", tags=["Dashboard"])
|
||||||
|
def get_dashboard_filtres():
|
||||||
|
"""
|
||||||
|
Valeurs de filtre réellement présentes dans VUE_CONSO.
|
||||||
|
|
||||||
|
Les référentiels SERVICE / CATEGORIE contiennent des entrées auxquelles
|
||||||
|
aucun monitoring actif n'est rattaché (ex. « Business Intelligence »).
|
||||||
|
Les proposer dans les menus déroulants du dashboard mène à un écran vide :
|
||||||
|
cet endpoint ne renvoie que les valeurs qui ramènent au moins un monitoring.
|
||||||
|
|
||||||
|
`combinaisons` permet au frontend de restreindre les catégories proposées
|
||||||
|
au service sélectionné.
|
||||||
|
"""
|
||||||
|
with get_cursor() as cursor:
|
||||||
|
cursor.execute(
|
||||||
|
"SELECT DISTINCT service, categorie FROM VUE_CONSO "
|
||||||
|
"ORDER BY service, categorie"
|
||||||
|
)
|
||||||
|
combinaisons = [{"service": r[0], "categorie": r[1]} for r in cursor.fetchall()]
|
||||||
|
|
||||||
|
return {
|
||||||
|
"services" : sorted({c["service"] for c in combinaisons}),
|
||||||
|
"categories" : sorted({c["categorie"] for c in combinaisons}),
|
||||||
|
"combinaisons" : combinaisons,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@router.get("/dashboard/summary", tags=["Dashboard"])
|
@router.get("/dashboard/summary", tags=["Dashboard"])
|
||||||
def get_dashboard_summary():
|
def get_dashboard_summary():
|
||||||
"""KPI globaux pour les 4 cartes du dashboard."""
|
"""KPI globaux pour les 4 cartes du dashboard."""
|
||||||
|
|||||||
@@ -22,6 +22,24 @@ def test_services_returns_list(client, cur, auth_headers):
|
|||||||
assert {"id_service": 1, "nom_service": "Contrat"} in r.json()
|
assert {"id_service": 1, "nom_service": "Contrat"} in r.json()
|
||||||
|
|
||||||
|
|
||||||
|
def test_dashboard_filtres_ne_renvoie_que_les_valeurs_utilisees(client, cur, auth_headers):
|
||||||
|
# VUE_CONSO ne contient que Contrat / Fournisseur : les services du
|
||||||
|
# référentiel sans monitoring rattaché ne doivent pas remonter.
|
||||||
|
cur.description = [("service",), ("categorie",)]
|
||||||
|
cur._rows = [
|
||||||
|
("Contrat", "DOM-TOM"),
|
||||||
|
("Contrat", "Tiers-payeurs"),
|
||||||
|
("Fournisseur", "Contreparties"),
|
||||||
|
]
|
||||||
|
r = client.get("/dashboard/filtres", headers=auth_headers)
|
||||||
|
assert r.status_code == 200
|
||||||
|
body = r.json()
|
||||||
|
assert body["services"] == ["Contrat", "Fournisseur"]
|
||||||
|
assert "Business Intelligence" not in body["services"]
|
||||||
|
assert body["categories"] == ["Contreparties", "DOM-TOM", "Tiers-payeurs"]
|
||||||
|
assert {"service": "Contrat", "categorie": "DOM-TOM"} in body["combinaisons"]
|
||||||
|
|
||||||
|
|
||||||
def test_auth_login_success(client, cur):
|
def test_auth_login_success(client, cur):
|
||||||
cur._one = (1, "admin", hash_password("Admin2026!"), "Admin", 1)
|
cur._one = (1, "admin", hash_password("Admin2026!"), "Admin", 1)
|
||||||
r = client.post("/auth/login", data={"username": "admin", "password": "Admin2026!"})
|
r = client.post("/auth/login", data={"username": "admin", "password": "Admin2026!"})
|
||||||
|
|||||||
Reference in New Issue
Block a user