-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathganttcharter.py
119 lines (94 loc) · 3.79 KB
/
ganttcharter.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import argparse
from pathlib import Path
import matplotlib
import matplotlib.pyplot as plt
import pandas as pd
from matplotlib.dates import SU, DateFormatter, WeekdayLocator
COLOR_TABLE = {
"red berry": "#cc4125",
"red": "#e06666",
"orange": "#f6b26b",
"yellow": "#ffd966",
"green": "#93c47d",
"cyan": "#76a5af",
"cornflower blue": "#6d9eeb",
"blue": "#6fa8dc",
"purple": "#8e7cc3",
"magenta": "#c27ba0",
}
BIOIN_BLUE = "#333399"
# HATCH_TABLE = {
# "Steven": "/",
# "Robyn": "\\",
# "Both": "x",
# np.nan: "",
# }
matplotlib.rcParams.update({'font.size': 14})
def gantt_chart(csv_file: Path, fig_file: Path, current_date=pd.Timestamp.today()):
"""
Generates a Gantt chart from a CSV file with title, start_date, end_date, and color columns.
Args:
csv_file: Path to the CSV file.
fig_file: Path to the output fig file.
current_date: The current date to mark on the chart (default: today's date).
Returns:
None. Displays the Gantt chart plot.
"""
# TODO: https://stackoverflow.com/questions/67197205/move-grid-inbetween-bars
# Read the CSV file
df = pd.read_csv(csv_file)
df = df.dropna(how="all")
# Convert dates to datetime objects
df["start_date"] = pd.to_datetime(df["start_date"])
df["end_date"] = pd.to_datetime(df["end_date"])
df["days_to_start"] = (df["start_date"] - df["start_date"].min()).dt.days
df["days_to_end"] = (df["end_date"] - df["start_date"].min()).dt.days
df["task_duration"] = df["days_to_end"] - df["days_to_start"] + 1 # to include also the end date
df["_color"] = df["color"].map(COLOR_TABLE)
# df["_hatch"] = df["label"].map(HATCH_TABLE)
fig, ax = plt.subplots(figsize=(16, 9))
# increase left padding
plt.subplots_adjust(left=0.2)
ax.barh(
y=df["title"],
width=df["task_duration"],
left=df["start_date"],
color=df["_color"],
label=df["label"],
# hatch=df["_hatch"],
)
# change x-axis labels to dates
ax.xaxis_date()
# set date format on x-axis
ax.xaxis.set_major_formatter(DateFormatter("%b %d"))
ax.xaxis.set_major_locator(WeekdayLocator(byweekday=SU))
# add vertical line for current date
ax.axvline(x=current_date, color=BIOIN_BLUE, linestyle="-")
# add text above vertical line with current date outside of the plot
# ax.text(current_date, 0, "Today", rotation=90, verticalalignment="bottom", horizontalalignment="right")
date_ratio = (current_date - df["start_date"].min()) / (df["end_date"].max() - df["start_date"].min())
plt.gcf().text(date_ratio - 0.025, 0.89, current_date.strftime("%b %d"), fontsize=14, color=BIOIN_BLUE)
ax.set_xlabel("Date")
ax.set_ylabel("Task")
# ax.set_title("Gantt Chart")
# horizontal lines
number_bars = len(df['title'].unique())
ax.set_yticks(list(map(lambda y: y - 0.5, range(number_bars + 1))), minor=True)
ax.set_ylim(-0.5, number_bars - 0.5)
ax.grid(axis='y', which='minor', zorder=0) # set the grid on the minor ticks
ax.tick_params(axis='y', which='minor', length=0) # hide minor tick marks
# add legend
handles, labels = plt.gca().get_legend_handles_labels()
by_label = dict(zip(labels, handles))
by_label = {k: v for k, v in by_label.items() if k != "nan"}
plt.legend(by_label.values(), by_label.keys(), loc="lower right")
ax.xaxis.grid(True)
plt.savefig(fig_file, bbox_inches="tight")
def main():
parser = argparse.ArgumentParser(description="Generate a Gantt chart from a CSV file.")
parser.add_argument("csv_file", help="Path to the input CSV file.")
parser.add_argument("fig_file", help="Path to the output fig file.")
args = parser.parse_args()
gantt_chart(Path(args.csv_file), Path(args.fig_file))
if __name__ == "__main__":
main()