rairo commited on
Commit
70ff97c
Β·
verified Β·
1 Parent(s): bfe6c69

Player list. Mmh

Browse files
Files changed (1) hide show
  1. main.py +135 -58
main.py CHANGED
@@ -15,6 +15,7 @@ from firebase_admin import credentials, db, storage, auth
15
  import firebase_admin
16
  import logging
17
  import traceback
 
18
  from bs4 import BeautifulSoup, Comment
19
 
20
  try:
@@ -240,7 +241,7 @@ def submit_feedback():
240
  message = data.get('message')
241
 
242
  if not feedback_type or not message:
243
- return jsonify({'error': 'Feedback type and message are required'}), 400
244
 
245
  user_data = db.reference(f'users/{uid}').get()
246
  user_email = user_data.get('email', 'unknown_email')
@@ -525,6 +526,13 @@ def admin_update_credits(uid):
525
  # NBA Analytics Hub Data Fetching Utilities
526
  # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
527
 
 
 
 
 
 
 
 
528
  def clean_firebase_keys(key_name):
529
  if not isinstance(key_name, str):
530
  key_name = str(key_name)
@@ -650,26 +658,39 @@ def get_player_index_brscraper():
650
  return df
651
 
652
  def _scrape_player_index_brscraper():
653
- try:
654
- latest_season_end_year = int(get_available_seasons_util(1)[0].split('–')[1])
655
- logging.info(f"Attempting to get player index for year: {latest_season_end_year}")
656
- df = nba.get_stats(latest_season_end_year, info='per_game', rename=False)
657
- if df.empty or 'Player' not in df.columns:
658
- logging.warning(f"Player index DataFrame empty or 'Player' column missing for {latest_season_end_year}.")
659
- raise ValueError("No player column or empty DataFrame from BRScraper.")
660
- player_names = df['Player'].dropna().unique().tolist()
661
- logging.info(f"Successfully retrieved {len(player_names)} players for index.")
662
- return pd.DataFrame({'name': player_names})
663
- except Exception as e:
664
- logging.error(f"Error fetching player index with BRScraper for {latest_season_end_year}: {e}. Falling back to common players.")
665
- common_players = [
666
- 'LeBron James', 'Stephen Curry', 'Kevin Durant', 'Giannis Antetokounmpo',
667
- 'Nikola Jokic', 'Joel Embiid', 'Jayson Tatum', 'Luka Doncic',
668
- 'Damian Lillard', 'Jimmy Butler', 'Kawhi Leonard', 'Paul George',
669
- 'Anthony Davis', 'Rudy Gobert', 'Donovan Mitchell', 'Trae Young',
670
- 'Devin Booker', 'Karl-Anthony Towns', 'Zion Williamson', 'Ja Morant'
671
- ]
672
- return pd.DataFrame({'name': common_players})
 
 
 
 
 
 
 
 
 
 
 
 
 
673
 
674
  def get_player_career_stats_brscraper(player_name, seasons_to_check=10, playoffs=False):
675
  if not BRSCRAPER_AVAILABLE:
@@ -677,37 +698,55 @@ def get_player_career_stats_brscraper(player_name, seasons_to_check=10, playoffs
677
  return pd.DataFrame()
678
  all_rows = []
679
 
 
 
680
 
681
- seasons_to_try = get_available_seasons_util(seasons_to_check) # Get all potentially available seasons
682
 
683
  for season_str in seasons_to_try:
684
  end_year = int(season_str.split('–')[1])
685
- try:
686
- logging.info(f"DEBUG: Calling nba.get_stats for player '{player_name}' in season {season_str} (year: {end_year}, playoffs: {playoffs})...")
687
-
688
- df_season = nba.get_stats(end_year, info='per_game', playoffs=playoffs, rename=False)
689
-
690
- if df_season.empty:
691
- logging.warning(f"DEBUG: nba.get_stats returned empty DataFrame for {player_name} in {season_str}. Skipping this season.")
692
- continue
693
-
694
- if 'Player' not in df_season.columns:
695
- logging.warning(f"DEBUG: DataFrame for {player_name} in {season_str} has no 'Player' column. Columns: {df_season.columns.tolist()}. Skipping this season.")
696
- continue
697
-
698
- row = df_season[df_season['Player'] == player_name]
699
- if not row.empty:
700
- row = row.copy()
701
- row['Season'] = season_str
702
- all_rows.append(row)
703
- logging.info(f"DEBUG: Found stats for {player_name} in {season_str}. Appending row.")
704
- else:
705
- logging.info(f"DEBUG: Player {player_name} not found in {season_str} stats (after getting season data).")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
706
 
707
- except Exception as e:
708
- # This is where the "not a valid season" error comes from. It's a warning, not a fatal error for the loop.
709
- logging.warning(f"DEBUG: Exception when fetching {season_str} {'playoff' if playoffs else 'regular season'} stats for {player_name}: {e}")
710
- # traceback.print_exc() # Uncomment for full traceback if needed
 
 
711
 
712
  if not all_rows:
713
  logging.warning(f"DEBUG: No stats found for {player_name} across all attempted seasons. Returning empty DataFrame.")
@@ -730,7 +769,7 @@ def get_player_career_stats_brscraper(player_name, seasons_to_check=10, playoffs
730
  if col not in non_num:
731
  df[col] = pd.to_numeric(df[col], errors='coerce')
732
 
733
- df['Player'] = player_name
734
  df = df.replace({np.nan: None})
735
  return df
736
 
@@ -908,19 +947,14 @@ def get_player_stats():
908
  all_player_season_data = []
909
  players_with_no_data = []
910
 
911
- # Iterate through each player requested
912
  for player_name in selected_players:
913
- # Call get_player_career_stats_brscraper to get all available seasons for this player
914
- # This function will log warnings if specific seasons fail to fetch
915
  df_player_career = get_player_career_stats_brscraper(player_name, playoffs=False)
916
 
917
  if df_player_career.empty:
918
  logging.info(f"No career data found for {player_name}. Adding to no_data list.")
919
  players_with_no_data.append(player_name)
920
- continue # Skip to next player if no career data at all
921
 
922
- # Filter the career data for the specific seasons requested in the frontend payload
923
- # Note: selected_seasons here is the list of seasons for *all* players in the request
924
  filtered_df = df_player_career[df_player_career['Season'].isin(selected_seasons)].copy()
925
 
926
  if not filtered_df.empty:
@@ -1269,7 +1303,7 @@ def awards_predictor():
1269
  return jsonify({'error': 'Award type and criteria are required'}), 400
1270
 
1271
  prompt = f"Predict top 5 {award_type} candidates based on {criteria}. Focus on 2024-25 season."
1272
- prediction = ask_perp(prompt,)
1273
  if "Error from AI" in prediction:
1274
  return jsonify({'error': prediction}), 500
1275
 
@@ -1302,7 +1336,7 @@ def young_player_projection():
1302
  "4. Comparison to similar players at the same age. 5. Career trajectory prediction. "
1303
  "Base your analysis on historical player development patterns and current NBA trends."
1304
  )
1305
- projection = ask_perp(prompt, system="You are an NBA projection expert AI.")
1306
  if "Error from AI" in projection:
1307
  return jsonify({'error': projection}), 500
1308
 
@@ -1328,12 +1362,11 @@ def similar_players():
1328
  if "Error from AI" in similar_players_analysis:
1329
  return jsonify({'error': similar_players_analysis}), 500
1330
 
1331
- # Extract user ID from auth header
1332
  auth_header = request.headers.get('Authorization', '')
1333
  token = auth_header.split(' ')[1]
1334
  uid = verify_token(token)
1335
 
1336
- analysis_id = str(uuid.uuid4()) # Unique ID for this analysis
1337
 
1338
  if FIREBASE_INITIALIZED:
1339
  user_analyses_ref = db.reference(f'user_analyses/{uid}')
@@ -1356,6 +1389,50 @@ def similar_players():
1356
  logging.error(f"Error in /api/nba/similar_players: {e}")
1357
  return jsonify({'error': str(e)}), 500
1358
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1359
 
1360
  if __name__ == '__main__':
1361
  app.run(debug=True, host="0.0.0.0", port=7860)
 
15
  import firebase_admin
16
  import logging
17
  import traceback
18
+ import unicodedata # For accent normalization
19
  from bs4 import BeautifulSoup, Comment
20
 
21
  try:
 
241
  message = data.get('message')
242
 
243
  if not feedback_type or not message:
244
+ return jsonify({'error': 'Feedback type and message are_required'}), 400
245
 
246
  user_data = db.reference(f'users/{uid}').get()
247
  user_email = user_data.get('email', 'unknown_email')
 
526
  # NBA Analytics Hub Data Fetching Utilities
527
  # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
528
 
529
+ def normalize_string(s):
530
+ """Removes accent marks and converts to lowercase for consistent comparison."""
531
+ if not isinstance(s, str):
532
+ return str(s)
533
+ s = unicodedata.normalize('NFKD', s).encode('ascii', 'ignore').decode('utf-8')
534
+ return s.strip()
535
+
536
  def clean_firebase_keys(key_name):
537
  if not isinstance(key_name, str):
538
  key_name = str(key_name)
 
658
  return df
659
 
660
  def _scrape_player_index_brscraper():
661
+ # Prioritize getting real player data from recent seasons
662
+ seasons_to_try_for_index = get_available_seasons_util(num_seasons=2) # Try current and previous season
663
+
664
+ for season_str in seasons_to_try_for_index:
665
+ end_year = int(season_str.split('–')[1])
666
+ try:
667
+ logging.info(f"Attempting to get player index for year: {end_year} from BRScraper...")
668
+ df = nba.get_stats(end_year, info='per_game', rename=False)
669
+
670
+ if not df.empty and 'Player' in df.columns:
671
+ player_names = df['Player'].dropna().unique().tolist()
672
+ # Normalize player names before returning
673
+ player_names = [normalize_string(name) for name in player_names]
674
+ logging.info(f"Successfully retrieved {len(player_names)} players for index from {season_str}.")
675
+ return pd.DataFrame({'name': player_names})
676
+ else:
677
+ logging.warning(f"Player index DataFrame empty or 'Player' column missing for {season_str}. Trying next season.")
678
+ except Exception as e:
679
+ logging.warning(f"Error fetching player index with BRScraper for {season_str}: {e}. Trying next season.")
680
+
681
+ # Fallback to a curated list if recent seasons fail
682
+ logging.error("Failed to fetch player index from recent seasons. Falling back to curated common players list.")
683
+ common_players = [
684
+ 'LeBron James', 'Stephen Curry', 'Kevin Durant', 'Giannis Antetokounmpo',
685
+ 'Nikola Jokic', # No accent here, as it will be normalized
686
+ 'Joel Embiid', 'Jayson Tatum', 'Luka Doncic', # No accent here, as it will be normalized
687
+ 'Damian Lillard', 'Jimmy Butler', 'Kawhi Leonard', 'Paul George',
688
+ 'Anthony Davis', 'Rudy Gobert', 'Donovan Mitchell', 'Trae Young',
689
+ 'Devin Booker', 'Karl-Anthony Towns', 'Zion Williamson', 'Ja Morant',
690
+ 'Shai Gilgeous-Alexander', 'Tyrese Maxey', 'Anthony Edwards', 'Victor Wembanyama',
691
+ 'Jalen Brunson', 'Paolo Banchero', 'Franz Wagner', 'Cade Cunningham'
692
+ ]
693
+ return pd.DataFrame({'name': common_players})
694
 
695
  def get_player_career_stats_brscraper(player_name, seasons_to_check=10, playoffs=False):
696
  if not BRSCRAPER_AVAILABLE:
 
698
  return pd.DataFrame()
699
  all_rows = []
700
 
701
+ # Normalize the input player name for consistent lookup
702
+ normalized_player_name = normalize_string(player_name)
703
 
704
+ seasons_to_try = get_available_seasons_util(seasons_to_check)
705
 
706
  for season_str in seasons_to_try:
707
  end_year = int(season_str.split('–')[1])
708
+
709
+ # Implement retry logic for each season fetch
710
+ for attempt in range(3): # Try up to 3 times
711
+ try:
712
+ logging.info(f"DEBUG: Attempt {attempt+1} for nba.get_stats for player '{player_name}' in season {season_str} (year: {end_year}, playoffs: {playoffs})...")
713
+
714
+ df_season = nba.get_stats(end_year, info='per_game', playoffs=playoffs, rename=False)
715
+
716
+ if df_season.empty:
717
+ logging.warning(f"DEBUG: nba.get_stats returned empty DataFrame for {player_name} in {season_str} on attempt {attempt+1}. Retrying...")
718
+ time.sleep(1) # Wait a bit before retrying
719
+ continue # Go to next attempt
720
+
721
+ if 'Player' not in df_season.columns:
722
+ logging.warning(f"DEBUG: DataFrame for {player_name} in {season_str} has no 'Player' column on attempt {attempt+1}. Columns: {df_season.columns.tolist()}. Retrying...")
723
+ time.sleep(1)
724
+ continue
725
+
726
+ # Normalize player names in the DataFrame for comparison
727
+ df_season['Player_Normalized'] = df_season['Player'].apply(normalize_string)
728
+
729
+ row = df_season[df_season['Player_Normalized'] == normalized_player_name]
730
+
731
+ if not row.empty:
732
+ row = row.copy()
733
+ row['Season'] = season_str
734
+ # Remove the temporary normalized column before appending
735
+ row = row.drop(columns=['Player_Normalized'], errors='ignore')
736
+ all_rows.append(row)
737
+ logging.info(f"DEBUG: Found stats for {player_name} in {season_str} on attempt {attempt+1}. Appending row.")
738
+ break # Break retry loop if successful
739
+ else:
740
+ logging.info(f"DEBUG: Player {player_name} not found in {season_str} stats (after getting season data) on attempt {attempt+1}. Retrying...")
741
+ time.sleep(1)
742
+ continue # Go to next attempt
743
 
744
+ except Exception as e:
745
+ logging.warning(f"DEBUG: Exception on attempt {attempt+1} when fetching {season_str} {'playoff' if playoffs else 'regular season'} stats for {player_name}: {e}")
746
+ time.sleep(1) # Wait before next retry
747
+ if attempt == 2: # If last attempt failed
748
+ logging.error(f"DEBUG: All 3 attempts failed for {player_name} in {season_str}. Giving up on this season.")
749
+ continue # Go to next attempt
750
 
751
  if not all_rows:
752
  logging.warning(f"DEBUG: No stats found for {player_name} across all attempted seasons. Returning empty DataFrame.")
 
769
  if col not in non_num:
770
  df[col] = pd.to_numeric(df[col], errors='coerce')
771
 
772
+ df['Player'] = player_name # Ensure original player name is kept
773
  df = df.replace({np.nan: None})
774
  return df
775
 
 
947
  all_player_season_data = []
948
  players_with_no_data = []
949
 
 
950
  for player_name in selected_players:
 
 
951
  df_player_career = get_player_career_stats_brscraper(player_name, playoffs=False)
952
 
953
  if df_player_career.empty:
954
  logging.info(f"No career data found for {player_name}. Adding to no_data list.")
955
  players_with_no_data.append(player_name)
956
+ continue
957
 
 
 
958
  filtered_df = df_player_career[df_player_career['Season'].isin(selected_seasons)].copy()
959
 
960
  if not filtered_df.empty:
 
1303
  return jsonify({'error': 'Award type and criteria are required'}), 400
1304
 
1305
  prompt = f"Predict top 5 {award_type} candidates based on {criteria}. Focus on 2024-25 season."
1306
+ prediction = ask_perp(prompt)
1307
  if "Error from AI" in prediction:
1308
  return jsonify({'error': prediction}), 500
1309
 
 
1336
  "4. Comparison to similar players at the same age. 5. Career trajectory prediction. "
1337
  "Base your analysis on historical player development patterns and current NBA trends."
1338
  )
1339
+ projection = ask_perp(prompt)
1340
  if "Error from AI" in projection:
1341
  return jsonify({'error': projection}), 500
1342
 
 
1362
  if "Error from AI" in similar_players_analysis:
1363
  return jsonify({'error': similar_players_analysis}), 500
1364
 
 
1365
  auth_header = request.headers.get('Authorization', '')
1366
  token = auth_header.split(' ')[1]
1367
  uid = verify_token(token)
1368
 
1369
+ analysis_id = str(uuid.uuid4())
1370
 
1371
  if FIREBASE_INITIALIZED:
1372
  user_analyses_ref = db.reference(f'user_analyses/{uid}')
 
1389
  logging.error(f"Error in /api/nba/similar_players: {e}")
1390
  return jsonify({'error': str(e)}), 500
1391
 
1392
+ @app.route('/api/nba/manual_player_compare', methods=['POST'])
1393
+ @credit_required(cost=1)
1394
+ @cross_origin()
1395
+ def manual_player_compare():
1396
+ try:
1397
+ data = request.get_json()
1398
+ player1_name = data.get('player1_name')
1399
+ player1_season = data.get('player1_season')
1400
+ player2_name = data.get('player2_name')
1401
+ player2_season = data.get('player2_season')
1402
+
1403
+ if not player1_name or not player2_name:
1404
+ return jsonify({'error': 'Both player names are required'}), 400
1405
+
1406
+ player1_str = f"{player1_name} ({player1_season} season)" if player1_season else player1_name
1407
+ player2_str = f"{player2_name} ({player2_season} season)" if player2_season else player2_name
1408
+
1409
+ comparison_context = "Statistical comparison"
1410
+ if player1_season and player2_season:
1411
+ comparison_context += f" (specifically {player1_season} vs {player2_season} seasons)"
1412
+ elif player1_season:
1413
+ comparison_context += f" (specifically {player1_season} season for {player1_name} vs {player2_name}'s career/prime)"
1414
+ elif player2_season:
1415
+ comparison_context += f" (specifically {player1_name}'s career/prime vs {player2_season} season for {player2_name})"
1416
+ else:
1417
+ comparison_context += " (career/prime comparison)"
1418
+
1419
+ prompt = (
1420
+ f"Compare {player1_str} vs {player2_str} in detail: "
1421
+ f"1. {comparison_context}. "
1422
+ "2. Playing style similarities and differences. 3. Strengths and weaknesses of each. "
1423
+ "4. Team impact and role. 5. Overall similarity score (1-10). "
1424
+ "Provide a comprehensive comparison with specific examples."
1425
+ )
1426
+
1427
+ comparison = ask_perp(prompt)
1428
+ if "Error from AI" in comparison:
1429
+ return jsonify({'error': comparison}), 500
1430
+
1431
+ return jsonify({'comparison': comparison})
1432
+ except Exception as e:
1433
+ logging.error(f"Error in /api/nba/manual_player_compare: {e}")
1434
+ return jsonify({'error': str(e)}), 500
1435
+
1436
 
1437
  if __name__ == '__main__':
1438
  app.run(debug=True, host="0.0.0.0", port=7860)