diff --git a/plots/radar-basic/implementations/python/pygal.py b/plots/radar-basic/implementations/python/pygal.py index 25ceba2afc..fbb0fbc1cb 100644 --- a/plots/radar-basic/implementations/python/pygal.py +++ b/plots/radar-basic/implementations/python/pygal.py @@ -1,7 +1,7 @@ """ anyplot.ai radar-basic: Basic Radar Chart -Library: pygal 3.1.0 | Python 3.13.13 -Quality: 90/100 | Updated: 2026-04-29 +Library: pygal 3.1.3 | Python 3.13.14 +Quality: 93/100 | Updated: 2026-07-24 """ import importlib @@ -20,6 +20,7 @@ PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17" INK = "#1A1A17" if THEME == "light" else "#F0EFE8" INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" +INK_FAINT = "rgba(107, 106, 99, 0.35)" if THEME == "light" else "rgba(168, 167, 159, 0.35)" IMPRINT = ("#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030", "#2ABCCD", "#954477") @@ -31,44 +32,79 @@ employee_b = [80, 68, 92, 82, 88, 74] # Team Leader: peaks at Teamwork & Leadership employee_c = [72, 76, 70, 74, 85, 95] # Creative Visionary: peaks at Creativity & Leadership -# Style — font sizes corrected to pixel-based minimums for 3600px canvas +# Highest overall average becomes the visual "hero" series — bolder stroke and +# larger dots build a focal point so the viewer immediately spots the top +# all-round performer, while the other two stay legible but recede slightly. +series = [ + ("Employee A — Technical Expert", employee_a), + ("Employee B — Team Leader", employee_b), + ("Employee C — Creative Visionary", employee_c), +] +hero_label, _ = max(series, key=lambda s: sum(s[1]) / len(s[1])) + +title = "radar-basic · python · pygal · anyplot.ai" + +# Style — native-pixel font sizes targeting ~67 source-px title height at the +# canonical 2400x2400 square canvas (see default-style-guide.md "Proportional +# Sizing"); title is well under the 67-char baseline so no length scaling needed. +# Radial gridlines get a two-tier treatment (faint dotted minor rings, slightly +# firmer dashed major rings every 2nd label) instead of pygal's flat default — +# a subtler ring rhythm beyond the mandated style-guide baseline. custom_style = Style( + font_family='"Liberation Sans", "DejaVu Sans", Arial, sans-serif', background=PAGE_BG, plot_background=PAGE_BG, foreground=INK, foreground_strong=INK, foreground_subtle=INK_MUTED, colors=IMPRINT, - title_font_size=28, - label_font_size=22, # axis category labels: minimum 22px - major_label_font_size=18, # major labels: minimum 18px - legend_font_size=18, - value_font_size=16, # spoke value labels: minimum 16px - opacity=0.25, - opacity_hover=0.6, + title_font_size=66, + label_font_size=56, + major_label_font_size=44, + legend_font_size=44, + value_font_size=36, + opacity=0.22, + opacity_hover=0.55, + stroke_opacity=0.9, + guide_stroke_color=INK_FAINT, + guide_stroke_dasharray="2,6", + major_guide_stroke_color=INK_MUTED, + major_guide_stroke_dasharray="9,4", ) # Plot chart = pygal.Radar( - width=3600, - height=3600, + width=2400, + height=2400, style=custom_style, - title="radar-basic · pygal · anyplot.ai", + title=title, show_legend=True, legend_at_bottom=True, - legend_at_bottom_columns=3, + legend_at_bottom_columns=1, fill=True, - dots_size=8, - stroke_style={"width": 5, "linecap": "round", "linejoin": "round"}, + dots_size=5, + stroke_style={"width": 3, "linecap": "round", "linejoin": "round", "dasharray": "14,6"}, show_y_guides=True, y_labels_major_every=2, range=(0, 100), + margin=30, + spacing=16, ) chart.x_labels = categories -chart.add("Employee A — Technical Expert", employee_a) -chart.add("Employee B — Team Leader", employee_b) -chart.add("Employee C — Creative Visionary", employee_c) +for label, values in series: + is_hero = label == hero_label + # Hero series reads as a bold, solid focal line; supporting series recede + # behind a thinner dashed stroke and smaller dots — a wider gap than a flat + # stroke-width bump alone so the top performer reads at a glance. + chart.add( + label, + values, + dots_size=11 if is_hero else 5, + stroke_style={"width": 7.5, "linecap": "round", "linejoin": "round"} + if is_hero + else {"width": 3, "linecap": "round", "linejoin": "round", "dasharray": "14,6"}, + ) # Save chart.render_to_png(f"plot-{THEME}.png") diff --git a/plots/radar-basic/metadata/python/pygal.yaml b/plots/radar-basic/metadata/python/pygal.yaml index bf17625b35..ae17f1291f 100644 --- a/plots/radar-basic/metadata/python/pygal.yaml +++ b/plots/radar-basic/metadata/python/pygal.yaml @@ -2,49 +2,59 @@ library: pygal language: python specification_id: radar-basic created: '2025-12-23T18:11:43Z' -updated: '2026-04-29T17:19:42Z' +updated: '2026-07-24T20:31:10Z' generated_by: claude-sonnet -workflow_run: 25121778506 +workflow_run: 30107728888 issue: 744 -python_version: 3.13.13 -library_version: 3.1.0 +language_version: 3.13.14 +library_version: 3.1.3 preview_url_light: https://storage.googleapis.com/anyplot-images/plots/radar-basic/python/pygal/plot-light.png preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/radar-basic/python/pygal/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/radar-basic/python/pygal/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/radar-basic/python/pygal/plot-dark.html -quality_score: 90 +quality_score: 93 review: strengths: - - Perfect spec compliance across all four criteria — fill, gridlines, labels, multi-series - with legend, correct axis count and scale - - 'Flawless palette and theme compliance: Okabe-Ito in canonical order, correct - warm backgrounds (#FAF8F1 light / #1A1A17 dark), fully adaptive chrome in both - renders' - - 'Thoughtful data design: three archetypal employee profiles (Technical Expert, - Team Leader, Creative Visionary) create immediately readable contrast across 6 - competency axes' - - Rounded stroke styling (linecap/linejoin round, width=5) adds visible polish to - polygon outlines - - Idiomatic pygal usage including distinctive interactive HTML export alongside - the PNG + - Two-tier radial gridline treatment (faint dotted minor rings at 20/60, slightly + firmer dashed major rings at 40/80/100) is a genuine custom design detail beyond + the mandated style-guide baseline, visually confirmed in both renders + - 'Hero-vs-supporting series contrast now reads immediately: Employee A (hero, highest + average) uses a solid 7.5px stroke with 11px dots, while both other series use + a dashed 3px stroke (dasharray 14,6) with 5px dots — verified by pixel-level crop + inspection' + - 'Correct Imprint categorical palette in canonical order (#009E73 / #C475FD / #4467A3), + identical data colors between light and dark renders, fully theme-adaptive chrome + with no dark-on-dark or light-on-light failures' + - Canvas is exactly 2400x2400 (square target, no canvas-gate flag raised); no edge + clipping of title, axis labels, or legend in either render + - Clean, deterministic KISS code structure with hardcoded literal data and the importlib + guard against the file shadowing the installed pygal package + - Realistic, neutral employee-competency-review scenario with genuinely differentiated + per-axis profiles (technical expert vs. team leader vs. creative visionary), matching + a spec-suggested application + - Title and legend both match the mandated format and the series data exactly weaknesses: - - Design sophistication stays at the well-configured-library-default tier (DE-01=4/8); - no typographic or layout refinement beyond pygal defaults for grid or background - treatment - - Axis label font sizes sit at the style-guide minimum (22px) rather than comfortably - above it — borderline at full 3600px resolution + - 'LM-02: still no use of pygal''s more distinctive display features (e.g. print_values=True + for on-chart value labels, custom value_formatter) — the dash-pattern differentiation + is a nice touch but the overall feature set used remains fairly conventional idiomatic + pygal.Radar usage' + - Non-hero dots (dots_size=5) are now a touch small in absolute terms on the 2400x2400 + canvas — legible but could be nudged up 1-2px without giving up the hero/non-hero + size gap + - Value ranges (68-95) stay fairly compressed across all three employees — a slightly + wider spread on 1-2 axes would sharpen the multi-series comparison further image_description: |- Light render (plot-light.png): - Background: Warm off-white (#FAF8F1) — correct theme surface, not pure white - Chrome: Title "radar-basic · pygal · anyplot.ai" is dark and readable; 6 axis category labels (Communication, Technical Skills, Teamwork, Problem Solving, Leadership, Creativity) are dark and readable at the sizes provided; grid ring labels (20/40/60/80/100) visible and legible; legend at bottom in 3 columns with dark text — all readable - Data: First series (Employee A) in brand green #009E73; second (Employee B) in vermillion #D55E00; third (Employee C) in blue #0072B2 — correct Okabe-Ito canonical order; filled polygons with opacity=0.25; vertex dots visible - Legibility verdict: PASS + Background: Warm off-white, consistent with #FAF8F1 — not pure white, not dark. + Chrome: Title "radar-basic · python · pygal · anyplot.ai" at top in dark ink, clearly legible. Six axis category labels (Communication, Creativity, Leadership, Problem Solving, Teamwork, Technical Skills) sit at the outer edge of each spoke in dark, readable text of similar size. Radial scale labels (0/20/40/60/80/100) run down the top (Communication) spoke in a slightly smaller, muted dark tone. Gridlines now use a two-tier treatment: faint dotted minor rings (20, 60) and slightly firmer dashed major rings (40, 80, 100) — a subtler rhythm than a flat default grid. Legend sits below the chart in a single column with three entries and small color swatches, fully readable with generous spacing from the chart, not clipped. + Data: Three overlapping filled polygons with ~22% opacity fill. Employee A — Technical Expert (hero series, highest average score) is brand green #009E73 with a solid, bold 7.5px stroke and large 11px dots. Employee B — Team Leader (#C475FD, lavender) and Employee C — Creative Visionary (#4467A3, blue) both use a thinner dashed 3px stroke and smaller 5px dots, visually receding behind the hero series. All three are clearly distinguishable from each other and from the background; verified at pixel level that the hero solid line contrasts with the two dashed non-hero lines. + Legibility verdict: PASS — all text is readable against the light background, no overlap between text elements or with data, no clipping at canvas edges. Dark render (plot-dark.png): - Background: Warm near-black (#1A1A17) — correct dark theme surface, not pure black - Chrome: Title renders in light warm-white text — readable; all 6 axis category labels render in light text against the dark surface — no dark-on-dark failures detected; grid ring labels in light muted text — readable; legend text in light tone — readable - Data: Colors identical to light render — green #009E73, orange #D55E00, blue #0072B2 — only chrome flips between themes, data colors stay constant - Legibility verdict: PASS + Background: Warm near-black, consistent with #1A1A17 — not pure black, not light. + Chrome: Same title, axis category labels, radial scale labels, and two-tier gridline treatment as the light render, flipped to light/off-white ink — clearly legible against the dark background, no dark-on-dark failures observed anywhere (title, category labels, tick labels, legend text all light-colored). + Data: Identical colors and stroke/dot treatment to the light render — brand green #009E73 hero series (solid, bold), lavender #C475FD and blue #4467A3 non-hero series (dashed, smaller dots) — confirming only chrome flipped between themes, not data colors or styling. + Legibility verdict: PASS — all text is readable against the dark background, no dark-on-dark issues, data colors and hero/non-hero styling match the light render exactly, no clipping at canvas edges. criteria_checklist: visual_quality: score: 29 @@ -55,78 +65,77 @@ review: score: 7 max: 8 passed: true - comment: All font sizes explicitly set (title=28, labels=22, major_labels=18, - legend=18, value=16); readable in both themes; minus 1 for sitting at style-guide - minimum rather than comfortably above + comment: All font sizes explicitly set via Style; readable in both themes + at full size, well-proportioned title - id: VQ-02 name: No Overlap score: 6 max: 6 passed: true - comment: No text collisions; legend at bottom does not overlap chart area + comment: No text overlap with other text or data in either render - id: VQ-03 name: Element Visibility score: 6 max: 6 passed: true - comment: Filled polygons with opacity=0.25 give good overlap visibility; dots_size=8 - vertex markers clearly visible; stroke_width=5 crisp outlines + comment: Hero series (larger dots, solid thick stroke) clearly distinguishable + from dashed, smaller-dot non-hero series; sparse 18-point dataset reads + clearly - id: VQ-04 name: Color Accessibility score: 2 max: 2 passed: true - comment: Okabe-Ito palette is CVD-safe; three series well-distinguished by - hue and luminance; fill transparency aids differentiation + comment: CVD-safe Imprint palette, alpha fill keeps overlaps distinguishable, + hero/non-hero also differ by stroke style and size, not hue alone - id: VQ-05 name: Layout & Canvas score: 4 max: 4 passed: true - comment: Square 3600x3600 correct for symmetric radar; chart fills ~65-70% - of canvas with balanced margins; legend well-placed at bottom + comment: Canvas is exactly 2400x2400; chart fills a large, balanced portion + with legend close below; no cut-off content - id: VQ-06 name: Axis Labels & Title score: 2 max: 2 passed: true - comment: Category labels are descriptive; title matches required format radar-basic - · pygal · anyplot.ai + comment: Descriptive competency-axis labels; 0-100 radial scale provides unit + context - id: VQ-07 name: Palette Compliance score: 2 max: 2 passed: true - comment: 'First series #009E73; canonical Okabe-Ito order; backgrounds #FAF8F1 - light / #1A1A17 dark; text chrome adapts correctly in both renders' + comment: 'Canonical Imprint order, first series #009E73, identical data colors + across themes, theme-correct backgrounds' design_excellence: - score: 12 + score: 16 max: 20 items: - id: DE-01 name: Aesthetic Sophistication - score: 4 + score: 6 max: 8 - passed: false - comment: Well-configured library default with correct palette and theme tokens; - rounded stroke linecaps/linejoin is deliberate polish; does not reach publication-ready - sophistication + passed: true + comment: Two-tier dotted/dashed radial gridline treatment and hero-vs-dashed-non-hero + line styling are genuine custom design choices beyond the mandated style-guide + baseline - id: DE-02 name: Visual Refinement - score: 4 + score: 5 max: 6 - passed: false - comment: stroke_style with round linecap/linejoin, dots_size=8, fill opacity=0.25, - legend_at_bottom_columns=3, y_labels_major_every=2 show intentional refinement; - grid itself is standard pygal output + passed: true + comment: Subtler two-tone gridline rhythm, alpha-blended fills, generous whitespace + and legend spacing show clear refinement beyond raw defaults - id: DE-03 name: Data Storytelling - score: 4 + score: 5 max: 6 - passed: false - comment: Three archetypal employee profiles clearly differentiated; viewer - immediately sees which archetype excels where; no additional visual emphasis - layer + passed: true + comment: Hero-series emphasis is now sharp (solid thick line + large dots + vs. dashed thin lines + small dots for the others), giving an immediate + focal point spec_compliance: score: 15 max: 15 @@ -136,53 +145,53 @@ review: score: 5 max: 5 passed: true - comment: pygal.Radar used correctly + comment: Correct radar/spider chart via pygal.Radar - id: SC-02 name: Required Features score: 4 max: 4 passed: true - comment: Fill with transparency, gridlines, axis labels, 3 series with legend, - polygon closure all present + comment: Filled polygons with transparency, regular gridlines, outer-edge + axis labels, distinct colors + legend, closed polygons all present - id: SC-03 name: Data Mapping score: 3 max: 3 passed: true - comment: 6 axes (within 4-8 spec), 3 series (within 1-3 spec), 0-100 scale + comment: Categories correctly mapped to axes, all 6 axes show all 3 series' + data - id: SC-04 name: Title & Legend score: 3 max: 3 passed: true - comment: Title exactly radar-basic · pygal · anyplot.ai; legend labels descriptive - and match data + comment: Title matches mandated format exactly; legend labels match series + data data_quality: - score: 15 + score: 14 max: 15 items: - id: DQ-01 name: Feature Coverage - score: 6 + score: 5 max: 6 passed: true - comment: 'All key radar chart aspects: multiple overlapping filled series, - varied axis profiles, full 0-100 scale utilization, transparent fills for - overlap visibility' + comment: Shows meaningfully different profiles per employee across axes; ranges + could be slightly wider for maximal contrast - id: DQ-02 name: Realistic Context score: 5 max: 5 passed: true - comment: Employee performance review is the first listed spec application; - three archetypes are realistic, neutral, and comprehensible + comment: Neutral, real-world employee performance review scenario directly + matching a spec-suggested application - id: DQ-03 name: Appropriate Scale score: 4 max: 4 passed: true - comment: Scores 68-95 plausible for competent employees; each archetype peak - aligns logically with its description + comment: Values (68-95) are realistic and sensible for a 0-100 competency + scale code_quality: score: 10 max: 10 @@ -192,33 +201,33 @@ review: score: 3 max: 3 passed: true - comment: 'Linear flow: path-fix, imports, tokens, data, style, chart, save; - no functions or classes' + comment: Imports -> data -> style -> plot -> save, no functions/classes - id: CQ-02 name: Reproducibility score: 2 max: 2 passed: true - comment: All data hardcoded; fully deterministic; no random numbers + comment: Fully deterministic hardcoded data, no randomness - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: Only os, sys, importlib, and pygal; all used + comment: Only necessary imports; importlib/os/sys workaround needed to avoid + self-shadowing the pygal package - id: CQ-04 name: Code Elegance score: 2 max: 2 passed: true - comment: importlib/sys.path workaround for filename shadowing is necessary - and well-scoped; no fake functionality + comment: Clean, appropriately complex, no fake functionality - id: CQ-05 name: Output & API score: 1 max: 1 passed: true - comment: Saves plot-{THEME}.png and plot-{THEME}.html; current pygal API + comment: Saves plot-{THEME}.png and plot-{THEME}.html via current render_to_png/render() + API library_mastery: score: 9 max: 10 @@ -228,23 +237,24 @@ review: score: 5 max: 5 passed: true - comment: Uses pygal.Radar with Style, x_labels, add(), render_to_png(), render() - — all idiomatic pygal patterns + comment: 'Idiomatic pygal.Radar + Style usage: x_labels, add(), legend_at_bottom, + dots_size, stroke_style, major/minor guide styling' - id: LM-02 name: Distinctive Features score: 4 max: 5 passed: true - comment: pygal.Radar built-in, interactive HTML export, stroke_style dict, - legend_at_bottom_columns, y_labels_major_every, dots_size — broad range - of distinctive features + comment: Creative use of per-series stroke_style dasharray plus major/minor + guide dasharray for storytelling; still no standout feature like print_values verdict: APPROVED impl_tags: dependencies: [] techniques: + - polar-projection - html-export patterns: - - data-generation + - iteration-over-groups dataprep: [] styling: - alpha-blending + - grid-styling