Skip to content

Instantly share code, notes, and snippets.

@1021ky
Last active August 6, 2025 13:41
Show Gist options
  • Save 1021ky/dfdc316c4abe98943ebde11dc22724fd to your computer and use it in GitHub Desktop.
Save 1021ky/dfdc316c4abe98943ebde11dc22724fd to your computer and use it in GitHub Desktop.
CLIで使う簡易ポモドーロタイマー
# geminiに作ってもらった。まだ使い勝手がいまいち。
# TODO: タスクの振り返りなどをgemini cliでやっていい感じにする
import json
import time
import os
from datetime import date, timedelta
# --- 設定 ---
WORK_MINUTES = 25
SHORT_BREAK_MINUTES = 5
LONG_BREAK_MINUTES = 30
POMODOROS_PER_LONG_BREAK = 4
TASK_FILE = 'tasks.json'
# --- プラットフォーム依存の通知 ---
def notify(message):
"""作業や休憩の開始・終了を音声で通知する"""
print(f"\n🔔 {message}\n")
try:
# macOS
if os.uname().sysname == 'Darwin':
os.system(f'say "{message}"')
# Linux (espeakが必要)
elif os.uname().sysname == 'Linux':
os.system(f'espeak "{message}"')
# Windowsの場合は、別途ライブラリ(winsoundなど)が必要です
except Exception as e:
print(f"音声通知に失敗しました: {e}")
def countdown(minutes):
"""指定された分数だけカウントダウンする"""
seconds = minutes * 60
while seconds > 0:
mins, secs = divmod(seconds, 60)
timer = f'{mins:02d}:{secs:02d}'
print(f'\r⏳ {timer}', end="")
time.sleep(1)
seconds -= 1
print("\r")
class Task:
"""タスクを表現するクラス"""
def __init__(self, name, estimated_pomodoros=0, actual_pomodoros=0, done=False):
self.name = name
self.estimated_pomodoros = estimated_pomodoros
self.actual_pomodoros = actual_pomodoros
self.done = done
def to_dict(self):
return self.__dict__
@classmethod
def from_dict(cls, data):
return cls(**data)
class TaskManager:
"""タスクリストを管理するクラス"""
def __init__(self, filename):
self.filename = filename
self.tasks = self.load_tasks()
def load_tasks(self):
"""ファイルからタスクを読み込む"""
try:
with open(self.filename, 'r', encoding='utf-8') as f:
data = json.load(f)
today_str = str(date.today())
if today_str in data:
return [Task.from_dict(t) for t in data[today_str]]
except (FileNotFoundError, json.JSONDecodeError):
pass
return []
def save_tasks(self, tasks_to_save, target_date):
"""指定された日付のタスクをファイルに保存する"""
all_data = {}
try:
with open(self.filename, 'r', encoding='utf-8') as f:
all_data = json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
pass
all_data[str(target_date)] = [t.to_dict() for t in tasks_to_save]
with open(self.filename, 'w', encoding='utf-8') as f:
json.dump(all_data, f, indent=4, ensure_ascii=False)
def get_task_by_index(self, index):
if 0 <= index < len(self.tasks):
return self.tasks[index]
return None
def add_task(self, task):
self.tasks.append(task)
def display_tasks(self, title="タスク一覧"):
"""タスクの一覧を表示する"""
print(f"\n--- {title} ---")
if not self.tasks:
print("タスクはありません。")
return
for i, task in enumerate(self.tasks):
status = "✅" if task.done else "🔲"
print(f"{i + 1}. {status} {task.name} "
f"(見積もり: {task.estimated_pomodoros}ポモ, "
f"実績: {task.actual_pomodoros}ポモ)")
print("--------------------")
def display_summary(self):
"""1日の作業サマリーを表示する"""
print("\n🎉 1日お疲れ様でした!本日のサマリーです 🎉")
done_tasks = [t for t in self.tasks if t.done]
if not done_tasks:
print("完了したタスクはありません。")
return
total_estimated = 0
total_actual = 0
for task in done_tasks:
diff = task.actual_pomodoros - task.estimated_pomodoros
diff_str = f"{diff:+}ポモ" if diff != 0 else "±0ポモ"
print(f"- {task.name}: 見積もり {task.estimated_pomodoros}, "
f"実績 {task.actual_pomodoros} (予実差: {diff_str})")
total_estimated += task.estimated_pomodoros
total_actual += task.actual_pomodoros
total_diff = total_actual - total_estimated
total_diff_str = f"{total_diff:+}ポモ" if total_diff != 0 else "±0ポモ"
print("\n[合計]")
print(f" 見積もり: {total_estimated} ポモドーロ")
print(f" 実  績: {total_actual} ポモドーロ")
print(f" 予実差 : {total_diff_str}")
print("----------------------------------------")
def get_initial_tasks(task_manager):
"""プログラム開始時に今日のタスクを準備する"""
if task_manager.tasks:
print(f"\n🗓️ {date.today()}のタスクです。")
task_manager.display_tasks("本日やること")
return
print("\nこんにちは!まず、今日終わらせるタスクをすべて入力してください。")
print("(入力が終わったら、何も入力せずにEnterキーを押してください)")
while True:
task_name = input("タスク名 > ").strip()
if not task_name:
break
task_manager.add_task(Task(task_name))
task_manager.display_tasks("本日やること")
def main():
"""メインの処理"""
tm = TaskManager(TASK_FILE)
pomodoro_count = 0
print("🍅 ポモドーロ・タスクマネージャーへようこそ! 🍅")
get_initial_tasks(tm)
while True:
# --- メインメニュー ---
print("\n何をしますか? (番号を入力してください)")
print("1. タスクを開始する")
print("2. タスクを追加する")
print("3. 今日の作業を終わりにする")
choice = input("> ").strip()
if choice == '1':
# --- タスク選択 ---
tm.display_tasks("どのタスクを開始しますか?")
try:
task_index = int(input("タスク番号 > ")) - 1
current_task = tm.get_task_by_index(task_index)
if not current_task:
print("⚠️ 無効な番号です。")
continue
if current_task.done:
print("⚠️ そのタスクは既に完了しています。")
continue
except ValueError:
print("⚠️ 番号で入力してください。")
continue
# --- 見積もり入力 ---
if current_task.estimated_pomodoros == 0:
try:
est = int(input(f"「{current_task.name}」は何ポモドーロかかりそうですか? > "))
current_task.estimated_pomodoros = est
except ValueError:
print("⚠️ 数字で入力してください。見積もりは0のままです。")
# --- ポモドーロ開始 ---
pomodoro_count += 1
notify(f"作業開始!「{current_task.name}」を頑張りましょう!")
countdown(WORK_MINUTES)
current_task.actual_pomodoros += 1
# --- タスク完了確認 ---
done_choice = input(f"「{current_task.name}」は完了しましたか? (y/n) > ").lower()
if done_choice == 'y':
current_task.done = True
notify(f"タスク「{current_task.name}」完了!お疲れ様でした!")
print(f"実際にかかったポモドーロ数: {current_task.actual_pomodoros}")
# --- 休憩 ---
if pomodoro_count % POMODOROS_PER_LONG_BREAK == 0:
notify(f"{POMODOROS_PER_LONG_BREAK}ポモドーロお疲れ様です!{LONG_BREAK_MINUTES}分の長めの休憩に入ります。")
print("この時間に、追加でやるべきことがないか確認しましょう。")
countdown(LONG_BREAK_MINUTES)
else:
notify(f"お疲れ様でした!{SHORT_BREAK_MINUTES}分の短い休憩です。")
countdown(SHORT_BREAK_MINUTES)
notify("休憩終了です。次の作業の準備をしましょう!")
tm.save_tasks(tm.tasks, date.today()) # 休憩後に進捗を保存
elif choice == '2':
# --- タスク追加 ---
task_name = input("追加するタスク名 > ").strip()
if task_name:
tm.add_task(Task(task_name))
print(f"タスク「{task_name}」を追加しました。")
tm.save_tasks(tm.tasks, date.today())
else:
print("⚠️ タスク名が空です。")
elif choice == '3':
# --- 終了処理 ---
tm.display_summary()
print("\n明日は何をしますか?タスクをすべて入力してください。")
print("(入力が終わったら、何も入力せずにEnterキーを押してください)")
next_day_tasks = []
while True:
task_name = input("明日のタスク名 > ").strip()
if not task_name:
break
next_day_tasks.append(Task(task_name))
next_day = date.today() + timedelta(days=1)
tm.save_tasks(next_day_tasks, next_day)
print(f"\n{next_day}のタスクを保存しました。ゆっくり休んでくださいね!")
break
else:
print("⚠️ 1, 2, 3のいずれかを入力してください。")
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n\nプログラムを中断します。お疲れ様でした。")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment