Ajoute les jours fériés, l'astreinte week-end 0h-0h et les ratios éditables

- Bouton F : marque un jour férié (dû 0, sans consommer de congé) ; il
  qualifie les règles « ferie » et étend la fenêtre d'astreinte du jour.
- Samedi, dimanche et fériés : l'astreinte compte de 0h00 à 0h00 ; un jour
  AS déclaré garde sa borne du lendemain 18h pour ne pas tronquer les
  interventions nocturnes.
- Ratios d'astreinte éditables à la main dans Réglages (nom, jours, fenêtre,
  ratio), avec ajout/suppression de règles et validation.
- Un quart d'heure entamé par deux interventions qui se suivent n'est compté
  qu'une fois : les lignes d'événements somment au total du jour.
- Décompte mieux expliqué dans l'interface : quarts d'horloge (:00/:15/:30/:45),
  fenêtre couverte par événement, exemple chiffré.

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
This commit is contained in:
Jacquin Antoine
2026-09-08 23:30:15 +02:00
parent 23271d736d
commit dc80e81db2
11 changed files with 599 additions and 111 deletions

View File

@ -181,6 +181,40 @@ def toggle_astreinte(date: str, user_id: str = ""):
save_astreintes(dates, user_id)
# ── Jours fériés (marqués à la main) ──────────────────────────────────────────
def _feries_file(user_id: str) -> Path:
return _user_root(user_id) / "feries.json"
def load_feries(user_id: str = "") -> list[str]:
"""Dates marquées comme jours fériés (triées, dédupliquées)."""
f = _feries_file(user_id)
if not f.exists():
return []
try:
dates = json.loads(f.read_text())
except json.JSONDecodeError:
return []
return sorted(set(d for d in dates if isinstance(d, str)))
def save_feries(dates: list[str], user_id: str = ""):
_ensure_user_dirs(user_id)
_feries_file(user_id).write_text(
json.dumps(sorted(set(dates)), indent=2, ensure_ascii=False)
)
def toggle_ferie(date: str, user_id: str = ""):
dates = load_feries(user_id)
if date in dates:
dates.remove(date)
else:
dates.append(date)
save_feries(dates, user_id)
def load_facteurs_astreinte() -> list[dict]:
"""Règles globales de comptage des heures d'astreinte (type de jour × fenêtre
horaire × facteur) ; défauts cohérents si absentes ou invalides dans la config."""
@ -190,6 +224,13 @@ def load_facteurs_astreinte() -> list[dict]:
return DEFAULT_FACTEURS_ASTREINTE
def save_facteurs_astreinte(facteurs: list[dict]):
"""Règles globales de comptage des heures d'astreinte (config partagée)."""
cfg = load_config()
cfg["astreinte_facteurs"] = facteurs
save_config(cfg)
# ── Auth (email + password) ────────────────────────────────────────────────────
def _auth_file(user_id: str) -> Path: