Rend les plages horaires configurables par utilisateur plutôt que globalement
Chaque compte a désormais son propre data/users/<user>/plages.json, réglable depuis une nouvelle carte "Plages horaires" sur /settings (avec validation HH:MM et ordre matin < aprem). Le calcul, les stats de conformité et les avertissements de saisie lisent la config de l'utilisateur courant au lieu du plages global. La valeur globale de data/config.json reste lue comme valeur de repli tant qu'un utilisateur n'a rien personnalisé.
This commit is contained in:
48
app/main.py
48
app/main.py
@ -18,8 +18,9 @@ from models import (
|
||||
toggle_conge, list_all_weeks,
|
||||
load_notif_config, save_notif_config, find_user_by_token, save_presence,
|
||||
load_auth, save_auth, find_user_by_reset_token,
|
||||
load_plages, save_plages,
|
||||
)
|
||||
from calcul import compute_week, minutes_to_hhmm, hhmm_to_minutes, heures_dues, DEFAULT_PLAGES
|
||||
from calcul import compute_week, minutes_to_hhmm, hhmm_to_minutes, heures_dues
|
||||
from stats import compute_all_stats
|
||||
from notifications import reminder_loop
|
||||
|
||||
@ -55,6 +56,7 @@ app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||
JOURS_FR = ["Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi"]
|
||||
_USERNAME_RE = re.compile(r"^[a-zA-Z0-9_.\-]{1,64}$")
|
||||
_EMAIL_RE = re.compile(r"^([a-zA-Z0-9_.\-]{1,64})@([a-zA-Z0-9.\-]{1,255})$")
|
||||
_HHMM_RE = re.compile(r"^([01]\d|2[0-3]):[0-5]\d$")
|
||||
|
||||
|
||||
# ── Auth helpers ──────────────────────────────────────────────────────────────
|
||||
@ -96,10 +98,6 @@ def heures_jour_min() -> int:
|
||||
return int(float(load_config().get("heures_jour", 7.8)) * 60)
|
||||
|
||||
|
||||
def plages_config() -> dict:
|
||||
return {**DEFAULT_PLAGES, **load_config().get("plages", {})}
|
||||
|
||||
|
||||
def _weeks_by_year(all_weeks: list) -> dict:
|
||||
result = {}
|
||||
for yr, wks in groupby(sorted(all_weeks, reverse=True), key=lambda x: x[0]):
|
||||
@ -116,7 +114,7 @@ def _build_soldes_ctx(year, week, conges, h_jour, user_id):
|
||||
"aprem_entree": None, "aprem_sortie": None})
|
||||
for d in dates
|
||||
]
|
||||
return {"result": compute_week(pointages, conges, h_jour, plages_config())}
|
||||
return {"result": compute_week(pointages, conges, h_jour, load_plages(user_id))}
|
||||
|
||||
|
||||
def _build_jour_ctx(date, conges, h_jour, user_id):
|
||||
@ -214,7 +212,7 @@ def _oob_time_cells(date, jour, plages):
|
||||
|
||||
def _htmx_conge_and_soldes(request, date, d, conges, h_jour, user_id):
|
||||
iso = d.isocalendar()
|
||||
plages = plages_config()
|
||||
plages = load_plages(user_id)
|
||||
jour = _build_jour_ctx(date, conges, h_jour, user_id)
|
||||
cg = jour["conge"]
|
||||
btn_ma_on = " on" if "matin" in cg else ""
|
||||
@ -241,7 +239,7 @@ def _htmx_conge_and_soldes(request, date, d, conges, h_jour, user_id):
|
||||
|
||||
def _htmx_calc_and_soldes(request, date, d, conges, h_jour, user_id):
|
||||
iso = d.isocalendar()
|
||||
plages = plages_config()
|
||||
plages = load_plages(user_id)
|
||||
jour = _build_jour_ctx(date, conges, h_jour, user_id)
|
||||
soldes_ctx = _build_soldes_ctx(iso.year, iso.week, conges, h_jour, user_id)
|
||||
soldes_html = templates.get_template("_soldes.html").render({"request": request, **soldes_ctx})
|
||||
@ -406,7 +404,7 @@ def semaine(request: Request, year: int, week: int):
|
||||
return RedirectResponse("/login", status_code=302)
|
||||
|
||||
h_jour = heures_jour_min()
|
||||
plages = plages_config()
|
||||
plages = load_plages(user_id)
|
||||
conges = load_conges(user_id)
|
||||
soldes_ctx = _build_soldes_ctx(year, week, conges, h_jour, user_id)
|
||||
|
||||
@ -527,6 +525,7 @@ def settings_page(request: Request):
|
||||
return RedirectResponse("/login", status_code=302)
|
||||
|
||||
notif = load_notif_config(user_id)
|
||||
plages = load_plages(user_id)
|
||||
base_url = str(request.base_url).rstrip("/")
|
||||
return templates.TemplateResponse("settings.html", {
|
||||
"request": request,
|
||||
@ -537,7 +536,10 @@ def settings_page(request: Request):
|
||||
"rappel_fin_h": notif.get("rappel_fin_h", 20),
|
||||
"arrivee_url": f"{base_url}/presence/{notif['token']}/arrivee",
|
||||
"depart_url": f"{base_url}/presence/{notif['token']}/depart",
|
||||
"plages": plages,
|
||||
"saved": request.query_params.get("saved") == "1",
|
||||
"plages_saved": request.query_params.get("plages_saved") == "1",
|
||||
"plages_error": request.query_params.get("plages_error") == "1",
|
||||
})
|
||||
|
||||
|
||||
@ -561,6 +563,34 @@ def settings_save(
|
||||
return RedirectResponse("/settings?saved=1", status_code=303)
|
||||
|
||||
|
||||
@app.post("/settings/plages", response_class=HTMLResponse)
|
||||
def settings_plages_save(
|
||||
request: Request,
|
||||
matin_debut: str = Form(""), matin_fin: str = Form(""),
|
||||
aprem_debut: str = Form(""), aprem_fin: str = Form(""),
|
||||
pause_dejeuner_fin: str = Form(""),
|
||||
):
|
||||
user_id = _get_user(request)
|
||||
if not user_id:
|
||||
return RedirectResponse("/login", status_code=302)
|
||||
|
||||
fields = {
|
||||
"matin_debut": matin_debut, "matin_fin": matin_fin,
|
||||
"aprem_debut": aprem_debut, "aprem_fin": aprem_fin,
|
||||
"pause_dejeuner_fin": pause_dejeuner_fin,
|
||||
}
|
||||
if not all(_HHMM_RE.match(v) for v in fields.values()):
|
||||
logger.warning("réglages plages rejetés (format invalide): user_id=%r valeurs=%r", user_id, fields)
|
||||
return RedirectResponse("/settings?plages_error=1", status_code=303)
|
||||
if not (fields["matin_debut"] < fields["matin_fin"] < fields["aprem_debut"] < fields["aprem_fin"]):
|
||||
logger.warning("réglages plages rejetés (ordre invalide): user_id=%r valeurs=%r", user_id, fields)
|
||||
return RedirectResponse("/settings?plages_error=1", status_code=303)
|
||||
|
||||
logger.info("réglages plages mis à jour: user_id=%r plages=%r", user_id, fields)
|
||||
save_plages(user_id, fields)
|
||||
return RedirectResponse("/settings?plages_saved=1", status_code=303)
|
||||
|
||||
|
||||
@app.get("/presence/{token}/arrivee")
|
||||
def presence_arrivee(token: str):
|
||||
user_id = find_user_by_token(token)
|
||||
|
||||
Reference in New Issue
Block a user