Spaces:
Sleeping
Sleeping
# 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""" | |
<div style='padding: 20px; background: #f5f5f5; border-radius: 10px;'> | |
<h3>Player's Recent Stats</h3> | |
<p>Wins: {wins} | Losses: {losses}</p> | |
<p>Winrate: {winrate}</p> | |
<p>Favorite Champions: {', '.join(favorite_champions)}</p> | |
</div> | |
""" | |
# <p>Playstyle: {playstyle_mapping.get(playstyle, 'N/A')}</p> | |
# <p>Role Specialization: {role_specialization_map.get(role_specialization, 'N/A')}</p> | |
# <p>Champion Loyalty Score: {champion_loyalty_score}</p> | |
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() |