Skip to content

Commit 39629f6

Browse files
update(gantt-dependencies): plotly — Fix misplaced dependency connection lines and ensure correct temporal scheduling
1 parent e8c0489 commit 39629f6

File tree

4 files changed

+98
-89
lines changed

4 files changed

+98
-89
lines changed

plots/gantt-dependencies/implementations/plotly.py

Lines changed: 89 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
""" pyplots.ai
1+
"""pyplots.ai
22
gantt-dependencies: Gantt Chart with Dependencies
3-
Library: plotly 6.5.2 | Python 3.13.11
4-
Quality: 91/100 | Created: 2026-01-15
3+
Library: plotly 6.5.2 | Python 3.14
4+
Quality: /100 | Updated: 2026-02-25
55
"""
66

77
import pandas as pd
@@ -42,8 +42,8 @@
4242
},
4343
{
4444
"task": "Database Design",
45-
"start": "2024-03-18",
46-
"end": "2024-03-24",
45+
"start": "2024-03-20",
46+
"end": "2024-03-26",
4747
"group": "Design",
4848
"depends_on": ["System Architecture"],
4949
},
@@ -71,8 +71,8 @@
7171
},
7272
{
7373
"task": "Frontend Development",
74-
"start": "2024-04-01",
75-
"end": "2024-04-18",
74+
"start": "2024-03-30",
75+
"end": "2024-04-16",
7676
"group": "Development",
7777
"depends_on": ["Design Review"],
7878
},
@@ -85,7 +85,7 @@
8585
},
8686
{
8787
"task": "Integration",
88-
"start": "2024-04-15",
88+
"start": "2024-04-16",
8989
"end": "2024-04-22",
9090
"group": "Development",
9191
"depends_on": ["Backend API Development", "Frontend Development", "Database Implementation"],
@@ -162,47 +162,47 @@
162162

163163
# Build task list with groups (groups first, then their tasks)
164164
ordered_tasks = []
165-
task_to_idx = {}
166-
idx = 0
165+
task_y_labels = {}
167166

168167
for group_name in group_order:
169168
# Add group summary bar
170-
ordered_tasks.append({"name": f"▼ {group_name}", "is_group": True, "group": group_name})
171-
task_to_idx[f"GROUP_{group_name}"] = idx
172-
idx += 1
169+
label = f"\u25bc {group_name}"
170+
ordered_tasks.append({"name": label, "is_group": True, "group": group_name})
173171
# Add tasks in this group
174172
group_tasks = df[df["group"] == group_name].sort_values("start")
175173
for _, task in group_tasks.iterrows():
176-
ordered_tasks.append({"name": f" {task['task']}", "is_group": False, "task_data": task})
177-
task_to_idx[task["task"]] = idx
178-
idx += 1
174+
label = f" {task['task']}"
175+
ordered_tasks.append({"name": label, "is_group": False, "task_data": task})
176+
task_y_labels[task["task"]] = label
179177

180-
# Build y-axis category order
178+
# Build y-axis category order (reversed so first task appears at top)
181179
y_categories = [item["name"] for item in ordered_tasks]
182180

183181
# Create figure
184182
fig = go.Figure()
185183

186-
# Add task bars using timeline-style scatter plot for proper horizontal bars
187-
for i, item in enumerate(ordered_tasks):
184+
# Add task bars
185+
for item in ordered_tasks:
188186
if item["is_group"]:
189-
# Group summary bar
190187
group_name = item["group"]
191188
group_data = groups[groups["group"] == group_name].iloc[0]
192189
fig.add_trace(
193190
go.Scatter(
194191
x=[group_data["start"], group_data["end"]],
195192
y=[item["name"], item["name"]],
196193
mode="lines",
197-
line=dict(color=group_colors[group_name], width=20),
194+
line={"color": group_colors[group_name], "width": 20},
198195
name=group_name,
199196
showlegend=True,
200197
legendgroup=group_name,
201-
hovertemplate=f"<b>{group_name}</b><br>Start: {group_data['start'].strftime('%Y-%m-%d')}<br>End: {group_data['end'].strftime('%Y-%m-%d')}<extra></extra>",
198+
hovertemplate=(
199+
f"<b>{group_name}</b><br>"
200+
f"Start: {group_data['start'].strftime('%Y-%m-%d')}<br>"
201+
f"End: {group_data['end'].strftime('%Y-%m-%d')}<extra></extra>"
202+
),
202203
)
203204
)
204205
else:
205-
# Individual task bar
206206
task = item["task_data"]
207207
group_name = task["group"]
208208
duration = (task["end"] - task["start"]).days
@@ -211,98 +211,104 @@
211211
x=[task["start"], task["end"]],
212212
y=[item["name"], item["name"]],
213213
mode="lines",
214-
line=dict(color=group_colors[group_name], width=14),
214+
line={"color": group_colors[group_name], "width": 14},
215215
opacity=0.85,
216216
showlegend=False,
217217
legendgroup=group_name,
218-
hovertemplate=f"<b>{task['task']}</b><br>Start: {task['start'].strftime('%Y-%m-%d')}<br>End: {task['end'].strftime('%Y-%m-%d')}<br>Duration: {duration} days<extra></extra>",
218+
hovertemplate=(
219+
f"<b>{task['task']}</b><br>"
220+
f"Start: {task['start'].strftime('%Y-%m-%d')}<br>"
221+
f"End: {task['end'].strftime('%Y-%m-%d')}<br>"
222+
f"Duration: {duration} days<extra></extra>"
223+
),
219224
)
220225
)
221226

222-
# Add dependency arrows using shapes for better control
223-
shapes = []
227+
# Add dependency arrows as annotations with arrowheads
224228
for item in ordered_tasks:
225229
if not item["is_group"]:
226230
task = item["task_data"]
227-
task_name = task["task"]
228231
depends_on = task["depends_on"]
229232
if depends_on:
230233
for dep in depends_on:
231-
if dep in task_to_idx:
232-
# Find predecessor task data
234+
if dep in task_y_labels:
233235
pred_task = df[df["task"] == dep].iloc[0]
234236
pred_end = pred_task["end"]
235237
curr_start = task["start"]
236-
pred_idx = task_to_idx[dep]
237-
curr_idx = task_to_idx[task_name]
238+
pred_y = task_y_labels[dep]
239+
curr_y = task_y_labels[task["task"]]
238240

239-
# Draw line from end of predecessor to start of current task
240-
# Using shapes for connector lines
241-
shapes.append(
242-
dict(
243-
type="line",
244-
x0=pred_end,
245-
y0=pred_idx,
246-
x1=curr_start,
247-
y1=curr_idx,
248-
xref="x",
249-
yref="y",
250-
line=dict(color="#555555", width=1.5, dash="dot"),
251-
opacity=0.5,
252-
)
241+
# Annotation arrow from predecessor end to successor start
242+
fig.add_annotation(
243+
x=curr_start,
244+
y=curr_y,
245+
ax=pred_end,
246+
ay=pred_y,
247+
xref="x",
248+
yref="y",
249+
axref="x",
250+
ayref="y",
251+
showarrow=True,
252+
arrowhead=3,
253+
arrowsize=1.2,
254+
arrowwidth=1.5,
255+
arrowcolor="#555555",
256+
opacity=0.7,
253257
)
254258

255-
# Update layout
259+
# Layout
256260
fig.update_layout(
257-
title=dict(
258-
text="gantt-dependencies · plotly · pyplots.ai", font=dict(size=32, color="#333333"), x=0.5, xanchor="center"
259-
),
260-
xaxis=dict(
261-
title=dict(text="Timeline (2024)", font=dict(size=24)),
262-
tickfont=dict(size=14),
263-
type="date",
264-
tickformat="%b %d",
265-
gridcolor="rgba(0,0,0,0.08)",
266-
showgrid=True,
267-
dtick=7 * 24 * 60 * 60 * 1000, # Weekly ticks (in milliseconds)
268-
tickangle=45,
269-
),
270-
yaxis=dict(
271-
title=dict(text="", font=dict(size=22)),
272-
tickfont=dict(size=15),
273-
categoryorder="array",
274-
categoryarray=y_categories[::-1],
275-
showgrid=False,
276-
),
261+
title={
262+
"text": "gantt-dependencies \u00b7 plotly \u00b7 pyplots.ai",
263+
"font": {"size": 32, "color": "#333333"},
264+
"x": 0.5,
265+
"xanchor": "center",
266+
},
267+
xaxis={
268+
"title": {"text": "Timeline (2024)", "font": {"size": 24}},
269+
"tickfont": {"size": 16},
270+
"type": "date",
271+
"tickformat": "%b %d",
272+
"gridcolor": "rgba(0,0,0,0.08)",
273+
"showgrid": True,
274+
"dtick": 7 * 24 * 60 * 60 * 1000,
275+
"tickangle": 45,
276+
},
277+
yaxis={
278+
"title": {"text": "", "font": {"size": 22}},
279+
"tickfont": {"size": 15},
280+
"categoryorder": "array",
281+
"categoryarray": y_categories[::-1],
282+
"showgrid": False,
283+
},
277284
template="plotly_white",
278-
shapes=shapes,
279-
legend=dict(
280-
title=dict(text="Project Phases", font=dict(size=20)),
281-
font=dict(size=16),
282-
orientation="h",
283-
yanchor="bottom",
284-
y=1.02,
285-
xanchor="center",
286-
x=0.5,
287-
itemwidth=30,
288-
),
289-
margin=dict(l=220, r=60, t=130, b=100),
285+
legend={
286+
"title": {"text": "Project Phases", "font": {"size": 20}},
287+
"font": {"size": 16},
288+
"orientation": "h",
289+
"yanchor": "bottom",
290+
"y": 1.02,
291+
"xanchor": "center",
292+
"x": 0.5,
293+
"itemwidth": 30,
294+
},
295+
margin={"l": 230, "r": 40, "t": 120, "b": 90},
290296
height=900,
291297
width=1600,
292298
)
293299

294-
# Add annotation for dependency legend
300+
# Dependency legend annotation
295301
fig.add_annotation(
296302
x=1.0,
297303
y=-0.1,
298304
xref="paper",
299305
yref="paper",
300-
text="····· Dependency (finish-to-start)",
306+
text="\u2192 Dependency (finish-to-start)",
301307
showarrow=False,
302-
font=dict(size=15, color="#555555"),
308+
font={"size": 15, "color": "#555555"},
303309
xanchor="right",
304310
)
305311

306-
# Save outputs
312+
# Save
307313
fig.write_image("plot.png", width=1600, height=900, scale=3)
308314
fig.write_html("plot.html")

plots/gantt-dependencies/metadata/plotly.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
library: plotly
22
specification_id: gantt-dependencies
33
created: '2026-01-15T21:03:27Z'
4-
updated: '2026-01-15T21:06:15Z'
5-
generated_by: claude-opus-4-5-20251101
4+
updated: '2026-02-25T14:40:00Z'
5+
generated_by: claude-opus-4-6
66
workflow_run: 21046151828
77
issue: 3830
8-
python_version: 3.13.11
8+
python_version: '3.14'
99
library_version: 6.5.2
1010
preview_url: https://storage.googleapis.com/pyplots-images/plots/gantt-dependencies/plotly/plot.png
1111
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/gantt-dependencies/plotly/plot_thumb.png
1212
preview_html: https://storage.googleapis.com/pyplots-images/plots/gantt-dependencies/plotly/plot.html
13-
quality_score: 91
13+
quality_score: null
1414
review:
1515
strengths:
1616
- Excellent hierarchical organization with group summary bars and indented child

plots/gantt-dependencies/specification.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@ A Gantt chart that visualizes project schedules with task dependencies and group
2323

2424
## Notes
2525

26-
- Draw dependency arrows/connectors from the end of predecessor tasks to the start of successor tasks
26+
- Draw dependency arrows from the right edge (end date) of each predecessor bar to the left edge (start date) of the successor bar
2727
- Use different visual styles for dependency types (finish-to-start is most common)
2828
- Group headers should show aggregate timeline spanning from earliest to latest task in the group
2929
- Consider indentation or color coding to distinguish groups from individual tasks
3030
- Arrows should avoid overlapping task bars where possible
3131
- Include a legend explaining dependency line styles if multiple types are used
32+
- Dependent tasks must be scheduled to start at or after the end of their predecessor — never before
3233
- Vertical alignment should clearly show task hierarchy (groups above their child tasks)

plots/gantt-dependencies/specification.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ title: Gantt Chart with Dependencies
66

77
# Specification tracking
88
created: 2026-01-15T20:43:22Z
9-
updated: 2026-01-15T20:43:22Z
9+
updated: 2026-02-25T12:00:00Z
1010
issue: 3830
1111
suggested: Eifi1
1212

@@ -27,3 +27,5 @@ tags:
2727
- grouped
2828
- hierarchical
2929
- annotated
30+
- temporal
31+
- dependencies

0 commit comments

Comments
 (0)