こんばんは。情シス(PCおじさん)の kadota です。
これは actindi Advent Calendar 2019、2日目の記事です。 12/2中に記事を出す予定でしたが、ぐずぐずしていたらすっかりこんな時間に…。こっそり記事をアップします。
Googleフォームを使って参加受付したい(かつ自動で締め切りたい)という話
Googleフォーム、アンケートを回収するときなどに便利ですよね。
このフォームを使ってあるイベントの参加受付をしたい、かつ自動で締め切りたいという話を社員から聞き、調べてみました。
※ネット上にはこの手の記事は色々とあるのですが、自分の場合はこうやった、という記録の意味も含めて残します。
単純に1つのイベントの参加を締め切る場合
イベント日時が単一のこの日、と決まっていて参加者の申し込みを受付けるだけの場合は、フォームにたまった回答の数を数えるだけです。LIMIT_COUNT
に上限値を入れておき、
var form = FormApp.getActiveForm(); if (form.getResponses().length >= LIMIT_COUNT) { //ここで何らかの処理をする }
と条件分岐します。
複数イベントの参加を締め切る場合
しかし今回は開催日が複数ありました。
こんな感じです。
それぞれのイベントで応募を受け付けるので、全ての上限に達してはじめてフォームを締め切ることができます。
そこで、各開催の受付数を調べて、上限に達したイベントについてはダイアログを出して注意しようと考えました(手抜き)。ダイアログはこんな感じのものを用意すれば出せる…
Browser.msgBox("(締め切り状況のテキスト)", Browser.Buttons.OK);
…はずでしたが、フォームには Browser.msgBox
が使えないということがわかりました。困った。
仕方なく、応募が上限に達したイベントを選択肢から消していく、という力技のやり方でいくことにしました。
まずはカウントするところから。フォームで受け取った全ての回答を集計します。
ANS_WORD1
ANS_WORD2
には選択肢を識別する特徴的な文字列を入れてやります。
var LIMIT_COUNT = 3; var ANS_WORD1 = '【1回目】'; var ANS_WORD2 = '【2回目】'; var form = FormApp.getActiveForm(); function _getAnswerCount() { var cnt1 = 0; var cnt2 = 0; form.getResponses().forEach(function(resp){ var items = resp.getItemResponses(); items.forEach(function(item){ var answer = item.getResponse(); if (answer.indexOf(ANS_WORD1) != -1) { cnt1++; } else if (answer.indexOf(ANS_WORD2) != -1) { cnt2++; } }); }); var r = {ans1: cnt1, ans2: cnt2}; return r; }
次に、上限に達したイベントを選択肢から消しこむ部分です。
_modifyChoices('特徴的な文字列')
と呼んで選択肢からその項目を外します。
function _modifyChoices(targetVal){ var TARGET_ITEM = '希望日時'; var CLOSE_TEXT = '※各回ともに締め切りました'; form.getItems().forEach(function(item){ if (item.getTitle() == TARGET_ITEM) { var choices = []; item.asMultipleChoiceItem().getChoices().forEach(function(choice){ var choiceVal = choice.getValue(); if (choiceVal.indexOf(targetVal) != -1) { Logger.log('DELETED: ' + choiceVal); // 選択肢を消す(何もしない) } else { choices.push(choiceVal); // 選択肢を残す } }); // 選択肢が1つもない場合は全て締め切った旨を表示する if (choices.length < 1) { choices.push(CLOSE_TEXT); } //選択肢を再生成する item.asMultipleChoiceItem().setChoiceValues(choices); } }); }
これらを繋ぎこむ部分。フォーム送信時の値を取得し、その選択肢の上限をチェックします。
function _choiceBuilder(itemResponses) { var value = itemResponses[0].getResponse(); //1問目の回答 var r = _getAnswerCount(); if (value.indexOf(ANS_WORD1) != -1 && r.ans1 >= LIMIT_COUNT) { _modifyChoices(ANS_WORD1); } else if (value.indexOf(ANS_WORD2) != -1 && r.ans2 >= LIMIT_COUNT) { _modifyChoices(ANS_WORD2); } } function onFormSubmit(e) { _choiceBuilder(e.response.getItemResponses()); }
この中の function onFormSubmit()
は、onEdit()
のようなシンプルトリガーと違って、明示的にトリガーを指定してやる必要があります。
イベントのソースを「フォームから」、イベントの種類を「フォーム送信時」にします。
トリガー設定時に権限を確認して「フォームの表示と管理」を許可するようにします。
トリガー設定完了。トリガー関係のUIが以前よりもモダンな感じに変わっていました。
フォームを送信すると、
定員に達したイベントから順に選択肢から消えていき…
全てのイベントが定員に達すると選択肢が無くなってしまうので、
代わりに締め切った旨のテキストを置きました。
ただここはそれよりもGoogleフォームの標準の「回答の受け付けを終了する」機能を呼び出したいところです。 そこで function _modifyChoices()
の一部を下記のように変えて、標準の回答締め切り状態にしました。
// 選択肢が1つもない場合は全て締め切った旨を表示する if (choices.length < 1) { //choices.push(CLOSE_TEXT); form.setAcceptingResponses(false); }
まとめ
今回の例は、選択肢が2つの場合に限ったものでろくに抽象化できてないですが、ある程度抽象化しておけば他にも使い回すのが楽になりそうです。
標準機能では対応できないはずのものも、工夫することで実現でき、手間を減らせるのはうれしいですね。
アクトインディでは、PCや自動化・効率化に興味のある情シス アルバイトも募集中です!