L'horaire de l'email récapitulatif se configure maintenant sur /destinataires (raccourcis courants + expression cron personnalisée) au lieu de .env, avec reprogrammation immédiate sans redémarrage. Stocké en base (ParametrePlanification) plutôt que dans un fichier de config. Défaut changé pour tous les lundis à 9h. Les exemples et le code n'utilisent que des noms de jours (mon, tue, ...) dans les expressions cron : vérifié empiriquement qu'APScheduler ne suit pas la convention Unix pour les jours numériques (0=lundi chez lui, pas 0=dimanche), ce qui rendrait "0 9 * * 1" trompeur.
104 lines
3.8 KiB
Python
104 lines
3.8 KiB
Python
"""
|
|
Routes pour gérer la liste des emails qui reçoivent les alertes de stock
|
|
bas, et la planification de l'email récapitulatif périodique.
|
|
"""
|
|
|
|
from fastapi import APIRouter, Depends, Form
|
|
from fastapi.requests import Request
|
|
from fastapi.responses import RedirectResponse
|
|
from sqlmodel import Session, select
|
|
|
|
from app.database import get_session
|
|
from app.models import DestinataireAlerte
|
|
from app.scheduler import (
|
|
EXEMPLES_CRON,
|
|
definir_expression_cron,
|
|
obtenir_expression_cron,
|
|
prochaine_execution,
|
|
valider_expression_cron,
|
|
)
|
|
from app.templates_engine import templates
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
def _contexte_planification(session: Session, **kwargs) -> dict:
|
|
contexte = {
|
|
"expression_cron": obtenir_expression_cron(session),
|
|
"exemples_cron": EXEMPLES_CRON,
|
|
"prochaine_execution": prochaine_execution(),
|
|
"erreur_cron": None,
|
|
}
|
|
contexte.update(kwargs)
|
|
return contexte
|
|
|
|
|
|
@router.get("/destinataires")
|
|
def lister_destinataires(request: Request, q: str = "", session: Session = Depends(get_session)):
|
|
"""Affiche la liste des emails configurés pour recevoir les alertes,
|
|
filtrée par ?q=... si fourni (même comportement que /materiels, pour
|
|
rester cohérent si la liste grandit), ainsi que la planification de
|
|
l'email récapitulatif périodique."""
|
|
tous = session.exec(select(DestinataireAlerte)).all()
|
|
destinataires = [d for d in tous if q.lower() in d.email.lower()] if q else tous
|
|
return templates.TemplateResponse(
|
|
request,
|
|
"destinataires.html",
|
|
{"destinataires": destinataires, "q": q, **_contexte_planification(session)},
|
|
)
|
|
|
|
|
|
@router.post("/destinataires")
|
|
def ajouter_destinataire(email: str = Form(...), session: Session = Depends(get_session)):
|
|
"""Ajoute un email à la liste des destinataires des alertes."""
|
|
session.add(DestinataireAlerte(email=email))
|
|
session.commit()
|
|
return RedirectResponse(url="/destinataires", status_code=303)
|
|
|
|
|
|
@router.post("/destinataires/{destinataire_id}/supprimer")
|
|
def supprimer_destinataire(destinataire_id: int, session: Session = Depends(get_session)):
|
|
"""Retire un email de la liste des destinataires."""
|
|
destinataire = session.get(DestinataireAlerte, destinataire_id)
|
|
if destinataire:
|
|
session.delete(destinataire)
|
|
session.commit()
|
|
return RedirectResponse(url="/destinataires", status_code=303)
|
|
|
|
|
|
@router.post("/destinataires/planification")
|
|
def modifier_planification(
|
|
request: Request, expression_cron: str = Form(...), session: Session = Depends(get_session)
|
|
):
|
|
"""Change l'horaire de l'email récapitulatif périodique et reprogramme
|
|
la tâche immédiatement (pas besoin de redémarrer le serveur).
|
|
|
|
Pas de redirection en cas d'expression cron invalide : on réaffiche la
|
|
page avec un message d'erreur et la valeur saisie, pour ne pas la
|
|
perdre (contrairement au reste du formulaire, elle n'est pas
|
|
enregistrée tant qu'elle n'est pas valide).
|
|
"""
|
|
expression_cron = expression_cron.strip()
|
|
if not valider_expression_cron(expression_cron):
|
|
tous = session.exec(select(DestinataireAlerte)).all()
|
|
return templates.TemplateResponse(
|
|
request,
|
|
"destinataires.html",
|
|
{
|
|
"destinataires": tous,
|
|
"q": "",
|
|
**_contexte_planification(
|
|
session,
|
|
expression_cron=expression_cron,
|
|
erreur_cron=(
|
|
f"Expression cron invalide : « {expression_cron} ». "
|
|
"Format attendu : minute heure jour mois jour_semaine "
|
|
"(ex: 0 9 * * mon)."
|
|
),
|
|
),
|
|
},
|
|
)
|
|
|
|
definir_expression_cron(session, expression_cron)
|
|
return RedirectResponse(url="/destinataires", status_code=303)
|