| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081 |
- from apscheduler.schedulers.background import BackgroundScheduler
- from apscheduler.triggers.cron import CronTrigger
- _flask_app = None
- scheduler = BackgroundScheduler(
- job_defaults={"coalesce": True, "max_instances": 1},
- timezone="UTC",
- )
- def init_scheduler(flask_app):
- global _flask_app
- _flask_app = flask_app
- if not scheduler.running:
- scheduler.start()
- # Nettoyage des runs bloqués à "running" (app redémarrée pendant un backup)
- scheduler.add_job(
- func=_cleanup_stuck_runs,
- trigger="interval",
- hours=1,
- id="cleanup_stuck_runs",
- replace_existing=True,
- )
- def _cleanup_stuck_runs():
- from datetime import timedelta
- with _flask_app.app_context():
- from db import db, Run
- cutoff = __import__("datetime").datetime.utcnow() - timedelta(hours=6)
- stuck = Run.query.filter(
- Run.status == "running",
- Run.started_at < cutoff,
- ).all()
- for run in stuck:
- run.status = "error"
- run.log_text = (run.log_text or "") + "\n[timeout] Run marqué en erreur par le nettoyage automatique."
- run.finished_at = __import__("datetime").datetime.utcnow()
- if stuck:
- db.session.commit()
- def _execute_job(job_id):
- with _flask_app.app_context():
- from jobs.ynh_backup import execute_job
- execute_job(job_id)
- def schedule_job(job):
- import logging
- job_key = f"job_{job.id}"
- try:
- trigger = CronTrigger.from_crontab(job.cron_expr)
- except Exception:
- logging.warning(f"Job #{job.id} « {job.name} » : expression cron invalide « {job.cron_expr} » — job non planifié.")
- return
- if scheduler.get_job(job_key):
- scheduler.reschedule_job(job_key, trigger=trigger)
- else:
- scheduler.add_job(
- func=_execute_job,
- trigger=trigger,
- id=job_key,
- kwargs={"job_id": job.id},
- replace_existing=True,
- )
- def remove_job(job_id):
- job_key = f"job_{job_id}"
- if scheduler.get_job(job_key):
- scheduler.remove_job(job_key)
- def get_next_run(job_id):
- job_key = f"job_{job_id}"
- apsjob = scheduler.get_job(job_key)
- if apsjob and apsjob.next_run_time:
- return apsjob.next_run_time
- return None
|