Skip to content

Commit 0df7683

Browse files
feat(pygal): implement gantt-basic (#2418)
## Implementation: `gantt-basic` - pygal Implements the **pygal** version of `gantt-basic`. **File:** `plots/gantt-basic/implementations/pygal.py` --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20543283930)* --------- 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 7e32281 commit 0df7683

2 files changed

Lines changed: 209 additions & 0 deletions

File tree

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
""" pyplots.ai
2+
gantt-basic: Basic Gantt Chart
3+
Library: pygal 3.1.0 | Python 3.13.11
4+
Quality: 91/100 | Created: 2025-12-27
5+
"""
6+
7+
from datetime import date
8+
9+
import cairosvg
10+
import pygal
11+
from pygal.style import Style
12+
13+
14+
# Custom style for large canvas (4800x2700)
15+
custom_style = Style(
16+
background="white",
17+
plot_background="white",
18+
foreground="#333333",
19+
foreground_strong="#333333",
20+
foreground_subtle="#666666",
21+
colors=("#306998", "#FFD43B", "#2E8B57", "#DC143C", "#9370DB"),
22+
title_font_size=60,
23+
label_font_size=28,
24+
major_label_font_size=28,
25+
legend_font_size=28,
26+
value_font_size=24,
27+
tooltip_font_size=24,
28+
)
29+
30+
# Project data: Software Development Project
31+
# Each tuple: (task_name, category, start_date, end_date)
32+
tasks = [
33+
("Requirements Analysis", "Planning", date(2025, 1, 6), date(2025, 1, 17)),
34+
("System Design", "Planning", date(2025, 1, 13), date(2025, 1, 31)),
35+
("Database Design", "Design", date(2025, 1, 27), date(2025, 2, 7)),
36+
("UI/UX Design", "Design", date(2025, 1, 20), date(2025, 2, 14)),
37+
("Backend Development", "Development", date(2025, 2, 3), date(2025, 3, 14)),
38+
("Frontend Development", "Development", date(2025, 2, 10), date(2025, 3, 21)),
39+
("API Integration", "Development", date(2025, 3, 3), date(2025, 3, 21)),
40+
("Unit Testing", "Testing", date(2025, 3, 10), date(2025, 3, 28)),
41+
("Integration Testing", "Testing", date(2025, 3, 24), date(2025, 4, 11)),
42+
("User Acceptance Testing", "Testing", date(2025, 4, 7), date(2025, 4, 18)),
43+
("Documentation", "Deployment", date(2025, 4, 7), date(2025, 4, 18)),
44+
("Deployment", "Deployment", date(2025, 4, 14), date(2025, 4, 25)),
45+
]
46+
47+
# Reference date for calculations
48+
reference_date = date(2025, 1, 1)
49+
50+
# Category colors matching the style
51+
category_colors = {
52+
"Planning": "#306998",
53+
"Design": "#FFD43B",
54+
"Development": "#2E8B57",
55+
"Testing": "#DC143C",
56+
"Deployment": "#9370DB",
57+
}
58+
59+
# Calculate date range
60+
all_dates = []
61+
for _, _, start, end in tasks:
62+
all_dates.extend([start, end])
63+
min_date = min(all_dates)
64+
max_date = max(all_dates)
65+
66+
# X-axis range in days
67+
start_day = (min_date - reference_date).days - 3
68+
end_day = (max_date - reference_date).days + 3
69+
day_range = end_day - start_day
70+
71+
# Create task labels for y-axis
72+
task_labels = [t[0] for t in tasks]
73+
num_tasks = len(tasks)
74+
75+
# Create pygal HorizontalBar chart as base structure
76+
chart = pygal.HorizontalBar(
77+
width=4800,
78+
height=2700,
79+
style=custom_style,
80+
title="Software Development Timeline · gantt-basic · pygal · pyplots.ai",
81+
show_legend=True,
82+
legend_at_bottom=True,
83+
legend_box_size=24,
84+
print_values=False,
85+
show_y_guides=False,
86+
show_x_guides=False, # Hide default x guides
87+
show_x_labels=False, # Hide default 0-100 labels
88+
margin=60,
89+
spacing=20,
90+
range=(0, 100),
91+
)
92+
93+
# Set task names as y-axis labels
94+
chart.x_labels = task_labels
95+
96+
# Add placeholder series for legend (one per category)
97+
categories_in_order = ["Planning", "Design", "Development", "Testing", "Deployment"]
98+
for cat in categories_in_order:
99+
chart.add(cat, [None] * num_tasks)
100+
101+
# Render base SVG
102+
svg_string = chart.render().decode("utf-8")
103+
104+
# Pygal coordinates (from actual SVG: transform="translate(466, 140)", plot size 4273.6x2368)
105+
PLOT_ORIGIN_X = 466
106+
PLOT_ORIGIN_Y = 140
107+
PLOT_WIDTH = 4273.6
108+
PLOT_HEIGHT = 2368.0
109+
110+
# Convert to absolute coordinates for injection
111+
PLOT_LEFT = PLOT_ORIGIN_X
112+
PLOT_TOP = PLOT_ORIGIN_Y
113+
114+
row_height = PLOT_HEIGHT / num_tasks
115+
bar_height = row_height * 0.55
116+
117+
# Build custom Gantt bar rectangles
118+
bar_elements = []
119+
120+
for i, (task_name, category, start, end) in enumerate(tasks):
121+
start_day_val = (start - reference_date).days
122+
end_day_val = (end - reference_date).days
123+
124+
# Convert days to x position
125+
x_start = PLOT_LEFT + ((start_day_val - start_day) / day_range) * PLOT_WIDTH
126+
x_end = PLOT_LEFT + ((end_day_val - start_day) / day_range) * PLOT_WIDTH
127+
width = x_end - x_start
128+
129+
# Y position (pygal HorizontalBar has first item at bottom, so reverse index)
130+
reversed_i = num_tasks - 1 - i
131+
y_center = PLOT_TOP + (reversed_i + 0.5) * row_height
132+
y_top = y_center - bar_height / 2
133+
134+
color = category_colors[category]
135+
duration = (end - start).days
136+
137+
bar_elements.append(
138+
f'<rect x="{x_start:.1f}" y="{y_top:.1f}" width="{width:.1f}" '
139+
f'height="{bar_height:.1f}" fill="{color}" rx="6" ry="6" opacity="0.9">'
140+
f"<title>{task_name}&#10;{start.strftime('%b %d')} - {end.strftime('%b %d')} "
141+
f"({duration} days)</title></rect>"
142+
)
143+
144+
# Add month markers and labels
145+
month_markers = []
146+
for month in range(1, 5):
147+
month_date = date(2025, month, 1)
148+
day_val = (month_date - reference_date).days
149+
if start_day <= day_val <= end_day:
150+
x_pos = PLOT_LEFT + ((day_val - start_day) / day_range) * PLOT_WIDTH
151+
month_name = month_date.strftime("%b")
152+
# Vertical guide line
153+
month_markers.append(
154+
f'<line x1="{x_pos:.1f}" y1="{PLOT_TOP}" x2="{x_pos:.1f}" '
155+
f'y2="{PLOT_TOP + PLOT_HEIGHT}" stroke="#bbb" stroke-width="2" '
156+
f'stroke-dasharray="8,4"/>'
157+
)
158+
# Month label
159+
month_markers.append(
160+
f'<text x="{x_pos:.1f}" y="{PLOT_TOP + PLOT_HEIGHT + 40}" '
161+
f'font-family="Consolas, monospace" font-size="28" fill="#333" '
162+
f'text-anchor="middle">{month_name} 1</text>'
163+
)
164+
165+
# X-axis title
166+
month_markers.append(
167+
f'<text x="{PLOT_LEFT + PLOT_WIDTH / 2}" y="{PLOT_TOP + PLOT_HEIGHT + 90}" '
168+
f'font-family="Consolas, monospace" font-size="32" fill="#333" '
169+
f'text-anchor="middle">Timeline (2025)</text>'
170+
)
171+
172+
# Inject custom elements before </svg>
173+
all_elements = "\n".join(bar_elements + month_markers)
174+
svg_output = svg_string.replace("</svg>", f"{all_elements}\n</svg>")
175+
176+
# Remove "No data" text that appears from empty series
177+
svg_output = svg_output.replace(">No data<", "><")
178+
179+
# Save outputs
180+
with open("plot.html", "w") as f:
181+
f.write(svg_output)
182+
183+
cairosvg.svg2png(bytestring=svg_output.encode(), write_to="plot.png")
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
library: pygal
2+
specification_id: gantt-basic
3+
created: '2025-12-27T19:25:30Z'
4+
updated: '2025-12-27T19:33:55Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20543283930
7+
issue: 0
8+
python_version: 3.13.11
9+
library_version: 3.1.0
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/gantt-basic/pygal/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/gantt-basic/pygal/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/gantt-basic/pygal/plot.html
13+
quality_score: 91
14+
review:
15+
strengths:
16+
- Excellent visual presentation with clear horizontal bars and proper spacing between
17+
tasks
18+
- Creative solution using SVG injection to implement Gantt chart in pygal (which
19+
lacks native Gantt support)
20+
- Good use of custom Style for consistent theming and proper font sizing at 4800x2700
21+
- Realistic software development project data with logical task sequences and overlaps
22+
- Clean color-coded categories with legend for easy interpretation
23+
weaknesses:
24+
- Legend color boxes are relatively small compared to the large canvas size
25+
- Missing current date vertical line marker mentioned in spec notes as a consideration
26+
- Red and green colors used together could be improved for colorblind accessibility

0 commit comments

Comments
 (0)