|
1 | | -""" pyplots.ai |
| 1 | +""" anyplot.ai |
2 | 2 | bar-diverging: Diverging Bar Chart |
3 | | -Library: highcharts unknown | Python 3.13.11 |
4 | | -Quality: 92/100 | Created: 2025-12-25 |
| 3 | +Library: highcharts unknown | Python 3.13.13 |
| 4 | +Quality: 90/100 | Updated: 2026-05-08 |
5 | 5 | """ |
6 | 6 |
|
| 7 | +import os |
7 | 8 | import tempfile |
8 | 9 | import time |
9 | 10 | import urllib.request |
|
14 | 15 | from highcharts_core.options.series.bar import BarSeries |
15 | 16 | from selenium import webdriver |
16 | 17 | from selenium.webdriver.chrome.options import Options |
17 | | -from selenium.webdriver.common.by import By |
18 | 18 |
|
19 | 19 |
|
| 20 | +# Theme-adaptive chrome tokens |
| 21 | +THEME = os.getenv("ANYPLOT_THEME", "light") |
| 22 | +PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17" |
| 23 | +ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420" |
| 24 | +INK = "#1A1A17" if THEME == "light" else "#F0EFE8" |
| 25 | +INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" |
| 26 | +GRID = "rgba(26,26,23,0.10)" if THEME == "light" else "rgba(240,239,232,0.10)" |
| 27 | + |
| 28 | +# Okabe-Ito palette |
| 29 | +POSITIVE = "#009E73" # bluish green (brand, position 1) |
| 30 | +NEGATIVE = "#D55E00" # vermillion (position 2) |
| 31 | + |
20 | 32 | # Data - Customer satisfaction survey results by department |
21 | 33 | categories = [ |
22 | 34 | "Customer Service", |
|
35 | 47 | values = [45, 32, 28, 15, 8, -5, -12, -18, -25, -38] |
36 | 48 |
|
37 | 49 | # Separate positive and negative for different colors |
38 | | -positive_data = [{"y": v, "color": "#306998"} if v >= 0 else {"y": v, "color": "#FFD43B"} for v in values] |
| 50 | +positive_data = [{"y": v, "color": POSITIVE} if v >= 0 else {"y": v, "color": NEGATIVE} for v in values] |
39 | 51 |
|
40 | 52 | # Create chart |
41 | 53 | chart = Chart(container="container") |
|
46 | 58 | "type": "bar", |
47 | 59 | "width": 4800, |
48 | 60 | "height": 2700, |
49 | | - "backgroundColor": "#ffffff", |
| 61 | + "backgroundColor": PAGE_BG, |
50 | 62 | "marginLeft": 280, |
51 | 63 | "marginRight": 100, |
52 | 64 | } |
53 | 65 |
|
54 | 66 | # Title |
55 | | -chart.options.title = { |
56 | | - "text": "bar-diverging · highcharts · pyplots.ai", |
57 | | - "style": {"fontSize": "48px", "fontWeight": "bold"}, |
58 | | -} |
59 | | - |
60 | | -chart.options.subtitle = {"text": "Department Net Satisfaction Scores", "style": {"fontSize": "32px"}} |
| 67 | +chart.options.title = {"text": "bar-diverging · highcharts · anyplot.ai", "style": {"fontSize": "28px", "color": INK}} |
61 | 68 |
|
62 | 69 | # X-axis (categories) |
63 | 70 | chart.options.x_axis = { |
64 | 71 | "categories": categories, |
65 | 72 | "title": {"text": None}, |
66 | | - "labels": {"style": {"fontSize": "28px"}}, |
67 | | - "lineWidth": 0, |
68 | | - "tickWidth": 0, |
| 73 | + "labels": {"style": {"fontSize": "18px", "color": INK_SOFT}}, |
| 74 | + "lineColor": INK_SOFT, |
| 75 | + "tickColor": INK_SOFT, |
| 76 | + "gridLineColor": GRID, |
69 | 77 | } |
70 | 78 |
|
71 | 79 | # Y-axis (values) - note: in horizontal bar, y_axis shows values |
72 | 80 | chart.options.y_axis = { |
73 | | - "title": {"text": "Net Satisfaction Score", "style": {"fontSize": "28px"}, "margin": 20}, |
74 | | - "labels": {"style": {"fontSize": "24px"}}, |
| 81 | + "title": {"text": "Net Satisfaction Score", "style": {"fontSize": "22px", "color": INK}}, |
| 82 | + "labels": {"style": {"fontSize": "18px", "color": INK_SOFT}}, |
| 83 | + "lineColor": INK_SOFT, |
| 84 | + "tickColor": INK_SOFT, |
| 85 | + "gridLineColor": GRID, |
75 | 86 | "gridLineWidth": 1, |
76 | | - "gridLineColor": "#e0e0e0", |
77 | | - "plotLines": [{"value": 0, "width": 3, "color": "#333333", "zIndex": 5}], |
| 87 | + "plotLines": [{"value": 0, "width": 2, "color": INK_SOFT, "zIndex": 5, "dashStyle": "Solid"}], |
78 | 88 | "min": -50, |
79 | 89 | "max": 60, |
80 | 90 | } |
|
89 | 99 | series = BarSeries() |
90 | 100 | series.name = "Net Satisfaction" |
91 | 101 | series.data = positive_data |
92 | | -series.data_labels = {"enabled": True, "style": {"fontSize": "22px", "fontWeight": "bold"}, "format": "{y}"} |
| 102 | +series.data_labels = {"enabled": True, "style": {"fontSize": "18px", "color": INK}, "format": "{y}"} |
93 | 103 | series.border_width = 0 |
94 | | -series.point_width = 60 |
| 104 | +series.point_width = 80 |
95 | 105 |
|
96 | 106 | chart.add_series(series) |
97 | 107 |
|
98 | 108 | # Plot options |
99 | 109 | chart.options.plot_options = {"bar": {"groupPadding": 0.1, "pointPadding": 0.05}} |
100 | 110 |
|
101 | | -# Download Highcharts JS |
102 | | -highcharts_url = "https://code.highcharts.com/highcharts.js" |
103 | | -with urllib.request.urlopen(highcharts_url, timeout=30) as response: |
104 | | - highcharts_js = response.read().decode("utf-8") |
| 111 | + |
| 112 | +# Download Highcharts JS (with fallback CDN) |
| 113 | +def fetch_js(urls): |
| 114 | + for url in urls: |
| 115 | + try: |
| 116 | + req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"}) |
| 117 | + with urllib.request.urlopen(req, timeout=30) as resp: |
| 118 | + return resp.read().decode("utf-8") |
| 119 | + except Exception: |
| 120 | + continue |
| 121 | + raise RuntimeError(f"Failed to download JS from: {urls}") |
| 122 | + |
| 123 | + |
| 124 | +hc_urls = ["https://code.highcharts.com/highcharts.js", "https://cdn.jsdelivr.net/npm/highcharts@11/highcharts.js"] |
| 125 | +highcharts_js = fetch_js(hc_urls) |
105 | 126 |
|
106 | 127 | # Generate HTML with inline scripts |
107 | 128 | html_str = chart.to_js_literal() |
|
111 | 132 | <meta charset="utf-8"> |
112 | 133 | <script>{highcharts_js}</script> |
113 | 134 | </head> |
114 | | -<body style="margin:0; padding:0; background:#ffffff;"> |
| 135 | +<body style="margin:0; padding:0; background:{PAGE_BG};"> |
115 | 136 | <div id="container" style="width: 4800px; height: 2700px;"></div> |
116 | 137 | <script>{html_str}</script> |
117 | 138 | </body> |
118 | 139 | </html>""" |
119 | 140 |
|
120 | 141 | # Save HTML |
121 | | -with open("plot.html", "w", encoding="utf-8") as f: |
| 142 | +with open(f"plot-{THEME}.html", "w", encoding="utf-8") as f: |
122 | 143 | f.write(html_content) |
123 | 144 |
|
124 | 145 | # Screenshot with Selenium |
|
131 | 152 | chrome_options.add_argument("--no-sandbox") |
132 | 153 | chrome_options.add_argument("--disable-dev-shm-usage") |
133 | 154 | chrome_options.add_argument("--disable-gpu") |
134 | | -chrome_options.add_argument("--window-size=4800,2800") |
| 155 | +chrome_options.add_argument("--window-size=4800,2700") |
135 | 156 | chrome_options.add_argument("--force-device-scale-factor=1") |
136 | 157 |
|
137 | 158 | driver = webdriver.Chrome(options=chrome_options) |
138 | 159 | driver.get(f"file://{temp_path}") |
139 | 160 | time.sleep(5) |
140 | 161 |
|
141 | | -# Get the container element and screenshot it at exact dimensions |
142 | | -container = driver.find_element(By.ID, "container") |
143 | | -container.screenshot("plot.png") |
| 162 | +driver.save_screenshot(f"plot-{THEME}.png") |
144 | 163 | driver.quit() |
145 | 164 |
|
146 | 165 | Path(temp_path).unlink() |
0 commit comments