"""Construye un catálogo de partidos (liga/temporada/fecha/local/visitante) para TODAS las ligas Opta, a partir de los nombres de los xlsx en Azure (raw/eventing/opta/Partidos/{liga}/{temporada}/{fecha} - {local} vs {visitante}.xlsx). Listar nombres es barato (no descarga). El catálogo alimenta los selectores de la web (ligas/temporadas/equipos/partidos) para todas las ligas, incluso las que no están en el dataset del modelo. Sale a vendor/data/matches_catalog.parquet (chico). Uso: python scripts/build_matches_catalog.py """ from __future__ import annotations import re import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src")) import pandas as pd from racing_reports.datastore import DataStore PAT = re.compile(r"^(\d{4}-\d{2}-\d{2}) - (.+?) vs (.+?)\.xlsx$") OUT = Path(__file__).resolve().parents[1] / "vendor" / "data" / "matches_catalog.parquet" def main() -> None: ds = DataStore() fs = ds._filesystem_client() base = "raw/eventing/opta/Partidos" rows = [] skipped = 0 for p in fs.get_paths(path=base, recursive=True): name = getattr(p, "name", "") or "" if not name.endswith(".xlsx"): continue parts = name.split("/") if len(parts) < 4: continue league, season, fname = parts[-3], parts[-2], parts[-1] m = PAT.match(fname) if not m: skipped += 1 continue date, home, away = m.group(1), m.group(2).strip(), m.group(3).strip() rows.append({"league": league, "season": season, "date": date, "home_team": home, "away_team": away}) df = pd.DataFrame(rows).drop_duplicates(["league", "season", "date", "home_team", "away_team"]) OUT.parent.mkdir(parents=True, exist_ok=True) df.to_parquet(OUT, index=False) print(f"Catálogo guardado en: {OUT}") print(f"Filas: {len(df)} | ligas: {df['league'].nunique()} | " f"combos liga-temporada: {df.groupby(['league','season']).ngroups} | xlsx sin parsear: {skipped}") if __name__ == "__main__": main()