見出し画像

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()
		)
	)
)

ここで、タスクを実行したらどうなるかを思い出して欲しいのですが、

開始ボタンを押すと、そのルーチン(タスク)は、「今日のプラン」には表示されなくなります。

よって、上の数式では、「進行中」のタスクの「見積」というか「残り時間」は、「終了予定日時」に反映されないわけですね。

では、どうするか?

ここで登場してくるのが、「今日のプラン」の「フィルター」設定にも含まれている、ルーチンデータベースの「実行日時」というプロパティです。

よく考えれば、「今日」プロパティの関数内で、まとめて処理すれば良いものを、なぜ「実行日時」だけ別扱いにしてあるのかという理由も、
実は、この辺に関わってくるのですが、ともかく、これがなんの日時を表していたか、ということから、振り返っていきましょう。

これは、ルーチンデータベース(今日のプランなど)において、そのルーチンの「最終実行日時」を示すプロパティで、
そのルーチンタスクの開始時、すなわち、「着手する」ボタンを押すたびに、更新されていくのですが、
その日付が「未入力」か「今日より前」なら、そのタスクは、まだ(今日は)実行していない、ということが分かります。

「今日のプラン」の「フィルター」設定を確認する

逆に言えば、「着手する」ボタンを押すと、この「フィルター」条件に該当しなくなるので、プランには表示されなくなる、という仕組みですね。

つまり、このプロパティと「今日の日付」とを比較することにより、少なくとも、以下の二つを区別することができます。

  1. 「実行日時」が「未入力」か「今日より前」なら、今日は「未実行」

  2. 「実行日時」が「今日」なら、今日「実行済」か「進行中」

さらに、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")

こちらは、以前よりも、かなりシンプルになりましたが、これにより、

  1.  最後に開始したタスクの「終了予定日時」が、「現在時刻」を過ぎていなければ、そのタスクの「実行(開始)日時」に、そのタスクの「見積」も含めた、残りのタスク全ての「見積」時間を足し、

  2.  そうでなければ、「現在時刻」に、その他のタスクの「見積」の合計だけを足したものが、セクションの「終了予定日時」として算出されます。

要するに、2. のケースでは、最後に開始したタスクの「見積」というか「終了予定時刻」は、すでに過ぎてしまっているので、
刻一刻と増えていくそのタスクの「実績」時間は、ある意味、もはや「現在時刻」に含まれていると言えば、分かりやすいでしょうか?

その日の終了予定の算出

最後に、セクションデータベースをリレーションした「終了予定データベース」を作成し、その日全体の「終了予定」も算出してみましょう。

「終了予定」データベースを作成する

と言っても、やり方さえ分かってしまえば、これは、そんなに難しいことではなくて、
例えば、全てのタスクの「見積」時間は、次のように計算できます。

その日の残りのタスクの「見積」を合計する
sum(prop("セクションリスト").map(current.prop("見積")))

同様に、「開始日時」の取得や「終了予定日時」の算出も、ただ一行の数式で書けてしまうくらいですね。

最後に実行(開始)したタスクの「開始日時」を取得する
prop("セクションリスト").filter(current.prop("現在")).last().prop("開始日時")
その日の「終了予定日時」を算出する
dateAdd(prop("開始日時"), prop("見積"), "minutes")

また、「現在」のセクションは、以下のように取得できるので、

「現在」のセクションを取得する
style(prop("セクションリスト").filter(current.prop("現在")), "blue")

この、新しいデータベースを利用して、トップページを改良するなら、例えば、次のようになります。

「RoutineGarden」のトップページを改良する

ちなみに、今までトップページの上の方に配置していた「セクションデータベース」は、この「@Sections」ページの中に収納しちゃいました。

「@Sections」ページを確認する

ところで、少し長くなってきたので、簡単な紹介で済ませますが、
一応、各セクションのページへ移動すれば、そのセクションのプランだけを見ながら、作業できるようにもしてあります。

上のページでは「@Sections」をサイドピークで開いていますが、
その左にある「07-10. モーニング」へのリンクをクリックすると、例えば、次のようなページが、あえてフルページで開くようになっていて、

「セクションページ」を開く

フォーカスビューを表示している部分は、トップページにあるテーブルやボタンを含む「同期ブロック」であり、
その下では、単に、ルーチンデータベースの「今日のプラン」を、さらにセクションで絞り込んで、表示しているだけですね。

セクションの「終了予定時刻」などは、タイトル下の固定表示やサイドバーを見れば分かりますし、これで、セクションごとに集中できるでしょう。

まぁ、この場合、開始・終了のボタンを押した際に、他のページへ飛んでしまう挙動を、調整した方がいいかもしれませんが。

まとめ

今回は、前回に続き、セクションデータベース周りの補足もかねて、「終了予定日時」などを算出する関数を、改良してみました。

かなり込み入った内容で、説明も難しかったのですが、何をやろうとしているかは、なんとなく伝わりましたでしょうか?

とは言え、この計算方法も、もちろん、完璧ではありません。

例えば、最後に開始したタスクが「進行中」か「実行済」かは分からないため、そのタスクの「見積」よりも「早く」終了した場合には、
実は、次のタスクを開始するまで、最後のタスクが「見積」通りに終わった場合の「終了予定日時」が表示されたままになってしまいます。

また、「タスク数」についても同様で、最新のタスクの「開始日時」に「見積」を足した時刻と「現在時刻」とを比較している都合上、
その条件で「現在時刻」の方が大きくなるか、次のタスクを開始して「実行日時」の参照先が変わるかしないと、1つ多い数を表示してしまいますし、
逆に、「見積」を過ぎた「進行中」のタスクは、タスク数の合計に反映されなくなってしまうわけです。

さらに、詳細は割愛させてもらいますが、ルーチン外の「割り込み」が発生したりした場合にも、
現状、その影響は(少なくとも、そのタスクの実行中には)反映されないままでしょう。

しかしながら、これらの問題の解決策は、明白とも言えます。

要は、何も迷う必要はなく、単に、次のタスクを開始すれば良いのです。

そうすれば、そのタスクの「開始日時」をベースに、正しい計算結果が表示されるのですから。

実際、私なりのタスクシュートの実践においては、それで、特に問題ないので、だからこそ、運用できているとも言えるでしょう。

ただし、次の点は、人によっては、さすがに、改良せざるを得ないと思われるかもしれません。

それは、セクションや日付が切り替わる際の挙動で、「RoutineGarden」においては、そこが、かなり厳格なんですよね。

例えば、日付が切り替わって、次の日になったら、前の日のプランがいくら残っていようとも関係なく、
すぐさま「今日(次の日)のプラン」が表示される仕様になっています。

その点、全く猶予はありません。

すでに「進行中」のタスクを、終了させることはできますが、それが終わったら、もう、寝るしかないのです。

セクションについては、一応、前のセクションでやり残したタスクについても、(日付が変わる前なら)実行することは可能ですが、
現在のセクション以外のタスクの実行中は、上述の「終了予定日時」や「タスク数」の計算に関する問題が発生しやすいので、注意が必要ですね。

この辺は、終了予定データベースを、セクションデータベースとリレーションさせるのではなく、直接、ルーチンデータベースと紐付けることで、
ある程度は、対応できたりするかもしれないので、気が向いたら、また考えてみたいと思います。

次回の記事の内容は未定ですが、そろそろ、Google カレンダーとの連携についても、改めて、説明しておく必要があるかもしれません。

そのおかげで、ルーチンの「周期」設定が、ごくシンプルな仕組みで済んでいる面は、結構、大きいですからね。

ではまた。

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