Spaces:
Sleeping
Sleeping
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,222 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
from utils.data_loader import fetch_and_save_chess_data
|
3 |
+
from utils.game_analysis import (
|
4 |
+
analyze_games,
|
5 |
+
generate_monthly_report,
|
6 |
+
calculate_average_and_median_games,
|
7 |
+
analyze_streaks,
|
8 |
+
analyze_sequences,
|
9 |
+
format_duration
|
10 |
+
)
|
11 |
+
from datetime import datetime
|
12 |
+
import os
|
13 |
+
import json
|
14 |
+
|
15 |
+
import matplotlib.pyplot as plt
|
16 |
+
import io
|
17 |
+
from PIL import Image
|
18 |
+
import plotly.graph_objects as go
|
19 |
+
import pandas as pd
|
20 |
+
import csv
|
21 |
+
|
22 |
+
logged_in_user = None # Global state to store the logged-in user
|
23 |
+
|
24 |
+
|
25 |
+
# Define your user credentials
|
26 |
+
auth_users = [
|
27 |
+
("Sacha", "SachaIsTheBest"),
|
28 |
+
("Florian", "FlorianIsTheBest"),
|
29 |
+
("Lucas", "Slevin"),
|
30 |
+
("Gael", "Kalel"),
|
31 |
+
("BlueNote", "MamaLinda")
|
32 |
+
]
|
33 |
+
|
34 |
+
|
35 |
+
|
36 |
+
DATA_FOLDER = 'data/'
|
37 |
+
LOG_FILE = 'user_logs.csv'
|
38 |
+
|
39 |
+
# Initialize log file if it doesn't exist
|
40 |
+
if not os.path.exists(LOG_FILE):
|
41 |
+
with open(LOG_FILE, 'w', newline='') as log_file:
|
42 |
+
writer = csv.writer(log_file)
|
43 |
+
writer.writerow(["Username", "Timestamp", "Action", "Query"])
|
44 |
+
|
45 |
+
def log_user_action(username, action, query=""):
|
46 |
+
"""Log user actions to a CSV file."""
|
47 |
+
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
48 |
+
with open(LOG_FILE, 'a', newline='') as log_file:
|
49 |
+
writer = csv.writer(log_file)
|
50 |
+
writer.writerow([username, timestamp, action, query])
|
51 |
+
|
52 |
+
def get_monthly_report(username, logged_in_user):
|
53 |
+
"""Fetch data for a given username, reuse existing data if available, and generate a monthly report."""
|
54 |
+
current_date = datetime.now().strftime('%Y-%m-%d')
|
55 |
+
filename = os.path.join(DATA_FOLDER, f"{username}_{current_date}.json")
|
56 |
+
|
57 |
+
log_user_action(logged_in_user, "Search", username)
|
58 |
+
|
59 |
+
if os.path.exists(filename):
|
60 |
+
print(f"Using existing data file: {filename}")
|
61 |
+
with open(filename, 'r') as file:
|
62 |
+
games = json.load(file)
|
63 |
+
else:
|
64 |
+
games = fetch_and_save_chess_data(username, filename)
|
65 |
+
if not games:
|
66 |
+
return "No data found for the specified username."
|
67 |
+
|
68 |
+
games_sorted = sorted(games, key=lambda x: x.get('end_time'))
|
69 |
+
|
70 |
+
# Perform monthly analysis
|
71 |
+
games_per_month, stats_per_month, total_games, total_wins, total_losses, total_timeouts, total_months_played = analyze_games(games, username)
|
72 |
+
report_df = generate_monthly_report(games_per_month, stats_per_month)
|
73 |
+
|
74 |
+
# Calculate average and median games per day
|
75 |
+
average_games, median_games = calculate_average_and_median_games(games)
|
76 |
+
|
77 |
+
# Calculate streak probabilities
|
78 |
+
win_prob, loss_prob = analyze_streaks(games_sorted, username)
|
79 |
+
|
80 |
+
# Calculate sequence probabilities
|
81 |
+
win_after_wl_prob, win_after_lw_prob = analyze_sequences(games_sorted, username)
|
82 |
+
|
83 |
+
# Format the duration of months played
|
84 |
+
formatted_duration = format_duration(total_months_played)
|
85 |
+
|
86 |
+
|
87 |
+
# Prepare the text summary
|
88 |
+
summary_text = (
|
89 |
+
f"Total duration played: {formatted_duration}\n"
|
90 |
+
f"Total games played: {total_games}\n"
|
91 |
+
f"Total wins: {total_wins}\n"
|
92 |
+
f"Total losses: {total_losses}\n"
|
93 |
+
f"Total timeouts: {total_timeouts}\n"
|
94 |
+
f"Average games played per day: {average_games:.2f}\n"
|
95 |
+
f"Median games played per day: {median_games}\n"
|
96 |
+
f"Probability of winning the next game after a win in the same hour: {win_prob:.2f}%\n"
|
97 |
+
f"Probability of losing the next game after a loss in the same hour: {loss_prob:.2f}%\n"
|
98 |
+
f"Probability of winning the next game after a 'win-loss' sequence in the same hour: {win_after_wl_prob:.2f}%\n"
|
99 |
+
f"Probability of winning the next game after a 'loss-win' sequence in the same hour: {win_after_lw_prob:.2f}%"
|
100 |
+
)
|
101 |
+
|
102 |
+
stacked_bar_img = generate_stacked_bar_chart(report_df)
|
103 |
+
|
104 |
+
return report_df, summary_text, stacked_bar_img
|
105 |
+
|
106 |
+
|
107 |
+
|
108 |
+
|
109 |
+
def generate_stacked_bar_chart(report_df):
|
110 |
+
"""Generate an interactive stacked bar chart with wins, losses, and a line plot for timeouts using Plotly."""
|
111 |
+
# Extract data
|
112 |
+
months = pd.to_datetime(report_df['Month']).dt.strftime('%b, %Y')
|
113 |
+
wins = report_df['Wins']
|
114 |
+
losses = report_df['Losses']
|
115 |
+
timeouts = report_df['Timeout Rate (%)']
|
116 |
+
total_games = report_df['Games Played']
|
117 |
+
|
118 |
+
# Create the figure
|
119 |
+
fig = go.Figure()
|
120 |
+
|
121 |
+
# Add wins bars
|
122 |
+
fig.add_trace(go.Bar(
|
123 |
+
x=months,
|
124 |
+
y=wins,
|
125 |
+
name='Wins',
|
126 |
+
marker=dict(color='#1f77b4'),
|
127 |
+
hovertemplate='<b>Wins</b>: %{y}<extra></extra>'
|
128 |
+
))
|
129 |
+
|
130 |
+
# Add losses bars stacked on top of wins
|
131 |
+
fig.add_trace(go.Bar(
|
132 |
+
x=months,
|
133 |
+
y=losses,
|
134 |
+
name='Losses',
|
135 |
+
marker=dict(color='#ff7f0e'),
|
136 |
+
hovertemplate='<b>Losses</b>: %{y}<extra></extra>'
|
137 |
+
))
|
138 |
+
|
139 |
+
# Add timeouts as a line plot
|
140 |
+
fig.add_trace(go.Scatter(
|
141 |
+
x=months,
|
142 |
+
y=timeouts,
|
143 |
+
mode='lines+markers',
|
144 |
+
name='Timeouts',
|
145 |
+
line=dict(color='#da5bac', width=2),
|
146 |
+
hovertemplate='<b>Timeouts</b>: %{y:.1f}%<extra></extra>'
|
147 |
+
))
|
148 |
+
|
149 |
+
# Add rotated annotations for total games on top of each bar
|
150 |
+
for i, total in enumerate(total_games):
|
151 |
+
fig.add_annotation(
|
152 |
+
x=months[i],
|
153 |
+
y=total + 5,
|
154 |
+
text=str(int(total)),
|
155 |
+
showarrow=False,
|
156 |
+
font=dict(size=12, color='white'),
|
157 |
+
align='center',
|
158 |
+
textangle=-45 # Rotate the text label by -45 degrees
|
159 |
+
)
|
160 |
+
|
161 |
+
# Update layout for stacked bars and hover information
|
162 |
+
fig.update_layout(
|
163 |
+
barmode='stack',
|
164 |
+
title='Monthly Win/Loss Stacked Bar Chart with Total Games and Timeouts',
|
165 |
+
xaxis_title='Month',
|
166 |
+
yaxis_title='Count',
|
167 |
+
legend_title='Legend',
|
168 |
+
hovermode='x unified',
|
169 |
+
template='plotly_dark'
|
170 |
+
)
|
171 |
+
|
172 |
+
# Return the Plotly figure
|
173 |
+
return fig
|
174 |
+
|
175 |
+
logged_in_user_state = gr.State()
|
176 |
+
|
177 |
+
# Authentication callback function
|
178 |
+
def auth_callback(username, password):
|
179 |
+
global logged_in_user
|
180 |
+
valid_users = dict(auth_users)
|
181 |
+
if username in valid_users and valid_users[username] == password:
|
182 |
+
log_user_action(username, "Login")
|
183 |
+
logged_in_user = username # Store the logged-in user globally
|
184 |
+
return True
|
185 |
+
return False
|
186 |
+
|
187 |
+
|
188 |
+
|
189 |
+
|
190 |
+
def get_report_with_user(username):
|
191 |
+
global logged_in_user
|
192 |
+
return get_monthly_report(username, logged_in_user or "Unknown")
|
193 |
+
|
194 |
+
|
195 |
+
|
196 |
+
|
197 |
+
|
198 |
+
|
199 |
+
|
200 |
+
# Custom layout using gr.Blocks with CSS
|
201 |
+
with gr.Blocks(css="style.css", theme=gr.themes.Soft()) as app:
|
202 |
+
gr.Markdown("# Chess Analysis App")
|
203 |
+
username_input = gr.Textbox(label="Chess.com Username", placeholder="Enter Chess.com username")
|
204 |
+
submit_button = gr.Button("Submit")
|
205 |
+
|
206 |
+
# Define outputs
|
207 |
+
output_data = gr.Dataframe(headers=["Month", "Games Played", "Wins", "Losses", "Win Rate (%)", "Loss Rate (%)", "Timeout Rate (%)"])
|
208 |
+
summary_text = gr.Textbox(label="Summary", interactive=False)
|
209 |
+
# stacked_bar_img = gr.Image(label="Monthly Stacked Bar Chart with Timeouts")
|
210 |
+
stacked_bar_img = gr.Plot(label="Monthly Stacked Bar Chart with Timeouts")
|
211 |
+
|
212 |
+
|
213 |
+
# Link the click event
|
214 |
+
submit_button.click(
|
215 |
+
fn=get_report_with_user,
|
216 |
+
inputs=[username_input],
|
217 |
+
outputs=[output_data, summary_text, stacked_bar_img]
|
218 |
+
)
|
219 |
+
|
220 |
+
|
221 |
+
|
222 |
+
app.launch(auth=auth_users, share=True)
|