Spaces:
Running
Running
import gradio as gr | |
import pandas as pd | |
import requests | |
import joblib | |
import xgboost | |
print(f"Gradio version at script start: {gr.__version__}") # For debugging | |
# --- Model Loading --- | |
try: | |
model = joblib.load('xgb_model.joblib') | |
print("Model loaded successfully.") | |
except FileNotFoundError: | |
print("ERROR: Model file 'xgb_model.joblib' not found. Predictions will fail.") | |
model = None | |
except Exception as e: | |
print(f"Error loading model: {e}") | |
model = None | |
# --- Helper Function for FDR (get_next_fdr) --- | |
# (Your get_next_fdr function code remains the same here) | |
def get_next_fdr(team_id, fixtures_df): | |
""" | |
Finds the next fixture difficulty rating (FDR) for a given team. | |
""" | |
upcoming = fixtures_df[ | |
((fixtures_df["team_h"] == team_id) | (fixtures_df["team_a"] == team_id)) & | |
(fixtures_df["finished"] == False) | |
] | |
if not upcoming.empty: | |
upcoming = upcoming.dropna(subset=['event']) | |
if upcoming.empty: | |
return None | |
next_fixture = upcoming.sort_values("event").iloc[0] | |
if next_fixture["team_h"] == team_id: | |
return next_fixture["team_h_difficulty"] | |
else: | |
return next_fixture["team_a_difficulty"] | |
else: | |
return None | |
# --- Main Prediction Function (predict_fpl_points) --- | |
# (Your predict_fpl_points function code remains the same here) | |
def predict_fpl_points(search_name=""): | |
""" | |
Fetches FPL player data, calculates their next FDR, | |
and predicts their FPL points for the next gameweek. | |
""" | |
if model is None: | |
return pd.DataFrame({"Error": ["Model could not be loaded. Please check server logs."]}) | |
print(f"Fetching latest FPL data... Search term: '{search_name}'") | |
try: | |
players_url = "https://fantasy.premierleague.com/api/bootstrap-static/" | |
response = requests.get(players_url, timeout=20) | |
response.raise_for_status() | |
players_data = response.json() | |
players = pd.DataFrame(players_data["elements"])[ | |
["id", "web_name", "team", "form", "element_type"] | |
] | |
players["element_type"] = pd.to_numeric(players["element_type"], errors='coerce') | |
players = players[players["element_type"].isin([1, 2, 3, 4])] | |
players["team"] = players["team"].astype(int) | |
fixtures_url = "https://fantasy.premierleague.com/api/fixtures/" | |
fixtures_response = requests.get(fixtures_url, timeout=20) | |
fixtures_response.raise_for_status() | |
fixtures_data = fixtures_response.json() | |
fixtures = pd.DataFrame(fixtures_data) | |
cols_to_numeric = ["team_h", "team_a", "team_h_difficulty", "team_a_difficulty", "event"] | |
for col in cols_to_numeric: | |
if col in fixtures.columns: | |
fixtures[col] = pd.to_numeric(fixtures[col], errors='coerce') | |
print("FPL data fetched successfully.") | |
except requests.exceptions.RequestException as e: | |
print(f"Error fetching data from FPL API: {e}") | |
return pd.DataFrame({"Error": [f"Failed to fetch FPL data: {e}. Please try again later."]}) | |
except Exception as e: | |
print(f"An unexpected error occurred during data fetching: {e}") | |
return pd.DataFrame({"Error": [f"An unexpected error occurred during data fetching: {e}"]}) | |
print("Calculating next FDR and preparing data for prediction...") | |
try: | |
players_with_fdr = players.copy() | |
players_with_fdr["next_fdr"] = players_with_fdr["team"].apply( | |
lambda team_id: get_next_fdr(team_id, fixtures) | |
) | |
players_clean = players_with_fdr.dropna(subset=["next_fdr", "form"]) | |
players_clean["form"] = pd.to_numeric(players_clean["form"], errors='coerce') | |
players_clean["next_fdr"] = pd.to_numeric(players_clean["next_fdr"], errors='coerce') | |
players_clean = players_clean.dropna(subset=["form", "next_fdr"]) | |
if players_clean.empty: | |
print("No players found after cleaning (missing form or next FDR).") | |
return pd.DataFrame({"Message": ["No eligible players found after data cleaning."]}) | |
print(f"Predicting points for {len(players_clean)} players...") | |
features = ["form", "next_fdr"] | |
X_next = players_clean[features] | |
players_clean.loc[:, "predicted_points"] = model.predict(X_next) | |
print("Prediction complete.") | |
output_df_base = players_clean[["web_name", "predicted_points", "form", "next_fdr"]].copy() | |
output_df_base.rename(columns={ | |
"web_name": "Player", | |
"predicted_points": "Predicted Points", | |
"form": "Form", | |
"next_fdr": "Next FDR (Lower is easier)" | |
}, inplace=True) | |
output_df_base.loc[:, "Predicted Points"] = output_df_base["Predicted Points"].round(2) | |
if search_name and search_name.strip(): | |
print(f"Filtering for player name containing: '{search_name}'") | |
search_results = output_df_base[ | |
output_df_base['Player'].str.contains(search_name.strip(), case=False, na=False) | |
] | |
if not search_results.empty: | |
return search_results.sort_values("Predicted Points", ascending=False) | |
else: | |
return pd.DataFrame({"Message": [f"No player found matching '{search_name}'."]}) | |
else: | |
print("No search term provided, returning top 20 predicted players.") | |
return output_df_base.sort_values("Predicted Points", ascending=False).head(20) | |
except KeyError as e: | |
print(f"KeyError during data processing: {e}.") | |
import traceback | |
traceback.print_exc() | |
return pd.DataFrame({"Error": [f"Data processing error (Missing Key): {e}."]}) | |
except Exception as e: | |
print(f"An error occurred during prediction/processing: {e}") | |
import traceback | |
traceback.print_exc() | |
return pd.DataFrame({"Error": [f"Prediction or data processing failed: {e}"]}) | |
# --- Gradio UI Definition using gr.Blocks --- | |
with gr.Blocks(theme=gr.themes.Soft(), title="Fantasy Premier League Player Point Predictor") as demo: | |
gr.Markdown( | |
""" | |
# Fantasy Premier League Player Point Predictor | |
Predicts FPL points for the *next* gameweek based on current form and upcoming fixture difficulty (FDR). | |
Enter a player name to search, or leave blank to see the top 20 predicted players. Fetches live data. | |
""" | |
) # You can add "(MCP Enabled)" to the title/markdown if MCP features load successfully | |
with gr.Row(): | |
player_search_input = gr.Textbox( | |
label="Search for Player (Optional)", | |
placeholder="E.g., Salah, Haaland, Saka... Leave blank for Top 20", | |
# Add mcp_label if MCP features are correctly installed and working | |
# mcp_label="player_search_input_blocks" | |
) | |
predict_button = gr.Button("Predict Points") | |
prediction_output_dataframe = gr.DataFrame( | |
label="Predicted Player Data (Next Gameweek)", | |
wrap=True, | |
# Add mcp_label if MCP features are correctly installed and working | |
# mcp_label="prediction_output_dataframe_blocks" | |
) | |
gr.Examples( | |
examples=[[""], ["Son"], ["Watkins"], ["Palmer"]], | |
inputs=player_search_input, | |
# You could also have outputs and fn here if examples should pre-run, | |
# but for dynamic input, just setting input is common. | |
) | |
# Define the action for the button click | |
predict_button.click( | |
fn=predict_fpl_points, | |
inputs=player_search_input, | |
outputs=prediction_output_dataframe | |
) | |
# --- Application Launch --- | |
if __name__ == "__main__": | |
print(f"Gradio version before launch: {gr.__version__}") | |
# Attempt to add MCP labels dynamically if the Gradio version seems to support it | |
# This is a bit of a workaround due to the persistent environment issues. | |
# Ideally, you'd just define them directly if the env was correct. | |
mcp_is_likely_available = False | |
try: | |
# A simple test: does Textbox accept mcp_label? | |
# This is a rough check and might not be perfectly reliable for all Gradio versions/setups. | |
# The real check is whether 'gradio[mcp]' was correctly installed. | |
_ = gr.Textbox(mcp_label="test") | |
mcp_is_likely_available = True | |
print("MCP features (like mcp_label) seem available in this Gradio version.") | |
except TypeError: | |
print("WARNING: MCP features (like mcp_label) are NOT available in this Gradio version. MCP server might fail or not collect specific data.") | |
print(f"This is likely due to an issue with 'gradio[mcp]' installation or an incompatible Gradio version ({gr.__version__}) in the environment.") | |
if mcp_is_likely_available: | |
# If MCP seems available, re-define components within Blocks with mcp_label | |
# This is done by re-creating the demo object if we want to add them now. | |
# For simplicity in this example, I'll just note that you would have defined them above directly. | |
# The `mcp_label`s in the Blocks definition above are commented out; | |
# you'd uncomment them if your environment was fixed. | |
# For now, we'll proceed, and launch will attempt mcp_server=True | |
print("Proceeding with mcp_server=True. If it fails, it confirms environment issues.") | |
if hasattr(player_search_input, 'mcp_label'): # This check is illustrative | |
player_search_input.mcp_label = "player_search_input_blocks" | |
prediction_output_dataframe.mcp_label = "prediction_output_dataframe_blocks" | |
demo.title = "Fantasy Premier League Player Point Predictor (MCP Enabled)" | |
demo.blocks[0].value = demo.blocks[0].value.replace("Player Point Predictor", "Player Point Predictor (MCP Enabled)") | |
print("Attempting to launch Gradio app...") | |
try: | |
# Try to launch with mcp_server=True | |
demo.launch(mcp_server=True) | |
except TypeError as e: | |
if 'mcp_server' in str(e) or 'mcp_label' in str(e): | |
print("\nERROR: Failed to launch with MCP features (mcp_server or mcp_label not recognized).") | |
print(f"Gradio version being used: {gr.__version__}") | |
print("This confirms that 'gradio[mcp]' extras are not correctly installed or it's an incompatible Gradio version.") | |
print("If you are on Hugging Face Spaces, ensure your environment (requirements.txt, Dockerfile if used, or SDK settings) correctly installs a recent 'gradio[mcp]'.\n") | |
print(f"Error details: {e}") | |
print("Attempting to launch Gradio app WITHOUT MCP server as a fallback...") | |
# Fallback launch: | |
# Create a new Blocks instance without attempting MCP features if the first one is problematic | |
with gr.Blocks(theme=gr.themes.Soft(), title="Fantasy Premier League Player Point Predictor (MCP Fallback)") as fallback_demo: | |
gr.Markdown( | |
""" | |
# Fantasy Premier League Player Point Predictor (MCP Features Failed to Load) | |
Predicts FPL points for the *next* gameweek based on current form and upcoming fixture difficulty (FDR). | |
Enter a player name to search, or leave blank to see the top 20 predicted players. Fetches live data. | |
""" | |
) | |
with gr.Row(): | |
player_search_input_fb = gr.Textbox( # no mcp_label | |
label="Search for Player (Optional)", | |
placeholder="E.g., Salah, Haaland, Saka... Leave blank for Top 20" | |
) | |
predict_button_fb = gr.Button("Predict Points") | |
prediction_output_dataframe_fb = gr.DataFrame( # no mcp_label | |
label="Predicted Player Data (Next Gameweek)", | |
wrap=True | |
) | |
gr.Examples( | |
examples=[[""], ["Son"], ["Watkins"], ["Palmer"]], | |
inputs=player_search_input_fb, | |
) | |
predict_button_fb.click( | |
fn=predict_fpl_points, | |
inputs=player_search_input_fb, | |
outputs=prediction_output_dataframe_fb | |
) | |
fallback_demo.launch() | |
else: | |
# Re-raise other TypeErrors not related to MCP | |
raise e | |
except Exception as general_e: | |
print(f"A general error occurred during launch: {general_e}") | |
# Potentially try the fallback launch here too if it's a critical launch failure |