metrics: share the series with members through the broker
Members asked to see how the co-op is doing, not just their own usage. The series was admin-only; now the same query serves both surfaces. - metrics_series(days) holds the query once; /admin/metrics/series and the new /broker/metrics both call it (no second copy to drift). - /broker/metrics is broker-authenticated and returns AGGREGATE, NON-PERSONAL figures only: co-op size, requests served, spend. Same class as /broker/model-leaderboard, and the same rule — no per-member figure passes through the broker.
This commit is contained in:
1 parent
6ab0554d00
commit
4ffadd223c
1 file changed
+42
-16
+42
-16
@@ -3054,22 +3054,14 @@ async def admin_overview(request: Request):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@app.get("/admin/metrics/series")
|
def metrics_series(days: int = 90) -> list[dict]:
|
||||||
@limiter.limit("30/minute")
|
"""Daily metrics rows, oldest first.
|
||||||
async def admin_metrics_series(request: Request, days: int = 90):
|
|
||||||
"""Daily metrics series for charts.
|
|
||||||
|
|
||||||
Authenticated by the X-Admin-Token header. Returns oldest-first rows from
|
One query, two callers: the admin panel (via /admin/metrics/series) and the
|
||||||
`metrics_daily` for the last `days` days, for the admin panel and the member
|
member dashboard (via /broker/metrics). Columns are cumulative as of that
|
||||||
dashboard to chart from one source of truth. A NULL column means that source
|
day except spend_usd/requests, which are that day only; NULL means the
|
||||||
could not be read on that day: render a gap, not a zero.
|
source could not be read that day.
|
||||||
|
|
||||||
Cumulative columns (members_*, newsletter_subs, matrix_space_members) are
|
|
||||||
counts as of that day; spend_usd/requests are that day's traffic only.
|
|
||||||
Backfilled days carry a LOWER BOUND for members_total (members provisioned
|
|
||||||
without a welcome email are absent from the history).
|
|
||||||
"""
|
"""
|
||||||
_verify_admin_token(request)
|
|
||||||
limit = max(1, min(int(days or 90), 730))
|
limit = max(1, min(int(days or 90), 730))
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
try:
|
try:
|
||||||
@@ -3092,8 +3084,27 @@ async def admin_metrics_series(request: Request, days: int = 90):
|
|||||||
"requests",
|
"requests",
|
||||||
"recorded_at",
|
"recorded_at",
|
||||||
]
|
]
|
||||||
series = [dict(zip(cols, row)) for row in reversed(rows)]
|
return [dict(zip(cols, row)) for row in reversed(rows)]
|
||||||
return {"series": series, "count": len(series), "days": limit}
|
|
||||||
|
|
||||||
|
@app.get("/admin/metrics/series")
|
||||||
|
@limiter.limit("30/minute")
|
||||||
|
async def admin_metrics_series(request: Request, days: int = 90):
|
||||||
|
"""Daily metrics series for charts.
|
||||||
|
|
||||||
|
Authenticated by the X-Admin-Token header. Returns oldest-first rows from
|
||||||
|
`metrics_daily` for the last `days` days, for the admin panel and the member
|
||||||
|
dashboard to chart from one source of truth. A NULL column means that source
|
||||||
|
could not be read on that day: render a gap, not a zero.
|
||||||
|
|
||||||
|
Cumulative columns (members_*, newsletter_subs, matrix_space_members) are
|
||||||
|
counts as of that day; spend_usd/requests are that day's traffic only.
|
||||||
|
Backfilled days carry a LOWER BOUND for members_total (members provisioned
|
||||||
|
without a welcome email are absent from the history).
|
||||||
|
"""
|
||||||
|
_verify_admin_token(request)
|
||||||
|
series = metrics_series(days)
|
||||||
|
return {"series": series, "count": len(series), "days": max(1, min(int(days or 90), 730))}
|
||||||
|
|
||||||
|
|
||||||
@app.get("/admin/model-usage")
|
@app.get("/admin/model-usage")
|
||||||
@@ -3469,6 +3480,21 @@ async def broker_usage(request: Request, days: int = 0):
|
|||||||
return await broker_get_usage(email, days=days)
|
return await broker_get_usage(email, days=days)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/broker/model-leaderboard")
|
||||||
|
@limiter.limit("30/minute")
|
||||||
|
async def broker_metrics(request: Request, days: int = 90):
|
||||||
|
"""Co-op metrics over time, for the member dashboard.
|
||||||
|
|
||||||
|
The same series the admin panel reads. It comes through the broker because
|
||||||
|
it is AGGREGATE and non-personal — co-op size, requests served, spend — the
|
||||||
|
same class of social-proof data as /broker/model-leaderboard. No per-member
|
||||||
|
figure passes through here, and members asked to see how the co-op is doing.
|
||||||
|
"""
|
||||||
|
_verify_broker_secret(request)
|
||||||
|
series = metrics_series(days)
|
||||||
|
return {"series": series, "count": len(series), "days": max(1, min(int(days or 90), 730))}
|
||||||
|
|
||||||
|
|
||||||
@app.get("/broker/model-leaderboard")
|
@app.get("/broker/model-leaderboard")
|
||||||
@limiter.limit("30/minute")
|
@limiter.limit("30/minute")
|
||||||
async def broker_model_leaderboard(request: Request, days: int = 0):
|
async def broker_model_leaderboard(request: Request, days: int = 0):
|
||||||
|
|||||||
Reference in new issue
Block a user