Skip to content

Commit 15b4fae

Browse files
feat(highcharts): implement timeseries-forecast-uncertainty (#7400)
## Implementation: `timeseries-forecast-uncertainty` - python/highcharts Implements the **python/highcharts** version of `timeseries-forecast-uncertainty`. **File:** `plots/timeseries-forecast-uncertainty/implementations/python/highcharts.py` **Parent Issue:** #3188 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/anyplot/actions/runs/26099407112)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com>
1 parent b380abc commit 15b4fae

2 files changed

Lines changed: 146 additions & 140 deletions

File tree

plots/timeseries-forecast-uncertainty/implementations/python/highcharts.py

Lines changed: 54 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
""" anyplot.ai
22
timeseries-forecast-uncertainty: Time Series Forecast with Uncertainty Band
33
Library: highcharts unknown | Python 3.13.13
4-
Quality: 92/100 | Updated: 2026-05-16
4+
Quality: 87/100 | Updated: 2026-05-19
55
"""
66

77
import os
@@ -19,6 +19,19 @@
1919
from selenium.webdriver.chrome.options import Options
2020

2121

22+
def _download_js(urls):
23+
for url in urls:
24+
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36"})
25+
for attempt in range(2):
26+
try:
27+
with urllib.request.urlopen(req, timeout=30) as response:
28+
return response.read().decode("utf-8")
29+
except (urllib.error.URLError, urllib.error.HTTPError):
30+
if attempt == 0:
31+
time.sleep(1)
32+
return None
33+
34+
2235
# Theme tokens
2336
THEME = os.getenv("ANYPLOT_THEME", "light")
2437
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
@@ -71,52 +84,24 @@
7184
forecast_start_ts = forecast_timestamps[0]
7285

7386
# Download Highcharts JS from CDN with fallbacks
74-
cdn_urls = [
75-
"https://cdn.jsdelivr.net/npm/highcharts@11/highcharts.min.js",
76-
"https://unpkg.com/highcharts@11/highcharts.js",
77-
"https://code.highcharts.com/highcharts.js",
78-
]
79-
80-
highcharts_js = None
81-
for url in cdn_urls:
82-
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36"})
83-
max_retries = 2
84-
for attempt in range(max_retries):
85-
try:
86-
with urllib.request.urlopen(req, timeout=30) as response:
87-
highcharts_js = response.read().decode("utf-8")
88-
break
89-
except (urllib.error.URLError, urllib.error.HTTPError):
90-
if attempt < max_retries - 1:
91-
time.sleep(1)
92-
if highcharts_js:
93-
break
94-
87+
highcharts_js = _download_js(
88+
[
89+
"https://cdn.jsdelivr.net/npm/highcharts@11/highcharts.min.js",
90+
"https://unpkg.com/highcharts@11/highcharts.js",
91+
"https://code.highcharts.com/highcharts.js",
92+
]
93+
)
9594
if not highcharts_js:
9695
raise RuntimeError("Could not download Highcharts from any CDN")
9796

9897
# Download Highcharts More for arearange
99-
cdn_more_urls = [
100-
"https://cdn.jsdelivr.net/npm/highcharts@11/highcharts-more.min.js",
101-
"https://unpkg.com/highcharts@11/highcharts-more.js",
102-
"https://code.highcharts.com/highcharts-more.js",
103-
]
104-
105-
highcharts_more_js = None
106-
for url in cdn_more_urls:
107-
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36"})
108-
max_retries = 2
109-
for attempt in range(max_retries):
110-
try:
111-
with urllib.request.urlopen(req, timeout=30) as response:
112-
highcharts_more_js = response.read().decode("utf-8")
113-
break
114-
except (urllib.error.URLError, urllib.error.HTTPError):
115-
if attempt < max_retries - 1:
116-
time.sleep(1)
117-
if highcharts_more_js:
118-
break
119-
98+
highcharts_more_js = _download_js(
99+
[
100+
"https://cdn.jsdelivr.net/npm/highcharts@11/highcharts-more.min.js",
101+
"https://unpkg.com/highcharts@11/highcharts-more.js",
102+
"https://code.highcharts.com/highcharts-more.js",
103+
]
104+
)
120105
if not highcharts_more_js:
121106
raise RuntimeError("Could not download Highcharts More from any CDN")
122107

@@ -127,27 +112,27 @@
127112
# Chart configuration
128113
chart.options.chart = {
129114
"type": "line",
130-
"width": 4800,
131-
"height": 2700,
115+
"width": 3200,
116+
"height": 1800,
132117
"backgroundColor": PAGE_BG,
133-
"spacingTop": 60,
134-
"spacingBottom": 120,
135-
"spacingLeft": 100,
136-
"spacingRight": 120,
118+
"spacingTop": 40,
119+
"spacingBottom": 100,
120+
"spacingLeft": 60,
121+
"spacingRight": 80,
137122
}
138123

139124
# Title
140125
chart.options.title = {
141-
"text": "timeseries-forecast-uncertainty · highcharts · anyplot.ai",
142-
"style": {"fontSize": "28px", "fontWeight": "medium", "color": INK},
143-
"margin": 40,
126+
"text": "timeseries-forecast-uncertainty · python · highcharts · anyplot.ai",
127+
"style": {"fontSize": "18px", "fontWeight": "medium", "color": INK},
128+
"margin": 30,
144129
}
145130

146131
# X-axis (datetime)
147132
chart.options.x_axis = {
148133
"type": "datetime",
149-
"title": {"text": "Date", "style": {"fontSize": "22px", "color": INK}, "margin": 25},
150-
"labels": {"style": {"fontSize": "18px", "color": INK_SOFT}},
134+
"title": {"text": "Date", "style": {"fontSize": "14px", "color": INK}, "margin": 15},
135+
"labels": {"style": {"fontSize": "12px", "color": INK_SOFT}},
151136
"dateTimeLabelFormats": {"month": "%b %Y"},
152137
"gridLineWidth": 1,
153138
"gridLineColor": GRID,
@@ -157,13 +142,13 @@
157142
{
158143
"value": forecast_start_ts,
159144
"color": INK_SOFT,
160-
"width": 3,
145+
"width": 2,
161146
"dashStyle": "Dash",
162147
"label": {
163148
"text": "Forecast Start",
164-
"style": {"fontSize": "18px", "color": INK_SOFT, "fontWeight": "normal"},
149+
"style": {"fontSize": "12px", "color": INK_SOFT, "fontWeight": "normal"},
165150
"rotation": 0,
166-
"y": -15,
151+
"y": -10,
167152
},
168153
"zIndex": 5,
169154
}
@@ -172,28 +157,28 @@
172157

173158
# Y-axis
174159
chart.options.y_axis = {
175-
"title": {"text": "Product Demand (Units)", "style": {"fontSize": "22px", "color": INK}, "margin": 25},
176-
"labels": {"style": {"fontSize": "18px", "color": INK_SOFT}},
160+
"title": {"text": "Product Demand (Units)", "style": {"fontSize": "14px", "color": INK}, "margin": 15},
161+
"labels": {"style": {"fontSize": "12px", "color": INK_SOFT}},
177162
"gridLineWidth": 1,
178163
"gridLineColor": GRID,
179164
"lineColor": INK_SOFT,
180165
"tickColor": INK_SOFT,
181166
}
182167

183-
# Legend with larger symbols
168+
# Legend
184169
chart.options.legend = {
185170
"enabled": True,
186-
"itemStyle": {"fontSize": "18px", "color": INK_SOFT},
187-
"symbolWidth": 40,
188-
"symbolHeight": 24,
171+
"itemStyle": {"fontSize": "12px", "color": INK_SOFT},
172+
"symbolWidth": 24,
173+
"symbolHeight": 14,
189174
"symbolRadius": 3,
190-
"margin": 30,
175+
"margin": 20,
191176
}
192177

193178
# Tooltip
194179
chart.options.tooltip = {
195180
"shared": True,
196-
"style": {"fontSize": "16px", "color": INK},
181+
"style": {"fontSize": "12px", "color": INK},
197182
"xDateFormat": "%B %Y",
198183
"valueDecimals": 1,
199184
}
@@ -207,8 +192,8 @@
207192
ci_95_series.data = [
208193
{"x": forecast_timestamps[i], "low": float(lower_95[i]), "high": float(upper_95[i])} for i in range(n_forecast)
209194
]
210-
ci_95_series.color = "#56B4E9" if THEME == "light" else "#87CEEB"
211-
ci_95_series.fill_opacity = 0.15
195+
ci_95_series.color = "#56B4E9"
196+
ci_95_series.fill_opacity = 0.25
212197
ci_95_series.line_width = 0
213198
ci_95_series.marker = {"enabled": False}
214199
ci_95_series.z_index = 0
@@ -265,7 +250,7 @@
265250
<script>{highcharts_more_js}</script>
266251
</head>
267252
<body style="margin:0; background:{PAGE_BG};">
268-
<div id="container" style="width: 4800px; height: 2700px;"></div>
253+
<div id="container" style="width: 3200px; height: 1800px;"></div>
269254
<script>{html_str}</script>
270255
</body>
271256
</html>"""
@@ -284,7 +269,7 @@
284269
chrome_options.add_argument("--no-sandbox")
285270
chrome_options.add_argument("--disable-dev-shm-usage")
286271
chrome_options.add_argument("--disable-gpu")
287-
chrome_options.add_argument("--window-size=4800,2700")
272+
chrome_options.add_argument("--window-size=3200,1800")
288273

289274
driver = webdriver.Chrome(options=chrome_options)
290275
driver.get(f"file://{temp_path}")

0 commit comments

Comments
 (0)