CalendarDocumentDrive

【GAS】カレンダーの予定にファイルをつける カレンダーの定例会議をチャットに通知⑥

前回のおさらい

前回は祝日に入れてしまった定例会議を削除しました。

定例会議の繰り返しルールは翌月の祝日を除く毎週月曜日10時〜11時です。祝日まで定例会議に参加するなんてたまったものではありません。

この処理を実現するために翌月のすべての定例会議を取得しました。

const defaultCalendar = calendarApp.getDefaultCalendar()
const events = defaultCalendar.getEvents(nextMonthFirstDay, nextMonthLastDay)
const regularMeetings = events.reduce((accum, event) => {
  if (event.getTitle() === title) accum.push(event)
  return accum
}, [])

さらに祝日に設定されてしまった定例会議を取得しました。

const targets = holidays.map(holiday => {
  return regularMeetings.find(regularMeeting => {
    return regularMeeting.getStartTime().getDate() === holiday.getStartTime().getDate()
  })
})

最後に祝日に設定されてしまった定例会議を削除しました。

targets.forEach(target => {
  if (target) target.deleteEvent()
})

今回は繰り返しルールを使ってつくられた定例会議に議事録をつけます。

手動は勘弁!

ここまでのコード全体

const setRegularMeeting = () => {
  // 翌月末を取得
  const current = new Date()
  const nextMonthLastDay = new Date(current.getFullYear(), current.getMonth() + 2, 0)

  // 繰り返しルールを設定
  const calendarApp = CalendarApp
  const recurrence = calendarApp.newRecurrence()
  const weeklyRecurrenceRule = recurrence.addWeeklyRule()
  const recurrenceRule = weeklyRecurrenceRule.onlyOnWeekday(calendarApp.Weekday.MONDAY).until(nextMonthLastDay)

  // 定例会議の名称、当月最初の定例会議の開始日時、終了日時を設定
  const title = '定例会議ですよ!'
  const dayIndex = new Date(current.getFullYear(), current.getMonth() + 1, 1).getDay()
  const firstDay = (9 - dayIndex) % 7 === 0 ? 7 : (9 - dayIndex) % 7
  const startTime = new Date(current.getFullYear(), current.getMonth() + 1, firstDay, 10, 0, 0)
  const endTime = new Date(current.getFullYear(), current.getMonth() + 1, firstDay, 11, 0, 0)

  // 予定の繰り返しを作成
  const defaultCalendar = calendarApp.getDefaultCalendar()
  const eventSeries = defaultCalendar.createEventSeries(title, startTime, endTime, recurrenceRule)
  
  // 翌月の祝日を取得
  const holidayCalendar = calendarApp.getCalendarById('ja.japanese#holiday@group.v.calendar.google.com')
  const nextMonthFirstDay = new Date(current.getFullYear(), current.getMonth() + 1, 1)
  const holidays = holidayCalendar.getEvents(nextMonthFirstDay, nextMonthLastDay)

  // 定例会議を取得
  const events = defaultCalendar.getEvents(nextMonthFirstDay, nextMonthLastDay)
  const regularMeetings = events.reduce((accum, event) => {
    if (event.getTitle() === title) accum.push(event)
    return accum
  }, [])

  if (regularMeetings.length === 0) return

  // 祝日に設定された定例会議を取得
  // 祝日に定例会議が設定されていない場合はundefinedを取得
  const targets = holidays.map(holiday => {
    return regularMeetings.find(regularMeeting => {
      return regularMeeting.getStartTime().getDate() === holiday.getStartTime().getDate()
    })
  })

  // 祝日に設定された定例会議を削除
  targets.forEach(target => {
    if (target) target.deleteEvent()
  })
}

全体として実現したいこと

  • カレンダーへの追加は毎月1日に翌月分を入れる 
  • 定例会議は毎週月曜日の10時から行われる 
  • 祝日は定例会議の予定が入らない 
  • 開始時刻の1時間前にチャットに定例会議の予定が通知される
  • 予定に定型の議事録をくっつける
  • 毎週金曜日に翌週の定例会議の議題を議事録に記入するよう参加者に通知する

定例会議の議事録テンプレートをつくる

定例会議の議事録テンプレートをGoogleドキュメントでつくります。手動は嫌ですが、テンプレを一つつくるくらいでしたらゆるゆるとつくれそうです。

マイドライブで右クリックメニューをひらいて「新しいフォルダ」を左クリック

フォルダ名を「議事録」にする

議事録フォルダ内で右クリックメニューをひらき「Googleドキュメント」を左クリック

Googleドキュメントで議事録テンプレートをつくっておきます

定例会議の説明に議事録のURLをはりつける

ここからの処理の順番はこのようにします。

  1. 定例会議の議事録テンプレートを取得する
  2. 定例会議の日時を取得する
  3. 日時をyyyymmdd形式にする
  4. 議事録テンプレートをコピーする
  5. コピーした議事録テンプレートの名前を「yyyymmdd_定例会議議事録」に変更する
  6. 定例会議の説明に議事録のURLをはりつける
  7. 祝日の議事録を削除する

一つずつやっていきます。

ちょいとややこしいのでがんばっていきまっしょい!

1.定例会議の議事録テンプレートを取得する

const minutesTemplateName = 'yyyymmdd_定例会議議事録'
const minutesTemplate = DriveApp.getFilesByName(minutesTemplateName).next()

getFilesByNameメソッド ー ファイル名からファイルイテレーターを取得するメソッド

構文:ドライブアプリオブジェクト.getFilesByName()

戻り値:ファイルイテレーター

参照)Google Apps Scriptのマニュアル ドライブアプリクラス


議事録テンプレートのファイル名である「yyyymmdd_定例会議議事録」をもとにgetFilesByNameメソッドを使ってファイルイテレーターを取得します。

ファイルイテレーターについては↓で説明してます。

配列に似ていますが、配列ではありません。

たとえると、配列はお団子がならんだ状態。

イテレーターはお団子製造機です。


ここで取得したファイルイテレーターは1つの定例会議議事録テンプレートのみを製造します。

よって、nextメソッドで生成され、取得できるのは定例会議議事録テンプレートです。

2.定例会議の日時を取得する

regularMeetings.forEach(regularMeeting => {
  // 議事録の名前のyyyymmddにあたる部分を定義
  const regularMeetingDate = new Date(regularMeeting.getStartTime())
  const yyyy = regularMeetingDate.getFullYear()
  const mm = (regularMeetingDate.getMonth() + 1).toString().padStart(2, '0')
  const dd = regularMeetingDate.getDate().toString().padStart(2, '0')
})

regularMeetings(複数形)は定例会議が入った配列です。

forEachメソッドで定例会議をregularMeeting(単数形)として一つずつ取り出しています。

定例会議の日時をDateオブジェクトとして取得し、yyyyに年、mmに月、ddに日をそれぞれ格納しています。

さらに月と日は2桁固定にして、1桁だった場合は0で埋めたいです。(8月の場合は08と表現したい)

そこでgetMonthメソッド、getDateメソッドで取得できる数値型をtoStringで文字列型に変換してから、padStartメソッドを使って2桁の0埋めを実現しています。(0埋めをするには文字列型にする必要があります)

3.日時をyyyymmdd形式にする

regularMeetings.forEach(regularMeeting => {
  // 議事録の名前のyyyymmddにあたる部分を定義
  const regularMeetingDate = new Date(regularMeeting.getStartTime())
  const yyyy = regularMeetingDate.getFullYear()
  const mm = (regularMeetingDate.getMonth() + 1).toString().padStart(2, '0')
  const dd = regularMeetingDate.getDate().toString().padStart(2, '0')
  const yyyymmdd = yyyy + mm + dd
})


7行目を追加しています。

yyyy、mm、ddを合体させてyyyymmdd変数をつくっています。

yyyymmdd変数にはたとえば、20240314みたいな文字列が入っています。

4.議事録テンプレートをコピーする

regularMeetings.forEach(regularMeeting => {
  // 議事録の名前のyyyymmddにあたる部分を定義
  const regularMeetingDate = new Date(regularMeeting.getStartTime())
  const yyyy = regularMeetingDate.getFullYear()
  const mm = (regularMeetingDate.getMonth() + 1).toString().padStart(2, '0')
  const dd = regularMeetingDate.getDate().toString().padStart(2, '0')
  const yyyymmdd = yyyy + mm + dd

  // 議事録のコピーを作成し、議事録の名前を変更
  const regularMeetingMinutes = minutesTemplate.makeCopy()
})

9行目、10行目を追加しています。

makeCopyメソッドを使って議事録テンプレートのコピーをつくっています。

5.コピーした議事録テンプレートの名前を「yyyymmdd_定例会議議事録」に変更する

regularMeetings.forEach(regularMeeting => {
  // 議事録の名前のyyyymmddにあたる部分を定義
  const regularMeetingDate = new Date(regularMeeting.getStartTime())
  const yyyy = regularMeetingDate.getFullYear()
  const mm = (regularMeetingDate.getMonth() + 1).toString().padStart(2, '0')
  const dd = regularMeetingDate.getDate().toString().padStart(2, '0')
  const yyyymmdd = yyyy + mm + dd

  // 議事録のコピーを作成し、議事録の名前を変更
  const regularMeetingMinutes = minutesTemplate.makeCopy()
  regularMeetingMinutes.setName(`${yyyymmdd}_定例会議議事録`)
})

11行目を追加しています。

コピーされた議事録テンプレートのsetNameメソッドを使って名前を変更しています。

ファイル名はテンプレートリテラルを使って`${yyyymmdd}_定例会議議事録`としています。

6.定例会議の説明に議事録のURLをはりつける

regularMeetings.forEach(regularMeeting => {
  // 議事録の名前のyyyymmddにあたる部分を定義
  const regularMeetingDate = new Date(regularMeeting.getStartTime())
  const yyyy = regularMeetingDate.getFullYear()
  const mm = (regularMeetingDate.getMonth() + 1).toString().padStart(2, '0')
  const dd = regularMeetingDate.getDate().toString().padStart(2, '0')
  const yyyymmdd = yyyy + mm + dd

  // 議事録のコピーを作成し、議事録の名前を変更
  const regularMeetingMinutes = minutesTemplate.makeCopy()
  regularMeetingMinutes.setName(`${yyyymmdd}_定例会議議事録`)

  // コピーした議事録のURLを定例会議の説明部分にはりつけ
  regularMeeting.setDescription(regularMeetingMinutes.getUrl())
})

setDescriptionメソッド ー ファイルに説明をつけるメソッド

構文:ファイルオブジェクト.setDescription()

戻り値:ファイルオブジェクト

参照)Google Apps Scriptのマニュアル ファイルクラス

7.祝日の議事録を削除する

  targets.forEach(target => {
    if (target) {
      const targetDate = target.getStartTime()
      const targetYyyy = targetDate.getFullYear()
      const targetMm = (targetDate.getMonth() + 1).toString().padStart(2, '0')
      const targetDd = (targetDate.getDate()).toString().padStart(2, '0')
      const targetYyyyMmDd = targetYyyy + targetMm + targetDd
      const targetMinutesName = `${targetYyyyMmDd}_定例会議議事録`
      const targetMinutes = DriveApp.getFilesByName(targetMinutesName).next()
      targetMinutes.setTrashed(true)
      target.deleteEvent()
    }
  })

祝日の定例会議だけでなく、祝日につくられた議事録も削除します。

祝日に設定された定例会議の年月日をYYYYMMDD形式で取得します。

「YYYYMMDD_定例会議議事録」というファイル名の議事録が削除対象です。議事録をgetFilesByNameで取得してゴミ箱に入れる処理を書いて完了です。


ではコード全体はどうなっているかというと、

const setRegularMeeting = () => {
  // 翌月末を取得
  const current = new Date()
  const nextMonthLastDay = new Date(current.getFullYear(), current.getMonth() + 2, 0)

  // 繰り返しルールを設定
  const calendarApp = CalendarApp
  const recurrence = calendarApp.newRecurrence()
  const weeklyRecurrenceRule = recurrence.addWeeklyRule()
  const recurrenceRule = weeklyRecurrenceRule.onlyOnWeekday(calendarApp.Weekday.MONDAY).until(nextMonthLastDay)

  // 定例会議の名称、当月最初の定例会議の開始日時、終了日時を設定
  const title = '定例会議ですよ!'
  const dayIndex = new Date(current.getFullYear(), current.getMonth() + 1, 1).getDay()
  const firstDay = (9 - dayIndex) % 7 === 0 ? 7 : (9 - dayIndex) % 7
  const startTime = new Date(current.getFullYear(), current.getMonth() + 1, firstDay, 10, 0, 0)
  const endTime = new Date(current.getFullYear(), current.getMonth() + 1, firstDay, 11, 0, 0)

  // 予定の繰り返しを作成
  const defaultCalendar = calendarApp.getDefaultCalendar()
  const eventSeries = defaultCalendar.createEventSeries(title, startTime, endTime, recurrenceRule)
  
  // 翌月の祝日を取得
  const holidayCalendar = calendarApp.getCalendarById('ja.japanese#holiday@group.v.calendar.google.com')
  const nextMonthFirstDay = new Date(current.getFullYear(), current.getMonth() + 1, 1)
  const holidays = holidayCalendar.getEvents(nextMonthFirstDay, nextMonthLastDay)

  // 定例会議を取得
  const events = defaultCalendar.getEvents(nextMonthFirstDay, nextMonthLastDay)
  const regularMeetings = events.reduce((accum, event) => {
    if (event.getTitle() === title) accum.push(event)
    return accum
  }, [])

  if (regularMeetings.length === 0) return

  // 祝日に設定された定例会議を取得
  // 祝日に定例会議が設定されていない場合はundefinedを取得
  const targets = holidays.map(holiday => {
    return regularMeetings.find(regularMeeting => {
      return regularMeeting.getStartTime().getDate() === holiday.getStartTime().getDate()
    })
  })

  // 議事録テンプレートを取得
  const minutesTemplateName = 'yyyymmdd_定例会議議事録'
  const minutesTemplate = DriveApp.getFilesByName(minutesTemplateName).next()

  // 議事録テンプレートをコピーして名前を変更し、定例会議の説明にURLを貼り付ける
  regularMeetings.forEach(regularMeeting => {
    // 議事録の名前のyyyymmddにあたる部分を定義
    const regularMeetingDate = new Date(regularMeeting.getStartTime())
    const yyyy = regularMeetingDate.getFullYear()
    const mm = (regularMeetingDate.getMonth() + 1).toString().padStart(2, '0')
    const dd = regularMeetingDate.getDate().toString().padStart(2, '0')
    const yyyymmdd = yyyy + mm + dd

    // 議事録のコピーを作成し、議事録の名前を変更
    const regularMeetingMinutes = minutesTemplate.makeCopy()
    regularMeetingMinutes.setName(`${yyyymmdd}_定例会議議事録`)

    // コピーした議事録のURLを定例会議の説明部分にはりつけ
    regularMeeting.setDescription(regularMeetingMinutes.getUrl())
  })

  // 祝日に設定された定例会議と祝日の議事録を削除
  targets.forEach(target => {
    if (target) {
      const targetDate = target.getStartTime()
      const targetYyyy = targetDate.getFullYear()
      const targetMm = (targetDate.getMonth() + 1).toString().padStart(2, '0')
      const targetDd = (targetDate.getDate()).toString().padStart(2, '0')
      const targetYyyyMmDd = targetYyyy + targetMm + targetDd
      const targetMinutesName = `${targetYyyyMmDd}_定例会議議事録`
      const targetMinutes = DriveApp.getFilesByName(targetMinutesName).next()
      targetMinutes.setTrashed(true)
      target.deleteEvent()
    }
  })
}


実行結果はというと、

カレンダーの月表示

定例会議の詳細

ドライブ

このようにすべて意図通りの結果となりました。万歳。

いやいや待ってください、作成された定例会議議事録を見てみるとこんな感じになってます。

どうせだったらドキュメント本文の日付、時間も変更したいですよね。

そして参加者も自動的に入るようにしたくないですか?


というわけで次回はドキュメント本文の日付、時間を変更します。

ちなみに今回、添付ファイルではなく、説明欄にURLをはりつけるようにしたのは添付ファイルをつけるためにはDriveAPIで予定を作成する必要があり、若干説明がややこしくなるからです。

今現在のドライブアプリでは添付ファイルをつけられないんです。


バイチャ

Copied title and URL