{"spec_id":"parliament-basic","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\nparliament-basic: Parliament Seat Chart\nLibrary: pygal 3.1.0 | Python 3.13.13\nQuality: 82/100 | Created: 2026-05-17\n\"\"\"\n\nimport math\nimport os\n\n# Import pygal using absolute path to avoid shadowing\nimport sys\n\n\n_saved_path = sys.path[:]\nsys.path = [p for p in sys.path if p not in (\"\", \".\") and \"parliament-basic\" not in p]\ntry:\n    import pygal\n    from pygal.style import Style\nfinally:\n    sys.path = _saved_path\n\n# Theme tokens\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\n# Okabe-Ito palette (first series ALWAYS #009E73)\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\"]\n\n# Parliament data: European Parliament 2024\nparties_data = [\n    {\"name\": \"European People's Party\", \"seats\": 188},\n    {\"name\": \"Progressive Alliance\", \"seats\": 136},\n    {\"name\": \"Renew Europe\", \"seats\": 77},\n    {\"name\": \"Greens/EFA\", \"seats\": 53},\n    {\"name\": \"European Conservatives\", \"seats\": 78},\n    {\"name\": \"Left Group\", \"seats\": 37},\n]\n\n\ndef parliament_layout(parties, num_rows=10):\n    \"\"\"Generate semicircular parliament seat positions in concentric arcs\"\"\"\n    seats_list = []\n    total_seats = sum(p[\"seats\"] for p in parties)\n\n    current_seat = 0\n    for party_idx, party in enumerate(parties):\n        for _ in range(party[\"seats\"]):\n            # Angle within semicircle (0 to π)\n            angle = (current_seat / max(total_seats - 1, 1)) * math.pi\n\n            # Row/arc number based on seat position\n            row = min(current_seat // (total_seats // num_rows), num_rows - 1)\n            radius = 40 + row * 11\n\n            # Convert to Cartesian coordinates\n            x = radius * math.cos(angle)\n            y = radius * math.sin(angle)\n\n            seats_list.append({\"x\": x, \"y\": y, \"party_idx\": party_idx, \"party\": party[\"name\"]})\n\n            current_seat += 1\n\n    return seats_list\n\n\n# Create custom style\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=tuple(IMPRINT),\n    title_font_size=28,\n    label_font_size=18,\n    major_label_font_size=16,\n    legend_font_size=16,\n)\n\n# Create XY scatter chart for parliament layout\n# Use range=custom to fit all data, and show_dots to enable dot rendering\nchart = pygal.XY(\n    width=4800,\n    height=2700,\n    style=custom_style,\n    title=\"parliament-basic · pygal · anyplot.ai\",\n    show_legend=True,\n    dots_size=7,\n    show_dots=True,\n    implicit_units=False,\n    range=(0, 100),\n)\n\n# Disable stroke (lines) between points\nchart.stroke = False\n\n# Generate seat positions\nseats = parliament_layout(parties_data)\n\n# Add data by party (each party is a separate series with individual points)\nfor party_idx, party in enumerate(parties_data):\n    party_seats = [s for s in seats if s[\"party_idx\"] == party_idx]\n    data = [(s[\"x\"], s[\"y\"]) for s in party_seats]\n    chart.add(f\"{party['name']} ({party['seats']})\", data)\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"}