from __future__ import annotations import logging from fastapi import APIRouter, Depends, HTTPException from fastapi.responses import JSONResponse from server.auth import require_auth from server.config_loader import load_meds_config, today_str from server.db import get_db, new_id, utc_now_iso from server.messages import pick from server.scheduler import schedule_snooze from server.schemas import LogEntry, SnoozeRequest from server.slots import build_today from server.stats import MILESTONE_DEFS, build_history, check_milestones, compute_stats router = APIRouter(prefix="/api", tags=["intake"]) logger = logging.getLogger(__name__) NO_STORE = {"Cache-Control": "no-store"} @router.get("/today") async def get_today(_: dict = Depends(require_auth)) -> JSONResponse: db = await get_db() try: data = await build_today(db) finally: await db.close() return JSONResponse(data, headers=NO_STORE) @router.post("/log") async def post_log(payload: LogEntry, _: dict = Depends(require_auth)) -> JSONResponse: config = load_meds_config() if payload.slot_id not in {s.id for s in config.slots}: raise HTTPException(status_code=400, detail="Unknown slot") day = payload.day.isoformat() if payload.day else today_str(config.timezone) logged_at = payload.logged_at or utc_now_iso() status = payload.status.value db = await get_db() try: await db.execute("DELETE FROM intake_log WHERE slot_id = ? AND day = ?", (payload.slot_id, day)) await db.execute( "INSERT INTO intake_log (id, slot_id, day, status, logged_at, source) VALUES (?, ?, ?, ?, ?, ?)", (new_id(), payload.slot_id, day, status, logged_at, payload.source), ) if status == "taken": await db.execute("DELETE FROM snooze WHERE slot_id = ? AND day = ?", (payload.slot_id, day)) await db.commit() stats = await compute_stats(db) new_milestones = await check_milestones(db, stats["streak"]) finally: await db.close() message = pick("success") if status == "taken" else pick("missed") return JSONResponse( { "ok": True, "message": message, "stats": stats, "new_milestones": [ {"id": m, "title": MILESTONE_DEFS.get(m, m)} for m in new_milestones ], }, headers=NO_STORE, ) @router.get("/history") async def get_history(days: int = 90, _: dict = Depends(require_auth)) -> JSONResponse: db = await get_db() try: data = await build_history(db, days=min(days, 365)) finally: await db.close() return JSONResponse(data, headers=NO_STORE) @router.post("/snooze") async def post_snooze(payload: SnoozeRequest, _: dict = Depends(require_auth)) -> JSONResponse: config = load_meds_config() if payload.slot_id not in {s.id for s in config.slots}: raise HTTPException(status_code=400, detail="Unknown slot") until = await schedule_snooze(payload.slot_id, payload.minutes) return JSONResponse( {"ok": True, "snooze_until": until, "message": pick("snooze")}, headers=NO_STORE, )