meirnm13's picture
Update app.py
f26fac2 verified
import gradio as gr
import pandas as pd
import numpy as np
import os
import random
from sklearn.metrics.pairwise import cosine_similarity
# --- SAFE IMPORT BLOCK ---
# Prevents crash if advanced ML libraries are missing
try:
import joblib
from sentence_transformers import SentenceTransformer
from huggingface_hub import hf_hub_download
LIBRARIES_AVAILABLE = True
except ImportError:
LIBRARIES_AVAILABLE = False
print("⚠️ Warning: Advanced ML libraries missing. Running in Simulation Mode.")
# ==========================================
# 1. LOAD MODELS & DATA (FAIL-SAFE)
# ==========================================
print("πŸ”„ System Startup...")
# Global Variables
MODE = "SIMULATION"
PREDICTION_MODEL = None
EMBEDDINGS = None
EMBEDDING_MODEL = None
DATASET = None
# 1.1 Attempt to load real AI models
if LIBRARIES_AVAILABLE:
try:
print("⏳ Attempting to load AI Models...")
# Load Embedder
EMBEDDING_MODEL = SentenceTransformer('all-MiniLM-L6-v2')
# Check and load local files
if os.path.exists("play_embeddings.npy") and os.path.exists("playticker_engine.pkl"):
EMBEDDINGS = np.load("play_embeddings.npy")
PREDICTION_MODEL = joblib.load("playticker_engine.pkl")
MODE = "PRODUCTION (AI-POWERED)"
print("βœ… SUCCESS: Real AI Models Loaded.")
else:
print("⚠️ Files not found on disk. Switching to Logic Fallback.")
except Exception as e:
print(f"❌ Error loading models: {e}")
print("⚠️ Switching to Logic Fallback.")
# 1.2 Attempt to load dataset (CSV)
try:
if LIBRARIES_AVAILABLE:
# Attempt to download from Repo
dataset_path = hf_hub_download(
repo_id="meirnm13/playticker-nba-momentum",
filename="synthetic_nba_momentum_data.csv",
repo_type="dataset"
)
DATASET = pd.read_csv(dataset_path)
print(f"βœ… Loaded Dataset: {len(DATASET)} rows.")
else:
raise Exception("No libraries")
except Exception as e:
print(f"⚠️ Could not load CSV ({e}). Generating dummy data.")
DATASET = pd.DataFrame({
'play_description': [
"LeBron James throws down a monster dunk",
"Stephen Curry hits a deep three pointer",
"Timeout called by the Miami Heat",
"Turnover by James Harden",
"Giannis blocks the shot"
] * 20,
'player_name': ["LeBron James", "Stephen Curry", "Team", "James Harden", "Giannis Antetokounmpo"] * 20,
'team': ["Lakers", "Warriors", "Heat", "Clippers", "Bucks"] * 20,
'momentum_score': [0.85, 0.92, -0.1, -0.6, 0.75] * 20
})
# ==========================================
# 2. LOGIC ENGINES (HYBRID)
# ==========================================
def heuristic_logic(text):
text = str(text).lower()
score = 0.0
if any(x in text for x in ["dunk", "3-pointer", "win", "clutch", "lead", "steal", "block"]):
score = random.uniform(0.6, 0.95)
elif any(x in text for x in ["miss", "turnover", "foul", "lose", "brick", "timeout"]):
score = random.uniform(-0.7, -0.3)
else:
score = random.uniform(-0.2, 0.2)
return score
def predict_momentum(user_input_text):
if not user_input_text: return "", 0.0
momentum_score = 0.0
if MODE.startswith("PRODUCTION") and PREDICTION_MODEL is not None:
try:
embedding = EMBEDDING_MODEL.encode([user_input_text])
momentum_score = PREDICTION_MODEL.predict(embedding.reshape(1, -1))[0]
if isinstance(momentum_score, (str, int, np.integer)):
mapping = {0: -0.8, 1: 0.0, 2: 0.8}
momentum_score = mapping.get(momentum_score, 0.5)
except:
momentum_score = heuristic_logic(user_input_text)
else:
momentum_score = heuristic_logic(user_input_text)
# Visualization
if momentum_score > 0.4:
category, color, icon = "HIGH POSITIVE", "#10b981", "πŸ”₯"
elif momentum_score > 0.1:
category, color, icon = "POSITIVE", "#22c55e", "βœ…"
elif momentum_score < -0.4:
category, color, icon = "HIGH NEGATIVE", "#ef4444", "πŸ›‘"
else:
category, color, icon = "NEUTRAL", "#6b7280", "βš–οΈ"
html_output = f"""
<div style="text-align:center; padding:20px; border:3px solid {color};
background:{color}10; border-radius:15px;">
<h1 style="color:{color}; margin:0;">{icon} {category}</h1>
<div style="font-size:40px; color:{color}; font-weight:bold;">{float(momentum_score):.3f}</div>
<div style="font-size:12px; color:#888;">System Mode: {MODE}</div>
</div>
"""
return html_output, float(momentum_score)
# ==========================================
# 3. RECOMMENDATION PIPELINE
# ==========================================
def find_similar_plays(user_input_text, top_k=5):
# Fixed: Always return a DataFrame, even if empty
if not user_input_text:
return pd.DataFrame(columns=['Similarity', 'Play', 'Player', 'Score'])
results = []
if MODE.startswith("PRODUCTION") and EMBEDDINGS is not None:
try:
input_embedding = EMBEDDING_MODEL.encode([user_input_text])
similarities = cosine_similarity(input_embedding, EMBEDDINGS)[0]
top_indices = similarities.argsort()[-top_k:][::-1]
for idx in top_indices:
play = DATASET.iloc[idx]
results.append({
'Similarity': f"{similarities[idx]:.2f}",
'Play': str(play['play_description'])[:80] + "...",
'Player': play['player_name'],
'Score': f"{play['momentum_score']:.2f}"
})
return pd.DataFrame(results)
except:
pass
# Fallback
try:
sample = DATASET.sample(top_k)
for _, play in sample.iterrows():
results.append({
'Similarity': "N/A (Sim)",
'Play': str(play['play_description'])[:80] + "...",
'Player': play['player_name'],
'Score': f"{play['momentum_score']:.2f}"
})
except:
pass
return pd.DataFrame(results)
# ==========================================
# 4. MAIN ANALYZER (UI HANDLER)
# ==========================================
def analyze_play(user_input):
# FIXED: Return valid empty states instead of None to prevent Gradio Crash
if not user_input or len(str(user_input).strip()) < 2:
return "", 0, pd.DataFrame(columns=['Similarity', 'Play', 'Player', 'Score'])
html, score = predict_momentum(user_input)
sim_df = find_similar_plays(user_input)
return html, score, sim_df
# ==========================================
# 5. GRADIO INTERFACE
# ==========================================
custom_css = ".gradio-container {max-width: 1100px !important; margin: auto;}"
with gr.Blocks(css=custom_css, theme=gr.themes.Soft(primary_hue="orange")) as demo:
gr.HTML(f"""<div style="text-align:center;">
<h1 style="color:#f59e0b;">πŸ€ PlayTicker Pro</h1>
<p style="color:#888;">System Status: {MODE}</p>
</div>""")
with gr.Row():
with gr.Column():
input_text = gr.Textbox(label="Play Description", placeholder="E.g., LeBron hits a clutch three...", lines=3)
btn = gr.Button("Analyze", variant="primary")
# FIXED: Added cache_examples=False to prevent startup timeout
gr.Examples(
examples=[
["LeBron James hits a clutch three-pointer"],
["Steph Curry turnover leading to a fast break"],
["Giannis blocks the shot"]
],
inputs=input_text,
cache_examples=False
)
with gr.Column():
out_html = gr.HTML(label="Prediction")
out_score = gr.Number(visible=False)
gr.Markdown("### πŸ” Similar Historical Plays")
out_table = gr.Dataframe(headers=['Similarity', 'Play', 'Player', 'Score'])
with gr.Accordion("πŸ“š Dataset Explorer", open=False):
gr.Dataframe(DATASET.head(50) if DATASET is not None else [], interactive=False)
btn.click(analyze_play, input_text, [out_html, out_score, out_table])
if __name__ == "__main__":
demo.launch()