【CLI】PythonとpostgreSQLを使って自分用Todoリストを作成【初心者】

# 1.はじめに
・「python3エンジニア認定基礎試験」と「OSS-DB silver」の資格を取得したのですが、せっかく資格をとってもこれらの知識を使わないと忘れてしまうので、なるべく使う機会が多くなるようにpythonとpostgreSQLの基礎的な知識を使って自分用のTodoリストを作成しました。
　最初は単純なCLIで操作できるように作成し、気が向いたらもっと自分好みにカスタマイズしていこうかなと考えています。

#### 必要事項
・プログラムを開いたら最初に未達成のタスクが分かるようにし、どんなものが残っているかチェックできるようにします。コマンドで「データの追加」「削除」「達成したタスクの一覧」「未達成を達成に更新」をできるようにします。
・各種タスクには、「ID(主キー)」「タスク名」「タスク登録日」「期限日」「難易度」「カテゴリー」「達成か未達成か」の属性を用意し、テーブルを作成します。

# 2.コード

・pgadminで、「testdb」というデータベースを作成
・「テーブル」のクエリツールで下記のSQLを入力
```
CREATE TABLE todo (
    id SERIAL PRIMARY KEY,
    task_name TEXT NOT NULL,
    added_date DATE NOT NULL,
    due_date DATE NOT NULL,
    difficulty TEXT NOT NULL,
    category TEXT NOT NULL,
    completed BOOLEAN DEFAULT FALSE
);
```

・pythonファイルの作成（pipで事前にpsycopg2をインストールしておく）

```
import psycopg2  # psycopg2ライブラリをインポートしてPostgreSQLに接続
from datetime import date  # 日付を操作するためのdateモジュールをインポート

# PostgreSQLに接続
conn = psycopg2.connect(
    dbname="testdb",  # データベース名
    user="postgres",  # ユーザー名
    password="password",  # パスワード
    host="localhost"  # ホスト名（通常はローカルホスト）
)

# カーソルを作成
cur = conn.cursor()

# タスクを追加する関数
def add_task(task_name, due_date, difficulty, category):
    added_date = date.today()  # 今日の日付を自動的に追加
    cur.execute('INSERT INTO todo (task_name, added_date, due_date, difficulty, category) VALUES (%s, %s, %s, %s, %s)', 
                (task_name, added_date, due_date, difficulty, category))  # タスクをテーブルに挿入
    conn.commit()  # 変更をコミット

# タスクを達成済みにする関数
def mark_as_done(task_id):
    cur.execute('UPDATE todo SET completed = TRUE WHERE id = %s', (task_id,))  # 指定されたIDのタスクを達成済みに更新
    conn.commit()  # 変更をコミット

# タスクを削除する関数
def delete_task(task_id):
    cur.execute('DELETE FROM todo WHERE id = %s', (task_id,))  # 指定されたIDのタスクを削除
    conn.commit()  # 変更をコミット

# 未達成のタスクを取得する関数
def fetch_undone_tasks():
    cur.execute('SELECT id, task_name, due_date, difficulty, category FROM todo WHERE completed = FALSE')  # 未達成のタスクを選択
    tasks = cur.fetchall()  # 結果を全て取得
    return tasks  # タスクのリストを返す

# 達成済みのタスクを取得する関数
def fetch_done_tasks():
    cur.execute('SELECT id, task_name, due_date, difficulty, category FROM todo WHERE completed = TRUE')  # 達成済みのタスクを選択
    tasks = cur.fetchall()  # 結果を全て取得
    return tasks  # タスクのリストを返す

# メイン関数
def main():
    while True:
        # 未達成のタスクを表示
        print("未達成のタスク一覧:")
        undone_tasks = fetch_undone_tasks()  # 未達成のタスクを取得
        for task in undone_tasks:
            print(f"ID: {task[0]}, タスク: {task[1]}, 期限日: {task[2]}, 難易度: {task[3]}, 分野: {task[4]}")

        # 達成済みのタスクを表示
        print("\n達成済みのタスク一覧:")
        done_tasks = fetch_done_tasks()  # 達成済みのタスクを取得
        for task in done_tasks:
            print(f"ID: {task[0]}, タスク: {task[1]}, 期限日: {task[2]}, 難易度: {task[3]}, 分野: {task[4]}")

        # メニューを表示
        print("\n1. データの追加")
        print("2. データを未達成から達成へ移動")
        print("3. タスクの削除")
        print("4. 達成済みのタスク一覧")
        print("5. 終了")
        choice = input("選択肢を入力してください: ")

        if choice == '1':
            # タスクを追加
            task_name = input("タスクの名前を入力してください: ")
            due_date = input("締め切り日を入力してください (YYYY-MM-DD): ")
            difficulty = input("難易度を入力してください (例: Easy, Medium, Hard): ")
            category = input("分野を入力してください: ")
            add_task(task_name, date.fromisoformat(due_date), difficulty, category)
            print("タスクが追加されました！")

        elif choice == '2':
            # タスクを達成済みに更新
            task_id = input("タスクIDを入力してください: ")
            mark_as_done(task_id)
            print("タスクが達成されました！")

        elif choice == '3':
            # タスクを削除
            task_id = input("削除するタスクIDを入力してください: ")
            delete_task(task_id)
            print("タスクが削除されました！")

        elif choice == '4':
            # 達成済みのタスクを再表示
            print("\n達成済みのタスク一覧:")
            done_tasks = fetch_done_tasks()
            for task in done_tasks:
                print(f"ID: {task[0]}, タスク: {task[1]}, 期限日: {task[2]}, 難易度: {task[3]}, 分野: {task[4]}")

        elif choice == '5':
            break  # ループを終了してプログラムを終了

        else:
            print("無効な選択肢です。もう一度お試しください。")

    cur.close()  # カーソルをクローズ
    conn.close()  # データベース接続をクローズ

if __name__ == '__main__':
    main()  # メイン関数を実行
```

# 3.できあがったもの
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3902112/bd9ace70-ec97-1653-6560-baeca4514828.png)

こんな感じで、コマンドでタスク管理をできるようになりました。
う～ん、ちょっと見づらいですね。。
しかし、今回はpythonとpostgreSQLを使うということが目的なのでこれはこれでOKです。

postgreSQLの方はこのようになってました。
しっかり追加も削除もできていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3902112/2f5cecce-63ec-e50d-5996-a3aed9b5ce6f.png)

シーケンス番号がちょっと飛び飛びなのは、1234のデータがある中で3を削除すると次にタスクを入力する際に5の番号が振られて1245のような並びになってしまうんですよね。今のところ気になっていませんがそのあたりも今後改修していきたいですね。

ひとまずはこれでpythonとpostgreSQLに触れる環境が出来上がりました。また余裕があればGUIにするとかいろんな機能をつけるとか実験してみようと思います。それでは。