From 65dc9a4d127d3be84e22a672df0eebf419ede220 Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Wed, 3 Dec 2025 10:40:08 -0800 Subject: [PATCH] Draw graph for performance history --- aocsync.py | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 118 insertions(+), 2 deletions(-) diff --git a/aocsync.py b/aocsync.py index dc10cee..ef4f948 100755 --- a/aocsync.py +++ b/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'') + + # Draw axes + svg_parts.append(f'') # Y-axis + svg_parts.append(f'') # 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'') + + # Y-axis label + svg_parts.append(f'{format_time(time_val)}') + + # 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'') + + # Draw points + for x, y in points: + svg_parts.append(f'') + + # 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'{date_str}') + + svg_parts.append('') + 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'
{hist_date}: {hist_time_str} ({hist_git_link})
') + + # 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'''
- {''.join(history_items)} +
+ Performance Trend: + {svg_graph} +
+
+ History Details: + {''.join(history_items)} +