🤖

pythonをチャッピー(ChatGPT)に教えてもらう (タスク管理ツール編) #1

に公開

はじめに

しばらく実務でコーディングの機会がなかったので、定期的にChatGPTから課題をもらって、それを解くことでコーディングの習慣を取り戻したいと思います。

チャッピー(ChatGPT)から与えられた課題

課題: タスク管理ツール
課題概要: コマンドラインインターフェース(CLI)で動作するタスク管理ツールを作成します。ユーザーがタスクを追加、削除、編集でき、CSVファイルに保存されたタスクのデータを扱います。
ステップ:
1. タスクを追加するコマンドを実装(例: add)
2. タスクを削除するコマンドを実装(例: delete)
3. タスクの一覧を表示するコマンドを実装(例: list)
4. タスクをCSVファイルに保存し、再起動後もデータが残るようにする。
難易度: 初級〜中級
使用するライブラリ: csv, argparse
追加のヒント:
ユーザーからの入力を処理するために、argparseライブラリを使ってコマンドライン引数を解析します。
CSVファイルへのデータ保存を行い、再度起動した際にタスクが保存されていることを確認します。

自分で書いたソースコード

import argparse
import csv

output_file = "task_list.csv"

def add_task(task):
    with open(output_file, 'a', newline='') as file:
        writer = csv.writer(file)
        writer.writerow([task])
    

def delete_task(task):
    with open(output_file, 'r') as infile:
        reader = csv.reader(infile)

        updated_rows = []
        for row in reader:
            if row[0] != task:
                updated_rows.append(row)
                
    with open(output_file, 'w', newline='') as file_write:
            writer = csv.writer(file_write)
            writer.writerows(updated_rows)

def list_tasks():
    with open(output_file, 'r') as file:
        reader = csv.reader(file)
        for row in reader:
            print(row[0])
def main():
    parser = argparse.ArgumentParser(description='タスク管理ツール')
    parser.add_argument('-command', choices=['add', 'delete', 'list'], help='コマンド実行')
    args = parser.parse_args()

    if args.command == 'add':
        task = input("タスクを追加してください: ")
        add_task(task)
    elif args.command == 'delete':
        list_tasks()
        task = input("削除するタスクを入力してください: ")
        delete_task(task)
    elif args.command == 'list':
        list_tasks()

if __name__ == '__main__':
    main()

チャッピー(ChatGPT)の評価

70点程度/100点

リファクタリング前のコードは、シンプルなタスク管理ツールとして基本的な動作をしており、初心者が理解するには良いコードですが、より実用的なツールとして改良するためには、いくつかの点で効率化や改善が必要です。データの一貫性を保つための工夫、ファイル操作の最適化、ユーザーインターフェースの向上が求められます。

リファクタリング

リファクタリングの主なポイント

  • 再利用性を高める: 入出力関連の処理を関数として分離。
  • エラー処理を追加: ファイルが存在しない場合のエラーハンドリングを追加。
  • コードの簡潔化: 冗長な部分を削減し、読みやすく整理。
  • 明示的な型ヒントを追加: 関数の引数や戻り値に型ヒントを追加。
  • 出力の改善: list_tasks の出力を番号付きリスト形式に改善。

修正後のソースコード

import argparse
import csv
import os

# ファイルパスの設定
OUTPUT_FILE = "task_list.csv" # 【修正】:定数は大文字で定義

def read_tasks() -> list[str]:  # 【修正】:明示的な型ヒントを追加 & 入出力処理を関数として分離
    """CSVファイルからタスクを読み込む"""
    if not os.path.exists(OUTPUT_FILE): # 【修正】:ファイルが存在しない場合は空のリストを返すエラーハンドリング
        return []
    with open(OUTPUT_FILE, 'r') as file:
        reader = csv.reader(file)
        return [row[0] for row in reader] # 【修正】:リスト内包表記(ループでリストを生成するコードを一行で書ける)

def write_tasks(tasks: list[str]) -> None:
    """タスクのリストをCSVファイルに書き込む"""
    with open(OUTPUT_FILE, 'w', newline='') as file:
        writer = csv.writer(file)
        writer.writerows([[task] for task in tasks])

def add_task(task: str) -> None:
    """タスクを追加する"""
    tasks = read_tasks()
    tasks.append(task)
    write_tasks(tasks)
    print(f"タスク '{task}' を追加しました。")

def delete_task(task: str) -> None:
    """指定したタスクを削除する"""
    tasks = read_tasks()
    if task in tasks:
        tasks.remove(task)
        write_tasks(tasks)
        print(f"タスク '{task}' を削除しました。")
    else:
        print(f"タスク '{task}' が見つかりませんでした。") # 【修正】:タスクが見つからない場合のエラーハンドリング

def list_tasks() -> None:
    """タスクの一覧を表示する"""
    tasks = read_tasks()
    if tasks:
        print("現在のタスク一覧:")
        for i, task in enumerate(tasks, start=1): # 【修正】:enumerate関数(インデックス番号を付与したリストを生成する関数)
            print(f"{i}. {task}")                 # 【修正】:タスクを番号付きで表示し、読みやすくした
    else:
        print("タスクはありません。")

def main() -> None:
    """メイン関数: コマンドライン引数を解析して対応する処理を実行"""
    parser = argparse.ArgumentParser(description='タスク管理ツール')
    parser.add_argument('command', choices=['add', 'delete', 'list'], help='コマンドを指定してください')
    parser.add_argument('-t', '--task', help='追加または削除するタスクを指定')
    args = parser.parse_args()

    if args.command == 'add':
        if args.task:
            add_task(args.task)
        else:
            print("タスクの内容を指定してください (例: --task '新しいタスク')")
    elif args.command == 'delete':
        if args.task:
            delete_task(args.task)
        else:
            print("削除するタスクを指定してください (例: --task '削除するタスク')")
    elif args.command == 'list':
        list_tasks()

if __name__ == '__main__':
    main()

使用例

タスク追加

python task_manager.py add --task "買い物に行く"

タスク削除

python task_manager.py delete --task "買い物に行く"

タスク一覧表示

python task_manager.py list

学んだポイント

  • 定数を小文字にするなど基礎的な命名規則を準拠できなかったので、静的解析(flake8)をVisual Studio Codeに設定した

https://qiita.com/enya314/items/374cca3c1b29dac1baab?utm_source=pocket_shared

  • 今後は明示的な型ヒントを忘れないようにしよう
  • 入出力処理を関数に分離するのに気付けなかったのが残念。もう少しメソッドの役割を抽象化させる曲をつけよう
  • ループでリストを生成するリスト内包表記の実用的な使い方を学べた
  • ファイルが存在しない場合のエラーハンドリングも確かに必要だ
  • タスクリストではインデックス番号を付けるなど、使い手の利便性に配慮した設計ができるようになりたい

Discussion