diff --git a/website/build.py b/website/build.py index cba524fa..6ca467ab 100644 --- a/website/build.py +++ b/website/build.py @@ -115,20 +115,24 @@ def load_downloads(path: Path) -> dict[str, int]: def sort_entries(entries: Sequence[TemplateEntry]) -> list[TemplateEntry]: - """Sort entries by stars descending, then name ascending. + """Sort entries by downloads descending, then name ascending. - Three tiers: starred entries first, stdlib second, other non-starred last. + Four tiers: entries with download counts first, then starred entries by + stars descending, then stdlib, then the rest. """ def sort_key(entry: TemplateEntry) -> tuple[int, int, int, str]: + downloads = entry["downloads"] stars = entry["stars"] name = entry["name"].lower() + if downloads is not None: + return (0, -downloads, 0, name) if stars is not None: builtin = 1 if entry.get("source_type") == "Built-in" else 0 - return (0, -stars, builtin, name) + return (1, -stars, builtin, name) if entry.get("source_type") == "Built-in": - return (1, 0, 0, name) - return (2, 0, 0, name) + return (2, 0, 0, name) + return (3, 0, 0, name) return sorted(entries, key=sort_key) diff --git a/website/static/main.js b/website/static/main.js index 073bb6c4..11d43861 100644 --- a/website/static/main.js +++ b/website/static/main.js @@ -5,7 +5,7 @@ function getScrollBehavior() { } let activeFilter = null; -let activeSort = { col: "stars", order: "desc" }; +let activeSort = { col: "downloads", order: "desc" }; const searchInput = document.querySelector(".search"); const filterBar = document.querySelector(".filter-bar"); const filterValue = document.querySelector(".filter-value"); @@ -211,7 +211,7 @@ function buildQueryString() { const params = new URLSearchParams(); const query = searchInput ? searchInput.value.trim() : ""; if (query) params.set("q", query); - if (activeSort.col !== "stars" || activeSort.order !== "desc") { + if (activeSort.col !== "downloads" || activeSort.order !== "desc") { params.set("sort", activeSort.col); params.set("order", activeSort.order); } @@ -392,7 +392,7 @@ sortHeaders.forEach(function (th) { if (activeSort.col === col) { if (activeSort.order === defaultOrder) activeSort = { col: col, order: altOrder }; - else activeSort = { col: "stars", order: "desc" }; + else activeSort = { col: "downloads", order: "desc" }; } else { activeSort = { col: col, order: defaultOrder }; } diff --git a/website/tests/test_build.py b/website/tests/test_build.py index 3b417b24..50627e27 100644 --- a/website/tests/test_build.py +++ b/website/tests/test_build.py @@ -463,6 +463,8 @@ class TestBuild: assert "1,234,567" in html # Built-in entries never show PyPI counts: the asyncio row is the backport package assert "26,305,454" not in html + # Default sort: entries with download counts come first + assert html.index("My-Lib") < html.index("no-pypi") def test_build_fails_when_group_and_category_slug_collide(self, tmp_path): readme = textwrap.dedent("""\