Add historical CRCON storage ingestion and weekly rankings

This commit is contained in:
devRaGonSa
2026-03-20 22:12:43 +01:00
parent 26ba1159de
commit 5788c11ad7
13 changed files with 2129 additions and 2 deletions

View File

@@ -69,5 +69,6 @@ Community website repository with a static landing in the current phase and a pl
- The logical storage foundation for persisting server snapshots is documented in `docs/stats-database-schema-foundation.md`.
- Historical match and player statistics must come from the public CRCON scoreboard JSON layer, not from A2S or the `/games` HTML shell.
- The validated discovery for those historical sources is documented in `docs/historical-crcon-source-discovery.md`.
- The persisted historical domain model for CRCON matches, players and ingestion runs is documented in `docs/historical-domain-model.md`.
- Frontend data consumption should remain progressive, endpoint by endpoint, with static fallbacks preserved during migration.
- The frontend integration strategy is documented in `docs/frontend-data-consumption-plan.md`.

View File

@@ -0,0 +1,102 @@
# TASK-028-historical-domain-model-and-storage-schema
## Goal
Definir e implementar la base de modelo de dominio y almacenamiento para estadísticas históricas de los 2 servidores reales de la comunidad, preparada para ingerir datos desde la capa JSON pública del scoreboard CRCON y para soportar rankings semanales posteriores.
## Context
La fase de discovery ya confirmó que la fuente histórica correcta para estos servidores no es A2S ni el HTML de `/games`, sino la capa JSON pública del scoreboard CRCON. El siguiente paso técnico es crear una base sólida de dominio y persistencia propia para poder ingerir datos históricos, deduplicarlos y consultarlos más adelante desde nuestra propia API.
Esta task NO debe crear todavía páginas históricas en frontend ni depender de URLs públicas de la comunidad como solución de producto. La capa histórica debe vivir en backend, con persistencia propia y preparada para exponer endpoints internos del proyecto en fases posteriores.
## Steps
1. Revisar la documentación de discovery histórica ya creada y confirmar qué datos están disponibles desde la capa JSON pública del scoreboard CRCON.
2. Definir el modelo de dominio histórico mínimo necesario. Incluir al menos:
- servidor
- partida
- mapa
- jugador
- estadísticas de jugador por partida
- ejecución de ingesta histórica
3. Diseñar el esquema de almacenamiento local inicial para esa información.
4. Definir claves e identidad estables para evitar duplicados. Incluir expresamente:
- identificación de partida
- identificación de servidor
- identificación de jugador
- estrategia de idempotencia
5. Incluir campos suficientes para soportar futuras consultas como:
- top kills de la última semana por servidor
- partidas recientes por servidor
- mapas jugados
6. Implementar la base de almacenamiento/esquema local de forma coherente con el backend actual del proyecto.
7. Mantener clara la separación entre:
- estado actual vía A2S
- histórico persistido vía CRCON scoreboard JSON
8. Documentar la estructura creada y cómo se usará en las siguientes tasks.
9. No implementar todavía:
- UI histórica
- páginas nuevas basadas en la URL de la comunidad
- redirecciones o vistas que repliquen la web de la comunidad
- rankings finales expuestos al frontend
10. Al completar la implementación:
- dejar el repositorio consistente
- hacer commit
- hacer push al remoto si el entorno lo permite
## Files to Read First
- AGENTS.md
- ai/repo-context.md
- ai/architecture-index.md
- docs/decisions.md
- docs/historical-crcon-source-discovery.md
- backend/README.md
- backend/app/config.py
- backend/app/routes.py
- backend/app/payloads.py
- cualquier almacenamiento local o capa de persistencia ya existente en `backend/`
- cualquier módulo collector o snapshot ya existente que pueda influir en la estructura de datos
## Expected Files to Modify
- ai/architecture-index.md
- docs/decisions.md
- backend/README.md
- uno o más archivos nuevos de backend para almacenamiento histórico, por ejemplo:
- backend/app/historical_models.py
- backend/app/historical_storage.py
- backend/app/historical_schema.py
- opcionalmente un documento nuevo, por ejemplo:
- docs/historical-domain-model.md
## Constraints
- No basar esta capa histórica en A2S.
- No crear páginas frontend nuevas usando la URL de la comunidad.
- No incrustar ni replicar directamente páginas de `scoreboard.comunidadhll.es`.
- No implementar todavía ingesta completa ni UI histórica.
- No romper el flujo actual de estado en tiempo real.
- No introducir complejidad innecesaria.
- No hacer cambios destructivos.
- Mantener el trabajo centrado en dominio, persistencia e idempotencia.
## Validation
- Existe un modelo de dominio histórico claro para los 2 servidores.
- Existe una base de almacenamiento/esquema local coherente con ese modelo.
- La estructura permite futuras consultas como top kills semanales por servidor.
- Queda clara la separación entre live status y histórico persistido.
- No se han creado páginas frontend nuevas ni soluciones basadas en URLs de la comunidad.
- Los cambios quedan committeados y se hace push al remoto si el entorno lo permite.
## Change Budget
- Preferir menos de 7 archivos modificados o creados.
- Preferir menos de 260 líneas cambiadas.
## Outcome
- Se añadio `backend/app/historical_models.py` como capa minima de dominio para servidor, mapa, partida, jugador, estadisticas por partida y ejecucion de ingesta.
- Se implemento `backend/app/historical_storage.py` con tablas `historical_*` separadas del flujo live A2S, claves estables e `UPSERT` idempotente para partidas, jugadores y estadisticas por jugador.
- Se documento el modelo e identidad estable en `docs/historical-domain-model.md`, y se alinearon `docs/decisions.md`, `ai/architecture-index.md` y `backend/README.md`.
- La inicializacion de storage incluye migracion segura desde una version legacy ya existente en la base SQLite local, preservando los datos previos en la nueva estructura.
## Validation Result
- Validado con `python -m compileall app` desde `backend/`.
- Validado con una comprobacion local de `initialize_historical_storage()`, `list_historical_servers()` y `list_recent_historical_matches(limit=3)`.
- Revisado `git diff --name-only` para confirmar que el cambio quedo centrado en `ai/`, `docs/`, `backend/` y archivos de task.
## Decision Notes
- El historico CRCON comparte el mismo SQLite local de desarrollo que los snapshots A2S para no introducir infraestructura prematura, pero queda estrictamente separado por tablas `historical_*`.

View File

@@ -0,0 +1,97 @@
# TASK-029-historical-crcon-ingestion-bootstrap
## Goal
Implementar una ingesta histórica inicial desde la capa JSON pública del scoreboard CRCON para los 2 servidores reales de la comunidad, persistiendo datos estructurados e idempotentes en el almacenamiento histórico propio del proyecto.
## Context
La fuente histórica real ya está descubierta y el modelo/base de almacenamiento histórico ya debe estar definido en la task previa. El siguiente paso es construir una primera ingesta real que recorra los datos históricos disponibles, los transforme al modelo propio y los guarde de forma segura y reejecutable.
La ingesta debe apoyarse en la capa JSON del scoreboard CRCON, no en A2S ni en scraping del HTML de `/games`, y no debe depender de crear páginas nuevas o de redirigir a la web de la comunidad.
## Steps
1. Revisar la documentación y la estructura histórica definida en la task previa.
2. Implementar un cliente o adaptador para consultar la capa JSON pública del scoreboard CRCON de ambos servidores.
3. Resolver y documentar el mapeo de cada servidor real de la comunidad con su fuente histórica correspondiente.
4. Implementar una ingesta inicial que obtenga y persista, como mínimo:
- servidor
- partida
- fecha/hora de partida
- mapa
- jugador
- kills
- muertes si están disponibles
- otras métricas estables que la fuente ofrezca de forma consistente
5. Diseñar la ingesta para ser idempotente:
- evitar duplicados
- actualizar registros si una partida cambia o se completa más tarde
6. Registrar cada ejecución de ingesta con metadatos útiles:
- inicio
- fin
- estado
- número de partidas procesadas
- número de filas insertadas/actualizadas
7. Documentar cómo lanzar la ingesta manualmente en local.
8. Mantener intacto el flujo actual de estado en tiempo real.
9. No implementar todavía endpoints finales de ranking ni UI histórica.
10. Al completar la implementación:
- dejar el repositorio consistente
- hacer commit
- hacer push al remoto si el entorno lo permite
## Files to Read First
- AGENTS.md
- ai/repo-context.md
- ai/architecture-index.md
- docs/historical-crcon-source-discovery.md
- docs/historical-domain-model.md
- backend/README.md
- backend/app/config.py
- backend/app/routes.py
- backend/app/payloads.py
- backend/app/historical_models.py
- backend/app/historical_storage.py
- cualquier collector o cliente HTTP ya existente en backend
## Expected Files to Modify
- backend/README.md
- backend/app/config.py
- uno o más archivos nuevos o existentes para ingesta histórica, por ejemplo:
- backend/app/historical_ingestion.py
- backend/app/historical_crcon_client.py
- backend/app/historical_storage.py
- backend/app/historical_models.py
- opcionalmente documentación técnica adicional si hace falta aclarar ejecución y alcance
## Constraints
- No basar la ingesta histórica en A2S.
- No scrapear el HTML de `/games` salvo fallback muy justificado y documentado.
- No crear páginas frontend nuevas usando la URL de la comunidad.
- No romper el flujo actual de live status.
- No introducir complejidad innecesaria.
- No hacer cambios destructivos.
- Mantener el trabajo centrado en ingesta, persistencia e idempotencia.
## Validation
- Existe una ingesta histórica inicial real para los 2 servidores.
- La ingesta persiste datos estructurados en almacenamiento propio.
- La ingesta puede reejecutarse sin duplicados graves.
- Queda documentado cómo ejecutarla localmente.
- No se han creado páginas frontend nuevas ni acoplamientos al HTML público de la comunidad.
- Los cambios quedan committeados y se hace push al remoto si el entorno lo permite.
## Change Budget
- Preferir menos de 8 archivos modificados o creados.
- Preferir menos de 320 líneas cambiadas.
## Outcome
- Se implemento `backend/app/historical_ingestion.py` como cliente y adaptador de la capa JSON publica CRCON usando solo libreria estandar.
- La ingesta bootstrap consulta `get_public_info`, `get_scoreboard_maps` y `get_map_scoreboard`, transforma los payloads reales envueltos en `result` y persiste partidas y estadisticas en `historical_*`.
- Cada ejecucion registra metadatos operativos en `historical_ingestion_runs`.
- `backend/README.md` documenta como lanzar el bootstrap manualmente en local.
## Validation Result
- Validado con `python -m compileall app`.
- Validado indirectamente con `python -m app.historical_ingestion refresh --max-pages 1` tras ajustar el cliente al payload real de CRCON; el flujo ya inserta partidas y filas de jugadores en el almacenamiento historico propio.
- No se introdujeron cambios en frontend ni dependencias hacia HTML publico de la comunidad.
## Decision Notes
- La API CRCON actual devuelve los datos historicos bajo una clave top-level `result`; la ingesta desempaqueta esa forma para evitar falsos payloads vacios.

View File

@@ -0,0 +1,80 @@
# TASK-030-historical-crcon-incremental-refresh
## Goal
Añadir un mecanismo de refresco incremental para la ingesta histórica CRCON que permita mantener actualizados los datos de ambos servidores de la comunidad sin reimportar todo el histórico completo en cada ejecución.
## Context
Tras disponer de una ingesta bootstrap, el sistema necesita una estrategia incremental para seguir incorporando partidas nuevas o actualizadas de forma eficiente. Esta task debe construir la capa de refresco histórico incremental sobre la base ya creada, manteniendo idempotencia y trazabilidad.
## Steps
1. Revisar la ingesta histórica bootstrap y el esquema de persistencia existente.
2. Diseñar una estrategia incremental adecuada para la fuente CRCON descubierta:
- paginación
- match ids
- detección de nuevos registros
- actualización de partidas cambiantes
3. Implementar el refresco incremental para ambos servidores.
4. Reutilizar o ampliar el registro de ejecuciones de ingesta para diferenciar:
- bootstrap completo
- refresh incremental
5. Asegurar que el refresco incremental:
- no duplica datos
- no rompe datos ya persistidos
- puede ejecutarse repetidamente
6. Documentar cómo ejecutar este refresco incremental en local.
7. No implementar todavía UI histórica.
8. No acoplar el frontend a las URLs públicas de la comunidad.
9. Al completar la implementación:
- dejar el repositorio consistente
- hacer commit
- hacer push al remoto si el entorno lo permite
## Files to Read First
- AGENTS.md
- ai/repo-context.md
- ai/architecture-index.md
- docs/historical-crcon-source-discovery.md
- docs/historical-domain-model.md
- backend/README.md
- backend/app/config.py
- backend/app/historical_ingestion.py
- backend/app/historical_storage.py
- backend/app/historical_models.py
## Expected Files to Modify
- backend/README.md
- backend/app/config.py
- backend/app/historical_ingestion.py
- backend/app/historical_storage.py
- opcionalmente nuevos módulos auxiliares si mejoran claridad del refresco incremental
## Constraints
- No basar el refresco histórico en A2S.
- No crear páginas frontend nuevas usando la URL de la comunidad.
- No introducir complejidad innecesaria.
- No romper el flujo actual de live status.
- No hacer cambios destructivos.
- Mantener el trabajo centrado en refresco incremental, coherencia y trazabilidad.
## Validation
- Existe un refresco incremental funcional para ambos servidores.
- El sistema puede mantener el histórico sin reimportar todo en cada ejecución.
- La persistencia sigue siendo idempotente.
- La documentación explica el flujo incremental.
- Los cambios quedan committeados y se hace push al remoto si el entorno lo permite.
## Change Budget
- Preferir menos de 6 archivos modificados o creados.
- Preferir menos de 240 líneas cambiadas.
## Outcome
- Se anadio `run_incremental_refresh()` en `backend/app/historical_ingestion.py` reutilizando la misma persistencia y el registro de ejecuciones.
- El refresco incremental calcula un cutoff por servidor desde la ultima partida persistida y aplica una ventana de solape de 12 horas para releer solo paginas recientes y absorber actualizaciones tardias.
- El resultado diferencia `mode: "incremental"` y conserva trazabilidad por servidor y por ejecucion.
## Validation Result
- Validado con `python -m app.historical_ingestion refresh --max-pages 1`.
- La ejecucion proceso las dos fuentes configuradas y persistio nuevas partidas y estadisticas sin reimportar el historico completo.
- La consulta agregada posterior y el route resolver siguieron funcionando sobre la misma base tras el refresh.
## Decision Notes
- El cutoff incremental se apoya en `MAX(ended_at, started_at, created_at_source)` por servidor y no en paginas absolutas, para tolerar cambios de orden o partidas que se completan mas tarde.

View File

@@ -0,0 +1,81 @@
# TASK-031-weekly-top-kills-api
## Goal
Exponer una primera API histórica útil que devuelva el ranking de jugadores con más kills de la última semana para cada uno de los 2 servidores reales de la comunidad.
## Context
El objetivo funcional histórico inicial del proyecto es poder consultar métricas agregadas como “jugadores con más kills de la última semana por servidor”. Con la fuente descubierta, el almacenamiento creado y la ingesta histórica ya en marcha, el siguiente valor real es exponer un endpoint backend estable que pueda alimentar futuras vistas propias del proyecto sin depender directamente de la web de la comunidad.
## Steps
1. Revisar el modelo de dominio histórico y la persistencia ya creada.
2. Definir el endpoint o endpoints mínimos necesarios para top kills semanales por servidor.
3. Diseñar e implementar la consulta agregada usando los datos históricos persistidos.
4. Definir un payload claro que incluya, como mínimo:
- servidor
- rango temporal usado
- jugador
- kills semanales
- posición/ranking
- número de partidas consideradas si resulta viable
5. Asegurar que la definición de “última semana” queda clara y consistente.
6. Documentar el endpoint en el backend.
7. No crear todavía páginas frontend nuevas usando la URL de la comunidad.
8. No incrustar ni duplicar scoreboard externos.
9. Al completar la implementación:
- dejar el repositorio consistente
- hacer commit
- hacer push al remoto si el entorno lo permite
## Files to Read First
- AGENTS.md
- ai/repo-context.md
- ai/architecture-index.md
- docs/historical-domain-model.md
- backend/README.md
- backend/app/routes.py
- backend/app/payloads.py
- backend/app/historical_storage.py
- backend/app/historical_models.py
- backend/app/historical_ingestion.py
## Expected Files to Modify
- backend/app/routes.py
- backend/app/payloads.py
- backend/README.md
- uno o más módulos nuevos o existentes de consulta histórica, por ejemplo:
- backend/app/historical_queries.py
- backend/app/historical_payloads.py
## Constraints
- No basar este endpoint en A2S.
- No consultar directamente la web de la comunidad desde el frontend.
- No crear todavía UI histórica.
- No romper endpoints actuales.
- No introducir complejidad innecesaria.
- No hacer cambios destructivos.
- Mantener el trabajo centrado en la primera API histórica útil y estable.
## Validation
- Existe un endpoint usable para top kills de la última semana por servidor.
- El endpoint funciona para los 2 servidores reales de la comunidad.
- El payload es claro y reutilizable.
- La documentación backend queda alineada.
- No se han creado páginas frontend nuevas ni dependencias directas del frontend respecto a la URL de la comunidad.
- Los cambios quedan committeados y se hace push al remoto si el entorno lo permite.
## Change Budget
- Preferir menos de 6 archivos modificados o creados.
- Preferir menos de 240 líneas cambiadas.
## Outcome
- Se expuso `GET /api/historical/weekly-top-kills` en `backend/app/routes.py`.
- La agregacion vive en `backend/app/historical_storage.py` y calcula top kills semanales con ranking independiente por cada servidor real.
- El payload reutiliza `build_weekly_top_kills_payload()` para devolver rango temporal, jugador, kills semanales, posicion y partidas consideradas.
- `backend/README.md` documenta el endpoint y sus parametros `limit` y `server`.
## Validation Result
- Validado con `python -m compileall app`.
- Validado con una comprobacion local de `resolve_get_payload('/api/historical/weekly-top-kills?limit=3')`, que devolvio `200`, `status: "ok"` y resultados para ambos servidores.
- El endpoint se apoya exclusivamente en historico persistido CRCON y no toca el flujo live A2S ni el frontend.
## Decision Notes
- El limite se aplica por servidor mediante `ROW_NUMBER() OVER (PARTITION BY historical_servers.slug ...)` para que una request con `limit=10` entregue hasta 10 jugadores por cada servidor, no un corte global mezclado.