First Push
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
"""Alarm and clock management."""
|
||||
import os
|
||||
from backend.time_util import now as tz_now
|
||||
from flask import current_app
|
||||
from backend.models import get_db
|
||||
|
||||
# Per-user "already triggered today" state: {user_id: (date_str, set)}
|
||||
_triggered_state: dict[int, tuple[str, set]] = {}
|
||||
|
||||
|
||||
|
||||
|
||||
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_alarms(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 alarms WHERE user_id=? ORDER BY time", (user_id,)
|
||||
).fetchall()]
|
||||
db.close()
|
||||
return rows
|
||||
|
||||
|
||||
def add_alarm(label: str, time: str, days: str = "1,2,3,4,5,6,7", enabled: bool = True,
|
||||
sound: str = "default", once: bool = False, user_id: int = None) -> dict:
|
||||
if user_id is None:
|
||||
user_id = _uid()
|
||||
db = get_db(current_app.config["DB_PATH"])
|
||||
cur = db.execute(
|
||||
"INSERT INTO alarms (user_id, label, time, days, enabled, sound, once) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
||||
(user_id, label, time, days, 1 if enabled else 0, sound, 1 if once else 0)
|
||||
)
|
||||
db.commit()
|
||||
row = dict(db.execute("SELECT * FROM alarms WHERE id=?", (cur.lastrowid,)).fetchone())
|
||||
db.close()
|
||||
return row
|
||||
|
||||
|
||||
def update_alarm(alarm_id: int, label: str = None, time: str = None,
|
||||
days: str = None, enabled: bool = None, sound: str = None, once: bool = None,
|
||||
user_id: int = None):
|
||||
if user_id is None:
|
||||
user_id = _uid()
|
||||
db = get_db(current_app.config["DB_PATH"])
|
||||
fields = []
|
||||
vals = []
|
||||
for key, val in [("label", label), ("time", time), ("days", days),
|
||||
("enabled", 1 if enabled else 0 if enabled is not None else None),
|
||||
("sound", sound),
|
||||
("once", 1 if once else 0 if once is not None else None)]:
|
||||
if val is not None:
|
||||
fields.append(f"{key}=?")
|
||||
vals.append(val)
|
||||
ok = False
|
||||
if fields:
|
||||
vals.append(user_id)
|
||||
vals.append(alarm_id)
|
||||
cur = db.execute(f"UPDATE alarms SET {', '.join(fields)} WHERE user_id=? AND id=?", vals)
|
||||
db.commit()
|
||||
ok = cur.rowcount > 0
|
||||
db.close()
|
||||
return ok
|
||||
|
||||
|
||||
def delete_alarm(alarm_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 alarms WHERE user_id=? AND id=?", (user_id, alarm_id))
|
||||
db.commit()
|
||||
db.close()
|
||||
return cur.rowcount > 0
|
||||
|
||||
|
||||
def check_alarms(user_id: int = None) -> list:
|
||||
if user_id is None:
|
||||
user_id = _uid()
|
||||
global _triggered_state
|
||||
|
||||
db = get_db(current_app.config["DB_PATH"])
|
||||
now = tz_now()
|
||||
today_str = now.strftime("%Y%m%d")
|
||||
|
||||
triggered_date, triggered_today = _triggered_state.get(user_id, ("", set()))
|
||||
if triggered_date != today_str:
|
||||
triggered_today = set()
|
||||
triggered_date = today_str
|
||||
|
||||
current_time = now.strftime("%H:%M")
|
||||
weekday = str(now.isoweekday())
|
||||
|
||||
rows = db.execute(
|
||||
"SELECT * FROM alarms WHERE user_id=? AND enabled=1 AND time=?",
|
||||
(user_id, current_time)
|
||||
).fetchall()
|
||||
|
||||
triggered = []
|
||||
for r in rows:
|
||||
days_list = r["days"].split(",")
|
||||
if weekday in days_list:
|
||||
alarm_id = r["id"]
|
||||
if alarm_id in triggered_today:
|
||||
continue
|
||||
triggered_today.add(alarm_id)
|
||||
triggered.append(dict(r))
|
||||
# Once-only alarms: auto-disable after trigger
|
||||
if r["once"]:
|
||||
db.execute("UPDATE alarms SET enabled=0 WHERE user_id=? AND id=?", (user_id, alarm_id))
|
||||
db.commit()
|
||||
|
||||
_triggered_state[user_id] = (triggered_date, triggered_today)
|
||||
db.close()
|
||||
|
||||
if triggered:
|
||||
current_app.logger.info(f"Triggering {len(triggered)} alarms: {[a['label'] for a in triggered]}")
|
||||
current_app.event_bus.publish("alarm_triggered", {
|
||||
"alarms": triggered,
|
||||
"ts": now.isoformat(),
|
||||
}, user_id=user_id)
|
||||
return triggered
|
||||
|
||||
|
||||
def get_ringtone_path(sound_name: str) -> str | None:
|
||||
if not sound_name or sound_name == "default":
|
||||
return None
|
||||
ringtone_dir = os.path.join(
|
||||
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||||
"frontend", "static", "ringtones"
|
||||
)
|
||||
path = os.path.join(ringtone_dir, sound_name)
|
||||
if os.path.isfile(path):
|
||||
return path
|
||||
return None
|
||||
Reference in New Issue
Block a user