docs(tasks): update task artifacts for stats workflow

This commit is contained in:
devRaGonSa
2026-06-07 21:59:55 +02:00
parent fbb8adbf01
commit 1b3cbbfb1b
4 changed files with 522 additions and 11 deletions

View File

@@ -0,0 +1,83 @@
---
id: TASK-161
title: Current match full player summary and feed badges
status: done
type: backend
team: Backend Senior
supporting_teams:
- Frontend Senior
- Experto en interfaz
roadmap_item: foundation
priority: medium
---
# TASK-161 - Current match full player summary and feed badges
## Goal
Ampliar el resumen de jugadores de `/api/current-match/players` para incluir a todos los participantes detectados de la partida actual y ajustar el feed de combate para mostrar nombre + facción en una sola línea sin cambiar su lógica funcional.
## Context
`frontend/partida-actual.html` ya expone feed de combate y tabla de estadísticas en vivo. El payload actual de jugadores depende solo de eventos `kill`, por lo que deja fuera jugadores conectados o vistos en la ventana de partida actual sin bajas registradas.
## Steps
1. Revisar parser, storage y payloads actuales de `current match`.
2. Ampliar la agregación backend usando eventos AdminLog y muestra live cuando exista.
3. Ajustar el render del feed para reutilizar el patrón visual de cápsulas de facción.
4. Validar el alcance con tests y checks pedidos.
## Files to Read First
- `AGENTS.md`
- `ai/architecture-index.md`
- `ai/repo-context.md`
- `backend/app/rcon_admin_log_storage.py`
- `backend/app/payloads.py`
- `frontend/assets/js/partida-actual.js`
## Expected Files to Modify
- `backend/app/rcon_admin_log_storage.py`
- `backend/app/payloads.py`
- `backend/tests/test_current_match_payload.py`
- `backend/tests/test_rcon_admin_log_storage.py`
- `frontend/assets/js/partida-actual.js`
- `frontend/assets/css/historico.css`
## Constraints
- No tocar histórico, rankings, retención, mantenimiento BDD ni export Gitea.
- No cambiar endpoints ni contratos incompatibles del frontend.
- No cambiar la lógica funcional del feed de combate.
- Mantener la derivación principal de estadísticas desde AdminLog y ampliarla.
## Validation
- `python -m compileall backend/app`
- `$env:PYTHONPATH = "backend"`
- `python -m unittest backend.tests.test_current_match_payload`
- `python -m unittest discover -s backend/tests -p "*current*"`
- `python -m unittest discover -s backend/tests -p "*rcon*"`
- `node --check frontend/assets/js/partida-actual.js`
- `git diff --check`
## Outcome
- `backend/app/rcon_admin_log_storage.py` ahora construye el resumen vivo de participantes de la ventana actual combinando eventos `kill`, `connected`, `disconnected`, `team_switch`, `chat` y `message`, manteniendo las estadísticas de bajas derivadas desde `kill`.
- `frontend/assets/js/partida-actual.js` y `frontend/assets/css/historico.css` ajustan solo el render visual del feed para mostrar nombre + cápsula en la misma línea reutilizando el badge de equipo de la tabla.
- Validación ejecutada:
- `python -m compileall backend/app`
- `node --check frontend/assets/js/partida-actual.js`
- `git diff --check`
- Arnes Python local que ejecuta directamente las funciones `test_*` de `backend/tests/test_current_match_payload.py` y `backend/tests/test_rcon_admin_log_storage.py`
- Observaciones de validación:
- `python -m unittest backend.tests.test_current_match_payload` y `python -m unittest discover -s backend/tests -p "*current*"` no detectan tests porque esos módulos usan estilo `pytest`.
- `python -m unittest discover -s backend/tests -p "*rcon*"` falla por un problema ajeno y preexistente en `test_rcon_materialization_pipeline`.
- Limitación conocida: el helper live actual del repositorio expone conteos y marcador (`GetServerInformation`), pero no un roster nominal de jugadores; por tanto el resumen usa todas las señales nominales ya persistidas en AdminLog y no puede inventar jugadores silenciosos si nunca fueron vistos por ningún evento.
## Change Budget
- Preferir menos de 5 archivos si es viable; si no, mantener el cambio concentrado en current match.
- Preferir cambios pequeños y verificables por tests.

View File

@@ -1,7 +1,7 @@
--- ---
id: TASK-161 id: TASK-161
title: Define Stats section functional contract title: Define Stats section functional contract
status: pending status: done
type: documentation type: documentation
team: Analista team: Analista
supporting_teams: supporting_teams:
@@ -176,17 +176,21 @@ Before completing the task ensure:
## Outcome ## Outcome
Document: Completed as documentation-only.
- validation performed - Created `docs/stats-section-functional-plan.md` as the focused contract for the future `Stats` section.
- files changed - Kept the contract aligned with the current RCON-first materialized historical read model and the weekly/monthly window policy already present in `backend/app/rcon_historical_leaderboards.py`.
- notable decisions - Defined the V1 user flow, player search contract, personal stats payload, ranking position rules and annual top 20 snapshot persistence direction.
- recommended next tasks, likely: - Explicitly kept backend endpoints, database migrations and frontend implementation out of scope.
- add player stats search endpoint
- add player personal stats endpoint ## Validation Result
- add annual leaderboard timeframe support
- design annual ranking snapshot schema - Verified `docs/stats-section-functional-plan.md` exists and is specific to HLL Vietnam.
- add Stats frontend section - Verified the document references the RCON-first materialized data direction.
- Verified the document defines V1 scope, API contracts, payload expectations and annual snapshot direction.
- Verified no backend implementation files were modified for this task.
- Verified no frontend implementation files were modified for this task.
- No automated or integration tests applied because this task is documentation-only.
## Change Budget ## Change Budget

View File

@@ -0,0 +1,113 @@
---
id: TASK-162
title: Add Stats player search endpoint (RCON materialized backend V1)
status: pending
type: backend
team: Backend Senior
supporting_teams: [Arquitecto de Base de Datos, Arquitecto Python]
roadmap_item: foundation
priority: medium
---
# TASK-162 - Add Stats player search endpoint (RCON materialized backend V1)
## Goal
Implement a backend V1 endpoint to support player search for the future Stats section:
`GET /api/stats/players/search?q=<query>`.
The endpoint must use existing RCON materialized tables and return search matches that include:
- `status`
- `query`
- `items`
- `player_id`
- `player_name`
- `matches_considered`
- `last_seen_at`
- optional `servers_seen` only if it can be added with low scope cost
No frontend work, no migrations, no yearly ranking, no Elo/MMR workarounds, no Comunidad Hispana #03 reactivation.
## Context
The repository already has a RCON materialized model (`rcon_match_player_stats` + `rcon_materialized_matches`) used by leaderboard reads.
The new endpoint is the first part of the Stats backend contract defined in `docs/stats-section-functional-plan.md`.
It should reuse existing routing/payload conventions in the current backend bootstrap.
## Steps
1. Inspect required files first (mandatory before implementation):
- `AGENTS.md`
- `ai/repo-context.md`
- `ai/architecture-index.md`
- `docs/stats-section-functional-plan.md`
- `backend/app/rcon_historical_leaderboards.py`
- `backend/app/main.py`
- `backend/app/rcon_admin_log_materialization.py` (for table/schema context)
2. Reuse the existing `routes.py` + `payloads.py` endpoint/payload pattern.
3. Implement one new endpoint: `GET /api/stats/players/search`.
4. Validate `q` parameter and optional `server` or `server_id` and `limit` handling using current project conventions.
5. Query existing RCON materialized tables with read-only logic (preferably `rcon_match_player_stats` joined with `rcon_materialized_matches`).
6. Keep response shape aligned with existing backend payload conventions.
7. Add a minimal, self-contained module if needed (preferably `backend/app/rcon_historical_player_stats.py`).
8. Run backend validation/checks and document outcome.
## Files to Read First
- `AGENTS.md`
- `ai/repo-context.md`
- `ai/architecture-index.md`
- `docs/stats-section-functional-plan.md`
- `backend/app/main.py`
- `backend/app/routes.py`
- `backend/app/payloads.py`
- `backend/app/rcon_historical_leaderboards.py`
- `backend/app/rcon_admin_log_materialization.py`
## Expected Files to Modify
- `backend/app/rcon_historical_player_stats.py` (new module preferred)
- `backend/app/payloads.py`
- `backend/app/routes.py`
- `backend/app/main.py` (solo si se detecta una dependencia de export explícita o import requerido)
## Constraints
- Keep the change small and verifiable.
- Keep frontend untouched.
- No migrations.
- No annual ranking implementation.
- No Elo/MMR reactivation.
- No Comunidad Hispana #03 behavior reintroduction.
- No historical worker changes.
- Do not modify `frontend/assets/js/partida-actual.js`.
- Do not create unnecessary abstractions beyond this endpoint.
- If `/ai/` is partially ignored by git-exclude, document traceability impact only.
## Validation
Before considering the task complete:
- Run `scripts/run-integration-tests.ps1`.
- Run relevant backend tests if they exist for stats/player or historical materialized reads.
- If no dedicated tests exist for this endpoint, run manual validation with `curl` or `Invoke-WebRequest`.
- Validate `git diff --name-only` and check that only expected files were changed.
- Move task to `ai/tasks/done` on completion, or to `ai/tasks/review` if human validation is required.
## Outcome (for worker)
Document:
- endpoint contract implemented and observed behavior
- SQL/search strategy used (scope, match logic, ordering, fallback behavior)
- validation command results
- whether `servers_seen` was included and why
- any schema/data limitations found
- any blocker from git tracking (`/ai/` ignore behavior) and impact on traceability if relevant
## Change Budget
- Prefer <5 modified files.
- Prefer <200 added/changed lines per file when feasible.
- Split into follow-up tasks if the scope starts expanding.

View File

@@ -0,0 +1,311 @@
# 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.py` already 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
- `all` aggregated across active supported servers
## User Flow
1. User opens the future `Stats` section.
2. User searches by partial player name or exact player id.
3. Backend returns compact search matches ordered by best textual match and recent activity.
4. User selects one player result.
5. Frontend requests the player detail payload for a chosen server scope and timeframe.
6. UI shows core totals, calculated ratios, weekly rank position and monthly rank position.
7. 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
```http
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:
- `q` is required
- `server_id` is optional and defaults to `all`
- `limit` is optional and defaults to `10`, with a hard cap such as `20`
Response shape:
```json
{
"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_id` is the canonical selection key for later requests
- `player_name` is the latest display name known from materialized stats
- `matches_considered` is the aggregated closed-match count in the selected scope
- `servers_seen` is optional and may be omitted when `server_id` is not `all`
### 2. Personal player stats
```http
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:
```json
{
"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=weekly` returns the player totals within the same weekly window policy used by leaderboard reads
- `timeframe=monthly` returns the player totals within the same monthly window policy used by leaderboard reads
- `timeframe=all` returns all-time totals for the selected scope, but still includes weekly and monthly ranking blocks as separate comparative context
- `kd_ratio`, `kills_per_match` and `deaths_per_match` should be rounded for display-safe payloads
### 3. Annual ranking snapshot
```http
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:
```json
{
"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=kills` needs 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_considered` descending
- `player_name` ascending
- 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:
- `id`
- `server_id`
- `year`
- `metric`
- `generated_at`
- `source_range_start`
- `source_range_end`
- `snapshot_status`
- `item_count`
- `generation_policy`
- `notes` nullable
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:
- `id`
- `snapshot_id`
- `ranking_position`
- `player_id`
- `player_name`
- `metric_value`
- `matches_considered`
- `kills`
- `deaths`
- `teamkills`
- `kd_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 `all` and individual active servers
- Prefer idempotent replace or upsert semantics for a given `(server_id, year, metric)`
- Store `generated_at` and 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 `Stats` section integration sequence
- implement the `Stats` frontend section with static-safe fallback behavior