{"spec_id":"count-basic","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\ncount-basic: Basic Count Plot\nLibrary: pygal 3.1.3 | Python 3.13.14\nQuality: 89/100 | Updated: 2026-08-11\n\"\"\"\n\nimport os\nimport sys\nfrom collections import Counter\n\n\n# Work around naming conflict: pygal.py filename shadows pygal package\nsys.path.pop(0)\n\nimport pygal\nfrom pygal.style import Style\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\nIMPRINT = (\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\")\n\n\n# Semantic anchors reused for the sentiment scale below (Imprint palette\n# \"Semantic exception\": positive -> green, negative -> red, neutral -> muted).\n# Each polarity gets two shades so the two intensity levels (\"Dissatisfied\"\n# vs \"Very Dissatisfied\", \"Satisfied\" vs \"Very Satisfied\") stay visually\n# distinguishable while keeping the hue. The lighter shade is a precomputed\n# *opaque* hex (Imprint hex blended 60% over white) rather than an rgba()\n# with embedded alpha -- an alpha-carrying fill would composite against\n# plot_background, which flips between themes and breaks palette identity\n# (VQ-07); the full-strength hex is reserved for the \"Very\" (most intense)\n# category.\nPOSITIVE = \"#66C5AB\"  # brand green blended 60% over white, \"Satisfied\"\nPOSITIVE_STRONG = IMPRINT[0]  # full-strength brand green, \"Very Satisfied\"\nNEGATIVE = \"#CE8383\"  # matte red blended 60% over white, \"Dissatisfied\"\nNEGATIVE_STRONG = IMPRINT[4]  # full-strength matte red, \"Very Dissatisfied\"\n# Fixed muted gray (average of the light/dark INK_MUTED tones) rather than\n# the theme-adaptive INK_MUTED itself: this is a *data* series color, so it\n# must stay palette-identical across themes like the other four bars (VQ-07)\n# -- only chrome (title, ticks, gridlines) is allowed to flip with the theme.\nNEUTRAL = \"#8A8981\"\n\n# Data - Survey responses from customer feedback\nresponses = [\n    \"Satisfied\",\n    \"Very Satisfied\",\n    \"Satisfied\",\n    \"Neutral\",\n    \"Dissatisfied\",\n    \"Very Satisfied\",\n    \"Satisfied\",\n    \"Satisfied\",\n    \"Very Satisfied\",\n    \"Neutral\",\n    \"Satisfied\",\n    \"Neutral\",\n    \"Very Satisfied\",\n    \"Satisfied\",\n    \"Very Satisfied\",\n    \"Dissatisfied\",\n    \"Satisfied\",\n    \"Very Satisfied\",\n    \"Neutral\",\n    \"Satisfied\",\n    \"Very Satisfied\",\n    \"Satisfied\",\n    \"Satisfied\",\n    \"Very Dissatisfied\",\n    \"Satisfied\",\n    \"Neutral\",\n    \"Very Satisfied\",\n    \"Satisfied\",\n    \"Dissatisfied\",\n    \"Satisfied\",\n    \"Very Satisfied\",\n    \"Satisfied\",\n    \"Neutral\",\n    \"Satisfied\",\n    \"Very Satisfied\",\n    \"Satisfied\",\n    \"Very Satisfied\",\n    \"Neutral\",\n    \"Dissatisfied\",\n    \"Satisfied\",\n    \"Very Satisfied\",\n    \"Satisfied\",\n    \"Satisfied\",\n    \"Very Satisfied\",\n    \"Neutral\",\n    \"Satisfied\",\n    \"Dissatisfied\",\n    \"Very Satisfied\",\n    \"Satisfied\",\n    \"Very Satisfied\",\n]\n\n# Count occurrences\ncounts = Counter(responses)\ntotal = len(responses)\n\n# Define category order (logical satisfaction order) and its sentiment color\ncategory_order = [\"Very Dissatisfied\", \"Dissatisfied\", \"Neutral\", \"Satisfied\", \"Very Satisfied\"]\ncategory_sentiment = {\n    \"Very Dissatisfied\": NEGATIVE_STRONG,\n    \"Dissatisfied\": NEGATIVE,\n    \"Neutral\": NEUTRAL,\n    \"Satisfied\": POSITIVE,\n    \"Very Satisfied\": POSITIVE_STRONG,\n}\n\n# Custom style, sized for the 3200x1800 canvas (see prompts/library/pygal.md\n# \"Sizing + Theme\" - unitless pygal sizes map ~1:1 onto source pixels)\ncustom_style = Style(\n    background=PAGE_BG,\n    plot_background=PAGE_BG,\n    foreground=INK,\n    foreground_strong=INK,\n    foreground_subtle=INK_MUTED,\n    colors=IMPRINT,\n    # Pin fill opacity so literal-hex bars render at full strength instead of\n    # compositing against plot_background (which flips between themes and\n    # would otherwise break palette identity across light/dark, VQ-07).\n    opacity=\"1\",\n    # Print-value text otherwise defaults to black/white per bar-fill\n    # luminance (pygal's Style.value_colors heuristic) rather than the\n    # theme-adaptive ink used everywhere else -- pin it to INK so the counts\n    # stay legible against the plot background in both themes.\n    value_colors=(INK,),\n    title_font_size=66,\n    label_font_size=56,\n    major_label_font_size=44,\n    legend_font_size=44,\n    value_font_size=36,\n    tooltip_font_size=32,\n    stroke_width=2.5,\n    # Style guide requires solid (not dashed) y-guides at low opacity;\n    # pygal defaults to a dashed stroke, so force it off here.\n    guide_stroke_dasharray=\"none\",\n    major_guide_stroke_dasharray=\"none\",\n)\n\n# Create chart\nchart = pygal.Bar(\n    width=3200,\n    height=1800,\n    style=custom_style,\n    title=\"count-basic · python · pygal · anyplot.ai\",\n    x_title=\"Satisfaction Level\",\n    y_title=\"Number of Responses\",\n    show_legend=False,\n    show_y_guides=True,\n    show_x_guides=False,\n    print_values=True,\n    print_values_position=\"top\",\n    value_formatter=lambda x: str(int(x)),\n    rounded_bars=8,\n    margin=50,\n    spacing=60,\n    x_label_rotation=30,\n)\n\n# Set x-axis labels\nchart.x_labels = category_order\n\n# Advanced pygal technique: per-bar metadata gives each category a sentiment\n# color (Imprint semantic exception: positive->green, negative->red,\n# neutral->muted) and a hover tooltip with the response share, instead of a\n# single flat series color.\nchart.add(\n    \"Responses\",\n    [\n        {\n            \"value\": counts.get(cat, 0),\n            \"color\": category_sentiment[cat],\n            \"tooltip\": f\"{cat}: {counts.get(cat, 0)} responses ({counts.get(cat, 0) / total:.0%})\",\n        }\n        for cat in category_order\n    ],\n)\n\n# Save outputs\nchart.render_to_png(f\"plot-{THEME}.png\")\nwith open(f\"plot-{THEME}.html\", \"wb\") as f:\n    f.write(chart.render())\n"}