9.7 KiB
9.7 KiB
Stats Section Functional Plan
Objective
Define the V1 functional contract for a future Stats section where a player can search their profile, review personal historical performance across HLL Vietnam community servers, and understand their weekly and monthly ranking position without implementing backend or frontend yet.
Current Foundation
- HLL Vietnam historical read paths are RCON-first.
backend/app/rcon_historical_leaderboards.pyalready defines weekly and monthly leaderboard windows over materialized RCON/AdminLog match stats.- Current reliable materialized counters for public player-facing V1 are kills, deaths, teamkills and matches considered.
- Public scoreboard data remains fallback or enrichment only; it must not become the normal primary source for this section while RCON coverage exists.
V1 Scope
The first Stats contract covers:
- player search by name or player id
- player detail view for one selected player
- personal totals for a selected timeframe and server scope
- weekly ranking position by kills
- monthly ranking position by kills
- data freshness and source metadata
- annual top 20 preparation through a dedicated snapshot model
V1 should support two server scopes:
- one specific server id
allaggregated across active supported servers
User Flow
- User opens the future
Statssection. - User searches by partial player name or exact player id.
- Backend returns compact search matches ordered by best textual match and recent activity.
- User selects one player result.
- Frontend requests the player detail payload for a chosen server scope and timeframe.
- UI shows core totals, calculated ratios, weekly rank position and monthly rank position.
- UI may later request annual ranking snapshot data for the selected year without recalculating it on each public request.
API Contract Direction
1. Player search
GET /api/stats/players/search?q=<query>&server_id=<server-or-all>&limit=10
Purpose:
- resolve a user-entered name fragment or player id into selectable player records backed by materialized historical data
Query rules:
qis requiredserver_idis optional and defaults toalllimitis optional and defaults to10, with a hard cap such as20
Response shape:
{
"status": "ok",
"data": {
"query": "rambo",
"server_id": "all",
"items": [
{
"player_id": "76561198000000000",
"player_name": "Rambo",
"matches_considered": 42,
"last_seen_at": "2026-06-06T21:40:00Z",
"servers_seen": ["comunidad-hispana-01", "comunidad-hispana-02"]
}
]
}
}
Search result notes:
player_idis the canonical selection key for later requestsplayer_nameis the latest display name known from materialized statsmatches_consideredis the aggregated closed-match count in the selected scopeservers_seenis optional and may be omitted whenserver_idis notall
2. Personal player stats
GET /api/stats/players/{player_id}?server_id=<server-or-all>&timeframe=weekly|monthly|all
Purpose:
- return the selected player summary for the chosen scope and timeframe plus ranking position context
Response shape:
{
"status": "ok",
"data": {
"player_id": "76561198000000000",
"player_name": "Rambo",
"server_id": "all",
"timeframe": "monthly",
"window_start": "2026-06-01T00:00:00Z",
"window_end": "2026-06-07T18:00:00Z",
"matches_considered": 12,
"kills": 356,
"deaths": 241,
"teamkills": 4,
"kd_ratio": 1.48,
"kills_per_match": 29.67,
"deaths_per_match": 20.08,
"weekly_ranking": {
"metric": "kills",
"ranking_position": 8,
"window_kind": "current-week",
"window_start": "2026-06-02T00:00:00Z",
"window_end": "2026-06-07T18:00:00Z"
},
"monthly_ranking": {
"metric": "kills",
"ranking_position": 5,
"window_kind": "current-month",
"window_start": "2026-06-01T00:00:00Z",
"window_end": "2026-06-07T18:00:00Z"
},
"source": {
"primary_source": "rcon",
"read_model": "rcon-materialized-admin-log-player-stats",
"generated_at": "2026-06-07T18:02:00Z",
"source_range_start": "2026-06-01T00:14:00Z",
"source_range_end": "2026-06-07T17:51:00Z",
"freshness": "runtime"
}
}
}
Field rules:
timeframe=weeklyreturns the player totals within the same weekly window policy used by leaderboard readstimeframe=monthlyreturns the player totals within the same monthly window policy used by leaderboard readstimeframe=allreturns all-time totals for the selected scope, but still includes weekly and monthly ranking blocks as separate comparative contextkd_ratio,kills_per_matchanddeaths_per_matchshould be rounded for display-safe payloads
3. Annual ranking snapshot
GET /api/stats/rankings/annual?year=<year>&server_id=<server-or-all>&metric=kills
Purpose:
- return a precomputed annual top 20 leaderboard snapshot without recalculating the full year on each public request
Response shape:
{
"status": "ok",
"data": {
"year": 2026,
"server_id": "all",
"metric": "kills",
"generated_at": "2027-01-01T01:30:00Z",
"snapshot_status": "ready",
"items": [
{
"ranking_position": 1,
"player_id": "76561198000000000",
"player_name": "Rambo",
"metric_value": 4210,
"matches_considered": 148,
"kills": 4210,
"deaths": 2950,
"teamkills": 18,
"kd_ratio": 1.43
}
]
}
}
V1 annual endpoint notes:
- only
metric=killsneeds to be committed in the first annual design pass - annual read path should be snapshot-backed, not runtime full-range aggregation
- if a requested year is not generated yet, respond with a controlled empty or pending state, not a slow public recalculation
Ranking Calculation Rules
Weekly position
- Use the same weekly window selected by
select_leaderboard_window(... timeframe="weekly") - Respect the existing sufficient-sample policy:
- use current week when the closed-match threshold is met
- fall back to previous week when the current week sample is insufficient
- Rank by total kills descending
- Break ties by:
matches_considereddescendingplayer_nameascending
- Player position should be derived from the same aggregate query family as the public leaderboard to avoid contradictory ordering
Monthly position
- Use the same monthly window selected by
select_leaderboard_window(... timeframe="monthly") - Respect the current rule already present in the read model:
- from day 1 to day 7, use previous month
- from day 8 onward, use current month
- Rank by total kills descending with the same tie-break rules used in weekly ranking
Personal totals
- Use the same RCON materialized match/player stats source as leaderboard calculations
- Only closed matches within the selected scope and selected timeframe window should count
- Do not add support, offense, defense or weapon breakdown fields until they are proven reliable in the RCON materialized model
Annual Snapshot Persistence Direction
Future implementation should add a dedicated persistence model instead of recomputing annual top 20 on every request.
Table: rcon_annual_ranking_snapshots
Expected columns:
idserver_idyearmetricgenerated_atsource_range_startsource_range_endsnapshot_statusitem_countgeneration_policynotesnullable
Expected uniqueness:
- unique on
(server_id, year, metric)
Expected behavior:
- one authoritative snapshot row per year, scope and metric
- regenerated only by an explicit batch job, scheduled maintenance command or year-close workflow
Table: rcon_annual_ranking_snapshot_items
Expected columns:
idsnapshot_idranking_positionplayer_idplayer_namemetric_valuematches_consideredkillsdeathsteamkillskd_ratio
Expected uniqueness:
- unique on
(snapshot_id, ranking_position) - unique on
(snapshot_id, player_id)
Expected behavior:
- store only the top 20 rows for the selected annual snapshot
- preserve display-ready values used by the frontend
- keep enough totals to show a concise annual card without joining additional tables at read time
Generation policy
- Run after year close or through an explicit maintenance workflow
- Read from materialized RCON historical data, not live RCON calls
- Default scope should support both
alland individual active servers - Prefer idempotent replace or upsert semantics for a given
(server_id, year, metric) - Store
generated_atand source range metadata for traceability
V1 Non-goals
- Elo or MMR reactivation
- Comunidad Hispana #03 reintroduction
- support, combat, offense or defense ranking when not already reliable in materialized RCON stats
- weapon-level breakdowns
- charts, heatmaps or advanced visualizations
- authenticated or private player profiles
- backend endpoint implementation
- database migrations
- frontend page, components or scripts
Future Extensions After V1
- map-by-map player breakdowns
- server comparison cards per player
- annual ranking filters beyond kills
- profile history charts when a stable event or snapshot series exists
- external links to trusted scoreboard detail when correlation is already validated
Recommended Follow-up Tasks
- add player search endpoint over materialized RCON player stats
- add personal player stats endpoint with weekly and monthly ranking context
- design and implement annual ranking snapshot schema and generation command
- extend frontend data consumption plan with the
Statssection integration sequence - implement the
Statsfrontend section with static-safe fallback behavior