見出し画像

notionでのルーティンタスクの管理

こんにちは。皆さんはnotionで生活系のタスクの管理されてますか?
仕事のタスクと違って、まあやった方がいいけどやらんくてもそこまで問題起きんし…と私は掃除家事等をつい後回しにしがちなので、DBを作ってゆるく管理しています。
生活タスク、と言っていますが、家事の他ソシャゲのやんなきゃいけないこと、充電等にも使っています。

いつも見るデイリーのページにリンクドビューしてるルーティンDB

ベースはこちらで書かれるお掃除カウントダウンのデータベース。
それを改良して使っているものを紹介します。


テンプレート

(2023/01/22追記)
・おまけ部分記載のGASの処理で、複数チェックがあると日付リレーションが同期してしまう記述になっておりました…!大変申し訳ありません。
現在記載のGASは修正済みとなります。
・notionの日本語の取り扱いの仕様変更かと思うのですが、フォーミュラで曜日表示にformatDateすると「Monday」と英語表記だったのが「月曜日」日本語表記に変わっていたので、テンプレート内では対応させています
(記事内では画像との兼ね合いもあり、ややこしいので変更していません)


全体

まず、登録タスクを条件別に3種類分けています
それぞれに設定項目、アラートのプロパティを用意し、アラートだけ最後にフォーミュラで一つのプロパティにまとめる、という方法をとっています。

<1>実行日から●●日後にアラート
 →掃除関係や、買って何日後に効果が切れるソシャゲのアイテム等

<2>曜日指定でアラート(今週実施済みは除く)
 →●●曜日に表示されてほしいもの。
  ウィークリーログの制作、充電、ソシャゲ週交換アイテム

<3>日付指定でアラート(該当期間に実施済みは除く)
 →月単位で●●日から●●日で表示したいもの。
  マンスリーログ、1ヶ月単位の掃除、家計簿等

使い方のフロー

  1. そろそろのアラート(アラート総合)が出ているものをよく見るページに表示

  2. タスクが完了したら、実施記録のプロパティに適当な文字で構わないのでページを作ってリレーション

終わり!

私が普段使っている時は、実施記録を日記ページにして使用していて、2でリレーションするのも面倒なので、チェックボックスを入れておけば、翌日の午前0-1時の間にGASが自動で動き、チェックを入ったものを検索し、下記処理を行う、というのを組んでいます
・実施日に昨日の日記(日付のマスタDB)をリレーション
・チェックを外す

今回はGASは使わない方法で解説します。が、一応この記事の最後にスクリプト自体は記載しますので、より楽に使いたい方はご使用ください。


1.実行日から●●日後にアラート

「実施記録」の持つ作成日時の時間を基準に、設定した日(プロパティ「何日ごとに実行」)を足した日にちが「予定実施日」に入り、アラートが出る、というものです。
比較的簡単なものですね。

「予定実施日」のフォーミュラ

if(empty(prop("何日ごとに実行")), prop("最新実施日"), dateAdd(prop("最新実施日"), +prop("何日ごとに実行"), "days"))

「1.アラート_チェック後+日数」のフォーミュラ
実施日をすぎたらアラームを出しています

if(empty(prop("何日ごとに実行")), "", if(prop("予定実施日") >= now(), "", "🔥そろそろ"))





2.曜日指定でアラート

曜日指定は下記の3つの条件を下にアラートを出しています。

追加条件に引っ掛かるかどうか
②選んだ「実施(週ごと)」に今日の曜日が含まれているかどうか
③「最新実施日」をもとに、今週はまだ実施されていないかどうか

「2.アラート_曜日」のフォーミュラ

if(and(not empty(prop("追加条件")), dateBetween(now(), prop("最新実施日"), "days") >= 30 * if(empty(prop("追加条件")), 1, prop("追加条件"))), "🔥そろそろ", if(contains(prop("実施(週ごと)"), formatDate(now(), "dddd")), if(formatDate(prop("最新実施日"), "Y-W") != formatDate(now(), "Y-W"), "🔥そろそろ", ""), ""))
フォーミュラの説明


①追加条件というのは、例えば私の部屋掃除の、下記の例の場合がそうです(太字部分)

毎週土、日曜日のみ表示。
ただし、前回実行から1ヶ月以上たってたらチェックされるまで表示する

私の部屋掃除の「表示条件」

この場合、前回実行から指定ヶ月経ってるかを計算し、それが指定ヶ月とどちらが大きいかを判断させています。

//使ってるフォーミュラ一部説明

//日付と日付の間を計算する
dateBetween(引かれる日付(小), 引く日付(大), 結果の表示形式)
dateBetween(prop("date"), now(), "days")

//日付の表示変更
formatDate(変更する日付, "表示させたい形式")
formatDate(now(), "MMMM D YYYY, HH:mm")

//使っている部分だと
formatDate(now(), "Y-W")  == 2022-28
formatDate(now(), "dddd")  == Thursday


今日が指定曜日と一致しているか、と今週は実施済みかどうか、を判断し表示しているので、使用時は下記のようにしてください。

・水曜日以降、チェックされるまで表示したい
 →水〜日曜日までを全て選んでおく
・水曜と金曜日のみ表示したい
 
→水曜、金曜だけを選ぶ
・水曜のみ表示したいけど、完了済みでもアラートを消したくない
 
→実施記録にリレーションをしない

ゆるくやりたいタスクと、かっちりやるタスクを共存させれるのは、使いやすいところだな〜と思います。




3.日付指定でアラート

若干プロパティ名が分かりづらいのですが、「月ごと・以降」日から「月ごと・以前」日まで、アラートを表示する、というものです。

「日付指定の実施予定日」のフォーミュラ

dateAdd(dateAdd(dateSubtract(dateSubtract(dateSubtract(now(), date(now()) - 1, "days"), hour(now()), "hours"), minute(now()), "minutes"), if(toNumber(formatDate(now(), "D")) < prop("月ごと・以降"), -1, 0), "months"), prop("月ごと・以降") - 1, "days")

分かりにくいですが、実施予定日を割り出すために今日の日付から今日の日付をひいたりして、その月の1日をだし、今月か来月かを判断し、実施予定日を割り出しています。力技ですね〜


完了済みを表示してる「アラート_予定日ごと」のフォーミュラ
これで、今月分が実施済みかどうかを判断させています

if(dateBetween(prop("最新実施日"), prop("日付指定の実施予定日"), "days") >= 0, "完了済み", "")


「3.アラート_日」のフォーミュラ

if(and(contains(prop("分岐✅"), "3."), and(not empty(prop("追加条件")), dateBetween(now(), prop("最新実施日"), "months") >= prop("追加条件"))), "🔥そろそろ", if(prop("月ごと・以前") >= 32, if(empty(prop("アラート_予定日ごと")), "🔥そろそろ", ""), if(prop("アラート_予定日ごと") == "完了済み", "", if(toNumber(formatDate(now(), "DD")) < prop("月ごと・以降"), "", if(toNumber(formatDate(now(), "DD")) <= prop("月ごと・以前"), "🔥そろそろ", "")))))

今日が指定期間と一致しているか、実施済みかどうか、を判断し表示しているので、使用時は下記のようにしてください。

・15日、チェックされるまで表示したい
 →「月ごと・以降」を15、「月ごと・以前」を32に設定する
・20日から25日のみ表示したい
 
→「月ごと・以降」を20、「月ごと・以前」を25に設定する
・20日から月末まで表示したい
 
→「月ごと・以降」を20、「月ごと・以前」を31に設定する




まとめ

かなりざっくりでしたが、以上で概ね完了です。
あとはよく見るホームなどに下記条件でフィルターをかけるといいかと思います。

アラート総合が未入力でない、最新実施日が今日より前か、未入力のもの


アラートまとめ

今まで見てきたアラートをまとめているフォーミュラです。

if(contains(prop("除外月"), formatDate(now(), "M")), "", if(contains(prop("分岐✅"), "1."), prop("1.アラート_チェック後+日数"), if(contains(prop("分岐✅"), "2."), prop("2.アラート_曜日"), if(contains(prop("分岐✅"), "3."), prop("3.アラート_日"), ""))))


除外月

追加条件、というのもオプションだったのですが、「除外月」というオプションも設けてあります。

これは例えば、加湿器の掃除など冬場は1ヶ月に1回やるけど春になったらやらない、というような場合に、ここを選択しておくと、その月は表示されない、ということです。


表示させないといえばほぼ使っていませんが、もうやらないルーティンや保留中、手動で止めておきたい場合などは「分岐✅」プロパティの4を選んでください。
アラート総合に何も表示されなくなります。


よければ、それぞれのタスクの中身に、簡単に行えるように情報やリンクを載せたり、アイコンを変えたり、色々工夫して使ってみてください。



おまけ GAS

私が使っているチェックしたものを探して、日付マスタのDBとリレーションさせるGASです。
これを午前0-1時の時間んをトリガーにして、回すようにしています。

下記の記事を参考に制作しました

properties.gs

// 手動実行で設定する

function setScriptProps() {
  let scriptProperties = PropertiesService.getScriptProperties();
  Logger.log(PropertiesService.getScriptProperties().getProperties());
  scriptProperties.deleteAllProperties();

  scriptProperties.setProperties({
    'NOTION_VERSION': '2022-02-22',
    'NOTION_TOKEN': 'ここにトークンを入れる',
    'DATEDB_ID':'日記DBのID'
  });

  // データベースとチェックボックスの列名、日付の列名を定義
  // データベースIDはデータベースのページを開き、URLから取得
  // https://www.notion.so/{userName}/{DatabaseID}?v={ViewID} となっているURLの{DatabaseID}の部分
  let checkItemList = JSON.stringify(
    [
      {
        'DATABASE_ID': '日記DBのID',
        'COUNTDOWN_DATABASE_ID':'ルーティンタスクDBのID'
      }
    ]
  );

  scriptProperties.setProperties({ 'CHECK_ITEM_LIST': checkItemList });
}

countdown.gs


function getdateDbId_yesterday() {
  const props = PropertiesService.getScriptProperties().getProperties();
  const date = new Date();
  date.setDate(date.getDate() - 1); 

  const getPayload2 = {
    "filter": {
        "and": [
        {
        "property": '日記DBの名前のプロパティ名',
        "title": {
            "contains" : Utilities.formatDate(date, "JST", "yyyy/MM/dd")
          }
        }
      ]
    }
  }
  return getDatabaseQuery(props, props.DATEDB_ID,getPayload2)["results"][0]["id"]

}


function UpdateCheckedTime_countdown() {
  const props = PropertiesService.getScriptProperties().getProperties();
  const list = JSON.parse(props['CHECK_ITEM_LIST']);


  for (let i = 0; i < list.length; i++) {
    const item = list[i];
    const dbID = item["COUNTDOWN_DATABASE_ID"];
    // チェック欄がtrueのものを検索
    const getPayload = {
      "filter": {
        "and": [
          {
            "property": "チェックボックスのプロパティ名",
            "checkbox": {
              "equals": true
            }
          }
        ]
      }
    }
    console.log(getPayload);

    const nonCheckedItemList = getDatabaseQuery(props, dbID, getPayload);
    Utilities.sleep(1000); // 平均3回/秒のリミット回避のためスリープ

    if (nonCheckedItemList.results.length === 0) {
      Logger.log("更新対象なし")
      continue;
    }

    // 更新処理
    nonCheckedItemList.results.forEach(t => {

      var jissi = t["properties"]["実施記録のプロパティ名"]["relation"]
      var add = { "id":getdateDbId_yesterday() }
      jissi.push(add);

      const updatePayload = {
        "properties": {
          "実施記録のプロパティ名": {
            "relation":jissi
          },
          "チェックボックスのプロパティ名": {
            "checkbox": false,
          }
        }
      };

      let pageId = t["id"]
      updatePage(props, pageId, updatePayload)
      Utilities.sleep(1000); // 平均3回/秒のリミット回避のためスリープ
    });
  }
}

/**
 * ページの内容を更新する
 * https://developers.notion.com/reference/patch-page
 */
function updatePage(props, pageId, payload) {
  let url = "https://api.notion.com/v1/pages/" + pageId
  let options = {
    "method" : "PATCH",
    "headers": {
      "Content-type": "application/json",
      "Authorization": "Bearer " + props.NOTION_TOKEN,
      "Notion-Version": props.NOTION_VERSION,
    },
    "muteHttpExceptions" : true,
    "payload": JSON.stringify(payload)
  };

  try {
    let res = UrlFetchApp.fetch(url, options);
    Logger.log(res);
    return JSON.parse(res);
  } catch (e) {
    Logger.log(e);
    return undefined;
  }

}

/**
 * DBから指定条件に合うものを取得する
 * https://developers.notion.com/reference/post-database-query
 */
function getDatabaseQuery(props, databaseId, payload) {
  let url = "https://api.notion.com/v1/databases/" + databaseId + "/query"
  let options = {
    "method" : "POST",
    "headers": {
      "Content-type": "application/json",
      "Authorization": "Bearer " + props.NOTION_TOKEN,
      "Notion-Version": props.NOTION_VERSION,
    },
    "payload" : JSON.stringify(payload),
    "muteHttpExceptions" : true
  }

  try {
    let res = UrlFetchApp.fetch(url, options);
    Logger.log(res);
    return JSON.parse(res);
  } catch (e) {
    Logger.log(e);
    return undefined;
  }
}


終わり。

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