はじめに
hkob の雑記録の第41回目は、Sprint 更新時にカレンダータスクを自動割り当てしていたスクリプトをデータベースオートメーションで実現する方法を解説します。少し複雑なので、作りながら執筆していきます。
従来のタスク割り当てスクリプト
現在、毎週月曜日 7:13 に calendar_to_sprint という Ruby スクリプトを定時実行しています。これは、カレンダーに登録された Notion タスクのうち、今週分のものを Current に、来週分のものを Next に割り当てるスクリプトです。このスクリプトについては、こちらに掲載されています。
スクリプトはこちらになります。これと同じことをデータベースオートメーションで実現しようと思います。
#! /usr/bin/env ruby require "date" require "notion_ruby_mapping" include NotionRubyMapping NotionRubyMapping.configure { |c| c.token = ENV["NOTION_API_KEY"] } SPRINT_DATABASE_ID = "ここにスプリントデータベースのIDを入れます" sprint_db = Database.find SPRINT_DATABASE_ID sp = sprint_db.properties today = Date.today cwday = today.cwday monday = today + (8 - cwday) sunday = monday + 6 next_sprint = sprint_db.query_database(sp["Sprint status"].filter_equals("Next")).first nsdp = next_sprint.properties["Dates"] next_sprint_week_key = if nsdp.start_date.nil? nsdp.start_date = monday nsdp.end_date = sunday next_sprint.save next_sprint.properties["week_id"].formula["string"] else next_sprint.properties["week_id"].formula["string"] end next_sprint_title = next_sprint.title if next_sprint_week_key != next_sprint_title next_sprint.properties["Sprint name"].text_objects.rich_text_objects = next_sprint_week_key next_sprint.save end TASK_DATABASE_ID = "ここにタスクDBのIDを入れます" task_db = Database.find TASK_DATABASE_ID tp = task_db.properties query = tp["Start date"].filter_on_or_before(sunday) .and(tp["Sprint"].filter_is_empty) .ascending(tp["Start date"]) checked_sprint = {next_sprint_week_key => next_sprint} unsettled_tasks = task_db.query_database(query) unsettled_tasks.each do |task| week_id = task.properties["week_id"].formula["string"] sprint = checked_sprint[week_id] unless sprint sprint = sprint_db.query_database(sp["Sprint name"].filter_equals(week_id)).first checked_sprint[week_id] = sprint end task.properties["Sprint"].relation = [sprint.id] print "set #{task.title} to #{sprint.title}\n" task.save end
時刻トリガによる実装
昨日のタスクリストメール送信と同様に時刻トリガで作り直すにあたり、ネックになるのはトリガページが存在しなくなることです。このため、機能と同じ時刻トリガでページを作成することにします。昨日は Mail trigger というデータベースにしていましたが、それを流用するのでデータベース名を triggers に変更しました。

1. 二つのプロパティを作成
スプリントが割り当てられていない Current になるべきタスクと、Next になるべきタスクを取得する必要があります。このため、Task データベースに二つのプロパティを準備しました。
WillChangeCurrentSprintTasks
lets( currentSprint, prop("map").first().prop("Sprints").filter(current.prop("Sprint status") == "Current").first(), currentSprintWeekID, currentSprint.prop("week_id"), prop("Sprint").empty() && prop("week_id")== currentSprintWeekID )WillChangeNextSprintTask
lets( nextSprint, prop("map").first().prop("Sprints").filter(current.prop("Sprint status") == "Next").first(), nextSprintWeekID, nextSprint.prop("week_id"), prop("Sprint").empty() && prop("week_id")== nextSprintWeekID )
2. 時刻トリガの設定
前回は毎週月曜日 7:13 にトリガーを設定していました。トリガーは「〜ごと」で週を選びます。

時刻はこれまでのものに合わせ 7:15 に駆動するようにしました。

3. トリガページの作成
まずは参照のきっかけとなる踏み台ページを作成します。これは昨日のメール送信と同じです。ここで重要なのは全ての参照のきっかけとなる map へのリンクを作っておくことです。

4. トリガーページから Current sprint, Next sprint を取得
この後、Current sprint と Next sprint は大量にアクセスするので、変数に定義しておきます。最初に sprints を定義した後で、さらに sprints から Current および Next の Sprint を取得します。前の変数の値を使うためには、一度変数定義を終了しないといけないため、定義部分を二つに分けています。

5. Next sprint のタイトル設定
Sprint が更新される前の状態の Sprints は以下のようになっています。Sprint 更新時には現在 Future となっている「スプリント 101」が Next となり、新しく「スプリント 102」が Future として作成されます。

まず、この Next スプリントの名前を変更しましょう。スクリプトではこの部分になります。
next_sprint_title = next_sprint.title if next_sprint_week_key != next_sprint_title next_sprint.properties["Sprint name"].text_objects.rich_text_objects = next_sprint_week_key next_sprint.save end
この処理は「ページを編集」で実施します。まず、Sprint データベースのうち、Sprint status が「Next」のものを抽出します。

このページの Sprint name を week_id の値で書き換えます。先ほど変数で定義した Next が使えます。

6. Current sprint の登録
スクリプトでは以下の部分になります。Notion API では複数個の設定は同時にできないので、繰り返しでスプリントを一つずつ設定しています。
TASK_DATABASE_ID = "ここにタスクDBのIDを入れます" task_db = Database.find TASK_DATABASE_ID tp = task_db.properties query = tp["Start date"].filter_on_or_before(sunday) .and(tp["Sprint"].filter_is_empty) .ascending(tp["Start date"]) checked_sprint = {next_sprint_week_key => next_sprint} unsettled_tasks = task_db.query_database(query) unsettled_tasks.each do |task| week_id = task.properties["week_id"].formula["string"] sprint = checked_sprint[week_id] unless sprint sprint = sprint_db.query_database(sp["Sprint name"].filter_equals(week_id)).first checked_sprint[week_id] = sprint end task.properties["Sprint"].relation = [sprint.id] print "set #{task.title} to #{sprint.title}\n" task.save end
一方で、データベースオートメーションではタスクごとの処理はできないので、Current sprint の設定と Next sprint の設定を別処理にします。まずは Current sprint の設定を行います。1. で準備した WillChangeCurrentSprintTasks にチェックが入った Tasks をフィルタします。

選択されたタスクに対して Sprint を Current に設定します。

7. Next sprint の登録
同様に 1. で準備した WillChangeNextSprintTasks にチェックが入った Tasks をフィルタします。

選択されたタスクに対して Sprint を Next に設定します。

おわりに
昨日に引き続き、crontab で実施していた作業がデータベースオートメーションで書き換えられました。あとは明日の 7:15 に無事に動作することを期待して、crontab の設定を外しておこうと思います。