SebastianAndreu commited on
Commit
abc7edd
Β·
verified Β·
1 Parent(s): cfd649c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +129 -49
app.py CHANGED
@@ -1,56 +1,98 @@
1
  import gradio as gr
2
  import requests
 
 
3
  import pandas as pd
4
  import os
 
5
  from datetime import datetime
6
- from huggingface_hub import snapshot_download
7
  from autogluon.tabular import TabularPredictor
8
 
 
9
  # --- Download Model and Embeddings ---
10
  def download_model_and_embeddings(repo_id="SebastianAndreu/2025-24679-NFL-Yards-Predictor", local_dir="nfl_model"):
11
  try:
12
- model_path = snapshot_download(
 
 
 
 
 
 
 
 
13
  repo_id=repo_id,
 
14
  repo_type="model",
15
- local_dir=local_dir,
16
- local_dir_use_symlinks=False
17
  )
18
-
19
- model_dir = os.path.join(local_dir, "model")
20
- predictor = TabularPredictor.load(model_dir, verbosity=0)
21
-
22
- emb_path = os.path.join(local_dir, "data", "player_historical_embeddings.csv")
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  if not os.path.exists(emb_path):
24
- emb_path = os.path.join(local_dir, "player_historical_embeddings.csv")
 
25
 
 
26
  emb_df = pd.read_csv(emb_path)
27
-
 
28
  return predictor, emb_df
 
29
  except Exception as e:
 
 
 
30
  return None, None
31
 
 
32
  # Load model at startup
 
33
  predictor, player_embeddings = download_model_and_embeddings()
34
 
 
35
  # Load player and game data
36
  try:
37
  players_df = pd.read_csv("players.csv")
38
  games_df = pd.read_csv("games.csv")
 
39
 
40
  # Create receiver and passer choices from players.csv
 
41
  receivers_df = players_df[
42
  (players_df['position'].isin(['WR', 'TE'])) &
43
  (players_df['status'] == 'ACT')
44
  ].copy()
45
  receiver_choices = sorted(receivers_df['display_name'].dropna().unique().tolist())
46
 
 
47
  passers_df = players_df[
48
  (players_df['position'] == 'QB') &
49
  (players_df['status'] == 'ACT')
50
  ].copy()
51
  passer_choices = sorted(passers_df['display_name'].dropna().unique().tolist())
52
 
 
 
53
  except Exception as e:
 
54
  players_df = pd.DataFrame()
55
  games_df = pd.DataFrame()
56
  receiver_choices = []
@@ -76,13 +118,6 @@ STADIUM_COORDS = {
76
  "TEN": {"lat": 36.1665, "lon": -86.7713}, "WAS": {"lat": 38.9076, "lon": -76.8645}
77
  }
78
 
79
- def get_player_info(player_name, players_df):
80
- """Get player's gsis_id, latest team, and headshot from display_name."""
81
- player_row = players_df[players_df['display_name'] == player_name]
82
- if player_row.empty:
83
- return None, None, None
84
- return player_row.iloc[0]['gsis_id'], player_row.iloc[0]['latest_team'], player_row.iloc[0].get('headshot', None)
85
-
86
  def update_receiver_image(receiver_name):
87
  """Update receiver headshot when selection changes."""
88
  if not receiver_name or players_df.empty:
@@ -96,6 +131,11 @@ def update_passer_image(passer_name):
96
  return None
97
  _, _, headshot = get_player_info(passer_name, players_df)
98
  return headshot
 
 
 
 
 
99
 
100
  def get_game_info(receiver_team, season, week, games_df):
101
  """Get game information based on receiver's team, season, and week."""
@@ -167,6 +207,7 @@ def get_weather_forecast(home_team, game_datetime):
167
  "is_rain": int(is_rain), "is_snow": int(is_snow), "is_clear": int(is_clear)
168
  }
169
  except Exception as e:
 
170
  return None
171
 
172
  def get_game_info_espn(home_team, away_team, week):
@@ -197,7 +238,7 @@ def get_game_info_espn(home_team, away_team, week):
197
  return result
198
 
199
  except Exception as e:
200
- pass
201
 
202
  return result
203
 
@@ -228,9 +269,16 @@ def predict_yards(model_input_dict, receiver_id, passer_id):
228
  }
229
 
230
  input_df = pd.DataFrame(input_data)
 
 
 
 
231
 
232
  # Merge player embeddings if available
233
  if player_embeddings is not None:
 
 
 
234
  emb_df = player_embeddings.copy()
235
  emb_df['player_id'] = emb_df['player_id'].astype(str)
236
 
@@ -243,25 +291,45 @@ def predict_yards(model_input_dict, receiver_id, passer_id):
243
 
244
  emb_cols = [c for c in emb_df.columns if c.startswith("emb_")]
245
  if input_df[emb_cols].isna().any().any():
 
246
  mean_emb = emb_df[emb_cols].mean()
247
  input_df[emb_cols] = input_df[emb_cols].fillna(mean_emb)
 
 
 
 
 
 
 
248
 
249
  # Make prediction
 
 
250
  try:
 
251
  prediction = predictor.predict(input_df)
252
  yards = float(prediction.values[0])
 
253
  return yards, None
254
- except Exception:
255
- try:
256
- leaderboard = predictor.leaderboard(silent=True)
257
- best_model = leaderboard.iloc[0]['model']
258
- prediction = predictor.predict(input_df, model=best_model)
259
- yards = float(prediction.values[0])
260
- return yards, None
261
- except Exception:
262
- return None, "Prediction failed"
263
-
 
 
 
 
 
 
264
  except Exception as e:
 
 
265
  return None, f"Prediction error: {str(e)}"
266
 
267
  def create_model_input_and_predict(receiver_name, passer_name, week, season):
@@ -270,17 +338,17 @@ def create_model_input_and_predict(receiver_name, passer_name, week, season):
270
  # Get receiver info from players.csv
271
  receiver_id, receiver_team, receiver_headshot = get_player_info(receiver_name, players_df)
272
  if receiver_id is None:
273
- return "❌ Prediction Failed", f"Could not find receiver '{receiver_name}'", f"❌ Error: Could not find receiver '{receiver_name}' in database", None, None
274
 
275
  # Get passer info from players.csv
276
  passer_id, passer_team, passer_headshot = get_player_info(passer_name, players_df)
277
  if passer_id is None:
278
- return "❌ Prediction Failed", f"Could not find passer '{passer_name}'", f"❌ Error: Could not find passer '{passer_name}' in database", None, None
279
 
280
  # Get game info from games.csv
281
  game_info = get_game_info(receiver_team, season, week, games_df)
282
  if game_info is None:
283
- return "❌ Prediction Failed", "Game not found", f"❌ Error: Could not find game for {receiver_team} in Week {week} of {season} season", None, None
284
 
285
  home_team = game_info['home_team']
286
  away_team = game_info['away_team']
@@ -340,6 +408,17 @@ def create_model_input_and_predict(receiver_name, passer_name, week, season):
340
  "NYJ": 25, "PHI": 26, "PIT": 27, "SEA": 28, "SF": 29, "TB": 30, "TEN": 31, "WAS": 32
341
  }
342
 
 
 
 
 
 
 
 
 
 
 
 
343
  surface_map = {
344
  "a_turf": 1, "grass": 2, "sportturf": 3,
345
  "fieldturf": 4, "matrixturf": 5, "astroturf": 6, "0": 0
@@ -350,9 +429,8 @@ def create_model_input_and_predict(receiver_name, passer_name, week, season):
350
  home_team_id = team_map.get(home_team, 0)
351
  away_team_id = team_map.get(away_team, 0)
352
 
353
- # Get surface from games.csv
354
- surface_type = game_info.get('surface', 'grass')
355
- surface_id = surface_map.get(surface_type.lower() if surface_type else 'grass', 2)
356
 
357
  # Get betting lines from games.csv or default to 0
358
  pregame_spread = game_info.get('spread_line', 0) or 0
@@ -417,6 +495,8 @@ def create_model_input_and_predict(receiver_name, passer_name, week, season):
417
  return prediction_text, predicted_value, output, receiver_headshot, passer_headshot
418
 
419
  except Exception as e:
 
 
420
  return "❌ Error", f"{str(e)}", f"❌ Error: {str(e)}", None, None
421
 
422
  # Create Gradio interface
@@ -475,21 +555,21 @@ with gr.Blocks(title="NFL Receiver Yards Predictor", theme=gr.themes.Soft()) as
475
  )
476
 
477
  gr.Markdown("""
478
- ### πŸ“‹ How It Works:
479
- 1. **Select Receiver** β†’ Headshot appears instantly
480
- 2. **Select Passer (QB)** β†’ Headshot appears instantly
481
- 3. **Enter Week & Season**
482
- 4. **Click "Predict Yards"** β†’ Get your AI-powered prediction!
483
-
484
- ### ⚑ What Happens Automatically:
485
- - πŸ–ΌοΈ Player headshots load in real-time as you select them
486
- - 🏟️ Determines matchup and venue based on receiver's team schedule
487
- - 🌀️ Fetches live weather forecast for game time
488
- - πŸ’° Loads betting lines (spread & total) from game data
489
- - πŸ€– Generates AI prediction using advanced machine learning model
490
- - πŸ“Š Displays comprehensive game analysis and prediction results
491
  """)
492
 
493
  # Launch the app
494
  if __name__ == "__main__":
495
- app.launch()
 
1
  import gradio as gr
2
  import requests
3
+ import json
4
+ import ast
5
  import pandas as pd
6
  import os
7
+ import pathlib, zipfile, shutil
8
  from datetime import datetime
9
+ from huggingface_hub import hf_hub_download, snapshot_download
10
  from autogluon.tabular import TabularPredictor
11
 
12
+
13
  # --- Download Model and Embeddings ---
14
  def download_model_and_embeddings(repo_id="SebastianAndreu/2025-24679-NFL-Yards-Predictor", local_dir="nfl_model"):
15
  try:
16
+ print(f"Downloading model from {repo_id}...")
17
+
18
+ # --- New Zip-Based Model Loading ---
19
+ ZIP_FILENAME = "autogluon_predictor_dir.zip"
20
+ CACHE_DIR = pathlib.Path(local_dir)
21
+ EXTRACT_DIR = CACHE_DIR / "predictor_native"
22
+
23
+ CACHE_DIR.mkdir(parents=True, exist_ok=True)
24
+ local_zip = hf_hub_download(
25
  repo_id=repo_id,
26
+ filename=ZIP_FILENAME,
27
  repo_type="model",
28
+ local_dir=str(CACHE_DIR),
29
+ local_dir_use_symlinks=False,
30
  )
31
+
32
+ if EXTRACT_DIR.exists():
33
+ shutil.rmtree(EXTRACT_DIR)
34
+ EXTRACT_DIR.mkdir(parents=True, exist_ok=True)
35
+
36
+ with zipfile.ZipFile(local_zip, "r") as zf:
37
+ zf.extractall(str(EXTRACT_DIR))
38
+
39
+ contents = list(EXTRACT_DIR.iterdir())
40
+ predictor_root = contents[0] if (len(contents) == 1 and contents[0].is_dir()) else EXTRACT_DIR
41
+
42
+ print(f"βœ“ Extracted model to: {predictor_root}")
43
+
44
+ predictor = TabularPredictor.load(predictor_root, require_py_version_match=False, verbosity=0)
45
+ print("βœ“ Successfully loaded AutoGluon predictor from zip")
46
+
47
+ # --- Download embeddings (same as before) ---
48
+ emb_path = os.path.join(local_dir, "player_historical_embeddings.csv")
49
  if not os.path.exists(emb_path):
50
+ print(f"Downloading player embeddings from repo...")
51
+ snapshot_download(repo_id=repo_id, repo_type="model", local_dir=local_dir)
52
 
53
+ emb_path = os.path.join(local_dir, "data", "player_historical_embeddings.csv")
54
  emb_df = pd.read_csv(emb_path)
55
+ print(f"βœ“ Loaded {len(emb_df)} player embeddings from {emb_path}")
56
+
57
  return predictor, emb_df
58
+
59
  except Exception as e:
60
+ import traceback
61
+ print(f"Error downloading model or embeddings: {e}")
62
+ traceback.print_exc()
63
  return None, None
64
 
65
+
66
  # Load model at startup
67
+ print("Loading NFL Yards Prediction Model...")
68
  predictor, player_embeddings = download_model_and_embeddings()
69
 
70
+
71
  # Load player and game data
72
  try:
73
  players_df = pd.read_csv("players.csv")
74
  games_df = pd.read_csv("games.csv")
75
+ print(f"βœ“ Loaded {len(players_df)} players and {len(games_df)} games")
76
 
77
  # Create receiver and passer choices from players.csv
78
+ # Filter for receivers (WR, TE) and active players
79
  receivers_df = players_df[
80
  (players_df['position'].isin(['WR', 'TE'])) &
81
  (players_df['status'] == 'ACT')
82
  ].copy()
83
  receiver_choices = sorted(receivers_df['display_name'].dropna().unique().tolist())
84
 
85
+ # Filter for quarterbacks
86
  passers_df = players_df[
87
  (players_df['position'] == 'QB') &
88
  (players_df['status'] == 'ACT')
89
  ].copy()
90
  passer_choices = sorted(passers_df['display_name'].dropna().unique().tolist())
91
 
92
+ print(f"βœ“ Found {len(receiver_choices)} active receivers and {len(passer_choices)} active QBs")
93
+
94
  except Exception as e:
95
+ print(f"⚠ Error loading player/game data: {e}")
96
  players_df = pd.DataFrame()
97
  games_df = pd.DataFrame()
98
  receiver_choices = []
 
118
  "TEN": {"lat": 36.1665, "lon": -86.7713}, "WAS": {"lat": 38.9076, "lon": -76.8645}
119
  }
120
 
 
 
 
 
 
 
 
121
  def update_receiver_image(receiver_name):
122
  """Update receiver headshot when selection changes."""
123
  if not receiver_name or players_df.empty:
 
131
  return None
132
  _, _, headshot = get_player_info(passer_name, players_df)
133
  return headshot
134
+ """Get player's gsis_id, latest team, and headshot from display_name."""
135
+ player_row = players_df[players_df['display_name'] == player_name]
136
+ if player_row.empty:
137
+ return None, None, None
138
+ return player_row.iloc[0]['gsis_id'], player_row.iloc[0]['latest_team'], player_row.iloc[0].get('headshot', None)
139
 
140
  def get_game_info(receiver_team, season, week, games_df):
141
  """Get game information based on receiver's team, season, and week."""
 
207
  "is_rain": int(is_rain), "is_snow": int(is_snow), "is_clear": int(is_clear)
208
  }
209
  except Exception as e:
210
+ print(f"Weather API error: {e}")
211
  return None
212
 
213
  def get_game_info_espn(home_team, away_team, week):
 
238
  return result
239
 
240
  except Exception as e:
241
+ print(f"⚠ ESPN API error: {e}")
242
 
243
  return result
244
 
 
269
  }
270
 
271
  input_df = pd.DataFrame(input_data)
272
+
273
+ print(f"Input DataFrame shape: {input_df.shape}")
274
+ print(f"Input DataFrame dtypes:\n{input_df.dtypes}")
275
+ print(f"Sample input:\n{input_df.head()}")
276
 
277
  # Merge player embeddings if available
278
  if player_embeddings is not None:
279
+ print(f"Merging embeddings for receiver: {receiver_id}")
280
+
281
+ # Ensure player_id column in embeddings is string type for matching
282
  emb_df = player_embeddings.copy()
283
  emb_df['player_id'] = emb_df['player_id'].astype(str)
284
 
 
291
 
292
  emb_cols = [c for c in emb_df.columns if c.startswith("emb_")]
293
  if input_df[emb_cols].isna().any().any():
294
+ print(f"⚠ Missing embeddings for receiver {receiver_id}. Using global mean.")
295
  mean_emb = emb_df[emb_cols].mean()
296
  input_df[emb_cols] = input_df[emb_cols].fillna(mean_emb)
297
+ else:
298
+ print(f"βœ“ Found embeddings for receiver {receiver_id}")
299
+ else:
300
+ print("⚠ No embeddings loaded")
301
+
302
+ print(f"Final input shape: {input_df.shape}")
303
+ print(f"Final columns: {input_df.columns.tolist()}")
304
 
305
  # Make prediction
306
+ yards = None
307
+
308
  try:
309
+ print("Attempting prediction with full ensemble...")
310
  prediction = predictor.predict(input_df)
311
  yards = float(prediction.values[0])
312
+ print(f"βœ“ Success with full ensemble: {yards:.2f} yards")
313
  return yards, None
314
+ except Exception as pred_error:
315
+ print(f"βœ— Full ensemble failed: {str(pred_error)[:100]}")
316
+
317
+ try:
318
+ print("Attempting prediction with best model only...")
319
+ leaderboard = predictor.leaderboard(silent=True)
320
+ best_model = leaderboard.iloc[0]['model']
321
+ print(f"Using best model: {best_model}")
322
+ prediction = predictor.predict(input_df, model=best_model)
323
+ yards = float(prediction.values[0])
324
+ print(f"βœ“ Success with best model: {yards:.2f} yards")
325
+ return yards, None
326
+ except Exception as best_error:
327
+ print(f"βœ— Best model failed: {str(best_error)[:100]}")
328
+
329
+ return None, "All prediction strategies failed."
330
  except Exception as e:
331
+ import traceback
332
+ traceback.print_exc()
333
  return None, f"Prediction error: {str(e)}"
334
 
335
  def create_model_input_and_predict(receiver_name, passer_name, week, season):
 
338
  # Get receiver info from players.csv
339
  receiver_id, receiver_team, receiver_headshot = get_player_info(receiver_name, players_df)
340
  if receiver_id is None:
341
+ return f"❌ Error: Could not find receiver '{receiver_name}' in database", None, None
342
 
343
  # Get passer info from players.csv
344
  passer_id, passer_team, passer_headshot = get_player_info(passer_name, players_df)
345
  if passer_id is None:
346
+ return f"❌ Error: Could not find passer '{passer_name}' in database", None, None
347
 
348
  # Get game info from games.csv
349
  game_info = get_game_info(receiver_team, season, week, games_df)
350
  if game_info is None:
351
+ return f"❌ Error: Could not find game for {receiver_team} in Week {week} of {season} season"
352
 
353
  home_team = game_info['home_team']
354
  away_team = game_info['away_team']
 
408
  "NYJ": 25, "PHI": 26, "PIT": 27, "SEA": 28, "SF": 29, "TB": 30, "TEN": 31, "WAS": 32
409
  }
410
 
411
+ home_team_surface_map = {
412
+ "ARI": "grass", "ATL": "fieldturf", "BAL": "grass", "BUF": "fieldturf",
413
+ "CAR": "fieldturf", "CHI": "grass", "CIN": "fieldturf", "CLE": "grass",
414
+ "DAL": "fieldturf", "DEN": "grass", "DET": "fieldturf", "GB": "grass",
415
+ "HOU": "fieldturf", "IND": "fieldturf", "JAX": "grass", "KC": "grass",
416
+ "LV": "grass", "LAC": "fieldturf", "LAR": "fieldturf", "MIA": "grass",
417
+ "MIN": "fieldturf", "NE": "fieldturf", "NO": "fieldturf", "NYG": "fieldturf",
418
+ "NYJ": "fieldturf", "PHI": "grass", "PIT": "grass", "SF": "grass",
419
+ "SEA": "fieldturf", "TB": "grass", "TEN": "fieldturf", "WAS": "grass"
420
+ }
421
+
422
  surface_map = {
423
  "a_turf": 1, "grass": 2, "sportturf": 3,
424
  "fieldturf": 4, "matrixturf": 5, "astroturf": 6, "0": 0
 
429
  home_team_id = team_map.get(home_team, 0)
430
  away_team_id = team_map.get(away_team, 0)
431
 
432
+ surface_type = game_info.get('surface', home_team_surface_map.get(home_team, "grass"))
433
+ surface_id = surface_map.get(surface_type, 2)
 
434
 
435
  # Get betting lines from games.csv or default to 0
436
  pregame_spread = game_info.get('spread_line', 0) or 0
 
495
  return prediction_text, predicted_value, output, receiver_headshot, passer_headshot
496
 
497
  except Exception as e:
498
+ import traceback
499
+ traceback.print_exc()
500
  return "❌ Error", f"{str(e)}", f"❌ Error: {str(e)}", None, None
501
 
502
  # Create Gradio interface
 
555
  )
556
 
557
  gr.Markdown("""
558
+ ### πŸ“‹ Instructions:
559
+ 1. Select the receiver from the dropdown (headshot appears automatically)
560
+ 2. Select the passer (QB) from the dropdown (headshot appears automatically)
561
+ 3. Enter the week number and season
562
+ 4. Click **"Predict Yards"** to get the prediction
563
+
564
+ The app will automatically:
565
+ - βœ… Display player headshots as you select them
566
+ - βœ… Determine which teams are playing based on the receiver's team
567
+ - βœ… Find the game in the schedule
568
+ - βœ… Fetch weather forecast for the game
569
+ - βœ… Load betting lines from historical data
570
+ - βœ… Generate AI-powered yards prediction
571
  """)
572
 
573
  # Launch the app
574
  if __name__ == "__main__":
575
+ app.launch(share=True, debug=True)