ググっても方法が出てこなかったので。
最適解感は全くないですが、done is better than perfectの精神で言い訳しながらやっていきます。
やりたいこと
Google Formsでは、設定によって回答内容を再編集できるようになります。
また、Google Formsにはイベントトリガーを仕込むことが可能で、GAS(Google Apps Script)で記述できる範囲であれば、フォーム内容が送信されるたびに任意の処理を実行できます。
この二つを組み合わせて、フォームの初回送信時には反応して、回答の再編集には反応しないようなイベントトリガーがほしいわけです。
今回は、フォームの初回送信時のみ、特定のメールアドレス(上司)に対してフォーム回答編集用のURLを通知するようにしてみます。
Google Forms側には(おそらく)適切なトリガーを仕込めない
Google Formsの送信イベントを捕まえるには、フォームの設問自体を管理する編集ページからスクリプトエディタを開き、以下のようなコードを書きます。
function sendEditUrl(editUrl) {
var recipient = 'hoge@example.com';
var subject = '【サンプルアンケート】回答がありました';
var body = 'サンプルアンケートの回答がありました。\n\n回答編集用URL: ' + editUrl;
MailApp.sendEmail(recipient, subject, body, { noReply: true });
}
function onFormSubmit(e){
var editUrl = e.response.getEditResponseUrl();
sendEditUrl(editUrl);
}
あとはonFormSubmit
にトリガーを別途設定すれば、フォーム送信時にメールを送信できるようになります。
しかし、この方法には問題があり、回答が初めて送信されたのか、既存の回答が更新されたのかを区別することができません。
このままだと、
- 回答者がフォームに回答して送信を実行する
- 上司宛に回答編集用URLの通知メールが届く
- 上司がフォームの回答内容を編集してコメントする
- もう一回上司宛に回答編集用URLの通知メールが届いてしまう(いらないよ!)
となってしまうわけです。
運用でカバーしてもいいですが、できれば初回送信だけをトリガーにしたいですね。
e
からINSERT/UPDATEのどちらが実行されているか判断できればいいのですが、Event Objectsのガイドを見てもそういうプロパティはなさそうです。
スプレッドシートでも適切なイベントを拾えない
Google Formsの回答内容はスプレッドシートに自動で移し書きできます。
このことを利用して、Google Forms eventsではなくGoogle Sheets eventsに応じた処理を書こうとしたのですが、
「フォーム回答を送信⇛スプレッドシートへ回答内容を保存」の間に発生するGoogle Sheets events
はForm Submitのみで、必要な情報を取得できませんでした。
ChangeイベントのプロパティであるchangeType
などは、もし使えれば回答に対応する行が挿入されたか更新されたかを判断できそうなのですが、トリガーを作成しても拾うことができません。
(※ユーザーが直接スプレッドシートに行挿入したときなどに発生するイベントのようです)
スプレッドシート側で無理やり条件分岐する
ということで、Event Objects
のみでフォーム回答が初回送信なのか否かは判断できません。少なくとも私は諦めました。
仕方がないので、苦肉の策でスプレッドシートに小細工します。
先ほどのGoogle Forms側のコードはもう必要ないので削除しておきます。
回答を保存するスプレッドシートを指定して作成し、
適当にフォームに回答すると、スプレッドシートには回答が保存されていると思います。
タイムスタンプ列の左にフラグ列を追加します。
先頭行のフラグは適当に埋めておきます。
Google Sheets events
のForm Submit
イベントは、スプレッドシートへの回答保存後に発生するようです。
このことから、Form Submit
イベントが発生したとき、最新行のA列が空白であれば新規回答であると見なすようにしてみます。
また、回答が編集されたときには新規回答と見なしたくないので、フラグ列は初回のForm Submit
イベントで埋めておきます。
タイムスタンプが左詰めでA列に入ってしまうのでは?という不安はありますが、多少フォーマットがずれていても回答は正しい列に格納されてくれます。
スプレッドシートのスクリプトエディタを開いたら、
以下のようなコードを書き、スプレッドシート側でイベントを捕まえます。
素のGASではアロー関数などは使えません。
const
が有効なのかはどうも分からず、適当なことも言えないのでvar
にしておきます。
function getLastInsertFlagCell() {
var spreadSheetId = 'スプレッドシートのID';
var sheetName = '回答を保存しているシート名';
var spreadSheet = SpreadsheetApp.openById(spreadSheetId);
var range = spreadSheet.getSheetByName(sheetName).getDataRange();
var lastRow = range.getLastRow();
var insertFlagCell = range.getCell(lastRow, 1);
return insertFlagCell;
}
function getEditUrl() {
var formId = 'Google FormsのID';
var formResponse = FormApp.openById(formId).getResponses();
// スプレッドシート側から最新行に対応する回答のIDを直接取得できないので、最新の回答を取得している
var editUrl = formResponse[Number(formResponse.length - 1)].getEditResponseUrl();
return editUrl;
}
function sendEditUrl(editUrl) {
var recipient = 'hoge@example.com';
var subject = '【サンプルアンケート】回答がありました';
var body = 'サンプルアンケートの回答がありました。\n\n回答編集用URL: ' + editUrl;
MailApp.sendEmail(recipient, subject, body, { noReply: true });
}
function onFormSubmit(e){
var lastInsertFlagCell = getLastInsertFlagCell();
// 最新行の行挿入フラグが埋まっていれば初回送信ではない
if (lastInsertFlagCell.getValue()){
return 0;
}
lastInsertFlagCell.setValue(true);
sendEditUrl(getEditUrl());
}
回答のIDをイベントから直接取得できれば良かったのですが、方法が見つかりませんでした。
代わりに最新の回答を取得するようにしていますが、複数回答がほぼ同時に送信されたとき、確実に正しい回答を取得できるのかは怪しいです。
ひとまずコードを書き終わったら、フォーム送信のトリガーを設定します。
フォーム送信時にonFormSubmit
を実行するようにしておきます。
これで、フォームの初回送信時にのみメールが送信されるようになりました。
冒頭でも述べましたが、目的は達成しているものの、なかなか美しくない解決法です。
初回送信を判断したいだけなら、スクリプトはスプレッドシート側ではなくフォーム側に書いた方が良さそうですし、
スプレッドシートを使うにしても、もう少し簡潔なやり方があるんじゃないかと思っています。
より良い方法をご存じの方がいたら、ご意見お待ちしております。
参考
011-フォーム送信時にスプレッドシートへ編集用URLを挿入する
- フォーム送信時のスプレッドシート更新部分を参考にしました。
余談: ワークフロー用のアドオンについて
お気づきかもしれませんが、今回本当にやりたかったのは簡易的なワークフローです。
わざわざ上司がフォームの回答内容を編集しなくても、単に承認コメントを送信する仕組みがあればそれで済む話だったわけです。
Google Formsは第三者がアドオンを開発できるようになっており、ワークフロー用のアドオンもいくつか用意されています。
有名どころの採用も考えたのですが、
- Form Approvals : 機能は十分だが、無料枠では月20件までしか承認依頼を通知できず絶妙に足りない。本腰入れた業務ではないので有料枠への移行は見送り
- Form Publisher : 2019年11月時点では、新規ユーザーはアドオンを追加できない模様
ということでいずれも使いませんでした。
ワークフローでGoogle Formsを使う場合、金の弾丸を放てるか無料枠の月20件で十分足りるなら、Form Approvalsを導入してみてもいいと思います。