Add Telegram listener, scheduler, and extended parsers admin UI.

Introduce background scheduling and migrations for analytics ingest, expand parser management and map toolbar UX, and ignore local data/session files from version control.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-02 20:47:56 +03:00
co-authored by Cursor
parent 1d6d54ab9f
commit 1492576fd9
23 changed files with 1124 additions and 267 deletions
@@ -0,0 +1,58 @@
import logging
import threading
import time
from datetime import datetime, timezone
from ..database import SessionLocal
from ..models import ParseJob
from .jobs import enqueue_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()
enqueue_job(job.id, job.source_type, job.source_config)
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