Ajoute page statistiques avec graphiques et onglets de période

- Nouvelle page /stats avec histogrammes de distribution par créneau
  horaire (entrée/sortie matin et après-midi)
- Camemberts animés de conformité aux directives (≤09:00, ≥12:00,
  ≤14:00, ≥17:00) globale, par slot et par période (mois, sem. N-1)
- Onglets Tout / 1 an / 3 mois / 1 mois : filtrage côté client avec
  recalcul μ, σ, n et conformité en temps réel
- Graphique des soldes hebdomadaires (barres animées, toutes semaines
  sur l'axe X en rotation -45°, semaines partielles distinctes)
- Clic sur un graphique → modal plein écran avec axe X détaillé (15 min)
- Légende σ / n / directive / moyenne sur chaque histogramme
- Journées incomplètes exclues des stats ; heures dues retirées du solde
- Lien "Statistiques" dans la nav

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
toto
2026-06-24 17:09:37 +02:00
parent 33a28dc4d6
commit 1a34bff1c6
4 changed files with 1119 additions and 0 deletions

View File

@ -1,3 +1,4 @@
import json
from datetime import datetime, timedelta
from itertools import groupby
@ -11,6 +12,7 @@ from models import (
toggle_conge, list_all_weeks,
)
from calcul import compute_week, minutes_to_hhmm, hhmm_to_minutes, heures_dues
from stats import compute_all_stats
app = FastAPI(title="Décompte Horaire")
templates = Jinja2Templates(directory="templates")
@ -264,6 +266,29 @@ def save_pointage(
return _htmx_calc_and_soldes(request, date, d, conges, h_jour)
@app.get("/stats", response_class=HTMLResponse)
def stats_page(request: Request):
stats = compute_all_stats()
data_json = json.dumps({
"time_stats": {
slot: {
"mean": s["mean"], "sigma": s["sigma"],
"mean_hhmm": s["mean_hhmm"], "sigma_min": s["sigma_min"],
"n": s["n"], "dated_values": s["dated_values"],
} if s else None
for slot, s in stats["time_stats"].items()
},
"compliance": stats["compliance"],
"weekly_balances": stats["weekly_balances"],
"today": stats["today"],
})
return templates.TemplateResponse("stats.html", {
"request": request,
"stats": stats,
"data_json": data_json,
})
@app.post("/conge/{date}/{type_conge}", response_class=HTMLResponse)
def toggle_conge_route(date: str, type_conge: str, request: Request):
toggle_conge(date, type_conge)