58 lines
1.7 KiB
Python
58 lines
1.7 KiB
Python
"""Background scheduler — runs run_check_all() every N hours.
|
|
|
|
Run as a separate process: `python -m app.scheduler`.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import time
|
|
|
|
from apscheduler.schedulers.blocking import BlockingScheduler
|
|
from apscheduler.triggers.interval import IntervalTrigger
|
|
|
|
from app.config import settings
|
|
from app.db import init_db
|
|
from app.services.monitor import run_check_all
|
|
|
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s")
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def job() -> None:
|
|
logger.info("Scheduled scan starting…")
|
|
start = time.time()
|
|
summary = run_check_all()
|
|
elapsed = time.time() - start
|
|
total_changes = sum(c for c in summary.values() if c > 0)
|
|
logger.info(
|
|
"Scan done in %.1fs. Projects: %d, total changes: %d",
|
|
elapsed, len(summary), total_changes,
|
|
)
|
|
|
|
|
|
def main() -> None:
|
|
init_db()
|
|
hours = max(1, settings.scrape_interval_hours)
|
|
scheduler = BlockingScheduler(timezone="UTC")
|
|
scheduler.add_job(
|
|
job,
|
|
trigger=IntervalTrigger(hours=hours),
|
|
# Omit next_run_time so APScheduler defaults the first run to now+interval
|
|
# (i.e. don't fire immediately at startup, fire after one interval, then
|
|
# every interval). Passing next_run_time=None instead creates the job in a
|
|
# PAUSED state and it never fires — that was the bug.
|
|
id="periodic-scan",
|
|
max_instances=1,
|
|
coalesce=True,
|
|
)
|
|
logger.info("Scheduler started — interval %d hour(s).", hours)
|
|
try:
|
|
scheduler.start()
|
|
except (KeyboardInterrupt, SystemExit):
|
|
logger.info("Scheduler stopped.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|