コンテンツにスキップ

2.3.1 プロジェクト:コマンドラインタスクマネージャー

コマンドラインタスクマネージャーのアーキテクチャ図

これは、Python 基礎段階で最初に取り組む、ひと通り完結した小さなプロジェクトです。データ構造、関数、ファイルの読み書き、例外処理を組み合わせて、タスクを保存したり、一覧表示したり、状態を変更したりできる、実用的なコマンドラインツールを作ります。

  • Python の基礎知識(データ構造、関数、ファイル操作、例外処理)を総合的に使う
  • 完全なプロジェクト開発の流れを体験する:要件分析 → 設計 → コーディング → テスト
  • 本当に使えるコマンドラインツールを作る

これから作るのは、コマンドラインタスクマネージャー(簡易版の Todoist のようなもの)です。次の機能をサポートします。

  • タスクを追加する
  • すべてのタスクを表示する
  • タスクを完了済みにする
  • タスクを削除する
  • データを永続化する(プログラムを終了してもデータが消えない)

完成イメージ:

領域CLI に表示される内容
メニュー表示、追加、完了、削除、終了
ユーザー操作1 を選んで全タスクを表示
タスクリスト3 件のタスク、1 件は完了済み
要約合計 3 件のタスク、完了済み 1 件

第1ステップ:プロジェクト計画

Section titled “第1ステップ:プロジェクト計画”

1件のタスクには、どんな情報が必要でしょうか?

task = {
"id": 1,
"title": "Python の基礎を学ぶ",
"done": False,
"created_at": "2026-02-09 14:30:00"
}

すべてのタスクは1つのリストに入れ、JSON ファイルに保存します。

モジュール機能
データ管理ファイルへのタスクの読み込み/保存
タスク操作追加・削除・更新・参照
ユーザーインターフェースメニュー表示、入力処理

まずは、ファイル保存なしのいちばんシンプルな版を実装してみましょう。

# todo.py —— コマンドラインタスクマネージャー
from datetime import datetime
def show_menu():
"""メニューを表示する"""
print("\n===== タスクマネージャー =====")
print("1. すべてのタスクを表示")
print("2. タスクを追加")
print("3. タスクを完了")
print("4. タスクを削除")
print("5. 終了")
print()
def show_tasks(tasks: list[dict]) -> None:
"""すべてのタスクを表示する"""
if not tasks:
print("📭 まだタスクがありません。さっそく追加してみましょう!")
return
print("\n📋 タスクリスト:")
for i, task in enumerate(tasks, 1):
status = "" if task["done"] else " "
print(f' {i}. [{status}] {task["title"]} '
f'(作成日: {task["created_at"][:10]})')
done_count = sum(1 for t in tasks if t["done"])
print(f"\n合計 {len(tasks)} 件のタスク、完了済み {done_count} 件")
def add_task(tasks: list[dict]) -> None:
"""新しいタスクを追加する"""
title = input("タスクのタイトルを入力してください: ").strip()
if not title:
print("❌ タスクのタイトルは空にできません!")
return
task = {
"id": len(tasks) + 1,
"title": title,
"done": False,
"created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
tasks.append(task)
print(f"✅ タスク「{title}」を追加しました!")
def complete_task(tasks: list[dict]) -> None:
"""タスクを完了済みにする"""
show_tasks(tasks)
if not tasks:
return
try:
num = int(input("完了にしたいタスク番号を入力してください: "))
if 1 <= num <= len(tasks):
task = tasks[num - 1]
if task["done"]:
print(f"⚠️ タスク「{task['title']}」はすでに完了しています")
else:
task["done"] = True
print(f"✅ タスク「{task['title']}」を完了済みにしました!")
else:
print("❌ 無効なタスク番号です!")
except ValueError:
print("❌ 数字を入力してください!")
def delete_task(tasks: list[dict]) -> None:
"""タスクを削除する"""
show_tasks(tasks)
if not tasks:
return
try:
num = int(input("削除したいタスク番号を入力してください: "))
if 1 <= num <= len(tasks):
removed = tasks.pop(num - 1)
print(f"🗑️ タスク「{removed['title']}」を削除しました!")
else:
print("❌ 無効なタスク番号です!")
except ValueError:
print("❌ 数字を入力してください!")
def main():
"""メイン関数"""
tasks = []
print("タスクマネージャーへようこそ!")
while True:
show_menu()
choice = input("操作を選んでください (1-5): ").strip()
if choice == "1":
show_tasks(tasks)
elif choice == "2":
add_task(tasks)
elif choice == "3":
complete_task(tasks)
elif choice == "4":
delete_task(tasks)
elif choice == "5":
print("👋 さようなら!")
break
else:
print("❌ 無効な選択です。1-5 を入力してください")
if __name__ == "__main__":
main()

やってみよう: 上のコードを todo.py として保存し、python todo.py を実行してみましょう。


第3ステップ:ファイルの永続化を追加する

Section titled “第3ステップ:ファイルの永続化を追加する”

このままだと、プログラムを終了するとデータが消えてしまいます。ファイル保存機能を追加しましょう。

import json
from pathlib import Path
DATA_FILE = Path("tasks.json")
def load_tasks() -> list[dict]:
"""ファイルからタスクを読み込む"""
if DATA_FILE.exists():
try:
with open(DATA_FILE, "r", encoding="utf-8") as f:
tasks = json.load(f)
print(f"📂 {len(tasks)} 件のタスクを読み込みました")
return tasks
except (json.JSONDecodeError, IOError) as e:
print(f"⚠️ データの読み込みに失敗しました: {e}。空のリストを使います")
return []
def save_tasks(tasks: list[dict]) -> None:
"""タスクをファイルに保存する"""
try:
with open(DATA_FILE, "w", encoding="utf-8") as f:
json.dump(tasks, f, ensure_ascii=False, indent=2)
except IOError as e:
print(f"⚠️ データの保存に失敗しました: {e}")

次に main() 関数を次のように変更します。

def main():
tasks = load_tasks() # 起動時に読み込む
print("タスクマネージャーへようこそ!")
while True:
show_menu()
choice = input("操作を選んでください (1-5): ").strip()
if choice == "1":
show_tasks(tasks)
elif choice == "2":
add_task(tasks)
save_tasks(tasks) # 追加後に保存
elif choice == "3":
complete_task(tasks)
save_tasks(tasks) # 変更後に保存
elif choice == "4":
delete_task(tasks)
save_tasks(tasks) # 削除後に保存
elif choice == "5":
save_tasks(tasks) # 終了前に保存
print("👋 さようなら!")
break
else:
print("❌ 無効な選択です。1-5 を入力してください")

基本版が完成したら、次の機能を追加してレベルアップしてみましょう。

タスクに優先度(高/中/低)を追加し、優先度順に表示できるようにする。

キーワードでタスクのタイトルを検索できるようにする。

次のような統計情報を表示する:総タスク数、完了率、本日の追加数 など。

チャレンジ 4:クラスでリファクタリング

Section titled “チャレンジ 4:クラスでリファクタリング”

プロジェクト全体をオブジェクト指向で書き直してみましょう。

class Task:
"""1件のタスク"""
def __init__(self, title: str, priority: str = ""):
self.title = title
self.priority = priority
self.done = False
self.created_at = datetime.now()
class TaskManager:
"""タスクマネージャー"""
def __init__(self, filename: str = "tasks.json"):
self.filename = filename
self.tasks: list[Task] = []
self.load()
def add(self, title: str, priority: str = "") -> None:
self.tasks.append(Task(title, priority))
def complete(self, index: int) -> None:
self.tasks[index].done = True
def delete(self, index: int) -> None:
self.tasks.pop(index)
def search(self, keyword: str) -> list[Task]:
return [task for task in self.tasks if keyword.lower() in task.title.lower()]
def save(self) -> None:
import json
from pathlib import Path
Path(self.filename).write_text(
json.dumps([task.__dict__ for task in self.tasks], ensure_ascii=False, indent=2),
encoding="utf-8",
)
def load(self) -> None:
import json
from pathlib import Path
path = Path(self.filename)
if not path.exists():
return
data = json.loads(path.read_text(encoding="utf-8"))
self.tasks = []
for item in data:
task = Task(item["title"], item.get("priority", ""))
task.done = item.get("done", False)
self.tasks.append(task)
プロジェクト参考とレビュー観点
  1. priority フィールドを high / medium / low のように追加し、表示前に優先度で並べ替えます。同じ優先度なら、created_at が早い順にします。
  2. search(keyword) ヘルパーを追加し、タイトル・状態テキスト・優先度を大文字小文字を区別せずに照合します。
  3. 総タスク数、完了済み数、未完了数、優先度ごとの件数を出力します。変更のたびに結果をすぐ確認できます。
  4. Task モデル + TaskManager クラスにリファクタします。loadsaveaddcompletedeletesearch はクラスが担当し、CLI は入力と表示だけを担当します。
  5. 自己チェックでは、再起動して保存済みタスクが残っていること、検索が正しい部分集合を返すこと、完了や削除の後に画面と tasks.json の両方が更新されることを確認します。

プロジェクト自己チェックリスト

Section titled “プロジェクト自己チェックリスト”

プロジェクトが完成したら、次の項目を確認してみましょう。

  • プログラムが正常に動作し、不正な入力でクラッシュしない
  • データがファイルに保存され、再起動しても残っている
  • コードが関数ごとに分かれていて、ひとまとまりになっていない
  • 適切なエラー処理がある(try/except)
  • 関数に docstring がある
  • 変数名が分かりやすい(PEP 8 に沿っている)
  • Git でプロジェクトコードを管理している

バージョンの進め方のおすすめ

Section titled “バージョンの進め方のおすすめ”
バージョン目標重点的に仕上げること
基本版最小限の一連の流れを動かす入力できる、処理できる、出力できる、そして一組のサンプルを残す
標準版発表できる形にする設定、ログ、エラー処理、README とスクリーンショットを追加する
チャレンジ版ポートフォリオ品質に近づける評価、比較実験、失敗例の分析、次のステップの計画を追加する

まずは基本版を完成させることをおすすめします。最初から何でも入れようとしなくて大丈夫です。バージョンを1つ上げるたびに、「何が新しくできるようになったか」「どうやって確認したか」「まだ何が課題か」を README に書き足していきましょう。

このページを終えたら、この evidence card を残します。

プロジェクト目標
CLI、スクレイパー、API、AI API 呼び出し、または統合 Python ワークショップの対象
実行コマンド
プロジェクトの起動に使った正確なコマンド
成果物
出力ファイル、API 応答、JSON レコード、スクリーンショット、または README メモ
失敗確認
依存関係、ネットワーク、パース、ルート、入力検証、または API キーの問題
期待される成果
実行結果と1件の失敗例を含む再現可能なミニプロジェクトフォルダ