TaskChute for Notion に Google カレンダーからタスクを追加する方法
前回は、TaskChute for Notion の一般リリースを記念して、というか、便乗しまして、個人的な TCN との関わりについて、書かせてもらいましたが、
最後に予告した通り、今回は、TCN を使っている/使おうとしている人に向けて、Notion API や GAS の使い方を、紹介してみたいと思います。
まぁ、タイトル通りなのですが、Google カレンダーとの連携?については、結構、需要もあるのではないでしょうか?
と言っても、Google カレンダーから「今日の予定」を取得し、「今日のタスク」として Notion のデータベースへ追加する方法については、
すでに、以前、書いたことがあるので、今回の内容は、単に、これの「TCN 版」といった感じですね。
一応、上の記事を書いてから、割と時間も経っていますし、改めて、手順を振り返っておくとしましょう。
もちろん、TCN 特有の工程が、ちょっと増えているところもあります。
そこそこ難易度は高いと思うので、まずは、一通り全体を眺めてみてから、真似してやってみるかどうか、判断した方がいいかもしれません。
それでは、少し長いですが、興味があれば、お付き合いください。
Google カレンダーから今日の予定を取得する
まずは、Google カレンダーから「今日の予定」を取得する方法ですが、
Google の Apps Script(GAS)自体については、細かいことを説明しているとキリがないので、慣れてもらうしかないといったスタンスでいきます。
色々とできることは多いのですが、とりあえず、今、必要な部分だけ、なんとなく使えるようになれば、それで十分だと考えましょう。
JavaScript についても、同様です。
Google Calendar API サービスを使ってみる
ともかく、Google にログインして、https://script.google.com/home にアクセスし、「新しいプロジェクト」を作成してください。
そして、左のサイドバーにある「サービス」の「+」ボタンから、「Google Calendar API」を追加して、
例えば、以下のような JavaScript のコードを書くと、
function myFunction() {
const calendarId = 'primary';
const now = new Date();
const max = new Date(new Date().setHours(23, 59, 0, 0));
const events = Calendar.Events.list(calendarId, {
timeMin: now.toISOString(),
timeMax: max.toISOString(),
singleEvents: true,
orderBy: 'startTime'
});
if (!events.items || events.items.length === 0) {
console.log('No events found.');
return;
}
for (const event of events.items) {
if (event.start.date) {
const start = new Date(event.start.date);
console.log('%s (%s)', event.summary, start.toLocaleDateString());
continue;
}
const start = new Date(event.start.dateTime);
console.log('%s (%s)', event.summary, start.toLocaleString());
}
}
ログインしている Google アカウントの、自分の(デフォルトの)カレンダーから、「今日の予定」だけを、取得できるはずです。
取得する予定を、どのような条件で絞り込むかは、当然、自分のタスクシュートの運用方針に合わせて、変えてもらっても構いませんが、
ここでは、「timeMax」に「今日の23時59分」を指定しておきました。
それにより、プログラムを実行した時の「現在時刻」から「23時59分」までの「今日の予定」が、取得できるというわけですね。
ちなみに、上のコードは、次のページのサンプルを元にしています。
仮に、以下のような予定が、カレンダーに入っているとすると、
上部のメニューバーにある「実行」ボタンを押せば、以下のような「実行ログ」が、出力されますでしょうか?
ひとまず、これで、Google カレンダーから、思った通りに「今日の予定」を取得できているかどうか、確認できるようになったのは、確かです。
少なくとも、上のコードの「events」変数の中に、必要なデータが入っているんだな、ということさえ、覚えておいてもらえれば、
下の方の「console.log()」を使った「実行ログ」の出力に関わる部分については、よく分かっていなくても、特に、問題ありません。
コードを編集したら、「実行」ボタンの左にある「プロジェクトを保存」ボタンか、「Command + S」などのショートカットを使って、
こまめに上書き保存しておくのも、お忘れなく。
アクセス権を承認する
ところで、「実行」ボタンを押すと、特定の関数、ここでは「myFunction()」に書かれた処理が、実行されるわけですが、
初めて動かす際には、次のように「承認」が求められると思います。
基本的には、普通に進めていってもらえばいいのですが、途中、ちょっと分かりにくい部分があるので、補足しておくと、
次のような画面では、左下の「詳細」という部分をクリックし、
詳細が表示されたら、その一番下にあるリンクに移動することで、
この「無題のプロジェクト」に対し、自分の Google アカウントの情報にアクセスすることを、許可してあげてください。
つまり、今回で言えば、Google カレンダーの情報へのアクセス権に関して、ちゃんと自分で書いたスクリプトで使うなら、承認してもいいのですが、
信頼できないプログラムに情報を取得されたりすると困るため、基本的にはセキュリティがかかっている感じですね。
管理ページ(https://myaccount.google.com/connections)から、アクセス権を削除することもできるので、念の為、覚えておきましょう。
Notion に今日のタスクを追加する
上では、Apps Script のページに、ログを出力しただけでしたが、
実際にやりたいのは、「今日の予定」を「今日のタスク」として、Notion のデータベースに追加してしまうことです。
そこで、次は、Notion API を使って、TCN のデータベースにページを追加する手順を、確認してみましょう。
内部インテグレーションを追加する
まず、https://www.notion.so/profile/integrations にアクセスして、「新しいインテグレーション」を追加してください。
例えば、インテグレーションの名前は、適当に「API」とし、
種類は「内部」で、もちろん、「関連ワークスペース」には、TCN が存在するワークスペースを選択しておく必要があります。
さらに、これは、オプションなのですが、一応、分かりやすいように、Notion のロゴも設定しておきました。
インテグレーション設定の画面へ移動したら、「内部インテグレーションシークレット」を表示して、コピーできることも確認しておきましょう。
あとは、TCN のページを開いて、右上の「…」メニューの下の方にある「接続先」に、上で作成した「API」を追加しておけば、
ともかく、Notion API の利用設定は、完了です。
タスクデータベースの ID を調べる
また、API からデータベースにアクセスするためには、そのデータベースを識別する ID も必要なので、ついでに、調べておきましょう。
と言っても、TCN 右上の「データベース」のトグルリスト内にある「タスクDB」ページを開くなどして、
その URL から、該当する文字列を取得するだけですね。
ページの URL は、次のような形式になっているはずなので、
ここから、{database_id} の部分を抜き出しておいてください。
ここまでで、Notion API を利用するための「内部インテグレーションシークレット」と、「タスクDB の ID」が、確保できました!
それでは、いよいよ、API を使って、Apps Script から「タスクDB」にタスクを追加してみましょう。
Notion API を使ってタスクを追加してみる
とりあえず、上で書いたコードを、例えば、以下のように、編集します。
function myFunction() {
const calendarId = 'primary';
const now = new Date();
const max = new Date(new Date().setHours(23, 59, 0, 0));
const events = Calendar.Events.list(calendarId, {
timeMin: now.toISOString(),
timeMax: max.toISOString(),
singleEvents: true,
orderBy: 'startTime'
});
if (!events.items || events.items.length === 0) {
return;
}
for (const event of events.items) {
const title = event.summary;
const date = { "time_zone": null };
if (event.start.date) {
const start = new Date(event.start.date);
date["start"] = start.toISOString().split('T')[0];
} else {
const start = new Date(new Date(event.start.dateTime).getTime() + (9 * 60 * 60 * 1000));
const end = new Date(new Date(event.end.dateTime).getTime() + (9 * 60 * 60 * 1000));
date["start"] = start.toISOString().replace('Z', '') + '+09:00';
date["end"] = end.toISOString().replace('Z', '') + '+09:00';
}
const data = {
"parent": { "type": "database_id", "database_id": "xxxxxxxxxx" },
"properties": {
"タスク名": { "type": "title", "title": [{ "type": "text", "text": { "content": title } }] },
"日付": { "type": "date", "date": date },
"集計DB": {
"type": "relation",
"relation": [
{ "id": "1214fdbd-c7b9-81dd-b4f0-d15ae72af79a" }
]
},
"カレンダー": { "type": "checkbox", "checkbox": true },
"セクションデータベース": {
"type": "relation",
"relation": [
{ "id": "1214fdbd-c7b9-81b3-9a19-f97a71de31ca" },
{ "id": "1214fdbd-c7b9-81b2-a719-eba5847a5438" },
{ "id": "1214fdbd-c7b9-8170-84df-c20a4c8591f1" },
{ "id": "1214fdbd-c7b9-8115-b441-c3d9b853c53f" },
{ "id": "1214fdbd-c7b9-81f5-846f-e70d0f7ee913" },
{ "id": "1214fdbd-c7b9-8141-9857-db3f73b645bd" },
{ "id": "1214fdbd-c7b9-81b2-9438-fb7914b9d2f6" },
{ "id": "1214fdbd-c7b9-8133-b530-cadc64a95e55" }
]
}
}
};
const options = {
'method': 'post',
'headers': {
'Authorization': 'Bearer ntn_XXXXXXXXXX',
'Content-Type': 'application/json',
'Notion-Version': '2022-06-28'
},
'payload': JSON.stringify(data)
};
UrlFetchApp.fetch('https://api.notion.com/v1/pages', options);
}
}
書いてみると、思った以上に、大変なコードになってしまいましたが、これから少しずつ、説明していきますね。
とは言え、基本的には、「data」変数の内容さえ、理解できたなら、
それを「options」変数の「payload」に含め、「UrlFetchApp.fetch()」関数に渡して、Notion API を実行しているだけです。
コード中の「xxxxxxxxxx」と「ntn_XXXXXXXXXX」という文字列は、
上で確認した「タスクDB の ID」と「内部インテグレーションシークレット」に、それぞれ、置き換えておいてください。
ちなみに、通常、Notion API は、次のように実行する必要があるので、参考までに。
では、そろそろ、「data」変数の中の「properties」で指定する必要がある項目について、具体的に見ていきましょう。
いちばん分かりやすいのは「タスク名」で、「title」変数には、Google カレンダーから取得した予定のタイトルが入っています。
"タスク名": { "type": "title", "title": [{ "type": "text", "text": { "content": title } }] },
つまり、新しいタスクを追加する際には、このようにして、設定しておきたいプロパティの値を含んだ、適切な(JSON)形式のデータを作成し、
API を使って、Notion に POST(送信)する必要があるのです。
同様に、「日付」プロパティに渡すための「date」変数も、上の方で作成しているのですが、ここは、ちょっと難しいかもしれませんね。
const date = { "time_zone": null };
if (event.start.date) {
const start = new Date(event.start.date);
date["start"] = start.toISOString().split('T')[0];
} else {
const start = new Date(new Date(event.start.dateTime).getTime() + (9 * 60 * 60 * 1000));
const end = new Date(new Date(event.end.dateTime).getTime() + (9 * 60 * 60 * 1000));
date["start"] = start.toISOString().replace('Z', '') + '+09:00';
date["end"] = end.toISOString().replace('Z', '') + '+09:00';
}
まぁ、結局のところ、何をやっているのかと言えば、主に、日時の「フォーマット」を整えているだけで、
Google カレンダーから取得した予定が、「終日」の予定なら、「date」変数の中身は、例えば、次のようになり、
{
"start": "2024-10-17",
"time_zone": null
}
10時から12時までの予定なら、以下のようになるのですが、
{
"start": "2024-10-17T10:00:00.000+09:00",
"end": "2024-10-17T12:00:00.000+09:00",
"time_zone": null
}
要するに、「日付」プロパティには、このような形式でデータを渡す必要があるから、頑張って整形している、というわけです。
簡単に補足するなら、日本時間に「toISOString()」を適用すると、
作ろうとしている表記には、かなり近づく代わりに、「9時間」巻き戻ってしまうので、あらかじめ「9時間」進めていたりするのですが、
細かいことは置いておいて、次の「集計DB」と「セクションデータベース」の話に移りましょうか。
厄介度合いで言えば、あるいは、こちらの方が、厄介かもしれません。
上の「data」変数の中でも、一際、目をひくデータとなっていますが、これらは、他のデータベースと関連する「リレーション」プロパティであり、
しかも、TCN で、普通にタスクを新規作成した際にも、特定の値が、デフォルトで入るようになっているため、
おそらくですが、Notion API からタスクを追加する際にも、ちゃんと値を入れておかないと、不具合が生じそうです。
例えば、Notion カレンダーからタスクを追加し、全プロパティを表示して確認してみると、次のようになっていました。
そこで、どうするのかと言うと、このタスクを API で取得し、中身を出力してみれば、どのようなデータになっているのか、具体的に分かりますよね。
試しに、上の「myFunction()」関数とは、別の関数「test()」を、以下のように作成し、実行してみるとしましょう。
function test() {
const data = { "filter": { "property": "日付", "date": { "on_or_after": "2024-10-17" } } };
const options = {
'method': 'post',
'headers': {
'Authorization': 'Bearer ntn_XXXXXXXXXX',
'Content-Type': 'application/json',
'Notion-Version': '2022-06-28'
},
'payload': JSON.stringify(data)
};
const response = UrlFetchApp.fetch('https://api.notion.com/v1/databases/xxxxxxxxxx/query', options);
const contents = JSON.parse(response.getContentText());
console.log(JSON.stringify(contents.results[0].properties, null , " "));
}
もちろん、関数の中の最初の行の日付「2024-10-17」は、少なくとも、なんらかのタスクが取得できる、適切なものに変更し、
コード中の「ntn_XXXXXXXXXX」や「xxxxxxxxxx」という文字列も、以前と同じように、置き換えてください。
ちなみに、「日付」による絞り込みについては、こちらに書かれています。
ともかく、この「test()」関数のいちばん下の行では、API で取得したデータの中から、
適当に、一つのタスクの内容(properties)だけを、ピックアップしてみているのですが、以下のように、出力されましたでしょうか?
メニューバーの「実行」ボタンを押す前に、その少し右にあるドロップダウンメニューで、「test」の方を選択しておくのも、お忘れなく。
また、初めて Notion API を使う際には、新たに外部サービスへ接続することになるので、再度、アクセス権の承認を求められるかもしれませんが、
Google カレンダーの時と同じように、許可しておいてください。
さて、ともあれ、これで、「実行ログ」の中をスクロールして確認すれば、
どのようなデータを「集計DB」や「セクションデータベース」プロパティに設定しておくと良いかも、分かると思うので、
それぞれ、以下のように、「data」変数内へ記入しておきましょう。
"集計DB": {
"type": "relation",
"relation": [
{ "id": "1214fdbd-c7b9-81dd-b4f0-d15ae72af79a" }
]
},
"セクションデータベース": {
"type": "relation",
"relation": [
{ "id": "1214fdbd-c7b9-81b3-9a19-f97a71de31ca" },
{ "id": "1214fdbd-c7b9-81b2-a719-eba5847a5438" },
{ "id": "1214fdbd-c7b9-8170-84df-c20a4c8591f1" },
{ "id": "1214fdbd-c7b9-8115-b441-c3d9b853c53f" },
{ "id": "1214fdbd-c7b9-81f5-846f-e70d0f7ee913" },
{ "id": "1214fdbd-c7b9-8141-9857-db3f73b645bd" },
{ "id": "1214fdbd-c7b9-81b2-9438-fb7914b9d2f6" },
{ "id": "1214fdbd-c7b9-8133-b530-cadc64a95e55" }
]
}
ただ、注意点として、この部分については、本記事のコードをコピペするのではなく、上で実行した、自分の「実行ログ」から、コピペしてください。
というのも、おそらく、これらの「id」の値は、TCN のテンプレートを複製した時点でランダムに決まるのか、人によって違うと思うんですよね。
最後に、「カレンダー」プロパティについては、必須ではないのですが、
一応、カレンダーから取得した予定なので、Notion 上のカレンダービューなどにも、表示させておきたいところかもしれません。
その場合には、次の行も必要となります。
"カレンダー": { "type": "checkbox", "checkbox": true },
それでは、ようやくですが、実際に「myFunction()」関数を実行して、TCN にタスクを追加してみましょう!
まず、Google カレンダーに、確認のための「終日の予定」と、
上で Notion カレンダーから追加したのとは別に、今日は、もう一つ「ミーティング」があると仮定し、予定を追加しておきます。
果たして、この状態で「myFunction()」関数を実行すると、以下のように、タスクが追加されましたでしょうか?
アイコンのついていないタスクが、今、新たに追加されたタスクですね。
どうやら、終日の予定は、「セクションなし」ではなく、「0:00」からという扱いになってしまうようなので、そこは、ご注意ください。
また、Notion カレンダー上には、そもそも Google カレンダーの予定も表示しておくことが可能なので、
その場合は、次のようになっているかもしれません。
青が Google カレンダーの予定で、オレンジの方が、TCN に存在するタスクの表示です。
ところで、実は、「data」変数の中に、次のような「icon」に関する行も追記しておけば、
API を使った場合でも、Notion や Notion カレンダー上からタスクを追加した時と同じようなアイコンを、タスクにつけておくことができます。
const data = {
"parent": { "type": "database_id", "database_id": "xxxxxxxxxx" },
"icon": { "type": "external", "external": { "url": "https://www.notion.so/icons/clipping_gray.svg" } },
"properties": {
...
}
}
トリガーを追加して自動実行させる
ここまでで、Google カレンダーから取得したデータを Notion API を使って、「今日のタスク」として追加することができるようになったので、
最後に、このプログラムが、毎日、自動的に実行されるように、設定しておきましょう。
まず、ページの左端の、アイコンが並んでいるバーにカーソルを合わせ、その中から「トリガー」を選択して、
今まで開いていた「エディタ」画面から、移動してください。
そこで、右下にある「トリガーを追加」ボタンから、例えば、以下のような設定で、トリガーを作成しておくだけで、
毎日、上で作った「myFunction()」関数を「午前0時〜1時」の間に自動実行し、その日の予定を Google カレンダーから取得して、
Notion に「今日のタスク」として、追加しておいてくれるというわけです。
普通、このような自動実行の仕組みを作るのは大変なのですが、これなら非常に簡単であり、これだけでも、GAS を使う価値があるかもしれません。
ただ、その代わり、用意されている選択肢の中から選ぶ必要はあるので、
実際に、どのような時刻設定で実行させるかは、ご自身の運用方針に合わせて、色々と検討してみてくださいね。
Google カレンダーのデータと「同期する」わけではなく、あくまでも、ある時刻に TCN へタスクを「追加する」という点にも、改めて、要注意です。
まとめ
さて、今回の記事は、今までにないくらい長くなってしまったので、ごく簡単にまとめさせてもらいますが、
結論として、やる気のある人は、是非、頑張ってください。笑
まぁ、あとは、今時、ChatGPT などに訊けば、なんでも答えてくれるし、
とりあえず、そもそも、どういった方法があるのかを知る上で、この記事が少しでも参考になったなら、幸いです。
ではまた。