Rend les plages horaires et les rappels ntfy configurables, supprime le
classement, ajoute une suite de tests pytest - calcul.py: matin_debut/fin, aprem_debut/fin et pause_dejeuner_fin sont paramétrables via config.json > plages (moteur de calcul de l'heure de sortie optimale inclus, pas seulement l'affichage). Défauts identiques à l'ancien comportement, validé par la suite de tests. - stats.py: les seuils de conformité utilisent la même config. - main.py/templates: warn de saisie et suffixes de cible dynamiques. - Rappels ntfy: serveur, topic et plage horaire (rappel_debut_h/fin_h) configurables par utilisateur depuis /settings, plus en dur dans le code. - Page classement supprimée (pas de compétition entre utilisateurs). - Nouvelle suite pytest (app/tests/), à lancer via `docker compose run --rm pointeuse pytest -v`.
This commit is contained in:
35
app/stats.py
35
app/stats.py
@ -3,7 +3,7 @@ import statistics as _st
|
||||
from datetime import datetime, timedelta, date as _date
|
||||
|
||||
from models import load_week_pointages, load_conges, list_all_weeks, load_config
|
||||
from calcul import hhmm_to_minutes, minutes_to_hhmm, heures_travaillees, heures_dues
|
||||
from calcul import hhmm_to_minutes, minutes_to_hhmm, heures_travaillees, heures_dues, DEFAULT_PLAGES
|
||||
|
||||
MOIS_FR = [
|
||||
"janvier", "février", "mars", "avril", "mai", "juin",
|
||||
@ -21,7 +21,7 @@ def _to_hhmm(minutes_float: float) -> str:
|
||||
return f"{m // 60:02d}:{m % 60:02d}"
|
||||
|
||||
|
||||
def _day_compliant(p: dict, mc: bool, ac: bool) -> bool | None:
|
||||
def _day_compliant(p: dict, mc: bool, ac: bool, plages: dict) -> bool | None:
|
||||
if mc and ac:
|
||||
return None
|
||||
matin_ok = mc or (bool(p.get("matin_entree")) and bool(p.get("matin_sortie")))
|
||||
@ -29,24 +29,24 @@ def _day_compliant(p: dict, mc: bool, ac: bool) -> bool | None:
|
||||
if not (matin_ok and aprem_ok):
|
||||
return None
|
||||
if not mc:
|
||||
if p.get("matin_entree") and hhmm_to_minutes(p["matin_entree"]) > 9 * 60:
|
||||
if p.get("matin_entree") and hhmm_to_minutes(p["matin_entree"]) > hhmm_to_minutes(plages["matin_debut"]):
|
||||
return False
|
||||
if p.get("matin_sortie") and hhmm_to_minutes(p["matin_sortie"]) < 12 * 60:
|
||||
if p.get("matin_sortie") and hhmm_to_minutes(p["matin_sortie"]) < hhmm_to_minutes(plages["matin_fin"]):
|
||||
return False
|
||||
if not ac:
|
||||
if p.get("aprem_entree") and hhmm_to_minutes(p["aprem_entree"]) > 14 * 60:
|
||||
if p.get("aprem_entree") and hhmm_to_minutes(p["aprem_entree"]) > hhmm_to_minutes(plages["aprem_debut"]):
|
||||
return False
|
||||
if p.get("aprem_sortie") and hhmm_to_minutes(p["aprem_sortie"]) < 17 * 60:
|
||||
if p.get("aprem_sortie") and hhmm_to_minutes(p["aprem_sortie"]) < hhmm_to_minutes(plages["aprem_fin"]):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _compliance_over(all_days: list[tuple], cg_set: set) -> dict:
|
||||
def _compliance_over(all_days: list[tuple], cg_set: set, plages: dict) -> dict:
|
||||
n = n_ok = 0
|
||||
for date, p in all_days:
|
||||
mc = (date, "matin") in cg_set or (date, "jour") in cg_set
|
||||
ac = (date, "aprem") in cg_set or (date, "jour") in cg_set
|
||||
result = _day_compliant(p, mc, ac)
|
||||
result = _day_compliant(p, mc, ac, plages)
|
||||
if result is None:
|
||||
continue
|
||||
n += 1
|
||||
@ -58,7 +58,9 @@ def _compliance_over(all_days: list[tuple], cg_set: set) -> dict:
|
||||
def compute_all_stats(user_id: str = "") -> dict:
|
||||
all_weeks = sorted(list_all_weeks(user_id))
|
||||
conges = load_conges(user_id)
|
||||
h_jour = int(float(load_config().get("heures_jour", 7.8)) * 60)
|
||||
cfg = load_config()
|
||||
h_jour = int(float(cfg.get("heures_jour", 7.8)) * 60)
|
||||
plages = {**DEFAULT_PLAGES, **cfg.get("plages", {})}
|
||||
cg_set = {(c["date"], c["type"]) for c in conges}
|
||||
|
||||
today = _date.today()
|
||||
@ -73,10 +75,10 @@ def compute_all_stats(user_id: str = "") -> dict:
|
||||
comp_ok = {s: 0 for s in SLOTS}
|
||||
|
||||
TARGETS = {
|
||||
"matin_entree": ("le", 9 * 60),
|
||||
"matin_sortie": ("ge", 12 * 60),
|
||||
"aprem_entree": ("le", 14 * 60),
|
||||
"aprem_sortie": ("ge", 17 * 60),
|
||||
"matin_entree": ("le", hhmm_to_minutes(plages["matin_debut"])),
|
||||
"matin_sortie": ("ge", hhmm_to_minutes(plages["matin_fin"])),
|
||||
"aprem_entree": ("le", hhmm_to_minutes(plages["aprem_debut"])),
|
||||
"aprem_sortie": ("ge", hhmm_to_minutes(plages["aprem_fin"])),
|
||||
}
|
||||
|
||||
weekly_balances = []
|
||||
@ -177,13 +179,14 @@ def compute_all_stats(user_id: str = "") -> dict:
|
||||
deltas = [b["delta_min"] for b in weekly_balances]
|
||||
avg_delta_min = round(_st.mean(deltas)) if deltas else 0
|
||||
|
||||
comp_globale = _compliance_over(all_days, cg_set)
|
||||
comp_mois = _compliance_over(month_days, cg_set)
|
||||
comp_sem_n1 = _compliance_over(prev_week_day_list, cg_set)
|
||||
comp_globale = _compliance_over(all_days, cg_set, plages)
|
||||
comp_mois = _compliance_over(month_days, cg_set, plages)
|
||||
comp_sem_n1 = _compliance_over(prev_week_day_list, cg_set, plages)
|
||||
|
||||
return {
|
||||
"time_stats": time_stats,
|
||||
"compliance": compliance,
|
||||
"plages": plages,
|
||||
"weekly_balances": weekly_balances,
|
||||
"total_weeks": len(weekly_balances),
|
||||
"n_jours": n_jours,
|
||||
|
||||
Reference in New Issue
Block a user