""" Scoring wrapper: takes a filtered DF and tunable weights, returns scored rows. Reuses the existing functions in scouting_score.py. """ import sys from functools import lru_cache from pathlib import Path import pandas as pd # Allow importing scouting_score.py from the parent directory in dev. In the Docker # image, scouting_score.py is copied into /app so this is a no-op. _PARENT = Path(__file__).resolve().parent.parent if str(_PARENT) not in sys.path: sys.path.insert(0, str(_PARENT)) import scouting_score as ss APP_DIR = Path(__file__).parent # weights xlsx path — searched in DATA_DIR first, fallback to project root def _weights_path(): import os data_dir = Path(os.environ.get('DATA_DIR', APP_DIR.parent / 'data' / 'clean')) candidates = [ data_dir / 'PESOS_POR_VARIABLE.xlsx', data_dir / 'PESOS POR VARIABLE (BLOQUES).xlsx', APP_DIR.parent / 'PESOS POR VARIABLE (BLOQUES).xlsx', ] for c in candidates: if c.exists(): return c raise FileNotFoundError(f'weights xlsx not found; tried: {candidates}') @lru_cache(maxsize=1) def get_block_weights(): return ss.load_weights(_weights_path()) def score_with_weights(df: pd.DataFrame, perf_weight=0.6, prof_weight=0.4, w_age=0.35, w_eu=0.25, w_league=0.20, w_mv=0.20): if df.empty: return df.assign(performance_score=pd.NA, profile_score=pd.NA, scouting_score=pd.NA, percentile_rank_in_position=pd.NA) weights = get_block_weights() df = df.copy() df['mapped_position'] = df['Posicion'].map(ss.POSITION_MAP) df = df[df['mapped_position'].notna()].copy() df = ss.compute_percentile_ranks(df, weights) df = ss.compute_performance_scores(df, weights) def age_factor(a): try: a = float(a) except (TypeError, ValueError): return None if pd.isna(a): return None if a < 23: return 1.0 if a <= 25: return 0.5 return 0.0 df['age_factor'] = df['_age_num'].apply(age_factor) df['eu_nationality_factor'] = df['_eu'].astype(float) def mv_factor(mv): if mv is None or pd.isna(mv): return None if mv < 3: return 1.0 if mv <= 5: return 0.5 return -1 df['market_value_factor'] = df['mv_millions'].apply(mv_factor) df['league_tier_factor'] = df['Competencia'].map(ss.LEAGUE_TIERS).fillna(ss.DEFAULT_LEAGUE_TIER) weights_p = {'age': w_age, 'eu_nationality': w_eu, 'league_tier': w_league, 'market_value': w_mv} def profile_score(row): comps, w_total = [], 0 if row['age_factor'] is not None: comps.append(weights_p['age'] * row['age_factor']); w_total += weights_p['age'] comps.append(weights_p['eu_nationality'] * row['eu_nationality_factor']); w_total += weights_p['eu_nationality'] comps.append(weights_p['league_tier'] * row['league_tier_factor']); w_total += weights_p['league_tier'] if row['market_value_factor'] is not None and row['market_value_factor'] >= 0: comps.append(weights_p['market_value'] * row['market_value_factor']); w_total += weights_p['market_value'] return sum(comps) / w_total if w_total else 0 df['profile_score'] = df.apply(profile_score, axis=1) df['scouting_score'] = (perf_weight * df['performance_score'].fillna(0) + prof_weight * df['profile_score'].fillna(0)) df['percentile_rank_in_position'] = ( df.groupby(['mapped_position', 'Temporada'])['performance_score'].rank(pct=True) ) return df