タスク依存関係に対応したコマンドライン版 NotionTimeRecoding の更新 : Notion 解説(72)

1. はじめに

Notion 2.19 にて、タスクの依存関係とサブアイテム対応が来ました。京都のイベントで紹介されていたものですね。

www.notion.so

私の場合は、タスクが日をまたがないので、サブタスクよりもより大きなプロジェクトとの関連になります。このため、こちらではなく依存関係の方を積極的に使っていこうと思います。せっかくなので、NotionTimeRecording もこれに対応する形で更新します。

2. データベースの修正

まずはデータベースに依存関係を追加します。デフォルトでは「Blocking」と「Blocked by」というプロパティ名(日本語版だともっと長い名称でした)となっていますが、わかりにくいので「後タスク」と「前タスク」という名称にしました。

依存関係の追加

3. 依存関係を選択して追加するコマンド (ntd) の追加

新規タスクを作成する際に、依存関係を設定できるようにします。今すぐやるタスクに依存関係を設定する必要はないので、st コマンドはそのままとし、日付なしタスクを登録する nt コマンドの派生系として ntd (nt with dependency の意味) コマンドを追加することにします。以前の nt コマンドと同様にページを作りますが、その前に既存タスクの選択を実施し、選択した page_id をリレーションとして追加します。

when "ntd"
  page = select_task
  Database.find(DATABASE_ID).create_child_page do |p, pp|
    pp["タスク名"] << ARGV.join(" ")
    pp["前タスク"].relation = page.id
  end
  exit # ntd コマンドの時には Notion を開かない

ntd コマンドを増やす必要があるので、シンボリックリンクで作成します。

ln -s nt ntd

これでいくつかのタスクをテストで登録してみます。

4. 日付なしタスクリストに後タスクを表示

日付なしタスクは LaunchPad にリストビューで表示しています。ここに後タスクを表示するようにします。

後タスクを追加

試験作成 → 採点 → ルーブリック入力 → 返却 → 成績提出の依存関係が正しく並んでいることがわかります。

5. まだできないタスクを非表示

依存関係があるので、後タスクは表示されていても仕方ありません。これを表示しないようにします。まず、それを確認するための情報収集のため PercentPreviousDone というロールアッププロパティを用意します。これは前タスクの Done のチェック率を表示するものです。前タスクが存在しない場合は空になりますし、存在する場合には Done のチェックボックスの割合が入力されます。前タスクが複数存在する場合があるので、これが 1 (100%) になった時に、そのタスクが実行できることになります。

PercentPreviousDone

これを使って日付なしタスクのフィルタは以下のようになりました。

  • Done にチェックがついていないこと
  • 日付が空欄であること
  • PercentPreviousDone が空であるか 1 (100%) であること

日付なしタスクのフィルタ更新

フィルタ変更後は一番先頭のタスクのみが表示されるようになりました。

フィルタ変更後

6. st, ot コマンドの修正

リストでは依存関係でフィルタすることができましたが、これからタスクを開始する st コマンドや対応可能なタスクを表示する ot コマンドは全てのタスクが表示されてしまいます。これらも PercentPreviousDone でフィルタするようにしましょう。

これはすぐ終わるはずだったんですが、NotionRubyMapping のロールアップクエリのバグに気づきました。前回の Formula クエリの対応でエンバグをしてしまったみたいです。こちらを v0.7.1 としてリリースしました。これを踏まえて、st, ot コマンドの時のみロールアップの値をチェックして、依存関係タスクを一覧に表示しないように変更しました。ntd コマンドについては、全てのタスクが表示されるようにこのフィルタ処理を除外しています。

前回の記事を書いてから、タスク関係はかなりスッキリさせてしまったので、こちらのスクリプトもだいぶ簡略化しました。とりあえず、参考になるかもしれないので全スクリプトをスナップショットとして公開しておきます。

#!/usr/bin/env ruby
# frozen_string_literal: true
require "date"
require "notion_ruby_mapping"
include NotionRubyMapping
NotionRubyMapping.configure do |config|
  config.notion_token = ENV["NOTION_API_KEY"]
  config.wait = 0
end

DATABASE_ID = "2395e3ffb55e4a8abc1ba426243776e3"
CMD_NAME = File.basename $0

def select_task(end_task: false, new_task: false)
  db = Database.find DATABASE_ID
  dps = db.properties
  dp, cp, tp, pdp = dps.values_at *%w[日付 Done タスク名 PercentPreviousDone]
  now = Time.now
  end_of_day = Time.local(now.year, now.month, now.mday, 23, 59, 59)
  date_query = dp.filter_before(end_of_day)
                 .and(cp.filter_equals false)
                 .ascending(dp)
  date_exists = db.query_database(date_query).to_a
  unsettled_query = dp.filter_is_empty
                      .and(cp.filter_equals false)
                      .ascending(tp)
  unless new_task
    query_is_empty = pdp.filter_is_empty another_type: "number"
    query_equals_1 = pdp.filter_equals 1, another_type: "number"
    unsettled_query.and(query_is_empty.or(query_equals_1))
  end
  date_unsettled = end_task ? [] : db.query_database(unsettled_query).to_a
  pages = date_exists + date_unsettled
  if pages.count.zero?
    puts "There are no tasks."
    exit
  else
    pages.each_with_index do |apage, i|
      puts "#{i}: #{apage.title}"
    end
    puts "\nPress number or q"
    num_or_q = STDIN.gets.chomp
    exit if num_or_q == "q"
    pages[num_or_q.to_i]
  end
end

case CMD_NAME
when "nt"
  Database.find(DATABASE_ID).create_child_page do |p, pp|
    pp["タスク名"] << ARGV.join(" ")
  end
  exit # nt コマンドの時には Notion を開かない
when "ntd"
  page = select_task new_task: true
  Database.find(DATABASE_ID).create_child_page do |p, pp|
    pp["タスク名"] << ARGV.join(" ")
    pp["前タスク"].relation = page.id
  end
  exit # ntd コマンドの時には Notion を開かない
when "ot"
  page = select_task
when "et"
  page = select_task end_task: true
  pp = page.properties
  pp["日付"].end_date = DateTime.now
  pp["Done"].checkbox = true
  page.save
when "st"
  if ARGV.empty?
    page = select_task
    pp = page.properties
    pp["日付"].start_date = DateTime.now
    page.save
  else
    page = Database.find(DATABASE_ID).create_child_page do |p, pp|
      pp["タスク名"] << ARGV.join(" ")
      pp["日付"].start_date = DateTime.now
    end
  end
end
url = page["url"]
system("open #{url}")

7. おわりに

この仕組み自体は、今回の新しい機能のおかげというわけではなく、以前からできていたことです。ただ、Notion が依存関係の作成自体を簡単に設定できるようにしてくれたので、紹介しやすくなりましたね。私の場合だと日付なしタスクにしか依存関係を設定していないので、タイムラインビューなどにおける矢印の恩恵は得られませんね。そのタスクが同一日付に行われた時だけうまく見える感じですかね。


hkob.notion.site