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

(Will add them shortly)

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: fe75ad59-87a4-483f-9715-6a29f84fa522

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
@herzog0
herzog0 force-pushed the teo/2486-badge-profile-display branch from c7c88d9 to d64af2b Compare August 24, 2026 13:40
@herzog0
herzog0 force-pushed the teo/2486-badge-profile-display branch from d64af2b to d86f82b Compare August 24, 2026 19:22
@herzog0
herzog0 force-pushed the teo/2486-badge-profile-display branch from d86f82b to bca9211 Compare August 25, 2026 14:16
@jlchilders11
jlchilders11 self-requested a review August 25, 2026 17:34
@julioest
julioest self-requested a review August 25, 2026 17:35

@jlchilders11 jlchilders11 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This looks good to me, and satisfies the requirements. Thanks for all of the hard work!

@julioest julioest left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approving! ✅

Two things that came up, neither blocking:

  • news/services.py:_entry_to_post_card still emits badge_url, so post-card authors on the Learn page and library detail get no badge. Rendering _user_profile.html with that author dict shows the badge markup simply absent. Minimal fix is swapping the inline author dict for entry.author.to_v3_profile_dict(), which I checked is field-for-field identical apart from adding badge and badge_label. Delegating the whole card to to_v3_post_card_dict() also fixes the badge but changes category, since that method derives it from the tag while services derives it from the news type. Either way _get_entry_post_cards will want active_badges_prefetch("author__badges").
  • Featuring reads display_badge_id while the ticket's AC 2 asks for the highest rank. Nothing back-fills it, so a member who never opens the picker features nothing. Either add a fallback or amend AC 2 and the PR body.

@herzog0

herzog0 commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator Author

@kattyode

QA: real badges on the website

Until now every badge shown on the website was the same fake gold medal for everybody. This
change makes the site show the badges members have actually earned.

  • Where to test: the public website, https://www.cppal-dev.boost.org/,
    plus the admin panel to award the badges you need.
  • Account needed: a full admin account (superuser), and a second ordinary member account (or a
    private browsing window) for the logged-out checks.

What this change does

  • Wherever a member's name and picture appear, the badge next to them is now a real one:
    their own profile, the news pages, the community page, the homepage and library contributor
    lists.
  • The badge shown next to a member's name is the one they picked on their profile edit page.
    If they have not picked one, no badge shows - that is correct, not a bug.
  • The Badges card on a member's profile lists every badge they hold, highest level first,
    each with the date it was awarded.
  • If a member ticks Hide badges on your profile, other people see no badges from them anywhere,
    while they still see their own on their own profile.

What it does not do

  • It does not change how badges are earned. That is the admin side, tested separately.
  • It does not show progress towards badges you have not earned yet ("3 of 5 to Gold"). Only what a
    member holds is shown.

Before you start

  1. Have a second, ordinary member account handy, or use a private browsing window.

Test 1: award yourself a badge

  1. Go to /admin/badges/userachievement/add/.
  2. Pick your own user, achievement Library Authoring, write anything in the note (it is
    required), and save.
  3. Open /admin/badges/userbadge/.

Expected: you immediately hold Library Author / Bronze. Nothing had to be pressed.

Test 2: pick it, and see it on your profile

  1. Go to /users/me/?edit=true.
  2. Find the Display Badge dropdown. Your Bronze badge is in the list. Select it and save.
  3. Go to /users/me/.

Expected:

  • The fake gold medal is gone. Your Bronze badge is on your profile card.
  • The Badges card lists it once, with the date it was awarded.
  • That date matches Awarded at on the row in
    /admin/badges/userbadge/. If the
    website shows a day earlier or later than the admin, report it - that is a timezone bug.

Test 3: no pick means no badge

  1. Go back to /users/me/?edit=true, set
    Display Badge back to empty, and save.
  2. Reload /users/me/.

Expected: no badge on your profile card, but the Badges card still lists the Bronze badge
you hold. The card shows your choice; the list shows everything you have earned.

Set your Display Badge back to Bronze before continuing.

Test 4: the list grows as you earn more

  1. Add a second Library Authoring achievement to yourself the same way as Test 1 (duplicate
    manual grants are allowed - the counts add up).
  2. Reload /users/me/.

Expected: the Badges card now lists Silver and Bronze, Silver first. Your profile card
still shows whatever you picked.

  1. Add two more (four in total) so you reach Gold, and check the list reads Gold, Silver,
    Bronze
    in that order.

Test 5: the badge follows you around the site

With a badge picked, and with a published news post authored by you, open each of:

Expected: your author card carries the same badge on all four.

Worth knowing: before this change the news author card showed no badge at all, even for
someone who had earned one. So on the news pages you are confirming a fix, not just a repeat.

Test 6: library contributor lists

  1. In the admin, go to /admin/libraries/libraryversion/,
    find a library's latest version, and add yourself to Authors. Save.
  2. Open that library's page on the website (/library/<version>/<library>/).

Expected: your contributor card there carries your badge too.

Note it must be the latest version - the contributor cards read the current release, not the
library as a whole.

Test 7: hiding badges works, and fails safe

  1. Go to /users/me/?edit=true, tick
    Hide badges on your profile, and save.
  2. Reload /users/me/ while still logged in as
    yourself.

Expected: you still see your own badges. Hiding only affects what other people see.

  1. Now open /community/,
    /news/ and your post's page in a private browsing
    window
    (logged out), and find your card.

Expected: no badge on your card anywhere. Any badge of yours visible to a logged-out visitor
while this box is ticked is a bug worth reporting.

Untick it before continuing.

Test 8: a badge kept after the rules change

  1. You should still hold Gold from Test 4 (four achievements).
  2. In the admin, open /admin/badges/badge/,
    open Library Author, and raise the Gold number from 4 to 6. Save.
  3. Reload /users/me/.

Expected: your Gold badge is still there, even though you now have four achievements
and Gold asks for six.

This is intentional - please do not report it as a bug. Members keep badges they already
earned when the requirement was lower. Set the number back to 4 afterwards.

Test 9: taking a badge away

  1. In the admin, on /admin/badges/userbadge/,
    select your Gold row, choose Revoke, write a note, and apply.
  2. Reload /users/me/.

Expected: Gold has left the Badges card. Silver and Bronze are still there. If Gold was the
badge you had picked to display, your profile card now shows no badge rather than a broken one.

Test 10: it works without JavaScript

Turn JavaScript off in your browser and reload
/users/me/ and
/community/.

Expected: all the badges are still there. They are drawn by the server, so nothing here depends
on JavaScript.

Test 11: a quick look in dark mode and on a phone

Switch the site to dark mode and check the profile, the news list and the community page. Then look
at the profile and the news list at phone width.

Expected: the badges are visible and legible in both.


Things that look wrong but are meant to be that way

  • A member with badges but no Display Badge picked shows no badge next to their name.
  • A member keeps a badge after the requirement for it is raised.
  • The Badges card shows only badges earned - never "locked" or "in progress" ones.
  • The member's own profile still shows their badges when Hide badges is ticked.

Reporting anything you find

Please include the member's email, the page URL where you saw it, whether you were logged
in or logged out, and which test above you were on. A screenshot of the member's rows on
/admin/badges/userbadge/ alongside the
page is the most useful thing, because it shows what the site should have drawn.

@herzog0
herzog0 force-pushed the teo/2486-badge-profile-display branch from bca9211 to dd37b28 Compare August 26, 2026 17:49
@herzog0
herzog0 force-pushed the teo/2486-badge-profile-display branch from dd37b28 to 624b7a3 Compare August 26, 2026 17:51
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)

3 participants