Correction des multiples bugs

This commit is contained in:
Mathis 2026-05-01 16:16:33 +02:00
parent 5fe70d754b
commit 07198c20b7
15 changed files with 707 additions and 348 deletions

View file

@ -9,6 +9,11 @@ from collections import defaultdict
from typing import List, Dict, Any, Optional
import discord
import matplotlib.pyplot as plt
import plotly.graph_objects as go
import plotly.express as px
from plotly.subplots import make_subplots
import numpy as np
from PIL import Image, ImageDraw, ImageFont
from .data.storage import get_storage
@ -102,213 +107,74 @@ class StaffAnalytics:
return result
def create_staff_rating_chart(self, staff_list: List[Dict[str, Any]]) -> io.BytesIO:
"""Create a comprehensive staff rating chart"""
"""Create a comprehensive staff rating chart using matplotlib"""
# Filter staff with ratings
rated_staff = [s for s in staff_list if s['total_ratings'] > 0]
if not rated_staff:
# Return empty chart
img = Image.new('RGB', (800, 400), '#2F3136')
fig, ax = plt.subplots(figsize=(10, 6), facecolor='#2F3136')
ax.set_facecolor('#2F3136')
ax.text(0.5, 0.5, 'Aucune évaluation disponible', ha='center', va='center',
transform=ax.transAxes, color='white')
ax.set_title('Évaluations des Membres du Staff', color='white')
buf = io.BytesIO()
img.save(buf, format='PNG')
plt.savefig(buf, format='png', bbox_inches='tight', facecolor='#2F3136')
buf.seek(0)
plt.close()
return buf
# Image dimensions
width, height = 1200, 700
img = Image.new('RGB', (width, height), '#36393F') # Discord dark theme
draw = ImageDraw.Draw(img)
# Try to load fonts with better Unicode support
try:
# Try different font paths for better Unicode support
font_paths = [
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
"/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf",
"/System/Library/Fonts/Arial.ttf",
"arial.ttf"
]
font_title = None
font_label = None
font_value = None
for font_path in font_paths:
try:
font_title = ImageFont.truetype(font_path, 24)
font_label = ImageFont.truetype(font_path, 16)
font_value = ImageFont.truetype(font_path, 14)
break
except:
continue
if not font_title:
raise Exception("No suitable font found")
except:
# Fallback to default with better handling
font_title = ImageFont.load_default()
font_label = ImageFont.load_default()
font_value = ImageFont.load_default()
# Title with better styling
title = "Évaluations des Membres du Staff"
# Add background for title
title_bbox = draw.textbbox((0, 0), title, font=font_title)
title_width = title_bbox[2] - title_bbox[0]
title_x = (width - title_width) // 2
draw.rectangle([title_x - 20, 15, title_x + title_width + 20, 55], fill='#5865F2', outline='#FFFFFF', width=2)
draw.text((width//2, 35), title, fill='white', font=font_title, anchor='mm')
# Chart dimensions with better spacing
chart_left = 200
chart_right = width - 100
chart_top = 100
chart_bottom = height - 200
chart_width = chart_right - chart_left
chart_height = chart_bottom - chart_top
# Prepare data - limit to top 8 for better readability
top_staff = sorted(rated_staff, key=lambda x: x['average_rating'], reverse=True)[:8]
names = []
for s in top_staff:
name = s['name']
# Handle Unicode properly
try:
# Ensure proper encoding
name = name.encode('utf-8').decode('utf-8')
except:
name = str(name)
# Truncate long names
if len(name) > 12:
name = name[:12] + '...'
names.append(name)
# Sort by average rating
rated_staff.sort(key=lambda x: x['average_rating'], reverse=True)
# Prepare data - limit to top 10 for better readability
top_staff = rated_staff[:10]
names = [s['name'][:15] + '...' if len(s['name']) > 15 else s['name'] for s in top_staff]
ratings = [s['average_rating'] for s in top_staff]
counts = [s['total_ratings'] for s in top_staff]
if not ratings:
buf = io.BytesIO()
img.save(buf, format='PNG')
buf.seek(0)
return buf
max_rating = 5
bar_width = min(80, chart_width // len(ratings)) if ratings else 1
spacing = 20
# Draw background grid
for i in range(6):
y = chart_bottom - (i * chart_height // 5)
draw.line([chart_left, y, chart_right, y], fill='#72767D', width=1)
# Rating labels
draw.text((chart_left - 40, y), f"{i}", fill='#FFFFFF', font=font_value, anchor='mm')
# Draw bars with gradient effect
for i, (name, rating, count) in enumerate(zip(names, ratings, counts)):
bar_height = int((rating / max_rating) * chart_height)
x1 = chart_left + i * (bar_width + spacing) + spacing//2
y1 = chart_bottom - bar_height
x2 = x1 + bar_width
y2 = chart_bottom
# Bar color based on rating with better colors
if rating >= 4.5:
color = '#57F287' # Green
shadow_color = '#4CAF50'
elif rating >= 3.5:
color = '#FEE75C' # Yellow
shadow_color = '#FFC107'
elif rating >= 2.5:
color = '#FAA61A' # Orange
shadow_color = '#FF9800'
else:
color = '#ED4245' # Red
shadow_color = '#F44336'
# Draw shadow for 3D effect
draw.rectangle([x1+2, y1+2, x2+2, y2], fill=shadow_color, outline=shadow_color)
# Main bar
draw.rectangle([x1, y1, x2, y2], fill=color, outline='#FFFFFF', width=2)
# Rating value on top with background
rating_text = f"{rating:.1f}"
text_bbox = draw.textbbox((0, 0), rating_text, font=font_value)
text_width = text_bbox[2] - text_bbox[0]
text_x = x1 + bar_width//2
draw.rectangle([text_x - text_width//2 - 5, y1 - 25, text_x + text_width//2 + 5, y1 - 5], fill='#36393F', outline='#FFFFFF', width=1)
draw.text((text_x, y1 - 15), rating_text, fill='white', font=font_value, anchor='mm')
# Count below with background
count_text = f"({count})"
count_bbox = draw.textbbox((0, 0), count_text, font=font_value)
count_width = count_bbox[2] - count_bbox[0]
draw.rectangle([text_x - count_width//2 - 5, y2 + 5, text_x + count_width//2 + 5, y2 + 25], fill='#36393F', outline='#FFFFFF', width=1)
draw.text((text_x, y2 + 15), count_text, fill='white', font=font_value, anchor='mm')
# Draw staff names with better positioning
for i, name in enumerate(names):
x = chart_left + i * (bar_width + spacing) + bar_width//2 + spacing//2
# Handle long names by splitting
lines = []
if len(name) > 8:
# Split at spaces if possible
words = name.split()
current_line = ""
for word in words:
if len(current_line + word) < 8:
current_line += word + " "
else:
lines.append(current_line.strip())
current_line = word + " "
if current_line:
lines.append(current_line.strip())
if not lines:
lines = [name[:8] + '...']
else:
lines = [name]
y_offset = chart_bottom + 40
for line in lines:
# Background for name
name_bbox = draw.textbbox((0, 0), line, font=font_label)
name_width = name_bbox[2] - name_bbox[0]
draw.rectangle([x - name_width//2 - 5, y_offset - 5, x + name_width//2 + 5, y_offset + 15], fill='#36393F', outline='#FFFFFF', width=1)
draw.text((x, y_offset + 5), line, fill='white', font=font_label, anchor='mm')
y_offset += 20
# Y-axis label
draw.text((chart_left - 120, height//2), 'Note Moyenne', fill='white', font=font_label, anchor='mm')
# Enhanced legend with better positioning
legend_x = chart_right - 250
legend_y = chart_top + 50
# Legend background
draw.rectangle([legend_x - 20, legend_y - 10, legend_x + 230, legend_y + 120], fill='#36393F', outline='#FFFFFF', width=2)
draw.text((legend_x, legend_y), "Légende des Notes:", fill='white', font=font_label)
legend_items = [
("🟢 Excellent", "4.5+", '#57F287'),
("🟡 Bon", "3.5-4.4", '#FEE75C'),
("🟠 Moyen", "2.5-3.4", '#FAA61A'),
("🔴 À améliorer", "<2.5", '#ED4245')
# Create figure
fig, ax = plt.subplots(figsize=(12, 8), facecolor='#2F3136')
fig.patch.set_facecolor('#2F3136')
ax.set_facecolor('#2F3136')
# Create bars with color coding
colors = ['#57F287' if r >= 4.5 else '#FEE75C' if r >= 3.5 else '#FAA61A' if r >= 2.5 else '#ED4245' for r in ratings]
bars = ax.bar(range(len(ratings)), ratings, color=colors)
# Customize
ax.set_xticks(range(len(names)))
ax.set_xticklabels(names, rotation=45, ha='right', color='white')
ax.set_ylabel('Note moyenne', color='white')
ax.set_title('Évaluations des Membres du Staff', color='white', fontsize=16)
ax.tick_params(colors='white')
ax.grid(True, alpha=0.3, color='gray')
# Add value labels on bars (avoiding Unicode stars to prevent warnings)
for i, (bar, rating, count) in enumerate(zip(bars, ratings, counts)):
ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.05,
f'{rating:.1f}\n({count})', ha='center', va='bottom', color='white', fontsize=8)
# Set y-axis limits
ax.set_ylim(0, 5.2)
# Add legend
legend_elements = [
plt.Rectangle((0,0),1,1, color='#57F287', label='Excellent (4.5+)'),
plt.Rectangle((0,0),1,1, color='#FEE75C', label='Bon (3.5-4.4)'),
plt.Rectangle((0,0),1,1, color='#FAA61A', label='Moyen (2.5-3.4)'),
plt.Rectangle((0,0),1,1, color='#ED4245', label='À améliorer (<2.5)')
]
for i, (emoji_text, range_text, color) in enumerate(legend_items):
y_pos = legend_y + 25 + i * 20
draw.text((legend_x, y_pos), f"{emoji_text} {range_text}", fill=color, font=font_value)
# Add some stats at the bottom
stats_text = f"Total évalué: {len(rated_staff)} membres • Moyenne générale: {sum(ratings)/len(ratings):.1f}"
draw.text((width//2, height - 30), stats_text, fill='#B9BBBE', font=font_value, anchor='mm')
ax.legend(handles=legend_elements, loc='upper right', facecolor='#2F3136', edgecolor='white', labelcolor='white')
# Adjust layout
plt.tight_layout()
# Save to buffer
buf = io.BytesIO()
img.save(buf, format='PNG')
plt.savefig(buf, format='png', bbox_inches='tight', facecolor='#2F3136')
buf.seek(0)
plt.close()
return buf
def create_individual_staff_chart(self, staff_data: Dict[str, Any]) -> io.BytesIO: