Skip to content

Story #2558 :: Task: Show real earned badges on the v3 user profile (and honour hide_badges) - #2627

Open
herzog0 wants to merge 2 commits into
julia/badge-selectionfrom
teo/2486-badge-profile-display
Open

Story #2558 :: Task: Show real earned badges on the v3 user profile (and honour hide_badges)#2627
herzog0 wants to merge 2 commits into
julia/badge-selectionfrom
teo/2486-badge-profile-display

Conversation

@herzog0

@herzog0 herzog0 commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Not a duplicate, Github thought the previous one (#2614) was closed since the branches changed order in the PR stack, so I'm reopening this.

Issue: #2558

⚠️ Base branch is julia/badge-selection

Summary & Context

The user-visible half. Real badges replace the placeholder gold medal everywhere a badge is
rendered: the profile page, the profile card, news author cards, the post list and the community
page.

badges/display.py is the whole presentation boundary. The v3 badge templates take a component
token and a label, never a model instance, so the rank-to-asset mapping lives there rather than on
the user model.

Changes

  • badges/display.py
    • held_badges - the member's active badges, highest rank first, each rank once. Retiring a
      tier keeps the badges already awarded against it, so a member who also qualifies under its
      replacement holds the same rank twice; both rows are real history but only one is a badge to
      show.
    • featured_badge / badge_cards / badge_card.
    • TIER_TOKENS - bronze/silver/gold/platinum/diamond to badge-tier-1..5, diamond at the
      top
      . A test asserts the token numbers climb with the rank ladder so the pair cannot drift
      apart again.
    • active_badges_prefetch(lookup) - the prefetch every multi-card page needs.
  • hide_badges fails closed. held_badges returns an empty list when the member has hidden
    their badges; only the owner's own views pass include_hidden.
  • User.featured_badge replaces the badge_url placeholder. It lives on the model only
    because templates read it as an attribute; everything about selection and rendering is in
    badges.display.
  • users/profile_cards.py: emits badge / badge_label. It previously emitted badge_url
    while _user_profile.html reads author.badge, so news author cards rendered no badge at
    all
    even once real data existed. That is a defect on develop, fixed here because it is the
    same feature.
  • Award dates are date objects rendered through Django's DATE_FORMAT, taken through
    timezone.localtime so an evening award is not dated to the next day west of UTC. There is a
    test under TIME_ZONE="America/New_York" pinning it.
  • N+1 prefetches at all four call sites that render more than one card: ak/homepage.py,
    core/views.py (community), news/views.py (post detail), libraries/mixins.py and
    libraries/utils.py (contributor lists). Each has a constant-query regression test. The
    querysets that select_related the author ask for badges through the path
    (author__badges), because Prefetch("author", queryset=...) is silently dropped when the FK
    is already cached.
  • Fixes an inverted tier ladder on develop: core/views.py's v3 examples page labelled
    tier-4 "Diamond" and tier-5 "Platinum" on both the badge and star sets. Platinum is tier 4,
    diamond is tier 5. The same inversion in the _badge_v3.html header comment and the
    profile-edit mock is corrected too.

‼️ Risks & Considerations ‼️

  • Every page that renders a user card is touched. The risk is a missed prefetch rather than a
    wrong badge; each call site has a django_assert_num_queries test, so a regression fails
    loudly.
  • featured_badge is a cached_property. A view that awards a badge and re-renders the same
    user instance in one request would show the stale value. Nothing does that today.
  • Which badge is "featured" is not yet the member's choice - it is simply the highest-ranked
    one. Letting them pick is a separate ticket
    Separate ticket.
  • Badge progress and locked states on the profile are also a separate ticket
    Separate ticket. This PR renders what a member has,
    not what they could earn.
  • templates/v3/user_profile_page.html switches from the demo_badges mock to profile_badges.
    The core/views.py examples page keeps its demo_* fixtures, correctly.

Screenshots

Before After
Placeholder gold medal on every card The member's real highest badge, or nothing
Desktop Light Mode Desktop Dark Mode Mobile

Peer-review testing steps

Setup. just migrate, docker compose up, and turn the v3 flag on for everyone at
/admin/waffle/flag/ - every template here is a v3 one. For step 8 you also need DEBUG_TOOLBAR=True
in .env and a restart.

  1. Award yourself a badge by hand. /admin/badges/userachievement/add/ - your user, achievement
    Library Authoring, and a note (it is required for a manual grant). Save. Bronze is one grant, so
    /admin/badges/userbadge/ shows Library Author / Bronze immediately.
  2. The profile. /users/me/ - the placeholder gold medal is gone and Bronze is on the card. The
    badges card lists it once, with the award date. Cross-check that date against Awarded at in the
    admin: they must be the same day, which is the timezone fix.
  3. Only the highest badge is featured. Add a second Library Authoring grant (duplicate manual
    grants are intended, counts accumulate). Reload /users/me/: the card now shows Silver, while
    the badges card lists Bronze and Silver. The card shows one badge, the list shows the ladder.
  4. Order and grandfathering. Grant yourself two more (four in total) so you hold Gold. The badges
    card lists Gold, Silver, Bronze in that order, highest rank first. Now raise Gold's threshold to 6 on
    /admin/badges/badge/: the admin says it retired the old tier and created a replacement, and your
    Gold stays on the profile even though you no longer meet the new number. That is the
    grandfathering working as intended, not a stale render. (One rank showing twice is the case the
    dedup in held_badges guards; it needs rows the admin will not create for you, so it is covered by
    test rather than by hand.)
  5. Author cards elsewhere. With a published post authored by you, open /news/, the post detail
    page, /community/ and the homepage. Your author card carries the same badge on all four. On the
    base branch the news author card renders no badge at all even with real data, so this is a fix to
    confirm, not just a check.
  6. Contributor lists. Add yourself to Authors on a library's latest version at
    /admin/libraries/libraryversion/ (the contributor cards read the version, not the library), then
    open /library/<version>/<library>/: those cards carry badges too.
  7. hide_badges fails closed. Tick Hide badges on your profile at /users/me/?edit=true. Your
    own profile still shows them, because the owner's view passes include_hidden. Then open
    /community/, /news/ and your post's detail page in a logged-out window: your card there has
    no badge at all.
  8. No N+1. With the debug toolbar open, note the SQL count on /community/, /news/ and a post
    detail page. Grant badges to several more members whose cards appear on those pages, reload, and the
    count must be unchanged. A count that climbs with the number of cards is the regression these
    prefetches exist to prevent.
  9. The ladder is the right way up. /v3/demo/components/ (staff only) - tier-4 is Platinum and
    tier-5 is Diamond, on both the badge set and the star set. It was inverted on the base branch.
  10. Dark mode on every page above, then mobile width on the profile and the post list.
  11. No JavaScript. Disable it and reload /users/me/ and /community/: badges are server-rendered
    and must all still be there.
  12. Revocation is visible. Revoke your Gold row from /admin/badges/userbadge/ (the action asks
    for a note). /users/me/ falls back to Silver on the card, Gold leaves the badges list, and nothing
    else moves.

Self-review Checklist

  • Tag at least one team member from each team to review this PR
  • Link this PR to the related GitHub Project ticket

Frontend

  • UI implementation matches Figma design
  • Tested in light and dark mode
  • Responsive / mobile verified
  • No hardcoded colors, spacing or typography - the badge component is unchanged, only its data
  • Test without JavaScript - badges are server-rendered
  • No console errors or warnings

Backend

  • Every multi-card call site has a constant-query test

@coderabbitai

coderabbitai Bot commented Aug 14, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b0b14529-8ad5-4705-84ed-c50cec91e099

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@herzog0 herzog0 linked an issue Aug 14, 2026 that may be closed by this pull request
11 tasks
@herzog0 herzog0 changed the title Task: Show real earned badges on the v3 user profile (and honour hide_badges) Story #2558 :: Task: Show real earned badges on the v3 user profile (and honour hide_badges) Aug 14, 2026
@herzog0
herzog0 force-pushed the teo/2486-badge-profile-display branch from 16e9173 to 6619fe7 Compare August 14, 2026 19:57
@herzog0
herzog0 force-pushed the teo/2486-badge-profile-display branch 2 times, most recently from bf61013 to 7e32217 Compare August 17, 2026 16:32
@herzog0
herzog0 force-pushed the teo/2486-badge-profile-display branch from 7e32217 to 91f238b Compare August 17, 2026 17:22
@herzog0
herzog0 force-pushed the teo/2486-badge-profile-display branch 2 times, most recently from 8d329a6 to 69c0dda Compare August 17, 2026 23:06
@herzog0
herzog0 force-pushed the teo/2486-badge-profile-display branch from 69c0dda to 92bb266 Compare August 18, 2026 14:28
@herzog0
herzog0 force-pushed the teo/2486-badge-profile-display branch from 92bb266 to c7c88d9 Compare August 21, 2026 14:48
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.

Task: Show real earned badges on the v3 user profile (and honour hide_badges)

1 participant