Notion で(自分なりの)タスク管理を行う方法?(日次レビュー編)
前回は、定期的に YouTube チャンネルの更新をチェックし、
新しい動画を観るためのタスクを自動的に「タスクリスト」へ追加しておくという荒技を紹介しましたが、
今回も「番外編」として、しかし、実は、タスクシュート的にも、結構、重要かもしれない「日次レビュー」について、考えてみたいと思います。
厳密に言うと、タスクシュート的というよりは、特に「先送り0」的に、鍵となり得るもの、と言った方が適切かもしれません。
一応、久しぶりに、宣伝?しておきましょうか。
つまり、やらなければならないことを、つい先送りにして、いつも締め切りギリギリになってしまうとか、
今やらなくてもいいことを、ダラダラと続けてしまって、やりたいことができないなどと悩んでいる人には、
まず、タスクシュート的なメソッドによる、タスク・時間管理を試してみることをオススメしているわけですが、
少なくとも、「先送り0」を一つの目標とするのであれば、その過程で大切になってくるのが、ここで言う「日次レビュー」なのです。
と言っても、始めるのは簡単で、とりあえず、最低限、1日のログを見返して、「先送りの数」を確認してみるだけでも良いでしょう。
そうして、何か気づいたことがあれば、随時、次の日のプランや、ルーチンを見直すようにし、
メソッドに慣れないうちや、環境や状況が変化したり、心身の調子が悪い時期などにも、この「日次レビュー」を元に、方針を再検討していきます。
例えば、昨日のログを表示する「テーブルビュー」を作成しておき、
ステータス列の一番下の「計算」で「カウント」→「グループごとのカウント」→「Complete」を選択すれば、それだけで十分かもしれません。
そう言えば、「超過時間」プロパティについては、触れたことがなかったかもしれないので、ついでに紹介しておきましょうか。
数式は、こんな感じで、単に「実績時間」の値が「見積値」を超えていたら、その差分を赤色太字で表示するようにしているだけですね。
if(empty(prop("実績時間")), "0分",
let(
over, subtract(toNumber(prop("実績時間").replace("分", "")), prop("見積値")),
if(over > 0, style(over + "分", "b", "red"), "0分")
)
)
ちなみに、開始終了時刻なども、何やら色が変わっている部分があったりしますが、その話は、ちょっと長くなりそうなので、またいつか。
ともかく、このテーブルを確認するだけなら、「日次レビュー」を行うこと自体は、特に難しい作業でもないでしょう。
しかし、どうですかね?
問題は、そもそも、このテーブルを、毎日、ちゃんと確認するかどうかだという気もします。
もちろん、それもルーチンにして、習慣化してしまえばいいだけですし、
どうしても継続できなかったり、いまいちモチベーションが上がらないという人は「100日チャレンジ」に参加するのもオススメなのですが、
今回、提案してみたいのは、例えば、昨日のログを、Notion からマークダウンファイルとして(無理やり)エクスポート?し、
それを、普通のエディタで開くようにした方が、なんとなく、レビューしやすい場合もあるのではないかという、
分かる人には分かるというか、分かる人にしか分からないような、なかなか特殊なニーズを満たす内容です。
あえて説明してみるなら、Notion に書かれていることを、同じ Notion の中でレビューするより、いったん外へ出力し、切り離して考えた方が、
客観的に捉えやすいとも思うのですが、どうでしょうか?
まぁ、単純に、クラウド上で作業するのではなく、ローカルにもファイルを確保しておきたいというような、好みの問題もあるかもしれません。
というわけで、いつも以上に前置きが長くなってしまいましたが、そろそろ、始めていきましょう!
昨日のログを取得してみる
まずは、データベースから必要なデータを取ってくるわけですが、
フィルターの条件が違うだけで、基本的には「セクションリスト」や「ルーチンリスト」などを取得した際と、同様の流れとなります。
今までは、例えば、「有効」チェックボックスがオンのものだけを取得するといったような、比較的、簡単な条件が多かったと思いますが、
今回は、「タスクリスト」データベースから「実行日」が「昨日」の「24時間以内」に当てはまるデータを取ってくるわけですね。
function test() {
const today = new Date();
const before = new Date(today.setHours(0, 0, 0, 0));
const ago = new Date(today.getTime() - (24 * 60 * 60 * 1000));
const after = new Date(ago.setHours(0, 0, 0, 0));
getLogs(after, before, "xxxxx", "secret_XXXXX");
}
function getLogs(after, before, database_id, secret) {
const data = {
"filter": { "and": [
{ "property": "実行日", "date": { "on_or_after": after } },
{ "property": "実行日", "date": { "on_or_before": before } }
] },
"sorts": [{ "property": "開始日時", "direction": "ascending" }]
};
const options = {
'method': 'post',
'headers': {
'Authorization': 'Bearer ' + secret,
'Content-Type': 'application/json',
'Notion-Version': '2022-06-28'
},
'payload': JSON.stringify(data)
};
const response = UrlFetchApp.fetch('https://api.notion.com/v1/databases/' + database_id + '/query', options);
const contents = JSON.parse(response.getContentText());
const results = contents.results;
results.forEach((result) => {
const properties = result.properties;
const title = properties["名前"].title[0].plain_text;
console.log(title);
const projects = properties["プロジェクト"].relation;
console.log(projects);
const status = properties["ステータス"].status.name;
console.log(status);
if (status == "完了") {
const startDate = properties["開始日時"].date.start;
console.log(startDate);
const endDate = properties["終了日時"].date.start;
console.log(endDate);
const record = properties["実績時間"].formula.string;
console.log(record);
}
});
}
これで、「実行日」プロパティが、変数「after」と「before」の間の時間帯、すなわち「昨日の0時」から「今日の0時」までのタスクの一覧が、
ソートの条件も指定しているので、「開始日時」の昇順(ascending)で、取得できるはずです。
例によって、"xxxxx" と "secret_XXXXX" の部分は、「タスクリストデータベースの ID」と「Notion API のシークレット」に書き換えてください。
また、ここでは、「console.log()」で、各タスクの「名前」や「ステータス」などを表示しようとしていますが、
もちろん、日次レビューに用いたいプロパティを、自由に選択できます。
それこそ、人によっては、上述した「超過時間」プロパティを参考にしたいという場合もあるでしょう。
ただし、例えば、「開始日時」や「終了日時」などは、「未着手」のタスクには設定されておらず、出力しようとするとエラーになってしまうので、
上のコードのように、「ステータス」の値で、場合分けしておく必要があるかもしれません。
プロジェクトの ID を名前に変換する
さらに、「プロジェクト」などの「リレーション」プロパティを使いたいなら、もうひと工夫必要ですね。
実は、以前「セクションリスト」で、似たようなことやっているのですが、
その時は、任意の「開始時刻」に対応した「セクションの ID」を出力する関数を作成しました。
今回は「プロジェクトの ID」から、対応する「プロジェクト名」へと変換して、出力に利用したいところです。
今のままだと、「プロジェクト」プロパティの出力は、以下のような、「プロジェクト ID の配列」になっているのではないでしょうか?
では、「プロジェクトリスト」のデータを Notion から取得し、ID と「プロジェクト名」との対応関係を保管したオブジェクトを作成してみます。
function getProjects(database_id, secret) {
const options = {
'method': 'post',
'headers': {
'Authorization': 'Bearer ' + secret,
'Content-Type': 'application/json',
'Notion-Version': '2022-06-28'
}
};
const response = UrlFetchApp.fetch('https://api.notion.com/v1/databases/' + database_id + '/query', options);
const contents = JSON.parse(response.getContentText());
const object = {};
for (const result of contents.results) {
object[result.id] = result.properties["名前"].title[0].plain_text;
}
return object;
}
これを使えば、次のようにして、各「プロジェクトの ID」を元に、「プロジェクト名」が出力できるはずです。
const id2project = getProjects("yyyyy", secret);
const projects = properties["プロジェクト"].relation;
projects.forEach((relation) => {
const project = id2project[relation.id];
console.log(project);
});
もちろん、こちらの "yyyyy" に入れるのは、「プロジェクトリストデータベースの ID」なので、ご注意ください。
出力するテキストデータを作成する
さて、これで、必要なデータは出揃ったので、いよいよ、ファイルへ出力するためのテキストを作成していきましょう。
基本的には、好きな順番で、自由にデータを連結していって、改行を含む、一つの文字列にまとめればいいだけです。
例えば、以下のように書くと、ある種のマークダウン形式の文字列「text」が出来上がります。
function getLogs(after, before, database_id, secret) {
const id2project = getProjects("yyyyy", secret);
const data = {
"filter": { "and": [
{ "property": "実行日", "date": { "on_or_after": after } },
{ "property": "実行日", "date": { "on_or_before": before } }
] },
"sorts": [{ "property": "開始日時", "direction": "ascending" }]
};
const options = {
'method': 'post',
'headers': {
'Authorization': 'Bearer ' + secret,
'Content-Type': 'application/json',
'Notion-Version': '2022-06-28'
},
'payload': JSON.stringify(data)
};
const response = UrlFetchApp.fetch('https://api.notion.com/v1/databases/' + database_id + '/query', options);
const contents = JSON.parse(response.getContentText());
const results = contents.results;
const date = after.toLocaleDateString("ja-JP", { weekday: "long", year: "numeric", month: "long", day: "numeric" });
let text = `### ログ(${date})\n\n`;
let todo = 0;
let complete = 0;
results.forEach((result) => {
text += "- ";
const properties = result.properties;
const status = properties["ステータス"].status.name;
text += `(**${status}**)`;
const projects = properties["プロジェクト"].relation;
projects.forEach((relation) => {
const project = id2project[relation.id];
text += `[[${project}]] `;
});
const title = properties["名前"].title[0].plain_text;
text = `${text.trim()}「${title}」`;
if (status == "完了") {
const startDate = properties["開始日時"].date.start;
const start = new Date(startDate).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: false })
const endDate = properties["終了日時"].date.start;
const end = new Date(endDate).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: false });
const record = properties["実績時間"].formula.string;
text += `${start} ~ ${end} (${record})`;
complete += 1;
} else {
todo += 1;
}
text += "\n";
});
const sum = todo + complete;
text += `#### ${complete} / ${sum} 着手(**先送り ${todo}**)\n`;
console.log(text);
}
タスクのステータスが「完了」かどうかにより、開始終了日時等が取得できるかどうかを判断するだけでなく、
ここでは、変数「todo」と「complete」で、各ステータスのタスクの数をカウントし、下の方で「先送りの数」などを出力している点にも注目です。
マークダウンファイルとして出力する
それでは、最後に、作成したテキストを、マークダウンファイルとして書き出してみましょう。
GAS(Google Apps Script)を使っているなら、Google ドライブに保存してしまうのが、最も簡単です。
ただ「console.log(text)」で出力する代わりに、例えば、以下のような行を追記するだけですね。
const fileName = after.toLocaleDateString("ja-JP", { year: "numeric", month: "2-digit", day: "2-digit" }).replaceAll('/', '-');
DriveApp.createFile(fileName + ".md", text);
今回、ファイル名は、「2024-08-22」のような形式にしておきました。
Google ドライブ( https://drive.google.com/drive/my-drive )上でファイルをプレビューしてみると、次のように出力されましたでしょうか?
また、Google ドライブのアプリをインストールした PC で、「マイドライブ」フォルダを「Obsidian」の「Vault」に指定し、
このファイルを表示すると、おそらく、以下のようになります。
そもそも、「プロジェクト名」を「[[two square brackets]]」で囲んでいるのは、実は、このためでした。
その他の点も含め、マークダウンの書き方は、人それぞれになるでしょう。
そして、そろそろ細かい説明は省略させてもらいますが、上の「test()」関数を「review()」などという名前に変えたりしつつ、
これまでの記事でやってきたのと同様に、その関数を、例えば、毎日「午前1時〜2時」の間に実行するようにするなど、
いつものように、お好みで、「トリガー」を設定しておいてください。
まとめ
今回は、場合によっては「日次レビュー」に取り組みやすくなるよう、昨日のログをマークダウンファイルとして出力する方法を紹介してみましたが、
これは、結構、応用範囲が広い内容でもあるのではないでしょうか?
関連して、クラウドとローカルの使い分けというか、その辺りも含めた運用方針についても、いずれは考えていきたいところですし、
Notion で書いたノートをマークダウンに変換するのは、意外と面倒臭いらしいのですが、そちらも検討してみたいですね。
ともあれ、そろそろ長くなってきたので、今回は、この辺で。
ではまた。