Skip to content

bucchiy/task-sync

Repository files navigation

task-sync

大学課題(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 の出力例

        Dry Run — 実行予定
┏━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ 操作   ┃ 内容                             ┃
┡━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ CREATE │ [応用プログラミング言語] 7/6 の課題 (8件) │
│ UPDATE │ [画像処理] 「第2回レポート課題」の提出期限 │
│ SKIP   │ [オペレーティングシステム] 実技試験       │
└────────┴────────────────────────────────┘

設定(.env

キー 説明 既定
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 は「提出期限」だけでなく「受験可能期間の開始」「〜開始」といった締切ではないイベントも配信します。これらはタスク化するとノイズになるため、タイトルに指定語を含むイベントを取り込み時に除外します。

.envEXCLUDE_TITLE_KEYWORDS で調整します(カンマ区切り・タイトルへの部分一致)。

EXCLUDE_TITLE_KEYWORDS=開始              # 既定: 「開始」を含むイベントを除外
EXCLUDE_TITLE_KEYWORDS=開始,オリエンテーション  # 複数指定
EXCLUDE_TITLE_KEYWORDS=                  # 空にすると全イベントを取り込む(フィルタ無効)
  • 英語 Moodle など言語が異なる場合は、その表記(例 opens)に合わせて変更してください。
  • 変更は次回同期から反映されます。除外されたイベントが既に Todoist にある場合は「消失」として扱われます(消失検知参照)。
  • ロジック本体は app/sources/ealps.pyfilter_tasks() です。より複雑な条件が必要ならここを拡張できます。

授業名のマッピング(courses.json

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 ⚪️ 色なし

大学と仕事を分けて表示(Todoist)

eALPS 課題には 大学 eALPS 自動登録 ラベルが自動付与されます。仕事タスクは Todoist に手で追加し 仕事 ラベルを付ければ、同期は手動タスクに一切触れずに共存します。ウィジェットはフィルターを指定して分離表示できます。

  • フィルター「大学」: @大学
  • フィルター「仕事」: @仕事
  • 統合ビュー: @大学 | @仕事

本番運用(systemd)

常時起動サーバに配置し、定期実行します。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=oneshotfcntl ファイルロックにより多重起動を防止します。

移設・冪等性の注意

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 で使う

Moodle の iCalendar 出力は標準フォーマットなので、EALPS_ICAL_URL を別の Moodle のカレンダー URL に差し替えれば基本的に動きます。タイトルの言語に合わせて EXCLUDE_TITLE_KEYWORDS(英語なら opens 等)と courses.json を調整してください。

対象外(MVP)

独自アプリ / Web UI、複数ユーザー、Slack / Notion / GitHub / Google Calendar 連携、AI 機能、双方向完全同期は本 MVP の対象外です。

License

MIT

About

eALPS(Moodle)の課題をTodoistへ自動同期する個人向け統合タスク管理システム (Python / systemd)

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors