D11 - 如何用 Apps Script 寄出客制化的表单并搜集分散在 Google Sheet 中的回应?(ㄧ)复制并客制你的 Google Form

今日目标

很多时候我们会需要搜集些不同的资料。像是 Marketing 在做大规模但针对不同组织的调查问卷。如果只是三份、五份的问卷要做客制化、统整算还好;但如果是一百份、甚至上千份时,总不能一个个复制了吧。此时就会遇到个问题——

  1. 要如何复制客制化 Google 表单?
  2. 要如何集中很多表单中的资料(回应)?

因为篇幅关系,这边会分成三篇来写,第一篇与第二篇回应第一题;第三篇回应第二题。但一样先讲结论,如果你很急着用,可以直接使用这份 Add-On: Form Publisher,功能非常强大。自己写的好处是,如果你一天突然要做些客制化,那就可以持续阅读阅读此篇,那让我们开始吧!


先来个小测验


答案会在今天的文章中!


Q1. 我要如何复制客制化 Google 表单?

复制表单有两种方式。一种是比较简单的「复制范本」,简单来说就是针对一个表单复制,然後再行更改其中的元素。第二种是「从零制作」,这种就比较复杂,因为会是透过 GAS 完整制作表单,会需要比较熟悉 GAS。我们今天会先讲方式一,方式二会在明天讲。

方式ㄧ:复制范本

Step 1 开启 Google Sheet,并串起 GAS

等等,不是要讲 Google Form 吗,怎麽又是先开启 Google Sheet?那是因为我们想要开很多个创客制化表单的话,总要先有个表先设定说,需要设定哪些参数,而这就是 Google Sheet 派上场的地方。方式一我们先简单地示范如何复制并客制表单表单,简单的会用下图左侧绿色的参数,来生成右侧橙色的参数。白话文就是,我期待在创造完後後它会告诉我各表单的连结。

好,那因为我们要用到 Google Sheet ,所以一样用其作为开启的管道。一样借用 D8 的影片。

一样执行时会有「需要验证」出现,借用一下 D2 的影片:Google Apps Script (GAS) 的环境设定、专案结构(Trigger)与四种打包方式

Step 2 设定要作为 Template 的 Google From ID

这边我们就直接取得 ID,那可以在 GAS 的後台用直接设定参数,也可以像这样写在 Google Sheet 上。考量到有些人会用不同的范本来生成表单,这边就直接写在 Google Sheet 上。

完整取得 ID 的方式在 D9 有详细的介绍可参考:如何用 Google Apps Script 自动化对 Google Drive 的操作?(一)列出所有档案 ID 与相关资讯

Step 3 从 GAS 中读取 Google Sheet 的资料

那我们之前从 D4 (如何透过 Google Apps Script 来整合 Google Form / Google Sheet 并自动寄出客制的 Email?)起介绍的抓「栏数」与「列数」,都是直接设定好要抓多少行与列,今天介绍比较弹性地设定的两个手法:

  1. getLastRow() :抓出表单中最下面有数值的「那一列」
  2. getLastColumn():抓出表单中最右侧有数值的「那一栏」。

所以以我们的范围来说,就会是「列」抓到第 10 列,「行」抓到第 4(D) 行。但,如果我们要搭配之前常用的 getRange(start_row, start_col, numRows, numCols) 来取得范围的话,我们会需要将 numRowsnumCols 写成——

let numRows = sheet.getLastRow() - start_row +1;
let numCols = sheet.getLastColumn() - start_col +1;

之所以最後还要有一个 +1 ,原因是因为如果一开始的 start_rowstart_col 本身也要算进去,如果单纯用最後一栏有资料的 index 值去减掉最一开始的 index 值,那跟我们想要的结果就会差了 1 如下图。这也是我们最一开始题目的答案。

所以完整的读档程序码长这样——

function readData(){
  let ss = SpreadsheetApp.getActiveSpreadsheet();
  let sheet = ss.getActiveSheet();
  let start_row = 3;
  let start_col = 1;
  let numRows = sheet.getLastRow() - start_row +1;
  let numCols = sheet.getLastColumn() - start_col +1;
  let values = sheet.getRange(start_row,start_col,numRows,numCols).getValues();
  Logger.log(values)
  return values;
}

跑起来长这样——

读出来後会以 Array in array 的形式操作,我们待会会直接进入操作,想补概念可以回到 D4 ~

Step 4 将读取到的范本用 makeCopy() 复制

好的,那我们现在将 Google Sheet 中的资料读到 GAS 当中後,我们要依据 ID 抓出档案并将其复制。我们先来快速看一下 makeCopy(name, destination) 怎麽用。

function copyForm(){
  let target_id = template_id;
  let form_file = DriveApp.getFileById(target_id);
  let new_form_Id = form_file.makeCopy("D11 Test").getId()
  Logger.log(new_form_Id)
}

跑起来会长这样——

补充的是,在上面的程序码中,我们用的是 makeCopy(name) ,也就是只填一个参数的话,那个参数就会是新档案的名称。如果填入两个,就会是 makeCopy(name, destination),也就是目的地的位置(要是个 folder object,需要用 DriveApp.getFolderById() 等方式来取得。那如果我们用的是没有参数,也就是直接输入 makeCopy() 的话,就会是在原地用预设的名称复制(通常会是「____的副本」或「Copy of ____」)

好,那我们复制完档案後,它现在仍是个一模一样的表单,我们要怎麽样放入客制的部分?这就到了 FormApp 的主场了。

Step 5 将复制後的表单用 FormApp() 的功能改写

好,那将这 makeCopy 和 Step 3 整合起来,就会变成下列这段程序码。

function copyForm(){
  let target_id = template_id;
  let form_file = DriveApp.getFileById(target_id);
  let new_form_Id = form_file.makeCopy("D11 Test").getId()
  let new_form = FormApp.openById(new_form_Id);
  new_form.setDescription('Hello, new form!')
          .setConfirmationMessage('Thanks for responding!')
          .setAcceptingResponses(false);
}

这边主要是对 form Object 进行操作,就简单地运以下三个功能。

  1. 设定叙述(`setDescription()``)
  2. 设定填答完毕的讯息(`setConfirmationMessage()``)
  3. 设定先不接受回应(setAcceptingResponses()

跑起来长这样——

整个 FormApp() 也正是「如何操作」 Google Form 的精髓所在,明天我们会大版面介绍如何完整使用其中的功能们。好,那这样我们接着就要整合 Step 3 的读取资料,和 Step 4 与 Step 5 了。

Step 6 依照 Google Sheet 上的资讯制作 Google 表单

而完整整合起来会长这样。

function copyForm(){
  let full_table_data = readData();
  for (row_data of full_table_data){
    let target_id = row_data[0];
    let form_file = DriveApp.getFileById(target_id);

    let new_form_name = row_data[1];
    let new_form_Id = form_file.makeCopy(new_form_name).getId()

    let new_form_description = row_data[2];
    let new_form = FormApp.openById(new_form_Id);
    new_form.setDescription(new_form_description)
            .setConfirmationMessage('Thanks for responding!')
            .setAcceptingResponses(false);
  }
}

让我们来试跑看看——

好,那我们最後当然会希望能追踪我们所产生的表单,这就到了 Step 7 的写入表单。

Step 7 将创造後的表单 ID 写回 Google Sheet

那一样搭配我们从 D8( 如何用 Google Apps Script 将 Google Calendar 上的事件与更新全部列出到 Google Sheet 上?)开始每天见面的老朋友 writeData() 将 ID 写回 Google Sheet。

function writeData(data){
  let starting_row = 3;
  let starting_col = 4;
  let num_row = data.length;
  let num_col = data[0].length;
  let sheet = SpreadsheetApp.getActiveSheet();
  let range = sheet.getRange(starting_row, 
                             starting_col, num_row, num_col);
  range.setValues(data);
}

并将其与 copyForm() 结合即可。

function copyForm(){
  let full_table_data = readData();
  let new_form_id_arr = []
  for (row_data of full_table_data){
    let target_id = row_data[0];
    let form_file = DriveApp.getFileById(target_id);

    let new_form_name = row_data[1];
    let new_form_Id = form_file.makeCopy(new_form_name).getId()

    let new_form_description = row_data[2];
    let new_form = FormApp.openById(new_form_Id);
    new_form.setDescription(new_form_description)
            .setConfirmationMessage('Thanks for responding!')
            .setAcceptingResponses(false);
            
    new_form_id_arr.push([new_form_Id]);
  }
  writeData(new_form_id_arr)
}

最後结果会长这样——

今天主要讲了下列功能:

  1. getLastRow()getLastColumn()
  2. makeCopy() 的细节使用差异
  3. FormApp 来基础地调整表单的内容

好,那看完今天的教学,至少知道如何将 Google 复制并调整基本的元素了(撒花)


好,那基本上我们就搞定了。但如果今天我们是想要高度客制化的表单怎麽办?像是不同人要设定不同的照片,甚至是有些人不要某些题目怎麽办?那就会到我们的方法二。也就是明天的主题。

一样提醒,用FormApp.create()来创造表单是有 Quota 限制——每天不超过 250 份。如果还有问题,透过留言之外,也可以到 Facebook Group,想开很久这次铁人赛才真的开起来哈哈哈,欢迎来当 Founding Member。如果不想错过可以订阅按赞小铃铛(?),也欢迎留言跟我说你还想知道什麽做法/主题。我们明天见。


<<:  基础建设: 原始码版本控制

>>:  OpenStack Neutron 介绍 2

Day 09 - Design System x 实作 — Typography

虽然昨天已经介绍了如何在你的网页中实作 Color System,但严格上来说今天才算是这系列第一...

centos7没有安装ifconfig命令的解决方法

ifconfig命令是设置或显示网络接口的程序,可以显示出我们机器的网卡信息, 可是有些时候最小化安...

预编译 - 变数和function的被建立、初始化/预编译、执行的全纪录

你以为JS拿来就乖乖照着我们打的一行一行跑吗?太天真了,我说我~~ 变数怎麽存,存哪里,在哪里叫得到...

[Day 3] Course 1_Foundation - Data Analytics 介绍

《30天带你上完 Google Data Analytics Certificate 课程》系列将...

Day19 Gin with Swagger

Background 在前後端分离的专案维护一份完整且及时更新的api文件会极大的提高我们的工作效率...