大学課題(Moodle / eALPS)の iCalendar を定期取得し、新規・締切変更を検知して Todoist へ自動同期する、個人向けの統合タスク管理システムです。
eALPS に新しい課題が出ても、RACSU やブラウザを開かない限り気づけない——その見落としをなくすために作りました。Todoist をハブにすることで、iPhone / Mac / Windows のどこからでも同じ一覧・ウィジェット・通知で大学課題(と仕事)を一元管理できます。
eALPS (iCal) ──▶ task-sync (常時稼働サーバ / systemd) ──▶ Todoist ┬─ iPhone
├─ Mac
└─ Windows
Python 3.10+ / pytest・ruff・mypy green / MIT License
- 📥 自動取得 — eALPS の iCalendar を定期取得(Moodle ベースなら他大学でも基本動作)
- 🔁 差分同期 — 新規/締切/タイトル変更を検知して反映。iCal の UID ベースで重複なし・冪等
- ✅ 完了を尊重 — Todoist で完了したタスクを同期で未完了に戻さない
- 📚 授業ごとにまとめ — 同一授業・同一締切日の課題を 1 タスクに集約してノイズ削減
- 🏷 授業名マッピング — 授業コード → 読みやすい名前(
courses.json) - 🎚 優先度の自動色分け — 締切までの残り時間で P1〜P4(Todoist の色)を自動計算・自動更新
- 🧹 取り込みフィルタ — 「開始」など締切でないイベントを除外(キーワード設定可)
- 🔔 通知 — 新規課題を ntfy などへ通知(Discord / Pushover 等に拡張可能)
- 🖥 常駐 — systemd timer で定期実行、再起動後も自動再開
- 🛟 消失検知 — eALPS から課題が消えても即削除せず、連続未取得で「確認必要」ラベル+通知
- Python 3.10 以上
- Todoist アカウントと API トークン
- eALPS(または Moodle)の iCalendar エクスポート URL
- 常時起動サーバ(任意 / systemd による定期実行に使用)
git clone https://github.com/bucchiy/task-sync.git
cd task-sync
# 1. 仮想環境
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]" # 本番のみなら pip install -e .
# ※ Ubuntu で "ensurepip is not available" と出たら python3-venv 未導入。
# sudo apt install python3-venv か、sudo 不可なら:
# python3 -m venv --without-pip .venv && curl -sS https://bootstrap.pypa.io/get-pip.py | .venv/bin/python
# 2. 秘密情報(.env)
cp .env.example .env
chmod 600 .env
# .env を編集して EALPS_ICAL_URL と TODOIST_API_TOKEN を設定
# 3. 授業名マッピング(任意)
cp courses.json.example courses.json
# courses.json を編集して 授業コード→授業名 を記入
# 4. DB 初期化
task-sync init-db.env / courses.json / data/ / logs/ / *.db は .gitignore 済み。トークンや URL は絶対にコミットしないでください(ログにも出力されないようマスクされます)。
task-sync run # 同期を実行(Todoist へ反映)
task-sync run --dry-run # 変更を加えず、実行予定 (CREATE/UPDATE/SKIP) のみ表示
task-sync status # 直近の同期結果(ヘルスチェック)を表示
task-sync init-db # DB を最新スキーマへ更新 (alembic upgrade head)python -m app run でも起動できます。EALPS_ICAL_URL にローカルパス(例 tests/fixtures/sample.ics)を指定すれば、資格情報なしでオフライン dry-run を試せます。
Dry Run — 実行予定
┏━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ 操作 ┃ 内容 ┃
┡━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ CREATE │ [応用プログラミング言語] 7/6 の課題 (8件) │
│ UPDATE │ [画像処理] 「第2回レポート課題」の提出期限 │
│ SKIP │ [オペレーティングシステム] 実技試験 │
└────────┴────────────────────────────────┘
| キー | 説明 | 既定 |
|---|---|---|
EALPS_ICAL_URL |
eALPS / Moodle の ICS URL(秘密) | — |
TODOIST_API_TOKEN |
Todoist API トークン(秘密) | — |
TODOIST_PROJECT_NAME |
登録先プロジェクト名(無ければ自動作成) | 統合タスク |
TODOIST_PROJECT_ID |
プロジェクトを ID 固定したい場合のみ | — |
TIMEZONE |
タイムゾーン | Asia/Tokyo |
MISSING_THRESHOLD |
消失候補と判定する連続未取得回数 | 3 |
EXCLUDE_TITLE_KEYWORDS |
タイトルにこの語を含むイベントを除外(カンマ区切り) | 開始 |
COURSES_FILE |
授業コード→名前の対応表 JSON | courses.json |
GROUP_SAME_DAY |
同一授業・同一締切日の課題を 1 つにまとめる | true |
LOG_LEVEL |
ログレベル | INFO |
DATABASE_URL |
SQLite の場所 | sqlite:///data/task_sync.db |
NTFY_TOPIC |
ntfy 通知トピック(任意) | — |
eALPS は「提出期限」だけでなく「受験可能期間の開始」「〜開始」といった締切ではないイベントも配信します。これらはタスク化するとノイズになるため、タイトルに指定語を含むイベントを取り込み時に除外します。
.env の EXCLUDE_TITLE_KEYWORDS で調整します(カンマ区切り・タイトルへの部分一致)。
EXCLUDE_TITLE_KEYWORDS=開始 # 既定: 「開始」を含むイベントを除外
EXCLUDE_TITLE_KEYWORDS=開始,オリエンテーション # 複数指定
EXCLUDE_TITLE_KEYWORDS= # 空にすると全イベントを取り込む(フィルタ無効)- 英語 Moodle など言語が異なる場合は、その表記(例
opens)に合わせて変更してください。 - 変更は次回同期から反映されます。除外されたイベントが既に Todoist にある場合は「消失」として扱われます(消失検知参照)。
- ロジック本体は
app/sources/ealps.pyのfilter_tasks()です。より複雑な条件が必要ならここを拡張できます。
eALPS の ICS には授業コード(例 T2054300)しか入らないため、courses.json で読みやすい名前に変換します。未登録のコードはそのまま表示されます。
{
"T2054300": "応用プログラミング言語",
"T2C05300": "オペレーティングシステム"
}GROUP_SAME_DAY=true(既定)のとき、同じ授業で同じ締切日の課題は次のように 1 タスクへ集約されます(締切は最も早い時刻)。件数が増減しても同じタスクが更新されます。
[応用プログラミング言語] 7/6 の課題 (8件)
・「課題23-1」の提出期限 (09:00)
・「課題23-3」の提出期限 (09:00)
・課題23-2 の受験可能期間の終了 (09:00)
...
締切までの残り時間で自動設定され、同期のたびに再計算されます。
| 残り時間 | 優先度 | Todoist の色 |
|---|---|---|
| 24時間以内 / 期限超過 | P1 | 🔴 赤 |
| 3日以内 | P2 | 🟠 オレンジ |
| 7日以内 | P3 | 🔵 青 |
| 7日より先 / 締切なし | P4 | ⚪️ 色なし |
eALPS 課題には 大学 eALPS 自動登録 ラベルが自動付与されます。仕事タスクは Todoist に手で追加し 仕事 ラベルを付ければ、同期は手動タスクに一切触れずに共存します。ウィジェットはフィルターを指定して分離表示できます。
- フィルター「大学」:
@大学 - フィルター「仕事」:
@仕事 - 統合ビュー:
@大学 | @仕事
常時起動サーバに配置し、定期実行します。systemd/*.service *.timer のパス(WorkingDirectory / ExecStart / EnvironmentFile)と User= を環境に合わせて調整してください。
sudo cp systemd/task-sync.service systemd/task-sync.timer /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now task-sync.timer
systemctl list-timers task-sync.timer # 次回実行予定
sudo systemctl start task-sync.service # 手動で 1 回実行
journalctl -u task-sync.service -n 50 # ログ確認- 既定タイマーは
OnCalendar=*-*-* 08,13,20:00(1日3回)+Persistent=true(再起動後も自動再開)。頻度はOnCalendarを編集して変更します。 - タイマーを希望の時刻(JST)で撃つには、サーバのタイムゾーンを
sudo timedatectl set-timezone Asia/Tokyoに設定してください。 Type=oneshotとfcntlファイルロックにより多重起動を防止します。
Todoist との対応関係は data/task_sync.db(SQLite)に保存されます。別マシンへ移す際は既存の data/task_sync.db も一緒にコピーしてください(空 DB で始めると Todoist に重複作成されます)。コピー後 task-sync run --dry-run がすべて SKIP になれば重複しません。
| 症状 | 対応 |
|---|---|
| 同期されない | journalctl -u task-sync.service -n 100 でエラー確認。task-sync run を手動実行して再現。 |
Network is unreachable |
サーバに IPv4 デフォルトルートが無い可能性。ip -4 route show default を確認し、無ければ netplan にデフォルトルート(routes: - to: default via: <ルーターIP>)を追加して sudo netplan apply。eALPS は IPv4 専用。 |
| 認証エラー (401/403) | TODOIST_API_TOKEN を確認・再発行(認証エラーは再試行されません)。 |
| 課題が重複登録された | 通常発生しません(task_mappings で冪等化)。移設時は DB を持ち込んだか確認。 |
| 「同期処理が実行中」でスキップ | 前回処理が未完了。長時間続く場合は data/task_sync.lock と実行中プロセスを確認。 |
| ログが肥大化 | logs/ は 5MB×5 世代でローテーションされます。 |
pytest # テスト
ruff check app tests
mypy app
pre-commit install # 任意外部サービス依存はインターフェース(Destination / Notifier)で分離し、テストでは respx とフェイク実装でモックしています。Todoist への書き込みは必ず --dry-run で事前確認できます。
app/
sources/ealps.py # ICS 取得・解析 → ExternalTask
grouping.py # 同一授業・同日課題のまとめ
courses.py # 授業コード→授業名マッピング
reconcile.py # 差分判定・ハッシュ・優先度(純粋関数)
destinations/todoist.py # Todoist API v1 クライアント
sync.py # 同期オーケストレーション・排他ロック・完了保持・消失検知
cli.py # CLI (run / status / init-db)
notifications/ # ntfy ほか
tests/ # pytest(sample.ics フィクスチャ + respx モック)
systemd/ # service / timer
alembic/ # マイグレーション
Moodle の iCalendar 出力は標準フォーマットなので、EALPS_ICAL_URL を別の Moodle のカレンダー URL に差し替えれば基本的に動きます。タイトルの言語に合わせて EXCLUDE_TITLE_KEYWORDS(英語なら opens 等)と courses.json を調整してください。
独自アプリ / Web UI、複数ユーザー、Slack / Notion / GitHub / Google Calendar 連携、AI 機能、双方向完全同期は本 MVP の対象外です。