Move team activity metrics to /team - #514
Conversation
There was a problem hiding this comment.
Pull request overview
This PR replaces the homepage “Leaderboard” UI with a dedicated /team activity page that presents raw team metrics (sortable columns, optional full roster) and a CSV export that matches the visible cohort and ordering.
Changes:
- Adds
/teampage +/partials/team/metricspartial to render a sortable team metrics table with an “everyone” toggle and time-window persistence. - Reworks export logic to emit raw metric columns (no score/stdev) via
/team.csv, aligned to the current table filters and sort. - Removes homepage leaderboard section and updates navigation, styling, docs, and tests accordingly.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/test_navigation.py | Updates header navigation expectations and validates /team page wiring. |
| tests/test_leaderboard_export.py | Updates export/table tests to assert raw-metric schema and /team.csv + /partials/team/metrics behavior. |
| tests/test_leaderboard_cache.py | Repoints Redis-cache behavior tests from homepage leaderboard partial to team metrics partial. |
| tests/test_github_oauth.py | Updates OAuth redirect expectations for the new partial route and /team URL. |
| templates/team_metrics.html | Adds the /team page template and client-side fetch of the team metrics partial. |
| templates/partials/time_window_controls.html | Preserves additional query params (e.g., sort/everyone) across time-window form submissions. |
| templates/partials/index_leaderboard.html | Repurposes the old leaderboard partial into the sortable team metrics table + export link. |
| templates/index.html | Removes the leaderboard section and client-side loading for it on the homepage. |
| templates/base.html | Adds “Team” to the local pages navigation menu. |
| static/styles.css | Introduces styling for the team metrics header/table and removes leaderboard-specific selectors. |
| README.md | Updates Redis caching docs to refer to team metrics instead of homepage leaderboard. |
| leaderboard_export.py | Replaces score-based export rows with raw team metric rows and CSV rendering. |
| app.py | Adds /team, /partials/team/metrics, and /team.csv; introduces shared context building and sorting/filtering logic. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
templates/team_metrics.html:19
- The metrics section is loaded via
fetch()without HTMX headers. If the GitHub OAuth session is missing/expired, the auth middleware will treat this as a normal HTML request and return a full sign-in page (200) or a redirect, which will be injected into#team-metricsinstead of navigating the browser to/login(and thenextURL may become the partial route). SendingHX-Request/HX-Current-URLand honoringHX-Redirectavoids broken embedded-auth HTML and preserves the correct return URL.
const teamMetrics = document.getElementById('team-metrics');
fetch({{ url_for("team_metrics_partial", **table_query) | tojson }})
.then((response) => response.ok ? response.text() : Promise.reject())
.then((html) => { teamMetrics.innerHTML = html; teamMetrics.removeAttribute('aria-busy'); })
.catch(() => { teamMetrics.innerHTML = '<article>Unable to load team metrics.</article>'; teamMetrics.removeAttribute('aria-busy'); });
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
app.py:1139
- When sorting by
person, the secondary stable sort currently uses the raw string (row[sort_key]) rather than the casefolded key used on the prior line. This makessort=person/sort=-personcase-sensitive and effectively overrides the intended case-insensitive ordering. Consider special-casing thepersonsort to use the same casefolded key (and reverse when needed), while keeping the initial casefolded sort as a tie-breaker for metric columns.
rows.sort(key=lambda row: str(row["person"]).casefold())
rows.sort(key=lambda row: row[sort_key], reverse=sort.startswith("-"))
Issue
Replace the ranked homepage score with a neutral team activity view that can optionally include the full configured roster.
Solution
/teamwith one sortable column per raw metric, defaulting to engineering and PRs merged descendingShow everyonetoggle while preserving the time window and canonical sort choice/projectsTo Test
/has no leaderboard and/teamdefaults to PRs merged descendingShow everyone; confirm the choices persist/team?sort=bogus&everyone=1, then choose 7d; confirm it uses and persistssort=-prs_mergedValidation
ruff format --check .ruff check .mypy .vulture . --config pyproject.tomlpython -m unittest discover -s tests -p 'test_*.py'(195 tests)Proof
Playwright against exact head
9377fa2verified Person ascending and descending order, matching arrows, and matching CSV links. It also verified that an invalid sort fetches/partials/team/metrics?days=30&sort=-prs_merged&everyone=1and that 7d navigation persists the canonical sort. The partial request carriedHX-Request: trueand the full Team URL inHX-Current-URL; OAuth coverage verifies an expired session returns the full-page login target inHX-Redirect. The live CSV attachment name, raw metric schema, full-roster cohort, and selected ordering were also verified.Default engineering view:
Show everyone with PR reviews sorting: