gas
googleform

GoogleFormで申込用紙を記入したらちゃんとした申込書メール送付(できればPDF)

やりたいこと

とある施設の申込書を手書きで記入するのがとてもツライので

  • 最低限の入力チェックをしつつ入力し
  • 自動的に施設受付担当者にメールし
  • 施設受付家担当者がA4の1枚に正しく美しく出力できる
  • 施設申込書をチーム・組織内で使いまわしたいのである程度フォームはプレインプット状態で共有

ことを目指す。簡単にできたがハマったところを中心に記録しておく。

全部で3時間ぐらいでできた。いやーGASよくできておるのう。

テンプレートをスプレッドシートで作る

原稿データはWordだったのでGoogleドキュメントにインポートした。これは簡単だったがGoogleドキュメントにはAPIが見当たらない?

後述するが、結局フォームのイベントをトリガしてGASを実行して操作するのだが、GASにはちゃんとAPIがあるので、本当はドキュメントでいいのかもしれない。シートのほうが楽そうだが。

入力を検証するフォームを作成

Googleフォームを採用。普通に作ればよい。現行(2017年末)の画面では、「必須ボタン」横の「…」を押すことで回答の検証、正規表現でのチェックなどができる。 どうせプレインプットなので電話番号や郵便番号は [0-9-]+ ぐらいにしておく。

年月日時分を入れさせたいが、これは別になってる。

フォーム送信にフックしたGASの作成

右上の…ボタンから「スクリプトエディタ」を選択して起動。

コードを書き換えたらトリガーを設定しなおす

保存アイコンの横に時計アイコンがあってそこに「フォーム送信時」を設定すること。そしてコードを書き換えるたびにこれを削除 > 保存 > また設定をやっておいたほうがよさげ。

このwebエディタ、いやIDEと呼ぶべきか?はおもしろくて、コードを追加していき、デバッグ実行や上記トリガー設定時に、追加されたそのコードが実行するのに必要な認可許可承諾画面が表示される。これによって常に最低限の認可しか与えなくて済むという利点はあるが、コードを追加しただけでは認可が与えられておらず、実行に失敗することがままある。

なぜか認可設定が飛んじゃったということが何度か発生したので、削除 > 保存 > また設定をオススメする。

例外をメールせよ

IDEからデバッグ実行する際に、引数に適当な値を渡してテストできるし、エラーログも Logger.log(string) を用いればそれなりに見ることができる。しかし、ステップ実行が働かないことがままある。(特に 〜App 系を用いたとき。)

そして本番運用になったとき、引数にフォームデータが渡ってくるのだがそこで例外でコケたときなどイマイチわからない。特に上記トリガ設定しなおしせずに認可系のエラーになったらさっぱりわからないことが多い。

なので、メイン関数で例外を拾ってメールすることをオススメする。

code.gs
function myFunction(form)
{
  try
  {
     // ... logic 1
     // ... logic 2
     // ... logic 3
  }
  catch(e)
  {
    Logger.log(e);
    GmailApp.sendEmail('ore@ore.mail', "GAS MyFunctionエラー" , Logger.getLog());     
    throw e;
  }
}

フォームの入力内容を読む

設定した関数の引数(ここではformとする)に入ってくるので var itemResponses = form.response.getItemResponses(); で取り出せる。とりあえず values['設問'] で取り出せるようにしておく。

code.gs
    var itemResponses = form.response.getItemResponses();
    var message = '';    
    var values = {};    
    for (var i = 0; i < itemResponses.length; i++) { 
      var itemResponse = itemResponses[i]; 
      var question = itemResponse.getItem().getTitle(); 
      var answer = itemResponse.getResponse(); 
      values[question] = answer;
    } 

テンプレートをコピーする

テンプレートのシートや、コピー先のフォルダ名などはURLにIDが埋まっているのでそれを取り出して DriveApp.getFileById DriveApp.getFolderById で指定する。

code.gs
  var template = DriveApp.getFileById("templateFileId");
  var destfolder = DriveApp.getFolderById("copyTargetFolderId");
  var newfile = template.makeCopy("fileName", destfolder);

テンプレートを書き換えてそのURLを得る

とりあえずURLで共有できるように設定し、コピーしたテンプレートのセルをフォームに入力された値で書き換えて書類を作成していく。

code.gs
  newfile.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.EDIT);
  var spreadsheet = SpreadsheetApp.open(newfile);
  var sheet = spreadsheet.getSheets()[0];
  var ck = {} // cell-key
  ck['A1']  = values['氏名'];
  // ... セル番号と値の組み合わせを延々書く
  Object.keys(ck).forEach(function(key) {
    sheet.getRange(key).setValue(ck[key]);
  });

  return newfile.getUrl();

PDFへのエクスポート(失敗)

簡単にできるが、縦1枚に収まるように印刷というのがどうしてもできなかったのでいったんパス。

URLつきでメール

code.gs
  GmailApp.sendEmail('tantousha@shisetsu.jp', "title", "contentsWithUrl");

A4に印刷(PDFファイル作成)

前述したとおり、本当はPDFを添付して送りたかったがあきらめ。スプレッドシートURLを送るので、担当者さんが開いて印刷 > スケール > 高さに合わせる でOK

プレインプットなURLを取得

Googleフォームに戻って右上から…を選んで「事前入力したURLを取得」でOK。URLを見るとどうやらentry.設問ID=値 のペアを指定しているだけみたいなので、簡単に置き換えることはできそう。