diff --git a/plots/scatter-basic/implementations/python/bokeh.py b/plots/scatter-basic/implementations/python/bokeh.py index 24d7e348b0..9542aa0cde 100644 --- a/plots/scatter-basic/implementations/python/bokeh.py +++ b/plots/scatter-basic/implementations/python/bokeh.py @@ -1,22 +1,35 @@ -""" anyplot.ai +"""anyplot.ai scatter-basic: Basic Scatter Plot -Library: bokeh 3.9.0 | Python 3.14.4 -Quality: 90/100 | Updated: 2026-04-23 +Library: bokeh 3.9.1 | Python 3.13.14 +Quality: 87/100 | Updated: 2026-06-25 """ import os +import sys +import time +from pathlib import Path + + +# Remove script directory from sys.path so 'import bokeh' finds the installed +# package rather than this file (bokeh.py would shadow itself otherwise). +_script_dir = os.path.dirname(os.path.abspath(__file__)) +sys.path = [p for p in sys.path if os.path.abspath(p) != _script_dir] import numpy as np -from bokeh.io import export_png, output_file, save +from bokeh.io import output_file, save from bokeh.models import ColumnDataSource, HoverTool from bokeh.plotting import figure +from selenium import webdriver +from selenium.webdriver.chrome.options import Options +# Theme tokens 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" -BRAND = "#009E73" +BRAND = "#009E73" # Imprint palette position 1 — ALWAYS first series # Data — study hours vs exam scores, moderate positive correlation np.random.seed(42) @@ -27,22 +40,42 @@ source = ColumnDataSource(data={"study_hours": study_hours, "exam_scores": exam_scores}) -# Plot +# Plot — 3200×1800 canonical landscape canvas +W, H = 3200, 1800 +title = "scatter-basic · python · bokeh · anyplot.ai" + p = figure( - width=4800, - height=2700, - title="scatter-basic · bokeh · anyplot.ai", + width=W, + height=H, + title=title, x_axis_label="Study Hours per Day", y_axis_label="Exam Score (%)", toolbar_location=None, x_range=(0, 10.5), y_range=(10, 104), + min_border_bottom=160, + min_border_left=180, + min_border_top=110, + min_border_right=50, ) scatter_renderer = p.scatter( - x="study_hours", y="exam_scores", source=source, size=34, color=BRAND, alpha=0.7, line_color=PAGE_BG, line_width=1.2 + x="study_hours", + y="exam_scores", + source=source, + size=28, + color=BRAND, + alpha=0.7, + line_color=INK_SOFT, + line_width=1.2, ) +# Trend line — linear regression to show positive correlation +coeffs = np.polyfit(study_hours, exam_scores, 1) +x_trend = np.array([0.0, 10.5]) +y_trend = np.polyval(coeffs, x_trend) +p.line(x=x_trend, y=y_trend, line_color=INK_SOFT, line_width=4, line_alpha=0.5, line_dash="dashed") + # HoverTool — Bokeh's distinctive interactive feature (HTML only; PNG stays clean) hover = HoverTool( renderers=[scatter_renderer], @@ -50,18 +83,18 @@ ) p.add_tools(hover) -# Typography — sized for 4800×2700 canvas -p.title.text_font_size = "42pt" +# Typography — bokeh.md canonical values for 3200×1800 canvas +p.title.text_font_size = "50pt" p.title.text_font_style = "bold" p.title.text_color = INK p.title.align = "center" -p.xaxis.axis_label_text_font_size = "32pt" -p.yaxis.axis_label_text_font_size = "32pt" +p.xaxis.axis_label_text_font_size = "42pt" +p.yaxis.axis_label_text_font_size = "42pt" p.xaxis.axis_label_text_font_style = "normal" p.yaxis.axis_label_text_font_style = "normal" -p.xaxis.major_label_text_font_size = "24pt" -p.yaxis.major_label_text_font_size = "24pt" +p.xaxis.major_label_text_font_size = "34pt" +p.yaxis.major_label_text_font_size = "34pt" p.xaxis.axis_label_standoff = 28 p.yaxis.axis_label_standoff = 28 @@ -81,18 +114,34 @@ p.xaxis.minor_tick_line_color = None p.yaxis.minor_tick_line_color = None -# Clean L-frame: keep left+bottom axis lines only (handled above via axis_line_color) +# Grid — both axes for scatter (style guide), subtle weight p.xgrid.grid_line_color = INK p.ygrid.grid_line_color = INK p.xgrid.grid_line_alpha = 0.10 p.ygrid.grid_line_alpha = 0.10 -p.xgrid.grid_line_width = 2 -p.ygrid.grid_line_width = 2 +p.xgrid.grid_line_width = 1 +p.ygrid.grid_line_width = 1 p.xaxis.ticker.desired_num_ticks = 10 p.yaxis.ticker.desired_num_ticks = 8 -# Save -export_png(p, filename=f"plot-{THEME}.png") -output_file(f"plot-{THEME}.html", title="scatter-basic · bokeh · anyplot.ai") +# Save — HTML first (interactive catalog artifact), then PNG via Selenium +output_file(f"plot-{THEME}.html", title="scatter-basic · python · bokeh · anyplot.ai") save(p) + +opts = Options() +for arg in ( + "--headless=new", + "--no-sandbox", + "--disable-dev-shm-usage", + "--disable-gpu", + f"--window-size={W},{H}", + "--hide-scrollbars", +): + opts.add_argument(arg) +driver = webdriver.Chrome(options=opts) +driver.set_window_size(W, H) +driver.get(f"file://{Path(f'plot-{THEME}.html').resolve()}") +time.sleep(3) +driver.save_screenshot(f"plot-{THEME}.png") +driver.quit() diff --git a/plots/scatter-basic/metadata/python/bokeh.yaml b/plots/scatter-basic/metadata/python/bokeh.yaml index c5e30ff91f..b1ee7ec7ed 100644 --- a/plots/scatter-basic/metadata/python/bokeh.yaml +++ b/plots/scatter-basic/metadata/python/bokeh.yaml @@ -2,42 +2,51 @@ library: bokeh language: python specification_id: scatter-basic created: '2025-12-10T20:55:10Z' -updated: '2026-04-23T22:00:58Z' -generated_by: claude-opus -workflow_run: 24860109612 +updated: '2026-06-25T01:30:12Z' +generated_by: claude-sonnet +workflow_run: 28140406523 issue: 611 -python_version: 3.14.4 -library_version: 3.9.0 +language_version: 3.13.14 +library_version: 3.9.1 preview_url_light: https://storage.googleapis.com/anyplot-images/plots/scatter-basic/python/bokeh/plot-light.png preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/scatter-basic/python/bokeh/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/scatter-basic/python/bokeh/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/scatter-basic/python/bokeh/plot-dark.html -quality_score: 90 +quality_score: 87 review: strengths: - - 'Clean, idiomatic Bokeh implementation: ColumnDataSource, HoverTool, and dual - PNG+HTML export all used correctly' - - Excellent theme-adaptive styling — both light and dark renders are visually polished - with correct backgrounds and adaptive chrome - - Realistic, neutral education dataset (study hours vs exam scores) with clear positive - correlation demonstrates scatter plot purpose effectively + - Correct Imprint brand green (#009E73) as sole data color, identical between both + renders + - Full theme-adaptive chrome — backgrounds, text, tick labels, grid all flip correctly + between light (#FAF8F1) and dark (#1A1A17) + - Realistic and neutral academic dataset (study hours vs exam scores) with good + positive correlation and natural scatter + - Canonical bokeh.md font sizes used (50pt title, 42pt axis labels, 34pt tick labels) + - 'All spec requirements met: alpha=0.7, grid lines, descriptive axis labels, 180 + points within 50-500 range' + - HoverTool and HTML export correctly leveraged as Bokeh-distinctive interactive + features weaknesses: - - Marker size (size=34) is slightly small for 180 points on a 4800x2700px canvas; - size=50-55 would improve visual presence - - Grid line width=2 on both axes adds minor visual weight; reducing to width=1 or - removing x-grid would refine the look + - Bokeh outline frame (p.outline_line_color = INK_SOFT) keeps a visible box around + the plot area — style guide prefers removing the border for a cleaner L-shape + or borderless look + - No visual storytelling beyond raw scatter — a trend line or annotation on a key + outlier would elevate DE-03 and guide the viewer + - Marker edge stroke (line_color=PAGE_BG) provides definition in light mode but + becomes invisible in dark mode; a theme-adaptive INK_SOFT or light stroke would + give consistent separation in both themes image_description: |- Light render (plot-light.png): - Background: Warm off-white (#FAF8F1) — correct, not pure white - Chrome: Title "scatter-basic · bokeh · anyplot.ai" in bold dark ink, centered; axis labels "Study Hours per Day" and "Exam Score (%)" in dark ink; tick labels in slightly lighter dark gray — all clearly readable - Data: ~180 scatter points in brand green (#009E73) with off-white marker edges; regression trend line (dashed) and semi-transparent confidence band visible; alpha ~0.7 - Legibility verdict: PASS + Background: Warm off-white #FAF8F1 — correct anyplot light surface, no pure white + Chrome: Title "scatter-basic · python · bokeh · anyplot.ai" in bold dark (#1A1A17) centered at top; y-axis label "Exam Score (%)" and x-axis label "Study Hours per Day" in dark ink; tick labels in slightly lighter INK_SOFT (#4A4A44). All text clearly readable against the light background. + Data: 180 scatter points in Imprint brand green (#009E73), size=28, alpha=0.7. Light background ring (line_color=PAGE_BG) provides subtle marker separation. Both x- and y-axis grids visible at ~10% opacity — subtle but present. + Legibility verdict: PASS — all title, axis label, and tick label text is clearly legible against the warm cream background. No light-on-light issues. Dark render (plot-dark.png): - Background: Warm near-black (#1A1A17) — correct, not pure black - Chrome: Title, axis labels, and tick labels all flip to light-colored text (near-white/light gray) — no dark-on-dark failures; grid lines remain subtle - Data: Scatter markers are identical #009E73 green — data colors match light render exactly; confidence band more saturated/darker on dark surface but still visible - Legibility verdict: PASS + Background: Warm near-black #1A1A17 — correct anyplot dark surface, not pure black + Chrome: Title and axis labels flip to light (#F0EFE8), tick labels to #B8B7B0. Grid remains at ~10% opacity using INK token (adapts to dark). All chrome elements readable. No dark-on-dark failures detected. + Data: Scatter points remain identical Imprint brand green (#009E73) — data colors unchanged between themes as required. The marker edge stroke (line_color=PAGE_BG=#1A1A17) becomes invisible against the dark background, which is an acceptable single-theme tradeoff. + Legibility verdict: PASS — all text is clearly readable against the near-black background. No dark-on-dark issues found. criteria_checklist: visual_quality: score: 29 @@ -45,51 +54,56 @@ review: items: - id: VQ-01 name: Text Legibility - score: 8 + score: 7 max: 8 passed: true - comment: Title 42pt, axis labels 32pt, tick labels 24pt explicitly set; readable - in both themes + comment: 'All text readable in both themes at canonical bokeh sizes (50pt + title, 42pt axis labels, 34pt tick labels). Minor: tick labels at 34pt are + slightly generous for the data density but within spec.' - id: VQ-02 name: No Overlap score: 6 max: 6 passed: true - comment: No text collisions + comment: No problematic text or element overlaps. Point overlap managed well + by alpha=0.7 at 180-point density. - id: VQ-03 name: Element Visibility - score: 5 + score: 6 max: 6 passed: true - comment: Markers visible with alpha=0.7; size=34 slightly small for 180 points - on 4800px canvas + comment: Markers clearly visible in both themes. Size=28 appropriate for 180-point + medium density. - id: VQ-04 name: Color Accessibility score: 2 max: 2 passed: true - comment: Single-series brand green, CVD-safe Okabe-Ito + comment: Single Imprint green series, CVD-safe, no red-green signal issue. - id: VQ-05 name: Layout & Canvas score: 4 max: 4 passed: true - comment: Data fills ~70% of canvas, balanced margins + comment: 3200x1800 confirmed (no canvas gate failure). Generous min_border + values prevent label clipping. Title proportions acceptable for the mandated + anyplot.ai title. - id: VQ-06 name: Axis Labels & Title score: 2 max: 2 passed: true - comment: Study Hours per Day and Exam Score (%) with units + comment: Title matches required format. Y-axis includes unit (%). Both labels + descriptive and clear. - id: VQ-07 name: Palette Compliance score: 2 max: 2 passed: true - comment: 'First series #009E73, backgrounds #FAF8F1/#1A1A17, theme-adaptive - chrome correct' + comment: 'First (only) series = #009E73. Backgrounds #FAF8F1 / #1A1A17 correct. + Full theme-adaptive chrome.' design_excellence: - score: 13 + score: 11 max: 20 items: - id: DE-01 @@ -97,22 +111,24 @@ review: score: 5 max: 8 passed: true - comment: 'Professional: warm backgrounds, white marker edges, regression feature - adds sophistication; above default but not publication-ready' + comment: Imprint palette, theme-adaptive chrome, subtle marker border ring + all demonstrate intentional design. Above default but no exceptional hierarchy + or emphasis techniques. - id: DE-02 name: Visual Refinement - score: 4 + score: 3 max: 6 - passed: true - comment: outline_line_color=None, grid alpha=0.10, minor ticks removed; further - reduction would improve + passed: false + comment: Subtle both-axis grid appropriate for scatter. However, Bokeh outline + frame (outline_line_color=INK_SOFT) keeps a visible box — style guide prefers + no top/right border. Whitespace generous. - id: DE-03 name: Data Storytelling - score: 4 + score: 3 max: 6 - passed: true - comment: Regression trend line + confidence band guides viewer to positive - correlation story + passed: false + comment: Clear positive correlation story from data alone. No trend line, + highlighted point, or annotation to create a visual focal point. spec_compliance: score: 15 max: 15 @@ -122,26 +138,28 @@ review: score: 5 max: 5 passed: true - comment: Correct scatter plot + comment: Correct 2D scatter plot using Bokeh scatter glyphs. - id: SC-02 name: Required Features score: 4 max: 4 passed: true - comment: Transparency, axis labels, grid lines, balanced point size all present + comment: alpha=0.7, descriptive axis labels, grid lines, 180 points (within + 50-500 range), moderate positive correlation. - id: SC-03 name: Data Mapping score: 3 max: 3 passed: true - comment: Study hours on x, exam scores on y, all data visible + comment: Study hours on x, exam scores on y. Full range visible. Realistic + data with seed-controlled randomness. - id: SC-04 name: Title & Legend score: 3 max: 3 passed: true - comment: Title 'scatter-basic · bokeh · anyplot.ai' correct; no legend appropriate - for single series + comment: Title exactly matches required format. No legend (single series — + correct omission). data_quality: score: 15 max: 15 @@ -151,19 +169,22 @@ review: score: 6 max: 6 passed: true - comment: Shows scatter density variation, outliers, clear positive trend + comment: 'All core scatter plot features present: point cloud, correlation + pattern, spread, alpha for overlap.' - id: DQ-02 name: Realistic Context score: 5 max: 5 passed: true - comment: 'Education scenario (study hours vs exam scores): neutral, real-world' + comment: Study hours vs exam scores — neutral, universally relatable academic + context. Values plausible (0.8-9.6 hrs, 18-99%). - id: DQ-03 name: Appropriate Scale score: 4 max: 4 passed: true - comment: Study hours 0.8-9.6 hrs/day, exam scores 18-99% — both realistic + comment: Sensible ranges for both variables. Correlation r~0.7 appropriate + for the domain. code_quality: score: 10 max: 10 @@ -173,52 +194,56 @@ review: score: 3 max: 3 passed: true - comment: 'Linear flow: imports → data → plot → save; no functions or classes' + comment: Flat procedural code, no functions or classes. Clear sequential flow. - id: CQ-02 name: Reproducibility score: 2 max: 2 passed: true - comment: np.random.seed(42) + comment: np.random.seed(42) ensures deterministic output. - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: All imported names used + comment: All imports used. sys.path manipulation is necessary to prevent self-shadowing. - id: CQ-04 name: Code Elegance score: 2 max: 2 passed: true - comment: Clean Pythonic code; HoverTool is real Bokeh functionality + comment: Appropriate complexity. No fake UI. CDP viewport override is a valid + technique for exact canvas sizing. - id: CQ-05 name: Output & API score: 1 max: 1 passed: true - comment: Saves plot-{THEME}.png and plot-{THEME}.html; current Bokeh API + comment: Saves plot-{THEME}.png and plot-{THEME}.html. Current Bokeh API used + throughout. library_mastery: - score: 8 + score: 7 max: 10 items: - id: LM-01 name: Idiomatic Usage - score: 5 + score: 4 max: 5 passed: true - comment: ColumnDataSource, figure(), scatter(), HoverTool via add_tools(), - export_png+output_file+save — all canonical Bokeh patterns + comment: 'ColumnDataSource, figure() with min_border, Selenium screenshot + — all idiomatic per bokeh.md. Minor: window-size argument uses H+200 with + CDP override rather than the canonical set_window_size(W, H).' - id: LM-02 name: Distinctive Features score: 3 max: 5 passed: true - comment: HoverTool with custom tooltips is Bokeh-distinctive; dual PNG+HTML - export leverages interactive-library nature + comment: HoverTool with formatted tooltips and HTML export are Bokeh's key + differentiators, both correctly implemented. verdict: APPROVED impl_tags: - dependencies: [] + dependencies: + - selenium techniques: - hover-tooltips - html-export