※ Claude でスクリプトを作って最後に以下のまとめを出力してもらった ※ 結局動くところまで作れてないからこのままだと動かないよ ## 概要 このスクリプトはTodoistから今週完了したタスクを取得し、Obsidianに出力するためのPython実装です。Obsidian Local REST APIを利用して、今開いているファイルに直接タスク情報を出力します。 ## 開発の流れ 1. **基本実装**:Todoistから完了タスクを取得し、新規Obsidianファイルに出力 2. **機能拡張**:アクティブなObsidianファイルに出力するよう改良 3. **セキュリティ向上**:API Tokenを環境変数から取得するよう変更 ## 主な機能 - 今週(月曜〜日曜)の完了タスクをTodoistから取得 - 日付ごとにタスクをグループ化してMarkdown形式に整形 - 現在開いているObsidianファイルに追加 - ファイル末尾への追加 - 特定の見出しの下への追加 ## 実装のポイント - Todoist REST API v2を使用(`/tasks/completed`エンドポイント) - Obsidian Local REST APIを使用(`/active/append`と`/active/patch`エンドポイント) - セキュリティのためAPI Tokenは環境変数から取得 - 挿入方法や対象見出しも環境変数で設定可能 ## 使用方法 1. 必要な環境変数を設定: ```bash export TODOIST_API_TOKEN="your_todoist_token" export OBSIDIAN_API_TOKEN="your_obsidian_token" export INSERT_METHOD="heading" # オプション:heading または append export TARGET_HEADING="# タスク管理" # オプション:見出し指定 ``` 2. Obsidianで出力先のファイルを開いてからスクリプトを実行 ## 今後の発展可能性 - プロジェクト名やラベル情報の表示 - タスク完了時間の表示 - 週次レビューテンプレートと連携 - スケジュールタスクとして自動実行 ## スクリプト ``` python import requests from datetime import datetime, timedelta import json import os # 環境変数からAPI Tokenを取得 TODOIST_API_TOKEN = os.environ.get("TODOIST_API_TOKEN") if not TODOIST_API_TOKEN: raise ValueError("環境変数 'TODOIST_API_TOKEN' が設定されていません。") OBSIDIAN_API_TOKEN = os.environ.get("OBSIDIAN_API_TOKEN") if not OBSIDIAN_API_TOKEN: raise ValueError("環境変数 'OBSIDIAN_API_TOKEN' が設定されていません。") # APIエンドポイント TODOIST_API_URL = "https://api.todoist.com/rest/v2/tasks/completed" OBSIDIAN_API_URL = os.environ.get("OBSIDIAN_API_URL", "http://localhost:27123/api") # デフォルト値も設定 def get_this_week_dates(): """今週の日曜日から土曜日までの日付範囲を取得""" today = datetime.now() # 今日の曜日を取得(0:月曜日, 1:火曜日, ... 6:日曜日) day_of_week = today.weekday() # 今週の月曜日の日付を計算 start_of_week = today - timedelta(days=day_of_week) start_date = start_of_week.replace(hour=0, minute=0, second=0, microsecond=0) # 今週の日曜日の日付を計算 end_of_week = start_of_week + timedelta(days=6) end_date = end_of_week.replace(hour=23, minute=59, second=59, microsecond=999999) return start_date, end_date def get_completed_tasks(since, until): """指定された期間内に完了したタスクをTodoistから取得""" headers = { "Authorization": f"Bearer {TODOIST_API_TOKEN}" } params = { "since": since.isoformat(), "until": until.isoformat() } response = requests.get(TODOIST_API_URL, headers=headers, params=params) if response.status_code == 200: return response.json() else: print(f"エラー: Todoistからデータを取得できませんでした。ステータスコード: {response.status_code}") print(response.text) return [] def format_tasks_for_obsidian(tasks): """タスクをObsidian形式にフォーマット""" if not tasks: return "今週は完了したタスクがありません。" # 日付ごとにタスクをグループ化 tasks_by_date = {} for task in tasks: completed_date = datetime.fromisoformat(task["completed_at"].replace("Z", "+00:00")).date() if completed_date not in tasks_by_date: tasks_by_date[completed_date] = [] tasks_by_date[completed_date].append(task) # Markdown形式でフォーマット now = datetime.now() content = f"## 今週の完了タスク\n\n" content += f"生成日時: {now.strftime('%Y-%m-%d %H:%M:%S')}\n\n" # 日付の新しい順に並べる for date in sorted(tasks_by_date.keys(), reverse=True): content += f"### {date.strftime('%Y-%m-%d (%a)')}\n\n" for task in tasks_by_date[date]: project_info = f" (プロジェクト: {task.get('project_id', 'なし')})" if task.get('project_id') else "" content += f"- ✅ {task['content']}{project_info}\n" content += "\n" return content def append_to_active_file(content): """現在開いているObsidianファイルに内容を追加""" headers = { "Authorization": f"Bearer {OBSIDIAN_API_TOKEN}", "Content-Type": "application/json" } try: response = requests.post( f"{OBSIDIAN_API_URL}/active/append", headers=headers, json={ "content": content } ) if response.status_code == 200: print("現在開いているファイルに内容を追加しました") return True else: print(f"エラー: ファイルに内容を追加できませんでした。ステータスコード: {response.status_code}") print(response.text) return False except Exception as e: print(f"エラー: {e}") return False def insert_under_heading(content, heading): """指定された見出しの下に内容を挿入""" headers = { "Authorization": f"Bearer {OBSIDIAN_API_TOKEN}", "Content-Type": "application/json" } try: response = requests.post( f"{OBSIDIAN_API_URL}/active/patch", headers=headers, json={ "target": heading, "targetType": "heading", "operation": "append", "content": "\n" + content } ) if response.status_code == 200: print(f"見出し '{heading}' の下に内容を追加しました") return True else: print(f"エラー: 見出し '{heading}' の下に内容を追加できませんでした。ステータスコード: {response.status_code}") print(response.text) # 見出しが見つからない場合はファイルの最後に追加 print("見出しが見つからないため、ファイルの最後に追加します") return append_to_active_file(content) except Exception as e: print(f"エラー: {e}") return False def main(): # 環境変数のチェック if not TODOIST_API_TOKEN or not OBSIDIAN_API_TOKEN: print("必要な環境変数が設定されていません。") print("以下の環境変数を設定してください:") print("TODOIST_API_TOKEN - TodoistのAPIトークン") print("OBSIDIAN_API_TOKEN - ObsidianのAPIトークン") print("OBSIDIAN_API_URL - Obsidian API URL (オプション、デフォルト: http://localhost:27123/api)") return # 今週の日付範囲を取得 start_date, end_date = get_this_week_dates() print(f"今週の日付範囲: {start_date.date()} から {end_date.date()}") # 完了したタスクを取得 completed_tasks = get_completed_tasks(start_date, end_date) print(f"取得したタスク数: {len(completed_tasks)}") # Markdown形式に整形 markdown_content = format_tasks_for_obsidian(completed_tasks) # 方法の選択(環境変数から取得) insert_method = os.environ.get("INSERT_METHOD", "append").lower() heading = os.environ.get("TARGET_HEADING", "") if insert_method == "heading" and heading: # 特定の見出しの下に追加 insert_under_heading(markdown_content, heading) else: # ファイルの末尾に追加 append_to_active_file("\n\n" + markdown_content) if __name__ == "__main__": main() ``` 動かしたけどエラーだった エラーを直したかったけど Claude の無料プランの制限がきたのでやめた