Notion で(自分なりの)タスク・時間管理を行う「新たな」方法(終了予定算出編)
前回は、「セクションデータベース」の仕様について、改めて、概要を説明しましたが、最後の「今後の改良予定」で予告している通り、
今回は、その改良というか、特に「終了予定日時」の算出方法などについて、補足していきたいと思います。
その上で、まず、全体の方針から、確認しておきましょう。
ここは、大きく道の分かれるところで、もちろん、複雑な仕組みを採用したり、長い関数を書けば、大体のことは、実現できるはずなのですが、
私としては、多くの人のニーズに対応したシステムを作るのではなく、なるべく簡単な処理で、個人的な必要のみを満たすことを目標としています。
つまり、「自分なりのタスクシュートの実践」的に不要な機能は、あえて実装しない代わりに、システムを簡潔にする、といった感じですね。
当然、自分には必要ない処理で動作が遅くなっていては、本末転倒ですし、
そのような取捨選択が、自由に行えるというのも、Notion を使う/使えるメリットと言えるかもしれません。
だからこそ、既存のツールや、テンプレートなどに依存し過ぎず、独自のシステムを、自分で構築していくことを推奨したい、というのもあり、
Notion は、そのハードルを、今までにないくらい下げてくれると思っているので、こうして、私も、参考例の一つを、note で発信しているわけです。
ただし、逆に言えば、人によっては、私のやり方だと、運用上、色々と不都合が出てくる可能性も否めないので、その点は、ご注意ください。
まぁ、そもそも「RoutineGarden」自体、かなりルーチンが安定していないと難しい、人を選ぶ手法なので、今更、心配はいらないとは思いますが。
ともあれ、前置きはこのくらいにして、そろそろ、本題に入りましょう。
終了予定日時の算出方法の再検討
とりあえず、前回の復習ですが、「終了予定日時」の算出方法は、単純に、以下のようになっていました。
if(
prop("現在"),
dateAdd(now(), prop("見積"), "minutes"),
dateAdd(prop("日時").dateStart(), prop("見積"), "minutes")
)
この方式の問題は、「見積」プロパティの値が、次のような、
ルーチンデータベースで「今日のプラン」を表示する際の「フィルター」設定を、そのまま数式にしたようなリストに依存している、という点です。
prop("ルーチンリスト").filter(
and(
current.prop("今日"),
or(
empty(current.prop("実行日時")),
current.prop("実行日時") < today()
)
)
)
ここで、タスクを実行したらどうなるかを思い出して欲しいのですが、
開始ボタンを押すと、そのルーチン(タスク)は、「今日のプラン」には表示されなくなります。
よって、上の数式では、「進行中」のタスクの「見積」というか「残り時間」は、「終了予定日時」に反映されないわけですね。
では、どうするか?
ここで登場してくるのが、「今日のプラン」の「フィルター」設定にも含まれている、ルーチンデータベースの「実行日時」というプロパティです。
よく考えれば、「今日」プロパティの関数内で、まとめて処理すれば良いものを、なぜ「実行日時」だけ別扱いにしてあるのかという理由も、
実は、この辺に関わってくるのですが、ともかく、これがなんの日時を表していたか、ということから、振り返っていきましょう。
これは、ルーチンデータベース(今日のプランなど)において、そのルーチンの「最終実行日時」を示すプロパティで、
そのルーチンタスクの開始時、すなわち、「着手する」ボタンを押すたびに、更新されていくのですが、
その日付が「未入力」か「今日より前」なら、そのタスクは、まだ(今日は)実行していない、ということが分かります。
逆に言えば、「着手する」ボタンを押すと、この「フィルター」条件に該当しなくなるので、プランには表示されなくなる、という仕組みですね。
つまり、このプロパティと「今日の日付」とを比較することにより、少なくとも、以下の二つを区別することができます。
「実行日時」が「未入力」か「今日より前」なら、今日は「未実行」
「実行日時」が「今日」なら、今日「実行済」か「進行中」
さらに、2. のタスクの内、最新の「実行日時」を持つものが、最後に開始したタスクであること、
というか、その日時は、最後に開始したタスクの「開始日時」であり、
そのタスクの「見積」時間を、その時刻に足せば、そのタスクの「終了予定時刻」が分かるのです。
ということは、その「終了予定時刻」に、その他の、残りのタスクの「見積」を、全て足せば、その日の「終了予定」も分かりますよね。
さて、一気に核心に迫ってしまいましたが、こんな説明で、付いて来てくれていますでしょうか?
ここから、改めて、具体的な数式を見ながら、確認していきます。
終了予定日時の算出手順の改良
まず、最後に実行したルーチン(タスク)を、「ルーチンリスト」から、例えば、以下のように抽出しておきましょう。
if(
prop("現在"),
prop("ルーチンリスト").filter(current.prop("実行日時") > today()).sort(current.prop("実行日時")).last(),
[].first()
)
現在のセクション以外では、常に「空」にしておきたいので、適当に場合分けしていますが、
基本的には、「実行日時」が、今日(以降)のものを、「実行日時」順で並び替えて、最後の要素を取り出しているだけですね。
この「最終実行」プロパティで取得されたタスクも考慮すると、セクションごとの「見積」は、次のように修正できます。
let(
total, sum(map(prop("今日"), current.prop("見積"))),
if(
empty(prop("最終実行")),
total,
if(
dateAdd(prop("最終実行").prop("実行日時"), prop("最終実行").prop("見積"), "minutes") > now(),
add(total, prop("最終実行").prop("見積")),
total
)
)
)
簡単に言えば、最後に実行したタスクの「実行(開始)日時」に、そのタスクの「見積」時間を足した時刻、
すなわち、そのタスクの「終了予定日時」が、「現在時刻」を過ぎていなければ、全体の「見積」値に、このタスクの「見積」も足しますが、
終了予定を超過している場合には、あえて、その他のタスクの「見積」だけの合計値(total)が、そのまま採用される、という数式ですね。
何故、そんなことをしているのかというと、これは、次に説明する、各セクションの「開始日時」の算出方法と、完全に連動しています。
if(
empty(prop("最終実行")),
if(prop("現在"), now(), prop("日時").dateStart()),
if(
dateAdd(prop("最終実行").prop("実行日時"), prop("最終実行").prop("見積"), "minutes") > now(),
prop("最終実行").prop("実行日時"),
now()
)
)
ここでも、「見積」の計算と全く同じ条件分岐になっていますね。
つまり、これらの二つの関数をまとめた(開始日時に見積時間を足した)のが、そのセクションの「終了予定日時」となるわけです。
dateAdd(prop("開始日時").dateStart(), prop("見積"), "minutes")
こちらは、以前よりも、かなりシンプルになりましたが、これにより、
最後に開始したタスクの「終了予定日時」が、「現在時刻」を過ぎていなければ、そのタスクの「実行(開始)日時」に、そのタスクの「見積」も含めた、残りのタスク全ての「見積」時間を足し、
そうでなければ、「現在時刻」に、その他のタスクの「見積」の合計だけを足したものが、セクションの「終了予定日時」として算出されます。
要するに、2. のケースでは、最後に開始したタスクの「見積」というか「終了予定時刻」は、すでに過ぎてしまっているので、
刻一刻と増えていくそのタスクの「実績」時間は、ある意味、もはや「現在時刻」に含まれていると言えば、分かりやすいでしょうか?
その日の終了予定の算出
最後に、セクションデータベースをリレーションした「終了予定データベース」を作成し、その日全体の「終了予定」も算出してみましょう。
と言っても、やり方さえ分かってしまえば、これは、そんなに難しいことではなくて、
例えば、全てのタスクの「見積」時間は、次のように計算できます。
sum(prop("セクションリスト").map(current.prop("見積")))
同様に、「開始日時」の取得や「終了予定日時」の算出も、ただ一行の数式で書けてしまうくらいですね。
prop("セクションリスト").filter(current.prop("現在")).last().prop("開始日時")
dateAdd(prop("開始日時"), prop("見積"), "minutes")
また、「現在」のセクションは、以下のように取得できるので、
style(prop("セクションリスト").filter(current.prop("現在")), "blue")
この、新しいデータベースを利用して、トップページを改良するなら、例えば、次のようになります。
ちなみに、今までトップページの上の方に配置していた「セクションデータベース」は、この「@Sections」ページの中に収納しちゃいました。
ところで、少し長くなってきたので、簡単な紹介で済ませますが、
一応、各セクションのページへ移動すれば、そのセクションのプランだけを見ながら、作業できるようにもしてあります。
上のページでは「@Sections」をサイドピークで開いていますが、
その左にある「07-10. モーニング」へのリンクをクリックすると、例えば、次のようなページが、あえてフルページで開くようになっていて、
フォーカスビューを表示している部分は、トップページにあるテーブルやボタンを含む「同期ブロック」であり、
その下では、単に、ルーチンデータベースの「今日のプラン」を、さらにセクションで絞り込んで、表示しているだけですね。
セクションの「終了予定時刻」などは、タイトル下の固定表示やサイドバーを見れば分かりますし、これで、セクションごとに集中できるでしょう。
まぁ、この場合、開始・終了のボタンを押した際に、他のページへ飛んでしまう挙動を、調整した方がいいかもしれませんが。
まとめ
今回は、前回に続き、セクションデータベース周りの補足もかねて、「終了予定日時」などを算出する関数を、改良してみました。
かなり込み入った内容で、説明も難しかったのですが、何をやろうとしているかは、なんとなく伝わりましたでしょうか?
とは言え、この計算方法も、もちろん、完璧ではありません。
例えば、最後に開始したタスクが「進行中」か「実行済」かは分からないため、そのタスクの「見積」よりも「早く」終了した場合には、
実は、次のタスクを開始するまで、最後のタスクが「見積」通りに終わった場合の「終了予定日時」が表示されたままになってしまいます。
また、「タスク数」についても同様で、最新のタスクの「開始日時」に「見積」を足した時刻と「現在時刻」とを比較している都合上、
その条件で「現在時刻」の方が大きくなるか、次のタスクを開始して「実行日時」の参照先が変わるかしないと、1つ多い数を表示してしまいますし、
逆に、「見積」を過ぎた「進行中」のタスクは、タスク数の合計に反映されなくなってしまうわけです。
さらに、詳細は割愛させてもらいますが、ルーチン外の「割り込み」が発生したりした場合にも、
現状、その影響は(少なくとも、そのタスクの実行中には)反映されないままでしょう。
しかしながら、これらの問題の解決策は、明白とも言えます。
要は、何も迷う必要はなく、単に、次のタスクを開始すれば良いのです。
そうすれば、そのタスクの「開始日時」をベースに、正しい計算結果が表示されるのですから。
実際、私なりのタスクシュートの実践においては、それで、特に問題ないので、だからこそ、運用できているとも言えるでしょう。
ただし、次の点は、人によっては、さすがに、改良せざるを得ないと思われるかもしれません。
それは、セクションや日付が切り替わる際の挙動で、「RoutineGarden」においては、そこが、かなり厳格なんですよね。
例えば、日付が切り替わって、次の日になったら、前の日のプランがいくら残っていようとも関係なく、
すぐさま「今日(次の日)のプラン」が表示される仕様になっています。
その点、全く猶予はありません。
すでに「進行中」のタスクを、終了させることはできますが、それが終わったら、もう、寝るしかないのです。
セクションについては、一応、前のセクションでやり残したタスクについても、(日付が変わる前なら)実行することは可能ですが、
現在のセクション以外のタスクの実行中は、上述の「終了予定日時」や「タスク数」の計算に関する問題が発生しやすいので、注意が必要ですね。
この辺は、終了予定データベースを、セクションデータベースとリレーションさせるのではなく、直接、ルーチンデータベースと紐付けることで、
ある程度は、対応できたりするかもしれないので、気が向いたら、また考えてみたいと思います。
次回の記事の内容は未定ですが、そろそろ、Google カレンダーとの連携についても、改めて、説明しておく必要があるかもしれません。
そのおかげで、ルーチンの「周期」設定が、ごくシンプルな仕組みで済んでいる面は、結構、大きいですからね。
ではまた。