NVB / app.py
Jofax's picture
Update app.py
8e602aa verified
import gradio as gr
import pandas as pd
import plotly.graph_objects as go
from datetime import datetime, timedelta
import json
import random
# --- DATA & CONFIG ---
# User Credentials
USERS = {
"admin": {"password": "admin123", "role": "Management"},
"coach": {"password": "coach123", "role": "Coach"},
"manager": {"password": "manager123", "role": "Manager"}
}
# The 14-Player Roster
ROSTER = [
{"id": 1, "name": "Sofia Reyes", "pos": "OH", "num": 4, "trend": "up", "change": "+1.4", "hist": [8.2, 8.4, 8.6, 8.9, 9.1], "focus": 9, "stamina": 9, "spirit": 10},
{"id": 2, "name": "Liam Chen", "pos": "S", "num": 10, "trend": "stable", "change": "0.0", "hist": [8.5, 8.4, 8.5, 8.5, 8.5], "focus": 9, "stamina": 8, "spirit": 9},
{"id": 3, "name": "Marcus Johnson", "pos": "MB", "num": 15, "trend": "up", "change": "+0.8", "hist": [7.1, 7.4, 7.6, 7.9, 8.1], "focus": 8, "stamina": 8, "spirit": 7},
{"id": 4, "name": "Kenji Tanaka", "pos": "OP", "num": 7, "trend": "down", "change": "-0.4", "hist": [9.0, 8.8, 8.7, 8.5, 8.4], "focus": 7, "stamina": 8, "spirit": 6},
{"id": 5, "name": "Elena Volkov", "pos": "L", "num": 1, "trend": "up", "change": "+1.2", "hist": [8.0, 8.2, 8.8, 9.0, 9.2], "focus": 10, "stamina": 9, "spirit": 9},
{"id": 6, "name": "Isaiah Thomas", "pos": "OH", "num": 11, "trend": "stable", "change": "+0.1", "hist": [7.5, 7.5, 7.6, 7.5, 7.6], "focus": 7, "stamina": 8, "spirit": 7},
{"id": 7, "name": "Anna Müller", "pos": "MB", "num": 9, "trend": "up", "change": "+2.0", "hist": [6.5, 7.0, 7.8, 8.2, 8.5], "focus": 9, "stamina": 9, "spirit": 8},
{"id": 8, "name": "Carlos Silva", "pos": "DS", "num": 12, "trend": "stable", "change": "0.0", "hist": [7.8, 7.9, 7.8, 7.9, 7.9], "focus": 8, "stamina": 9, "spirit": 8},
{"id": 9, "name": "Sarah Jenkins", "pos": "OH", "num": 3, "trend": "down", "change": "-1.0", "hist": [8.8, 8.4, 8.1, 7.9, 7.8], "focus": 5, "stamina": 7, "spirit": 5},
{"id": 10, "name": "David Park", "pos": "S", "num": 2, "trend": "up", "change": "+0.5", "hist": [7.0, 7.2, 7.4, 7.5, 7.5], "focus": 8, "stamina": 8, "spirit": 8},
{"id": 11, "name": "Mia Williams", "pos": "OP", "num": 8, "trend": "stable", "change": "+0.2", "hist": [7.2, 7.3, 7.4, 7.4, 7.4], "focus": 7, "stamina": 7, "spirit": 7},
{"id": 12, "name": "James Wilson", "pos": "MB", "num": 14, "trend": "up", "change": "+1.5", "hist": [6.0, 6.5, 7.0, 7.5, 8.0], "focus": 8, "stamina": 8, "spirit": 7},
{"id": 13, "name": "Emily Davis", "pos": "L", "num": 5, "trend": "stable", "change": "0.0", "hist": [8.7, 8.7, 8.6, 8.7, 8.7], "focus": 10, "stamina": 9, "spirit": 10},
{"id": 14, "name": "Ryan Patel", "pos": "OH", "num": 6, "trend": "down", "change": "-0.3", "hist": [8.0, 7.9, 7.8, 7.7, 7.7], "focus": 6, "stamina": 7, "spirit": 6}
]
# Initial Schedule Data
INITIAL_SCHEDULE = [
{"title": "Pre-Season Camp", "date": "2023-10-15", "type": "Training", "status": "Done"},
{"title": "Regional Finals", "date": "2023-12-05", "type": "Match", "status": "Scheduled"}
]
# --- LOGIC HELPERS ---
def create_sparkline(data):
"""Creates a Plotly sparkline figure"""
fig = go.Figure(go.Scatter(
y=data,
mode='lines',
fill='tozeroy',
line={'color': '#38bdf8', 'width': 2}
))
fig.update_layout(
margin=dict(l=0, r=0, t=0, b=0),
paper_bgcolor='rgba(0,0,0,0)',
plot_bgcolor='rgba(0,0,0,0)',
xaxis=dict(visible=False),
yaxis=dict(visible=False),
height=40
)
return fig
def get_ai_response(message):
"""Simulates the Tactical Advisor AI"""
msg = message.lower()
if "prayer" in msg or "spirit" in msg:
return "Prayer is the pillar of discipline. I recommend a 10-minute mindfulness session post-training. 'Verily, with hardship comes ease.' (94:6)"
elif "gym" in msg or "strength" in msg:
return "I have generated a Plyometric plan:\n1. Box Jumps (4x8)\n2. Lateral Bounds (3x10)\nFocus on explosive landing mechanics."
elif "lineup" in msg or "team" in msg:
names = [p['name'] for p in random.sample(ROSTER, 6)]
return f"Based on current form, I suggest starting: {', '.join(names)}. This combination maximizes court coverage and attack efficiency."
elif "defense" in msg:
return "Switching to Perimeter Defense. The middle blocker must read the setter's hands. Trust your back-row libero."
else:
return "Tactical Advisor Online. I have analyzed the roster metrics. We have 3 players with declining stamina trends. Shall I adjust the training load?"
# --- GRADIO APP ---
css = """
.gradio-container { background-color: #0f172a !important; }
.card-bg { background-color: #1e293b !important; border-radius: 12px; padding: 16px; border: 1px solid #334155; height: 100%; }
.table-bg { background-color: #1e293b !important; }
input, textarea { background-color: #0f172a !important; color: white !important; border: 1px solid #334155 !important; }
"""
with gr.Blocks(css=css, theme=gr.themes.Soft(primary_hue="sky", neutral_hue="slate", font=gr.themes.GoogleFont("Inter"))) as demo:
# --- STATE ---
user_state = gr.State(None)
schedule_state = gr.State(INITIAL_SCHEDULE)
# --- HEADER ---
gr.Markdown("""
<div style='text-align: center; margin-bottom: 20px;'>
<h1 style='color: white; font-weight: 800; letter-spacing: -1px;'>ELITE TEAM OS</h1>
<p style='color: #94a3b8; font-size: 12px; text-transform: uppercase; letter-spacing: 2px;'>High Performance Management System</p>
</div>
""")
# --- LOGIN SCREEN ---
with gr.Row(visible=True) as login_row:
with gr.Column(scale=1, min_width=400):
gr.Markdown("<h3 style='text-align:center; color:white;'>Secure Access</h3>")
login_user = gr.Textbox(label="Username", placeholder="admin")
login_pass = gr.Textbox(label="Password", type="password", placeholder="admin123")
login_btn = gr.Button("Enter System", variant="primary")
login_err = gr.Markdown("", visible=False)
# --- MAIN APP ---
with gr.Row(visible=False) as app_row:
# Sidebar Navigation
with gr.Column(scale=1, min_width=200):
# FIXED LINE BELOW: Removed the empty {} placeholder
gr.Markdown("<div style='color:#94a3b8; font-size:10px; text-transform:uppercase; margin-bottom:10px;'>System Active</div>")
nav_hub = gr.Button("📈 Team Hub", variant="secondary")
nav_advisor = gr.Button("🤖 Tactical Advisor", variant="secondary")
nav_schedule = gr.Button("🗓️ Schedule", variant="secondary")
logout_btn = gr.Button("Sign Out", variant="stop")
# Content Area
with gr.Column(scale=5):
# VIEW 1: TEAM HUB
with gr.Column(visible=True) as view_hub:
gr.Markdown("<h2 style='color:white;'>Team Roster & Analytics</h2>")
# Roster Grid
roster_html = ""
for p in ROSTER:
trend_color = "#22c55e" if p['trend'] == 'up' else "#ef4444" if p['trend'] == 'down' else "#94a3b8"
roster_html += f"""
<div style='background: #1e293b; padding: 15px; border-radius: 8px; margin-bottom: 10px; border-left: 4px solid {trend_color};'>
<div style='display: flex; justify-content: space-between; align-items: center;'>
<div>
<span style='font-weight: bold; color: white;'>{p['name']}</span>
<span style='font-size: 10px; color: #64748b; margin-left: 5px;'>{p['pos']} | #{p['num']}</span>
</div>
<span style='font-size: 12px; font-weight: bold; color: {trend_color};'>{p['change']}</span>
</div>
<div style='margin-top: 8px;'>
<span style='font-size: 10px; color: #64748b;'>Focus: {p['focus']}/10</span> |
<span style='font-size: 10px; color: #64748b;'>Stamina: {p['stamina']}/10</span>
</div>
</div>
"""
gr.HTML(roster_html)
# Performance Charts
gr.Markdown("<h3 style='color:white; margin-top:20px;'>Performance Trends</h3>")
with gr.Row():
for p in ROSTER[:3]: # Show top 3 for brevity
with gr.Column():
gr.Markdown(f"<div style='color:white; font-size:12px; text-align:center;'>{p['name']}</div>")
spark = gr.Plot(value=create_sparkline(p['hist']), show_label=False)
# VIEW 2: ADVISOR
with gr.Column(visible=False) as view_advisor:
gr.Markdown("<h2 style='color:white;'>Tactical Advisor</h2>")
gr.Markdown("<p style='color:#94a3b8; font-size:12px;'>AI-powered coaching assistant. Ask about lineups, drills, or player psychology.</p>")
chatbot = gr.Chatbot(height=400, show_label=False)
with gr.Row():
txt = gr.Textbox(show_label=False, placeholder="Ask for tactics...", container=False, scale=9)
btn = gr.Button("Send", variant="primary", scale=1)
# Quick Actions
with gr.Row():
btn_strategy = gr.Button("Strategy Sync", size="sm")
btn_gym = gr.Button("Gym Plan", size="sm")
# VIEW 3: SCHEDULE
with gr.Column(visible=False) as view_schedule:
gr.Markdown("<h2 style='color:white;'>Team Schedule</h2>")
schedule_table = gr.Dataframe(
value=pd.DataFrame(INITIAL_SCHEDULE),
headers=["Title", "Date", "Type", "Status"],
datatype=["str", "str", "str", "str"],
row_count=5,
col_count=(4, "fixed"),
interactive=False
)
with gr.Row():
new_title = gr.Textbox(label="New Event Title")
new_type = gr.Dropdown(choices=["Training", "Match", "Gym"], label="Type")
add_btn = gr.Button("Add Event")
# --- FUNCTIONS ---
def handle_login(u, p):
if u in USERS and USERS[u]['password'] == p:
return (
gr.Row(visible=False), # Hide login
gr.Row(visible=True), # Show app
u # Update state
)
else:
return gr.Row(visible=True), gr.Row(visible=False), None
def handle_logout():
return gr.Row(visible=True), gr.Row(visible=False), None
def switch_view(show_view):
return {
view_hub: gr.Column(visible=show_view == "hub"),
view_advisor: gr.Column(visible=show_view == "advisor"),
view_schedule: gr.Column(visible=show_view == "schedule")
}
def add_event(title, ev_type, current_schedule):
new_event = {
"title": title,
"date": datetime.now().strftime("%Y-%m-%d"),
"type": ev_type,
"status": "Scheduled"
}
updated_schedule = current_schedule + [new_event]
df = pd.DataFrame(updated_schedule)
return df, updated_schedule
def respond(message, chat_history):
response = get_ai_response(message)
chat_history.append((message, response))
return "", chat_history
# --- EVENTS ---
login_btn.click(
handle_login,
[login_user, login_pass],
[login_row, app_row, user_state]
)
logout_btn.click(
handle_logout,
None,
[login_row, app_row, user_state]
)
# Navigation
nav_hub.click(lambda: switch_view("hub"), None, [view_hub, view_advisor, view_schedule])
nav_advisor.click(lambda: switch_view("advisor"), None, [view_hub, view_advisor, view_schedule])
nav_schedule.click(lambda: switch_view("schedule"), None, [view_hub, view_advisor, view_schedule])
# Chat Logic
txt.submit(respond, [txt, chatbot], [txt, chatbot])
btn.click(respond, [txt, chatbot], [txt, chatbot])
btn_gym.click(lambda: respond("Give me a gym plan for vertical jump.", []), None, [txt, chatbot])
# Schedule Logic
add_btn.click(
add_event,
[new_title, new_type, schedule_state],
[schedule_table, schedule_state]
)
if __name__ == "__main__":
demo.launch()