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='Wins: %{y}' )) # Add losses bars stacked on top of wins fig.add_trace(go.Bar( x=months, y=losses, name='Losses', marker=dict(color='#ff7f0e'), hovertemplate='Losses: %{y}' )) # 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='Timeouts: %{y:.1f}%' )) # 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