※ 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 の無料プランの制限がきたのでやめた