Skip to content

Commit 8656852

Browse files
feat(highcharts): implement sparkline-basic (#1072)
## Implementation: `sparkline-basic` - highcharts Implements the **highcharts** version of `sparkline-basic`. **File:** `plots/sparkline-basic/implementations/highcharts.py` **Parent Issue:** #1011 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20275207199)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 15a23b1 commit 8656852

2 files changed

Lines changed: 200 additions & 0 deletions

File tree

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
"""
2+
sparkline-basic: Basic Sparkline
3+
Library: highcharts
4+
"""
5+
6+
import tempfile
7+
import time
8+
import urllib.request
9+
from pathlib import Path
10+
11+
import numpy as np
12+
from highcharts_core.chart import Chart
13+
from highcharts_core.options import HighchartsOptions
14+
from highcharts_core.options.series.area import LineSeries
15+
from selenium import webdriver
16+
from selenium.webdriver.chrome.options import Options
17+
18+
19+
# Data - simulate daily sales trend over 60 days with realistic patterns
20+
np.random.seed(42)
21+
base = 100
22+
trend = np.linspace(0, 20, 60) # Gradual upward trend
23+
seasonal = 10 * np.sin(np.linspace(0, 4 * np.pi, 60)) # Cyclical pattern
24+
noise = np.random.randn(60) * 5 # Random variation
25+
values = base + trend + seasonal + noise
26+
27+
# Create chart - sparklines are minimal with no chrome
28+
chart = Chart(container="container")
29+
chart.options = HighchartsOptions()
30+
31+
# Chart settings - standard pyplots size with sparkline aesthetic
32+
# Sparklines are naturally compact, shown in center of larger canvas
33+
chart.options.chart = {
34+
"type": "line",
35+
"width": 4800,
36+
"height": 2700,
37+
"backgroundColor": "#ffffff",
38+
"margin": [100, 100, 100, 100], # Balanced margins for compact look
39+
"spacing": [0, 0, 0, 0],
40+
}
41+
42+
# Title with pyplots format - large for 4800x2700 canvas
43+
chart.options.title = {
44+
"text": "sparkline-basic · highcharts · pyplots.ai",
45+
"align": "center",
46+
"style": {"fontSize": "72px", "fontWeight": "bold"},
47+
"y": 80,
48+
}
49+
50+
# Hide axes - sparklines have no axes
51+
chart.options.x_axis = {"visible": False, "lineWidth": 0, "tickWidth": 0, "labels": {"enabled": False}}
52+
chart.options.y_axis = {"visible": False, "gridLineWidth": 0, "labels": {"enabled": False}}
53+
54+
# Hide legend
55+
chart.options.legend = {"enabled": False}
56+
57+
# Series data - the sparkline itself
58+
series = LineSeries()
59+
series.data = values.tolist()
60+
series.name = "Trend"
61+
series.color = "#306998" # Python Blue
62+
series.line_width = 8 # Visible but clean line at 4800x2700
63+
64+
# Marker settings - highlight min/max and endpoints
65+
series.marker = {
66+
"enabled": False, # Hide most markers
67+
"radius": 0,
68+
}
69+
70+
chart.add_series(series)
71+
72+
# Add min point marker - larger for 4800x2700
73+
min_idx = int(np.argmin(values))
74+
min_series = LineSeries()
75+
min_series.data = [{"x": min_idx, "y": float(values[min_idx])}]
76+
min_series.name = "Min"
77+
min_series.color = "#E74C3C" # Bright red for minimum
78+
min_series.marker = {"enabled": True, "radius": 24, "symbol": "circle"}
79+
min_series.line_width = 0
80+
min_series.states = {"hover": {"lineWidthPlus": 0}}
81+
chart.add_series(min_series)
82+
83+
# Add max point marker - larger for 4800x2700
84+
max_idx = int(np.argmax(values))
85+
max_series = LineSeries()
86+
max_series.data = [{"x": max_idx, "y": float(values[max_idx])}]
87+
max_series.name = "Max"
88+
max_series.color = "#27AE60" # Bright green for maximum
89+
max_series.marker = {"enabled": True, "radius": 24, "symbol": "circle"}
90+
max_series.line_width = 0
91+
max_series.states = {"hover": {"lineWidthPlus": 0}}
92+
chart.add_series(max_series)
93+
94+
# Add first point marker (reference) - larger for 4800x2700
95+
first_series = LineSeries()
96+
first_series.data = [{"x": 0, "y": float(values[0])}]
97+
first_series.name = "Start"
98+
first_series.color = "#6B7280" # Gray for start
99+
first_series.marker = {"enabled": True, "radius": 20, "symbol": "circle"}
100+
first_series.line_width = 0
101+
first_series.states = {"hover": {"lineWidthPlus": 0}}
102+
chart.add_series(first_series)
103+
104+
# Add last point marker (reference) - larger for 4800x2700
105+
last_series = LineSeries()
106+
last_series.data = [{"x": len(values) - 1, "y": float(values[-1])}]
107+
last_series.name = "End"
108+
last_series.color = "#FFD43B" # Python Yellow for end
109+
last_series.marker = {"enabled": True, "radius": 20, "symbol": "circle"}
110+
last_series.line_width = 0
111+
last_series.states = {"hover": {"lineWidthPlus": 0}}
112+
chart.add_series(last_series)
113+
114+
# Plot options for clean rendering
115+
chart.options.plot_options = {
116+
"series": {"animation": False, "enableMouseTracking": False},
117+
"line": {"marker": {"enabled": False}},
118+
}
119+
120+
# Download Highcharts JS for headless rendering
121+
highcharts_url = "https://code.highcharts.com/highcharts.js"
122+
with urllib.request.urlopen(highcharts_url, timeout=30) as response:
123+
highcharts_js = response.read().decode("utf-8")
124+
125+
# Generate HTML with inline scripts
126+
html_str = chart.to_js_literal()
127+
html_content = f"""<!DOCTYPE html>
128+
<html>
129+
<head>
130+
<meta charset="utf-8">
131+
<script>{highcharts_js}</script>
132+
</head>
133+
<body style="margin:0; background-color: #ffffff;">
134+
<div id="container" style="width: 4800px; height: 2700px;"></div>
135+
<script>{html_str}</script>
136+
</body>
137+
</html>"""
138+
139+
# Save interactive HTML version
140+
with open("plot.html", "w", encoding="utf-8") as f:
141+
# For HTML file, use CDN links for better compatibility
142+
html_interactive = (
143+
"""<!DOCTYPE html>
144+
<html>
145+
<head>
146+
<meta charset="utf-8">
147+
<script src="https://code.highcharts.com/highcharts.js"></script>
148+
</head>
149+
<body style="margin:0; background-color: #ffffff;">
150+
<div id="container" style="width: 100%; height: 100vh;"></div>
151+
<script>"""
152+
+ html_str
153+
+ """</script>
154+
</body>
155+
</html>"""
156+
)
157+
f.write(html_interactive)
158+
159+
# Write temp HTML and take screenshot
160+
with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f:
161+
f.write(html_content)
162+
temp_path = f.name
163+
164+
chrome_options = Options()
165+
chrome_options.add_argument("--headless")
166+
chrome_options.add_argument("--no-sandbox")
167+
chrome_options.add_argument("--disable-dev-shm-usage")
168+
chrome_options.add_argument("--disable-gpu")
169+
chrome_options.add_argument("--window-size=4800,2700")
170+
171+
driver = webdriver.Chrome(options=chrome_options)
172+
driver.get(f"file://{temp_path}")
173+
time.sleep(5) # Wait for chart to render
174+
driver.save_screenshot("plot.png")
175+
driver.quit()
176+
177+
Path(temp_path).unlink() # Clean up temp file
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Per-library metadata for highcharts implementation of sparkline-basic
2+
# Auto-generated by impl-generate.yml
3+
4+
library: highcharts
5+
specification_id: sparkline-basic
6+
7+
# Preview URLs (filled by workflow)
8+
preview_url: https://storage.googleapis.com/pyplots-images/plots/sparkline-basic/highcharts/plot.png
9+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/sparkline-basic/highcharts/plot_thumb.png
10+
preview_html: https://storage.googleapis.com/pyplots-images/plots/sparkline-basic/highcharts/plot.html
11+
12+
current:
13+
version: 0
14+
generated_at: 2025-12-16T16:35:22Z
15+
generated_by: claude-opus-4-5-20251101
16+
workflow_run: 20275207199
17+
issue: 1011
18+
quality_score: 91
19+
# Version info (filled by workflow)
20+
python_version: "3.13.11"
21+
library_version: "unknown"
22+
23+
history: []

0 commit comments

Comments
 (0)