LoginSignup
3
5
記事投稿キャンペーン 「2024年!初アウトプットをしよう」

【0からGASを学ぶ】GASを用いてGoogleフォーム内の項目を自動更新しよう

Last updated at Posted at 2024-01-03

はじめに

本シリーズでは、GASの始め方や便利な使い方、ビジネス活用まで幅広く解説します。シリーズをひと通り読んでいただければ、あなたもきっとGASマスターになれるはずです。

シリーズの対象者

  • そもそもGASってなんだかわからない
  • GASを学びたいけど何から始めればいいかわからない方
  • GASはわかり始めたけど、もっと活用ができないかと模索している方
  • とにかくGoogleが好き! という方

前回記事

GoogleフォームをGASで更新する

では早速始めていきましょう。【0からGASを学ぶ】シリーズの第18回は「GASを用いてGoogleフォーム内の項目を自動更新しよう」です。前回まではGemini Pro APIの無料期間がいつまで続くかわからないため、急遽Geminiシリーズを間髪入れずにお伝えしました。
少し話を戻して、今回はまたGoogleフォームに関する小ネタをお伝えします。第18回ではGoogleフォーム内の質問項目をGASから更新するというのをやってみます。また、様々なフォーム項目に関する更新ではなく、ピンポイントにプルダウンの更新を行い、簡易的なGoogleフォームによる予約受付フォームを実現してみます。まずはこれでイメージを膨らませ、次回Googleフォームで提供する各種項目の追加方法をお伝えすることにします。

今回やること

  1. Googleフォームのフォーム項目をGASで取得する。
  2. 1で取得した値を用いてスプレッドシートを更新する。
  3. スプレッドシートの内容を用いてGoogleフォームを更新する。
  4. 予約受付結果をメールで送信する。

事前準備

Googleフォームを作成

今回の起動元となるフォームを作成していきましょう。どのようなフォームでもよいのですが予約受付フォームっぽく以下のようなフォームを作成しました。

項目名 タイプ 必須
予約者氏名 記述式
予約者メールアドレス 記述式
予約日 プルダウン

参考に私が作成したフォームは以下となります。

予約日空き状況を管理するスプレッドシートを作成

Googleスプレッドシートを用いて、予約日空き状況を管理するシートを作成しましょう。どんな様式でも構いませんがこのシートのカラム順を前提に今回のプログラムは作成します。なお、私は以下のように作成しました。

このシートの予約受付日に記載の2024/1/1から1/8までを、先述のGoogleフォームの予約日に初期セットアップしています。

IDの取得

予約日空き状況シートのIDは事前に取得しておきましょう。

試しに一度回答

Googleフォームを作成したら、右上の「プレビュー」ボタンを押下して、一度回答してみましょう。のちほど回答データを取得するためだけなので、どのような回答でも構いません。

プログラム開始

GASエディタを開く

準備したGoogleフォームからコンテナバインド型でGASプログラムを記述していきましょう。Googleフォームからコンテナバインド型のGASを開く場合は、こちらを参考にしてください。では、どんどんいきますよ、ついてきてください。

STEP.1 Googleフォームのフォーム項目をGASで取得する

Qiita018
function Qiita018_formSubmit(e) {
  let itemResponses;
  // フォームの回答をイベントオブジェクトまたはフォーム自身から取得する。
  if (e !== undefined) {
    itemResponses = e.response.getItemResponses();
  } else {
    const wFormRes = FormApp.getActiveForm().getResponses();
    itemResponses =  wFormRes[wFormRes.length-1].getItemResponses();
  }
  console.log(itemResponses);
}
実行結果
21:02:40	お知らせ	実行開始
21:02:40	情報	[ { toString: [Function],
    getFeedback: [Function],
    getItem: [Function],
    setFeedback: [Function],
…
    getResponse: [Function],
    getScore: [Function],
    setScore: [Function] } ]
21:02:41	お知らせ	実行完了

これだとどんな値がとれているかはわかりませんが、ばっちりとオブジェクトは取得できていますので準備万端です。

今回もdayjsライブラリを使って日付の操作を行うため、こちらの記事を参考にライブラリを追加しておいてください。

STEP.2 1で取得した値を用いてスプレッドシートを更新する

次に、回答内容を用いて予約日空き状況シートを更新していきましょう。では、どのように更新していくかを考えてみます。

  1. 回答内容の予約日と一致する予約受付日を探す
  2. 予約受付日の空き枠が予約済を超えていないかを確認する
  3. 超えていなければ予約可とし、予約済をインクリメントする

といった具合になるかと思います。

Qiita018
+// Qiita018_予約空き状況
+const SPREAD_ID = '**事前準備で取得しておいたIDを記述**';
+// Qiita018_予約空き状況シートのカラム情報
+const COL = {
+  DAY:1,
+  FREE:2,
+  RESERVE:3
+};
+
/**
 * 予約受付フォームが送信された際のトリガーメソッド
 * @param {EventObject} e - イベントオブジェクト
 */
function Qiita018_formSubmit(e) {
+  const activeForm = FormApp.getActiveForm()
+        , wSheet = SpreadsheetApp.openById(SPREAD_ID).getSheets()[0]
+        , vals = wSheet.getDataRange().getValues();
+
  let itemResponses;
  // フォームの回答をイベントオブジェクトまたはフォーム自身から取得する。
  if (e !== undefined) {
    itemResponses = e.response.getItemResponses();
  } else {
    const wFormRes = activeForm.getResponses();
    itemResponses =  wFormRes[wFormRes.length-1].getItemResponses();
  }
-  console.log(itemResponses);
+
+  vals.shift();
+  // 回答から空き状況を更新する
+  itemResponses.forEach(function(itemResponse){
+    switch (itemResponse.getItem().getTitle()) {
+      case '予約日':
+        vals.forEach(function(val, idx){
+          // 予約日と台帳上の日付を比較して同日を判定
+          if (dayjs.dayjs(val[COL.DAY-1]).isSame(dayjs.dayjs(itemResponse.getResponse()), 'd')) {
+            // 空き枠 > 予約済なら予約成功
+            if (!val[COL.RESERVE-1] || Number(val[COL.FREE-1])>Number(val[COL.RESERVE-1])) {
+              val[COL.RESERVE-1]++;
+              wSheet.getRange(idx+2, COL.RESERVE).setValue(val[COL.RESERVE-1]);
+            } 
+          }
+        });
+        break;
+
+      default:
+        break;
+    }
+  });
}

これを実行すると、先ほどお試し送信した回答に応じて予約日空き状況シートの予約済が更新されます。

ここでのポイントは

vals.shift();

とし、wSheet.getDataRange().getValues();で取得した値リストの1行目(ヘッダー)を削除しています。こうすると、ヘッダーかどうかをループ処理の中で判定する必要がなくなるのでおすすめです。

STEP.3 スプレッドシートの内容を用いてGoogleフォームを更新する

では今回の本題であるGoogleフォームの更新をしていきましょう。フォームを更新する場合や項目を新規に作成する場合は、どういった項目を使うのかを定義することがポイントです。

更新をする場合

FormApp.getActiveForm().getItems().forEach(function(item) {
  item.asXXXXItem().setYYYYYY();
});

新規作成する場合

FormApp.getActiveForm().addXXXXItem().setTitle('AAAAAAAAA')
                                     .setYYYYYY();

今はざっくりとしたイメージを持つだけで構いません。冒頭にも記載した通り、各種項目の新規作成・更新方法を次回お伝えします。

では実装してみましょう

Qiita018
// Qiita018_予約空き状況
const SPREAD_ID = '**事前準備で取得しておいたIDを記述**';
// Qiita018_予約空き状況シートのカラム情報
const COL = {
  DAY:1,
  FREE:2,
  RESERVE:3
};

/**
 * 予約受付フォームが送信された際のトリガーメソッド
 * @param {EventObject} e - イベントオブジェクト
 */
function Qiita018_formSubmit(e) {
  const activeForm = FormApp.getActiveForm()
        , wSheet = SpreadsheetApp.openById(SPREAD_ID).getSheets()[0]
        , vals = wSheet.getDataRange().getValues()
+        , dateLst = new Array();

  let itemResponses;
  // フォームの回答をイベントオブジェクトまたはフォーム自身から取得する。
  if (e !== undefined) {
    itemResponses = e.response.getItemResponses();
  } else {
    const wFormRes = activeForm.getResponses();
    itemResponses =  wFormRes[wFormRes.length-1].getItemResponses();
  }

  vals.shift();
  // 回答から空き状況を更新する
  itemResponses.forEach(function(itemResponse){
    switch (itemResponse.getItem().getTitle()) {
      case '予約日':
        vals.forEach(function(val, idx){
          // 予約日と台帳上の日付を比較して同日を判定
          if (dayjs.dayjs(val[COL.DAY-1]).isSame(dayjs.dayjs(itemResponse.getResponse()), 'd')) {
            // 空き枠 > 予約済なら予約成功
            if (!val[COL.RESERVE-1] || Number(val[COL.FREE-1])>Number(val[COL.RESERVE-1])) {
              val[COL.RESERVE-1]++;
              wSheet.getRange(idx+2, COL.RESERVE).setValue(val[COL.RESERVE-1]);
            }
          }
        });
        break;

      default:
        break;
    }
  });

+  // 空き枠がある日付のみでフォームを更新する
+  vals.forEach(function(val){
+    if (!val[COL.RESERVE-1] || Number(val[COL.FREE-1])>Number(val[COL.RESERVE-1])) {
+      dateLst.push(dayjs.dayjs(val[COL.DAY-1]).format('YYYY/MM/DD'));
+    }
+  });
+  if (dateLst.length>0) {
+    activeForm.getItems().forEach(function(item) {
+      if (item.getTitle()=='予約日') {
+        item.asListItem().setChoiceValues(dateLst);
+      }
+    });
+  } else {
+    // 全日程が受付終了となれば予約フォーム自体の受付を終了
+    activeForm.setAcceptingResponses(false);
+  }
}

上記のようにすると、

  1. まだあまりがある日程のみがフォームで選択できる
  2. 全日程の予約が埋まった場合(dateLstの要素が0の場合)、フォームの受付を終了する

といった機能が実現できます。

実行例

予約日空き状況
更新後のフォーム

STEP.4 予約受付結果をメールで送信する

これはもうものすごく簡単です。

if (!val[COL.RESERVE-1] || Number(val[COL.FREE-1])>Number(val[COL.RESERVE-1])) {
  val[COL.RESERVE-1]++;
  wSheet.getRange(idx+2, COL.RESERVE).setValue(val[COL.RESERVE-1]);
}

この箇所で予約ができるか否かを判断しているため、ここで傷をつけましょう。

結論 こんな感じのプログラムができました

Qiita018.gs
// Qiita018_予約空き状況
const SPREAD_ID = '**事前準備で取得しておいたIDを記述**';
// Qiita018_予約空き状況シートのカラム情報
const COL = {
  DAY:1,
  FREE:2,
  RESERVE:3
};

/**
 * 予約受付フォームが送信された際のトリガーメソッド
 * @param {EventObject} e - イベントオブジェクト
 */
function Qiita018_formSubmit(e) {
  const activeForm = FormApp.getActiveForm()
        , wSheet = SpreadsheetApp.openById(SPREAD_ID).getSheets()[0]
        , vals = wSheet.getDataRange().getValues()
        , dateLst = new Array;

  let itemResponses;
  // フォームの回答をイベントオブジェクトまたはフォーム自身から取得する。
  if (e !== undefined) {
    itemResponses = e.response.getItemResponses();
  } else {
    const wFormRes = activeForm.getResponses();
    itemResponses =  wFormRes[wFormRes.length-1].getItemResponses();
  }

  vals.shift();
  // 回答から空き状況を更新する
  let replyAddress, reserveErr;
  itemResponses.forEach(function(itemResponse){
    switch (itemResponse.getItem().getTitle()) {
      case '予約者メールアドレス':
        replyAddress = itemResponse.getResponse();
        break;

      case '予約日':
        vals.forEach(function(val, idx){
          // 予約日と台帳上の日付を比較して同日を判定
          if (dayjs.dayjs(val[COL.DAY-1]).isSame(dayjs.dayjs(itemResponse.getResponse()), 'd')) {
            // 空き枠 > 予約済なら予約成功
            if (!val[COL.RESERVE-1] || Number(val[COL.FREE-1])>Number(val[COL.RESERVE-1])) {
              val[COL.RESERVE-1]++;
              wSheet.getRange(idx+2, COL.RESERVE).setValue(val[COL.RESERVE-1]);
              reserveErr = false;
            } else {
              reserveErr = true;
            }
          }
        });
        break;

      default:
        break;
    }
  });

  // 空き枠がある日付のみでフォームを更新する
  vals.forEach(function(val){
    if (!val[COL.RESERVE-1] || Number(val[COL.FREE-1])>Number(val[COL.RESERVE-1])) {
      dateLst.push(dayjs.dayjs(val[COL.DAY-1]).format('YYYY/MM/DD'));
    }
  });
  if (dateLst.length>0) {
    activeForm.getItems().forEach(function(item) {
      if (item.getTitle()=='予約日') {
        item.asListItem().setChoiceValues(dateLst);
      }
    });
  } else {
    // 全日程が受付終了となれば予約フォーム自体の受付を終了
    activeForm.setAcceptingResponses(false);
  }

  // 予約エラーとなった方には予約できなかった旨をメール送信
  if (reserveErr) {
    sendEmailEx(
      replyAddress,
      '【自動送信】予約不可',
      '予約を受け付けることができませんでした。'
    );
  } else {
    sendEmailEx(
      replyAddress,
      '【自動送信】予約可',
      '予約を受け付けました。'
    );
  }
}

/**
 * メール送信の裏技メソッド
 * @param {String} _recipient - 送信先
 * @param {String} _subject - 件名
 * @param {String} _body - 本文
 * @param {Object} _option - 送信オプション
 */
function sendEmailEx(_recipient, _subject, _body, _option) {
  // 引数の内容でメールを下書き保存する
  const mailDraft = GmailApp.createDraft(_recipient, _subject, _body, _option);
  // 下書き保存したメールIDから下書きを取得し、メール送信を依頼する
  GmailApp.getDraft(mailDraft.getId()).send();
}

Googleフォームを回答がある度に更新しているため、基本的には回答を送信すれば予約を受け付けることが可能です。しかしながら、ちょうど同じタイミングでフォームをひらいた方が複数いた場合、また両者が同じ日付を選んだ場合にダブルブッキングになることもあります。そのため、念のため、プログラムの中でも予約の可否を判断しています。

おわりに

お疲れ様でした。
第18回は「GASを用いてGoogleフォーム内の項目を自動更新しよう」ということで、Googleフォーム内の項目に設定するリストをスプレッドシートで管理し、回答を受け付けるごとにGoogleフォームを更新するということを実現しました。次回は、各項目の新規作成・更新方法をお伝えします。今回と次回予定のテクニックはGoogleフォームを業務活用する中で必須のテクニックとなります。ぜひ、皆さんも実業務での活用に取り組んでみてください。引き続き、GASを楽しんでいきましょう!!
記事を読んで、「良いな」や「今後に期待できる!」と感じて頂けたらいいねフォローコメントいただけると幸いです。それではまた次回をお楽しみに!

ブログでより詳しく解説しています!

以下画像をクリックしてブログにアクセス!!

3
5
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
3
5