Files
MapMil/centers/analytics/api/app/services/scheduler.py
T
gitrusprusandCursor 71f8cf169e Add Profile/Channel entities with CRUD and remove legacy parser builder.
Introduce reusable ParserProfile and ParseChannel, pair jobs with enqueue flatten, Events admin CRUD, and drop inline/legacy parser-builder UI and aliases.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-16 14:51:03 +03:00

66 lines
1.9 KiB
Python

import logging
import threading
import time
from datetime import datetime, timezone
from ..database import SessionLocal
from ..models import ParseJob
from .jobs import enqueue_parse_job
logger = logging.getLogger(__name__)
TICK_SECONDS = 30
RECURRING_STATUSES = ("completed", "failed")
def run_scheduler_tick() -> None:
db = SessionLocal()
try:
now = datetime.now(timezone.utc)
jobs = (
db.query(ParseJob)
.filter(
ParseJob.is_active.is_(True),
ParseJob.interval_seconds > 0,
ParseJob.status.in_(RECURRING_STATUSES),
)
.all()
)
for job in jobs:
if job.last_run_at is None:
continue
elapsed = (now - job.last_run_at).total_seconds()
if elapsed < job.interval_seconds:
continue
job.status = "queued"
job.last_error = None
db.commit()
try:
enqueue_parse_job(db, job)
except ValueError as exc:
job.status = "failed"
job.last_error = str(exc)
db.commit()
logger.warning("Skip re-queue job %s: %s", job.id, exc)
continue
logger.info("Re-queued recurring job %s (interval %ss)", job.id, job.interval_seconds)
except Exception:
logger.exception("Scheduler tick failed")
db.rollback()
finally:
db.close()
def _scheduler_loop(stop_event: threading.Event) -> None:
while not stop_event.wait(TICK_SECONDS):
run_scheduler_tick()
def start_scheduler() -> threading.Event:
stop_event = threading.Event()
thread = threading.Thread(target=_scheduler_loop, args=(stop_event,), daemon=True)
thread.start()
logger.info("Parse job scheduler started (tick every %ss)", TICK_SECONDS)
return stop_event