はじめに
今朝、Twitter をみたら、こんなツイートがありました。
このアイディア面白いなと思いました。これまで、CRUD の C(reate) しか作っていなかったので、R(ead)とU(pdate)を試してみたくて実装してみました。#まいにちNotion 期限が過ぎていて、doneにチェックがついていないものに、🟠が自動的に入力される、みたいなやつを作りたい。
— tomito (@to3Eto) 2021年6月28日
事前準備
ここで、Rails でいう index と update に相当する API 呼び出しが発生します。前回までのスクリプトでは、スクリプトの中に MY_NOTION_ID や DATABASE_ID を直書きしていましたが、何度も出てくるのでメソッド化するとともに、前回使ったスクリプトプロパティに書き出すことにしました。これで、スクリプトを気兼ねせずに人に見せることができます。
この対応のために、以下のスクリプトを追加しました。以前のエディタでは、GUI でプロパティを設定できたのですが、新しいエディタはなくなってしまったので、コードで登録します(これが面倒だと思うなら、以前のエディタに一時的に戻すのもありです)。スクリプトを記述したら storeTokenAndId を実行します。キーと値が表示されたらこのメソッドは消してしまって良いです。
// MY_NOTION_TOKEN と DATABASE_ID をプロパティに登録する(1回だけ使い、終わったらIDを消す) function storeTokenAndId() { const scriptProperties = PropertiesService.getScriptProperties() scriptProperties.setProperties({ "MY_NOTION_TOKEN": "ここに「Notion Token」を記述", "DATABASE_ID": "ここに「データベースID」を記述" }) // 登録できたことを確認 const data = scriptProperties.getProperties() for (var key in data) { Logger.log("キー: %s, 値: %s", key, data[key]) } }
これらを読み出すメソッド myNotionToken() および databaseId() メソッドを作っておきます。
// MY_NOTION_TOKEN を取得 function myNotionToken() { return PropertiesService.getScriptProperties().getProperty("MY_NOTION_TOKEN") } // DATABASE_ID を取得 function databaseId() { return PropertiesService.getScriptProperties().getProperty("DATABASE_ID") }
汎用的な読み出しメソッド
他の作業でも使いそうなので、getNotion という汎用メソッドを作っておきます。先ほどの myNotionToken() メソッドや databaseId() メソッドを使っています。filter やorder は引数の payload で指定します。
// Notion のデータベースを query するメソッド (payload にフィルタや並び順を設定しておく) function getNotion(payload) { let url = "https://api.notion.com/v1/databases/" + databaseId() + "/query" // API URL let options = { "method" : "POST", "headers": { "Content-type": "application/json", "Authorization": "Bearer " + myNotionToken(), "Notion-Version": "2021-05-13", }, "payload" : JSON.stringify(payload) } // デバッグ用 // Logger.log(url) // Logger.log(options) return JSON.parse(UrlFetchApp.fetch(url, options)) }
未終了のタスクを取得
先程の getNotion を使って、未終了のタスクを取得します。Done プロパティが false で日付が今日より前のものを取得します。並び順は今回は関係ないですが、説明用につけています。
// 未終了のタスクを一括で取得する function getUnfinished() { let payload = { "filter": { "and": [ { "property": "Done", "checkbox": { "equals": false } }, { "property": "日付", "date": { "before": new Date() } } ] }, "sorts": [ { "property": "日付", "direction": "ascending" } ] } return getNotion(payload) }
データを更新する汎用メソッド
getNotion と同じように汎用のパッチメソッドを作ります。対象となるページの id を pageId として渡し、payload に更新情報を渡します。
// Notion のデータベースを query するメソッド (payload に更新するデータを設定しておく) function patchNotion(pageId, payload) { let url = "https://api.notion.com/v1/pages/" + pageId // API URL let options = { "method" : "PATCH", "headers": { "Content-type": "application/json", "Authorization": "Bearer " + myNotionToken(), "Notion-Version": "2021-05-13", }, "payload": JSON.stringify(payload) }; // デバッグ用 // Logger.log(url) // Logger.log(options) UrlFetchApp.fetch(url, options) }
遅延プロパティの更新
これまでに作ったものを組み合わせて、今回の目的である遅延プロパティを更新します。rich_text プロパティが配列であることに気づかず、なぜ更新できないのだろうとハマって時間がかかりました。
rich_text プロパティがあるときには、plain_text 属性を取得し、その後ろに「●」をつけています。🟠は見た目がズレるので、今回はとりあえず「●」にしています。後で別の絵文字などに変えるかもしれません。Notion の制限を回避するために、1秒に1更新にしています。
function updateDelayedTask() { // unfinished let unfinished = getUnfinished()["results"]; unfinished.forEach(t => { let pageId = t["id"] Logger.log(t) let delayedText = t["properties"]["遅延"]["rich_text"][0] let plainText = delayedText ? delayedText.plain_text : "" let payload = { "properties": { "遅延": { "rich_text": [ { "text": { "content": plainText + "●" } } ] } } } patchNotion(pageId, payload) // Notion は 3アクセス/1秒なので、余裕を持って1秒待つ Utilities.sleep(1000); }) }
テスト実行
updateDelayedTask を選択し、実行してみました。テストのために、Done がついていないものを作ってテストしてみました。また、一つ「aaa」という余計な文字列が入っている時のテストも実施してみました。
もう一回実行してみました。●がちゃんと増えていますね。サボっといるだけどんどん●が増えていき、ヤバさが伝わってきます。
毎日実行するように設定
毎日実行される関数があるので、そこに今回の関数を追加しました。GAS で朝4時から5時の間に doEverydayPost が実行されるので、その際に未完了タスクにも●が追加されるようになります。
// これまで通り、毎朝呼ばれる関数 function doEverydayPost() { selectEvents(new Date()) updateDelayedTask() }
おわりに
Twitter の「#まいにちNotion」タグから、またヒントをいただきました。いろんな人がいろんなアイディアを持っているので、参考になりますね。