Download data.py from RRC-ARG/scouting: direct link, hf CLI and curl.
- Browser
- Download file 6.29 kB
-
https://huggingface.co/spaces/RRC-ARG/scouting/resolve/main/data.py
- Command line
-
hf download hf://spaces/RRC-ARG/scouting/data.py
-
curl -L -o data.py https://huggingface.co/spaces/RRC-ARG/scouting/resolve/main/data.py
6.29 kB
| """ | |
| Data layer for the browsable MV database app. | |
| Loads data/clean/mv_database.parquet (one row per player-season, MV-matched) | |
| into DuckDB and exposes filter / detail helpers. No scoring — pure browse. | |
| """ | |
| import os | |
| import sys | |
| import time | |
| from pathlib import Path | |
| from threading import Lock | |
| import duckdb | |
| _PARENT = Path(__file__).resolve().parent.parent | |
| if str(_PARENT) not in sys.path: | |
| sys.path.insert(0, str(_PARENT)) | |
| APP_DIR = Path(__file__).parent | |
| DATA_DIR = Path(os.environ.get('DATA_DIR', APP_DIR.parent / 'data' / 'clean')).resolve() | |
| DB_PARQUET = DATA_DIR / 'mv_database.parquet' | |
| DATASET_REPO = os.environ.get('DATASET_REPO_ID') | |
| HF_TOKEN = os.environ.get('HF_TOKEN') | |
| MAX_AGE_HOURS = float(os.environ.get('DATA_MAX_AGE_HOURS', '24')) | |
| # columns shown in the browse table (compact payload) | |
| TABLE_COLS = ['playerId', 'tm_id', 'Jugador', 'Posicion', 'Temporada', 'age', | |
| 'nationality', 'Equipo', 'Competencia', '"Minutos totales"', | |
| 'mv_eos', 'mv_eos_prev', 'tm_mv_now', 'mv_source', 'match_conf', | |
| 'tm_url', 'tm_contract_until', 'tm_foot'] | |
| _conn = None | |
| _conn_lock = Lock() | |
| def _is_stale(p): | |
| if not p.exists(): | |
| return True | |
| return (time.time() - p.stat().st_mtime) / 3600 > MAX_AGE_HOURS | |
| def bootstrap_data(): | |
| DATA_DIR.mkdir(parents=True, exist_ok=True) | |
| if not _is_stale(DB_PARQUET): | |
| print(f'[data] using local {DB_PARQUET.name}', flush=True) | |
| return | |
| if not DATASET_REPO: | |
| if DB_PARQUET.exists(): | |
| print(f'[data] DATASET_REPO_ID not set; using local {DB_PARQUET.name}', flush=True) | |
| return | |
| raise RuntimeError(f'No data at {DB_PARQUET} and DATASET_REPO_ID not set.') | |
| print(f'[data] pulling {DATASET_REPO} -> {DATA_DIR}', flush=True) | |
| from huggingface_hub import snapshot_download | |
| snapshot_download(repo_id=DATASET_REPO, repo_type='dataset', local_dir=str(DATA_DIR), | |
| local_dir_use_symlinks=False, token=HF_TOKEN, | |
| allow_patterns=['mv_database.parquet']) # app only needs this | |
| global _conn | |
| _conn = None | |
| print('[data] download complete', flush=True) | |
| def get_conn(): | |
| global _conn | |
| with _conn_lock: | |
| if _conn is None: | |
| _conn = duckdb.connect(database=':memory:') | |
| tmp = os.environ.get('DUCKDB_TEMP_DIR', '/tmp/duckdb') | |
| os.makedirs(tmp, exist_ok=True) | |
| _conn.execute(f"PRAGMA memory_limit='{os.environ.get('DUCKDB_MEMORY_LIMIT', '1GB')}'") | |
| _conn.execute(f"PRAGMA threads={os.environ.get('DUCKDB_THREADS', '2')}") | |
| _conn.execute(f"PRAGMA temp_directory='{tmp}'") | |
| _conn.execute(f"CREATE OR REPLACE VIEW base AS SELECT * FROM read_parquet('{DB_PARQUET}');") | |
| return _conn | |
| def season_sort_key(s): | |
| s = str(s).strip() | |
| if '-' in s: | |
| a, b = s.split('-') | |
| return int(a) * 100 + int(b) | |
| try: | |
| return int(s) * 100 | |
| except ValueError: | |
| return 0 | |
| def get_meta(): | |
| conn = get_conn() | |
| seasons = sorted([r[0] for r in conn.execute( | |
| "SELECT DISTINCT Temporada FROM base WHERE Temporada IS NOT NULL").fetchall()], | |
| key=season_sort_key) | |
| leagues = sorted([r[0] for r in conn.execute( | |
| "SELECT DISTINCT Competencia FROM base WHERE Competencia IS NOT NULL").fetchall()]) | |
| positions = sorted([r[0] for r in conn.execute( | |
| "SELECT DISTINCT Posicion FROM base WHERE Posicion IS NOT NULL").fetchall()]) | |
| n_players = conn.execute("SELECT COUNT(DISTINCT playerId) FROM base").fetchone()[0] | |
| n_rows = conn.execute("SELECT COUNT(*) FROM base").fetchone()[0] | |
| return {'seasons': seasons, 'latest_season': seasons[-1] if seasons else None, | |
| 'leagues': leagues, 'positions': positions, | |
| 'n_rows': n_rows, 'n_players': n_players} | |
| def get_rows(seasons=None, leagues=None, positions=None, age_min=None, age_max=None, | |
| min_minutes=0, foot=None, mv_min_millions=None, mv_max_millions=None, | |
| nationality=None, conf=None, sort='mv_eos', sort_dir='desc', limit=2000): | |
| conn = get_conn() | |
| where, params = ['Jugador IS NOT NULL'], [] | |
| if seasons: | |
| where.append(f"Temporada IN ({','.join(['?']*len(seasons))})"); params += seasons | |
| if leagues: | |
| where.append(f"Competencia IN ({','.join(['?']*len(leagues))})"); params += leagues | |
| if positions: | |
| where.append(f"Posicion IN ({','.join(['?']*len(positions))})"); params += positions | |
| if min_minutes: | |
| where.append('"Minutos totales" >= ?'); params.append(float(min_minutes)) | |
| if age_min is not None: | |
| where.append('age >= ?'); params.append(age_min) | |
| if age_max is not None: | |
| where.append('age <= ?'); params.append(age_max) | |
| if foot: | |
| where.append('lower(tm_foot) = ?'); params.append(foot.lower()) | |
| if mv_min_millions is not None: | |
| where.append('mv_eos >= ?'); params.append(mv_min_millions * 1e6) | |
| if mv_max_millions is not None: | |
| where.append('mv_eos <= ?'); params.append(mv_max_millions * 1e6) | |
| if nationality: | |
| where.append('lower(nationality) LIKE ?'); params.append(f'%{nationality.lower()}%') | |
| if conf: | |
| where.append('match_conf = ?'); params.append(conf) | |
| safe_sort = {'mv_eos', 'mv_eos_prev', 'age', 'Minutos totales', 'Jugador', | |
| 'Temporada', 'Competencia', 'Posicion', 'nationality'} | |
| sort_col = sort if sort in safe_sort else 'mv_eos' | |
| direction = 'DESC' if str(sort_dir).lower() == 'desc' else 'ASC' | |
| sql = (f"SELECT {', '.join(TABLE_COLS)} FROM base WHERE {' AND '.join(where)} " | |
| f'ORDER BY "{sort_col}" {direction} NULLS LAST LIMIT {int(limit)}') | |
| return conn.execute(sql, params).df() | |
| def get_player(player_id, season): | |
| conn = get_conn() | |
| df = conn.execute("SELECT * FROM base WHERE playerId = ? AND Temporada = ? LIMIT 1", | |
| [player_id, season]).df() | |
| if df.empty: | |
| return None | |
| history = conn.execute( | |
| """SELECT Temporada, Competencia, "Minutos totales", "Partidos jugados", | |
| Goles_totales, asistencias, mv_eos, mv_eos_prev | |
| FROM base WHERE playerId = ? ORDER BY Temporada""", | |
| [player_id]).df() | |
| return {'row': df.iloc[0].to_dict(), 'history': history.to_dict('records')} | |