Paginate the leaderboard from materialized player ratings
CI / Build and push docker image (push) Successful in 1m19s
CI / Build and push docker image (push) Successful in 1m19s
Store each player's leaderboard totals (wins, accumulated points and the latest display name) on player_rating alongside the Elo rating, updated in the same transaction as the match, so the board is a single indexed table query instead of aggregating every participation on each request. GET /api/leaderboard is now page-number paginated (page/limit) and returns the page, page count and total, plus the caller's own rank and page when authenticated; focus=me returns the caller's page directly. The migration adds the columns, backfills them from the match history (inserting rows for players from pre-Elo matches at the initial rating) and replaces the index. backfill_elo rebuilds the aggregates too. The web page opens on the page containing the signed-in player, marks their row and adds a numbered footer pager.
This commit is contained in:
+10
-8
@@ -251,8 +251,10 @@ aggregate with `max`, not `sum`.
|
||||
seat, game-defined team label (nullable), whether they won, the points
|
||||
they scored, the Elo change the match produced (`elo_delta`) and
|
||||
game-specific extras (`details` JSON). Unique per `(match, user_sub)`.
|
||||
- `player_rating` — current Elo rating per `(user_sub, game_type)`, with
|
||||
the number of rated matches played.
|
||||
- `player_rating` — current Elo rating and leaderboard totals per
|
||||
`(user_sub, game_type)`: the rating, matches played, wins, accumulated
|
||||
points and the most recent display name. Kept in sync with the match
|
||||
transaction, so the leaderboard is a single indexed table query.
|
||||
|
||||
When a match ends, the engine's result is written transactionally to
|
||||
Postgres (once, guarded by a flag on the session); the finished session
|
||||
@@ -266,10 +268,10 @@ Players carry a chess-style Elo rating per game type
|
||||
mean of its members, and the standard formula
|
||||
`E = 1 / (1 + 10 ** ((R_opp - R_team) / 400))` with `K = 32` decides how
|
||||
many points the match result moves — the same delta for every member of
|
||||
a team, zero-sum between the two teams. Ratings update in the same
|
||||
transaction as the match result. To recompute every rating from the
|
||||
recorded match history (e.g. to backfill matches recorded before ratings
|
||||
existed):
|
||||
a team, zero-sum between the two teams. Ratings and the leaderboard
|
||||
aggregates update in the same transaction as the match result. To
|
||||
rebuild every rating and aggregate from the recorded match history (e.g.
|
||||
to backfill matches recorded before ratings existed):
|
||||
|
||||
```sh
|
||||
.venv/bin/python -m tavolo.platform.backfill_elo \
|
||||
@@ -292,7 +294,7 @@ All endpoints except `/api/health`, `/api/docs`, `/api/openapi.json`,
|
||||
| `GET` | `/api/me/games` | Live games the caller is seated in (waiting lobbies, matches in progress or between hands), newest first, for the lobby's "Continue" list. Finished matches are not included |
|
||||
| `GET` | `/api/me/matches` | Cursor-paginated match history with the engine's `result` and per-player Elo deltas (`?limit=&cursor=&game_type=`) |
|
||||
| `GET` | `/api/me/ratings` | The caller's Elo rating per game type |
|
||||
| `GET` | `/api/leaderboard` | Elo rating, aggregated wins / matches / points per player, sorted by Elo (`?game_type=`) |
|
||||
| `GET` | `/api/leaderboard` | Elo rating, wins / matches / points per rated player, sorted by Elo (`?game_type=`, `?page=`, `?limit=`, `?focus=me`). The response carries the requested page, the page count and — when authenticated — the caller's own rank and page; `focus=me` returns the caller's page directly |
|
||||
|
||||
## WebSocket protocol
|
||||
|
||||
@@ -560,7 +562,7 @@ packages/tavolo-platform/ # game-independent platform (own pyproject + tests
|
||||
├── models.py # Match / MatchPlayer / PlayerRating (Postgres)
|
||||
├── elo.py # chess-style Elo math (1500 start, K=32)
|
||||
├── stats.py # MatchResult -> Postgres persistence + Elo update
|
||||
├── backfill_elo.py # recompute all ratings from the match history
|
||||
├── backfill_elo.py # recompute all ratings and aggregates from match history
|
||||
├── store.py # Redis / in-memory live-session store (+ deadline queue)
|
||||
├── deadlines.py # connection-independent timeout scheduler
|
||||
├── ws.py # WebSocket live-play endpoint (generic envelope)
|
||||
|
||||
Reference in New Issue
Block a user