【複数対応版】Googleスプレッドシートを使って複数名のGoogleカレンダーに予定をワンクリックで追加・削除する方法【第三弾】

作業効率化
この記事は約27分で読めます。

前回はGoogleスプレッドシートを使ってGoogleカレンダーに予定をワンクリックで追加・削除するということを紹介いたしました。

今回はお待ちかね、第三弾!

前回は、Googleカレンダーに日々の予定を一括で追加、削除できたら便利ということで、そういうスクリプトを書いてみたわけです。

今回は、それが複数名に一気に反映されたら嬉しいよねということで、日々の予定を一括で複数名のカレンダーに追加、削除できるようなものを作ってみたいと思います。実はメールでアイデアをいただきまして、この度作成する運びとなりました。

手間のかかる作業とはおさらば!Googleスプレッドシートを使って日々の予定を一括でワンクリックで、指定したメンバー全員に、追加・削除する方法について解説していきたいと思います。

他人に迷惑をかけるような使い方は許可しません。

スポンサーリンク

Google スプレッドシートを開く

Googleのサイトを開き、右上の3つの「」をクリックして、下にスクロールして出てくる「スプレッドシート」を選択します。

スプレッドシートを開いて、「空白のスプレッドシート」を作成します。

新しいスプレッドシートが開けました。

表を作る

まず表を作成します。まずタイトルを付けます。「カレンダー追加・削除用(複数名対応版)」とでもしましょう。

カレンダーシートの基本設定

次にシート名も変更しておきます。わかりやすく「カレンダー」とでも付けておくとよいでしょう。行番号と列番号に気をつけて表を作成してください

前回のデータを使いまわししているので、追加・削除ボタンが既に付いていますが気にしないで下さい。

9行目」に「連携」「日付」「曜日」「開始時間」「終了時間」「タイトル」「場所」「説明」の項目を作り、幅や色、枠線などを付けて成形します。

メールアドレスシートの基本設定

カレンダーを登録するユーザーの「メールアドレス」も登録します。メールアドレスは新しいシートを開き、シート名を「メールアドレス」と変更しておきます。

図のようにタイトルを記述して、A2からA3A4…とメールアドレスを記述していきます。A列だけが必要なので、B列などの情報は適当で構いません。また、登録できるメールアドレスの数はほぼ無限となっています。

2024/10/22 13:12 追記:
メールアドレスの間違いに注意してください。私も実際やらかしました。細かいところまで正しくメールアドレスが入力できているかを今一度ご確認ください。

特定のユーザーまたはグループと共有する(登録アカウントが異なる場合)

登録用に使用しているスプレッドシートのユーザーと、登録先のカレンダーのユーザーが異なる場合には、次の設定を行う必要があります。

本記事では、複数名のユーザーのカレンダーに一気にスプレッドシートで記入した予定を登録するというものですが、あらかじめ個々の登録先カレンダーのユーザーに許可して頂く必要があります。

2024/10/03 18:17 追記:
プログラムを実行する方が、使用するカレンダーの持ち主に許可をもらうという形になります。逆ではありませんので、ご注意を。

設定と共有を開く

マイカレンダーの登録先のカレンダーのユーザー名の横にある三点リーダー「」をクリックし、「設定と共有」を開きます。

予定のアクセス権限にユーザーを追加する

ユーザー名の「予定のアクセス権限」をクリックし、画面右側の「ユーザーやグループを追加」でスプレッドシートのユーザーを登録します。

次に与える権限として、「予定の変更」を選択します。

拡張機能を使ってプログラムを組み込む

メインプログラム

拡張機能」>「Apps Script」を開きます。

無題のプロジェクト」が開きます。タイトルを変更して「カレンダー追加・削除用マクロ」とでもしておいてください。

次の文章をコピペします。(バグ修正等の情報はこちら

/**
 * スプレッドシートを開いたときにカスタムメニューを追加する関数
 */
function onOpen() {
  const ui = SpreadsheetApp.getUi();
  ui.createMenu('カレンダーツール')
    .addItem('予定を作成', 'createSchedule')
    .addItem('予定を削除', 'deleteSchedule')
    .addToUi();
}

/**
 * カレンダーを追加登録する関数
 * この関数は一度だけ実行することを推奨します
 */
function addCalendar(id) {
  try {
    CalendarApp.subscribeToCalendar(id);
    Logger.log(`カレンダー ${id} を登録しました。`);
  } catch (e) {
    Logger.log(`カレンダー登録中にエラーが発生しました: ${e}`);
    SpreadsheetApp.getUi().alert(`カレンダー登録中にエラーが発生しました:\n${e.message}`);
  }
}

/**
 * 時間を "HH:mm" 形式にフォーマットする関数
 * @param {Date} date 
 * @returns {string}
 */
function timeFormatter(date) {
  if (date instanceof Date && !isNaN(date)) {
    return Utilities.formatDate(date, Session.getScriptTimeZone(), 'HH:mm');
  } else {
    return '00:00';
  }
}

/**
 * 予定を作成する関数
 */
function createSchedule() {
  
  // 初期設定
  const spreadsheet       = SpreadsheetApp.getActiveSpreadsheet();
  const sheet             = spreadsheet.getSheetByName('カレンダー');
  const sheet_mailaddress = spreadsheet.getSheetByName('メールアドレス');
  
  // カレンダーIDを取得
  let i = 0; // カウント用
  const gAccounts = [];

  while (!sheet_mailaddress.getRange(i + 2, 1).isBlank()) {
    const email = sheet_mailaddress.getRange(i + 2, 1).getValue();
    gAccounts.push(email);
    i++;
  }
  
  const roop = gAccounts.length; // メールアドレスの数を取得
  
  // 読み取り範囲(表の始まり行と終わり列)
  const topRow  = 10;  // データの開始行
  const lastCol = 10;  // データの終了列(J列まで含む)
  
  // 列インデックス(0ベース)
  const STATUS_COL      = 1; // B列
  const DAY_COL         = 2; // C列
  const START_TIME_COL  = 4; // E列
  const END_TIME_COL    = 5; // F列
  const TITLE_COL       = 6; // G列
  const LOCATION_COL    = 7; // H列
  const DESCRIPTION_COL = 8; // I列
  const EVENT_ID_COL    = 9; // J列
  
  // 予定の最終行を取得
  const lastRowSheet = sheet.getLastRow();
  const numRows = lastRowSheet - topRow + 1;
  
  if (numRows <= 0) {
    SpreadsheetApp.getUi().alert("データが存在しません。");
    return;
  }
  
  // 予定の一覧を取得
  const contents = sheet.getRange(topRow, 1, numRows, lastCol).getValues();
  
  // Googleカレンダーの取得
  const calendars = [];
  for (let i = 0; i < roop; i++) {
    // カレンダーを登録(必要に応じて一度だけ実行)
    addCalendar(gAccounts[i]);

    const calendar = CalendarApp.getCalendarById(gAccounts[i]);
    if (calendar) {
      calendars.push(calendar);
    } else {
      Logger.log(`カレンダーが取得できませんでした。カレンダーID: ${gAccounts[i]}`);
      SpreadsheetApp.getUi().alert(`カレンダーが取得できませんでした。カレンダーID: ${gAccounts[i]}`);
    }
  }
  
  if (calendars.length === 0) {
    SpreadsheetApp.getUi().alert("有効なカレンダーがありません。");
    return;
  }
  
  // ユーザーインターフェースの取得
  const ui = SpreadsheetApp.getUi();
  
  // 予定を作成
  for (let i = 0; i < roop; i++) {
    const calendar = calendars[i];
    if (!calendar) continue;
    
    for (let j = 0; j < numRows; j++) {
      
      // ステータスと日付を取得
      const status = contents[j][STATUS_COL];
      const dayValue = contents[j][DAY_COL];
      
      if (status === "済" || status === "済み" || dayValue === "") {
        continue;
      }
      
      Logger.log(`カレンダー ${gAccounts[i]} の Row ${topRow + j}: ステータス=${status}, 日付=${dayValue}`);
      
      // データの取得と検証
      const day = new Date(dayValue);
      const startTime = contents[j][START_TIME_COL];
      const endTime = contents[j][END_TIME_COL];
      const title = contents[j][TITLE_COL] || "タイトルなし";
      const location = contents[j][LOCATION_COL];
      const description = contents[j][DESCRIPTION_COL];
      
      // オプションをセット
      const options = {
        location: location,
        description: description
      };
      
      try {
        let event;
        if (!startTime || !endTime) {
          // 終日イベントとして作成
          Logger.log(`終日イベント作成: タイトル=${title}, 日付=${day}`);
          event = calendar.createAllDayEvent(title, day, options);
        } else {
          // 開始時間と終了時間が有効なDateオブジェクトであることを確認
          if (!(startTime instanceof Date) || !(endTime instanceof Date)) {
            throw new Error("開始時間または終了時間が有効な日時形式ではありません。");
          }
          
          // 時間指定イベントとして作成
          const startDate = new Date(day);
          startDate.setHours(startTime.getHours());
          startDate.setMinutes(startTime.getMinutes());
          
          const endDate = new Date(day);
          endDate.setHours(endTime.getHours());
          endDate.setMinutes(endTime.getMinutes());
          
          Logger.log(`時間指定イベント作成: タイトル=${title}, 開始=${startDate}, 終了=${endDate}`);
          event = calendar.createEvent(title, startDate, endDate, options);
        }
        
        // 無事に予定が作成されたら「済」にする
        sheet.getRange(topRow + j, STATUS_COL + 1).setValue("済");
        
      } catch (e) {
        Logger.log(`カレンダー ${gAccounts[i]} の Row ${topRow + j} の予定作成中にエラーが発生しました: ${e}`);
        ui.alert(`カレンダー ${gAccounts[i]} の Row ${topRow + j} の予定作成中にエラーが発生しました:\n${e.message}`);
      }
    }
  }
  
  // 完了通知
  ui.alert("予定の追加が完了しました。");
}

/**
 * 予定を削除する関数
 */
function deleteSchedule() {
  
  // 初期設定
  const spreadsheet       = SpreadsheetApp.getActiveSpreadsheet();
  const sheet             = spreadsheet.getSheetByName('カレンダー');
  const sheet_mailaddress = spreadsheet.getSheetByName('メールアドレス');
  
  // カレンダーIDを取得
  let i = 0; // カウント用
  const gAccounts = [];
  
  while (!sheet_mailaddress.getRange(i + 2, 1).isBlank()) {
    const email = sheet_mailaddress.getRange(i + 2, 1).getValue();
    gAccounts.push(email);
    i++;
  }
  
  const roop = gAccounts.length; // メールアドレスの数を取得
  
  // 読み取り範囲(表の始まり行と終わり列)
  const topRow  = 10;  // データの開始行
  const lastCol = 10;  // データの終了列(J列まで含む)
  
  // 列インデックス(0ベース)
  const STATUS_COL      = 1; // B列
  const DAY_COL         = 2; // C列
  const START_TIME_COL  = 4; // E列
  const END_TIME_COL    = 5; // F列
  const TITLE_COL       = 6; // G列
  const EVENT_ID_COL    = 9; // J列
  
  // 予定の最終行を取得
  const lastRowSheet = sheet.getLastRow();
  const numRows = lastRowSheet - topRow + 1;
  
  if (numRows <= 0) {
    SpreadsheetApp.getUi().alert("データが存在しません。");
    return;
  }
  
  // 予定の一覧を取得
  const contents = sheet.getRange(topRow, 1, numRows, lastCol).getValues();
  
  // Googleカレンダーの取得
  const calendars = [];
  for (let i = 0; i < roop; i++) {
    // カレンダーを登録(必要に応じて一度だけ実行)
    addCalendar(gAccounts[i]);

    const calendar = CalendarApp.getCalendarById(gAccounts[i]);
    if (calendar) {
      calendars.push(calendar);
    } else {
      Logger.log(`カレンダーが取得できませんでした。カレンダーID: ${gAccounts[i]}`);
      SpreadsheetApp.getUi().alert(`カレンダーが取得できませんでした。カレンダーID: ${gAccounts[i]}`);
    }
  }
  
  if (calendars.length === 0) {
    SpreadsheetApp.getUi().alert("有効なカレンダーがありません。");
    return;
  }
  
  // ユーザーインターフェースの取得
  const ui = SpreadsheetApp.getUi();
  
  // 予定を削除
  for (let i = 0; i < roop; i++) {
    const calendar = calendars[i];
    if (!calendar) continue;
    
    for (let j = 0; j < numRows; j++) {
      
      // ステータスと日付を取得
      const status = contents[j][STATUS_COL];
      const dayValue = contents[j][DAY_COL];
      
      if (status === "済" || status === "済み" || status === "消" || status === "消し" || dayValue === "") {
        continue;
      }
      
      const day = new Date(dayValue);
      const eventId = contents[j][EVENT_ID_COL];
      const title = contents[j][TITLE_COL];
      
      if (eventId) {
        // イベントIDが存在する場合はIDで削除
        try {
          const event = calendar.getEventById(eventId);
          if (event) {
            event.deleteEvent();
            Logger.log(`カレンダー ${gAccounts[i]} の Row ${topRow + j} のイベントを削除しました: タイトル=${title}, 日付=${day}`);
            
            // 無事に予定が削除されたら「消」にする
            sheet.getRange(topRow + j, STATUS_COL + 1).setValue("消");
            
            // イベントIDをクリア
            sheet.getRange(topRow + j, EVENT_ID_COL + 1).setValue("");
          }
        } catch (e) {
          Logger.log(`カレンダー ${gAccounts[i]} の Row ${topRow + j} のイベント削除中にエラーが発生しました: ${e}`);
          ui.alert(`カレンダー ${gAccounts[i]} の Row ${topRow + j} のイベント削除中にエラーが発生しました:\n${e.message}`);
        }
      } else {
        // イベントIDが存在しない場合はタイトルと時間で削除
        const startTime = timeFormatter(contents[j][START_TIME_COL]);
        const endTime   = timeFormatter(contents[j][END_TIME_COL]);
        
        try {
          // 指定日のイベントを取得
          const events = calendar.getEventsForDay(day);
          
          for (let k = 0; k < events.length; k++) {
            const event = events[k];
            const eventTitle = event.getTitle();
            const eventStartTime = timeFormatter(event.getStartTime());
            const eventEndTime = timeFormatter(event.getEndTime());
            
            // 終日イベントの判定
            const isAllDay = event.isAllDayEvent();
            const formattedStartTime = isAllDay ? '00:00' : eventStartTime;
            const formattedEndTime = isAllDay ? '00:00' : eventEndTime;
            
            // タイトルと時間が一致する場合に削除
            if (title === eventTitle &&
                ((formattedStartTime === '00:00' && formattedEndTime === '00:00') ||
                 (formattedStartTime === startTime && formattedEndTime === endTime))) {
              event.deleteEvent();
              Logger.log(`カレンダー ${gAccounts[i]} の Row ${topRow + j} のイベントを削除しました: タイトル=${title}, 日付=${day}`);
              
              // 無事に予定が削除されたら「消」にする
              sheet.getRange(topRow + j, STATUS_COL + 1).setValue("消");
            }
          }
        } catch (e) {
          Logger.log(`カレンダー ${gAccounts[i]} の Row ${topRow + j} の予定削除中にエラーが発生しました: ${e}`);
          ui.alert(`カレンダー ${gAccounts[i]} の Row ${topRow + j} の予定削除中にエラーが発生しました:\n${e.message}`);
        }
      }
    }
  }
  
  // 完了通知
  ui.alert("予定の削除が完了しました。");
}

コピペし終わったら、「プロジェクトを保存」しておきます。

jsonファイルの設定

次に、Googleカレンダーへの実行権限を許可するためにjsonファイルの編集を行います。jsonファイルを編集するには、下図の左にある設定歯車ボタンをクリックして、「appsscript.json」マニフェスト ファイルをエディタで表示するにチェックを入れます。

そして、以下の文章をappsscript.jsonsに書き込みます。appsscript.jsonsファイルは、<>をクリックしたところにあります。

{
  "timeZone": "Asia/Tokyo",
  "dependencies": {
  },
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",

  "oauthScopes": [
    "https://www.googleapis.com/auth/script.external_request",
    "https://www.googleapis.com/auth/spreadsheets",
    "https://www.googleapis.com/auth/forms",
    "https://www.googleapis.com/auth/calendar"
 ]
}

コピペが終わったら、保存をしておいてください。

実行用のボタンを設置する(追加・削除)

ボタンを作成する

前回のスプレッドシートを使用した方は、ボタンの移動程度で特に作成の必要はありません。ちなみに、ボタンはCTRLを押してボタンをクリックした後に移動させられます。

まず実行用のボタンを作成します。メニューバーの「挿入」>「図形描画」をクリックします。

図形」「テキストボックス」を駆使して、「追加」「削除」の実行ボタンを作成します。作成が終わったら、「保存して終了」をクリックします。※ご自身でわかればいいので、どんなボタンでも構いません。

ボタンを設置する

保存して終了」をクリックすると、自動で表にボタンが設置されますので、クリックしやすい位置に場所を移動し、大きさも調整してください。

ボタンにマクロを追加する(追加用マクロ)

実行ボタンの右上に縦に「」マークがあるのがわかるかと思います。

」マークをクリックし、「スクリプトを割り当て」をクリックします。

スクリプトを割り当てる画面になりますので、「createSchedule」と入力して「確定」をクリックしてください。

これで追加用の実行ボタンの設置ができました。※一度マクロを登録した実行用ボタンを移動したい場合は、CTRLを押しながらボタンをクリックすることで移動可能になります。

ボタンにマクロを追加する(削除用マクロ)

実行ボタンの右上に縦に「」マークがあるのがわかるかと思います。

」マークをクリックし、「スクリプトを割り当て」をクリックします。

スクリプトを割り当てる画面になりますので、「deleteSchedule」と入力して「確定」をクリックしてください。

これで削除用の実行ボタンの設置ができました。※一度マクロを登録した実行用ボタンを移動したい場合は、CTRLを押しながらボタンをクリックすることで移動可能になります。

ワンクリックで予定を組み込んでみる(初回だけの手順)

適当でも実務的でも良いので予定を記入してから、どちらでも良いので実行ボタンをクリックしてください。すると次のような許可画面が出ると思いますので、「OK」をクリックします。

次にアカウントの選択画面になりますので、アカウントを選択します。

すると次のような警告が出ますが、このプログラムをGoogleに登録をしているわけではない為、警告が出るのは当たり前なので、ここは無視して「詳細」をクリックします。

下に表示されるリンクの「カレンダー追加・更新用マクロ(安全ではないページ)に移動」をクリックします。

作成したマクロ「Google Apps Script(GAS)」を登録するので「許可」をクリックします。

このような面倒な許可が必要なのは一度目だけです。二度目以降は面倒な手続きはないので安心してください。

ワンクリックで予定を追加してみる

適当にデータを入れて登録をしてみます。

実行ボタンの「追加」ボタンをクリックすると、以下のような画面が出て、Google カレンダーに予定が組み込まれます。

Google カレンダーを見てみると、登録してあるメールアドレスすべてに予定が反映されます。

ワンクリックで予定を削除してみる

先程登録したデータを利用します。「連携」の部分に「」という文字が入っていると思います。「」という文字を消してから、「削除」の実行ボタンを押します。

実行ボタンの「削除」ボタンをクリックすると、以下のような画面が出て、Google カレンダーから予定が削除されます。

Google カレンダーを見てみると、登録されているすべてのメールアドレスに紐づいたカレンダーの予定が削除されます。

バグ対応・バージョンアップ

  • 2024/09/14 19:38修正 タブがたくさん登録されている際に「カレンダー」タブを認識しないバグの修正。
  • 2024/09/24 22:24修正 削除で日付指定のときに削除が上手くいかないのを修正。暫定版。
  • 2024/09/24 23:25修正 登録の時は「済」、削除の際は「消」とするように修正。
  • 2024/10/02 15:26修正 カレンダーを追加登録する関数を追加し、他のユーザー情報にアクセスできるよう修正。「特定のユーザーまたはグループと共有する(登録アカウントが異なる場合)」の項目をブログに追加。

最後に

いかがでしたでしょうか?
追加・削除を複数のユーザーへ同時に反映させることができるようになりました。

ちなみにメールアドレスの数は無限に登録することができますが、管理しやすい範囲での作成をオススメいたします。(動作が劇的に遅くなるということではありません)
第三段も、大変便利となっておりますので、ご利用いただけると幸いです。

謎爺
謎爺

以下、この記事を書くにあたり、参考にしたサイトじゃ。

参考サイト

GASでGoogleカレンダーの予定を変更・削除する方法!件名や開始日・終了日(時間)を修正 | AutoWorker〜Google Apps Script(GAS)とSikuliで始める業務改善入門
Googleカレンダーの予定をすべてGASで削除する方法 | iwb.jp
Google Apps ScriptからGoogleカレンダーを削除する方法 | Kokoroe
スプレッドシートからGoogleカレンダーに予定を登録する方法(GAS) | Googleスプレッドシート 完全攻略
スプレッドシートからGoogleカレンダーに簡単に予定を登録する方法(初心者向け) | Googleスプレッドシート 完全攻略
スプレッドシートからカレンダーに連携してスケジュールを一括登録する【Google Apps Script(GAS)】 | 大阪のシステム開発なら 株式会社ウィズテクノロジー
【GAS】スプレッドシートに記載した予定をGoogleカレンダーにまとめて反映させたい #GAS – Qiita
GAS実行ユーザーのメールアドレス(Googleアカウント)を取得する方法 | AutoWorker〜Google Apps Script(GAS)とSikuliで始める業務改善入門
【Google Apps Script入門】セルの取得・変更をする | UX MILK
【GAS入門】変数/定数を宣言するvar・const・letの違い | Log Stocker
Googleスプレッドシートのスクリプト実行方法とボタンの実行方法 – Google Apps Script(GAS)を用いたシステム開発を支援
[GAS] シートを取得する方法
GASのwhile文の使い方解説!条件式がtrueの間くり返し処理し、無限ループに注意 | AutoWorker〜Google Apps Script(GAS)とSikuliで始める業務改善入門
GASのfor文の繰り返しループ処理とbreak、continueの使い分けを解説 | AutoWorker〜Google Apps Script(GAS)とSikuliで始める業務改善入門
【Google Apps Script入門】セルの取得・変更をする | UX MILK
【GAS】スプレッドシートif文セルの値の複数処理、空白かどうかisBlank()【Google Apps Script】 | スプレッドシートでGASる
[GASの使い方03]コンソール出力 | ITスキルアップ相談室
【GAS】配列の宣言・初期化・頻出メソッド

コメント

タイトルとURLをコピーしました