Skip to content

Commit 83a30d5

Browse files
Merge remote-tracking branch 'origin/main'
2 parents 5d89c81 + b2b5991 commit 83a30d5

4 files changed

Lines changed: 819 additions & 0 deletions

File tree

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
""" pyplots.ai
2+
network-hierarchical: Hierarchical Network Graph with Tree Layout
3+
Library: highcharts unknown | Python 3.13.11
4+
Quality: 92/100 | Created: 2026-01-08
5+
"""
6+
7+
import tempfile
8+
import time
9+
import urllib.request
10+
from pathlib import Path
11+
12+
from selenium import webdriver
13+
from selenium.webdriver.chrome.options import Options
14+
15+
16+
# Data: Organizational hierarchy - CEO -> VPs -> Directors -> Managers
17+
# Structure: list of [from, to] connections for organization chart
18+
connections = [
19+
# CEO to VPs
20+
["CEO", "VP Engineering"],
21+
["CEO", "VP Sales"],
22+
["CEO", "VP Operations"],
23+
# VP Engineering to Directors
24+
["VP Engineering", "Dir Frontend"],
25+
["VP Engineering", "Dir Backend"],
26+
["VP Engineering", "Dir DevOps"],
27+
# VP Sales to Directors
28+
["VP Sales", "Dir Americas"],
29+
["VP Sales", "Dir EMEA"],
30+
# VP Operations to Directors
31+
["VP Operations", "Dir Logistics"],
32+
["VP Operations", "Dir HR"],
33+
# Directors to Managers
34+
["Dir Frontend", "Mgr React"],
35+
["Dir Frontend", "Mgr Vue"],
36+
["Dir Backend", "Mgr API"],
37+
["Dir Backend", "Mgr Database"],
38+
["Dir DevOps", "Mgr Cloud"],
39+
["Dir Americas", "Mgr NA Sales"],
40+
["Dir Americas", "Mgr LATAM"],
41+
["Dir EMEA", "Mgr UK Sales"],
42+
["Dir EMEA", "Mgr DE Sales"],
43+
["Dir Logistics", "Mgr Supply"],
44+
["Dir HR", "Mgr Talent"],
45+
]
46+
47+
# Node levels for coloring
48+
node_levels = {
49+
"CEO": 0,
50+
"VP Engineering": 1,
51+
"VP Sales": 1,
52+
"VP Operations": 1,
53+
"Dir Frontend": 2,
54+
"Dir Backend": 2,
55+
"Dir DevOps": 2,
56+
"Dir Americas": 2,
57+
"Dir EMEA": 2,
58+
"Dir Logistics": 2,
59+
"Dir HR": 2,
60+
"Mgr React": 3,
61+
"Mgr Vue": 3,
62+
"Mgr API": 3,
63+
"Mgr Database": 3,
64+
"Mgr Cloud": 3,
65+
"Mgr NA Sales": 3,
66+
"Mgr LATAM": 3,
67+
"Mgr UK Sales": 3,
68+
"Mgr DE Sales": 3,
69+
"Mgr Supply": 3,
70+
"Mgr Talent": 3,
71+
}
72+
73+
# Level colors (colorblind-safe): Python Blue, Python Yellow, Teal, Pink
74+
level_colors = ["#306998", "#FFD43B", "#17BECF", "#E377C2"]
75+
76+
# Build nodes config with colors based on level
77+
nodes_js = "[\n"
78+
for node_name, level in node_levels.items():
79+
color = level_colors[level]
80+
nodes_js += f' {{id: "{node_name}", color: "{color}"}},\n'
81+
nodes_js += "]"
82+
83+
# Build data connections
84+
data_js = "[\n"
85+
for conn in connections:
86+
data_js += f' ["{conn[0]}", "{conn[1]}"],\n'
87+
data_js += "]"
88+
89+
# Download Highcharts JS modules
90+
highcharts_url = "https://code.highcharts.com/highcharts.js"
91+
sankey_url = "https://code.highcharts.com/modules/sankey.js"
92+
organization_url = "https://code.highcharts.com/modules/organization.js"
93+
94+
with urllib.request.urlopen(highcharts_url, timeout=30) as response:
95+
highcharts_js = response.read().decode("utf-8")
96+
97+
with urllib.request.urlopen(sankey_url, timeout=30) as response:
98+
sankey_js = response.read().decode("utf-8")
99+
100+
with urllib.request.urlopen(organization_url, timeout=30) as response:
101+
organization_js = response.read().decode("utf-8")
102+
103+
# Chart JavaScript using organization chart
104+
chart_js = f"""
105+
Highcharts.chart('container', {{
106+
chart: {{
107+
type: 'organization',
108+
width: 4800,
109+
height: 2700,
110+
backgroundColor: '#ffffff',
111+
inverted: true,
112+
marginBottom: 100,
113+
spacingBottom: 50
114+
}},
115+
title: {{
116+
text: 'network-hierarchical · highcharts · pyplots.ai',
117+
style: {{fontSize: '56px', fontWeight: 'bold'}}
118+
}},
119+
subtitle: {{
120+
text: '<span style="color:#306998; font-size:40px;">●</span> CEO &nbsp;&nbsp;&nbsp; <span style="color:#FFD43B; font-size:40px;">●</span> VPs &nbsp;&nbsp;&nbsp; <span style="color:#17BECF; font-size:40px;">●</span> Directors &nbsp;&nbsp;&nbsp; <span style="color:#E377C2; font-size:40px;">●</span> Managers',
121+
useHTML: true,
122+
style: {{fontSize: '32px', color: '#666666'}}
123+
}},
124+
accessibility: {{
125+
enabled: false
126+
}},
127+
plotOptions: {{
128+
organization: {{
129+
nodeWidth: 120,
130+
nodePadding: 15,
131+
borderRadius: 10,
132+
dataLabels: {{
133+
enabled: true,
134+
style: {{
135+
fontSize: '22px',
136+
fontWeight: 'bold',
137+
textOutline: 'none'
138+
}},
139+
color: '#333333'
140+
}},
141+
colorByPoint: false,
142+
link: {{
143+
color: '#888888',
144+
lineWidth: 3
145+
}},
146+
hangingIndentTranslation: 'shrink'
147+
}}
148+
}},
149+
series: [{{
150+
type: 'organization',
151+
name: 'Organization',
152+
keys: ['from', 'to'],
153+
data: {data_js},
154+
nodes: {nodes_js},
155+
levels: [{{
156+
level: 0,
157+
color: '#306998',
158+
dataLabels: {{style: {{color: 'white'}}}}
159+
}}, {{
160+
level: 1,
161+
color: '#FFD43B',
162+
dataLabels: {{style: {{color: '#333333'}}}}
163+
}}, {{
164+
level: 2,
165+
color: '#17BECF',
166+
dataLabels: {{style: {{color: '#333333'}}}}
167+
}}, {{
168+
level: 3,
169+
color: '#E377C2',
170+
dataLabels: {{style: {{color: '#333333'}}}}
171+
}}]
172+
}}]
173+
}});
174+
"""
175+
176+
# Generate HTML with inline scripts
177+
html_content = f"""<!DOCTYPE html>
178+
<html>
179+
<head>
180+
<meta charset="utf-8">
181+
<script>{highcharts_js}</script>
182+
<script>{sankey_js}</script>
183+
<script>{organization_js}</script>
184+
</head>
185+
<body style="margin:0; padding:0;">
186+
<div id="container" style="width: 4800px; height: 2700px;"></div>
187+
<script>{chart_js}</script>
188+
</body>
189+
</html>"""
190+
191+
# Save HTML file
192+
with open("plot.html", "w", encoding="utf-8") as f:
193+
f.write(html_content)
194+
195+
# Take screenshot with Selenium
196+
with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f:
197+
f.write(html_content)
198+
temp_path = f.name
199+
200+
chrome_options = Options()
201+
chrome_options.add_argument("--headless")
202+
chrome_options.add_argument("--no-sandbox")
203+
chrome_options.add_argument("--disable-dev-shm-usage")
204+
chrome_options.add_argument("--disable-gpu")
205+
chrome_options.add_argument("--window-size=4800,2800")
206+
207+
driver = webdriver.Chrome(options=chrome_options)
208+
driver.get(f"file://{temp_path}")
209+
time.sleep(5) # Wait for chart to render
210+
driver.save_screenshot("plot.png")
211+
driver.quit()
212+
213+
Path(temp_path).unlink()
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
""" pyplots.ai
2+
network-hierarchical: Hierarchical Network Graph with Tree Layout
3+
Library: plotnine 0.15.2 | Python 3.13.11
4+
Quality: 91/100 | Created: 2026-01-08
5+
"""
6+
7+
import pandas as pd
8+
from plotnine import (
9+
aes,
10+
element_blank,
11+
element_rect,
12+
element_text,
13+
geom_point,
14+
geom_segment,
15+
geom_text,
16+
ggplot,
17+
labs,
18+
scale_color_manual,
19+
scale_size_identity,
20+
theme,
21+
xlim,
22+
ylim,
23+
)
24+
25+
26+
# Data: Software company organizational hierarchy (22 employees, 4 levels)
27+
# Reduced from 30 to prevent label crowding at bottom level
28+
nodes = [
29+
# Level 0 - CEO
30+
{"id": 0, "label": "CEO", "level": 0},
31+
# Level 1 - VPs (3 people)
32+
{"id": 1, "label": "VP Engineering", "level": 1},
33+
{"id": 2, "label": "VP Product", "level": 1},
34+
{"id": 3, "label": "VP Operations", "level": 1},
35+
# Level 2 - Directors/Managers (6 people)
36+
{"id": 4, "label": "Frontend Dir", "level": 2},
37+
{"id": 5, "label": "Backend Dir", "level": 2},
38+
{"id": 6, "label": "PM Lead", "level": 2},
39+
{"id": 7, "label": "UX Lead", "level": 2},
40+
{"id": 8, "label": "IT Manager", "level": 2},
41+
{"id": 9, "label": "HR Manager", "level": 2},
42+
# Level 3 - Team Members (12 people)
43+
{"id": 10, "label": "FE Dev 1", "level": 3},
44+
{"id": 11, "label": "FE Dev 2", "level": 3},
45+
{"id": 12, "label": "BE Dev 1", "level": 3},
46+
{"id": 13, "label": "BE Dev 2", "level": 3},
47+
{"id": 14, "label": "PM 1", "level": 3},
48+
{"id": 15, "label": "Designer", "level": 3},
49+
{"id": 16, "label": "UX Rsrch", "level": 3},
50+
{"id": 17, "label": "SysAdmin", "level": 3},
51+
{"id": 18, "label": "DevOps", "level": 3},
52+
{"id": 19, "label": "Recruiter", "level": 3},
53+
{"id": 20, "label": "Payroll", "level": 3},
54+
{"id": 21, "label": "Benefits", "level": 3},
55+
]
56+
57+
edges = [
58+
# CEO to VPs
59+
(0, 1),
60+
(0, 2),
61+
(0, 3),
62+
# VP Engineering to Directors
63+
(1, 4),
64+
(1, 5),
65+
# VP Product to Leads
66+
(2, 6),
67+
(2, 7),
68+
# VP Operations to Managers
69+
(3, 8),
70+
(3, 9),
71+
# Directors to Team Members
72+
(4, 10),
73+
(4, 11),
74+
(5, 12),
75+
(5, 13),
76+
(6, 14),
77+
(7, 15),
78+
(7, 16),
79+
(8, 17),
80+
(8, 18),
81+
(9, 19),
82+
(9, 20),
83+
(9, 21),
84+
]
85+
86+
# Compute hierarchical layout positions
87+
# Group nodes by level
88+
levels = {}
89+
for node in nodes:
90+
lvl = node["level"]
91+
if lvl not in levels:
92+
levels[lvl] = []
93+
levels[lvl].append(node)
94+
95+
# Calculate positions: levels spread vertically, nodes at each level spread horizontally
96+
positions = {}
97+
y_spacing = 0.22 # Vertical spacing between levels
98+
for lvl in sorted(levels.keys()):
99+
nodes_at_level = levels[lvl]
100+
n = len(nodes_at_level)
101+
# Spread nodes horizontally with even distribution
102+
if n > 1:
103+
x_positions = [0.05 + i * (0.90 / (n - 1)) for i in range(n)]
104+
else:
105+
x_positions = [0.5]
106+
y_pos = 0.90 - lvl * y_spacing # Root at top
107+
for i, node in enumerate(nodes_at_level):
108+
positions[node["id"]] = (x_positions[i], y_pos)
109+
110+
# Define colors by level - Python Blue for CEO, Yellow for VPs
111+
level_colors = {
112+
"Level 0: CEO": "#306998",
113+
"Level 1: VPs": "#FFD43B",
114+
"Level 2: Directors": "#4ECDC4",
115+
"Level 3: Team": "#FF6B6B",
116+
}
117+
level_names = {0: "Level 0: CEO", 1: "Level 1: VPs", 2: "Level 2: Directors", 3: "Level 3: Team"}
118+
119+
# Node sizes by level (higher = larger)
120+
size_map = {0: 16, 1: 12, 2: 9, 3: 6}
121+
122+
# Create node dataframe
123+
node_df = pd.DataFrame(
124+
{
125+
"x": [positions[node["id"]][0] for node in nodes],
126+
"y": [positions[node["id"]][1] for node in nodes],
127+
"label": [node["label"] for node in nodes],
128+
"level": [level_names[node["level"]] for node in nodes],
129+
"size": [size_map[node["level"]] for node in nodes],
130+
}
131+
)
132+
133+
# Create edge dataframe
134+
edge_data = []
135+
for parent, child in edges:
136+
edge_data.append(
137+
{"x": positions[parent][0], "y": positions[parent][1], "xend": positions[child][0], "yend": positions[child][1]}
138+
)
139+
edge_df = pd.DataFrame(edge_data)
140+
141+
# Create the plot
142+
plot = (
143+
ggplot()
144+
# Draw edges first (underneath nodes)
145+
+ geom_segment(
146+
data=edge_df, mapping=aes(x="x", y="y", xend="xend", yend="yend"), color="#555555", size=1.2, alpha=0.7
147+
)
148+
# Draw nodes colored by level
149+
+ geom_point(data=node_df, mapping=aes(x="x", y="y", color="level", size="size"), alpha=0.95, stroke=0.5)
150+
# Add node labels with offset above nodes
151+
+ geom_text(
152+
data=node_df, mapping=aes(x="x", y="y", label="label"), size=8, va="bottom", nudge_y=0.025, color="#222222"
153+
)
154+
+ scale_color_manual(values=level_colors)
155+
+ scale_size_identity()
156+
+ labs(title="network-hierarchical · plotnine · pyplots.ai", color="Hierarchy Level")
157+
+ xlim(-0.02, 1.02)
158+
+ ylim(0.18, 1.0)
159+
+ theme(
160+
figure_size=(16, 9),
161+
plot_title=element_text(size=24, ha="center"),
162+
legend_title=element_text(size=18),
163+
legend_text=element_text(size=16),
164+
legend_position="bottom",
165+
legend_box_margin=10,
166+
legend_margin=5,
167+
legend_background=element_rect(fill="white", alpha=0.95),
168+
legend_key=element_rect(fill="white"),
169+
# Remove axis elements for network graph
170+
axis_title=element_blank(),
171+
axis_text=element_blank(),
172+
axis_ticks=element_blank(),
173+
panel_grid=element_blank(),
174+
panel_background=element_rect(fill="white"),
175+
plot_background=element_rect(fill="white"),
176+
)
177+
)
178+
179+
# Save the plot
180+
plot.save("plot.png", dpi=300)

0 commit comments

Comments
 (0)