From 3385588b33906f2b7c1ca84014bd728baaf17adb Mon Sep 17 00:00:00 2001 From: "Teodoro B. Mendes" Date: Fri, 31 Jul 2026 19:15:31 -0300 Subject: [PATCH 1/2] feat: derive the library versioning achievement from version authors --- badges/sources.py | 12 ++++++++++++ badges/tests/test_commands.py | 16 ++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/badges/sources.py b/badges/sources.py index 03f3f4af2..d0c9b2ed6 100644 --- a/badges/sources.py +++ b/badges/sources.py @@ -64,6 +64,17 @@ def _iter_library_maintenance(): yield user, version.library +def _iter_library_versioning(): + """Yield (user, library_version) for every per-version authorship.""" + from libraries.models import LibraryVersion + + for version in LibraryVersion.objects.prefetch_related("authors").iterator( + chunk_size=500 + ): + for user in version.authors.all(): + yield user, version + + def _iter_code_commits(): """Yield (user, commit) for every attributed commit.""" from libraries.models import Commit @@ -80,6 +91,7 @@ def _iter_code_commits(): BACKFILL_ITERATORS = { AchievementSlug.LIBRARY_AUTHORING: _iter_library_authoring, AchievementSlug.LIBRARY_MAINTENANCE: _iter_library_maintenance, + AchievementSlug.LIBRARY_VERSIONING: _iter_library_versioning, AchievementSlug.CODE_COMMITS: _iter_code_commits, } diff --git a/badges/tests/test_commands.py b/badges/tests/test_commands.py index a063dd853..016041591 100644 --- a/badges/tests/test_commands.py +++ b/badges/tests/test_commands.py @@ -105,6 +105,22 @@ def test_backfill_library_maintenance(plain_user): ).exists() +def test_backfill_library_versioning(plain_user): + """Backfill grants the versioning achievement per LibraryVersion authored.""" + for _ in range(2): + version = baker.make("libraries.LibraryVersion") + version.authors.add(plain_user) + + call_command("backfill_achievements", "--source", "library-versioning") + + assert ( + UserAchievement.objects.filter( + user=plain_user, achievement__slug="library-versioning" + ).count() + == 2 + ) + + def test_backfill_fails_loudly_on_an_explicit_unseeded_source(plain_user): """A named source with no Achievement row is a deploy bug, not a skip.""" Achievement.objects.filter(slug=AchievementSlug.CODE_COMMITS).delete() From f7b1e1f137efe05b448fc62e16304b1dde8c29ed Mon Sep 17 00:00:00 2001 From: "Teodoro B. Mendes" Date: Tue, 18 Aug 2026 08:56:36 -0300 Subject: [PATCH 2/2] feat: count parent libraries only for library versioning --- badges/sources.py | 12 ++++++++---- badges/tests/test_sources.py | 9 +++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/badges/sources.py b/badges/sources.py index d0c9b2ed6..18067d3d1 100644 --- a/badges/sources.py +++ b/badges/sources.py @@ -65,12 +65,16 @@ def _iter_library_maintenance(): def _iter_library_versioning(): - """Yield (user, library_version) for every per-version authorship.""" + """Yield (user, library_version) for every authorship of a parent's release. + + Only parent libraries count: see the module docstring. + """ from libraries.models import LibraryVersion - for version in LibraryVersion.objects.prefetch_related("authors").iterator( - chunk_size=500 - ): + versions = LibraryVersion.objects.exclude( + library__key__in=SUB_LIBRARIES + ).prefetch_related("authors") + for version in versions.iterator(chunk_size=500): for user in version.authors.all(): yield user, version diff --git a/badges/tests/test_sources.py b/badges/tests/test_sources.py index fc1ee82f3..ef46a81d0 100644 --- a/badges/tests/test_sources.py +++ b/badges/tests/test_sources.py @@ -86,6 +86,15 @@ def test_iter_library_maintenance_skips_sub_libraries(plain_user): assert list(sources._iter_library_maintenance()) == [] +def test_iter_library_versioning_skips_sub_libraries(plain_user): + """A sub-library's releases belong to its parent, and count for nothing here.""" + sub = baker.make("libraries.Library", key="math/quaternion") + version = baker.make("libraries.LibraryVersion", library=sub) + version.authors.add(plain_user) + + assert list(sources._iter_library_versioning()) == [] + + def test_iter_code_commits_skips_unlinked(plain_user): """Only commits whose author has a linked user are yielded.""" linked = baker.make("libraries.CommitAuthor", user=plain_user)