{"spec_id":"network-hierarchical","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\nnetwork-hierarchical: Hierarchical Network Graph with Tree Layout\nLibrary: pygal 3.1.0 | Python 3.13.13\nQuality: 81/100 | Updated: 2026-05-17\n\"\"\"\n\nimport os\nimport shutil\nimport subprocess\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# Define organizational hierarchy (CEO -> VPs -> Directors -> Managers)\nhierarchy = {\n    0: (\"CEO\", 0, None),\n    1: (\"VP Eng\", 1, 0),\n    2: (\"VP Sales\", 1, 0),\n    3: (\"VP Ops\", 1, 0),\n    4: (\"Dir FE\", 2, 1),\n    5: (\"Dir BE\", 2, 1),\n    6: (\"Dir DevOps\", 2, 1),\n    7: (\"Dir Americas\", 2, 2),\n    8: (\"Dir EMEA\", 2, 2),\n    9: (\"Dir Logistics\", 2, 3),\n    10: (\"Dir HR\", 2, 3),\n    11: (\"Mgr React\", 3, 4),\n    12: (\"Mgr Vue\", 3, 4),\n    13: (\"Mgr API\", 3, 5),\n    14: (\"Mgr DB\", 3, 5),\n    15: (\"Mgr Cloud\", 3, 6),\n    16: (\"Mgr NA\", 3, 7),\n    17: (\"Mgr LATAM\", 3, 7),\n    18: (\"Mgr UK\", 3, 8),\n    19: (\"Mgr DE\", 3, 8),\n    20: (\"Mgr Supply\", 3, 9),\n    21: (\"Mgr Talent\", 3, 10),\n}\n\n# Group nodes by level\nlevels = {}\nfor node_id, (_label, level, _parent) in hierarchy.items():\n    if level not in levels:\n        levels[level] = []\n    levels[level].append(node_id)\n\n# Calculate node positions using tree layout\nnode_positions = {}\nmax_level = max(levels.keys())\ny_min, y_max = 10, 90\ny_spacing = (y_max - y_min) / max_level\n\nfor level, nodes in levels.items():\n    num_nodes = len(nodes)\n    x_spacing = 90 / (num_nodes + 1)\n    for i, node_id in enumerate(nodes):\n        x = 5 + (i + 1) * x_spacing\n        y = y_max - (level * y_spacing)\n        node_positions[node_id] = (x, y)\n\n# Level colors using Okabe-Ito palette\nlevel_colors = [IMPRINT[0], IMPRINT[1], IMPRINT[2], IMPRINT[3]]\nlevel_names = [\"Executive\", \"VPs\", \"Directors\", \"Managers\"]\n\n# Create custom style with theme-adaptive colors\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    guide_stroke_color=INK_MUTED,\n    guide_stroke_dasharray=\"none\",\n    colors=IMPRINT,\n    title_font_size=28,\n    label_font_size=22,\n    major_label_font_size=18,\n    legend_font_size=16,\n    value_font_size=14,\n    tooltip_font_size=14,\n    stroke_width=3,\n)\n\n# Create XY chart with legend\nchart = pygal.XY(\n    width=4800,\n    height=2700,\n    style=custom_style,\n    title=\"network-hierarchical · pygal · anyplot.ai\",\n    x_title=\"Horizontal Position (Peer Distribution)\",\n    y_title=\"Management Level (0=Top, 3=Bottom)\",\n    show_legend=True,\n    legend_at_bottom=True,\n    show_x_guides=False,\n    show_y_guides=False,\n    stroke=True,\n    dots_size=28,\n    show_dots=True,\n    range=(0, 100),\n    xrange=(0, 100),\n    explicit_size=True,\n    truncate_legend=-1,\n    margin_bottom=120,\n    margin_top=100,\n    margin_left=100,\n    margin_right=100,\n)\n\n# Add reporting lines\nall_edges = []\nfor node_id, (_label, _level, parent_id) in hierarchy.items():\n    if parent_id is not None:\n        parent_pos = node_positions[parent_id]\n        child_pos = node_positions[node_id]\n        all_edges.append((parent_pos, child_pos))\n\nedge_data = []\nfor start, end in all_edges:\n    if edge_data:\n        edge_data.append(None)\n    edge_data.append(start)\n    edge_data.append(end)\n\nchart.add(\"Edges\", edge_data, show_dots=False, stroke=True, color=INK_MUTED)\n\n# Add nodes by level with labels visible\nfor level_idx in range(max_level + 1):\n    level_nodes = levels[level_idx]\n    level_data = []\n    for node_id in level_nodes:\n        pos = node_positions[node_id]\n        node_label = hierarchy[node_id][0]\n        # Create data point with label visible as annotation\n        level_data.append({\"value\": pos, \"label\": node_label})\n    chart.add(level_names[level_idx], level_data, stroke=False, color=level_colors[level_idx])\n\n# Save to PNG and HTML\nchart.render_to_file(f\"plot-{THEME}.svg\")\n# For PNG, we write SVG first then use a simple conversion approach\nwith open(f\"plot-{THEME}.html\", \"wb\") as f:\n    f.write(chart.render())\n\n# Create static PNG using basic SVG rendering\ntry:\n    subprocess.run([\"rsvg-convert\", \"-f\", \"png\", \"-o\", f\"plot-{THEME}.png\", f\"plot-{THEME}.svg\"], check=True)\nexcept (FileNotFoundError, subprocess.CalledProcessError):\n    # Fallback: use cairosvg if available\n    try:\n        import cairosvg\n\n        cairosvg.svg2png(url=f\"plot-{THEME}.svg\", write_to=f\"plot-{THEME}.png\")\n    except ImportError:\n        # If no PNG converter available, just keep the SVG\n        shutil.copy(f\"plot-{THEME}.svg\", f\"plot-{THEME}.png\")\n"}