Ajoute les rappels de pointage via ntfy et détection de présence Tasker

Notifie l'utilisateur toutes les 5 min s'il est présent dans les locaux
sans avoir pointé le matin, ou parti sans avoir dépointé la sortie.
This commit is contained in:
Antoine
2026-07-17 20:45:27 +02:00
parent 47c05ee7f1
commit 1ab1cf27c1
5 changed files with 265 additions and 1 deletions

View File

@ -1,22 +1,30 @@
import asyncio
import json
import re
from datetime import datetime, timedelta
from itertools import groupby
from fastapi import FastAPI, Form, Request, Response
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse
from fastapi.templating import Jinja2Templates
from fastapi.staticfiles import StaticFiles
from models import (
load_config, load_week_pointages, save_week_pointages, load_conges,
toggle_conge, list_all_weeks, needs_migration, migrate_legacy_to_user,
load_notif_config, save_notif_config, find_user_by_token, save_presence,
)
from calcul import compute_week, minutes_to_hhmm, hhmm_to_minutes, heures_dues
from stats import compute_all_stats
from classement import compute_classement
from notifications import reminder_loop
app = FastAPI(title="Décompte Horaire")
@app.on_event("startup")
async def _start_reminder_loop():
asyncio.create_task(reminder_loop())
templates = Jinja2Templates(directory="templates")
templates.env.filters["zip"] = zip
app.mount("/static", StaticFiles(directory="static"), name="static")
@ -370,6 +378,52 @@ def stats_page(request: Request):
})
@app.get("/settings", response_class=HTMLResponse)
def settings_page(request: Request):
user_id = _get_user(request)
if not user_id:
return RedirectResponse("/login", status_code=302)
notif = load_notif_config(user_id)
base_url = str(request.base_url).rstrip("/")
return templates.TemplateResponse("settings.html", {
"request": request,
"current_user": user_id,
"ntfy_topic": notif.get("ntfy_topic") or "",
"arrivee_url": f"{base_url}/presence/{notif['token']}/arrivee",
"depart_url": f"{base_url}/presence/{notif['token']}/depart",
"saved": request.query_params.get("saved") == "1",
})
@app.post("/settings", response_class=HTMLResponse)
def settings_save(request: Request, ntfy_topic: str = Form("")):
user_id = _get_user(request)
if not user_id:
return RedirectResponse("/login", status_code=302)
save_notif_config(user_id, ntfy_topic)
return RedirectResponse("/settings?saved=1", status_code=303)
@app.get("/presence/{token}/arrivee")
def presence_arrivee(token: str):
user_id = find_user_by_token(token)
if not user_id:
return JSONResponse({"error": "invalid token"}, status_code=404)
save_presence(user_id, present=True, since=datetime.now().isoformat())
return {"ok": True}
@app.get("/presence/{token}/depart")
def presence_depart(token: str):
user_id = find_user_by_token(token)
if not user_id:
return JSONResponse({"error": "invalid token"}, status_code=404)
save_presence(user_id, present=False, since=datetime.now().isoformat())
return {"ok": True}
@app.get("/classement", response_class=HTMLResponse)
def classement_page(request: Request):
user_id = _get_user(request)