LoginSignup
0
0

タスク管理どうされていますか

日々のタスク管理どのようにしていますか。
私はGoogleのToDoリストを使いやすくするChrome拡張を作り、それで管理しています。

個人的には、かなり使い勝手は良く、ストア公開したいのですがGoogle Tasks APIは機密性が高いAPIに分類されるため、ストア公開するにはかなりのお金をかけて審査機関へ審査依頼する必要があるので公開できてません・・・。

image.png

会社の定型業務のタスク作成を自動化したい

会社で毎月勤怠の申請や、経費精算があります。
毎月の最終営業日にその作業を行うことになるのですが、Googleカレンダーでは毎月の繰り返し条件に最終営業日のような指定はできません。
Googleカレンダーでできるのはせいぜい「毎月30日」「毎月最終x曜日」のようなタスクで、祝日の考慮もされません。

なので手動でタスクを追加することになるのですが、これはかなり手間です。

GASで定型業務のタスクを自動作成

毎月手動登録も芸がないので、Google Tasks APIを手軽に使えるGASからタスクを登録するようにしています
以下のページで、新しいプロジェクト をクリックします

プロジェクト名は何でも良いです。私は定型業務タスクとしました
コード.gs の中身を以下に置き換えます

function main() {
  const holidayCalendarId = 'ja.japanese#holiday@group.v.calendar.google.com';
  const processedDateKey = 'processedDate';
  const timeZone = 'Asia/Tokyo';

  const today = new Date();
  const { targetMonth, lastDate } = getNextMonthDates(today);
  const processedDate = new Date(PropertiesService.getScriptProperties().getProperty(processedDateKey));

  if (processedDate >= lastDate) {
    return;
  }

  const holidays = getHolidays(holidayCalendarId, targetMonth, lastDate, timeZone);

  const notifyDate = getNextBusinessDay(new Date(targetMonth.getFullYear(), targetMonth.getMonth(), 15), holidays);
  if (!isTaskAlreadyExists('勤怠・経費精算', notifyDate)) {
    addTask('勤怠・経費精算', notifyDate);
  }

  if (today.getMonth() === 5 || lastDate.getMonth() === 5) {
    const juneLastDate = getLastBusinessDay(new Date(today.getFullYear(), 5, 30), holidays);
    if (!isTaskAlreadyExists('年度末経費精算', juneLastDate)) {
      addTask('年度末経費精算', juneLastDate);
    }
  }

  PropertiesService.getScriptProperties().setProperty(processedDateKey, lastDate.toISOString());
}

function getNextMonthDates(today) {
  const nextMonth = today.getMonth() === 11 ? 0 : today.getMonth() + 1;
  const nextYear = today.getMonth() === 11 ? today.getFullYear() + 1 : today.getFullYear();
  
  const targetMonth = new Date(nextYear, nextMonth, 1);
  const lastDate = new Date(nextYear, nextMonth + 1, 0);

  return { targetMonth, lastDate };
}

function getHolidays(holidayCalendarId, start, end, timeZone) {
  const holidayEvents = CalendarApp.getCalendarById(holidayCalendarId).getEvents(start, end, { timeZone: timeZone });
  return holidayEvents.map(event => event.getStartTime());
}

function isBusinessDay(date, holidays) {
  const day = date.getDay();
  return day !== 6 && day !== 0 && !holidays.some(h => h.getTime() === date.getTime());
}

function getNextBusinessDay(date, holidays) {
  while (!isBusinessDay(date, holidays)) {
    date.setDate(date.getDate() + 1);
  }
  return date;
}

function getLastBusinessDay(date, holidays) {
  while (!isBusinessDay(date, holidays)) {
    date.setDate(date.getDate() - 1);
  }
  return date;
}

function addTask(title, dueDate) {
  Tasks.Tasks.insert({
    title: title,
    due: Utilities.formatDate(dueDate, "JST", "yyyy-MM-dd'T'HH:mm:ss") + ".000Z"
  }, Tasks.Tasklists.list().items[0].id);
}

function isTaskAlreadyExists(title, dueDate) {
  const taskList = Tasks.Tasklists.list().items[0];
  const tasks = Tasks.Tasks.list(taskList.id).items;

  const dueDateString = Utilities.formatDate(dueDate, "JST", "yyyy-MM-dd'T'HH:mm:ss") + ".000Z";

  return tasks.some(task => task.title === title && task.due === dueDateString);
}

工夫ポイント

  1. getNextBusinessDay関数
    この関数は、与えられた日付から次の営業日を見つけます。休日や週末を考慮して、次の営業日を返します。
function getNextBusinessDay(date, holidays) {
  while (!isBusinessDay(date, holidays)) {
    date.setDate(date.getDate() + 1);
  }
  return date;
}
  1. getLastBusinessDay関数
    これは逆に、与えられた日付から前の営業日を見つけます。月末から遡って最後の営業日を探すのに使います。
function getLastBusinessDay(date, holidays) {
  while (!isBusinessDay(date, holidays)) {
    date.setDate(date.getDate() - 1);
  }
  return date;
}
  1. isTaskAlreadyExists関数
    この関数は、同じタイトルと期日のタスクが既に存在するかをチェックします。これで重複登録を防いでいます。
function isTaskAlreadyExists(title, dueDate) {
  const taskList = Tasks.Tasklists.list().items[0];
  const tasks = Tasks.Tasks.list(taskList.id).items;

  const dueDateString = Utilities.formatDate(dueDate, "JST", "yyyy-MM-dd'T'HH:mm:ss") + ".000Z";

  return tasks.some(task => task.title === title && task.due === dueDateString);
}
  1. スクリプトプロパティ
    スクリプトが最後に処理した日付を記録するために使います。これにより、一度削除したタスクが再度登録されることを防ぎます。
const processedDateKey = 'processedDate';
const processedDate = new Date(PropertiesService.getScriptProperties().getProperty(processedDateKey));

if (processedDate >= lastDate) {
  return;
}

// ... (タスク作成処理)

PropertiesService.getScriptProperties().setProperty(processedDateKey, lastDate.toISOString());

これで、毎日実行しても不要なタスクの作成を避けられます。

Tasks APIを使えるようにする

コード.gs を書き換えたら、Tasks APIを使えるようにします。

image.png

サービスの + をクリックします。

image.png

Google Tasks API ドキュメント を選択し、追加をクリックします。

アクセス権限を承認する

Googleカレンダーによる祝日判定とTasks APIを利用しているためアクセス権限を承認する必要があります。

image.png

実行をクリックします。

image.png

権限を確認をクリックします。

image.png

詳細をクリックします。

image.png

<プロジェクト名> (安全ではないページ) に移動をクリックします。

image.png

許可をクリックします。

image.png

再度実行をクリックします。

これで以下のような実行ログが出れば完了です。

スクリーンショット 2024-06-26 10.55.33.png

作成されたタスクを確認する

image.png

6月は年度末の精算があるので、最終営業日にタスクが追加されました。

image.png

7月分のタスクも追加されています。ちゃんと祝日判定されていますね。

定期実行されるようにする

この状態では毎月実行をクリックする必要があるので、実行を自動化します。

image.png

トリガーをクリックします。

image.png

時間ベースのトリガーのタイプを選択で、日付ベースのタイマーを選択。
時刻を選択は何でも良いです。
**保存**をクリックします。

これで毎日実行されるようになりました。

まとめ

これで毎月の定型業務のタスクを自動で登録することができるようになりました。
GASはサーバレスで手軽にGoogleのAPIが使えるので良いですよね。
毎月x日以降の直近の営業日、毎月x日以前の直近の営業日を取得するメソッドを作成しましたが、ChatGPTに投げれば他の日付計算も簡単にできると思います。
タスク管理を効率的に行って日々の作業忘れを防ぎましょう。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0