Draw graph for performance history

This commit is contained in:
Bill Thiede 2025-12-03 10:40:08 -08:00
parent 4503246bcc
commit 65dc9a4d12

View File

@ -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})">&times;</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>