Draw graph for performance history
This commit is contained in:
parent
4503246bcc
commit
65dc9a4d12
120
aocsync.py
120
aocsync.py
@ -832,6 +832,97 @@ class HTMLGenerator:
|
||||
|
||||
logger.info(f"Generated HTML report: {output_file}")
|
||||
|
||||
@staticmethod
|
||||
def _generate_svg_graph(data_points: List[dict]) -> str:
|
||||
"""Generate an SVG line graph showing performance over time"""
|
||||
if len(data_points) < 2:
|
||||
return ""
|
||||
|
||||
# Graph dimensions
|
||||
width = 600
|
||||
height = 200
|
||||
padding = 40
|
||||
graph_width = width - 2 * padding
|
||||
graph_height = height - 2 * padding
|
||||
|
||||
# Extract time values
|
||||
times = [dp['time_ns'] for dp in data_points]
|
||||
min_time = min(times)
|
||||
max_time = max(times)
|
||||
# Add 10% padding to range for better visualization, or minimum 1% of max value
|
||||
time_range = max_time - min_time if max_time > min_time else max(max_time * 0.01, 1)
|
||||
if time_range > 0:
|
||||
padding_amount = time_range * 0.1
|
||||
min_time = max(0, min_time - padding_amount)
|
||||
max_time = max_time + padding_amount
|
||||
time_range = max_time - min_time
|
||||
|
||||
# Format time for display
|
||||
def format_time(ns):
|
||||
ms = ns / 1_000_000
|
||||
us = ns / 1_000
|
||||
if ms >= 1:
|
||||
return f"{ms:.2f}ms"
|
||||
elif us >= 1:
|
||||
return f"{us:.2f}μs"
|
||||
else:
|
||||
return f"{ns}ns"
|
||||
|
||||
# Generate SVG
|
||||
svg_parts = []
|
||||
svg_parts.append(f'<svg width="{width}" height="{height}" style="border: 1px solid #ddd; background: #fafafa;">')
|
||||
|
||||
# Draw axes
|
||||
svg_parts.append(f'<line x1="{padding}" y1="{padding}" x2="{padding}" y2="{height - padding}" stroke="#333" stroke-width="2"/>') # Y-axis
|
||||
svg_parts.append(f'<line x1="{padding}" y1="{height - padding}" x2="{width - padding}" y2="{height - padding}" stroke="#333" stroke-width="2"/>') # X-axis
|
||||
|
||||
# Draw grid lines and labels
|
||||
num_grid_lines = 5
|
||||
for i in range(num_grid_lines + 1):
|
||||
y_pos = padding + (graph_height * i / num_grid_lines)
|
||||
time_val = max_time - (time_range * i / num_grid_lines)
|
||||
|
||||
# Grid line
|
||||
if i < num_grid_lines:
|
||||
svg_parts.append(f'<line x1="{padding}" y1="{y_pos}" x2="{width - padding}" y2="{y_pos}" stroke="#e0e0e0" stroke-width="1"/>')
|
||||
|
||||
# Y-axis label
|
||||
svg_parts.append(f'<text x="{padding - 5}" y="{y_pos + 4}" text-anchor="end" font-size="10" fill="#666">{format_time(time_val)}</text>')
|
||||
|
||||
# Draw data points and line
|
||||
points = []
|
||||
for i, dp in enumerate(data_points):
|
||||
x = padding + (graph_width * i / (len(data_points) - 1)) if len(data_points) > 1 else padding
|
||||
y = padding + graph_height - (graph_height * (dp['time_ns'] - min_time) / time_range)
|
||||
points.append((x, y))
|
||||
|
||||
# Draw line connecting points
|
||||
if len(points) > 1:
|
||||
path_d = f"M {points[0][0]} {points[0][1]}"
|
||||
for x, y in points[1:]:
|
||||
path_d += f" L {x} {y}"
|
||||
svg_parts.append(f'<path d="{path_d}" fill="none" stroke="#667eea" stroke-width="2"/>')
|
||||
|
||||
# Draw points
|
||||
for x, y in points:
|
||||
svg_parts.append(f'<circle cx="{x}" cy="{y}" r="4" fill="#667eea" stroke="#fff" stroke-width="2"/>')
|
||||
|
||||
# X-axis labels (show first, middle, last)
|
||||
if len(data_points) > 0:
|
||||
indices_to_label = [0]
|
||||
if len(data_points) > 2:
|
||||
indices_to_label.append(len(data_points) // 2)
|
||||
if len(data_points) > 1:
|
||||
indices_to_label.append(len(data_points) - 1)
|
||||
|
||||
for idx in indices_to_label:
|
||||
x = padding + (graph_width * idx / (len(data_points) - 1)) if len(data_points) > 1 else padding
|
||||
date_str = data_points[idx]['date'][:10] if data_points[idx]['date'] else ''
|
||||
svg_parts.append(f'<text x="{x}" y="{height - padding + 15}" text-anchor="middle" font-size="9" fill="#666">{date_str}</text>')
|
||||
|
||||
svg_parts.append('</svg>')
|
||||
return ''.join(svg_parts)
|
||||
|
||||
def _generate_html(self, data: dict, years: List[int], users: List[str], db: Database) -> str:
|
||||
"""Generate HTML content"""
|
||||
# Sort years descending (most recent first)
|
||||
@ -1119,6 +1210,13 @@ class HTMLGenerator:
|
||||
border-bottom: none;
|
||||
}}
|
||||
|
||||
.history-graph {{
|
||||
margin: 15px 0;
|
||||
padding: 10px;
|
||||
background: white;
|
||||
border-radius: 4px;
|
||||
}}
|
||||
|
||||
.summary {{
|
||||
margin-top: 30px;
|
||||
padding: 15px;
|
||||
@ -1347,7 +1445,8 @@ class HTMLGenerator:
|
||||
history_link = ""
|
||||
if len(historical) > 1:
|
||||
history_items = []
|
||||
for hist in historical[:10]: # Show last 10 runs
|
||||
hist_data_points = []
|
||||
for hist in historical[:20]: # Show last 20 runs for graph
|
||||
hist_total = hist['time_ns'] + hist.get('generator_time_ns', 0)
|
||||
hist_ms = hist_total / 1_000_000
|
||||
hist_us = hist_total / 1_000
|
||||
@ -1366,6 +1465,16 @@ class HTMLGenerator:
|
||||
else:
|
||||
hist_git_link = hist_git
|
||||
history_items.append(f'<div class="history-item">{hist_date}: {hist_time_str} ({hist_git_link})</div>')
|
||||
|
||||
# Store data for graph (reverse order for chronological display)
|
||||
hist_data_points.insert(0, {
|
||||
'time_ns': hist_total,
|
||||
'timestamp': hist.get('timestamp', ''),
|
||||
'date': hist_date
|
||||
})
|
||||
|
||||
# Generate SVG graph
|
||||
svg_graph = HTMLGenerator._generate_svg_graph(hist_data_points)
|
||||
|
||||
history_modal = f'''
|
||||
<div id="history-{user}-{year}-{day}-{part}" class="modal">
|
||||
@ -1375,7 +1484,14 @@ class HTMLGenerator:
|
||||
<span class="modal-close" onclick="closeHistory('{user}', {year}, {day}, {part})">×</span>
|
||||
</div>
|
||||
<div>
|
||||
{''.join(history_items)}
|
||||
<div class="history-graph">
|
||||
<strong>Performance Trend:</strong>
|
||||
{svg_graph}
|
||||
</div>
|
||||
<div style="margin-top: 20px;">
|
||||
<strong>History Details:</strong>
|
||||
{''.join(history_items)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user