bug fix

import tkinter as tk
from tkinter import ttk, messagebox
import nfl_data_py as nfl
import time
import pandas as pd

# Precomputed dictionary of NFL team names to their abbreviations
TEAM_ABBREVIATIONS = {
    "Arizona Cardinals": "ARI",
    "Atlanta Falcons": "ATL",
    "Baltimore Ravens": "BAL",
    "Buffalo Bills": "BUF",
    "Carolina Panthers": "CAR",
    "Chicago Bears": "CHI",
    "Cincinnati Bengals": "CIN",
    "Cleveland Browns": "CLE",
    "Dallas Cowboys": "DAL",
    "Denver Broncos": "DEN",
    "Detroit Lions": "DET",
    "Green Bay Packers": "GB",
    "Houston Texans": "HOU",
    "Indianapolis Colts": "IND",
    "Jacksonville Jaguars": "JAX",
    "Kansas City Chiefs": "KC",
    "Las Vegas Raiders": "LV",
    "Los Angeles Chargers": "LAC",
    "Los Angeles Rams": "LA",
    "Miami Dolphins": "MIA",
    "Minnesota Vikings": "MIN",
    "New England Patriots": "NE",
    "New Orleans Saints": "NO",
    "New York Giants": "NYG",
    "New York Jets": "NYJ",
    "Philadelphia Eagles": "PHI",
    "Pittsburgh Steelers": "PIT",
    "San Francisco 49ers": "SF",
    "Seattle Seahawks": "SEA",
    "Tampa Bay Buccaneers": "TB",
    "Tennessee Titans": "TEN",
    "Washington Commanders": "WAS"
}

# Define available statistics
STAT_OPTIONS = {
    'Passing Yards': 'passing_yards',
    'Rushing Yards': 'rushing_yards',
    'Receiving Yards': 'receiving_yards',
    'Touchdowns': 'touchdowns'
}

def get_top_players(selected_teams, selected_stat, min_threshold, apply_min_filter, max_players):
    # Convert team names to abbreviations
    selected_team_abbrs = [TEAM_ABBREVIATIONS[team] for team in selected_teams]
    
    # Fetch play-by-play data for the specified season
    try:
        print("Fetching play-by-play data...")
        df = nfl.import_pbp_data([2023])  # Replace with desired year(s)
        print("Data fetched successfully.")
        # Uncomment below to inspect the columns
        # print("Columns available in DataFrame:")
        # print(df.columns)
    except Exception as e:
        print(f"Error fetching data: {e}")
        messagebox.showerror("Data Fetch Error", f"An error occurred while fetching data:\n{e}")
        return pd.DataFrame()
    
    # Filter plays based on selected teams
    team_df = df[df['posteam'].isin(selected_team_abbrs)]
    
    # Determine the player and stat columns based on selected_stat
    if selected_stat == 'passing_yards':
        player_col = 'passer_player_name'
        stat_col = 'passing_yards'
    elif selected_stat == 'rushing_yards':
        player_col = 'rusher_player_name'
        stat_col = 'rushing_yards'
    elif selected_stat == 'receiving_yards':
        player_col = 'receiver_player_name'
        stat_col = 'receiving_yards'
    elif selected_stat == 'touchdowns':
        player_col = None  # Will handle separately
        stat_col = 'touchdowns'
    else:
        player_col = 'passer_player_name'
        stat_col = 'passing_yards'
    
    if selected_stat != 'touchdowns':
        # Handle passing, rushing, and receiving yards
        # Drop rows where the relevant player column or stat is NaN or zero
        relevant_df = team_df.dropna(subset=[player_col, stat_col])
        relevant_df = relevant_df[relevant_df[stat_col] > 0]
        
        # Group by the relevant player column and aggregate the stats
        players_stats = relevant_df.groupby(player_col).agg({
            stat_col: 'sum',
            'play_id': 'nunique'  # Using unique play events as a proxy for "plays_count"
        }).reset_index().rename(columns={'play_id': 'plays_count'})
        
        # Apply minimum plays filter if enabled
        if apply_min_filter:
            players_stats = players_stats[players_stats['plays_count'] >= min_threshold]
        
        # Remove players with missing stats
        players_stats = players_stats.dropna(subset=[stat_col])
        
        # Sort players based on selected stat in descending order
        players_sorted = players_stats.sort_values(by=stat_col, ascending=False)
    else:
        # Handle touchdowns by aggregating all touchdown types
        # Extract passing touchdowns
        pass_td = team_df[['passer_player_name', 'pass_touchdown']].dropna()
        pass_td = pass_td[pass_td['pass_touchdown'] == 1]
        pass_td = pass_td.rename(columns={
            'passer_player_name': 'player_name',
            'pass_touchdown': 'touchdowns'
        })
        
        # Extract rushing touchdowns
        rush_td = team_df[['rusher_player_name', 'rush_touchdown']].dropna()
        rush_td = rush_td[rush_td['rush_touchdown'] == 1]
        rush_td = rush_td.rename(columns={
            'rusher_player_name': 'player_name',
            'rush_touchdown': 'touchdowns'
        })
        
        # Extract receiving touchdowns
        rec_td = team_df[['receiver_player_name', 'rec_touchdown']].dropna()
        rec_td = rec_td[rec_td['rec_touchdown'] == 1]
        rec_td = rec_td.rename(columns={
            'receiver_player_name': 'player_name',
            'rec_touchdown': 'touchdowns'
        })
        
        # Combine all touchdowns into a single DataFrame
        all_tds = pd.concat([pass_td, rush_td, rec_td], ignore_index=True)
        
        # Group by player_name and sum touchdowns
        players_stats = all_tds.groupby('player_name').agg({
            'touchdowns': 'sum'
        }).reset_index()
        
        # Apply minimum touchdowns filter if enabled
        if apply_min_filter:
            players_stats = players_stats[players_stats['touchdowns'] >= min_threshold]
        
        # Remove players with missing stats
        players_stats = players_stats.dropna(subset=['touchdowns'])
        
        # Sort players based on touchdowns in descending order
        players_sorted = players_stats.sort_values(by='touchdowns', ascending=False)
    
    if selected_stat != 'touchdowns':
        # Map player to team
        player_team_map = team_df[[player_col, 'posteam']].dropna().drop_duplicates()
        # Count number of plays per player per team
        player_team_counts = player_team_map.groupby([player_col, 'posteam']).size().reset_index(name='count')
        # Assign the team with the highest count to each player
        player_primary_team = player_team_counts.loc[player_team_counts.groupby(player_col)['count'].idxmax()]
        player_primary_team = player_primary_team.rename(columns={player_col: 'player_name', 'posteam': 'team_abbr'})
        # Merge with sorted players
        players_sorted = players_sorted.merge(player_primary_team, left_on=player_col, right_on='player_name', how='left')
        # Map team abbreviations back to team names
        players_sorted['Team'] = players_sorted['team_abbr'].map({v: k for k, v in TEAM_ABBREVIATIONS.items()})
    else:
        # Map player to team for touchdowns
        player_team_map = all_tds[['player_name', 'posteam']].dropna().drop_duplicates()
        player_team_map = player_team_map.rename(columns={'posteam': 'team_abbr'})
        players_sorted = players_sorted.merge(player_team_map, on='player_name', how='left')
        # Map team abbreviations back to team names
        players_sorted['Team'] = players_sorted['team_abbr'].map({v: k for k, v in TEAM_ABBREVIATIONS.items()})
    
    # Handle missing team names
    players_sorted['Team'] = players_sorted['Team'].fillna('Unknown Team')
    
    # Prepare final DataFrame
    if selected_stat != 'touchdowns':
        final_df = players_sorted[[player_col, stat_col, 'plays_count', 'Team']]
        final_df = final_df.rename(columns={
            player_col: 'Player',
            stat_col: selected_stat_key.capitalize(),
            'plays_count': 'Plays Counted'
        })
    else:
        final_df = players_sorted[['player_name', 'touchdowns', 'Team']]
        final_df = final_df.rename(columns={
            'player_name': 'Player',
            'touchdowns': 'Touchdowns'
        })
    
    return final_df

def display_top_players():
    selected_teams = [team_listbox.get(i) for i in team_listbox.curselection()]
    selected_stat_key = selected_stat.get()
    selected_stat_value = STAT_OPTIONS[selected_stat_key]
    try:
        max_players = int(max_players_entry.get())
        if max_players <= 0:
            raise ValueError
    except ValueError:
        result_label.config(text="Please enter a valid positive integer for Max Players.")
        return
    try:
        min_threshold = int(consistency_entry.get())
        if min_threshold < 0:
            raise ValueError
    except ValueError:
        result_label.config(text="Please enter a valid non-negative integer for Minimum Threshold.")
        return

    apply_min_filter = consistency_var.get()
    
    if not selected_teams:
        result_label.config(text="Please select at least one team.")
        return

    result_label.config(text="Fetching data...")
    root.update()  # Refresh UI during processing

    start_time = time.time()
    top_players_df = get_top_players(
        selected_teams,
        selected_stat_value,
        min_threshold,
        apply_min_filter,
        max_players
    )
    end_time = time.time()

    # Display results
    result_area.delete("1.0", tk.END)
    if top_players_df.empty:
        result_label.config(text="No top players found based on the selected criteria.")
        return

    result_label.config(text=f"Data fetched successfully in {end_time - start_time:.2f} seconds!")

    # Displaying the selected stat
    stat_display = selected_stat_key.capitalize()

    result_area.insert(tk.END, f"Top {len(top_players_df)} players based on {stat_display}:\n\n")
    result_area.insert(tk.END, top_players_df.to_string(index=False))
    result_area.insert(tk.END, "\n\n")

# UI Setup
root = tk.Tk()
root.title("NFL Best Players Comparison")
root.geometry("900x700")
root.rowconfigure(7, weight=1)
root.columnconfigure(1, weight=1)

# Team Selection
team_label = ttk.Label(root, text="Select Teams:")
team_label.grid(row=0, column=0, padx=10, pady=10, sticky="NW")

team_listbox = tk.Listbox(root, selectmode=tk.MULTIPLE, height=20, exportselection=False)
for team in TEAM_ABBREVIATIONS.keys():
    team_listbox.insert(tk.END, team)
team_listbox.grid(row=0, column=1, padx=10, pady=10, sticky="NSEW")

# Max Players Entry
max_players_label = ttk.Label(root, text="Max Players:")
max_players_label.grid(row=1, column=0, padx=10, pady=10, sticky="W")

max_players_entry = ttk.Entry(root, width=5)
max_players_entry.grid(row=1, column=1, padx=10, pady=10, sticky="W")
max_players_entry.insert(0, "10")

# Stat Selection
selected_stat = tk.StringVar(value='Passing Yards')

stat_label = ttk.Label(root, text="Select Stat to Compare:")
stat_label.grid(row=2, column=0, padx=10, pady=10, sticky="W")

stat_frame = ttk.Frame(root)
stat_frame.grid(row=2, column=1, padx=10, pady=10, sticky="W")

for text in STAT_OPTIONS.keys():
    rb = ttk.Radiobutton(stat_frame, text=text, variable=selected_stat, value=text)
    rb.pack(side='left')

# Consistency Threshold
consistency_label = ttk.Label(root, text="Minimum Plays/Touchdowns:")
consistency_label.grid(row=3, column=0, padx=10, pady=10, sticky="W")

consistency_entry = ttk.Entry(root, width=5)
consistency_entry.grid(row=3, column=1, padx=10, pady=10, sticky="W")
consistency_entry.insert(0, "10")

# Checkbutton for Filters
consistency_var = tk.BooleanVar(value=True)
consistency_toggle = ttk.Checkbutton(root, text="Apply Minimum Filter", variable=consistency_var)
consistency_toggle.grid(row=4, column=0, columnspan=2, padx=10, pady=5, sticky="W")

# Submit Button
submit_button = ttk.Button(root, text="Get Top Players", command=display_top_players)
submit_button.grid(row=5, column=0, columnspan=2, pady=10)

# Result Labels and Text Area
result_label = ttk.Label(root, text="")
result_label.grid(row=6, column=0, columnspan=2, padx=10, pady=10)

result_area = tk.Text(root, wrap="word")
result_area.grid(row=7, column=0, columnspan=2, padx=10, pady=10, sticky="NSEW")

scrollbar = ttk.Scrollbar(root, orient="vertical", command=result_area.yview)
scrollbar.grid(row=7, column=2, sticky="NS")
result_area.configure(yscrollcommand=scrollbar.set)

root.mainloop()