From 1b3cbbfb1b76ae606c9f1537ceaf762ad700c72c Mon Sep 17 00:00:00 2001 From: devRaGonSa Date: Sun, 7 Jun 2026 21:59:55 +0200 Subject: [PATCH] docs(tasks): update task artifacts for stats workflow --- ...tch-full-player-summary-and-feed-badges.md | 83 +++++ ...efine-stats-section-functional-contract.md | 26 +- ...SK-162-add-stats-player-search-endpoint.md | 113 +++++++ docs/stats-section-functional-plan.md | 311 ++++++++++++++++++ 4 files changed, 522 insertions(+), 11 deletions(-) create mode 100644 ai/tasks/done/TASK-161-current-match-full-player-summary-and-feed-badges.md rename ai/tasks/{pending => done}/TASK-161-define-stats-section-functional-contract.md (84%) create mode 100644 ai/tasks/pending/TASK-162-add-stats-player-search-endpoint.md create mode 100644 docs/stats-section-functional-plan.md diff --git a/ai/tasks/done/TASK-161-current-match-full-player-summary-and-feed-badges.md b/ai/tasks/done/TASK-161-current-match-full-player-summary-and-feed-badges.md new file mode 100644 index 0000000..8b5372b --- /dev/null +++ b/ai/tasks/done/TASK-161-current-match-full-player-summary-and-feed-badges.md @@ -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. diff --git a/ai/tasks/pending/TASK-161-define-stats-section-functional-contract.md b/ai/tasks/done/TASK-161-define-stats-section-functional-contract.md similarity index 84% rename from ai/tasks/pending/TASK-161-define-stats-section-functional-contract.md rename to ai/tasks/done/TASK-161-define-stats-section-functional-contract.md index ea48aad..19eddbf 100644 --- a/ai/tasks/pending/TASK-161-define-stats-section-functional-contract.md +++ b/ai/tasks/done/TASK-161-define-stats-section-functional-contract.md @@ -1,7 +1,7 @@ --- id: TASK-161 title: Define Stats section functional contract -status: pending +status: done type: documentation team: Analista supporting_teams: @@ -176,17 +176,21 @@ Before completing the task ensure: ## Outcome -Document: +Completed as documentation-only. -- validation performed -- files changed -- notable decisions -- recommended next tasks, likely: - - add player stats search endpoint - - add player personal stats endpoint - - add annual leaderboard timeframe support - - design annual ranking snapshot schema - - add Stats frontend section +- Created `docs/stats-section-functional-plan.md` as the focused contract for the future `Stats` section. +- 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`. +- Defined the V1 user flow, player search contract, personal stats payload, ranking position rules and annual top 20 snapshot persistence direction. +- Explicitly kept backend endpoints, database migrations and frontend implementation out of scope. + +## Validation Result + +- Verified `docs/stats-section-functional-plan.md` exists and is specific to HLL Vietnam. +- 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 diff --git a/ai/tasks/pending/TASK-162-add-stats-player-search-endpoint.md b/ai/tasks/pending/TASK-162-add-stats-player-search-endpoint.md new file mode 100644 index 0000000..8164572 --- /dev/null +++ b/ai/tasks/pending/TASK-162-add-stats-player-search-endpoint.md @@ -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=`. + +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. diff --git a/docs/stats-section-functional-plan.md b/docs/stats-section-functional-plan.md new file mode 100644 index 0000000..c883df1 --- /dev/null +++ b/docs/stats-section-functional-plan.md @@ -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=&server_id=&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=&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=&server_id=&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