# Datalake e pipelines ## Camadas do datalake ```mermaid flowchart LR RSS[RSS feeds] --> Bronze Bronze --> Silver Silver --> Gold Fixtures[Fixtures CSV/TXT] --> Gold Gold --> JSONL[JSONL treino LM] ``` | Camada | Path | Conteúdo | |--------|------|----------| | **Bronze** | `data/lake/bronze/` | Feed RSS bruto, metadados, parquet por fonte/data | | **Silver** | `data/lake/silver/` | Artigos limpos, times, sentimento, menções lesão | | **Gold** | `data/lake/gold/` | Contexto agregado por confronto + labels | | **Fixtures** | `data/lake/fixtures/` | `world_cup_YYYY.parquet`, brasileirao | --- ## Fontes RSS Configuração: [`data/sources.yaml`](../data/sources.yaml) ```yaml sources: - id: globo_esporte name: Globo Esporte url: https://... ``` | ID | Portal | |----|--------| | `globo_esporte` | Globo Esporte | | `espn_br` | ESPN Brasil | | `uol_esporte` | UOL Esporte | | `fogaonet` | Fogaonet | | `gazeta_esportiva` | Gazeta Esportiva | ```bash collect-news --list-sources Por padrão, até **~1000 artigos únicos** por sync (14 fontes RSS + feeds Google Notícias; limite `RSS_MAX_ENTRIES_PER_SOURCE=160`). ``` --- ## Comandos CLI ### Ingestão | Comando | Descrição | |---------|-----------| | `collect-news` | RSS → bronze | | `daily-sync` | Coleta + pipeline silver | | `import-brasileirao` | Fixtures Brasileirão (openfootball) | | `import-world-cup` | Fixtures Copa 1930–2022 | **Copa do Mundo:** ```bash import-world-cup --list import-world-cup --missing-only import-world-cup --seasons 1970 1982 2022 import-world-cup --force ``` ### Transformação | Comando | Descrição | |---------|-----------| | `run-pipeline silver` | bronze → silver | | `run-pipeline gold --season 2024` | silver + fixtures → gold | | `run-pipeline export` | exporta JSONL para treino LM | ### Previsão | Comando | Descrição | |---------|-----------| | `predict-round` | Rodada `data/rounds/current.json` | | `predict-wc` | Palpites WC (`--json`, `--home`, `--away`) | | `study-wc-model` | Relatório JSON do ensemble | ### Odds e valor | Comando | Descrição | |---------|-----------| | `fetch-wc-odds` | Odds ao vivo → `wc_2026_odds.json` | | `value-wc-odds` | EV positivo vs modelo | ### Benchmark ```bash benchmark-wc-models --eval-season 2022 benchmark-wc-models --eval-season 2022 --mlflow ``` --- ## Arquivos de rodada ### `data/rounds/wc_2026.json` ```json { "season": 2026, "competition": "Copa do Mundo", "phase": "group", "round": 1, "matches": [ { "home_team": "Brasil", "away_team": "Marrocos", "phase": "group", "group": "G" } ] } ``` ### `data/rounds/current.json` Rodada ativa do Brasileirão para `predict-round` e `GET /round/predict`. --- ## Pipeline gold (contexto por jogo) [`pipelines/gold.py`](../pipelines/gold.py) agrega: - Artigos silver dos times - Posição, forma, H2H (fixtures Brasileirão) - Features para baseline e futura LM Modo live (`live_mode=True` na API): usa notícias recentes sem label. --- ## Validação histórica WC [`pipelines/wc_validate.py`](../pipelines/wc_validate.py) - Lista edições e jogos - `validate_historical_match`: `before_date = match_date`, filtro estrito `<` - Sanitiza `NaN` em `group_name` (mata-mata) --- ## Agendamento - Cron local: [`scripts/cron_collect.sh`](../scripts/cron_collect.sh) - GitHub Actions: [`.github/workflows/daily-collect.yml`](../.github/workflows/daily-collect.yml) --- ## Export para treino LM ```python from models.dataset import export_jsonl export_jsonl("data/training/bolao_train.jsonl") ``` Formato JSONL: contexto textual + label `1`/`X`/`2`. Treino: ```bash pip install -e ".[ml]" python -m models.train ``` Integração recomendada: [Unsloth](https://github.com/unslothai/unsloth). --- ## Storage: Parquet local + DuckDB + BigQuery ### Camada local (padrão) | Dado | Path | Formato | |------|------|---------| | Notícias | `data/lake/bronze`, `silver`, `gold` | Parquet particionado | | Fixtures | `data/lake/fixtures/` | Parquet | | Stats Sofascore | `data/lake/sofascore/match_stats.parquet` | Parquet único | | Auditoria Sofascore | `data/lake/sofascore/*_stats.json` | JSON por jogo (opcional) | Cada ingestão Sofascore grava JSON + upsert no parquet. Para reconstruir o parquet a partir dos JSONs: ```bash ingest-sofascore --compact-parquet ingest-sofascore --compact-parquet --json ``` ### Consultas SQL locais (DuckDB) ```bash pip install -e ".[analytics]" lake-query --preset summary lake-query --preset team-xg --team Brasil --limit 10 lake-query --sql "SELECT home_team, AVG(home_xg) AS avg_xg FROM sofascore GROUP BY 1 ORDER BY 2 DESC LIMIT 5" ``` Views disponíveis: `bronze`, `silver`, `gold`, `fixtures` / `silver_fixtures`, `sofascore` / `silver_sofascore`. ### Quando ativar BigQuery + GCS | Gatilho | Ação | |---------|------| | Lake > ~500 MB ou histórico longo de notícias | `sync-gcp --layer all` | | Múltiplos ambientes (dev/staging/prod) | Sync periódico para GCS | | Dashboards / SQL ad hoc na nuvem | BigQuery como camada analítica | | Sofascore no BQ para joins com fixtures | `sync-gcp --layer silver_sofascore` | ```bash pip install -e ".[gcp]" # .env: GCP_PROJECT, BQ_DATASET, GCS_BUCKET (opcional) sync-gcp --list-layers sync-gcp --layer all --truncate sync-gcp --layer silver_sofascore --truncate sync-gcp --layer bronze_sofascore --truncate sync-gcp --layer gold_wc --truncate ``` Tabelas BigQuery (medalhão em `sports_news_lake`): | Camada | Tabela BQ | |--------|-----------| | Bronze notícias | `bronze_articles` | | Bronze Sofascore | `bronze_sofascore_events` | | Silver notícias | `silver_articles` | | Silver Sofascore | `silver_sofascore_match_stats` | | Silver fixtures WC | `silver_fixtures_results` | | Gold bolão | `gold_bolao_context` | | Gold WC features | `gold_wc_match_features` | Tabelas legadas (`sofascore_match_stats`, `fixtures_results`) podem ser removidas manualmente no console BQ após migração. **Não substitua** o Parquet local — a API e os modelos ML continuam lendo arquivos no volume (`LAKE_ROOT`). O BigQuery é camada analítica/backup, não storage primário de runtime. ### GCS (camada de objetos — padrão GCP) No desenho correto, o fluxo é **Parquet local → GCS → BigQuery**: ```mermaid flowchart LR Local[data/lake Parquet] --> GCS[gs://bucket/lake/...] GCS --> BQ[BigQuery sports_news_lake] ``` Layout medalhão no bucket (quando `GCS_BUCKET` está definido): ``` gs://{GCS_BUCKET}/ └── lake/ ├── bronze/articles/... ├── bronze/sofascore/events.parquet ├── silver/articles/... ├── silver/sofascore/match_stats.parquet ├── silver/fixtures/world_cup_fixtures.parquet ├── gold/bolao/... └── gold/wc/match_features.parquet ``` **Configuração no GCP** (service account `modelo-cp@beanalytic-dev.iam.gserviceaccount.com`): 1. Criar bucket, ex.: `beanalytic-dev-sports-news-lake` (região `US`, mesma do BQ) 2. IAM no bucket: `Storage Object Admin` (ou `Storage Admin`) 3. `.env`: `GCS_BUCKET=beanalytic-dev-sports-news-lake` 4. `sync-gcp --layer all --truncate` — sobe Parquet ao GCS e carrega no BQ a partir de `gs://` Sem `GCS_BUCKET`, o `sync-gcp` usa **fallback direto** (Parquet local → BQ), útil em dev quando a SA ainda não tem permissão no Storage. ## Escalabilidade GCP (opcional) ```bash pip install -e ".[gcp]" ``` Variáveis: `GCP_PROJECT`, `BQ_DATASET`, `GCS_BUCKET` — ver `.env.example`.