diff --git a/plots/venn-labeled-items/implementations/python/pygal.py b/plots/venn-labeled-items/implementations/python/pygal.py index de0b7fa785..56abedaa1f 100644 --- a/plots/venn-labeled-items/implementations/python/pygal.py +++ b/plots/venn-labeled-items/implementations/python/pygal.py @@ -1,7 +1,7 @@ """ anyplot.ai venn-labeled-items: Chartgeist-Style Venn Diagram with Labeled Items -Library: pygal 3.1.0 | Python 3.14.4 -Quality: 86/100 | Created: 2026-04-25 +Library: pygal 3.1.3 | Python 3.13.14 +Quality: 84/100 | Updated: 2026-06-25 """ import importlib @@ -26,10 +26,10 @@ INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" -# Okabe-Ito categorical palette: brand green, vermillion, blue -COLOR_A = "#009E73" -COLOR_B = "#C475FD" -COLOR_C = "#4467A3" +# Imprint palette — positions 1–3 +COLOR_A = "#009E73" # brand green +COLOR_B = "#C475FD" # lavender +COLOR_C = "#4467A3" # blue # Symmetric three-circle Venn: equilateral triangle of centers (apex up) RADIUS = 1.0 @@ -39,45 +39,54 @@ cx, cy = 0.0, OFFSET circles = [ - {"name": "OVERHYPED", "color": COLOR_A, "center": (ax, ay), "label_xy": (ax - 0.95, ay - 1.10), "anchor": "start"}, { - "name": "ACTUALLY USEFUL", + "name": "TREND REPORT", + "color": COLOR_A, + "center": (ax, ay), + "label_xy": (ax - 0.90, ay - 1.05), + "anchor": "start", + }, + { + "name": "WARDROBE STAPLE", "color": COLOR_B, "center": (bx, by), - "label_xy": (bx + 0.95, by - 1.10), + "label_xy": (bx + 0.90, by - 1.05), "anchor": "end", }, - {"name": "SECRETLY LOVED", "color": COLOR_C, "center": (cx, cy), "label_xy": (cx, cy + 1.18), "anchor": "middle"}, + {"name": "GUILTY CLOSET", "color": COLOR_C, "center": (cx, cy), "label_xy": (cx, cy + 1.12), "anchor": "middle"}, ] -# Items distributed across the seven interior zones +# Fashion micro-trends distributed across the seven interior zones items_raw = [ - ("NFTs", "A"), - ("Metaverse", "A"), - ("Web3", "A"), - ("Google Maps", "B"), - ("Sticky Notes", "B"), - ("USB Hubs", "B"), - ("Karaoke", "C"), - ("Postcards", "C"), - ("Smartphones", "AB"), - ("Email", "AB"), - ("Crocs", "AC"), - ("Pumpkin Spice", "AC"), - ("Spotify", "BC"), - ("Dolly Parton", "BC"), - ("Sourdough", "ABC"), - ("TikTok", "ABC"), + ("Shackets", "A"), + ("Digital Fashion", "A"), + ("Micro Bags", "A"), + ("White Sneakers", "B"), + ("Trench Coats", "B"), + ("Good Denim", "B"), + ("Fast Fashion", "C"), + ("Matching Sets", "C"), + ("Oversized Blazers", "AB"), + ("Straight-Leg Jeans", "AB"), + ("Cottagecore", "AC"), + ("Tie-Dye", "AC"), + ("Minimalist Sneakers", "BC"), + ("Linen Separates", "BC"), + ("Quiet Luxury", "ABC"), + ("Ballet Flats", "ABC"), ] +# ABC zone items that receive bold emphasis (most editorially interesting intersection) +ABC_ITEMS = {"Quiet Luxury", "Ballet Flats"} + # Centroids of each Venn region in chart-data units zone_centers = { - "A": (ax - 0.55, ay + 0.05), - "B": (bx + 0.55, by + 0.05), - "C": (cx, cy + 0.50), - "AB": (0.0, by - 0.32), - "AC": (-0.45, 0.20), - "BC": (0.45, 0.20), + "A": (ax - 0.52, ay + 0.05), + "B": (bx + 0.52, by + 0.05), + "C": (cx, cy + 0.48), + "AB": (0.0, by - 0.30), + "AC": (-0.43, 0.18), + "BC": (0.43, 0.18), "ABC": (0.0, -0.05), } @@ -95,13 +104,11 @@ item_points.append({"value": (zx, start_y - i * LINE_HEIGHT), "label": label}) -# Parametric points for a circle outline (closed polyline) def circle_outline(center, r, n=120): cx0, cy0 = center return [(cx0 + r * math.cos(2 * math.pi * i / n), cy0 + r * math.sin(2 * math.pi * i / n)) for i in range(n + 1)] -# Style — derived from theme tokens custom_style = Style( background=PAGE_BG, plot_background=PAGE_BG, @@ -111,15 +118,15 @@ def circle_outline(center, r, n=120): colors=(COLOR_A, COLOR_B, COLOR_C, INK, INK, INK, INK), opacity="1", opacity_hover="1", - stroke_width=6, + stroke_width=5, stroke_opacity=".90", stroke_opacity_hover=".90", - title_font_size=72, + title_font_size=50, label_font_size=22, major_label_font_size=22, legend_font_size=22, - value_font_size=42, - value_label_font_size=42, + value_font_size=52, + value_label_font_size=52, title_font_family="serif", label_font_family="serif", major_label_font_family="serif", @@ -129,12 +136,12 @@ def circle_outline(center, r, n=120): transition="0", ) -# Plot — square 3600×3600 canvas suits the radial Venn layout +# Canvas: 2400×2400 (square) — canonical pygal square size; tighter range fills more canvas chart = pygal.XY( - width=3600, - height=3600, + width=2400, + height=2400, style=custom_style, - title="Pop Culture Vibes 2026 · venn-labeled-items · pygal · anyplot.ai", + title="Fashion Micro-Trends 2026 · venn-labeled-items · python · pygal · anyplot.ai", show_legend=False, show_x_labels=False, show_y_labels=False, @@ -142,8 +149,8 @@ def circle_outline(center, r, n=120): show_y_guides=False, show_minor_x_labels=False, show_minor_y_labels=False, - xrange=(-2.30, 2.30), - range=(-2.30, 2.30), + xrange=(-2.0, 2.0), + range=(-2.0, 2.0), margin=20, spacing=0, show_dots=True, @@ -153,28 +160,27 @@ def circle_outline(center, r, n=120): pretty_print=True, ) -# Three circle outlines (one series per circle) — fills are added via post-processing +# Three circle outlines (one series per circle) — fills added via SVG post-processing for c in circles: chart.add("", circle_outline(c["center"], RADIUS), stroke=True, fill=False, show_dots=False) # Item names — text-only placement at zone centroids chart.add("Items", item_points, stroke=False, show_dots=True) -# Category names — same labeling mechanism, restyled by post-processor below +# Category names — restyled by post-processor below for c in circles: chart.add("", [{"value": c["label_xy"], "label": c["name"]}], stroke=False, show_dots=True) svg = chart.render().decode("utf-8") -# Post-process — pygal cannot natively (a) fill a closed polyline or (b) per-label -# typography. Both are added directly to the SVG output. +# Post-process: pygal cannot natively fill a closed polyline, so we patch the SVG directly. def fill_circle_path(svg_text, serie_idx, color, opacity): pattern = re.compile( r'(\s*]*?)class="line reactive nofill"' ) return pattern.sub( - r'\1class="line reactive" style="fill:' + color + ";fill-opacity:" + str(opacity) + r';stroke-width:7"', + r'\1class="line reactive" style="fill:' + color + ";fill-opacity:" + str(opacity) + r';stroke-width:6"', svg_text, count=1, ) @@ -184,45 +190,55 @@ def fill_circle_path(svg_text, serie_idx, color, opacity): svg = fill_circle_path(svg, idx, c["color"], 0.18) -# Restyle category labels by matching their text content def restyle_label(svg_text, label_text, color, anchor, font_size): - pattern = re.compile(r'' + re.escape(label_text) + r"") - return pattern.sub( + """Apply bold italic colored style to a category name label element.""" + pattern = re.compile( + r"" + re.escape(label_text) + r"" + ) + repl = ( r'' + + '">' + label_text - + "", - svg_text, - count=1, + + "" ) + return pattern.sub(repl, svg_text, count=1) for c in circles: - svg = restyle_label(svg, c["name"], c["color"], c["anchor"], 64) + svg = restyle_label(svg, c["name"], c["color"], c["anchor"], 56) -# Pygal auto-picks white text whenever a series color is dark — that turns -# the item labels invisible on our cream/charcoal background. Rewrite those -# rules in place so labels inherit the theme INK instead. +# Pygal auto-assigns white text for dark series colors — rewrite so labels use INK instead svg = re.sub(r"(\.text-overlay \.color-\d+ text \{\s*fill:\s*)[^;}\s]+", r"\1" + INK, svg) -# Bump the rendered label size to the 42px set in Style above; pygal's -# CSS hard-codes 36px ignoring `value_label_font_size` for XY plots. -svg = re.sub(r"(\.text-overlay text\.label \{[^}]*font-size:\s*)\d+px", r"\g<1>42px", svg) +# Bump item label size from pygal's hardcoded 36px to the target 52px +svg = re.sub(r"(\.text-overlay text\.label \{[^}]*font-size:\s*)\d+px", r"\g<1>52px", svg) + + +def emphasize_abc(svg_text, item_label): + """Bold ABC triple-intersection items to visually distinguish the most interesting zone.""" + return re.sub( + r"()(" + re.escape(item_label) + r")", + r'\1 style="font-weight:bold;font-size:60px"\2\3', + svg_text, + ) + + +for item in ABC_ITEMS: + svg = emphasize_abc(svg, item) # Editorial subtitle injected at a fixed canvas position, theme-aware subtitle = ( - '' - "A field guide to sixteen things, three feelings, and seven overlapping truths" + '' + "Sixteen micro-trends, three wardrobe moods, and the truth in the overlap" "" ) svg = svg.replace("", subtitle + "") - # Save — interactive SVG embedded in HTML, plus rasterized PNG via cairosvg with open(f"plot-{THEME}.svg", "w") as f: f.write(svg) @@ -230,4 +246,4 @@ def restyle_label(svg_text, label_text, color, anchor, font_size): with open(f"plot-{THEME}.html", "w") as f: f.write("" + svg + "") -cairosvg.svg2png(bytestring=svg.encode("utf-8"), write_to=f"plot-{THEME}.png", output_width=3600, output_height=3600) +cairosvg.svg2png(bytestring=svg.encode("utf-8"), write_to=f"plot-{THEME}.png", output_width=2400, output_height=2400) diff --git a/plots/venn-labeled-items/metadata/python/pygal.yaml b/plots/venn-labeled-items/metadata/python/pygal.yaml index ac567567fe..069791260e 100644 --- a/plots/venn-labeled-items/metadata/python/pygal.yaml +++ b/plots/venn-labeled-items/metadata/python/pygal.yaml @@ -2,52 +2,51 @@ library: pygal language: python specification_id: venn-labeled-items created: '2026-04-25T06:01:58Z' -updated: '2026-04-25T06:07:53Z' -generated_by: claude-opus -workflow_run: 24923831951 +updated: '2026-06-25T12:14:48Z' +generated_by: claude-sonnet +workflow_run: 28166518718 issue: 5364 -python_version: 3.14.4 -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/venn-labeled-items/python/pygal/plot-light.png preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/venn-labeled-items/python/pygal/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/venn-labeled-items/python/pygal/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/venn-labeled-items/python/pygal/plot-dark.html -quality_score: 86 +quality_score: 84 review: strengths: - - 'Perfect spec compliance: all seven zones populated with 16 items correctly placed' - - 'Editorial aesthetic nailed: bold italic serif category labels in their circle - colors, witty subtitle, no axes/grid — genuinely magazine-style' - - Correct Okabe-Ito palette in canonical order with proper theme-adaptive chrome - in both light and dark renders - - SVG post-processing approach elegantly solves pygal's lack of native polygon fills - and per-label typography - - Deterministic, entirely reproducible layout with zero text collisions + - 'Full spec compliance: all 7 Venn zones populated, editorial aesthetic matches + WIRED Chartgeist intent exactly' + - Creative SVG post-processing to achieve semi-transparent circle fills that pygal + cannot do natively + - 'Excellent data quality: real fashion domain, plausible categorizations, ABC emphasis + on most interesting intersection' + - Theme-adaptive chrome correctly applied in both renders (INK tokens, INK_SOFT + subtitle, correct backgrounds) + - Editorial serif typography and bold italic colored category labels create genuine + magazine-print polish weaknesses: - - Empty space below the Venn circle group is large (~35% of canvas below circles) - — zones could be shifted upward or subtitle repositioned to better balance the - canvas - - SVG regex post-processing is fragile (pattern matching against pygal's internal - class naming); minor refactoring would improve robustness - - The ABC triple-intersection zone (Sourdough/TikTok) — the most editorially interesting - region — is not visually differentiated or emphasized from other zones - - Item label font size (42px on 3600x3600) reads slightly small at full resolution; - bumping to 52-56px would improve readability without crowding + - Four helper functions (fill_circle_path, restyle_label, emphasize_abc, circle_outline) + break the KISS linear structure expected for these implementations + - Library Mastery limited because most visual differentiation is achieved via SVG + regex manipulation rather than pygal's native styling API + - Minor potential crowding in AC/BC zones at full resolution; LINE_HEIGHT=0.13 may + be tight when 2+ items occupy a small intersection region image_description: |- Light render (plot-light.png): - Background: Warm off-white #FAF8F1 — correct, not pure white - Chrome: Title "Pop Culture Vibes 2026 · venn-labeled-items · pygal · anyplot.ai" in dark serif at top — clearly readable. Category labels (OVERHYPED, ACTUALLY USEFUL, SECRETLY LOVED) in bold italic serif, colored in their respective Okabe-Ito color — readable. Italic subtitle in muted INK at bottom — readable. - Data: Circle A (OVERHYPED) = #009E73 green at bottom-left; Circle B (ACTUALLY USEFUL) = #D55E00 vermillion at bottom-right; Circle C (SECRETLY LOVED) = #0072B2 blue at top. Semi-transparent fills (~18% opacity). Item labels in dark INK text across all seven zones. All 16 items visible. + Background: Warm off-white (#FAF8F1) — correct + Chrome: Title "Fashion Micro-Trends 2026 · venn-labeled-items · python · pygal · anyplot.ai" wraps to two lines, dark ink readable. Category labels (TREND REPORT/WARDROBE STAPLE/GUILTY CLOSET) in bold italic serif in their respective Imprint colors — all readable. Editorial subtitle in INK_SOFT (#4A4A44) at bottom — readable. + Data: Three overlapping circles with semi-transparent fills at 18% opacity: #009E73 (green/TREND REPORT), #C475FD (lavender/WARDROBE STAPLE), #4467A3 (blue/GUILTY CLOSET). All 16 fashion item labels distributed across 7 Venn zones. "Quiet Luxury" and "Ballet Flats" in ABC zone are bold. First series is correct brand green #009E73. Legibility verdict: PASS — all text readable against light background, no light-on-light issues Dark render (plot-dark.png): - Background: Warm near-black #1A1A17 — correct, not pure black - Chrome: Title in light/off-white text — clearly readable. Category labels retain Okabe-Ito colors (#009E73, #D55E00, #0072B2) — visible against dark background. Subtitle in muted light text — readable. - Data: Circle colors identical to light render (green, vermillion, blue). Fills appear darker due to dark background but circles remain distinguishable. Item labels in light/off-white text — readable over the dark-tinted zone fills. No dark-on-dark failure observed. - Legibility verdict: PASS — all text readable against dark background, no black-text-on-dark issues + Background: Warm near-black (#1A1A17) — correct + Chrome: Title and subtitle readable in light cream (#F0EFE8 INK for dark mode). Category labels retain colored bold italic styling (same Imprint colors) — all visible on dark surface. INK token correctly overrides pygal's CSS .text-overlay .color-N text fill to #F0EFE8. + Data: Circle fill colors identical to light render (#009E73, #C475FD, #4467A3) at 18% opacity over dark background — Imprint data colors unchanged between themes as required. Item labels appear in light cream against dark background. + Legibility verdict: PASS — no dark-on-dark failures observed; INK token application is correct criteria_checklist: visual_quality: - score: 28 + score: 27 max: 30 items: - id: VQ-01 @@ -55,49 +54,52 @@ review: score: 7 max: 8 passed: true - comment: Font sizes explicitly set (title 72px, category labels 64px, items - 42px); all readable in both themes; item labels slightly small for 3600x3600 - canvas + comment: All font sizes explicitly set; title wraps to two lines at 50px (intentional + for long 78-char title); item labels bumped to 52px via SVG; category labels + at 56px. All readable in both themes. - id: VQ-02 name: No Overlap - score: 6 + score: 5 max: 6 passed: true - comment: Items stacked with deliberate vertical spacing within zones; no collisions + comment: Items distributed via vertical stacking with LINE_HEIGHT=0.13; minor + potential crowding in AC/BC zones at full resolution but no hard overlaps + observed. - id: VQ-03 name: Element Visibility - score: 6 + score: 5 max: 6 passed: true - comment: Circles clearly outlined, fills semi-transparent, all 16 item labels - distinctly readable + comment: Text-only labels (dots_size=0) are readable. Circle fills at 0.18 + opacity clearly show overlapping regions. All 16 items visible. - id: VQ-04 name: Color Accessibility score: 2 max: 2 passed: true - comment: Okabe-Ito positions 1-3; CVD-safe; no red-green sole signal + comment: Imprint positions 1-3 are CVD-safe (green/purple/blue perceptually + distinct). Semi-transparent fills allow overlap visibility. - id: VQ-05 name: Layout & Canvas - score: 3 + score: 4 max: 4 passed: true - comment: Diagram fills ~65% of canvas; notable empty space below circles and - above subtitle + comment: Canvas gate passed (2400x2400 square). Venn occupies ~65% of canvas + with balanced margins. Editorial subtitle anchors bottom space well. - id: VQ-06 name: Axis Labels & Title score: 2 max: 2 passed: true - comment: Descriptive title; axes intentionally hidden; category labels serve - as region identifiers + comment: N/A for traditional axes on a Venn. Title has descriptive prefix. + Category labels serve as descriptive region identifiers. - id: VQ-07 name: Palette Compliance score: 2 max: 2 passed: true - comment: 'Circle A = #009E73 (brand green), B = #D55E00, C = #0072B2; backgrounds - #FAF8F1/#1A1A17; chrome flips correctly' + comment: 'Imprint positions 1-3 (#009E73, #C475FD, #4467A3) for three circles. + Background #FAF8F1 light / #1A1A17 dark. INK tokens correctly applied.' design_excellence: score: 14 max: 20 @@ -108,21 +110,23 @@ review: max: 8 passed: true comment: Editorial serif typography, bold italic colored category labels, - witty subtitle — clearly above defaults, WIRED/Chartgeist aesthetic achieved + ABC emphasis, editorial subtitle, gridless/frameless composition show deliberate + design well above defaults. Matches WIRED Chartgeist spec intent. - id: DE-02 name: Visual Refinement score: 4 max: 6 passed: true - comment: No axes/grid/spines; semi-transparent fills; minor deduction for - empty space below diagram and margin imbalance + comment: No grid, no axes, no spines, semi-transparent fills at 0.18 opacity, + INK_SOFT subtitle. Some residual pygal chrome. Generous whitespace. - id: DE-03 name: Data Storytelling score: 4 max: 6 passed: true - comment: Editorial category naming and zone placements feel intentional; loses - points for no visual emphasis on the most interesting ABC intersection + comment: Bold ABC items create focal point. Editorial subtitle provides narrative. + Witty category names create editorial taxonomy matching magazine-commentary + spec intent. spec_compliance: score: 15 max: 15 @@ -132,28 +136,31 @@ review: score: 5 max: 5 passed: true - comment: Three-circle symmetric Venn with labeled items + comment: Correct three-circle symmetric Venn with all seven interior zones + populated. - id: SC-02 name: Required Features score: 4 max: 4 passed: true - comment: Semi-transparent fills, category names outside circles, item labels - in zones, all seven regions populated + comment: Semi-transparent fills, category labels outside circles, items inside + each zone, text-only placement, editorial subtitle, gridless background, + all 7 interior zones used. - id: SC-03 name: Data Mapping score: 3 max: 3 passed: true - comment: All 16 items correctly placed in declared zones (A, B, C, AB, AC, - BC, ABC) + comment: All 16 items correctly placed in assigned zones (A/B/C/AB/AC/BC/ABC) + via zone_centers positioning logic. - id: SC-04 name: Title & Legend score: 3 max: 3 passed: true - comment: Title contains venn-labeled-items · pygal · anyplot.ai; category - labels serve as legend + comment: Title is 'Fashion Micro-Trends 2026 · venn-labeled-items · python + · pygal · anyplot.ai' — correct descriptive prefix + required format. No + legend (appropriate). data_quality: score: 15 max: 15 @@ -163,21 +170,24 @@ review: score: 6 max: 6 passed: true - comment: All 7 interior zones populated; 16 items; demonstrates solo, pairwise, - and triple membership + comment: All seven interior Venn zones populated. ABC emphasis demonstrates + the most interesting intersection. Full coverage of plot type's expressive + range. - id: DQ-02 name: Realistic Context score: 5 max: 5 passed: true - comment: Pop culture, technology, and food items — recognizable, neutral, - editorial-appropriate + comment: Fashion micro-trends 2026 is a real-world neutral domain matching + spec's pop-culture editorial intent. Items are real and relevant. - id: DQ-03 name: Appropriate Scale score: 4 max: 4 passed: true - comment: Set membership is the only scale; all items fit naturally + comment: Category assignments are plausible and editorially defensible (Fast + Fashion → GUILTY CLOSET, White Sneakers → WARDROBE STAPLE, Digital Fashion + → TREND REPORT). code_quality: score: 8 max: 10 @@ -187,36 +197,40 @@ review: score: 2 max: 3 passed: true - comment: Three helper functions break strict KISS; justified by pygal's lack - of native polygon fill and per-label typography API + comment: Four helper functions defined (circle_outline, fill_circle_path, + restyle_label, emphasize_abc). Necessary for SVG post-processing but breaks + pure linear structure. - id: CQ-02 name: Reproducibility score: 2 max: 2 passed: true - comment: Entirely deterministic (hardcoded items and layout geometry) + comment: Data fully hardcoded; deterministic circle geometry via math. No + randomness. - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: 'All imports used: importlib, math, os, re, sys, collections.defaultdict' + comment: All imports used (importlib, math, os, re, sys, defaultdict, pygal, + Style, cairosvg). - id: CQ-04 name: Code Elegance score: 1 max: 2 passed: true - comment: SVG regex post-processing is inherently fragile, dependent on pygal's - internal class naming conventions + comment: SVG post-processing via multiple regex passes is necessary but creates + a complex chain with 5+ separate SVG mutations. Creative given pygal's constraints + but slightly over-engineered. - id: CQ-05 name: Output & API score: 1 max: 1 passed: true - comment: Produces plot-{THEME}.svg, plot-{THEME}.html, plot-{THEME}.png; interactive - HTML correct for pygal + comment: Saves plot-{THEME}.svg, plot-{THEME}.html, and plot-{THEME}.png correctly. + Uses current cairosvg API. library_mastery: - score: 6 + score: 5 max: 10 items: - id: LM-01 @@ -224,21 +238,24 @@ review: score: 3 max: 5 passed: true - comment: Correct use of pygal.XY, pygal.Style with theme tokens, chart.render() - for SVG; significant non-pygal SVG post-processing required + comment: pygal.XY is correct chart type for coordinate-based placement. Style + object used properly. However majority of visual differentiation comes from + SVG post-processing rather than native API. - id: LM-02 name: Distinctive Features - score: 3 + score: 2 max: 5 passed: true - comment: Leverages pygal's SVG output for post-processing; generates interactive - HTML; print_labels=True for in-plot text; cairosvg integration for PNG - verdict: REJECTED + comment: Uses pygal's SVG output nature for post-processing fills and label + restyling — pygal-specific technique. Interactive HTML output is also distinctive. + However most visual work bypasses the library. + verdict: APPROVED impl_tags: dependencies: - cairosvg techniques: - html-export + - annotations patterns: - iteration-over-groups dataprep: []