First Push
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
"""Background scheduler for periodic tasks.
|
||||
|
||||
All user-scoped jobs iterate over every active user (with that user's
|
||||
timezone/context active) so each user's alarms, reminders, calendars,
|
||||
pomodoro, countdowns and weather are serviced independently.
|
||||
"""
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from backend.calendar_mgr import sync_all_subscriptions, get_upcoming_reminders
|
||||
from backend.weather_mgr import fetch_weather, fetch_hourly
|
||||
from backend.alarm_mgr import check_alarms
|
||||
from backend.countdown_mgr import check_countdowns
|
||||
from backend.pomodoro_mgr import check_and_trigger
|
||||
from backend.models import get_db
|
||||
from backend.time_util import set_thread_user, clear_thread_user
|
||||
|
||||
|
||||
_scheduler: BackgroundScheduler | None = None
|
||||
|
||||
|
||||
def _each_user(app, fn):
|
||||
"""Run fn(user_id) for every active user inside the app context."""
|
||||
with app.app_context():
|
||||
db = get_db(app.config.get("DB_PATH", "data.db"))
|
||||
rows = db.execute("SELECT id FROM users WHERE status='active' ORDER BY id").fetchall()
|
||||
db.close()
|
||||
for u in rows:
|
||||
uid = u["id"]
|
||||
try:
|
||||
set_thread_user(uid)
|
||||
fn(uid)
|
||||
except Exception as e:
|
||||
app.logger.error(f"Scheduler error for user {uid}: {e}")
|
||||
finally:
|
||||
clear_thread_user()
|
||||
|
||||
|
||||
def start_scheduler(app):
|
||||
global _scheduler
|
||||
_scheduler = BackgroundScheduler()
|
||||
|
||||
@_scheduler.scheduled_job("interval", seconds=10, id="check_alarms")
|
||||
def _check_alarms():
|
||||
_each_user(app, check_alarms)
|
||||
|
||||
@_scheduler.scheduled_job("interval", minutes=1, id="check_reminders")
|
||||
def _check_reminders():
|
||||
def _run(uid):
|
||||
due = get_upcoming_reminders(uid)
|
||||
if due:
|
||||
app.event_bus.publish("reminder_due", {"events": due}, user_id=uid)
|
||||
app.logger.info(f"Reminders due for user {uid}: {len(due)} events")
|
||||
_each_user(app, _run)
|
||||
|
||||
@_scheduler.scheduled_job("interval", minutes=30, id="sync_calendars")
|
||||
def _sync_calendars():
|
||||
with app.app_context():
|
||||
sync_all_subscriptions()
|
||||
|
||||
@_scheduler.scheduled_job("cron", minute="0,30", id="fetch_weather")
|
||||
def _fetch_weather():
|
||||
_each_user(app, fetch_weather)
|
||||
|
||||
@_scheduler.scheduled_job("interval", seconds=1, id="check_pomodoro")
|
||||
def _check_pomodoro():
|
||||
_each_user(app, check_and_trigger)
|
||||
|
||||
@_scheduler.scheduled_job("cron", minute="1", id="fetch_hourly")
|
||||
def _fetch_hourly():
|
||||
_each_user(app, fetch_hourly)
|
||||
|
||||
@_scheduler.scheduled_job("interval", seconds=1, id="check_countdowns")
|
||||
def _check_countdowns():
|
||||
def _run(uid):
|
||||
triggered = check_countdowns(uid)
|
||||
if triggered:
|
||||
app.logger.info(f"Countdowns triggered for user {uid}: {[c['label'] for c in triggered]}")
|
||||
_each_user(app, _run)
|
||||
|
||||
_scheduler.start()
|
||||
|
||||
|
||||
def stop_scheduler():
|
||||
global _scheduler
|
||||
if _scheduler:
|
||||
_scheduler.shutdown(wait=False)
|
||||
_scheduler = None
|
||||
Reference in New Issue
Block a user