見出し画像

Notion で(自分なりの)タスク・時間管理を始めよう!(拡張編⑤)

前回は、「セクションリスト」データベースからデータを取得して、各タスクの「セクション」プロパティを設定する方法を紹介しましたが、

最後に予告した通り、今回は、ついに、ルーチンデータベースから取得したデータを元にタスクを自動生成する手順について、説明していきましょう。

これにより、データベーステンプレートの「繰り返し」を使ったルーチンの管理からは、基本的に、卒業できると思います。

自動生成するタスクのテンプレートの作り方を紹介したのが、もはや懐かしいですね。

もちろん、テンプレートの利用も完全にやめるのではなく、併用しても良いのですが、役割を明確に分けておいた方が分かりやすいでしょう。

あるいは、開始日時に「今」を設定し、ステータスを「進行中」にしたテンプレートを「デフォルトに設定」しておいて、
手動でタスクを追加した時、直ちに実行中の状態になるようにする、といった利用の方が、メインになってくるかもしれません。


ルーチンリストからデータを取得する

では、早速、Notion で、次のような「ルーチンリスト」データベースを作成したとしましょう。

「ルーチンリスト」データベースを作成する

タスクリストと同じように、リレーションで「セクションリスト」プロパティを追加し、グループ化もしておくと、整理しやすいでしょうか。

というか、この管理のしやすさと柔軟なカスタマイズ性こそが、データベーステンプレートを使わずにルーチンを管理する一番のメリットです。

デメリットは、多少のプログラミングスキルが求められるという点ですが、
前回までで、Google カレンダーの予定からタスクを自動生成しているのであれば、どうせなら、ここまでやってしまいましょう!

Notion のデータベースからデータを取得する手順は、前回「セクションリスト」でやったのと同様で、
以下のようなコードを書いて「test()」を実行してみて、何やら複雑なオブジェクトが取得できれば、それが、上のテーブルのデータになります。

function test() {
  const routines = getRoutines("xxxxx", "secret_XXXXX");
  console.log(routines);
}

function getRoutines(database_id, secret) {
  var data = { "filter": { "property": "有効", "checkbox": { "equals": true } } }
  var options = {
    'method': 'post',
    'headers': {
      'Authorization': 'Bearer ' + secret,
      'Content-Type': 'application/json',
      'Notion-Version': '2022-06-28'
    },
    'payload': JSON.stringify(data)
  };
  var response = UrlFetchApp.fetch('https://api.notion.com/v1/databases/' + database_id + '/query', options);
  var contents = JSON.parse(response.getContentText());
  return contents.results;
}

以前の記事から続けて、これを読んでくれている人には、そろそろ説明するまでもないかもしれませんが、
ここで、「getRoutines()」の引数の "xxxxx" は、自分の「ルーチンリストデータベースの ID」に置き換えて、実行してください。
シークレット "secret_XXXXX" の方は、前回、前々回と同じです。

一応、次のようなフィルター設定により、「有効」プロパティのチェックボックスがオンになっているデータだけを取得するようにしておきました。

{ "filter": { "property": "有効", "checkbox": { "equals": true } } }

このチェックボックスの導入は必須ではありませんが、一部のルーチンだけをテストしたい場合などにも、何かと便利だと思います。

これで、あとは「postNotion()」でタスクを追加するだけ、と思いきや、
実を言うと、今日のタスクに追加するかどうかを「繰り返し」プロパティから判断するには、もう一手間、いや二手間?ほど必要です。

繰り返しの判定を行う

次に、その「繰り返し」の判定用の関数を作成しましょう。

function repeat2boolean(repeat, day) {
  if (repeat == "毎日") {
    return true;
  } else if (repeat == "平日" && 0 < day && day < 6) {
    return true;
  } else if (repeat == "土日" && !(0 < day && day < 6)) {
    return true;
  } else if (repeat == day2repeat(day)) {
    return true;
  }
  return false;
}

function day2repeat(day) {
  switch (day) {
    case 0:
      return "日曜日";
    case 1:
      return "月曜日";
    case 2:
      return "火曜日";
    case 3:
      return "水曜日";
    case 4:
      return "木曜日";
    case 5:
      return "金曜日";
    case 6:
      return "土曜日";
    default:
      return "";
  }
}

とりあえず、「repeat2boolean()」関数の中で「day2repeat()」関数を利用していることは分かると思いますが、
厄介なのは「day」という変数の扱いで、ここに何が入るのかというと、試しに、以下のようなコードを書いて「test()」を実行してみてください。

function test() {
  const routines = getRoutines("xxxxx", "secret_XXXXX");
  const today = new Date();
  const day = today.getDay();
  routines.forEach((routine) => {
    const properties = routine.properties;
    const repeats = properties["繰り返し"].multi_select.map(option => option.name);
    repeats.forEach((repeat) => {
      if (repeat2boolean(repeat, day)) {
        console.log(properties["名前"].title[0].plain_text);
      }
    });
  });
}

さて、条件を満たすルーチンの名前だけが、ログに出力されたでしょうか?

ここまで同じように作業してきて、このプログラムを「平日に実行した」としたら、次のような結果になるはずです。

今日のタスクに追加するかどうかを「繰り返し」プロパティから判断する

あるいは、土曜日に実行すれば「 タスク B 」と出力されるでしょう。

上のコードの中では「Date.prototype.getDay()」を利用することにより、プログラムを実行した日の「曜日」を取得しているのですが、
実は、これが「 0 〜 6 」の「数値」で返ってくるんですよね。

例えば、土曜日に実行すると「day」には「 6 」が入ります。

つまり、それを「日曜日 〜 土曜日」に変換して、ルーチンデータの「繰り返し」プロパティの値と比較しているわけです。

あと、平日や土日を判断する際にも、条件として使っていて、
例えば「0 < day && day < 6」を満たすなら、day が「 1 〜 5 」だということ、すなわち「月曜日 〜 金曜日」だと分かるでしょう。

ともかく、これで「repeat2boolean()」関数を使えば、各ルーチンの「繰り返し」プロパティと「今日の曜日」とを比較したりすることによって、
当該ルーチンから「今日のタスク」を自動生成すべきかどうか、判定できるようになりました。

そして、上では「console.log()」でタスク名を表示しただけでしたが、代わりに「postNotion()」関数を呼び出せば、タスクリストに追加されます。

ルーチンから今日のタスクを自動生成する

もちろん、新しいプロジェクトを作っても構いませんが、それはそれで、ちょっと手間が増えるので、
カレンダーから今日の予定を取得して、タスクとして追加した後に、ルーチンリストからも「今日のタスク」を生成するよう、拡張しちゃいましょう!

例えば、すでに自動実行されるように設定してある「myFunction()」関数の中に、以下のようなコードを追記してください。

const routines = getRoutines("xxxxx", "secret_XXXXX");
const day = today.getDay();
routines.forEach((routine) => {
  const properties = routine.properties;
  const title = properties["名前"].title[0].plain_text;
  const start = new Date(today.toISOString().split('T')[0] + 'T' + properties["開始予定時刻"].rich_text[0].plain_text);
  const minutes = properties["見積値"].number;
  const repeats = properties["繰り返し"].multi_select.map(option => option.name);
  repeats.forEach((repeat) => {
    if (repeat2boolean(repeat, day)) {
      postNotion("yyyyy", title, start, minutes, "secret_XXXXX");
    }
  });
});

ただし、これより上で、「const today = new Date();」のようにして、今日の日時が変数「today」に入っていることは前提です。
特に変更したりしていなければ、おそらく、カレンダーからデータを取得する前に、すでに定義してあるでしょう。

途中、「開始予定時刻」を「日時」に加工している部分は異様に複雑なのですが、これで、ルーチンからもタスクが自動的に生成されると思います。

ちなみに「postNotion()」の引数を "yyyyy" としたのは「getRoutines()」関数の引数と区別するためで、
こちらには、カレンダーからタスクを追加した時と同じ「タスクリストデータベースの ID 」を入れておく必要があるので、注意してくださいね。

ルーチンページの内容も複製する

簡単にではありますが、ここまでで「ルーチンからタスクを自動生成する方法」の基本は、説明できたかと思います。

ただ、データベーステンプレートを利用していた時とは違って、各データベースの「プロパティ」しか、これまで扱ってきませんでした。

例えば、次のように、あるルーチンのページの中に「ブックマーク」ブロックでリンクを配置していたとしましょう。

ルーチンページに内容を記述する

しかし、現状では、タスクを生成する際、このページの内容までは複製されません!

そこで、その記述も自動生成されるタスクに引き継がれるようにするための新しい関数を、最後に紹介しておきたいと思います。

function test() {
  const routines = getRoutines("xxxxx", "secret_XXXXX");
  const today = new Date();
  const day = today.getDay();
  routines.forEach((routine) => {
    const properties = routine.properties;
    const children = getChildren(routine.id, "secret_XXXXX");
    const repeats = properties["繰り返し"].multi_select.map(option => option.name);
    repeats.forEach((repeat) => {
      if (repeat2boolean(repeat, day)) {
        console.log(children);
      }
    });
  });
}

function getChildren(id, secret) {
  const options = {
    'method': 'get',
    'headers': {
      'Authorization': 'Bearer ' + secret,
      'Notion-Version': '2022-06-28'
    }
  };
  const response = UrlFetchApp.fetch('https://api.notion.com/v1/blocks/' + id + '/children?page_size=100', options);
  const contents = JSON.parse(response.getContentText());
  return contents.results;
}

細かい説明は割愛しますが、これで「test」を実行すると、以下のように、各ルーチンページの内容を、取得することができるはずです。

getChildren() を利用して「ページの内容」を取得する

ログに出力されたオブジェクトの最後の方に「bookmark」とあって、確かに「url」が書かれているのが分かるでしょうか?

タスクを生成する際、このオブジェクトも一緒に渡してやれば、ページの内容まで、ちゃんと記述されるというわけですね。

まず、「myFunction()」の中では、以下のように「getChildren()」関数を呼び出すよう、編集してください。

repeats.forEach((repeat) => {
  if (repeat2boolean(repeat, day)) {
    const children = getChildren(routine.id, "secret_XXXXX");
    postNotion("xxxxx", title, start, minutes, children, "secret_XXXXX");
  }
});

それに伴って、「postNotion()」が受け取る引数も増やす必要があり、

function postNotion(database_id, title, start, minutes, children, secret) { ...

その中では、次のようにして、新たな変数「children」も Notion へ「data」の追加要素として送るように、設定しておきます。

const data = {
  "parent": { "type": "database_id", "database_id": database_id },
  "properties": {
    "title": { "type": "title", "title": [{ "type": "text", "text": { "content": title } }] },
    "実行日": { "type": "date", "date": { "start": start } },
    "開始予定時刻": { "rich_text": [{ "type": "text", "text": { "content": time } }] },
    "セクション": { "relation": [{ "id": section }] },
    "見積値": { "type": "number", "number": minutes }
  },
  "children": children
};

実は、ページに記述したブロックの種類によっては、上手くいかないものもあるのですが、これで、基本的なものには対応できるでしょう。

ただ、注意点として、「postNotion()」の引数を増やした場合、Google カレンダーの予定を今日のタスクとして追加する際にも、
何かしら「children」にも渡す必要があるので、とりあえず、次のように、空の配列を渡すなどしておくと良いかもしれません。

postNotion("xxxxx", event.summary, start, minutes, [], "secret_XXXXX");

まぁ、いずれ、私が「実際にこれをどのように運用しているのか」を紹介していく中で、また、補足していければと思います。

まとめ

今回で、一応「拡張編」としても、大体の説明は終えることができました。

しかし、特に最後の方は、込み入った話が多くなってしまったので、次は、いったん、ここまでの全記事の内容をまとめる回を設けようと思います。

というか、今回も長くなってきてしまいましたし、まとめは、次回に委ねるとしましょう。

ではまた。

この記事が気に入ったらサポートをしてみませんか?