Fastwhisper / app /routers /stats.py
Mbonea's picture
Deploy Habit Journal backend S0-S10 to Hugging Face Space.
990895d
Raw
History Blame Contribute Delete
2.87 kB
"""Expose authenticated server-side statistics endpoints.
Probabilities and ranks are computed in Python, never by the model.
"""
from datetime import date, datetime, timezone
from fastapi import APIRouter, Depends, Query, Request
from app.deps import require_login
from app.models import ApiEnvelope, ok
from app.stats_math import build_stats, by_remedy
router = APIRouter(
prefix="/api/stats",
tags=["stats"],
dependencies=[Depends(require_login)],
)
def _entry_dicts(request: Request) -> list[dict]:
return [e.model_dump(mode="json") for e in request.app.state.entry_store._load()]
def _daily_dicts(
request: Request,
start: date | None,
end: date | None,
) -> list[dict]:
rows = request.app.state.daily_store._load()
out = []
for row in rows:
if start and row.date < start:
continue
if end and row.date > end:
continue
out.append(row.model_dump(mode="json"))
return out
def _filter_entries(
entries: list[dict],
start: date | None,
end: date | None,
) -> list[dict]:
if start is None and end is None:
return entries
filtered = []
for entry in entries:
ts = entry.get("ts")
if isinstance(ts, str):
day = date.fromisoformat(ts[:10])
else:
continue
if start and day < start:
continue
if end and day > end:
continue
filtered.append(entry)
return filtered
@router.get("", response_model=ApiEnvelope)
def get_stats(
request: Request,
start: date | None = None,
end: date | None = None,
min_n: int | None = Query(default=None, ge=1),
) -> dict[str, object]:
"""Return aggregate statistics for entries and daily rows."""
settings = request.app.state.settings
effective_min_n = settings.min_stats_n if min_n is None else min_n
entries = _filter_entries(_entry_dicts(request), start, end)
daily = _daily_dicts(request, start, end)
payload = build_stats(
entries,
daily,
min_n=effective_min_n,
shrink_k=settings.stats_shrink_k,
generated_at=datetime.now(timezone.utc).isoformat(),
)
return ok(payload)
@router.get("/remedies", response_model=ApiEnvelope)
def get_remedy_leaderboard(
request: Request,
start: date | None = None,
end: date | None = None,
min_n: int | None = Query(default=None, ge=1),
) -> dict[str, object]:
"""Return remedies sorted by shrinkage rank."""
settings = request.app.state.settings
effective_min_n = settings.min_stats_n if min_n is None else min_n
entries = _filter_entries(_entry_dicts(request), start, end)
rows = by_remedy(
entries,
min_n=effective_min_n,
shrink_k=settings.stats_shrink_k,
)
return ok({"items": rows, "min_n": effective_min_n})