見出し画像

繰り返しアイテムの日付プロパティに未来の日時を設定する ~ イチ(以下)から学ぶNotionAPI×Google Apps Script【Day.6】

こんばんわ、ねここです。
昨日はちょっと仕事バタバタしてて何もできなかったですが、今日は頑張って書いていきます(仕事メールを返しながら)。

前回は日付プロパティをあれこれしました。やはり少しややこしかったですが、乗り越えられてえらい。

今日はここまでやったマルチセレクトと日時の操作を駆使して「繰り返しアイテムの日付プロパティに未来の日時を設定する」のをやりたいと思います。シンプルにぼくが今欲しい機能です。欲には忠実に生きたい。

あと今日はAPI周りよりも、Notion側での工夫の話のほうが多そうな予感がします。タイトルに偽りあるな?🤔


要件、仕様を考える

今回、自分が使っているワークスペースに導入したいのは、Day.3でも書いた「GameTodoリストで毎週繰り返し設定で発行している”ウィークリーミッションを管理するページ”の、日付プロパティを1週間後(翌週発行されるタイミング)に設定したい」というやつです。言葉にするとややこしいw

今のぼくがホーム画面で見ているGameTodoリストビューで、フラグ周りは以下のようなフィルタ設定をしています。

達成済み(Done)チェックボックスも、未達成(Retire)チェックボックスもONになっていないものだけ表示する、という実装です。
Todo項目としては作ったけど「大変すぎてとりあえずやーめた」とか「完走する前にイベント終わっちゃった」とかになることがあるのでw、それ用にRetireフラグを用意しています。Doneはしなかったけど、一覧からは見えなくするのに使ってますね。
で、今回はそれを活用していきます。

APIからデータベースを操作するタイミングは2回ありそうです。

  1. 繰り返し設定でページを作成するタイミング

  2. 1週間後、期限がきたタイミング

ただ、期限がくるタイミングというのは、同時にその週のページを作成するタイミングでもあるので、処理としては1回でまとめられそうですね。
例えば、毎週月曜5:00にウィークリー切り替えのタイミングが来るとしたら、

  • 月曜5:00に繰り返し設定でアイテムを作成する

  • 作成したあと(5:00以降)にスクリプト起動

    • 前週に作成して達成されていないページにRetireフラグを立てる
      (達成済みのものにはRetireフラグは立てない)
      (繰り返し設定以外で作成したページには手を出さない)

    • 5:00に作成したページに1週間後(翌週月曜5:00)の期限設定をする

といった感じでしょうか。

繰り返し設定で作成したページを識別する

さて、流れの中に「繰り返し設定以外で作成したページには手を出さない」という要件を書きました。
これはぼくがゲーム内イベントに合わせてページを作って、手動で期限設定をすることがあり、それについては勝手に処理してほしくない(期限切れたページも滞留してていい)と思ってるからですね。わがままですね。

ということでコレを実現するためには「それぞれのページが手動で作成したもの」か「繰り返し設定で作成したもの」かを識別する必要が出てきます。

  • 作成時間で識別する

  • ページ名で識別する

  • 繰り返し設定で作ったものだとわかるプロパティを付ける

明らかにクソみたいな方法も含めてw、手段を考えてみました。
まず、「作成時間で識別」する。
例えば5:00に繰り返し作成する設定にしているのであれば、作成日時が5:00になっているものを繰り返し作成したものと見なす、という感じですね。
完全にダメなわけではなさそうですが、ぼくがうっかり朝方まで起きていて5:00きっかりに別のtodoを作成しようものなら、それも繰り返し作成したものと見なされちゃいます。
またウィークリーの項目によっては0:00切り替えとか、17:00切り替えだったりすることもあるので、使っているとどこかで1回ぐらい事故りそうな気がします。自分のことなのでわかります。忘れたころに絶対やります。
ということで、これはなしで。

次に「ページ名で識別する」ですが、まぁこれも基本的になしですかね。
同じページ名で手動で作る可能性もゼロではないですし、例えばページ名に @今日 などを使って日時データを入れこむようにしちゃうと、このやり方はできなくなります。
使わなきゃいい、ではあるけれど、設計時点で可能性を閉ざしたくはないですね。将来便利な仕様が追加されるかもしれんし。

ということで現実的なところで、「繰り返し設定のときだけ操作するプロパティを用意しておく」というのがいいかなと思ってます。
(ほかにいいアイデアあるぜ、という方はコメントで教えてください😘)

さて、識別用のプロパティですが、これにはマルチセレクトを使おうと思います。
「チェックボックスじゃダメなん?」という意見も聞こえてきそうですが、たくさんの定期Todoを動かすようになったら、Todoの種類ごとにチェックボックスプロパティを増やしていく必要がでてきます。ひとつふたつならいいですが、10個とか20個とかになったら地獄です(そしてぼくはそれをやりかねない)。気軽に増やせるような仕様にしたいですね。

これが無限に続くのはしんどい

......というときに大活躍するのがマルチセレクトですね。
Todoの種類ごとに設定するタグを変えるようにすれば、プロパティひとつで無限の可能性です。

これなら種類がいくら増えても邪魔にならない

プロパティを常に非表示にしておけば、手動で触ることも基本的にないですね。

これで隠しておける

繰り返し作成したアイテムをGASで抽出する

さて、ようやくNotionAPI×GASの出番です。

プロパティを用意して、テンプレートを作成。

仕事中ですか? いいえ、休憩中です。

APIflagには、テンプレートの時点であらかじめ識別タグを設定しておきます。わかればなんでもいいと思います。

これをNotionAPIを通して抽出しにいきます。
条件は「APIflagが”RepeatTest"」と併せて「Timelimitが”未入力"」を設定します。
APIflagのチェックだけだと、長期間運用でアイテム数が増えた時に、DoneがRetireにチェックが入って見えなくなってるものを軒並み抽出してしまい、件数が増えてしまいます(Timelimitを過ぎたときに識別タグを消す、という方法はあります)。

Notion上でフィルタ設定するとこんな感じ
// 更新対象の抽出するフィルタを設定する
// APIflagに指定タグが入っている & Timelimitが未入力
filter = {
  "filter": {
    "and": [
      {
        "property":"APIflag",
        "multi_select": {
          "contains": "RepeatTest"
        }
      },
      {
        "property":"Timelimit",
        "date": {
          "is_empty": true
        }
      }
    ]
  }
};

そういえば複数条件でのフィルタはここまでやってなかったので、ちゃんとリファレンスを読みました。”and"と"or"を組み合わせたJSON形式のデータを作成することで複雑なフィルタを構築することができます。"and"と"or"でそれぞれ配列に入れるのがミソのようです
まぁあまり複雑にならないようにプロパティの設定を工夫したほうがいい説もあります(余談)。

今回はand条件だけなので複雑ではないですね。

今回テストするデータはこの4件。

手動で作成したものと、繰り返し設定で作成した(というテイ)ものを2つずつ。それぞれTimelimitを入れているもの、入れてないものに分けています
(パッと見てわかりやすいように(?)、10分後に手動設定してます)。

読み込み、更新の基本的な流れはDay.5で作成したものとほぼ変わらないです。上のフィルタでクエリを掛けてヒットしたものに対して、作成日時 + ((7 * (24 * 60 * 60000))) を Timelimitプロパティのstartに設定しています。

15:06に作成したもののみがヒット
手動作成したものはもちろん無傷

無事うまくいきました。よかった。
これで期限を設定する部分はOKですね。

期限が過ぎたアイテムをGASで抽出する

さて、次はTimelimitを過ぎて残っているアイテムを抽出していきます。
と言っても、基本的な流れは同じで、抽出条件だけを変えます。
今回は条件が多そうだ。

  • APIflagが”RepeatTest" (手動作成のものは触らない)

  • Timelimitが現在時刻より前 (期限を過ぎている)

  • Doneにチェックが入っていない (完了済みのものは触らない)

  • Retireにチェックが入っていない (期限切れ処理済みのものは触らない)

現在時刻基準でフィルタできない......!

そうだー、Notionのフィルタ設定では現在時刻をベースにフィルタ掛けるのできなかったわ!
これAPI経由だといけるのかしら?
ということで、それっぽい感じで条件を組んでいきます。

nowtime = new Date(Date.now());
filter = {
  "filter": {
    "and": [
      {
        "property":"APIflag",
        "multi_select": {
          "contains": "RepeatTest"
        }
      },
      {
        "property":"Timelimit",
        "date": {
          "before": nowtime.toJSON()
        }
      },
      {
        "property":"Done",
        "checkbox": {
          "equals": false 
        }
      },
      {
        "property":"Retire",
        "checkbox": {
          "equals": false
        }
      }
    ]
  }
};

Timelimitと比較する用の日時は現在時間(nowtime)を設定します。リファレンス見る限りはこれで通るはず……!
そして、このフィルタを喰らわせるテストデータは5件。

他にも試したいテストパターンはあるけど最低限で

アイコンが赤いものは抽出されないはずのもの、青いものが抽出される予定のものです。
いざ….!

ちゃんと抽出された
Retireにチェックが入った

いけたわ!
フィルタ周りもNotion本体よりも柔軟に設定できるんですね。JSON形式と仲良くできればw

トリガーを使ってスクリプトを定時動作させる

これで、無事に「繰り返し作成したアイテムに期限を設定する」「期限切れのアイテムにRetireフラグを立てる」処理ができました。やったね。

あとは、この処理を定時動作させれば完成です。

それを実現するGASの機能がトリガーです。
条件が揃ったら実行する、ということができます。

まずはデプロイ

そのためにはまず作ったスクリプトを「デプロイ」する必要があります。
(実は保存されている最新のスクリプトを動作させることもできますが、改修途中で動作されても困るのでデプロイすることをお勧めします)。

上部に「デプロイ」ボタンがあります。

ウェブアプリを設定

種類は「ウェブアプリ」を設定。説明文は任意なので空白でもいいし、未来の自分が「これなんだっけ……」ってならないように説明を書いてもいいのです。
他は基本的に触らなくて大丈夫なはず。
デプロイボタンをクリックすると、その時点でのウェブアプリが生成されます(このあとコードを弄っても、このタイミングで作成したアプリの動作は変わりません)。

デプロイしたらトリガー設定

デプロイできたら、次はトリガーの設定をします。

右下にトリガーを追加するボタンがあるのでクリックします。

なんかいっぱい聞かれる!

「実行するデプロイを選択」で、先ほど作成したデプロイバージョンを選びます。

イベントソースは今回は「時間主導型」を選びます。このタイミングで定期的に動いてちょ、って感じです。

時間主導型にすると「どのタイミングで動作させる?」と聞かれますので、自分の都合に合う設定をします。

ところで時刻選択するところが1時間幅なんだけど、何時何分、みたいな指定はできないんかな。順番待ちとかがあるのかしら。

ということで、試してみました。

…….待てど暮らせど動作しないw

ということで、分単位で時間を指定しつつ定時処理をする方法を探します。

分単位で時間を指定しつつ定時処理をする方法

ありました(早い)
今回はこちらの記事で紹介されている方法を試します。

要約すると、

  1. 初回動作日時を設定する

  2. 次回を登録後、今回動作させたトリガーをスクリプトで削除する

  3. 動作させるスクリプト内に、トリガーを登録する処理を追加し、次回動作日時をスクリプトから設定する

という感じでした。
さっそくおためし。

function setNextTrigger() {
  // 現在設定されているトリガーを抹消する
  triggers = ScriptApp.getProjectTriggers();
  for(trigger of triggers){
    funcName = trigger.getHandlerFunction();
    if(funcName == 'myFunction'){
      ScriptApp.deleteTrigger(trigger);
    }
  }

  // 新しくトリガーを設定する
  nowTime = new Date(Date.now());
  nextTime = new Date();
  // ひとまず5分後をセット
  nextTime.setTime(nowTime.getTime() + (5 * 60000));
  ScriptApp.newTrigger("myFunction").timeBased().at(nextTime).create();
}

記事で使用されていた ScriptApp.getScriptTriggers(); は、現在非推奨になっていたので、現状サポートされている ScriptApp.getProjectTriggers(); に差し替えました。使い方はたぶん変わらないと思います。

実行間隔はテストで5分にセット。実際に使うときには想定している時間を設定します。
これを組み込んだバージョンをデプロイして、トリガーを手動設定します。

17:52に設定して、時間が来るのを待つ
無事に実行された
5分後の時刻が設定された!

無事に設定できました!
こんな感じで細かく指定できるのよいですね。1分単位で動かしたくなっちゃいます(迷惑)。

ということで、DBの繰り返し作成×NotionAPI×GASで定時アイテムを回すフローができました。最後のトリガー設定の部分以外はここまで学んだことで十分立ち向かえましたね。ちゃんと勉強できてえらい。

例えば繰り返しで毎週月曜5:00に作成するようにした場合、トリガーはページが作成された2分後ぐらいを設定して、1週間後に次のトリガーを設定する、という感じでいけそうです。

あとは時間あるときに実際に自分のページに組み込んでみて、動作検証してみることにしましょう。
使い勝手はまた報告しますね(備忘)
ちなみに今回はゲームのTodoで話をしましたが、もちろん日々の業務とかでも同じように使えるはずです。毎週月曜にTaskが発生して、翌日中に完了できればいい、みたいなものであれば、今回のが使えそうです。
こういうのを構築する部分でお仕事とか発生したり?

余談という名のあとがき

BlueSkyのアカウントを作ってみました。

最近Blueskyが招待制から自由制(?)に移行したので、アカウントを作ってみました。
Twitterでは用途でアカウント分けたりしてますが、こっちではいろんなことを雑多にしゃべるアカウントにしようと思うので、もしよければ見に来てどうぞ(°ω°)
そしてカバー画像は恐怖映像w

マガジンを作ってみました。

この「イチ(以下)から学ぶNotionAPI×Google Apps Script」シリーズをお試しでマガジンにしてみました。
メリットがあるかはわかりませんが、作ってみたかったので作りました。
もっとこうしたらいいよ、みたいなナレッジをお持ちのかたはぜひ教えてください。なにとぞ。

今日のカバー画像

どんどんプログラムしてる感が出てきて......るといいなぁ。そろそろ髪型とか変えてみる?


NEXT


いいなと思ったら応援しよう!