mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-08-10 18:59:56 +00:00
The profile card's "Your best scores" panel was a hardcoded placeholder
(`#v3-profile-bests` was never filled), so it always read "Play a song to
start tracking..." regardless of how many songs had been scored. The
backend already records best_score/best_accuracy per song; only this
panel was left unwired.
- server.py: add MetadataDB.top_stats(limit) (per-song aggregate, best
score first, scored songs only, dead songs skipped) + /api/stats/top
route that enriches rows with title/artist/art, mirroring
/api/stats/recent. Declared before the /api/stats/{filename} catch-all.
- static/v3/profile.js: renderBests() fetches /api/stats/top and fills the
panel (rank, title/artist, best accuracy %, score; click to play),
keeping the placeholder only when nothing's been scored.
- tests: cover ordering, per-song aggregation, limit, and
resume-only/dead-song exclusion.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
9f35fedeef
commit
a0867f8bfd
@@ -1180,6 +1180,25 @@ class MetadataDB:
|
||||
).fetchall()
|
||||
return {r[0]: r[1] for r in rows if r[2] and r[2] > 0}
|
||||
|
||||
def top_stats(self, limit: int = 5) -> list[dict]:
|
||||
"""Top scored songs (best score first) for the profile 'Your best
|
||||
scores' panel. Aggregated per-song across arrangements (best score,
|
||||
best accuracy, total plays), only SCORED songs (plays > 0), dead songs
|
||||
skipped. Mirrors best_accuracy_map's grouping; enriched with metadata
|
||||
by the /api/stats/top route."""
|
||||
limit = max(1, min(50, int(limit)))
|
||||
rows = self.conn.execute(
|
||||
"SELECT filename, MAX(best_score), MAX(best_accuracy), SUM(plays) "
|
||||
"FROM song_stats WHERE 1=1 " + self._existing_song_filter() + # skip dead songs
|
||||
"GROUP BY filename HAVING SUM(plays) > 0 "
|
||||
"ORDER BY MAX(best_score) DESC, MAX(best_accuracy) DESC LIMIT ?",
|
||||
(limit,),
|
||||
).fetchall()
|
||||
return [
|
||||
{"filename": r[0], "best_score": r[1], "best_accuracy": r[2], "plays": r[3]}
|
||||
for r in rows
|
||||
]
|
||||
|
||||
# ── Playlists ─────────────────────────────────────────────────────────--
|
||||
SAVED_KEY = "saved_for_later"
|
||||
|
||||
@@ -5011,6 +5030,28 @@ def api_stats_best():
|
||||
return meta_db.best_accuracy_map()
|
||||
|
||||
|
||||
@app.get("/api/stats/top")
|
||||
def api_top_stats(limit: int = 5):
|
||||
"""Top scored songs (best first), joined to song metadata, for the profile
|
||||
'Your best scores' panel (defined before the {filename} catch-all)."""
|
||||
from urllib.parse import quote
|
||||
out = []
|
||||
for r in meta_db.top_stats(limit):
|
||||
meta = meta_db.conn.execute(
|
||||
"SELECT title, artist, tuning_name FROM songs WHERE filename = ?",
|
||||
(r["filename"],),
|
||||
).fetchone()
|
||||
title, artist, tuning_name = meta if meta else (None, None, None)
|
||||
out.append({
|
||||
**r,
|
||||
"title": title or r["filename"],
|
||||
"artist": artist or "",
|
||||
"tuning_name": tuning_name or "",
|
||||
"art_url": f"/api/song/{quote(r['filename'])}/art",
|
||||
})
|
||||
return out
|
||||
|
||||
|
||||
@app.get("/api/stats/{filename:path}")
|
||||
def api_song_stats(filename: str):
|
||||
return meta_db.get_song_stats(filename)
|
||||
|
||||
Reference in New Issue
Block a user