masuTomo’s blog

競技プログラミングの勉強メモです.主にPythonを使用します.数学の記事も書くかもしれません.

【誰得】Google Forms からGoogle Documentを生成

はじめに

暇つぶしにGoogleAppsScriptでいろいろ調べて遊んでいます.そこで作ってみたスクリプトを公開したいと思います.

作ったもの

  • Google Formsで作成したアンケート等をGoogle Document形式に変換します.

    機能詳細

  • GoogleFormsのタイトル,質問,選択肢,セクションを取得して,それらしいGoogleDocumentを生成する.

使いどころ

  • GoogleFormsでアンケートを作成したが,社内が未だに書面による決済のため,作成したGoogleFormsを印刷したい.(Formsでも印刷できるが,編集ができない)
  • GoogleFormsでアンケートを実施したいが,スマホ等を所持していない可能性も考慮して,紙面でのアンケートも準備したい.
  • GoogleFormsで印刷したら選択肢のプルダウンが多すぎて見るに堪えないので,プルダウンの選択肢は削除して印刷したい(どれだけニッチな場面なのか)

注意点

作成したFormsに編集権限がないと使えません.

使い方

  • 自分のGoogleDriveのフォルダ内でGoogle Apps Scriptを新規作成して,以下のスクリプトをコピペ(Google Apps Scriptの配布の仕方がわかりませんでした).

  • スクリプト内の保存先フォルダーのIDを書き換える

生成したDocumentを保存したいフォルダをマイドライブ内のどこかに作成し,そこのフォルダに移動する.アドレスバーの以下の画像のようにアドレスの末尾がフォルダーIDなので,それをコピーして貼り付けてください.f:id:masuTomo:20210226232144p:plain

  • 読み込むフォームのIDを書き換える

読み込みたいフォームを開いて,フォルダーIDと同じような文字列がアドレスのところに表示されているので,それをコピーして貼り付け.

  • 関数がmainになっていることを確認して実行 f:id:masuTomo:20210226230618p:plain

スクリプト

//保存先フォルダーのID
const saveFolderId = 'ここに保存先フォルダーのID';
//読み込むフォームのID
const formId = 'ここに読み込みたいフォームのID';

//各質問の先頭に付与するためのNumber
var qNo = 1;

//ファイルの保存
function saveDoc(doc,titleText){
  var file = DriveApp.getFileById(doc.getId());
  // 出力フォルダ
  var OutputFolder = DriveApp.getFolderById(saveFolderId);  
  file.moveTo(OutputFolder);
}

//Docの最上部に表示する文字列を設定
//見出し,センタリング
function setDocTitle(body, str){
  var par = body.appendParagraph(str);
  par.setHeading(DocumentApp.ParagraphHeading.HEADING1);
  par.setAlignment(DocumentApp.HorizontalAlignment.CENTER);
  return body;
}

//追加されたセクションの処理
function setSectionTitle(body,str){
    var par = body.appendParagraph(str);
  par.setHeading(DocumentApp.ParagraphHeading.HEADING2);
  par.setAlignment(DocumentApp.HorizontalAlignment.CENTER);
  return body;
}

function setContents(body, item){
  const indent = '    ';
  var type = item.getType();
  switch(type){

    //チェックボックスの処理
    case FormApp.ItemType.CHECKBOX:
      var res = String(qNo) + '.' + item.getTitle() +'\n\n';
      qNo++;
      console.log('CHCECKBOX開始-' + item.getTitle());
      var choices = item.asCheckboxItem().getChoices();
      for( var i = 0; i < choices.length; i++)  {
        res += indent + '□'+choices[i].getValue() + '\n';
      }
      res += '\n';
      body.appendParagraph(res);
      console.log('CHCECKBOX終了');
    break;

    //複数選択方式のグリッドの処理
    case FormApp.ItemType.CHECKBOX_GRID:
      console.log('CHCECKBOX_GRID開始-'+item.getTitle());
      var res = String(qNo) + '.' + item.getTitle() +'\n\n';
      qNo++;
      body.appendParagraph(res)
      var cols = item.asCheckboxGridItem().getColumns();
      var rows = item.asCheckboxGridItem().getRows();
      var cells = Array(rows.length+1);
      cols = [''].concat(cols);
      cells[0] = cols;
      for( var i = 1; i < rows.length+1; i++){
        cells[i] = Array(rows[i-1]).concat(Array(cols.length-1).fill('□'));
      }
      body.appendTable(cells);
      console.log('CHCECKBOX_GRID終了');
    break;

    //日付型の処理
    case FormApp.ItemType.DATE:
      var res = String(qNo) + '.' + item.getTitle() + '\n';
      qNo++;
      console.log('DATE開始-' + item.getTitle());
      console.log('DATE終了');
    break;

    case FormApp.ItemType.DATETIME:
      var res = String(qNo) + '.' + item.getTitle() + '\n';
      qNo++;
      console.log('DATETIME開始-' + item.getTitle());
      console.log('DATETIME終了');
    break;

    case FormApp.ItemType.DURATION:
      console.log('DURATION開始-' + item.getTitle());
      console.log('DURATION終了');
    break;

    //単一選択形式のグリッドの処理
    case FormApp.ItemType.GRID:
      console.log('GRID開始-'+item.getTitle());
      var res = String(qNo) + '.' + item.getTitle() +'\n\n';
      qNo++;
      body.appendParagraph(res)
      var cols = item.asGridItem().getColumns();
      var rows = item.asGridItem().getRows();
      var cells = Array(rows.length+1);
      cols = [''].concat(cols);
      cells[0] = cols;
      for( var i = 1; i < rows.length+1; i++){
        cells[i] = Array(rows[i-1]).concat(Array(cols.length-1).fill('〇'));
      }
      body.appendTable(cells);
      console.log('GRID終了');

    break;
    case FormApp.ItemType.IMAGE:
      console.log('IMAGE開始-' + item.getTitle());
      console.log('IMAGE終了');
    break;

    //プルダウンの処理
    case FormApp.ItemType.LIST:
      var res = String(qNo) + '.' + item.getTitle() + '\n\n';
      qNo++;
      console.log('LIST開始-' + res);
      var choices = item.asListItem().getChoices();
      for( var i = 0; i < choices.length; i++)  {
        res += indent + '・'+choices[i].getValue() + '\n';
      }
      res += '\n';
      body.appendParagraph(res);
      console.log('LIST終了');

    break;

    //ラジオボタンの処理
    case FormApp.ItemType.MULTIPLE_CHOICE:
      var res = String(qNo) + '.' + item.getTitle() + '\n\n';
      qNo++;
      console.log('MULTIPLE_CHOICE開始-' + res);
      var choices = item.asMultipleChoiceItem().getChoices();
      for( var i = 0; i < choices.length; i++)  {
        res += indent + '・'+choices[i].getValue() + '\n';
      }
      res += '\n';
      body.appendParagraph(res);
      console.log('MULTIPLE_CHOICE終了');
    break;

    //段落型の処理
    case FormApp.ItemType.PARAGRAPH_TEXT:
      var res = String(qNo) + '.' + item.getTitle() + '\n\n';
      qNo++;
      console.log('PARAGRAPH_TEXT開始-' + item.getTitle());
      res += '(自由記述)';
      res += '\n';
      body.appendParagraph(res);
      console.log('PARAGRAPH_TEXT終了');
    break;

    //均等目盛型の処理
    case FormApp.ItemType.SCALE:
      var res = String(qNo) + '.' + item.getTitle() + '\n\n';
      qNo++;
      console.log('SCALE開始-' + res);
      var llabel = item.asScaleItem().getLeftLabel();
      var rlabel = item.asScaleItem().getRightLabel();
      var lbound = item.asScaleItem().getLowerBound();
      var rbound = item.asScaleItem().getUpperBound();
      var line = '';
      for(var i = 0; i < rbound-lbound; i++){
        line += '----' + String(lbound + i + 1);
      }
      res += indent + llabel + ' | ' + String(lbound) + line + ' | ' + rlabel + '\n';
      body.appendParagraph(res);
      console.log('SCALE終了');
    break;

    case FormApp.ItemType.SECTION_HEADER:
      console.log('SECTION_HEADER開始-' + item.getTitle());
      console.log('SECTION_HEADER終了');
    break;

    //記述式の処理
    case FormApp.ItemType.TEXT:
      var res = String(qNo) + '.' + item.getTitle() + '\n\n';
      qNo++;
      console.log('TEXT開始-' + item.getTitle());
      res += '(自由記述)';
      res += '\n';
      body.appendParagraph(res);
      console.log('TEXT終了');
    break;

    //時刻の処理
    case FormApp.ItemType.TIME:
      console.log('TIME開始-' + item.getTitle());
      console.log('TIME終了');    
    break;

    case FormApp.ItemType.VIDEO:
      console.log('VIDEO開始-' + item.getTitle());
      console.log('VIDEO終了');
    break;

    //追加されたセクションの処理
    case FormApp.ItemType.PAGE_BREAK:
      body = setSectionTitle(body,item.getTitle() + '\n');
      console.log('PAGE_BREAK開始-' + item.getTitle());
      console.log('PAGE_BREAK終了');
    break;

  }
}

function main() {
  //読み込み先のFormsを指定
  var f = FormApp.openById(formId);
  //フォームの上部にある説明文
  var des = f.getDescription();
  var formTitle = f.getTitle();
  //フォームと同じタイトルでGoogle Docsを作成する
  var date = new Date();
  const doc = DocumentApp.create(formTitle + '_'+Utilities.formatDate(new Date(), 'JST', 'yyyyMMdd'));
  var body = doc.getBody();
  body = setDocTitle(body,formTitle + '\n');
  body.appendParagraph(des + '\n\n\n');

  //質問項目を取得
  var itemList = f.getItems();
  for(let i = 0; i < itemList.length; i++) {
    setContents(body,itemList[i]);    
  }
  saveDoc(doc,formTitle);
}

注意点

  • フォーム内の画像は取り込めません
  • 回答によるセクション移動には対応していません
  • テスト機能は反映しません
  • 必須回答は反映しません(これはそのうち対応するかも)
  • 入力チェックも反映しません
  • バグがあると思います.お気づきのことがあればお知らせいただけるとありがたいです.
  • その他,私が思い至らず考慮漏れしれいる事項があると思います.

参考

ここに全部書いてある. developers.google.com