"""Countdown timer management — duration-based, once-only.""" import time from datetime import datetime, timedelta from backend.time_util import isoformat as tz_isoformat from flask import current_app from backend.models import get_db def _uid() -> int: """Resolve the current user (request session or scheduler thread).""" try: from backend.auth import current_user_id return current_user_id() or 0 except Exception: return 0 def get_countdowns(user_id: int = None) -> list: if user_id is None: user_id = _uid() db = get_db(current_app.config["DB_PATH"]) rows = [dict(r) for r in db.execute( "SELECT * FROM countdowns WHERE user_id=? ORDER BY id DESC", (user_id,) ).fetchall()] db.close() return rows def add_countdown(label: str, duration_seconds: int, enabled: bool = True, sound: str = "default", user_id: int = None) -> dict: if user_id is None: user_id = _uid() end_ts = str(time.time() + duration_seconds) db = get_db(current_app.config["DB_PATH"]) cur = db.execute( "INSERT INTO countdowns (user_id, label, duration_seconds, end_time, enabled, sound) " "VALUES (?, ?, ?, ?, ?, ?)", (user_id, label, duration_seconds, end_ts, 1 if enabled else 0, sound) ) db.commit() row = dict(db.execute("SELECT * FROM countdowns WHERE id=?", (cur.lastrowid,)).fetchone()) db.close() return row def update_countdown(cd_id: int, label: str = None, duration_seconds: int = None, enabled: bool = None, sound: str = None, user_id: int = None): if user_id is None: user_id = _uid() db = get_db(current_app.config["DB_PATH"]) fields = [] vals = [] if duration_seconds is not None: fields.append("duration_seconds=?") vals.append(duration_seconds) fields.append("end_time=?") vals.append(str(time.time() + duration_seconds)) for key, val in [("label", label), ("enabled", 1 if enabled else 0 if enabled is not None else None), ("sound", sound)]: if val is not None: fields.append(f"{key}=?") vals.append(val) ok = False if fields: vals.append(user_id) vals.append(cd_id) cur = db.execute(f"UPDATE countdowns SET {', '.join(fields)} WHERE user_id=? AND id=?", vals) db.commit() ok = cur.rowcount > 0 db.close() return ok def delete_countdown(cd_id: int, user_id: int = None) -> bool: if user_id is None: user_id = _uid() db = get_db(current_app.config["DB_PATH"]) cur = db.execute("DELETE FROM countdowns WHERE user_id=? AND id=?", (user_id, cd_id)) db.commit() db.close() return cur.rowcount > 0 def check_countdowns(user_id: int = None) -> list: """Check for countdowns whose end_time has passed. Auto-disable after trigger.""" if user_id is None: user_id = _uid() now_ts = str(time.time()) db = get_db(current_app.config["DB_PATH"]) rows = db.execute( "SELECT * FROM countdowns WHERE user_id=? AND enabled=1 AND end_time <= ? AND end_time != ''", (user_id, now_ts) ).fetchall() triggered = [] for r in rows: cd_id = r["id"] triggered.append(dict(r)) db.execute("UPDATE countdowns SET enabled=0 WHERE user_id=? AND id=?", (user_id, cd_id)) db.commit() db.close() if triggered: current_app.logger.info( f"Countdowns triggered: {[c['label'] for c in triggered]}" ) current_app.event_bus.publish("countdown_triggered", { "countdowns": triggered, "ts": tz_isoformat(), }, user_id=user_id) return triggered