51 lines
2 KiB
Python
51 lines
2 KiB
Python
|
|
"""
|
||
|
|
Planification de l'email récapitulatif de stock (voir
|
||
|
|
email_alerts.envoyer_resume_stock).
|
||
|
|
|
||
|
|
Choix : un planificateur qui tourne dans le process FastAPI lui-même
|
||
|
|
(APScheduler), configuré par une expression cron classique (RESUME_STOCK_CRON
|
||
|
|
dans .env, ex: "0 8 * * *" = tous les jours à 8h). Rien à configurer côté
|
||
|
|
système d'exploitation du serveur — tout reste dans ce dossier, comme le
|
||
|
|
reste du projet.
|
||
|
|
|
||
|
|
Limite à connaître : la tâche ne se déclenche que si le process est en
|
||
|
|
cours d'exécution au moment prévu, sans rattrapage si le serveur était
|
||
|
|
arrêté à ce moment-là. Pour un outil interne redémarré rarement, c'est un
|
||
|
|
compromis raisonnable face à la complexité d'un vrai cron système. Si ça
|
||
|
|
devient un problème, scripts/envoyer_resume_stock.py peut être branché
|
||
|
|
sur un cron/Planificateur de tâches Windows à la place.
|
||
|
|
"""
|
||
|
|
|
||
|
|
from apscheduler.schedulers.background import BackgroundScheduler
|
||
|
|
from apscheduler.triggers.cron import CronTrigger
|
||
|
|
from sqlmodel import Session
|
||
|
|
|
||
|
|
from app.config import settings
|
||
|
|
from app.database import engine
|
||
|
|
from app.email_alerts import envoyer_resume_stock
|
||
|
|
|
||
|
|
_scheduler = BackgroundScheduler()
|
||
|
|
|
||
|
|
|
||
|
|
def _tache_resume_stock() -> None:
|
||
|
|
"""Ouvre sa propre session DB : cette fonction est appelée par le
|
||
|
|
thread du planificateur, en dehors du cycle requête/réponse FastAPI où
|
||
|
|
la session habituelle (get_session) n'existe pas."""
|
||
|
|
with Session(engine) as session:
|
||
|
|
envoyer_resume_stock(session)
|
||
|
|
|
||
|
|
|
||
|
|
def demarrer_planification() -> None:
|
||
|
|
"""Démarre la tâche planifiée. Appelé une fois au démarrage de l'app
|
||
|
|
(voir lifespan dans main.py)."""
|
||
|
|
trigger = CronTrigger.from_crontab(settings.resume_stock_cron)
|
||
|
|
_scheduler.add_job(_tache_resume_stock, trigger, id="resume_stock", replace_existing=True)
|
||
|
|
if not _scheduler.running:
|
||
|
|
_scheduler.start()
|
||
|
|
|
||
|
|
|
||
|
|
def arreter_planification() -> None:
|
||
|
|
"""Arrête proprement le planificateur (appelé à l'extinction de l'app)."""
|
||
|
|
if _scheduler.running:
|
||
|
|
_scheduler.shutdown(wait=False)
|