import json
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from datetime import datetime
# -----------------------------
# JSON読み込み
# -----------------------------
with open("sample.json", encoding="utf-8") as f:
root = json.load(f)
TITLE = root["title"]
data = root["tasks"]
rows = []
completed_rows = []
remaining_rows = []
milestones = []
# -----------------------------
# データ整形
# -----------------------------
for t in data:
start = pd.to_datetime(t["start"]).normalize()
end = pd.to_datetime(t["end"]).normalize()
duration = end - start
progress_end = (start + duration * (t["progress"] / 100)).normalize()
# hover用情報
rows.append({
"Task": t["name"],
"Start": start,
"Finish": end,
"Owner": t["owner"],
"Summary": t["summary"],
"Memo": t["memo"],
"StartStr": start.strftime("%Y-%m-%d"),
"FinishStr": end.strftime("%Y-%m-%d")
})
# 完了部分
if progress_end > start:
completed_rows.append({
"Task": t["name"],
"Start": start,
"Finish": progress_end
})
# 未完了部分
if progress_end < end:
remaining_rows.append({
"Task": t["name"],
"Start": progress_end,
"Finish": end,
"Owner": t["owner"]
})
# マイルストーン
for ms in t["milestones"]:
ms_date = pd.to_datetime(ms["date"]).normalize()
milestones.append({
"Task": t["name"],
"Date": ms_date,
"Label": f'{ms["name"]}\n{ms_date.strftime("%Y-%m-%d")}'
})
df = pd.DataFrame(rows)
completed_df = pd.DataFrame(completed_rows)
remaining_df = pd.DataFrame(remaining_rows)
ms_df = pd.DataFrame(milestones)
# -----------------------------
# ガント(未完了)
# -----------------------------
fig = px.timeline(
remaining_df,
x_start="Start",
x_end="Finish",
y="Task",
color="Owner",
hover_data=[]
)
fig.update_yaxes(autorange="reversed")
# ★バー枠線なし
fig.update_traces(marker_line_width=0)
# -----------------------------
# hover情報追加
# -----------------------------
for _, r in df.iterrows():
fig.add_trace(go.Scatter(
x=[r["Start"]],
y=[r["Task"]],
mode="markers",
marker=dict(size=0),
showlegend=False,
hovertemplate=
f"<b>{r['Task']}</b><br>"
f"担当: {r['Owner']}<br>"
f"開始: {r['StartStr']}<br>"
f"終了: {r['FinishStr']}<br>"
f"概要: {r['Summary']}<br>"
f"メモ: {r['Memo']}<extra></extra>"
))
# -----------------------------
# 完了バー(グレー)
# -----------------------------
if not completed_df.empty:
fig_completed = px.timeline(
completed_df,
x_start="Start",
x_end="Finish",
y="Task"
)
for trace in fig_completed.data:
trace.marker.color = "lightgray"
trace.marker.line.width = 0
trace.showlegend = False
fig.add_trace(trace)
# -----------------------------
# マイルストーン
# -----------------------------
for _, r in ms_df.iterrows():
fig.add_trace(go.Scatter(
x=[r["Date"]],
y=[r["Task"]],
mode="markers+text",
marker=dict(size=12, color="red", symbol="diamond"),
text=r["Label"],
textposition="top center",
showlegend=False
))
# -----------------------------
# 今日ライン
# -----------------------------
today = pd.to_datetime(datetime.now()).normalize()
fig.add_shape(
type="line",
x0=today,
x1=today,
y0=0,
y1=1,
xref="x",
yref="paper",
line=dict(color="black", width=2, dash="dash")
)
fig.add_annotation(
x=today,
y=1,
xref="x",
yref="paper",
text="Today",
showarrow=False,
yshift=10
)
# -----------------------------
# 軸の日付表示形式
# -----------------------------
fig.update_xaxes(tickformat="%Y-%m-%d")
# -----------------------------
# タイトル
# -----------------------------
fig.update_layout(
title=TITLE,
title_x=0.5
)
# -----------------------------
# 出力
# -----------------------------
fig.write_html("gantt.html")
print("gantt.html を出力しました")