from pathlib import Path
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib import font_manager, rcParams
import pandas as pd
out_dir = Path('/home/ubuntu')
# 日本語フォントを可能な範囲で自動検出
font_candidates = [
'Noto Sans CJK JP', 'Noto Sans JP', 'IPAexGothic', 'IPAGothic',
'TakaoGothic', 'Yu Gothic', 'Hiragino Sans', 'DejaVu Sans'
]
available = {f.name for f in font_manager.fontManager.ttflist}
selected_font = next((f for f in font_candidates if f in available), 'DejaVu Sans')
rcParams['font.family'] = selected_font
rcParams['axes.unicode_minus'] = False
# 1日=6時間稼働を想定。開始時刻は各日9:00、終了は目安。
# day_start はガントチャート上の開始日、duration_days は可視化上の幅。
# work_hours は実作業時間。
tasks = [
{'day': 'Day 1', 'category': '原文理解', 'task': '原文章立て確認', 'start_day': 1.00, 'duration_days': 0.10, 'work_hours': 0.5, 'deliverable': '章・見出し一覧'},
{'day': 'Day 1', 'category': '原文理解', 'task': '背景・目的・成功条件の抽出', 'start_day': 1.10, 'duration_days': 0.25, 'work_hours': 1.5, 'deliverable': '背景・目的・KPI候補'},
{'day': 'Day 1', 'category': '原文理解', 'task': '制約条件・固有用語の抽出', 'start_day': 1.35, 'duration_days': 0.25, 'work_hours': 1.5, 'deliverable': '制約リスト・用語集'},
{'day': 'Day 1', 'category': '原文理解', 'task': '未決事項・論点リスト作成', 'start_day': 1.60, 'duration_days': 0.25, 'work_hours': 1.5, 'deliverable': '未決事項一覧・原文論点リスト'},
{'day': 'Day 2', 'category': '構造化', 'task': '分類軸の決定', 'start_day': 2.00, 'duration_days': 0.13, 'work_hours': 0.75, 'deliverable': '分類ルール'},
{'day': 'Day 2', 'category': '構造化', 'task': '機能・画面・データ要件の抽出', 'start_day': 2.13, 'duration_days': 0.46, 'work_hours': 2.75, 'deliverable': '要件分類表'},
{'day': 'Day 2', 'category': '構造化', 'task': 'AI/ロジック・運用要件の抽出', 'start_day': 2.59, 'duration_days': 0.30, 'work_hours': 1.8, 'deliverable': 'AI/運用要件リスト'},
{'day': 'Day 2', 'category': '構造化', 'task': '章構成ドラフト作成', 'start_day': 2.89, 'duration_days': 0.16, 'work_hours': 1.0, 'deliverable': '章構成ドラフト'},
{'day': 'Day 3', 'category': 'ID管理', 'task': 'ID体系設計', 'start_day': 3.00, 'duration_days': 0.08, 'work_hours': 0.5, 'deliverable': '採番ルール'},
{'day': 'Day 3', 'category': 'ID管理', 'task': '要件粒度調整・ID付与', 'start_day': 3.08, 'duration_days': 0.50, 'work_hours': 3.0, 'deliverable': '要件ID一覧'},
{'day': 'Day 3', 'category': 'ID管理', 'task': '優先度・受入基準の設定', 'start_day': 3.58, 'duration_days': 0.38, 'work_hours': 2.25, 'deliverable': '優先度・受入基準'},
{'day': 'Day 3', 'category': 'ID管理', 'task': '検証方法・依存関係の整理', 'start_day': 3.96, 'duration_days': 0.28, 'work_hours': 1.7, 'deliverable': '検証方法・依存関係'},
{'day': 'Day 4', 'category': '移行基準', 'task': 'フェーズ分解・目的定義', 'start_day': 4.00, 'duration_days': 0.25, 'work_hours': 1.5, 'deliverable': 'フェーズ定義'},
{'day': 'Day 4', 'category': '移行基準', 'task': '必須成果物・Exit Criteria設定', 'start_day': 4.25, 'duration_days': 0.42, 'work_hours': 2.5, 'deliverable': 'Exit Criteria表'},
{'day': 'Day 4', 'category': '移行基準', 'task': '承認者・未達時対応の整理', 'start_day': 4.67, 'duration_days': 0.25, 'work_hours': 1.5, 'deliverable': '承認・未達時対応表'},
{'day': 'Day 5', 'category': 'ロジック統制', 'task': 'ロジック一覧化', 'start_day': 5.00, 'duration_days': 0.13, 'work_hours': 0.75, 'deliverable': 'ロジック一覧'},
{'day': 'Day 5', 'category': 'ロジック統制', 'task': '採用基準・停止基準の定義', 'start_day': 5.13, 'duration_days': 0.50, 'work_hours': 3.0, 'deliverable': '採用・停止基準表'},
{'day': 'Day 5', 'category': 'ロジック統制', 'task': '状態区分・例外承認ルール作成', 'start_day': 5.63, 'duration_days': 0.25, 'work_hours': 1.5, 'deliverable': '状態遷移・例外承認'},
{'day': 'Day 5', 'category': 'ロジック統制', 'task': '証跡設計', 'start_day': 5.88, 'duration_days': 0.17, 'work_hours': 1.0, 'deliverable': '証跡設計'},
{'day': 'Day 6', 'category': '図解・運用', 'task': '業務フロー図・画面遷移図作成', 'start_day': 6.00, 'duration_days': 0.50, 'work_hours': 3.0, 'deliverable': '業務フロー・画面遷移'},
{'day': 'Day 6', 'category': '図解・運用', 'task': 'データフロー図・非機能要件表作成', 'start_day': 6.50, 'duration_days': 0.50, 'work_hours': 3.0, 'deliverable': 'データフロー・非機能要件'},
{'day': 'Day 6', 'category': '図解・運用', 'task': '権限・監査ログ・障害復旧要件', 'start_day': 7.00, 'duration_days': 0.33, 'work_hours': 2.0, 'deliverable': '権限・監査・障害対応'},
{'day': 'Day 7', 'category': 'レビュー・汎用化', 'task': '要件ID・受入基準・Exit Criteriaレビュー', 'start_day': 7.33, 'duration_days': 0.46, 'work_hours': 2.75, 'deliverable': 'レビュー指摘表'},
{'day': 'Day 7', 'category': 'レビュー・汎用化', 'task': '採用停止基準・未決事項レビュー', 'start_day': 7.79, 'duration_days': 0.25, 'work_hours': 1.5, 'deliverable': '未決事項・リスク表'},
{'day': 'Day 7', 'category': 'レビュー・汎用化', 'task': '汎用テンプレート化・最終統合', 'start_day': 8.04, 'duration_days': 0.42, 'work_hours': 2.5, 'deliverable': '最終要件定義書・汎用テンプレート'},
]
df = pd.DataFrame(tasks)
df['end_day'] = df['start_day'] + df['duration_days']
category_colors = {
'原文理解': '#1455D9',
'構造化': '#2A7F62',
'ID管理': '#D88A00',
'移行基準': '#7B3FB2',
'ロジック統制': '#B23A48',
'図解・運用': '#008C9E',
'レビュー・汎用化': '#4E5A70',
}
# 詳細ガントチャート(可読性重視:左軸にタスク名、バー終端に所要時間)
fig_h = max(10, len(df) * 0.42)
fig, ax = plt.subplots(figsize=(18, fig_h))
ax.set_facecolor('#F6F8FB')
fig.patch.set_facecolor('#F6F8FB')
plot_df = df.iloc[::-1].reset_index(drop=True)
y_labels = []
for i, row in plot_df.iterrows():
color = category_colors[row['category']]
ax.barh(y=i, width=row['duration_days'], left=row['start_day'], height=0.58, color=color, edgecolor='#162033', linewidth=0.5)
ax.text(row['end_day'] + 0.03, i, f"{row['work_hours']:.1f}h", va='center', ha='left', fontsize=9.5, color='#162033', fontweight='bold')
y_labels.append(f"{row['day']}|{row['task']}")
ax.set_yticks(range(len(plot_df)))
ax.set_yticklabels(y_labels, fontsize=9.5, color='#162033')
ax.set_xlim(0.9, 8.65)
ax.set_xticks(range(1, 9))
ax.set_xticklabels(['Day 1','Day 2','Day 3','Day 4','Day 5','Day 6','Day 7','予備'], fontsize=12, fontweight='bold')
ax.grid(axis='x', color='#1455D9', alpha=0.25, linewidth=1)
for x in range(1, 9):
ax.axvline(x, color='#1455D9', alpha=0.16, linewidth=1)
ax.set_title('世界最高基準の要件定義書作成:7日間詳細ガントチャート', fontsize=19, fontweight='bold', pad=18, color='#162033')
ax.set_xlabel('作業日(1日あたり4〜6時間、Day 6〜7は一部連続作業を想定)', fontsize=12, color='#34405A')
ax.tick_params(axis='y', length=0)
legend_handles = [mpatches.Patch(color=color, label=cat) for cat, color in category_colors.items()]
ax.legend(handles=legend_handles, loc='lower center', bbox_to_anchor=(0.5, -0.10), ncol=4, frameon=False, fontsize=10)
plt.subplots_adjust(left=0.30, right=0.98, top=0.93, bottom=0.08)
png_path = out_dir / 'requirements_7day_gantt_detailed.png'
fig.savefig(png_path, dpi=200, bbox_inches='tight')
plt.close(fig)
# 日別サマリーガントチャート(可読性重視)
summary = df.groupby(['day', 'category'], as_index=False).agg(
start_day=('start_day', 'min'), end_day=('end_day', 'max'), work_hours=('work_hours', 'sum')
)
summary['duration_days'] = summary['end_day'] - summary['start_day']
plot_sum = summary.iloc[::-1].reset_index(drop=True)
fig, ax = plt.subplots(figsize=(16, 8))
ax.set_facecolor('#F6F8FB')
fig.patch.set_facecolor('#F6F8FB')
summary_labels = []
for i, row in plot_sum.iterrows():
color = category_colors[row['category']]
ax.barh(i, row['duration_days'], left=row['start_day'], height=0.58, color=color, edgecolor='#162033', linewidth=0.5)
ax.text(row['end_day'] + 0.04, i, f"{row['work_hours']:.1f}h", va='center', ha='left', fontsize=11, color='#162033', fontweight='bold')
summary_labels.append(f"{row['day']}|{row['category']}")
ax.set_yticks(range(len(plot_sum)))
ax.set_yticklabels(summary_labels, fontsize=12, color='#162033')
ax.set_xlim(0.9, 8.65)
ax.set_xticks(range(1, 9))
ax.set_xticklabels(['Day 1','Day 2','Day 3','Day 4','Day 5','Day 6','Day 7','予備'], fontsize=12, fontweight='bold')
ax.grid(axis='x', color='#1455D9', alpha=0.25, linewidth=1)
ax.set_title('7日間ロードマップ:日別サマリーガントチャート', fontsize=19, fontweight='bold', pad=18, color='#162033')
ax.set_xlabel('作業日', fontsize=12, color='#34405A')
ax.tick_params(axis='y', length=0)
plt.subplots_adjust(left=0.23, right=0.97, top=0.90, bottom=0.08)
summary_png_path = out_dir / 'requirements_7day_gantt_summary.png'
fig.savefig(summary_png_path, dpi=200, bbox_inches='tight')
plt.close(fig)
# CSVとMarkdown表を出力
csv_path = out_dir / 'requirements_7day_gantt_tasks.csv'
df[['day','category','task','work_hours','start_day','end_day','deliverable']].to_csv(csv_path, index=False, encoding='utf-8-sig')
md_path = out_dir / 'requirements_7day_gantt_plan.md'
lines = []
lines.append('# 世界最高基準の要件定義書作成:7日間ガントチャート詳細計画\n')
lines.append('この計画は、既存の要件定義原文を、実装・検証・運用・改善に使える世界最高基準の要件定義書へ変換するための詳細作業計画です。1日あたり4〜6時間を標準とし、全体で約40時間前後を想定します。\n')
lines.append('| Day | 分類 | タスク | 所要時間 | 開始位置 | 終了位置 | 完成物 |\n')
lines.append('|---|---|---|---:|---:|---:|---|\n')
for _, r in df.iterrows():
lines.append(f"| {r['day']} | {r['category']} | {r['task']} | {r['work_hours']:.1f}h | {r['start_day']:.2f} | {r['end_day']:.2f} | {r['deliverable']} |\n")
lines.append('\n## 重要な依存関係\n\n')
lines.append('| 依存元 | 依存先 | 理由 |\n')
lines.append('|---|---|---|\n')
lines.append('| Day 1 原文論点リスト | Day 2 構造分解 | 原文の意図を失わずに分類するため。 |\n')
lines.append('| Day 2 章構成ドラフト | Day 3 要件ID台帳 | 要件IDは分類体系に沿って採番するため。 |\n')
lines.append('| Day 3 要件ID台帳 | Day 4 Exit Criteria | フェーズ移行条件は要件IDの完了状態と連動するため。 |\n')
lines.append('| Day 3 要件ID台帳 | Day 5 採用・停止基準 | AI/ロジック要件を個別に追跡するため。 |\n')
lines.append('| Day 4・Day 5 基準表 | Day 6 図解・非機能・運用要件 | 判定基準を実際の業務・画面・データ・運用に落とすため。 |\n')
lines.append('| Day 1〜Day 6 全成果物 | Day 7 レビュー・汎用化 | 固有案件の要件定義と汎用テンプレートを同時に完成させるため。 |\n')
lines.append('\n## 読み方\n\n')
lines.append('詳細ガントチャートでは、1本のバーが1タスクを表します。バーの長さは作業量の目安であり、色は作業カテゴリを示します。Day 6からDay 7にかけて一部作業が連続しているのは、図解・非機能・運用要件の補強が最も重く、翌日のレビュー作業と連続しやすいためです。\n')
md_path.write_text(''.join(lines), encoding='utf-8')
print(f'created: {png_path}')
print(f'created: {summary_png_path}')
print(f'created: {csv_path}')
print(f'created: {md_path}')
print(f'font: {selected_font}')
print(f'total_hours: {df.work_hours.sum():.1f}')
図解生成スクリプト: 要件定義作業のガントチャート作成
元ファイル: システム要件定義の分析と汎用化方法/create_requirements_gantt.py
要約
要件定義書を作る作業工程を日次タスクに分解し、matplotlibでガントチャート画像を生成するPythonスクリプト。原文理解・構造化・ID管理・移行基準・ロジック統制・図解運用などのカテゴリ別に開始日/期間/実作業時間/成果物を定義し、日本語フォントを自動検出して描画する。
要点
- 日本語フォントを候補リストから自動選択して描画
- 1日6時間稼働前提でDay1〜のタスクを定義
- カテゴリ: 原文理解/構造化/ID管理/移行基準/ロジック統制/図解運用
- 各タスクにstart_day・duration_days・work_hours・deliverableを付与
- matplotlib patchesでガント帯を可視化