diff --git a/plots/waterfall-basic/implementations/python/highcharts.py b/plots/waterfall-basic/implementations/python/highcharts.py index 17b36ea26e..6e0ff10549 100644 --- a/plots/waterfall-basic/implementations/python/highcharts.py +++ b/plots/waterfall-basic/implementations/python/highcharts.py @@ -1,10 +1,11 @@ -""" pyplots.ai +""" anyplot.ai waterfall-basic: Basic Waterfall Chart -Library: highcharts unknown | Python 3.13.11 -Quality: 91/100 | Created: 2025-12-24 +Library: highcharts unknown | Python 3.13.13 +Quality: 83/100 | Updated: 2026-05-06 """ import json +import os import tempfile import time import urllib.request @@ -12,8 +13,22 @@ from selenium import webdriver from selenium.webdriver.chrome.options import Options +from selenium.webdriver.support.ui import WebDriverWait +# Theme tokens (see prompts/default-style-guide.md "Background" + "Theme-adaptive Chrome") +THEME = os.getenv("ANYPLOT_THEME", "light") +PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17" +ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420" +INK = "#1A1A17" if THEME == "light" else "#F0EFE8" +INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" +GRID = "rgba(26,26,23,0.10)" if THEME == "light" else "rgba(240,239,232,0.10)" + +# Okabe-Ito palette: positive = brand green, negative = vermillion +POSITIVE_COLOR = "#009E73" # Okabe-Ito position 1 (brand green) +NEGATIVE_COLOR = "#D55E00" # Okabe-Ito position 2 (vermillion) +TOTAL_COLOR = "#0072B2" # Okabe-Ito position 3 (blue) for start/end totals + # Data - Quarterly financial breakdown from revenue to net income categories = [ "Revenue", @@ -27,17 +42,15 @@ ] # Values: positive = increase, negative = decrease -# Revenue (start), costs (negative), income (positive), and final net income (total) -# Using colorblind-safe colors: Python Blue for totals, teal for increases, orange for decreases values = [ - {"y": 500000, "isIntermediateSum": False, "color": "#306998"}, # Revenue - starting total (Python Blue) - {"y": -150000, "color": "#E67300"}, # Product Costs (orange - decrease) - {"y": -80000, "color": "#E67300"}, # Operating Expenses (orange - decrease) - {"y": -45000, "color": "#E67300"}, # Marketing (orange - decrease) - {"y": -35000, "color": "#E67300"}, # R&D (orange - decrease) - {"y": 20000, "color": "#17BECF"}, # Other Income (teal - increase) - {"y": -52000, "color": "#E67300"}, # Taxes (orange - decrease) - {"isSum": True, "color": "#306998"}, # Net Income - ending total (Python Blue) + {"y": 500000, "color": TOTAL_COLOR}, # Revenue - starting total + {"y": -150000, "color": NEGATIVE_COLOR}, # Product Costs + {"y": -80000, "color": NEGATIVE_COLOR}, # Operating Expenses + {"y": -45000, "color": NEGATIVE_COLOR}, # Marketing + {"y": -35000, "color": NEGATIVE_COLOR}, # R&D + {"y": 20000, "color": POSITIVE_COLOR}, # Other Income + {"y": -52000, "color": NEGATIVE_COLOR}, # Taxes + {"isSum": True, "color": TOTAL_COLOR}, # Net Income - ending total ] # Chart options for Highcharts waterfall @@ -46,59 +59,51 @@ "type": "waterfall", "width": 4800, "height": 2700, - "backgroundColor": "#ffffff", + "backgroundColor": PAGE_BG, "marginBottom": 280, - "style": {"fontFamily": "Arial, sans-serif"}, + "style": {"fontFamily": "Arial, sans-serif", "color": INK}, }, "title": { - "text": "Quarterly Financial Breakdown · waterfall-basic · highcharts · pyplots.ai", - "style": {"fontSize": "48px", "fontWeight": "bold"}, + "text": "waterfall-basic · highcharts · anyplot.ai", + "style": {"fontSize": "28px", "fontWeight": "normal", "color": INK}, }, "xAxis": { "categories": categories, - "title": {"text": "Category", "style": {"fontSize": "36px"}}, - "labels": {"style": {"fontSize": "28px"}, "rotation": -45, "y": 40}, + "title": {"text": "", "style": {"fontSize": "22px", "color": INK}}, + "labels": {"style": {"fontSize": "18px", "color": INK_SOFT}}, + "lineColor": INK_SOFT, + "tickColor": INK_SOFT, + "gridLineColor": GRID, }, "yAxis": { - "title": {"text": "Amount ($)", "style": {"fontSize": "36px"}}, - "labels": {"style": {"fontSize": "28px"}, "formatter": "__FORMATTER_PLACEHOLDER__"}, - "gridLineColor": "#e0e0e0", + "title": {"text": "Amount ($)", "style": {"fontSize": "22px", "color": INK}}, + "labels": {"style": {"fontSize": "18px", "color": INK_SOFT}, "formatter": "__FORMATTER_PLACEHOLDER__"}, + "lineColor": INK_SOFT, + "tickColor": INK_SOFT, + "gridLineColor": GRID, }, "legend": {"enabled": False}, - "tooltip": {"pointFormat": "${point.y:,.0f}", "style": {"fontSize": "24px"}}, + "tooltip": { + "pointFormat": "${point.y:,.0f}", + "headerFormat": "{point.key}
", + "style": {"fontSize": "18px"}, + }, "plotOptions": { "waterfall": { "lineWidth": 2, - "lineColor": "#333333", + "lineColor": INK_SOFT, "borderWidth": 0, "pointPadding": 0.15, "dataLabels": { "enabled": True, "formatter": "__DATALABEL_FORMATTER_PLACEHOLDER__", - "style": {"fontSize": "24px", "fontWeight": "bold", "textOutline": "2px white"}, + "style": {"fontSize": "18px", "fontWeight": "bold", "color": INK, "textOutline": "none"}, }, } }, - "series": [ - { - "name": "Financial Breakdown", - "data": values, - "upColor": "#17BECF", # Teal for positive changes (colorblind-safe) - "color": "#E67300", # Orange for negative changes (colorblind-safe) - } - ], + "series": [{"name": "Financial Breakdown", "data": values}], } -# Download Highcharts JS for inline embedding -highcharts_url = "https://code.highcharts.com/highcharts.js" -with urllib.request.urlopen(highcharts_url, timeout=30) as response: - highcharts_js = response.read().decode("utf-8") - -# Download highcharts-more.js (needed for waterfall chart) -highcharts_more_url = "https://code.highcharts.com/highcharts-more.js" -with urllib.request.urlopen(highcharts_more_url, timeout=30) as response: - highcharts_more_js = response.read().decode("utf-8") - # Generate chart options JSON and add custom formatters chart_options_json = json.dumps(chart_options) @@ -119,15 +124,37 @@ }""" chart_options_json = chart_options_json.replace('"__DATALABEL_FORMATTER_PLACEHOLDER__"', data_label_formatter) -# Generate HTML with inline scripts -html_content = f""" +# Try to download JS, fallback to CDN links if network unavailable +highcharts_js = "" +highcharts_more_js = "" + +try: + highcharts_url = "https://code.highcharts.com/highcharts.js" + req = urllib.request.Request(highcharts_url, headers={"User-Agent": "Mozilla/5.0"}) + with urllib.request.urlopen(req, timeout=10) as response: + highcharts_js = response.read().decode("utf-8") +except Exception: + highcharts_js = None + +try: + highcharts_more_url = "https://code.highcharts.com/highcharts-more.js" + req = urllib.request.Request(highcharts_more_url, headers={"User-Agent": "Mozilla/5.0"}) + with urllib.request.urlopen(req, timeout=10) as response: + highcharts_more_js = response.read().decode("utf-8") +except Exception: + highcharts_more_js = None + +# Generate HTML content based on whether we downloaded the scripts +if highcharts_js and highcharts_more_js: + # Use inline scripts + html_content = f""" - +
""" +else: + # Fallback to CDN script tags + html_content = f""" + + + + + + + +
+ + +""" -# Write temp HTML file -with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f: +# Write HTML artifact for the site (both themes) +with open(f"plot-{THEME}.html", "w", encoding="utf-8") as f: f.write(html_content) - temp_path = f.name -# Also save the HTML for interactive viewing -with open("plot.html", "w", encoding="utf-8") as f: +# Write temp HTML and take screenshot for the PNG artifact +with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f: f.write(html_content) + temp_path = f.name -# Take screenshot with headless Chrome chrome_options = Options() 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("--window-size=4800,2700") +chrome_options.add_argument("--disable-web-resources") driver = webdriver.Chrome(options=chrome_options) driver.get(f"file://{temp_path}") -time.sleep(5) -driver.save_screenshot("plot.png") + +try: + # Wait for either Highcharts to be available or max timeout + WebDriverWait(driver, 15).until(lambda d: d.execute_script("return typeof Highcharts !== 'undefined'")) +except Exception: + pass + +time.sleep(3) +driver.save_screenshot(f"plot-{THEME}.png") driver.quit() -# Clean up temp file Path(temp_path).unlink() diff --git a/plots/waterfall-basic/metadata/python/highcharts.yaml b/plots/waterfall-basic/metadata/python/highcharts.yaml index dfaf354a78..0f18423ffb 100644 --- a/plots/waterfall-basic/metadata/python/highcharts.yaml +++ b/plots/waterfall-basic/metadata/python/highcharts.yaml @@ -1,164 +1,198 @@ library: highcharts +language: python specification_id: waterfall-basic created: '2025-12-24T09:53:31Z' -updated: '2025-12-24T10:04:46Z' -generated_by: claude-opus-4-5-20251101 -workflow_run: 20483449525 -issue: 0 -python_version: 3.13.11 +updated: '2026-05-06T22:05:50Z' +generated_by: claude-haiku +workflow_run: 25410954779 +issue: 777 +python_version: 3.13.13 library_version: unknown -preview_url: https://storage.googleapis.com/anyplot-images/plots/waterfall-basic/highcharts/plot.png -preview_html: https://storage.googleapis.com/anyplot-images/plots/waterfall-basic/highcharts/plot.html -quality_score: 91 -impl_tags: - dependencies: - - selenium - techniques: - - html-export - patterns: - - data-generation - dataprep: [] - styling: [] +preview_url_light: https://storage.googleapis.com/anyplot-images/plots/waterfall-basic/python/highcharts/plot-light.png +preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/waterfall-basic/python/highcharts/plot-dark.png +preview_html_light: https://storage.googleapis.com/anyplot-images/plots/waterfall-basic/python/highcharts/plot-light.html +preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/waterfall-basic/python/highcharts/plot-dark.html +quality_score: 83 review: strengths: - - Excellent use of colorblind-safe color palette (blue for totals, orange for decreases, - teal for increases) - - Clean waterfall visualization with proper connecting lines between bars - - Data labels show +/- prefixes for clarity on direction of change - - Realistic financial scenario that demonstrates the waterfall concept well - - Proper use of Highcharts isSum property for the final total bar - - Title follows the required pyplots.ai format exactly + - Uses Highcharts native waterfall chart type with isSum flag for accurate automatic + cumulative sum tracking + - 'Okabe-Ito palette applied correctly: brand green (#009E73) for positive changes, + vermillion (#D55E00) for negatives, blue (#0072B2) for totals' + - 'Full theme-adaptive chrome: background, text (INK/INK_SOFT), and grid all switch + correctly between light (#FAF8F1) and dark (#1A1A17)' + - 'All spec requirements met: color-coded bars, dotted connector lines, distinct + start/end total bars, data labels on every bar' + - Realistic and internally consistent financial data (quarterly P&L breakdown + from revenue to net income) + - HTML artifacts generated for both themes alongside PNG exports weaknesses: - - Grid lines are very subtle and could be more visible for easier value reading - - Data labels on smaller bars appear somewhat cramped - - X-axis Category label is not particularly informative for this financial context - image_description: 'The plot displays a waterfall chart showing a quarterly financial - breakdown. The chart starts with a tall blue "Revenue" bar at $500,000, followed - by orange bars representing decreases: Product Costs (-$150,000), Operating Expenses - (-$80,000), Marketing (-$45,000), R&D (-$35,000), and Taxes (-$52,000). There - is one teal/cyan bar for "Other Income" (+$20,000) representing an increase. The - final blue bar shows "Net Income" at $158,000. Connecting dotted lines link the - bars to show cumulative flow. Data labels appear on each bar showing the change - amount with +/- prefixes. The y-axis shows "Amount ($)" with values from $0 to - $540,000. X-axis labels are rotated at -45 degrees. The title follows the required - format. Colors are colorblind-safe (blue for totals, orange for decreases, teal - for increases).' + - 'Design Excellence is mid-tier: chart is a well-configured Highcharts default + with no standout aesthetic touches — remove the default chart border/plot area + border if present, tighten inter-bar spacing, add a subtle horizontal reference + line at $0' + - Data label formatter labels the Revenue starting bar as '+$500,000' (change format) + instead of '$500,000' (total format) — the isSum path in the formatter only applies + to the final Net Income bar, not the first Revenue total + - JS formatter insertion via string placeholder replacement is a code smell — the + placeholder hack adds fragility; consider using highcharts_core Python API or + a raw JavaScript string template instead + - Canvas utilisation is approximately 60% — the chart is visually centred but with + significant empty space in the upper-right; reducing chart height or increasing + bar width would help + - Data labels inside bars are 18px which renders small at 4800×2700; 22-24px would + improve on-bar readability + image_description: |- + Light render (plot-light.png): + Background: Warm off-white (#FAF8F1) — correct theme surface, not pure white. + Chrome: Title "waterfall-basic · highcharts · anyplot.ai" is visible at top-center in dark ink; y-axis title "Amount ($)" is rotated on the left in dark ink; x-axis category labels (Revenue, Product Costs, etc.) appear at the bottom in INK_SOFT dark grey; y-axis tick labels ($0–$540,000) in INK_SOFT. All text is readable against the light background. + Data: Revenue bar is blue (#0072B2), Product Costs/Operating Expenses/Marketing/R&D/Taxes bars are vermillion-orange (#D55E00), Other Income bar is brand green (#009E73), Net Income bar is blue (#0072B2). Dotted horizontal connector lines link each bar to the next. Data labels (e.g. "+$500,000", "-$150,000", "$158,000") appear inside each bar in dark INK color. + Legibility verdict: PASS — all text readable; no light-on-light issues. + + Dark render (plot-dark.png): + Background: Near-black (#1A1A17) — correct dark theme surface, not pure black. + Chrome: Title text appears in light #F0EFE8 against the dark background; axis labels and tick labels appear in light INK_SOFT (#B8B7B0); no dark-on-dark text failures observed. Theme token switching is working correctly throughout. + Data: Bar colors are identical to the light render — blue totals, orange negatives, green positive — confirming Okabe-Ito positions 1–3 are theme-invariant. Connector lines and data labels are visible. Data labels appear in light INK color (#F0EFE8) on the dark-mode bars. + Legibility verdict: PASS — all text readable against dark background; no dark-on-dark failures. criteria_checklist: visual_quality: - score: 36 - max: 40 + score: 26 + max: 30 items: - id: VQ-01 name: Text Legibility - score: 9 - max: 10 + score: 6 + max: 8 passed: true - comment: Title and labels are readable at full size, though data labels on - bars could be slightly larger + comment: All font sizes explicitly set (28px title, 22px axis, 18px ticks/labels) + per style guide; readable in both themes but on-bar data labels could be + larger at this canvas size - id: VQ-02 name: No Overlap - score: 8 - max: 8 + score: 6 + max: 6 passed: true - comment: No overlapping text elements + comment: No overlapping text elements in either render - id: VQ-03 name: Element Visibility - score: 8 - max: 8 + score: 5 + max: 6 passed: true - comment: Bars are well-sized and clearly visible with appropriate spacing + comment: Bars and connector lines clearly visible; data labels slightly small + at 18px for 4800x2700 canvas - id: VQ-04 name: Color Accessibility - score: 5 - max: 5 + score: 2 + max: 2 passed: true - comment: Uses colorblind-safe palette (blue/orange/teal instead of red/green) + comment: Okabe-Ito palette (positions 1-3) used — CVD-safe; adequate contrast + throughout - id: VQ-05 - name: Layout Balance - score: 4 - max: 5 + name: Layout & Canvas + score: 3 + max: 4 passed: true - comment: Good proportions, though bottom margin could use the full space better + comment: Chart fills roughly 60% of canvas; upper-right area somewhat empty + but not severe; bottom margin (280px) prevents label clipping - id: VQ-06 - name: Axis Labels + name: Axis Labels & Title score: 2 max: 2 passed: true - comment: Y-axis has "Amount ($)" with currency indicator + comment: 'Y-axis: ''Amount ($)'' with units; title format ''waterfall-basic + · highcharts · anyplot.ai'' correct' - id: VQ-07 - name: Grid & Legend - score: 0 + name: Palette Compliance + score: 2 max: 2 - passed: false - comment: Legend is disabled which is fine for this chart, but grid lines are - quite subtle/minimal + passed: true + comment: 'Brand green #009E73 for positive bars; vermillion #D55E00 for negative; + blue #0072B2 for totals; backgrounds #FAF8F1/#1A1A17 correct; data colors + identical across themes; chrome fully adaptive' + design_excellence: + score: 11 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 4 + max: 8 + passed: true + comment: 'Well-configured Highcharts default: clean palette, no chartjunk, + but no standout design choices beyond the Okabe-Ito colors' + - id: DE-02 + name: Visual Refinement + score: 3 + max: 6 + passed: true + comment: Legend disabled, generous bottom margin, no visible chart border + box, subtle connector lines — above default but not exceptional + - id: DE-03 + name: Data Storytelling + score: 4 + max: 6 + passed: true + comment: Color-coded bars (green gain, orange costs, blue totals) immediately + communicate the financial story; waterfall cascade is clear; color alone + creates 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 waterfall chart type - - id: SC-02 - name: Data Mapping score: 5 max: 5 passed: true - comment: Categories and values correctly mapped - - id: SC-03 + comment: Highcharts native waterfall chart type used correctly + - id: SC-02 name: Required Features - score: 5 - max: 5 + score: 4 + max: 4 passed: true - comment: Has connecting lines, distinct colors for positive/negative/totals, - data labels - - id: SC-04 - name: Data Range + comment: 'All spec requirements met: color-coded positive/negative bars, dotted + connector lines, distinct start/end total bars (blue), data labels on every + bar' + - id: SC-03 + name: Data Mapping score: 3 max: 3 passed: true - comment: All data visible within axis range - - id: SC-05 - name: Legend Accuracy - score: 2 - max: 2 - passed: true - comment: Legend appropriately disabled for this single-series chart - - id: SC-06 - name: Title Format - score: 2 - max: 2 + comment: Categories on x-axis, dollar amounts on y-axis, correct cumulative + flow + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 passed: true - comment: 'Uses correct format: "Quarterly Financial Breakdown · waterfall-basic - · highcharts · pyplots.ai"' + comment: Title 'waterfall-basic · highcharts · anyplot.ai' exact format; legend + disabled (single-series, appropriate) 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 positive changes, negative changes, start total, and end total; - good variety of cost categories + comment: 'Shows all waterfall features: positive change (Other Income), multiple + negative changes, start total (Revenue), end total (Net Income), intermediate + steps' - id: DQ-02 name: Realistic Context - score: 7 - max: 7 + score: 5 + max: 5 passed: true - comment: Quarterly financial breakdown is a classic waterfall use case with - realistic business categories + comment: Quarterly P&L breakdown from Revenue to Net Income — real-world business + finance scenario, neutral and recognisable - id: DQ-03 name: Appropriate Scale score: 4 - max: 5 + max: 4 passed: true - comment: Values are realistic for a business scenario, though the ratio of - net income to revenue (~32%) is quite healthy + comment: Revenue $500k, costs proportional (30%/16%/9%/7%), net income $158k + (31.6% margin) — internally consistent and plausible for a mid-size company code_quality: score: 9 max: 10 @@ -168,42 +202,60 @@ review: score: 3 max: 3 passed: true - comment: 'Clean linear flow: imports → data → chart config → render → save' + comment: 'Linear flow: imports → tokens → data → chart options → JS download + → HTML → screenshot → cleanup' - id: CQ-02 name: Reproducibility - score: 3 - max: 3 + score: 2 + max: 2 passed: true - comment: Deterministic data (no random values) + comment: Data is fully deterministic (hardcoded values, no randomness) - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: Only necessary imports used + comment: All imported modules (json, os, tempfile, time, urllib.request, pathlib, + selenium) are used - id: CQ-04 - name: No Deprecated API + name: Code Elegance score: 1 - max: 1 + max: 2 passed: true - comment: Uses current Highcharts API + comment: Placeholder-replacement hack to inject JS formatter functions into + JSON string is fragile and inelegant; CDN fallback logic adds complexity - id: CQ-05 - name: Output Correct - score: 0 + name: Output & API + score: 1 max: 1 - passed: false - comment: Saves as plot.png correctly, but also saves plot.html (acceptable - for interactive library) - library_features: - score: 3 - max: 5 + passed: true + comment: Saves plot-{THEME}.png and plot-{THEME}.html for both themes; current + Selenium API used + library_mastery: + score: 7 + max: 10 items: - - id: LF-01 - name: Uses distinctive library features + - id: LM-01 + name: Idiomatic Usage + score: 4 + max: 5 + passed: true + comment: Uses Highcharts native waterfall type, per-point isSum flag, per-point + color overrides, and JS formatter functions — idiomatic but not expert-level + - id: LM-02 + name: Distinctive Features score: 3 max: 5 passed: true - comment: Uses Highcharts native waterfall type with isSum/isIntermediateSum, - custom formatters for data labels, but could leverage more Highcharts-specific - features like animations or drill-down + comment: isSum flag for automatic cumulative sum at the final bar, per-point + color overrides in the data array, and custom JS numberFormat formatters + are Highcharts-specific capabilities verdict: APPROVED +impl_tags: + dependencies: + - selenium + techniques: + - html-export + patterns: [] + dataprep: [] + styling: []