Skip to content

Commit 2d9884b

Browse files
feat(bokeh): implement gantt-basic (#2415)
## Implementation: `gantt-basic` - bokeh Implements the **bokeh** version of `gantt-basic`. **File:** `plots/gantt-basic/implementations/bokeh.py` --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20543282696)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent a220715 commit 2d9884b

2 files changed

Lines changed: 193 additions & 0 deletions

File tree

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
""" pyplots.ai
2+
gantt-basic: Basic Gantt Chart
3+
Library: bokeh 3.8.1 | Python 3.13.11
4+
Quality: 92/100 | Created: 2025-12-27
5+
"""
6+
7+
import pandas as pd
8+
from bokeh.io import export_png, save
9+
from bokeh.models import ColumnDataSource, Legend, LegendItem
10+
from bokeh.palettes import Category10
11+
from bokeh.plotting import figure
12+
from bokeh.resources import CDN
13+
14+
15+
# Data - Software Development Project
16+
tasks = [
17+
{"task": "Requirements Analysis", "start": "2025-01-06", "end": "2025-01-17", "category": "Planning"},
18+
{"task": "System Design", "start": "2025-01-13", "end": "2025-01-31", "category": "Planning"},
19+
{"task": "Database Schema", "start": "2025-01-27", "end": "2025-02-07", "category": "Development"},
20+
{"task": "Backend API", "start": "2025-02-03", "end": "2025-02-28", "category": "Development"},
21+
{"task": "Frontend UI", "start": "2025-02-10", "end": "2025-03-14", "category": "Development"},
22+
{"task": "Integration", "start": "2025-03-03", "end": "2025-03-21", "category": "Development"},
23+
{"task": "Unit Testing", "start": "2025-02-17", "end": "2025-03-14", "category": "Testing"},
24+
{"task": "System Testing", "start": "2025-03-17", "end": "2025-03-28", "category": "Testing"},
25+
{"task": "User Acceptance", "start": "2025-03-24", "end": "2025-04-04", "category": "Testing"},
26+
{"task": "Documentation", "start": "2025-03-10", "end": "2025-04-04", "category": "Deployment"},
27+
{"task": "Deployment", "start": "2025-04-01", "end": "2025-04-11", "category": "Deployment"},
28+
{"task": "Training", "start": "2025-04-07", "end": "2025-04-18", "category": "Deployment"},
29+
]
30+
31+
df = pd.DataFrame(tasks)
32+
df["start"] = pd.to_datetime(df["start"])
33+
df["end"] = pd.to_datetime(df["end"])
34+
35+
# Sort by start date for chronological order
36+
df = df.sort_values(["start", "category"], ascending=[True, True]).reset_index(drop=True)
37+
38+
# Convert dates to numeric for plotting (milliseconds since epoch)
39+
df["start_ms"] = df["start"].astype("int64") // 10**6
40+
df["end_ms"] = df["end"].astype("int64") // 10**6
41+
42+
# Assign y positions (inverted so first task is at top)
43+
df["y"] = list(range(len(df) - 1, -1, -1))
44+
45+
# Color mapping by category
46+
categories = df["category"].unique().tolist()
47+
color_map = {cat: Category10[10][i % 10] for i, cat in enumerate(categories)}
48+
# Override with Python colors for first two categories
49+
color_map[categories[0]] = "#306998" # Python Blue
50+
if len(categories) > 1:
51+
color_map[categories[1]] = "#FFD43B" # Python Yellow
52+
df["color"] = df["category"].map(color_map)
53+
54+
# Create ColumnDataSource
55+
source = ColumnDataSource(
56+
data={
57+
"task": df["task"],
58+
"y": df["y"],
59+
"left": df["start_ms"],
60+
"right": df["end_ms"],
61+
"color": df["color"],
62+
"category": df["category"],
63+
}
64+
)
65+
66+
# Create figure with extra left margin for task labels
67+
p = figure(
68+
width=4800,
69+
height=2700,
70+
title="gantt-basic · bokeh · pyplots.ai",
71+
x_axis_type="datetime",
72+
y_range=(-0.5, len(df) - 0.5),
73+
tools="",
74+
toolbar_location=None,
75+
)
76+
77+
# Bar height
78+
bar_height = 0.65
79+
80+
# Draw Gantt bars using hbar
81+
p.hbar(
82+
y="y",
83+
left="left",
84+
right="right",
85+
height=bar_height,
86+
color="color",
87+
alpha=0.9,
88+
source=source,
89+
line_color="#444444",
90+
line_width=3,
91+
)
92+
93+
# Add task labels on the left side with larger font
94+
x_range_span = df["end_ms"].max() - df["start_ms"].min()
95+
for i, row in df.iterrows():
96+
y_pos = df.loc[i, "y"]
97+
task_name = row["task"]
98+
p.text(
99+
x=[df["start_ms"].min() - x_range_span * 0.015],
100+
y=[y_pos],
101+
text=[task_name],
102+
text_font_size="32pt",
103+
text_align="right",
104+
text_baseline="middle",
105+
text_color="#333333",
106+
)
107+
108+
# Title styling
109+
p.title.text_font_size = "48pt"
110+
p.title.text_font_style = "bold"
111+
p.title.text_color = "#333333"
112+
113+
# X-axis styling
114+
p.xaxis.axis_label = "Timeline"
115+
p.xaxis.axis_label_text_font_size = "36pt"
116+
p.xaxis.major_label_text_font_size = "28pt"
117+
p.xaxis.axis_label_text_color = "#333333"
118+
p.xaxis.major_label_text_color = "#333333"
119+
p.xaxis.axis_line_width = 3
120+
p.xaxis.major_tick_line_width = 3
121+
p.xaxis.minor_tick_line_color = None
122+
123+
# Hide y-axis (task labels are added as text)
124+
p.yaxis.visible = False
125+
126+
# Grid styling
127+
p.xgrid.grid_line_color = "#cccccc"
128+
p.xgrid.grid_line_alpha = 0.5
129+
p.xgrid.grid_line_dash = [8, 4]
130+
p.xgrid.grid_line_width = 2
131+
p.ygrid.grid_line_color = None
132+
133+
# Background
134+
p.background_fill_color = "#fafafa"
135+
p.border_fill_color = "white"
136+
p.outline_line_color = None
137+
138+
# Extend x-range to accommodate task labels
139+
x_min = df["start_ms"].min()
140+
x_max = df["end_ms"].max()
141+
x_padding = (x_max - x_min) * 0.22
142+
p.x_range.start = x_min - x_padding
143+
p.x_range.end = x_max + (x_max - x_min) * 0.03
144+
145+
# Add legend manually using dummy glyphs
146+
legend_items = []
147+
for cat in categories:
148+
dummy = p.hbar(y=[-100], left=[0], right=[1], height=0.1, color=color_map[cat], visible=False)
149+
legend_items.append(LegendItem(label=cat, renderers=[dummy]))
150+
151+
legend = Legend(items=legend_items, location="top_right")
152+
legend.label_text_font_size = "30pt"
153+
legend.glyph_height = 40
154+
legend.glyph_width = 50
155+
legend.spacing = 25
156+
legend.padding = 30
157+
legend.background_fill_alpha = 0.85
158+
legend.border_line_color = "#cccccc"
159+
legend.border_line_width = 2
160+
p.add_layout(legend, "right")
161+
162+
# Save as PNG
163+
export_png(p, filename="plot.png")
164+
165+
# Save as HTML for interactivity
166+
save(p, filename="plot.html", resources=CDN, title="gantt-basic")
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
library: bokeh
2+
specification_id: gantt-basic
3+
created: '2025-12-27T19:24:09Z'
4+
updated: '2025-12-27T19:33:32Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20543282696
7+
issue: 0
8+
python_version: 3.13.11
9+
library_version: 3.8.1
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/gantt-basic/bokeh/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/gantt-basic/bokeh/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/gantt-basic/bokeh/plot.html
13+
quality_score: 92
14+
review:
15+
strengths:
16+
- Excellent visual clarity with well-sized task labels and bars
17+
- Perfect color coding by category with Python-themed blue/yellow for first two
18+
categories
19+
- Professional layout with proper canvas utilization at 4800x2700
20+
- Realistic software development project data showing overlapping and sequential
21+
tasks
22+
- Clean datetime handling with proper x-axis formatting
23+
- Good use of Bokeh ColumnDataSource pattern
24+
weaknesses:
25+
- Missing HoverTool for task details on hover (Bokeh key interactive feature)
26+
- Generates both PNG and HTML but only PNG is required
27+
- Axis label Timeline could be more descriptive

0 commit comments

Comments
 (0)