今回は、生成AIに本連載で作ってきたPythonスクリプトを整える作業を行ってもらう。従来、こうした作業はプログラマーが自分で行うことが当然だったが、生成AIが登場してからは生成AIに行わせることができる。生産的ではないが時間を浪費する作業には、積極的に生成AIを活用して行きたいところだ。
連載「生成AI×プログラミング」のこれまでの回はこちらを参照。
これまでの成果物
これまでの成果物をまとめておこう。SQLiteのデータ構造、構成しているPythonスクリプトファイル、アプリのサブコマンド、Pythonスクリプトを実行するためのPowerShellラッパースクリプトは次のようになる。
列名 | 型 | 内容 |
---|---|---|
id | INTEGER (主キー) | タスクの一意識別子 |
title | TEXT | タスクのタイトル |
description | TEXT | タスクの詳細な説明 |
due_date | DATE | タスクの期日 |
priority | INTEGER | タスクの優先度。1が最も高い優先度で、3が最も低い優先度 |
completed | INTEGER | タスクが完了したかどうかを示すフラグ。0は未完了、1は完了 |
ファイル名 | 内容 |
---|---|
todo_app.py | CUIの処理を行うファイル。ユーザーが直接操作する |
todo_db_manager.py | SQLiteデータベースとのやり取りを担当するファイル |
todo_app.ps1 | todo_app.pyを実行するためのラッパースクリプト |
サブコマンド | 引数 | 内容 |
---|---|---|
add | ー | タスクの追加 |
remove | id | タスクの削除 |
list | ー | タスクの一覧表示 |
todo_app.py
import sys
import datetime
from todo_db_manager import create_todo_table, add_todo, remove_todo, get_todos
def parse_date(date_str):
"""日付文字列を datetime オブジェクトに変換 """
for fmt in ("%Y-%m-%d", "%Y/%m/%d", "%Y%m%d"):
try:
# 時刻が含まれている場合に備えて、日付部分のみを抽出
date_time = datetime.datetime.strptime(date_str, fmt)
return date_time.date()
except ValueError:
continue
raise ValueError("無効な日付形式です。")
def main():
# コマンドライン引数の処理
if len(sys.argv) < 2:
print("サブコマンドが指定されていません。")
return
command = sys.argv[1]
# テーブルの作成
create_todo_table()
if command == "add":
# タスクの追加
title = input("タイトルを入力してください (必須): ").strip()
if not title:
print("タイトルは必須です。")
return
description = input("説明を入力してください (任意): ").strip()
# 期限の入力と処理
date_input = input("期限を入力してください (年-月-日 または 年/月/日 または 年月日) (省略すると今日の日付になります): ").strip()
if date_input:
try:
due_date = parse_date(date_input)
except ValueError as e:
print(e)
return
else:
due_date = datetime.date.today()
# プライオリティの入力と処理
priority_input = input("プライオリティを入力してください (1, 2, 3) (省略すると3になります): ").strip()
try:
priority = int(priority_input) if priority_input else 3
if priority not in {1, 2, 3}:
raise ValueError("プライオリティは1, 2, 3のいずれかでなければなりません。")
except ValueError as e:
print(e)
return
# タスクの追加
add_todo(title, description, due_date=due_date, priority=priority)
print(f"タスク '{title}' が追加されました。")
elif command == "remove":
# ユーザーに数字の入力を求める
try:
todo_id = int(input("削除するタスクのIDを入力してください: "))
remove_todo(todo_id) # remove_todo 関数を呼び出す
print(f"タスクID {todo_id} が削除されました。")
except ValueError:
print("無効なIDが入力されました。数字を入力してください。")
except Exception as e:
print(f"エラーが発生しました: {e}")
elif command == "list":
# すべてのタスクを一覧表示
todos = get_todos()
today = datetime.date.today()
# タスクの期日列を日付形式で処理
def get_due_date(todo):
due_date_str = todo[3]
if due_date_str:
try:
# 日付部分だけを抽出するために、datetimeオブジェクトを使用する
return datetime.datetime.strptime(due_date_str.split()[0], "%Y-%m-%d").date()
except ValueError:
return None
return None
# 期限が今日以降のタスクを取り出す
upcoming_todos = [todo for todo in todos if get_due_date(todo) and get_due_date(todo) >= today]
overdue_todos = [todo for todo in todos if get_due_date(todo) and get_due_date(todo) < today]
no_due_date_todos = [todo for todo in todos if get_due_date(todo) is None]
# 期限が今日以降のタスクを期日で昇順に並べる
upcoming_todos.sort(key=lambda x: get_due_date(x))
# 期限が今日より前のタスクを期日で降順に並べる
overdue_todos.sort(key=lambda x: get_due_date(x), reverse=True)
# 期限がないタスクは追加のソートなし
# タスクの表示
for todo in upcoming_todos:
print(todo)
print("----")
for todo in overdue_todos:
print(todo)
for todo in no_due_date_todos:
print(todo)
else:
print("無効なサブコマンドです。")
if __name__ == "__main__":
main(
todo_db_manager.py
import sqlite3
from datetime import datetime
DB_FILE = "todos.db"
def create_todo_table():
conn = sqlite3.connect(DB_FILE)
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS todos (
id INTEGER PRIMARY KEY,
title TEXT,
description TEXT,
due_date DATE,
priority INTEGER,
completed INTEGER
)
''')
conn.commit()
conn.close()
def add_todo(title, description=None, due_date=None, priority=3):
conn = sqlite3.connect(DB_FILE)
cursor = conn.cursor()
cursor.execute('''
INSERT INTO todos (title, description, due_date, priority, completed)
VALUES (?, ?, ?, ?, ?)
''', (title, description, due_date, priority, 0))
conn.commit()
conn.close()
def remove_todo(id):
conn = sqlite3.connect(DB_FILE)
cursor = conn.cursor()
cursor.execute('''
DELETE FROM todos WHERE id = ?
''', (id,))
conn.commit()
conn.close()
def get_todos():
conn = sqlite3.connect(DB_FILE)
cursor = conn.cursor()
cursor.execute('''
SELECT * FROM todos
''')
todos = cursor.fetchall()
conn.close()
return todo
todo_app.ps1
# PythonのパスとPythonスクリプトのパスを設定する
$pythonExe = "C:\Users\daichi\AppData\Local\Microsoft\WindowsApps\python.exe"
$pythonScript = "C:\Users\daichi\Documents\python\todo\todo_app.py"
# 現在のカレントディレクトリを保存する
$originalLocation = Get-Location
# Pythonスクリプトのディレクトリを取得する
$scriptDir = Split-Path $pythonScript -Parent
try {
# Pythonスクリプトを実行する
Push-Location $scriptDir
& $pythonExe $pythonScript @args
}
catch {
# エラーメッセージを表示する(必要に応じて)
Write-Error "${pythonScript}実行中にエラーが発生しました: $_"
}
finally {
# カレントディレクトリを戻す
Pop-Location