Skip to content

Commit 0993d7e

Browse files
Merge branch 'main' into implementation/line-timeseries/letsplot
2 parents c7311bb + 29b0138 commit 0993d7e

4 files changed

Lines changed: 411 additions & 323 deletions

File tree

plots/line-timeseries/implementations/python/highcharts.py

Lines changed: 70 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
""" pyplots.ai
1+
""" anyplot.ai
22
line-timeseries: Time Series Line Plot
3-
Library: highcharts unknown | Python 3.13.11
4-
Quality: 92/100 | Created: 2025-12-26
3+
Library: highcharts unknown | Python 3.13.13
4+
Quality: 90/100 | Updated: 2026-05-09
55
"""
66

77
import math
8+
import os
89
import random
910
import tempfile
1011
import time
@@ -19,6 +20,19 @@
1920
from selenium.webdriver.chrome.options import Options
2021

2122

23+
# Read theme from environment
24+
THEME = os.getenv("ANYPLOT_THEME", "light")
25+
26+
# Theme-adaptive color palette
27+
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
28+
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
29+
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
30+
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
31+
GRID = "rgba(26,26,23,0.10)" if THEME == "light" else "rgba(240,239,232,0.10)"
32+
33+
# Okabe-Ito palette (first series must be #009E73)
34+
BRAND = "#009E73"
35+
2236
# Data - Daily temperature readings over one year (365 days)
2337
random.seed(42)
2438
start_date = datetime(2024, 1, 1)
@@ -45,96 +59,120 @@
4559
chart = Chart(container="container")
4660
chart.options = HighchartsOptions()
4761

48-
# Chart configuration
62+
# Chart configuration - theme-adaptive background
4963
chart.options.chart = {
5064
"type": "line",
5165
"width": 4800,
5266
"height": 2700,
53-
"backgroundColor": "#ffffff",
67+
"backgroundColor": PAGE_BG,
5468
"spacingTop": 60,
5569
"spacingBottom": 100,
5670
"spacingLeft": 80,
5771
"spacingRight": 80,
5872
}
5973

60-
# Title
74+
# Title - theme-adaptive color
6175
chart.options.title = {
6276
"text": "line-timeseries · highcharts · pyplots.ai",
63-
"style": {"fontSize": "56px", "fontWeight": "bold"},
77+
"style": {"fontSize": "56px", "fontWeight": "bold", "color": INK},
6478
"margin": 40,
6579
}
6680

67-
chart.options.subtitle = {
68-
"text": "Daily Temperature Readings - 2024",
69-
"style": {"fontSize": "36px", "color": "#666666"},
70-
}
81+
# Subtitle - theme-adaptive color
82+
chart.options.subtitle = {"text": "Daily Temperature Readings - 2024", "style": {"fontSize": "36px", "color": INK_SOFT}}
7183

7284
# X-axis (datetime) - with monthly tick intervals to prevent label overlap
7385
chart.options.x_axis = {
7486
"type": "datetime",
75-
"title": {"text": "Date", "style": {"fontSize": "36px"}, "margin": 25},
76-
"labels": {"style": {"fontSize": "28px"}},
87+
"title": {"text": "Date", "style": {"fontSize": "36px", "color": INK}, "margin": 25},
88+
"labels": {"style": {"fontSize": "28px", "color": INK_SOFT}},
7789
"gridLineWidth": 1,
78-
"gridLineColor": "rgba(0, 0, 0, 0.1)",
90+
"gridLineColor": GRID,
7991
"tickInterval": 30 * 24 * 3600 * 1000, # Monthly ticks (30 days in ms)
8092
"dateTimeLabelFormats": {"month": "%b %Y"},
93+
"lineColor": INK_SOFT,
94+
"tickColor": INK_SOFT,
8195
}
8296

83-
# Y-axis
97+
# Y-axis - theme-adaptive colors
8498
chart.options.y_axis = {
85-
"title": {"text": "Temperature (°C)", "style": {"fontSize": "36px"}, "margin": 25},
86-
"labels": {"style": {"fontSize": "28px"}},
99+
"title": {"text": "Temperature (°C)", "style": {"fontSize": "36px", "color": INK}, "margin": 25},
100+
"labels": {"style": {"fontSize": "28px", "color": INK_SOFT}},
87101
"gridLineWidth": 1,
88-
"gridLineColor": "rgba(0, 0, 0, 0.1)",
102+
"gridLineColor": GRID,
103+
"lineColor": INK_SOFT,
104+
"tickColor": INK_SOFT,
89105
}
90106

91-
# Legend
92-
chart.options.legend = {"enabled": True, "itemStyle": {"fontSize": "32px"}, "margin": 30}
107+
# Legend - theme-adaptive styling with improved visibility
108+
chart.options.legend = {
109+
"enabled": True,
110+
"itemStyle": {"fontSize": "32px", "color": INK_SOFT},
111+
"backgroundColor": ELEVATED_BG,
112+
"borderColor": INK_SOFT,
113+
"borderWidth": 1,
114+
"margin": 30,
115+
}
93116

94-
# Tooltip
95-
chart.options.tooltip = {"xDateFormat": "%A, %b %d, %Y", "valueSuffix": " °C", "style": {"fontSize": "28px"}}
117+
# Tooltip - theme-adaptive styling
118+
chart.options.tooltip = {
119+
"xDateFormat": "%A, %b %d, %Y",
120+
"valueSuffix": " °C",
121+
"style": {"fontSize": "28px", "color": INK},
122+
"backgroundColor": ELEVATED_BG,
123+
"borderColor": INK_SOFT,
124+
"borderRadius": 4,
125+
}
96126

97127
# Plot options
98128
chart.options.plot_options = {
99129
"line": {"lineWidth": 5, "marker": {"enabled": False}, "states": {"hover": {"lineWidth": 6}}}
100130
}
101131

102-
# Add series
132+
# Add series with Okabe-Ito brand color
103133
series = LineSeries()
104134
series.name = "Temperature"
105135
series.data = data_points
106-
series.color = "#306998" # Python Blue
136+
series.color = BRAND # #009E73 - Okabe-Ito position 1
107137

108138
chart.add_series(series)
109139

110-
# Export to PNG via Selenium
111-
highcharts_url = "https://code.highcharts.com/highcharts.js"
112-
with urllib.request.urlopen(highcharts_url, timeout=30) as response:
140+
# Download Highcharts JS (required for headless Chrome)
141+
highcharts_url = "https://unpkg.com/highcharts@11/highcharts.js"
142+
req = urllib.request.Request(
143+
highcharts_url, headers={"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36"}
144+
)
145+
with urllib.request.urlopen(req, timeout=30) as response:
113146
highcharts_js = response.read().decode("utf-8")
114147

148+
# Generate chart JS
115149
html_str = chart.to_js_literal()
150+
151+
# Create HTML content with proper theme-adaptive styling
116152
html_content = f"""<!DOCTYPE html>
117153
<html>
118154
<head>
119155
<meta charset="utf-8">
120156
<script>{highcharts_js}</script>
121157
</head>
122-
<body style="margin:0;">
158+
<body style="margin:0; background:{PAGE_BG};">
123159
<div id="container" style="width: 4800px; height: 2700px;"></div>
124160
<script>{html_str}</script>
125161
</body>
126162
</html>"""
127163

128-
# Save HTML version
129-
with open("plot.html", "w", encoding="utf-8") as f:
164+
# Save HTML version with theme suffix
165+
with open(f"plot-{THEME}.html", "w", encoding="utf-8") as f:
130166
cdn_html = (
131167
"""<!DOCTYPE html>
132168
<html>
133169
<head>
134170
<meta charset="utf-8">
135171
<script src="https://code.highcharts.com/highcharts.js"></script>
136172
</head>
137-
<body style="margin:0;">
173+
<body style="margin:0; background:"""
174+
+ PAGE_BG
175+
+ """;">
138176
<div id="container" style="width: 100%; height: 600px;"></div>
139177
<script>"""
140178
+ html_str
@@ -162,7 +200,7 @@
162200

163201
# Screenshot the chart container element for exact dimensions
164202
container = driver.find_element("id", "container")
165-
container.screenshot("plot.png")
203+
container.screenshot(f"plot-{THEME}.png")
166204
driver.quit()
167205

168206
Path(temp_path).unlink()
Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,33 @@
1-
""" pyplots.ai
1+
""" anyplot.ai
22
line-timeseries: Time Series Line Plot
3-
Library: pygal 3.1.0 | Python 3.13.11
4-
Quality: 91/100 | Created: 2025-12-26
3+
Library: pygal 3.1.0 | Python 3.13.13
4+
Quality: 85/100 | Updated: 2026-05-09
55
"""
66

7+
import os
78
import random
9+
import sys
810
from datetime import datetime, timedelta
911

10-
import pygal
11-
from pygal.style import Style
1212

13+
# Ensure site-packages is in path before current directory to avoid shadowing
14+
site_packages = next((p for p in sys.path if "site-packages" in p), None)
15+
if site_packages and sys.path[0] == os.path.dirname(__file__):
16+
sys.path.remove(sys.path[0])
17+
sys.path.insert(0, site_packages)
18+
19+
import pygal # noqa: E402
20+
from pygal.style import Style # noqa: E402
21+
22+
23+
# Theme tokens
24+
THEME = os.getenv("ANYPLOT_THEME", "light")
25+
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
26+
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
27+
INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F"
28+
29+
# Okabe-Ito palette
30+
OKABE_ITO = ("#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00", "#56B4E9", "#F0E442")
1331

1432
# Seed for reproducibility
1533
random.seed(42)
@@ -22,45 +40,42 @@
2240
price = 150.0
2341
prices = []
2442
for _ in range(365):
25-
# Add slight upward trend with random daily changes
2643
change = random.gauss(0.1, 2.5)
27-
price = max(100, price + change) # Prevent going too low
44+
price = max(100, price + change)
2845
prices.append(round(price, 2))
2946

30-
# Custom style for 4800x2700 canvas with larger fonts
47+
# Custom style for 4800x2700 canvas
3148
custom_style = Style(
32-
background="white",
33-
plot_background="white",
34-
foreground="#333333",
35-
foreground_strong="#333333",
36-
foreground_subtle="#666666",
37-
colors=("#306998",), # Python Blue
38-
title_font_size=72,
39-
label_font_size=48,
40-
major_label_font_size=42,
41-
legend_font_size=42,
42-
value_font_size=36,
43-
guide_stroke_color="#cccccc",
44-
guide_stroke_dasharray="2,4",
49+
background=PAGE_BG,
50+
plot_background=PAGE_BG,
51+
foreground=INK,
52+
foreground_strong=INK,
53+
foreground_subtle=INK_MUTED,
54+
colors=OKABE_ITO,
55+
title_font_size=28,
56+
label_font_size=22,
57+
major_label_font_size=18,
58+
legend_font_size=16,
59+
value_font_size=14,
60+
stroke_width=6,
4561
)
4662

4763
# Create line chart
4864
chart = pygal.Line(
4965
width=4800,
5066
height=2700,
5167
style=custom_style,
52-
title="line-timeseries · pygal · pyplots.ai",
68+
title="line-timeseries · pygal · anyplot.ai",
5369
x_title="Date",
5470
y_title="Stock Price (USD)",
55-
show_x_guides=False,
71+
show_x_guides=True,
5672
show_y_guides=True,
5773
x_label_rotation=45,
5874
show_legend=True,
5975
legend_at_bottom=True,
6076
truncate_legend=-1,
6177
show_dots=False,
62-
stroke_style={"width": 5},
63-
margin=60,
78+
margin=100,
6479
)
6580

6681
# Add data series
@@ -70,7 +85,7 @@
7085
x_labels = []
7186
x_labels_major = []
7287
for d in dates:
73-
if d.day == 1: # First of each month
88+
if d.day == 1:
7489
x_labels.append(d.strftime("%b %Y"))
7590
x_labels_major.append(d.strftime("%b %Y"))
7691
else:
@@ -80,5 +95,5 @@
8095
chart.x_labels_major = x_labels_major
8196

8297
# Save as PNG and HTML
83-
chart.render_to_file("plot.html")
84-
chart.render_to_png("plot.png")
98+
chart.render_to_file(f"plot-{THEME}.html")
99+
chart.render_to_png(f"plot-{THEME}.png")

0 commit comments

Comments
 (0)