TrelloのチェックリストをGoogleカレンダーと同期する

# 目次

- [目次](#目次)
- [この記事を読むと何ができる？](#この記事を読むと何ができる)
- [前書き](#前書き)
- [目的と方針](#目的と方針)
    - [GoogleカレンダーにTrelloのチェックリストを同期してGoogle todo的に扱いたい。](#googleカレンダーにtrelloのチェックリストを同期してgoogle-todo的に扱いたい)
    - [それ、Cronofyで良くない？](#それcronofyで良くない)
    - [解決策](#解決策)
- [ツール](#ツール)
    - [Cronofy](#cronofy)
    - [Trelloでの親子関係](#trelloでの親子関係)
    - [Butler](#butler)
- [最低限の実装と使用方法](#最低限の実装と使用方法)
    - [チェックリスト項目の追加](#チェックリスト項目の追加)
    - [チェックリスト項目の完了](#チェックリスト項目の完了)
- [現在使用中の実装](#現在使用中の実装)
    - [チェックリストの追加+α](#チェックリストの追加α)
    - [チェックリスト項目の完了+α](#チェックリスト項目の完了α)
    - [ボード上から子カードを直接アーカイブ](#ボード上から子カードを直接アーカイブ)
    - [単純なtodoリスト作成](#単純なtodoリスト作成)
- [まとめ](#まとめ)
- [おまけ : 作成したけど最終的にオミットした機能](#おまけ--作成したけど最終的にオミットした機能)
    - [Trelloを開かずにカレンダー上の操作だけで完了させる](#trelloを開かずにカレンダー上の操作だけで完了させる)
- [Reference](#reference)

# この記事を読むと何ができる？

- GoogleカレンダーにTrelloの **チェックリスト** を（疑似的に）同期できる
- Trelloでの親子関係の実装方法が（ちょっと）わかる
- 期限が設定されたTrelloのカードに対して、Trelloを開かなくてもGoogleカレンダー上からアーカイブ・期限の完了・アクティビティログの記録などの操作ができるようになる。

# 前書き
Qiita初投稿ですので細かいところは多めに見てください（懇願）  
Trello使用歴は1年くらいで、Butlerの自動化は今回初めて試しました。もっといい実装があったら是非教えてください。  
※ [Trelloでの親子関係](#trelloでの親子関係) はかなり込み入った話なので、具体的な実装方法だけ知りたい方は読み飛ばしてください。

# 目的と方針

Trello、便利ですよね。最近は研究のメモなどもほとんどTrelloに集約させているので、最近（2021年8月）のアップデートで無料版での機能が拡充されたのは願ったり叶ったりです。  
いくつかのPower-Upを導入するだけでもう十二分に便利なのですが、あまりに便利なので欲が出てしまいます。例えば

### GoogleカレンダーにTrelloのチェックリストを同期してGoogle todo的に扱いたい。
具体的に実現したいのは

- 期限付きのチェックリスト作成 --> Googleカレンダー（以後カレンダーと表記）に予定を作成
- チェックリスト側で期限を変更 --> カレンダーに反映
- チェックリストにチェックを入れる --> カレンダーの予定が消える
- カレンダーで時刻を変更 --> チェックリスト側の期限に反映
- カレンダーで予定を完了する --> チェックリストにチェックが入る

このあたりです。ここまで読んだ時点でTrelloのヘビーユーザーからはこう突っ込みが入るでしょう。

### それ、Cronofyで良くない？
Trelloの期限をカレンダーと同期するために、Cronofyという非常に便利なPower-Upが提供されています。長くても数十秒程度で同期してくれる素晴らしい機能です。こいつで解決するかと思いきや、微妙な問題が発生します。  

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2229832/fd1575dc-2dfa-e73a-20d3-3b9a5827f62b.png)  
皆さんお馴染み、Trelloのカード画面です。
Trelloでは

```
リスト1/
　├ カード1/
　│　 ├ チェックリスト1/
　│ 　│　 ├ チェックリスト項目1/
　│ 　│　 ├ チェックリスト項目2/
　│ 　│　 └ チェックリスト項目3/
　│　 └ チェックリスト2/
　│ 　 　 ├ チェックリスト項目4/
　│ 　 　 └ チェックリスト項目5/
　└ カード2/
```

のように
>ボード >> リスト >> カード >> チェックリスト >> チェックリスト項目

という階層構造になっています。    
このうち、 **期限を時刻単位で指定できるのはカードのみ** です。より正確には、有料版であればチェックリスト項目にも時計マークから「期日」は設定可能です。ところがカードでの「期限」とは微妙に仕様が異なり、時刻単位の細かい指定はできません。（2021年10月時点）

普段私たちがチェックを入れるのは **チェックリスト項目** ですので、今回の目的をより正確に書くなら

- **チェックリスト項目** に対して
- **時刻単位で** 期限を設定し
- カレンダーと同期させたい
- （ついでに無料だと嬉しい）

というものです。  
そして大変残念ながら、 **「Cronofyは、Trelloのカードの「期限」に対応しているのみで、チェックリストの「期日」は反映しない（2021年10月時点）」** ため、今回の目的を達成できません。（「お前のチェックリストの運用方法を変えろ」で済む話ではありますが、そこは突っ込まない方針で）

### 解決策
Cronofyは非常に便利なのですが今回の目的である

>チェックリスト項目 <---> カレンダー  

の直接同期はできません。チェックリストではなくカードなら可能なのですが...  

...ということで、今回はチェックリストをカードに無理やり変換し、

>チェックリスト項目 <---Butler---> 子カード <---Cronofy---> カレンダー  

の3つを相互に同期する形で解決しました。これによりチェックリスト項目とカレンダーを疑似的に同期させます。

# ツール
使ったツール、役割についての説明です。

### Cronofy
導入はこの方の記事（[リンク](https://seeder.site/work/efficiency/235.html)）が分かりやすかったです。"Cards to sync" 以外はハマる所は無いと思います。
Cronofyができるのは

- Trelloのカードの期限と、Googleカレンダーを同期する
- 期限の作成・削除はTrello側で行う（期限の移動は可能）

先ほども書きましたが、残念ながらカードへの対応のみで **チェックリスト項目の「期日」には非対応** です。（有料版のトライアル期間中にウキウキしながら試しましたがダメでした）

### Trelloでの親子関係
※ 少々複雑な話なのでとりあえず実装したい方は [次の項](#butler) まで飛ばしてください。   

先ほども述べたように、Trelloの階層構造は

>ボード >> リスト >> カード >> チェックリスト >> チェックリスト項目

となっているため、構造上はカードの一段下にカードを作ってサブタスク的に扱うことができません。（競合サービスのAsanaならできるらしいです。）  
Trello公式としてもこの点は認識しているようで、最近のアップデートでは後述のButlerをフル活用することで疑似的にカード間での親子関係を表現できるようになっています。  

```
リスト1/
　 ├ 親カード1/
　 │ 　 ├ 子カード1/
　 │ 　 ├ 子カード2/
　 │ 　 └ 子カード3/
　 └ 親カード2/
```

のような親子関係をButlerを用いて実装すると、ボード上ではこうなります。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2229832/bdd6bf50-ed60-abdd-da62-886c4f6abd2a.png)  
「リスト1」の中にある「カード1」が親カード、「ToDo」の中にある「子カード1」などが子カードです。親カード1のチェックリスト項目と同名のカードが「ToDo」の中に並んでいます。（ボード上でもチェックリストが見えるように"Show Checklist"のPower-Upを導入しています。）

ボード上だと単にカードが対等に並んでいるだけに見えるのですが、「親カード1」を開くとこのようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2229832/0d00a0bb-b9e1-14ae-a658-e9c7d4624c57.png)  
親カードの中では、子カードはチェックリスト項目として表現されています。このチェックリストは子カードへのリンクになっており、クリックすると子カードを開くことができます。

そして「子カード1」を開くと
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2229832/99a6ec04-7f72-563e-8db4-57bc2cfba893.png)
子カードの側では親カードは添付ファイルとして扱われ、クリックすれば親カードに戻れるようになっています。

画像だけだと正直分かりにくいので、Trello公式での解説ページ（[リンク](https://help.Trello.com/article/1165-task-dependencies)）の動画を見てください（丸投げ）。

### Butler
Trelloのボード右上の"自動化"というボタンからButlerに入れます。私は今まで触れたことが無かったのですが、いろいろ便利な自動化ができそうです。  
前項の要点としては

>ボード >> リスト >> カード >> チェックリスト >> チェックリスト項目

のチェックリスト項目の部分に子カードを入れることで

>ボード >> リスト >> 親カード >> チェックリスト >> 子カード >> ... 

のような親子関係を疑似的に組める、というものでした。

そしてありがたいことに、一度Butlerでロジックを組んでしまえばこれらの親子関係の作成は完全に自動化できます。

今回は条件が満たされたかを常時判定して処理してくれる"ルール"機能を使います。この記事では親子関係を実現するためのButlerの具体的なロジックには踏み込みませんが、この後の記事を読むに当たって最低限必要な認識としては

- 親カードのチェックリスト項目を子カードに自動変換し、親子関係を表現することができる。
- 子カードの状態変化をトリガーとして、親カードに変化（チェックリスト項目にチェックを入れるなど）を与えることができる。

くらいで十分です。

# 最低限の実装と使用方法
この項では必要最低限の実装を書き、次の項で私が今使っている実装を書きます。  
Cronofyの連携が済んだら、ToDoというリストを作ったうえで、Rulesで以下の2種類のRuleを作ります。

### チェックリスト項目の追加
```
when an item is added to checklist "todo" by anyone,  
convert the item to a linked card in list "ToDo", and set due date now
```
親子関係の実現に関しては[Trello公式](https://help.Trello.com/article/1165-task-dependencies)の一つ目のコードをパクりました。後半の `and set due date now` で作成した子カードに期限を設定し、Cronofyで同期できるようにしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2229832/726ddf50-1f0c-53fb-4ef0-86109bd353f3.png)

1. 親カードで「todo」というチェックリストを作成すると
1. カードに期限が設定されると、Cronofyが子カードをカレンダーに自動で同期し始めてくれます。  
1. 数十秒後にはカレンダー側で同期が済んでいるので、カレンダー上で子カード（=チェックリスト項目）をぐりぐり動かして好きな時間に配置しましょう。

### チェックリスト項目の完了
```
when an item is checked in a checklist named "todo", 
find a card with link "{checklistitemname}", remove the due date from the card, and archive the card
```
トリガーは `when an item is checked in a checklist named "todo"` で、親カードでチェックリスト項目にチェックを入れる作業です。 

`find a card with link "{checklistitemname}"` で、先ほどチェックリスト項目から作成した子カードを探します。`{ }` で囲まれた表記はbutler変数と呼ばれるもので、今回の `{checklistitemname}` ではトリガーでチェックを入れたチェックリスト項目と紐付いた子カードのリンクが格納されています。

`remove the due date from the card` で期限を削除すると、カードとCronofyとの同期が解除されてカレンダー上から消えてくれます。完了したタスクもカレンダーに表示し続けたいかは個人の好みに寄ると思いますが、私はカレンダーがごちゃごちゃしているのは嫌いなので消しました。 

`archive the card` しておかないと終了済みのチェックリスト項目がToDoのリストに残り続けて邪魔になります。カレンダー側で見るだけならTrello側で表示し続けても良いのですが、せっかく同期しているのでTrello側でも見やすい方が便利です。  

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2229832/855b871d-93c4-f266-1d4c-4fba6c6d48e1.png)  

1. 親カードで子カード1のチェックリスト項目にチェックを入れると
1. 子カード1の期限が削除され、アーカイブされます。
1. 期限が消えたので、Cronofyによる子カード1とカレンダーの同期が解除されます。

「チェックリストをカレンダーと同期する」という最低限の目標はひとまずこの項で達成できたので、あとは使いやすいようにカスタマイズしてください。

# 現在使用中の実装
上記の実装だと

- チェックリストの作成時刻がそのまま期限になるので、（多くの場合）時刻のキリが悪い
- 完了時刻を記録しておきたい
- 子カードから完了の処理を済ませたい
- 単純にカレンダーと連携したtodoリストとして扱いたい ~~（Google todoは扱いにくいので）~~

などの欲望が湧いてきます。3つ目は若干分かりにくいのですが、[チェックリスト項目の完了](#チェックリスト項目の完了) で

>親カードでチェックリスト項目にチェック --> 子カードの期限を削除してアーカイブ  

を自動化したのに

>子カードの期限を完了 --> 親カードのチェックリスト項目にチェック

が無いようでは片手落ち、というものです。

色々追加して遊んでみたので、備忘録的に現在の実装を置いておきます。

### チェックリストの追加+α
```
when an item is added to checklist "todo" by anyone,  
convert the item to a linked card in list "ToDo" copying labels, and set due date today at 8:00 am
```
設定する期限をその日の朝8時に変えています。

本当は
>13:23にチェックリストを作成 --> 期限を当日の14:00に設定  

のように現在時刻の1時間後付近のキリのいい時刻を設定させたかったのですが、カスタムフィールド（有料版限定）での演算なしでは上手くいかないようです。  
私は遅起きで朝8時からタスクを処理するなんてあり得ないので、カレンダー上のこの時間帯は生成された子カードを放り込むための枠だと割り切りました。時間帯は別に深夜でも良いのですが、昼間に近い方がカレンダー上で時間をずらすときに楽なのでギリギリを攻めています。

### チェックリスト項目の完了+α

```
when an item is checked in a checklist named "todo",  
find a card with link "{checklistitemname}", post comment "completed on {isodate} {time24}", remove the due date from the card, and archive the card
```

post commentで、チェックが入ったらアクティビティログに日付と時刻を記録してからアーカイブするようにしています。

### ボード上から子カードを直接アーカイブ
>子カードの期限を完了 --> 親カードのチェックリスト項目にチェック

の実装+αです。

```
when the due date is marked as complete in a card by anyone,  
post comment "completed on {isodate} {time24}", remove the due date from the card, archive the card, for each card linked in the attachments, check item "{triggercardlink}"
```

トリガーは `when the due date is marked as complete in a card by anyone` で、「カードの期限を完了済にする」という作業を想定しています。期限の削除とアーカイブ、post commentについては [チェックリスト項目の完了+α](#チェックリスト項目の完了α) と全く同じです。

最後の部分の `for each card linked in the attachments, check item "{triggercardlink}` は、[Trello公式](https://help.Trello.com/article/1165-task-dependencies)の四つ目のコードから パクりました。ここで親カードのチェックリストにチェックが入ります。Rule作成時にどこにあるか分かりにくかったのですが、Cascadeの中にありました。  

副産物として「カードの期限を完了済みにする」という動作はカードを開かなくてもボード上で完結するので、完了時には子カードを開く必要すらなくなりました。

### 単純なtodoリスト作成

```
when a card is created in list "ToDo",  
set due date today at 8:00 am
```

期限さえ設定すればカレンダーとの同期はCronofyがやってくれるのでtodoリストを作成するのは簡単です。完了時には期限を完了に変えると、[ボード上から子カードを直接アーカイブ](ボード上から子カードを直接アーカイブ) で処理できます。

今の仕組みだと完了後にアーカイブしてしまい探しづらくなるので、「歯医者の予約」のような本当にしょうもないタスクに対して使っています。これであの使いづらいGoogle todoから完全に脱却しつつ、todoリストとカレンダーを一体化できます。

# まとめ
既存のPower-Up + Butler = 痒い所に手が届く  

2021年8月から無料版でもPower-Upの使用数が無制限になり、Cronofyのような便利なPower-UpをButlerで自分好みにカスタマイズしやすくなったので色々と夢が広がりますね。  
次はPower-Upの中のガントチャートを試してみたいです。今使っているElegantt（Trello用のchrome拡張）は操作性がイマイチなので...

# おまけ : 作成したけど最終的にオミットした機能
明らかに蛇足なのですが、実装がそこそこ大変だったのでここで供養しておきます。

### Trelloを開かずにカレンダー上の操作だけで完了させる
カレンダーとTrelloの相互同期を謳うからには、カレンダーから子カードをアーカイブできるようにしたいものです。ところが、Cronofyが提供している機能は「カードの期限をカレンダーと相互に同期する」のみで、カードに対するその他の動作はできないため、この実装は少し難しかったです。  

>カードの期限の変更 --> 期限へのチェック

という流れを作る必要があるのですが、要求がマイナーすぎるのか一発で処理してくれる関数はButlerに無さそうでした。
若干ハマったのですが最終的には  

>カードの期限を変更 --> アクティビティログに変更後の時刻を記録 --> 特定の時刻に変更された場合、期限にチェック

というフローで解決しました。以下の2つの実装です。

```
when a due date is moved in a card in list "ToDo",  
post comment "due date is moved to {cardduedateisodate} {cardduedatetime}"
```
カードの期限が変更されたときに、変更後の日時をアクティビティログに記録します。この方式だとカレンダー上でぐりぐり動かすたびにログが溜まっていくのですが、子カードはどうせすぐにアーカイブするので目をつぶっています。

```
when a comment containing "7:00am" is posted to a card in list "ToDo",  
post comment "completed on {isodate} {time24}", remove the due date from the card, archive the card, for each card linked in the attachments, check item "{triggercardlink}"
```
`when a comment containing "7:00am" is posted to a card in list "ToDo"`でToDoリスト内のアクティビティログを監視し、期限が朝7時に設定されたとき（＝アクティビティログに7:00amが記録されたとき）がトリガーになります。その後の動作は[チェックリストに自動でチェックを入れる+α](#チェックリストに自動でチェックを入れるα)と全く同じです。  

私は遅起きで朝7時からタスクを処理するなんてあり得ないので（2回目）、カレンダー上のこの時間帯は完了した子カードを放り込む枠だと割り切りました。

ここまでの実装自体は全く問題なかったのですが、この実装だとカレンダー上で子カードを動かすたびにButlerが動作するため、Bulterの動作回数上限（無料版で月250回、Standardで月1000回）を簡単にオーバーしてしまいます。Premiumを契約すれば動作回数の上限は無くなるのですが、この自動化のために月1000円払うのは流石に勿体ないので泣く泣く諦めました。

今回はチェックリストに対して実装しましたが、同じ発想でTrelloを開かずともカレンダー上からカードに対して様々な処理ができそうなので、できればどこかで活用してみたいものです。。


# Reference

- [TrelloとGoogleカレンダーをリアルタイムで自動同期する「Cronofy」の設定方法 | SEEDER（シーダー）](https://seeder.site/work/efficiency/235.html)  
- [タスクの依存関係の作成と管理 - Trelloヘルプ](https://help.trello.com/article/1165-task-dependencies)