From 64b16fa64fc6b81fb72807caaa089df84b5657bc Mon Sep 17 00:00:00 2001 From: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com> Date: Mon, 23 Feb 2026 14:50:27 +0100 Subject: [PATCH 1/4] =?UTF-8?q?update(band-basic):=20highcharts=20?= =?UTF-8?q?=E2=80=94=20comprehensive=20quality=20review?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Comprehensive quality review of highcharts band-basic implementation. --- .../band-basic/implementations/highcharts.py | 129 ++++++++++-------- plots/band-basic/metadata/highcharts.yaml | 10 +- plots/band-basic/specification.md | 2 +- plots/band-basic/specification.yaml | 6 +- 4 files changed, 82 insertions(+), 65 deletions(-) diff --git a/plots/band-basic/implementations/highcharts.py b/plots/band-basic/implementations/highcharts.py index b3e7a0a05f..8997588c37 100644 --- a/plots/band-basic/implementations/highcharts.py +++ b/plots/band-basic/implementations/highcharts.py @@ -1,7 +1,7 @@ -""" pyplots.ai +"""pyplots.ai band-basic: Basic Band Plot -Library: highcharts unknown | Python 3.13.11 -Quality: 92/100 | Created: 2025-12-23 +Library: highcharts 1.10.3 | Python 3.14 +Quality: /100 | Updated: 2026-02-23 """ import json @@ -16,22 +16,26 @@ from selenium.webdriver.chrome.options import Options -# Data - Time series with 95% confidence interval +# Data - Daily temperature forecast with 95% prediction interval np.random.seed(42) -x = np.linspace(0, 10, 50) -# Central trend with sinusoidal pattern -y_center = 50 + 20 * np.sin(x) + x * 2 -# Uncertainty increases with x (heteroscedastic) -uncertainty = 3 + 0.5 * x -# Upper and lower bounds -y_lower = y_center - 1.96 * uncertainty -y_upper = y_center + 1.96 * uncertainty +days = np.arange(1, 31) +# Central forecast: warming trend with daily variation +temp_center = 12 + 0.3 * days + 4 * np.sin(days * 0.4) +# Prediction uncertainty widens over the forecast horizon +uncertainty = 1.5 + 0.08 * days +temp_lower = temp_center - 1.96 * uncertainty +temp_upper = temp_center + 1.96 * uncertainty # Prepare data for Highcharts # arearange series expects [[x, low, high], ...] -band_data = [[float(xi), float(lo), float(hi)] for xi, lo, hi in zip(x, y_lower, y_upper, strict=True)] +band_data = [ + [int(d), round(float(lo), 1), round(float(hi), 1)] for d, lo, hi in zip(days, temp_lower, temp_upper, strict=True) +] # line series expects [[x, y], ...] -line_data = [[float(xi), float(yi)] for xi, yi in zip(x, y_center, strict=True)] +line_data = [[int(d), round(float(t), 1)] for d, t in zip(days, temp_center, strict=True)] + +# Font stack +font_family = "'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif" # Chart options using arearange for band and line for center chart_options = { @@ -40,61 +44,75 @@ "height": 2700, "backgroundColor": "#ffffff", "marginBottom": 180, - "marginLeft": 200, + "marginLeft": 220, "marginRight": 100, - "style": {"fontFamily": "Arial, sans-serif"}, + "spacing": [40, 40, 40, 40], + "style": {"fontFamily": font_family}, + }, + "title": { + "text": "30-Day Temperature Forecast \u00b7 band-basic \u00b7 highcharts \u00b7 pyplots.ai", + "style": {"fontSize": "48px", "fontWeight": "bold", "fontFamily": font_family}, + }, + "subtitle": { + "text": "Daily forecast with 95% prediction interval", + "style": {"fontSize": "30px", "color": "#555555", "fontFamily": font_family}, }, - "title": {"text": "band-basic · highcharts · pyplots.ai", "style": {"fontSize": "64px", "fontWeight": "bold"}}, - "subtitle": {"text": "Time series with 95% confidence interval", "style": {"fontSize": "38px", "color": "#666666"}}, "xAxis": { - "title": {"text": "Time", "style": {"fontSize": "48px"}, "margin": 20}, - "labels": {"style": {"fontSize": "36px"}}, - "gridLineWidth": 1, - "gridLineColor": "rgba(0, 0, 0, 0.1)", - "gridLineDashStyle": "Dash", - "tickInterval": 1, + "title": {"text": "Forecast Day", "style": {"fontSize": "36px", "fontFamily": font_family}, "margin": 20}, + "labels": {"style": {"fontSize": "28px", "fontFamily": font_family}}, + "gridLineWidth": 0, + "tickInterval": 5, + "lineColor": "#cccccc", + "tickColor": "#cccccc", }, "yAxis": { - "title": {"text": "Value", "style": {"fontSize": "48px"}, "margin": 20}, - "labels": {"style": {"fontSize": "36px"}}, + "title": { + "text": "Temperature (\u00b0C)", + "style": {"fontSize": "36px", "fontFamily": font_family}, + "margin": 20, + }, + "labels": {"format": "{value}\u00b0", "style": {"fontSize": "28px", "fontFamily": font_family}}, "gridLineWidth": 1, - "gridLineColor": "rgba(0, 0, 0, 0.1)", - "gridLineDashStyle": "Dash", + "gridLineColor": "rgba(0, 0, 0, 0.08)", + "gridLineDashStyle": "Dot", + "lineColor": "#cccccc", + "lineWidth": 1, }, "legend": { "enabled": True, "align": "right", "verticalAlign": "top", "layout": "vertical", - "x": -50, - "y": 100, - "itemStyle": {"fontSize": "36px"}, + "x": -40, + "y": 80, + "itemStyle": {"fontSize": "28px", "fontFamily": font_family}, }, "plotOptions": { - "arearange": {"fillOpacity": 0.3, "lineWidth": 0, "marker": {"enabled": False}}, - "line": {"lineWidth": 6, "marker": {"enabled": False}}, + "arearange": {"fillOpacity": 0.25, "lineWidth": 0, "marker": {"enabled": False}}, + "line": {"lineWidth": 5, "marker": {"enabled": False}}, }, "series": [ { - "name": "95% Confidence Interval", + "name": "95% Prediction Interval", "type": "arearange", "data": band_data, "color": "#306998", - "fillOpacity": 0.3, + "fillOpacity": 0.25, "zIndex": 0, }, - {"name": "Mean Value", "type": "line", "data": line_data, "color": "#FFD43B", "lineWidth": 6, "zIndex": 1}, + {"name": "Forecast", "type": "line", "data": line_data, "color": "#FFD43B", "lineWidth": 5, "zIndex": 1}, ], + "credits": {"enabled": False}, } -# Download Highcharts JS and highcharts-more (needed for arearange) -highcharts_url = "https://code.highcharts.com/highcharts.js" -highcharts_more_url = "https://code.highcharts.com/highcharts-more.js" - -with urllib.request.urlopen(highcharts_url, timeout=30) as response: - highcharts_js = response.read().decode("utf-8") -with urllib.request.urlopen(highcharts_more_url, timeout=30) as response: - highcharts_more_js = response.read().decode("utf-8") +# Download Highcharts JS files (jsDelivr CDN with version pin) +cdn_base = "https://cdn.jsdelivr.net/npm/highcharts@11.4" +js_urls = {"highcharts": f"{cdn_base}/highcharts.js", "highcharts_more": f"{cdn_base}/highcharts-more.js"} +js_modules = {} +for name, url in js_urls.items(): + req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"}) + with urllib.request.urlopen(req, timeout=30) as response: + js_modules[name] = response.read().decode("utf-8") # Generate HTML with inline scripts chart_options_json = json.dumps(chart_options) @@ -102,8 +120,8 @@ - - + +
@@ -120,33 +138,28 @@ f.write(html_content) temp_path = f.name -# Also save the HTML for interactive viewing +# Save HTML for interactive viewing with open("plot.html", "w", encoding="utf-8") as f: f.write(html_content) # Take screenshot with headless Chrome chrome_options = Options() -chrome_options.add_argument("--headless=new") +chrome_options.add_argument("--headless") chrome_options.add_argument("--no-sandbox") chrome_options.add_argument("--disable-dev-shm-usage") chrome_options.add_argument("--disable-gpu") -chrome_options.add_argument("--force-device-scale-factor=1") +chrome_options.add_argument("--window-size=4800,2900") driver = webdriver.Chrome(options=chrome_options) -driver.set_window_size(4900, 2900) driver.get(f"file://{temp_path}") time.sleep(5) - -# Take screenshot driver.save_screenshot("plot_raw.png") driver.quit() -# Crop/resize to exact 4800x2700 using PIL +# Crop to exact 4800x2700 dimensions img = Image.open("plot_raw.png") -final_img = Image.new("RGB", (4800, 2700), (255, 255, 255)) -final_img.paste(img.crop((0, 0, min(img.width, 4800), min(img.height, 2700))), (0, 0)) -final_img.save("plot.png") - -# Clean up +img_cropped = img.crop((0, 0, 4800, 2700)) +img_cropped.save("plot.png") Path("plot_raw.png").unlink() + Path(temp_path).unlink() diff --git a/plots/band-basic/metadata/highcharts.yaml b/plots/band-basic/metadata/highcharts.yaml index 39f2d5145e..8e99d82cab 100644 --- a/plots/band-basic/metadata/highcharts.yaml +++ b/plots/band-basic/metadata/highcharts.yaml @@ -1,16 +1,16 @@ library: highcharts specification_id: band-basic created: '2025-12-23T09:09:27Z' -updated: '2025-12-23T09:11:45Z' -generated_by: claude-opus-4-5-20251101 +updated: '2026-02-23T13:41:00Z' +generated_by: claude-opus-4-6 workflow_run: 20456388074 issue: 0 -python_version: 3.13.11 -library_version: unknown +python_version: '3.14' +library_version: 1.10.3 preview_url: https://storage.googleapis.com/pyplots-images/plots/band-basic/highcharts/plot.png preview_thumb: https://storage.googleapis.com/pyplots-images/plots/band-basic/highcharts/plot_thumb.png preview_html: https://storage.googleapis.com/pyplots-images/plots/band-basic/highcharts/plot.html -quality_score: 92 +quality_score: null impl_tags: dependencies: - pillow diff --git a/plots/band-basic/specification.md b/plots/band-basic/specification.md index 4614c2a26c..a18f9caee9 100644 --- a/plots/band-basic/specification.md +++ b/plots/band-basic/specification.md @@ -16,7 +16,7 @@ A band plot displays a filled region between two boundary lines, commonly used t - `x` (numeric) - Independent variable, often representing time or sequence - `y_lower` (numeric) - Lower boundary values defining the bottom of the band - `y_upper` (numeric) - Upper boundary values defining the top of the band -- `y_center` (numeric, optional) - Central trend line values (mean/median) +- `y_center` (numeric) - Central trend line values (mean/median), shown as a contrasting line - Size: 20-200 data points - Example: Time series with 95% confidence interval bounds diff --git a/plots/band-basic/specification.yaml b/plots/band-basic/specification.yaml index c239433ec4..76a86a91e9 100644 --- a/plots/band-basic/specification.yaml +++ b/plots/band-basic/specification.yaml @@ -6,7 +6,7 @@ title: Basic Band Plot # Specification tracking created: 2025-12-15T20:42:54Z -updated: 2025-12-15T20:42:54Z +updated: 2026-02-23T12:00:00Z issue: 979 suggested: MarkusNeusinger @@ -18,10 +18,14 @@ tags: data_type: - numeric - continuous + - timeseries domain: - statistics - science + - general + - engineering features: - basic - confidence-interval - uncertainty + - 2d From 2330c514541c9ae3e57b7ad37e19c828d9fe1a18 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 23 Feb 2026 13:55:45 +0000 Subject: [PATCH 2/4] chore(highcharts): update quality score 88 and review feedback for band-basic --- .../band-basic/implementations/highcharts.py | 4 +- plots/band-basic/metadata/highcharts.yaml | 244 +++++++++--------- 2 files changed, 129 insertions(+), 119 deletions(-) diff --git a/plots/band-basic/implementations/highcharts.py b/plots/band-basic/implementations/highcharts.py index 8997588c37..1c1bbf3d1c 100644 --- a/plots/band-basic/implementations/highcharts.py +++ b/plots/band-basic/implementations/highcharts.py @@ -1,7 +1,7 @@ -"""pyplots.ai +""" pyplots.ai band-basic: Basic Band Plot Library: highcharts 1.10.3 | Python 3.14 -Quality: /100 | Updated: 2026-02-23 +Quality: 88/100 | Updated: 2026-02-23 """ import json diff --git a/plots/band-basic/metadata/highcharts.yaml b/plots/band-basic/metadata/highcharts.yaml index 8e99d82cab..039b19c375 100644 --- a/plots/band-basic/metadata/highcharts.yaml +++ b/plots/band-basic/metadata/highcharts.yaml @@ -1,7 +1,7 @@ library: highcharts specification_id: band-basic created: '2025-12-23T09:09:27Z' -updated: '2026-02-23T13:41:00Z' +updated: '2026-02-23T13:55:45Z' generated_by: claude-opus-4-6 workflow_run: 20456388074 issue: 0 @@ -10,11 +10,11 @@ library_version: 1.10.3 preview_url: https://storage.googleapis.com/pyplots-images/plots/band-basic/highcharts/plot.png preview_thumb: https://storage.googleapis.com/pyplots-images/plots/band-basic/highcharts/plot_thumb.png preview_html: https://storage.googleapis.com/pyplots-images/plots/band-basic/highcharts/plot.html -quality_score: null +quality_score: 88 impl_tags: dependencies: - - pillow - selenium + - pillow techniques: - html-export patterns: @@ -22,151 +22,155 @@ impl_tags: dataprep: [] styling: - alpha-blending + - grid-styling review: strengths: - - Excellent use of Highcharts arearange series for band visualization with proper - upper/lower bounds - - Very clear visual hierarchy with contrasting band (blue, 0.3 alpha) and center - line (yellow, solid) - - Heteroscedastic uncertainty pattern demonstrates the value of band plots effectively - - Proper zIndex layering ensures center line renders on top of band - - Large, readable text sizes throughout (64px title, 48px axis labels, 36px tick - labels) - - Clean Selenium-based PNG export with proper image cropping + - Excellent use of Highcharts arearange type for band visualization + - Widening prediction interval data creates genuine data storytelling about growing + forecast uncertainty + - All font sizes explicitly set and clearly readable at target resolution + - Perfect spec compliance with all required features implemented correctly + - Clean well-structured code with deterministic seed and sensible data generation weaknesses: - - Axis labels lack units (e.g., "Time (s)" or "Value (units)" would be more informative) - - Uses raw dictionary configuration instead of highcharts-core Python library classes - as shown in library rules - - Legend could be slightly larger for the 4800x2700 canvas size - image_description: 'The plot displays a band chart with a light blue semi-transparent - area representing the 95% confidence interval, and a golden-yellow center line - showing the mean value. The visualization follows a sinusoidal pattern over time - (x-axis: 0-10), with values ranging approximately from 24 to 104 on the y-axis. - The uncertainty band visibly widens as time increases, demonstrating heteroscedastic - behavior. The title "band-basic · highcharts · pyplots.ai" appears at the top - in bold, with a subtitle "Time series with 95% confidence interval". A legend - in the top-right corner identifies both the confidence interval band and mean - value line. The background is clean white with subtle dashed grid lines.' + - Yellow forecast line (#FFD43B) is too bright and saturated for refined color harmony + - Raw JSON dict approach bypasses highcharts-core Python wrapper reducing idiomatic + library usage + - Could push visual refinement further with softer axis lines and tighter legend + placement + image_description: The plot displays a 30-day temperature forecast band chart on + a white background. A light blue semi-transparent band (arearange) represents + the 95% prediction interval, clearly widening from left to right as forecast uncertainty + increases over the horizon. A bright yellow line (#FFD43B) shows the central forecast + trend, exhibiting a sinusoidal warming pattern ranging roughly from 12°C to 22°C. + The title reads "30-Day Temperature Forecast · band-basic · highcharts · pyplots.ai" + in bold, with a gray subtitle "Daily forecast with 95% prediction interval". The + x-axis is labeled "Forecast Day" (ticks at 5, 10, 15, 20, 25, 30) and the y-axis + "Temperature (°C)" with degree symbols from 5° to 30°. A vertical legend in the + upper right shows "95% Prediction Interval" (blue) and "Forecast" (yellow). The + y-axis has subtle dotted grid lines; x-axis grid is removed. Overall layout is + clean with generous margins. criteria_checklist: visual_quality: - score: 37 - max: 40 + score: 29 + max: 30 items: - id: VQ-01 name: Text Legibility - score: 10 - max: 10 + score: 8 + max: 8 passed: true - comment: Title at 64px, axis labels at 48px, tick labels at 36px - all perfectly - readable at 4800x2700 + comment: 'All font sizes explicitly set: title 48px, subtitle 30px, axis titles + 36px, labels 28px, legend 28px' - id: VQ-02 name: No Overlap - score: 8 - max: 8 + score: 6 + max: 6 passed: true - comment: No overlapping text elements anywhere + comment: No overlapping elements anywhere - id: VQ-03 name: Element Visibility - score: 8 - max: 8 + score: 5 + max: 6 passed: true - comment: Band and line clearly visible with appropriate opacity (0.3 for band), - line width of 6 + comment: Band visible at 0.25 opacity, yellow line has moderate contrast against + light background - id: VQ-04 name: Color Accessibility - score: 5 - max: 5 + score: 4 + max: 4 passed: true - comment: Blue (#306998) and yellow (#FFD43B) are colorblind-safe and high - contrast + comment: Blue-yellow scheme is colorblind-safe - id: VQ-05 - name: Layout Balance + name: Layout & Canvas score: 4 - max: 5 + max: 4 passed: true - comment: Good proportions with appropriate margins, slight excess whitespace - on right side + comment: Well-balanced margins, plot fills ~65% of canvas - id: VQ-06 - name: Axis Labels - score: 1 + name: Axis Labels & Title + score: 2 max: 2 passed: true - comment: Labels are descriptive ("Time", "Value") but lack units - - id: VQ-07 - name: Grid & Legend - score: 1 - max: 2 + comment: Temperature (°C) with units, Forecast Day descriptive + design_excellence: + score: 13 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 5 + max: 8 + passed: true + comment: Intentional palette and font stack, above defaults but bright yellow + is garish + - id: DE-02 + name: Visual Refinement + score: 4 + max: 6 + passed: true + comment: Dotted y-axis-only grid, removed x-axis grid, subtle axis colors, + markers disabled + - id: DE-03 + name: Data Storytelling + score: 4 + max: 6 passed: true - comment: Grid is subtle with dashed style and 0.1 alpha, legend well-placed - but could be larger + comment: Widening prediction interval tells clear uncertainty story with visual + hierarchy spec_compliance: - score: 25 - max: 25 + score: 15 + max: 15 items: - id: SC-01 name: Plot Type - score: 8 - max: 8 - passed: true - comment: Correct band/arearange chart type - - id: SC-02 - name: Data Mapping score: 5 max: 5 passed: true - comment: X mapped to time, Y to value with proper upper/lower bounds - - id: SC-03 + comment: Correct band plot using arearange + line series + - id: SC-02 name: Required Features - score: 5 - max: 5 + score: 4 + max: 4 passed: true - comment: Band with upper/lower bounds, center line, semi-transparent fill - (0.3 alpha) - - id: SC-04 - name: Data Range + comment: Semi-transparent fill, central line, smooth interpolation, uncertainty + source in subtitle + - id: SC-03 + name: Data Mapping score: 3 max: 3 passed: true - comment: All data visible, axes auto-scaled appropriately - - id: SC-05 - name: Legend Accuracy - score: 2 - max: 2 - passed: true - comment: Legend correctly labels "95% Confidence Interval" and "Mean Value" - - id: SC-06 - name: Title Format - score: 2 - max: 2 + comment: X=days, band boundaries correct, center line correct + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 passed: true - comment: 'Uses exact format: "band-basic · highcharts · pyplots.ai"' + comment: Title format correct with descriptive prefix, legend labels match + data data_quality: - score: 18 - max: 20 + score: 15 + max: 15 items: - id: DQ-01 name: Feature Coverage - score: 7 - max: 8 + score: 6 + max: 6 passed: true - comment: Shows sinusoidal trend with heteroscedastic uncertainty (widening - with x), demonstrates key band plot features + comment: Shows widening uncertainty, sinusoidal variation with warming trend - id: DQ-02 name: Realistic Context - score: 6 - max: 7 + score: 5 + max: 5 passed: true - comment: Time series with confidence interval is plausible; generic "Value" - label slightly reduces real-world applicability + comment: Temperature forecasting is real-world neutral scenario - id: DQ-03 name: Appropriate Scale - score: 5 - max: 5 + score: 4 + max: 4 passed: true - comment: Values in reasonable 24-104 range, 50 data points appropriate for - smooth band + comment: Temperatures 5-30°C realistic for 30-day forecast code_quality: - score: 9 + score: 10 max: 10 items: - id: CQ-01 @@ -174,40 +178,46 @@ review: score: 3 max: 3 passed: true - comment: 'Linear structure: imports → data → chart config → render → save' + comment: 'Linear flow: imports, data, chart options, HTML, screenshot' - id: CQ-02 name: Reproducibility - score: 3 - max: 3 + score: 2 + max: 2 passed: true - comment: Uses np.random.seed(42) + comment: np.random.seed(42) set - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: Only necessary imports used + comment: All imports used - id: CQ-04 - name: No Deprecated API - score: 0 - max: 1 + name: Code Elegance + score: 2 + max: 2 passed: true - comment: Using raw dict config instead of highcharts-core library classes + comment: Clean, well-organized, appropriate complexity - id: CQ-05 - name: Output Correct + name: Output & API score: 1 max: 1 passed: true - comment: Saves as plot.png - library_features: - score: 3 - max: 5 + comment: Saves plot.png, uses current Highcharts v11.4 + library_mastery: + score: 6 + max: 10 items: - - id: LF-01 - name: Uses distinctive library features + - id: LM-01 + name: Idiomatic Usage + score: 3 + max: 5 + passed: false + comment: Raw JSON dict approach instead of highcharts-core Python wrapper + - id: LM-02 + name: Distinctive Features score: 3 max: 5 passed: true - comment: Uses arearange series type correctly, but doesn't use highcharts-core - Python library as recommended - verdict: APPROVED + comment: Uses Highcharts-specific arearange type with highcharts-more.js, + series zIndex + verdict: REJECTED From 1ceefc2e2048149ce2878287cbd956b68b34c6ca Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 23 Feb 2026 14:00:38 +0000 Subject: [PATCH 3/4] fix(highcharts): address review feedback for band-basic Attempt 1/3 - fixes based on AI review --- .../band-basic/implementations/highcharts.py | 183 ++++++++++-------- 1 file changed, 101 insertions(+), 82 deletions(-) diff --git a/plots/band-basic/implementations/highcharts.py b/plots/band-basic/implementations/highcharts.py index 1c1bbf3d1c..8da4c7a071 100644 --- a/plots/band-basic/implementations/highcharts.py +++ b/plots/band-basic/implementations/highcharts.py @@ -1,16 +1,18 @@ -""" pyplots.ai +"""pyplots.ai band-basic: Basic Band Plot Library: highcharts 1.10.3 | Python 3.14 Quality: 88/100 | Updated: 2026-02-23 """ -import json import tempfile import time import urllib.request from pathlib import Path import numpy as np +from highcharts_core.chart import Chart +from highcharts_core.options import HighchartsOptions +from highcharts_core.options.series.area import AreaRangeSeries, LineSeries from PIL import Image from selenium import webdriver from selenium.webdriver.chrome.options import Options @@ -19,93 +21,118 @@ # Data - Daily temperature forecast with 95% prediction interval np.random.seed(42) days = np.arange(1, 31) -# Central forecast: warming trend with daily variation temp_center = 12 + 0.3 * days + 4 * np.sin(days * 0.4) -# Prediction uncertainty widens over the forecast horizon uncertainty = 1.5 + 0.08 * days temp_lower = temp_center - 1.96 * uncertainty temp_upper = temp_center + 1.96 * uncertainty -# Prepare data for Highcharts -# arearange series expects [[x, low, high], ...] band_data = [ [int(d), round(float(lo), 1), round(float(hi), 1)] for d, lo, hi in zip(days, temp_lower, temp_upper, strict=True) ] -# line series expects [[x, y], ...] line_data = [[int(d), round(float(t), 1)] for d, t in zip(days, temp_center, strict=True)] -# Font stack +# Build chart using highcharts-core Python wrapper font_family = "'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif" -# Chart options using arearange for band and line for center -chart_options = { - "chart": { - "width": 4800, - "height": 2700, - "backgroundColor": "#ffffff", - "marginBottom": 180, - "marginLeft": 220, - "marginRight": 100, - "spacing": [40, 40, 40, 40], - "style": {"fontFamily": font_family}, - }, +chart = Chart(container="container") +chart.options = HighchartsOptions() + +chart.options.chart = { + "width": 4800, + "height": 2700, + "backgroundColor": "#ffffff", + "marginBottom": 180, + "marginLeft": 220, + "marginRight": 100, + "spacing": [40, 40, 40, 40], + "style": {"fontFamily": font_family}, +} + +chart.options.title = { + "text": "30-Day Temperature Forecast \u00b7 band-basic \u00b7 highcharts \u00b7 pyplots.ai", + "style": {"fontSize": "48px", "fontWeight": "bold", "fontFamily": font_family}, +} + +chart.options.subtitle = { + "text": "Daily forecast with 95% prediction interval", + "style": {"fontSize": "30px", "color": "#666666", "fontFamily": font_family}, +} + +chart.options.x_axis = { "title": { - "text": "30-Day Temperature Forecast \u00b7 band-basic \u00b7 highcharts \u00b7 pyplots.ai", - "style": {"fontSize": "48px", "fontWeight": "bold", "fontFamily": font_family}, - }, - "subtitle": { - "text": "Daily forecast with 95% prediction interval", - "style": {"fontSize": "30px", "color": "#555555", "fontFamily": font_family}, - }, - "xAxis": { - "title": {"text": "Forecast Day", "style": {"fontSize": "36px", "fontFamily": font_family}, "margin": 20}, - "labels": {"style": {"fontSize": "28px", "fontFamily": font_family}}, - "gridLineWidth": 0, - "tickInterval": 5, - "lineColor": "#cccccc", - "tickColor": "#cccccc", - }, - "yAxis": { - "title": { - "text": "Temperature (\u00b0C)", - "style": {"fontSize": "36px", "fontFamily": font_family}, - "margin": 20, - }, - "labels": {"format": "{value}\u00b0", "style": {"fontSize": "28px", "fontFamily": font_family}}, - "gridLineWidth": 1, - "gridLineColor": "rgba(0, 0, 0, 0.08)", - "gridLineDashStyle": "Dot", - "lineColor": "#cccccc", - "lineWidth": 1, - }, - "legend": { - "enabled": True, - "align": "right", - "verticalAlign": "top", - "layout": "vertical", - "x": -40, - "y": 80, - "itemStyle": {"fontSize": "28px", "fontFamily": font_family}, + "text": "Forecast Day", + "style": {"fontSize": "36px", "color": "#444444", "fontFamily": font_family}, + "margin": 20, }, - "plotOptions": { - "arearange": {"fillOpacity": 0.25, "lineWidth": 0, "marker": {"enabled": False}}, - "line": {"lineWidth": 5, "marker": {"enabled": False}}, + "labels": {"style": {"fontSize": "28px", "color": "#555555", "fontFamily": font_family}}, + "gridLineWidth": 0, + "tickInterval": 5, + "lineColor": "rgba(0, 0, 0, 0.12)", + "lineWidth": 1, + "tickColor": "rgba(0, 0, 0, 0.12)", + "tickLength": 8, +} + +chart.options.y_axis = { + "title": { + "text": "Temperature (\u00b0C)", + "style": {"fontSize": "36px", "color": "#444444", "fontFamily": font_family}, + "margin": 20, }, - "series": [ - { - "name": "95% Prediction Interval", - "type": "arearange", - "data": band_data, - "color": "#306998", - "fillOpacity": 0.25, - "zIndex": 0, - }, - {"name": "Forecast", "type": "line", "data": line_data, "color": "#FFD43B", "lineWidth": 5, "zIndex": 1}, - ], - "credits": {"enabled": False}, + "labels": {"format": "{value}\u00b0", "style": {"fontSize": "28px", "color": "#555555", "fontFamily": font_family}}, + "gridLineWidth": 1, + "gridLineColor": "rgba(0, 0, 0, 0.06)", + "gridLineDashStyle": "Dot", + "lineColor": "rgba(0, 0, 0, 0.12)", + "lineWidth": 1, +} + +chart.options.legend = { + "enabled": True, + "align": "right", + "verticalAlign": "top", + "layout": "vertical", + "x": -60, + "y": 60, + "floating": True, + "backgroundColor": "rgba(255, 255, 255, 0.85)", + "borderWidth": 0, + "shadow": False, + "itemStyle": {"fontSize": "28px", "fontWeight": "normal", "fontFamily": font_family}, + "itemMarginBottom": 8, + "symbolRadius": 4, +} + +chart.options.plot_options = { + "arearange": {"fillOpacity": 0.25, "lineWidth": 0, "marker": {"enabled": False}}, + "line": {"lineWidth": 5, "marker": {"enabled": False}}, } -# Download Highcharts JS files (jsDelivr CDN with version pin) +chart.options.credits = {"enabled": False} + +# Band series using AreaRangeSeries +band = AreaRangeSeries() +band.data = band_data +band.name = "95% Prediction Interval" +band.color = "#306998" +band.fill_opacity = 0.25 +band.z_index = 0 + +# Forecast line using LineSeries with refined deep amber color +forecast = LineSeries() +forecast.data = line_data +forecast.name = "Forecast" +forecast.color = "#C49000" +forecast.line_width = 5 +forecast.z_index = 1 + +chart.add_series(band) +chart.add_series(forecast) + +# Generate JS via highcharts-core wrapper +chart_js = chart.to_js_literal() + +# Download Highcharts JS files for inline embedding (headless Chrome cannot load CDN) cdn_base = "https://cdn.jsdelivr.net/npm/highcharts@11.4" js_urls = {"highcharts": f"{cdn_base}/highcharts.js", "highcharts_more": f"{cdn_base}/highcharts-more.js"} js_modules = {} @@ -114,8 +141,7 @@ with urllib.request.urlopen(req, timeout=30) as response: js_modules[name] = response.read().decode("utf-8") -# Generate HTML with inline scripts -chart_options_json = json.dumps(chart_options) +# Build HTML with inline Highcharts JS and chart literal from wrapper html_content = f""" @@ -125,20 +151,14 @@
- + """ -# Write temp HTML file with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f: f.write(html_content) temp_path = f.name -# Save HTML for interactive viewing with open("plot.html", "w", encoding="utf-8") as f: f.write(html_content) @@ -156,7 +176,6 @@ driver.save_screenshot("plot_raw.png") driver.quit() -# Crop to exact 4800x2700 dimensions img = Image.open("plot_raw.png") img_cropped = img.crop((0, 0, 4800, 2700)) img_cropped.save("plot.png") From f0e5ac44a7d239b366482eaf7596a84b728c7af3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 23 Feb 2026 14:07:13 +0000 Subject: [PATCH 4/4] chore(highcharts): update quality score 91 and review feedback for band-basic --- .../band-basic/implementations/highcharts.py | 4 +- plots/band-basic/metadata/highcharts.yaml | 142 ++++++++++-------- 2 files changed, 81 insertions(+), 65 deletions(-) diff --git a/plots/band-basic/implementations/highcharts.py b/plots/band-basic/implementations/highcharts.py index 8da4c7a071..7868c80216 100644 --- a/plots/band-basic/implementations/highcharts.py +++ b/plots/band-basic/implementations/highcharts.py @@ -1,7 +1,7 @@ -"""pyplots.ai +""" pyplots.ai band-basic: Basic Band Plot Library: highcharts 1.10.3 | Python 3.14 -Quality: 88/100 | Updated: 2026-02-23 +Quality: 91/100 | Updated: 2026-02-23 """ import tempfile diff --git a/plots/band-basic/metadata/highcharts.yaml b/plots/band-basic/metadata/highcharts.yaml index 039b19c375..744becb772 100644 --- a/plots/band-basic/metadata/highcharts.yaml +++ b/plots/band-basic/metadata/highcharts.yaml @@ -1,7 +1,7 @@ library: highcharts specification_id: band-basic created: '2025-12-23T09:09:27Z' -updated: '2026-02-23T13:55:45Z' +updated: '2026-02-23T14:07:12Z' generated_by: claude-opus-4-6 workflow_run: 20456388074 issue: 0 @@ -10,7 +10,7 @@ library_version: 1.10.3 preview_url: https://storage.googleapis.com/pyplots-images/plots/band-basic/highcharts/plot.png preview_thumb: https://storage.googleapis.com/pyplots-images/plots/band-basic/highcharts/plot_thumb.png preview_html: https://storage.googleapis.com/pyplots-images/plots/band-basic/highcharts/plot.html -quality_score: 88 +quality_score: 91 impl_tags: dependencies: - selenium @@ -25,33 +25,36 @@ impl_tags: - grid-styling review: strengths: - - Excellent use of Highcharts arearange type for band visualization - - Widening prediction interval data creates genuine data storytelling about growing - forecast uncertainty - - All font sizes explicitly set and clearly readable at target resolution - - Perfect spec compliance with all required features implemented correctly - - Clean well-structured code with deterministic seed and sensible data generation + - Excellent visual quality with all font sizes explicitly set and readable + - Harmonious blue-amber color pairing with strong contrast and colorblind accessibility + - Data storytelling through widening uncertainty band communicates increasing forecast + uncertainty over time + - Clean well-refined chart with subtle dotted gridlines and minimal visual noise + - Realistic temperature forecasting context with statistically plausible 95% prediction + intervals + - Clean code structure with proper CDN download pattern for headless Chrome weaknesses: - - Yellow forecast line (#FFD43B) is too bright and saturated for refined color harmony - - Raw JSON dict approach bypasses highcharts-core Python wrapper reducing idiomatic - library usage - - Could push visual refinement further with softer axis lines and tighter legend - placement - image_description: The plot displays a 30-day temperature forecast band chart on - a white background. A light blue semi-transparent band (arearange) represents - the 95% prediction interval, clearly widening from left to right as forecast uncertainty - increases over the horizon. A bright yellow line (#FFD43B) shows the central forecast - trend, exhibiting a sinusoidal warming pattern ranging roughly from 12°C to 22°C. - The title reads "30-Day Temperature Forecast · band-basic · highcharts · pyplots.ai" - in bold, with a gray subtitle "Daily forecast with 95% prediction interval". The - x-axis is labeled "Forecast Day" (ticks at 5, 10, 15, 20, 25, 30) and the y-axis - "Temperature (°C)" with degree symbols from 5° to 30°. A vertical legend in the - upper right shows "95% Prediction Interval" (blue) and "Forecast" (yellow). The - y-axis has subtle dotted grid lines; x-axis grid is removed. Overall layout is - clean with generous margins. + - Library mastery could be deeper — could leverage more Highcharts-specific features + like gradient fills or custom tooltip formatting + - Axis lines could be further minimized or removed for a more modern look + - Legend symbol (circle) does not perfectly represent the band series visually + image_description: The plot displays a 30-day temperature forecast as a band plot + on a clean white background. A semi-transparent light blue band (#306998 at 25% + opacity) represents the 95% prediction interval, stretching from approximately + 5°C to 28°C at its widest. A bold amber/gold center line (#C49000) traces the + forecast temperature, following a sinusoidal pattern with peaks near days 4 (~17°C) + and 20 (~22°C) and troughs near days 10 (~11.5°C) and 27 (~16°C). The band visibly + widens over time, illustrating increasing forecast uncertainty. The title reads + "30-Day Temperature Forecast · band-basic · highcharts · pyplots.ai" in bold at + the top, with a smaller subtitle "Daily forecast with 95% prediction interval." + The y-axis is labeled "Temperature (°C)" with degree-symbol tick labels every + 1°C from 5° to 30°, and uses subtle dotted gridlines at very low opacity. The + x-axis is labeled "Forecast Day" with ticks at intervals of 5. A floating legend + in the upper-right corner identifies "95% Prediction Interval" and "Forecast." + No content is cut off and the layout is well-balanced. criteria_checklist: visual_quality: - score: 29 + score: 30 max: 30 items: - id: VQ-01 @@ -60,63 +63,66 @@ review: max: 8 passed: true comment: 'All font sizes explicitly set: title 48px, subtitle 30px, axis titles - 36px, labels 28px, legend 28px' + 36px, tick labels 28px, legend 28px. All perfectly readable.' - id: VQ-02 name: No Overlap score: 6 max: 6 passed: true - comment: No overlapping elements anywhere + comment: No overlapping text. Legend well-positioned in upper right, clear + of data. Axis labels well-spaced. - id: VQ-03 name: Element Visibility - score: 5 + score: 6 max: 6 passed: true - comment: Band visible at 0.25 opacity, yellow line has moderate contrast against - light background + comment: Band clearly visible with semi-transparent fill. Center line at width + 5 is prominent. Smooth curves, no markers. - id: VQ-04 name: Color Accessibility score: 4 max: 4 passed: true - comment: Blue-yellow scheme is colorblind-safe + comment: Blue and amber are colorblind-safe, distinguishable by hue and luminance. + No red-green dependency. - id: VQ-05 name: Layout & Canvas score: 4 max: 4 passed: true - comment: Well-balanced margins, plot fills ~65% of canvas + comment: Chart fills ~65-70% of canvas. Balanced margins. No content cut off. - id: VQ-06 name: Axis Labels & Title score: 2 max: 2 passed: true - comment: Temperature (°C) with units, Forecast Day descriptive + comment: 'Y-axis: Temperature (°C) with units. X-axis: Forecast Day. Tick + labels include degree symbols.' design_excellence: - score: 13 + score: 14 max: 20 items: - id: DE-01 name: Aesthetic Sophistication - score: 5 + score: 6 max: 8 passed: true - comment: Intentional palette and font stack, above defaults but bright yellow - is garish + comment: Harmonious cool/warm color pairing, custom font stack, clear typographic + hierarchy. Clearly above library defaults. - id: DE-02 name: Visual Refinement score: 4 max: 6 passed: true - comment: Dotted y-axis-only grid, removed x-axis grid, subtle axis colors, - markers disabled + comment: Dotted y-grid at low opacity, no x-grid, subtle axis lines, credits + disabled, no markers. Clean but axis lines present rather than removed. - id: DE-03 name: Data Storytelling score: 4 max: 6 passed: true - comment: Widening prediction interval tells clear uncertainty story with visual - hierarchy + comment: Widening band communicates increasing uncertainty over time. Warm/cool + color contrast creates visual hierarchy. Subtitle contextualizes the visualization. spec_compliance: score: 15 max: 15 @@ -126,27 +132,29 @@ review: score: 5 max: 5 passed: true - comment: Correct band plot using arearange + line series + comment: Correct band/area-range plot with AreaRangeSeries for band and LineSeries + for center line. - id: SC-02 name: Required Features score: 4 max: 4 passed: true - comment: Semi-transparent fill, central line, smooth interpolation, uncertainty - source in subtitle + comment: Semi-transparent fill at 0.25, contrasting center line, 30 data points, + smooth interpolation, uncertainty context. - id: SC-03 name: Data Mapping score: 3 max: 3 passed: true - comment: X=days, band boundaries correct, center line correct + comment: X maps to days, Y maps to temperature. Full data range visible on + both axes. - id: SC-04 name: Title & Legend score: 3 max: 3 passed: true - comment: Title format correct with descriptive prefix, legend labels match - data + comment: Title includes band-basic · highcharts · pyplots.ai format. Legend + labels accurately describe series. data_quality: score: 15 max: 15 @@ -156,19 +164,22 @@ review: score: 6 max: 6 passed: true - comment: Shows widening uncertainty, sinusoidal variation with warming trend + comment: 'Shows all band plot features: filled region, varying band width, + center line, smooth data, multiple oscillation cycles.' - id: DQ-02 name: Realistic Context score: 5 max: 5 passed: true - comment: Temperature forecasting is real-world neutral scenario + comment: Temperature forecasting with 95% prediction intervals is a genuine, + neutral, real-world scientific application. - id: DQ-03 name: Appropriate Scale score: 4 max: 4 passed: true - comment: Temperatures 5-30°C realistic for 30-day forecast + comment: Temperatures 5-28°C realistic for temperate climate. 30-day period + standard. Uncertainty widths statistically plausible. code_quality: score: 10 max: 10 @@ -178,46 +189,51 @@ review: score: 3 max: 3 passed: true - comment: 'Linear flow: imports, data, chart options, HTML, screenshot' + comment: 'Linear script: imports, data generation, chart config, series, HTML + export, screenshot. No functions or classes.' - id: CQ-02 name: Reproducibility score: 2 max: 2 passed: true - comment: np.random.seed(42) set + comment: np.random.seed(42) set at start for deterministic data generation. - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: All imports used + comment: 'All imports used: tempfile, time, urllib.request, Path, numpy, highcharts-core + classes, PIL, selenium.' - id: CQ-04 name: Code Elegance score: 2 max: 2 passed: true - comment: Clean, well-organized, appropriate complexity + comment: Clean, well-organized. Appropriate complexity. Vectorized numpy operations + for data generation. - id: CQ-05 name: Output & API score: 1 max: 1 passed: true - comment: Saves plot.png, uses current Highcharts v11.4 + comment: Saves as plot.png. Uses current highcharts-core API. Proper temp + file cleanup. library_mastery: - score: 6 + score: 7 max: 10 items: - id: LM-01 name: Idiomatic Usage - score: 3 + score: 4 max: 5 - passed: false - comment: Raw JSON dict approach instead of highcharts-core Python wrapper + passed: true + comment: 'Uses highcharts-core properly: Chart with container, HighchartsOptions, + typed series classes, plot_options, to_js_literal().' - id: LM-02 name: Distinctive Features score: 3 max: 5 passed: true - comment: Uses Highcharts-specific arearange type with highcharts-more.js, - series zIndex - verdict: REJECTED + comment: Uses AreaRangeSeries, z_index layering, floating legend with transparency, + declarative plot_options. Leverages Highcharts strengths. + verdict: APPROVED