ページ

2026-02-11

json形式のタスク情報から html形式のガントチャートを作成するプログラムを作ってみた

 python で json形式のタスク情報から html形式のガントチャートを作成するプログラムを作ってみました


特に説明はなしです 💦

実際に動かして試してみて楽しんでみてください ('ω')ノシ


ちゃんと動けば、gantt.html が出力されて、ブラウザに読み込ませると下図のようになります (*´ω`*)




ではでは・・・


まずは anaconda 環境に必要なライブラリのインストールする

pip install pandas

pip install plotly


sample.json という名前のタスク情報を記したファイルを作成する

{

  "title": "戦国時代(+450年)",


  "tasks": [

    {

      "id": "task1",

      "name": "織田信長",

      "start": "1984-06-23",

      "end": "2032-06-21",

      "owner": "織田信長",

      "progress": 70,

      "summary": "",

      "memo": "",

      "milestones": []

    },

    {

      "id": "task2",

      "name": "豊臣秀吉",

      "start": "1987-03-17",

      "end": "2048-09-18",

      "owner": "豊臣秀吉",

      "progress": 40,

      "summary": "",

      "memo": "",

      "milestones": [

        {"name": "天下統一", "date": "2040-01-01"}

      ]

    },

    {

      "id": "task3",

      "name": "徳川家康",

      "start": "2043-01-31",

      "end": "2066-06-01",

      "owner": "徳川家康",

      "progress": 90,

      "summary": "",

      "memo": "",

      "milestones": [

        {"name": "征夷大将軍就任", "date": "2053-01-01"},

        {"name": "大坂夏の陣", "date": "2065-01-01"}

      ]

    }

  ]

}


肝心かなめのプログラムはこちらです

 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 を出力しました")


これで動かしてみてください ('ω')ノシ
 


 

 

 

 

0 件のコメント:

コメントを投稿