# app.py import gradio as gr import xgboost as xgb from xgboost import DMatrix from huggingface_hub import hf_hub_download from app_training_df_getter import create_app_user_training_df import pandas as pd from sklearn.model_selection import train_test_split from sklearn.preprocessing import LabelEncoder from helper import * from helper import ChampionConverter import joblib # Define champion list for dropdowns CHAMPIONS = [ "Aatrox", "Ahri", "Akali", "Akshan", "Alistar", "Amumu", "Anivia", "Annie", "Aphelios", "Ashe", "Aurelion Sol", "Azir", "Bard", "Bel'Veth", "Blitzcrank", "Brand", "Braum", "Caitlyn", "Camille", "Cassiopeia", "Cho'Gath", "Corki", "Darius", "Diana", "Dr. Mundo", "Draven", "Ekko", "Elise", "Evelynn", "Ezreal", "Fiddlesticks", "Fiora", "Fizz", "Galio", "Gangplank", "Garen", "Gnar", "Gragas", "Graves", "Gwen", "Hecarim", "Heimerdinger", "Illaoi", "Irelia", "Ivern", "Janna", "Jarvan IV", "Jax", "Jayce", "Jhin", "Jinx", "Kai'Sa", "Kalista", "Karma", "Karthus", "Kassadin", "Katarina", "Kayle", "Kayn", "Kennen", "Kha'Zix", "Kindred", "Kled", "Kog'Maw", "KSante", "LeBlanc", "Lee Sin", "Leona", "Lillia", "Lissandra", "Lucian", "Lulu", "Lux", "Malphite", "Malzahar", "Maokai", "Master Yi", "Milio", "Miss Fortune", "Mordekaiser", "Morgana", "Naafiri", "Nami", "Nasus", "Nautilus", "Neeko", "Nidalee", "Nilah", "Nocturne", "Nunu & Willump", "Olaf", "Orianna", "Ornn", "Pantheon", "Poppy", "Pyke", "Qiyana", "Quinn", "Rakan", "Rammus", "Rek'Sai", "Rell", "Renata Glasc", "Renekton", "Rengar", "Riven", "Rumble", "Ryze", "Samira", "Sejuani", "Senna", "Seraphine", "Sett", "Shaco", "Shen", "Shyvana", "Singed", "Sion", "Sivir", "Skarner", "Sona", "Soraka", "Swain", "Sylas", "Syndra", "Tahm Kench", "Taliyah", "Talon", "Taric", "Teemo", "Thresh", "Tristana", "Trundle", "Tryndamere", "Twisted Fate", "Twitch", "Udyr", "Urgot", "Varus", "Vayne", "Veigar", "Vel'Koz", "Vex", "Vi", "Viego", "Viktor", "Vladimir", "Volibear", "Warwick", "Wukong", "Xayah", "Xerath", "Xin Zhao", "Yasuo", "Yone", "Yorick", "Yuumi", "Zac", "Zed", "Zeri", "Ziggs", "Zilean", "Zoe", "Zyra" ] try: label_encoder = joblib.load('util/label_encoder.joblib') n_classes = len(label_encoder.classes_) print("Label encoder loaded successfully") except Exception as e: print(f"Error loading label encoder: {e}") label_encoder = None # Load model try: model_path = hf_hub_download( repo_id="ivwhy/champion-predictor-model", filename="champion_predictor.json" ) # Initialize the model with proper parameters model = xgb.XGBClassifier( use_label_encoder=False, objective='multi:softprob', num_class=n_classes ) # Load the model model._Booster = xgb.Booster() model._Booster.load_model(model_path) # Set only n_classes_ model.n_classes_ = n_classes except Exception as e: print(f"Error loading model: {e}") model = None # Initialize champion name encoder # champion_encoder = LabelEncoder() # champion_encoder.fit(CHAMPIONS) #==================================== Functions ================================================= def get_user_training_df(player_opgg_url): try: print("========= Inside get_user_training_df(player_opgg_url) ============= \n") #print("player_opgg_url: ", player_opgg_url, "\n type(player_opgg_url): ", type(player_opgg_url), "\n") # Add input validation if not player_opgg_url or not isinstance(player_opgg_url, str): return "Invalid URL provided" training_df = create_app_user_training_df(player_opgg_url) return training_df except Exception as e: # Add more detailed error information import traceback error_trace = traceback.format_exc() print(f"Full error trace:\n{error_trace}") return f"Error getting training data: {str(e)}" #return f"Error getting training data: {e}" def show_stats(player_opgg_url): """Display player statistics and recent matches""" ''' to add: playstyle, role_specialization, champion_loyalty_score, most_champ_1, most_champ_2 , ''' if not player_opgg_url: return "Please enter a player link to OPGG", None try: training_features = get_user_training_df(player_opgg_url) print("training_features: ", training_features, "\n") if isinstance(training_features, str): # Error message return training_features, None wins = training_features['result'].sum() losses = len(training_features) - wins winrate = f"{(wins / len(training_features)) * 100:.0f}%" favorite_champions = ( training_features['champion'] .value_counts() .head(3) .index.tolist() ) # print("training_features['playstyle']: \n", training_features['playstyle']) # print("training_features['role_specialization]: \n", training_features['role_specialization']) # print("training_features: \n", training_features[champion]) # Extract additional stats # playstyle = training_features['playstyle'].mode()[0] if 'playstyle' in training_features else 'N/A' # print("processed playstyle.\n") # role_specialization = training_features['role_specialization'].mode()[0] if 'role_specialization' in training_features else 'N/A' # Most common role # print("processed role_specialization.\n") #champion_loyalty_score = training_features['champion_loyalty_score'].mean().round(2) if 'champion_loyalty_score' in training_features else 'N/A' # Average loyalty # print("processed champion_loyalty_score.\n") # Map numeric playstyle to descriptive text # playstyle_mapping = { # 0: "Assassin/Carry", # 1: "Support/Utility", # 2: "Tank/Initiator", # 3: "Split-pusher", # 4: "Aggressive/Fighter", # 5: "Undefined" # } # role_specialization_map = { # 0: "Pure Specialist", # 1: "Strong Dual Role", # 2: "Primary Role with Backups", # 3: "Role Swapper", # 4: "True Flex", # 5: "Undefined" # } stats_html = f"""
Wins: {wins} | Losses: {losses}
Winrate: {winrate}
Favorite Champions: {', '.join(favorite_champions)}
Playstyle: {playstyle_mapping.get(playstyle, 'N/A')}
#Role Specialization: {role_specialization_map.get(role_specialization, 'N/A')}
#Champion Loyalty Score: {champion_loyalty_score}
return stats_html, None except Exception as e: return f"Error processing stats: {e}. ", None def predict_top_5_champion_w_confidence(player_opgg_url, *champions): """Make prediction based on selected champions""" print("Selected Champions from Dropdowns:", champions) if not player_opgg_url or None in champions: return "Please fill in all fields" try: if model is None: return "Model not loaded properly" if label_encoder is None: return "Label encoder not loaded properly" # Get and process the data training_df = get_user_training_df(player_opgg_url) if isinstance(training_df, str): return training_df training_df = convert_df(training_df) print("check_datatypes(training_df) BEFORE feature eng: \n", check_datatypes(training_df), "\n") training_df = apply_feature_engineering(training_df) print("check_datatypes(training_df) AFTER feature eng: \n", check_datatypes(training_df), "\n") label_column = training_df['champion'] predict_column = training_df.drop(columns=['champion', 'region']) # Mapping dropdown selections to the correct columns champ_columns = [ 'team_champ1', 'team_champ2', 'team_champ3', 'team_champ4', 'opp_champ1', 'opp_champ2', 'opp_champ3', 'opp_champ4', 'opp_champ5' ] champion_converter = ChampionConverter() # Update predict_column with user-selected champions for col, champ_name in zip(champ_columns, champions): champ_num = champion_converter.champion_to_num(champ_name) predict_column.at[0, col] = champ_num proba = model.predict_proba(predict_column) # Get top 5 indices and probabilities top_5_idx = np.argsort(proba, axis=1)[:, -5:][:, ::-1] top_5_proba = np.take_along_axis(proba, top_5_idx, axis=1) # Initialize results DataFrame results = pd.DataFrame() champion_converter = ChampionConverter() # Add true champion - convert numeric label to champion name true_numbers = label_column results['True_Champion'] = [champion_converter.num_to_champion(int(num)) for num in true_numbers] # Process each rank separately for i in range(5): # Convert indices to champion names using the champion converter champions = [champion_converter.num_to_champion(int(label_encoder.classes_[idx])) for idx in top_5_idx[:, i]] probabilities = top_5_proba[:, i] # Add to results results[f'Rank_{i+1}_Champion'] = champions results[f'Rank_{i+1}_Confidence'] = probabilities.round(4) try: def find_champion_rank(row): true_champ = row['True_Champion'] for i in range(1, 6): if row[f'Rank_{i}_Champion'] == true_champ: return f'Rank_{i}' return 'Not in Top 5' results['Prediction_Rank'] = results.apply(find_champion_rank, axis=1) # Select the last row and specific columns latest_result = results.iloc[-1][["Rank_1_Champion", "Rank_2_Champion", "Rank_3_Champion"]].tolist() latest_result = results.iloc[-1][["Rank_1_Champion", "Rank_2_Champion", "Rank_3_Champion"]] clean_output = "\n".join(f"{col}: {val}" for col, val in latest_result.items()) print(clean_output) return clean_output except Exception as e: print(f"Error getting top 5 champions: {e}") except Exception as e: import traceback print(f"Full error trace:\n{traceback.format_exc()}") return f"Error making prediction: {e}" # Define your interface with gr.Blocks() as demo: gr.Markdown("# League of Legends Champion Prediction") with gr.Row(): player_opgg_url = gr.Textbox(label="OPGG Player URL") show_button = gr.Button("Show Player Stats") with gr.Row(): stats_output = gr.HTML(label="Player Statistics") recent_matches = gr.HTML(label="Recent Matches") with gr.Row(): champion_dropdowns = [ gr.Dropdown(choices=CHAMPIONS, label=f"Champion {i+1}") for i in range(9) ] with gr.Row(): predict_button = gr.Button("Predict") prediction_output = gr.Text(label="Prediction") # Set up event handlers show_button.click( fn=show_stats, inputs=[player_opgg_url], outputs=[stats_output, recent_matches] ) predict_button.click( fn=predict_top_5_champion_w_confidence, inputs=[player_opgg_url] + champion_dropdowns, outputs=prediction_output ) # Optional: Save the champion encoder for future use #joblib.dump(champion_encoder, 'champion_encoder.joblib') # Enable queuing demo.launch(debug=True) # For local testing if __name__ == "__main__": demo.launch()