rairo commited on
Commit
ececa88
·
verified ·
1 Parent(s): f21a17d

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +72 -126
main.py CHANGED
@@ -530,7 +530,6 @@ 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
- # Normalize to NFD (Canonical Decomposition) and remove combining characters
534
  s = unicodedata.normalize('NFKD', s).encode('ascii', 'ignore').decode('utf-8')
535
  return s.strip()
536
 
@@ -659,7 +658,8 @@ def get_player_index_brscraper():
659
  return df
660
 
661
  def _scrape_player_index_brscraper():
662
- seasons_to_try_for_index = get_available_seasons_util(num_seasons=3)
 
663
 
664
  for season_str in seasons_to_try_for_index:
665
  end_year = int(season_str.split('–')[1])
@@ -669,6 +669,7 @@ def _scrape_player_index_brscraper():
669
 
670
  if not df.empty and 'Player' in df.columns:
671
  player_names = df['Player'].dropna().unique().tolist()
 
672
  player_names = [normalize_string(name) for name in player_names]
673
  logging.info(f"Successfully retrieved {len(player_names)} players for index from {season_str}.")
674
  return pd.DataFrame({'name': player_names})
@@ -677,49 +678,35 @@ def _scrape_player_index_brscraper():
677
  except Exception as e:
678
  logging.warning(f"Error fetching player index with BRScraper for {season_str}: {e}. Trying next season.")
679
 
 
680
  logging.error("Failed to fetch player index from recent seasons. Falling back to curated common players list.")
681
  common_players = [
682
  'LeBron James', 'Stephen Curry', 'Kevin Durant', 'Giannis Antetokounmpo',
683
- 'Nikola Jokic',
684
- 'Joel Embiid', 'Jayson Tatum', 'Luka Doncic',
685
  'Damian Lillard', 'Jimmy Butler', 'Kawhi Leonard', 'Paul George',
686
  'Anthony Davis', 'Rudy Gobert', 'Donovan Mitchell', 'Trae Young',
687
  'Devin Booker', 'Karl-Anthony Towns', 'Zion Williamson', 'Ja Morant',
688
  'Shai Gilgeous-Alexander', 'Tyrese Maxey', 'Anthony Edwards', 'Victor Wembanyama',
689
- 'Jalen Brunson', 'Paolo Banchero', 'Franz Wagner', 'Cade Cunningham',
690
- 'Michael Jordan', 'Kobe Bryant', 'Larry Bird', 'Magic Johnson', 'Kareem Abdul-Jabbar'
691
  ]
692
  return pd.DataFrame({'name': common_players})
693
 
694
- def get_player_career_stats_brscraper(player_name, seasons_to_fetch: list[str], playoffs=False):
695
  if not BRSCRAPER_AVAILABLE:
696
  logging.error("BRScraper is not available. Cannot fetch player career stats.")
697
  return pd.DataFrame()
 
698
 
 
699
  normalized_player_name = normalize_string(player_name)
700
- all_rows = []
701
 
702
- for season_str in seasons_to_fetch: # Iterate only through the requested seasons
 
 
703
  end_year = int(season_str.split('–')[1])
704
 
705
- # Define cache key for this specific player-season-playoff combination
706
- cache_key = f"{normalized_player_name}_{end_year}_{'playoffs' if playoffs else 'regular'}"
707
- db_ref = db.reference(f'scraped_data/player_season_stats/{cache_key}')
708
-
709
- # Check Firebase cache first for this specific season
710
- if FIREBASE_INITIALIZED:
711
- cached_data = db_ref.get()
712
- if cached_data and not is_data_stale(cached_data.get('last_updated'), max_age_hours=24*7): # Cache for 7 days
713
- logging.info(f"Loading stats for {player_name} in {season_str} (playoffs: {playoffs}) from Firebase cache.")
714
- # Return a DataFrame with a single row for this season
715
- # Note: This function is designed to return a concatenated DF of ALL requested seasons.
716
- # So, if a single season is found in cache, we append it and continue to check others.
717
- all_rows.append(pd.DataFrame.from_records(cached_data['data']))
718
- continue # Skip scraping for this season if found in cache
719
- else:
720
- logging.info(f"Stats for {player_name} in {season_str} cache stale or not found. Scraping...")
721
-
722
- # If not in cache or stale, attempt to scrape
723
  for attempt in range(3): # Try up to 3 times
724
  try:
725
  logging.info(f"DEBUG: Attempt {attempt+1} for nba.get_stats for player '{player_name}' in season {season_str} (year: {end_year}, playoffs: {playoffs})...")
@@ -729,50 +716,40 @@ def get_player_career_stats_brscraper(player_name, seasons_to_fetch: list[str],
729
  if df_season.empty:
730
  logging.warning(f"DEBUG: nba.get_stats returned empty DataFrame for {player_name} in {season_str} on attempt {attempt+1}. Retrying...")
731
  time.sleep(1) # Wait a bit before retrying
732
- continue
733
 
734
  if 'Player' not in df_season.columns:
735
  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...")
736
  time.sleep(1)
737
  continue
738
 
 
739
  df_season['Player_Normalized'] = df_season['Player'].apply(normalize_string)
 
740
  row = df_season[df_season['Player_Normalized'] == normalized_player_name]
741
 
742
  if not row.empty:
743
  row = row.copy()
744
  row['Season'] = season_str
745
- row = row.drop(columns=['Player_Normalized'], errors='ignore') # Remove temp column
746
-
747
- # Save this specific season's data to Firebase cache
748
- if FIREBASE_INITIALIZED:
749
- df_cleaned_for_firebase = clean_df_for_firebase(row.copy())
750
- db_ref.set({
751
- 'last_updated': datetime.utcnow().isoformat(),
752
- 'data': df_cleaned_for_firebase.to_dict(orient='records')
753
- })
754
- logging.info(f"Stats for {player_name} in {season_str} saved to Firebase cache.")
755
-
756
- all_rows.append(row) # Add to results for current request
757
  logging.info(f"DEBUG: Found stats for {player_name} in {season_str} on attempt {attempt+1}. Appending row.")
758
- break # Break retry loop if successful for this season
759
  else:
760
  logging.info(f"DEBUG: Player {player_name} not found in {season_str} stats (after getting season data) on attempt {attempt+1}. Retrying...")
761
  time.sleep(1)
762
- continue
763
 
764
  except Exception as e:
765
  logging.warning(f"DEBUG: Exception on attempt {attempt+1} when fetching {season_str} {'playoff' if playoffs else 'regular season'} stats for {player_name}: {e}")
766
  time.sleep(1) # Wait before next retry
767
- if attempt == 2: # If last attempt failed for this season
768
  logging.error(f"DEBUG: All 3 attempts failed for {player_name} in {season_str}. Giving up on this season.")
769
  continue # Go to next attempt
770
 
771
- # Add a small delay between processing different seasons for the same player
772
- time.sleep(0.5) # Delay between seasons
773
-
774
  if not all_rows:
775
- logging.warning(f"DEBUG: No stats found for {player_name} in the requested seasons: {seasons_to_fetch}. Returning empty DataFrame.")
776
  return pd.DataFrame()
777
 
778
  df = pd.concat(all_rows, ignore_index=True)
@@ -792,7 +769,7 @@ def get_player_career_stats_brscraper(player_name, seasons_to_fetch: list[str],
792
  if col not in non_num:
793
  df[col] = pd.to_numeric(df[col], errors='coerce')
794
 
795
- df['Player'] = player_name
796
  df = df.replace({np.nan: None})
797
  return df
798
 
@@ -970,47 +947,22 @@ def get_player_stats():
970
  all_player_season_data = []
971
  players_with_no_data = []
972
 
973
- # Handle individual player stats (1 player, 1 season)
974
- if len(selected_players) == 1 and len(selected_seasons) == 1:
975
- player_name = selected_players[0]
976
- season_str = selected_seasons[0]
977
- df_player_data = get_player_career_stats_brscraper(player_name, seasons_to_fetch=[season_str], playoffs=False)
978
 
979
- if not df_player_data.empty:
980
- all_player_season_data.append(df_player_data)
981
- logging.info(f"Successfully retrieved data for {player_name} in {season_str}.")
982
- else:
983
  players_with_no_data.append(player_name)
984
- logging.info(f"No data found for {player_name} in {season_str}.")
985
-
986
- # Handle comparison (2 players, 2 seasons)
987
- elif len(selected_players) == 2 and len(selected_seasons) == 2:
988
- player1_name = selected_players[0]
989
- player1_season = selected_seasons[0]
990
- player2_name = selected_players[1]
991
- player2_season = selected_seasons[1]
992
-
993
- df_player1_data = get_player_career_stats_brscraper(player1_name, seasons_to_fetch=[player1_season], playoffs=False)
994
- if not df_player1_data.empty:
995
- all_player_season_data.append(df_player1_data)
996
- logging.info(f"Successfully retrieved data for {player1_name} in {player1_season}.")
997
- else:
998
- players_with_no_data.append(player1_name)
999
- logging.info(f"No data found for {player1_name} in {player1_season}.")
1000
-
1001
- # Add a delay between fetching data for player 1 and player 2
1002
- time.sleep(2) # Introduce a 2-second delay
1003
 
1004
- df_player2_data = get_player_career_stats_brscraper(player2_name, seasons_to_fetch=[player2_season], playoffs=False)
1005
- if not df_player2_data.empty:
1006
- all_player_season_data.append(df_player2_data)
1007
- logging.info(f"Successfully retrieved data for {player2_name} in {player2_season}.")
 
1008
  else:
1009
- players_with_no_data.append(player2_name)
1010
- logging.info(f"No data found for {player2_name} in {player2_season}.")
1011
- else:
1012
- return jsonify({'error': 'Invalid combination of players and seasons. Expected 1 player/1 season or 2 players/2 seasons.'}), 400
1013
-
1014
 
1015
  if not all_player_season_data:
1016
  logging.warning("After processing all players, 'all_player_season_data' is empty. Returning 404.")
@@ -1021,7 +973,11 @@ def get_player_stats():
1021
 
1022
  comparison_df_raw = pd.concat(all_player_season_data, ignore_index=True)
1023
 
1024
- basic_display_df = comparison_df_raw.copy()
 
 
 
 
1025
  basic_cols = ['Player', 'Season', 'GP', 'MIN', 'PTS', 'REB', 'AST', 'STL', 'BLK', 'FG_PCT', 'FT_PCT', 'FG3_PCT']
1026
  basic_display_df = basic_display_df[[c for c in basic_cols if c in basic_display_df.columns]].round(2)
1027
 
@@ -1033,8 +989,13 @@ def get_player_stats():
1033
  lambda r: r['PTS'] / (2 * (r['FGA'] + 0.44 * r['FTA'])) if (r['FGA'] + 0.44 * r['FTA']) else 0,
1034
  axis=1
1035
  )
 
 
 
 
 
1036
  advanced_cols = ['Player', 'Season', 'PTS', 'REB', 'AST', 'FG_PCT', 'TS_PCT']
1037
- advanced_display_df = advanced_df[[c for c in advanced_cols if c in advanced_df.columns]].round(3)
1038
 
1039
  return jsonify({
1040
  'basic_stats': basic_display_df.to_dict(orient='records'),
@@ -1059,45 +1020,21 @@ def get_player_playoff_stats():
1059
  all_player_season_data = []
1060
  players_with_no_data = []
1061
 
1062
- if len(selected_players) == 1 and len(selected_seasons) == 1:
1063
- player_name = selected_players[0]
1064
- season_str = selected_seasons[0]
1065
- df_player_data = get_player_career_stats_brscraper(player_name, seasons_to_fetch=[season_str], playoffs=True)
1066
-
1067
- if not df_player_data.empty:
1068
- all_player_season_data.append(df_player_data)
1069
- logging.info(f"Successfully retrieved playoff data for {player_name} in {season_str}.")
1070
- else:
1071
  players_with_no_data.append(player_name)
1072
- logging.info(f"No playoff data found for {player_name} in {season_str}.")
1073
-
1074
- elif len(selected_players) == 2 and len(selected_seasons) == 2:
1075
- player1_name = selected_players[0]
1076
- player1_season = selected_seasons[0]
1077
- player2_name = selected_players[1]
1078
- player2_season = selected_seasons[1]
1079
-
1080
- df_player1_data = get_player_career_stats_brscraper(player1_name, seasons_to_fetch=[player1_season], playoffs=True)
1081
- if not df_player1_data.empty:
1082
- all_player_season_data.append(df_player1_data)
1083
- logging.info(f"Successfully retrieved playoff data for {player1_name} in {player1_season}.")
1084
- else:
1085
- players_with_no_data.append(player1_name)
1086
- logging.info(f"No playoff data found for {player1_name} in {player1_season}.")
1087
-
1088
- # Add a delay between fetching data for player 1 and player 2
1089
- time.sleep(2) # Introduce a 2-second delay
1090
 
1091
- df_player2_data = get_player_career_stats_brscraper(player2_name, seasons_to_fetch=[player2_season], playoffs=True)
1092
- if not df_player2_data.empty:
1093
- all_player_season_data.append(df_player2_data)
1094
- logging.info(f"Successfully retrieved playoff data for {player2_name} in {player2_season}.")
 
1095
  else:
1096
- players_with_no_data.append(player2_name)
1097
- logging.info(f"No playoff data found for {player2_name} in {player2_season}.")
1098
- else:
1099
- return jsonify({'error': 'Invalid combination of players and seasons. Expected 1 player/1 season or 2 players/2 seasons.'}), 400
1100
-
1101
 
1102
  if not all_player_season_data:
1103
  logging.warning("After processing all players, 'all_player_season_data' is empty for playoffs. Returning 404.")
@@ -1108,7 +1045,11 @@ def get_player_playoff_stats():
1108
 
1109
  comparison_df_raw = pd.concat(all_player_season_data, ignore_index=True)
1110
 
1111
- basic_display_df = comparison_df_raw.copy()
 
 
 
 
1112
  basic_cols = ['Player', 'Season', 'GP', 'MIN', 'PTS', 'REB', 'AST', 'STL', 'BLK', 'FG_PCT', 'FT_PCT', 'FG3_PCT']
1113
  basic_display_df = basic_display_df[[c for c in basic_cols if c in basic_display_df.columns]].round(2)
1114
 
@@ -1120,8 +1061,13 @@ def get_player_playoff_stats():
1120
  lambda r: r['PTS'] / (2 * (r['FGA'] + 0.44 * r['FTA'])) if (r['FGA'] + 0.44 * r['FTA']) else 0,
1121
  axis=1
1122
  )
 
 
 
 
 
1123
  advanced_cols = ['Player', 'Season', 'PTS', 'REB', 'AST', 'FG_PCT', 'TS_PCT']
1124
- advanced_display_df = advanced_df[[c for c in advanced_cols if c in advanced_df.columns]].round(3)
1125
 
1126
  return jsonify({
1127
  'basic_stats': basic_display_df.to_dict(orient='records'),
@@ -1489,4 +1435,4 @@ def manual_player_compare():
1489
 
1490
 
1491
  if __name__ == '__main__':
1492
- app.run(debug=True, host="0.0.0.0", port=7860)
 
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
 
 
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])
 
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})
 
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:
697
  logging.error("BRScraper is not available. Cannot fetch player career stats.")
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})...")
 
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.")
753
  return pd.DataFrame()
754
 
755
  df = pd.concat(all_rows, ignore_index=True)
 
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:
961
+ all_player_season_data.append(filtered_df)
962
+ logging.info(f"Successfully filtered data for {player_name} in requested seasons.")
963
  else:
964
+ logging.info(f"No data found for {player_name} in the specific requested seasons: {selected_seasons}. Adding to no_data list.")
965
+ players_with_no_data.append(player_name)
 
 
 
966
 
967
  if not all_player_season_data:
968
  logging.warning("After processing all players, 'all_player_season_data' is empty. Returning 404.")
 
973
 
974
  comparison_df_raw = pd.concat(all_player_season_data, ignore_index=True)
975
 
976
+ if len(selected_seasons) > 1:
977
+ basic_display_df = comparison_df_raw.groupby('Player').mean(numeric_only=True).reset_index()
978
+ else:
979
+ basic_display_df = comparison_df_raw.copy()
980
+
981
  basic_cols = ['Player', 'Season', 'GP', 'MIN', 'PTS', 'REB', 'AST', 'STL', 'BLK', 'FG_PCT', 'FT_PCT', 'FG3_PCT']
982
  basic_display_df = basic_display_df[[c for c in basic_cols if c in basic_display_df.columns]].round(2)
983
 
 
989
  lambda r: r['PTS'] / (2 * (r['FGA'] + 0.44 * r['FTA'])) if (r['FGA'] + 0.44 * r['FTA']) else 0,
990
  axis=1
991
  )
992
+ if len(selected_seasons) > 1:
993
+ advanced_display_df = advanced_df.groupby('Player').mean(numeric_only=True).reset_index()
994
+ else:
995
+ advanced_display_df = advanced_df.copy()
996
+
997
  advanced_cols = ['Player', 'Season', 'PTS', 'REB', 'AST', 'FG_PCT', 'TS_PCT']
998
+ advanced_display_df = advanced_display_df[[c for c in advanced_cols if c in advanced_display_df.columns]].round(3)
999
 
1000
  return jsonify({
1001
  'basic_stats': basic_display_df.to_dict(orient='records'),
 
1020
  all_player_season_data = []
1021
  players_with_no_data = []
1022
 
1023
+ for player_name in selected_players:
1024
+ df_player_career = get_player_career_stats_brscraper(player_name, playoffs=True)
1025
+ if df_player_career.empty:
1026
+ logging.info(f"No career playoff data found for {player_name}. Adding to no_data list.")
 
 
 
 
 
1027
  players_with_no_data.append(player_name)
1028
+ continue
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1029
 
1030
+ filtered_df = df_player_career[df_player_career['Season'].isin(selected_seasons)].copy()
1031
+
1032
+ if not filtered_df.empty:
1033
+ all_player_season_data.append(filtered_df)
1034
+ logging.info(f"Successfully filtered playoff data for {player_name} in requested seasons.")
1035
  else:
1036
+ logging.info(f"No playoff data found for {player_name} in the specific requested seasons: {selected_seasons}. Adding to no_data list.")
1037
+ players_with_no_data.append(player_name)
 
 
 
1038
 
1039
  if not all_player_season_data:
1040
  logging.warning("After processing all players, 'all_player_season_data' is empty for playoffs. Returning 404.")
 
1045
 
1046
  comparison_df_raw = pd.concat(all_player_season_data, ignore_index=True)
1047
 
1048
+ if len(selected_seasons) > 1:
1049
+ basic_display_df = comparison_df_raw.groupby('Player').mean(numeric_only=True).reset_index()
1050
+ else:
1051
+ basic_display_df = comparison_df_raw.copy()
1052
+
1053
  basic_cols = ['Player', 'Season', 'GP', 'MIN', 'PTS', 'REB', 'AST', 'STL', 'BLK', 'FG_PCT', 'FT_PCT', 'FG3_PCT']
1054
  basic_display_df = basic_display_df[[c for c in basic_cols if c in basic_display_df.columns]].round(2)
1055
 
 
1061
  lambda r: r['PTS'] / (2 * (r['FGA'] + 0.44 * r['FTA'])) if (r['FGA'] + 0.44 * r['FTA']) else 0,
1062
  axis=1
1063
  )
1064
+ if len(selected_seasons) > 1:
1065
+ advanced_display_df = advanced_df.groupby('Player').mean(numeric_only=True).reset_index()
1066
+ else:
1067
+ advanced_display_df = advanced_df.copy()
1068
+
1069
  advanced_cols = ['Player', 'Season', 'PTS', 'REB', 'AST', 'FG_PCT', 'TS_PCT']
1070
+ advanced_display_df = advanced_display_df[[c for c in advanced_cols if c in advanced_display_df.columns]].round(3)
1071
 
1072
  return jsonify({
1073
  'basic_stats': basic_display_df.to_dict(orient='records'),
 
1435
 
1436
 
1437
  if __name__ == '__main__':
1438
+ app.run(debug=True, host="0.0.0.0", port=7860)