Skip to content

feat(library): sort and badge by personal difficulty rating - #810

Merged
byrongamatos merged 3 commits into
got-feedBack:mainfrom
mhglover:feat/difficulty-sort-and-badge
Jul 7, 2026
Merged

feat(library): sort and badge by personal difficulty rating#810
byrongamatos merged 3 commits into
got-feedBack:mainfrom
mhglover:feat/difficulty-sort-and-badge

Conversation

@mhglover

@mhglover mhglover commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds sort=difficulty / sort=difficulty-desc to the library API — a correlated subquery over song_user_meta.user_difficulty, following the same pattern as the existing mastery sort (unrated songs pushed to the bottom in both directions).
  • Surfaces the personal difficulty rating as a ◆N badge on library cards, in both the v2 grid/tree views and the v3 grid.

The rating itself (song_user_meta.user_difficulty) already existed and was editable in the per-song details drawer — this just makes it sortable and visible at a glance in the main library, so it's no longer something you can only see by opening each song individually.

Built as a companion to a new community plugin (difficulty_tagger) that auto-seeds this rating from chart analysis, but this PR has no dependency on it — it works the same whether the rating was set manually or by any plugin.

Test plan

  • Full pytest suite passes (2318 passed, 4 skipped)
  • Verified live against a real ~700-song library: sort=difficulty-desc correctly surfaces known-hard songs first, sort=difficulty surfaces the built-in beginner tutorial pak first
  • Verified the badge renders correctly on both v2 (grid + tree) and v3 grid cards

Summary by CodeRabbit

  • New Features
    • Added library sorting by your personal difficulty (“Difficulty (easiest first)” and “Difficulty (hardest first)”).
    • Unrated songs (no difficulty set) are now placed at the bottom in both difficulty sort directions.
    • Songs with a recorded difficulty now show a blue “your difficulty” badge (with tooltip) in both grid and tree/list views, including the tree view.
    • Difficulty sort options are available in the library dropdown and persist across the app.
  • Tests
    • Added coverage for difficulty sorting and ensuring the correct difficulty badge/rendering in the tree view.

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>
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5d891933-1eab-4e99-8e85-9b5beb974954

📥 Commits

Reviewing files that changed from the base of the PR and between b5b9f75 and e5d77ef.

📒 Files selected for processing (2)
  • server.py
  • static/tailwind.min.css

📝 Walkthrough

Walkthrough

Adds personal difficulty sorting to the library, tree-view song enrichment, and frontend sort options plus difficulty badges in grid and list views.

Changes

Difficulty Sorting Feature

Layer / File(s) Summary
Backend sort and metadata batching
server.py, tests/test_library_filters.py
Adds difficulty sort modes to the library query, batches user difficulty lookups for large filename sets, and verifies unrated songs stay last for both ascending and descending difficulty sorts.
Artist song difficulty enrichment
server.py, tests/test_library_filters.py
Attaches user_difficulty to songs returned by query_artists() and tests that the artists endpoint returns the expected rated and unrated values.
Frontend sort options and badges
static/app.js, static/index.html, static/v3/songs.js, CHANGELOG.md
Adds difficulty sort options to the UI, accepts the new sort keys, renders difficulty badges when present, and records the feature in the changelog.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main library change: sorting and displaying personal difficulty ratings.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

…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>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server.py`:
- Around line 4396-4400: The user_meta_map call in query_artists is batching too
many song IDs into a single IN query and can exceed SQLite’s older variable
limit for prolific artists. Update query_artists to split the rows into the same
400-row batches used by overrides_map, and call user_meta_map per batch before
merging the results so the difficulty badge logic still works without hitting
placeholder limits.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4586a437-2e7b-480c-823a-71454fc310dc

📥 Commits

Reviewing files that changed from the base of the PR and between 75673c3 and b5b9f75.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • server.py
  • static/app.js
  • tests/test_library_filters.py
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • static/app.js

Comment thread server.py
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>
@byrongamatos
byrongamatos merged commit fadaa15 into got-feedBack:main Jul 7, 2026
4 checks passed
topkoa added a commit that referenced this pull request Jul 19, 2026
Finding un-split songs took five taps (cycle each stem pill to its
"lacks" state) — and was quietly wrong even then: the drawer offered
five of the canonical six stems, so a piano-only song lacked all five
listed and matched a hand-built "not split" filter despite being split.

The stems section gains a "Not split" toggle that sets stem_lacks to
every instrument stem in one tap (the same lacks-ALL query Stem
Splitter's missing-stems view runs, backend semantics unchanged), and
piano joins the pill row (already in the backend's allowed set).

(Rebuilt on current main after #1003/#810/#92e78be rewrote the drawer
region — the original branch conflicted whole-file.)

Signed-off-by: topkoa <topkoa@gmail.com>
topkoa added a commit that referenced this pull request Jul 19, 2026
Finding un-split songs took five taps (cycle each stem pill to its
"lacks" state) — and was quietly wrong even then: the drawer offered
five of the canonical six stems, so a piano-only song lacked all five
listed and matched a hand-built "not split" filter despite being split.

The stems section gains a "Not split" toggle that sets stem_lacks to
every instrument stem in one tap (the same lacks-ALL query Stem
Splitter's missing-stems view runs, backend semantics unchanged), and
piano joins the pill row (already in the backend's allowed set).

(Rebuilt on current main after #1003/#810/#92e78be rewrote the drawer
region — the original branch conflicted whole-file.)

Signed-off-by: topkoa <topkoa@gmail.com>
@mhglover
mhglover deleted the feat/difficulty-sort-and-badge branch July 22, 2026 23:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants