Spaces:
Sleeping
Sleeping
import gradio as gr | |
from utils.data_loader import fetch_and_save_chess_data | |
from utils.game_analysis import ( | |
analyze_games, | |
generate_monthly_report, | |
calculate_average_and_median_games, | |
analyze_streaks, | |
analyze_sequences, | |
format_duration | |
) | |
from datetime import datetime | |
import os | |
import json | |
import matplotlib.pyplot as plt | |
import io | |
from PIL import Image | |
import plotly.graph_objects as go | |
import pandas as pd | |
import csv | |
logged_in_user = None # Global state to store the logged-in user | |
# Define your user credentials | |
auth_users = [ | |
("Sacha", "SachaIsTheBest"), | |
("Florian", "FlorianIsTheBest"), | |
("Lucas", "Slevin"), | |
("Gael", "Kalel"), | |
("BlueNote", "MamaLinda") | |
] | |
DATA_FOLDER = 'data/' | |
LOG_FILE = 'user_logs.csv' | |
# Initialize log file if it doesn't exist | |
if not os.path.exists(LOG_FILE): | |
with open(LOG_FILE, 'w', newline='') as log_file: | |
writer = csv.writer(log_file) | |
writer.writerow(["Username", "Timestamp", "Action", "Query"]) | |
def log_user_action(username, action, query=""): | |
"""Log user actions to a CSV file.""" | |
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S') | |
with open(LOG_FILE, 'a', newline='') as log_file: | |
writer = csv.writer(log_file) | |
writer.writerow([username, timestamp, action, query]) | |
def get_monthly_report(username, logged_in_user): | |
"""Fetch data for a given username, reuse existing data if available, and generate a monthly report.""" | |
current_date = datetime.now().strftime('%Y-%m-%d') | |
filename = os.path.join(DATA_FOLDER, f"{username}_{current_date}.json") | |
log_user_action(logged_in_user, "Search", username) | |
if os.path.exists(filename): | |
print(f"Using existing data file: {filename}") | |
with open(filename, 'r') as file: | |
games = json.load(file) | |
else: | |
games = fetch_and_save_chess_data(username, filename) | |
if not games: | |
return "No data found for the specified username." | |
games_sorted = sorted(games, key=lambda x: x.get('end_time')) | |
# Perform monthly analysis | |
games_per_month, stats_per_month, total_games, total_wins, total_losses, total_timeouts, total_months_played = analyze_games(games, username) | |
report_df = generate_monthly_report(games_per_month, stats_per_month) | |
# Calculate average and median games per day | |
average_games, median_games = calculate_average_and_median_games(games) | |
# Calculate streak probabilities | |
win_prob, loss_prob = analyze_streaks(games_sorted, username) | |
# Calculate sequence probabilities | |
win_after_wl_prob, win_after_lw_prob = analyze_sequences(games_sorted, username) | |
# Format the duration of months played | |
formatted_duration = format_duration(total_months_played) | |
# Prepare the text summary | |
summary_text = ( | |
f"Total duration played: {formatted_duration}\n" | |
f"Total games played: {total_games}\n" | |
f"Total wins: {total_wins}\n" | |
f"Total losses: {total_losses}\n" | |
f"Total timeouts: {total_timeouts}\n" | |
f"Average games played per day: {average_games:.2f}\n" | |
f"Median games played per day: {median_games}\n" | |
f"Probability of winning the next game after a win in the same hour: {win_prob:.2f}%\n" | |
f"Probability of losing the next game after a loss in the same hour: {loss_prob:.2f}%\n" | |
f"Probability of winning the next game after a 'win-loss' sequence in the same hour: {win_after_wl_prob:.2f}%\n" | |
f"Probability of winning the next game after a 'loss-win' sequence in the same hour: {win_after_lw_prob:.2f}%" | |
) | |
stacked_bar_img = generate_stacked_bar_chart(report_df) | |
return report_df, summary_text, stacked_bar_img | |
def generate_stacked_bar_chart(report_df): | |
"""Generate an interactive stacked bar chart with wins, losses, and a line plot for timeouts using Plotly.""" | |
# Extract data | |
months = pd.to_datetime(report_df['Month']).dt.strftime('%b, %Y') | |
wins = report_df['Wins'] | |
losses = report_df['Losses'] | |
timeouts = report_df['Timeout Rate (%)'] | |
total_games = report_df['Games Played'] | |
# Create the figure | |
fig = go.Figure() | |
# Add wins bars | |
fig.add_trace(go.Bar( | |
x=months, | |
y=wins, | |
name='Wins', | |
marker=dict(color='#1f77b4'), | |
hovertemplate='<b>Wins</b>: %{y}<extra></extra>' | |
)) | |
# Add losses bars stacked on top of wins | |
fig.add_trace(go.Bar( | |
x=months, | |
y=losses, | |
name='Losses', | |
marker=dict(color='#ff7f0e'), | |
hovertemplate='<b>Losses</b>: %{y}<extra></extra>' | |
)) | |
# Add timeouts as a line plot | |
fig.add_trace(go.Scatter( | |
x=months, | |
y=timeouts, | |
mode='lines+markers', | |
name='Timeouts', | |
line=dict(color='#da5bac', width=2), | |
hovertemplate='<b>Timeouts</b>: %{y:.1f}%<extra></extra>' | |
)) | |
# Add rotated annotations for total games on top of each bar | |
for i, total in enumerate(total_games): | |
fig.add_annotation( | |
x=months[i], | |
y=total + 5, | |
text=str(int(total)), | |
showarrow=False, | |
font=dict(size=12, color='white'), | |
align='center', | |
textangle=-45 # Rotate the text label by -45 degrees | |
) | |
# Update layout for stacked bars and hover information | |
fig.update_layout( | |
barmode='stack', | |
title='Monthly Win/Loss Stacked Bar Chart with Total Games and Timeouts', | |
xaxis_title='Month', | |
yaxis_title='Count', | |
legend_title='Legend', | |
hovermode='x unified', | |
template='plotly_dark' | |
) | |
# Return the Plotly figure | |
return fig | |
logged_in_user_state = gr.State() | |
# Authentication callback function | |
def auth_callback(username, password): | |
global logged_in_user | |
valid_users = dict(auth_users) | |
if username in valid_users and valid_users[username] == password: | |
log_user_action(username, "Login") | |
logged_in_user = username # Store the logged-in user globally | |
return True | |
return False | |
def get_report_with_user(username): | |
global logged_in_user | |
return get_monthly_report(username, logged_in_user or "Unknown") | |
# Custom layout using gr.Blocks with CSS | |
with gr.Blocks(css="style.css", theme=gr.themes.Soft()) as app: | |
gr.Markdown("# Chess Analysis App") | |
username_input = gr.Textbox(label="Chess.com Username", placeholder="Enter Chess.com username") | |
submit_button = gr.Button("Submit") | |
# Define outputs | |
output_data = gr.Dataframe(headers=["Month", "Games Played", "Wins", "Losses", "Win Rate (%)", "Loss Rate (%)", "Timeout Rate (%)"]) | |
summary_text = gr.Textbox(label="Summary", interactive=False) | |
# stacked_bar_img = gr.Image(label="Monthly Stacked Bar Chart with Timeouts") | |
stacked_bar_img = gr.Plot(label="Monthly Stacked Bar Chart with Timeouts") | |
# Link the click event | |
submit_button.click( | |
fn=get_report_with_user, | |
inputs=[username_input], | |
outputs=[output_data, summary_text, stacked_bar_img] | |
) | |
app.launch() # No authentication for Hugging Face Spaces |