mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-08-11 11:19:24 +00:00
feat(library): sort and badge by personal difficulty rating (#810)
* feat(library): sort and badge by personal difficulty rating
Adds sort=difficulty/difficulty-desc to the library API (correlated
subquery over song_user_meta.user_difficulty, unrated songs pushed to
the bottom either direction, same pattern as the existing mastery
sort) and surfaces the rating as a badge on library cards in both the
v2 grid/tree views and the v3 grid. The rating itself already existed
(song_user_meta) — this just makes it sortable and visible, so it's
no longer only readable in the per-song edit drawer.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix(library): escape difficulty badge, wire tree view, add changelog+tests
- Wrap song.user_difficulty in esc() at both badge call sites
(static/app.js ~2082 and ~2283) for XSS-consistency with the
sibling tuning badge, which already uses esc().
- server.py: query_artists (the classic tree view's data source, used
by /api/library/artists) never batch-attached user_difficulty the
way query_page does for the grid, so the tree-view difficulty badge
added in 75673c3 was unreachable dead code (song.user_difficulty was
always undefined there). Now attaches it via the existing
user_meta_map() helper, same pattern as query_page.
- Add an [Unreleased] CHANGELOG.md entry for the difficulty sort +
badge feature, matching the repo's existing entry format.
- Add tests/test_library_filters.py::test_difficulty_sort_pushes_unrated_to_bottom
asserting unrated songs sort to the bottom in both sort=difficulty
and sort=difficulty-desc directions, and
::test_tree_view_songs_carry_user_difficulty covering the
query_artists fix above.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(library): chunk user_meta_map + rebuild stale tailwind css
Address review-bot findings on the difficulty sort/badge:
- user_meta_map now chunks filenames into 400-row batches (like
overrides_map) before the IN (...) query. query_artists (tree view)
passes every song across up to 50 artists, which could push the
placeholder count past SQLite's older variable limit; query_page's
small pages are unaffected. (CodeRabbit: Stability & Availability)
- Rebuild static/tailwind.min.css: the ◆N difficulty badge introduced
bg-blue-900/30 + text-blue-300, which were never compiled into the
committed stylesheet, failing the tailwind-fresh CI gate.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: byrongamatos <xasiklas@gmail.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
byrongamatos
parent
e446b05a99
commit
fadaa154e9
@@ -293,6 +293,45 @@ def test_year_sort_asc_oldest_first(client, seeded):
|
||||
assert files == ["b.archive", "a.archive", "f.archive", "d.sloppak", "c.sloppak", "e.sloppak"]
|
||||
|
||||
|
||||
def test_difficulty_sort_pushes_unrated_to_bottom(client, server_mod):
|
||||
"""Personal difficulty (song_user_meta.user_difficulty) sorts like
|
||||
mastery: an unrated (NULL) row must fall to the bottom in BOTH
|
||||
directions rather than colliding with a real 1..5 rating at either
|
||||
end."""
|
||||
_put(server_mod, filename="easy.archive", title="Easy", artist="A",
|
||||
arrangements=[{"index": 0, "name": "Lead", "notes": 1}])
|
||||
_put(server_mod, filename="hard.archive", title="Hard", artist="B",
|
||||
arrangements=[{"index": 0, "name": "Lead", "notes": 1}])
|
||||
_put(server_mod, filename="unrated.archive", title="Unrated", artist="C",
|
||||
arrangements=[{"index": 0, "name": "Lead", "notes": 1}])
|
||||
server_mod.meta_db.set_song_user_meta("easy.archive", user_difficulty=1)
|
||||
server_mod.meta_db.set_song_user_meta("hard.archive", user_difficulty=5)
|
||||
|
||||
asc = [s["filename"] for s in _get(client, sort="difficulty")["songs"]]
|
||||
assert asc == ["easy.archive", "hard.archive", "unrated.archive"]
|
||||
|
||||
desc = [s["filename"] for s in _get(client, sort="difficulty-desc")["songs"]]
|
||||
assert desc == ["hard.archive", "easy.archive", "unrated.archive"]
|
||||
|
||||
|
||||
def test_tree_view_songs_carry_user_difficulty(client, server_mod):
|
||||
"""`/api/library/artists` (the classic tree view's `query_artists`) must
|
||||
batch-attach `user_difficulty` the same way `query_page` does for the
|
||||
grid — otherwise the tree view's difficulty badge silently never
|
||||
renders (song.user_difficulty stays undefined for every row)."""
|
||||
_put(server_mod, filename="rated.archive", title="Rated", artist="A",
|
||||
arrangements=[{"index": 0, "name": "Lead", "notes": 1}])
|
||||
_put(server_mod, filename="unrated.archive", title="Unrated", artist="A",
|
||||
arrangements=[{"index": 0, "name": "Lead", "notes": 1}])
|
||||
server_mod.meta_db.set_song_user_meta("rated.archive", user_difficulty=4)
|
||||
|
||||
data = client.get("/api/library/artists").json()
|
||||
songs = data["artists"][0]["albums"][0]["songs"]
|
||||
by_filename = {s["filename"]: s for s in songs}
|
||||
assert by_filename["rated.archive"]["user_difficulty"] == 4
|
||||
assert by_filename["unrated.archive"]["user_difficulty"] is None
|
||||
|
||||
|
||||
def test_tuning_sort_down_tuned_before_up_tuned_at_same_distance(client, server_mod):
|
||||
"""Within an ABS(tuning_sort_key) tier, the down-tuned variant
|
||||
must come before the up-tuned one so the order matches the chart's
|
||||
|
||||
Reference in New Issue
Block a user