用程式自動產生 Google Forms 調查問卷

By Shaform, Sat 16 September 2017, in category Notes

Google Forms

前言

最近因為一些研究上的需要,需要進行問卷調查。雖然也可以用自己架的伺服器和網頁界面,可是難免擔心資安的疑慮。 於是就想到可以使用 Google Forms,來進行問卷調查與回收。 為了避免未來有需要時忘記作法,便記下筆記當作未來的參考。

這次將使用 KaggleDogs vs. Cats 資料集作為示範,每題會給兩張圖片,要求使用者選出貓貓照片。

資料準備

首先下載 train.zip 並且解壓縮,緊接著我們撰寫一個程式來產生必要的檔案:

# prepare.py
def prepare(input_dir, output_dir, web_root, num_outputs, names, seed):
    random.seed(seed)
    image_dir = os.path.join(output_dir, 'images')
    os.makedirs(image_dir, exist_ok=True)

    # load choices for each question
    choices = []
    for name in names:
        named_images = []
        # write hashes of this name to a file
        # so we can analyse results later
        id_path = os.path.join(output_dir, name + '.txt')
        with open(id_path, 'w') as id_file:
            for i in range(1, num_outputs + 1):
                fname = os.path.join(input_dir, '{}.{}.jpg'.format(name, i))
                h = md5(fname)
                named_images.append(h)
                id_file.write(h + '\n')
                # put images to a separate directory
                outpath = os.path.join(image_dir, '{}.jpg'.format(h))
                shutil.copyfile(fname, outpath)

        choices.append(named_images)

    # output url for each image
    url_path = os.path.join(output_dir, 'urls.tsv')
    with open(url_path, 'w') as outfile:
        outfile.write('h\turl\n')
        for named_images in choices:
            for h in named_images:
                outfile.write('{}\t{}/images/{}.jpg\n'.format(h, web_root, h))

    # output survey file
    survey_path = os.path.join(output_dir, 'survey.tsv')
    with open(survey_path, 'w') as outfile:
        header = '\t'.join([str(n) for n in range(1, len(names) + 1)])
        outfile.write('num\t' + header + '\n')
        for n in range(num_outputs):
            options = [named_images[n] for named_images in choices]
            random.shuffle(options)
            outfile.write('{}\t{}\n'.format(n + 1, '\t'.join(options)))

這裡包含三個部份,第一個部份會把貓和狗指定題數 num_outputs 的圖片抓出,計算 hash,並且紀錄每一題要用哪個 hash。最後把 hash 寫進獨立的檔案,好讓未來可以知道每個 hash 對應到貓還是狗。

第二個部份則是根據指定的 web_root 來產生每個 hash 對應的網址,在產生問卷前要先將圖片上傳到這個位置,才能讓 script 抓取到對應的圖片。不過抓取完後,圖片就會存在 Google 裡,所以問卷本身不再需要原始的網址。

第三個部份則是產生實際的問卷檔案,別忘了隨機打亂選項的順序,好讓使用者看不出哪個選項是來自哪裡。

寫好後執行

python prepare.py train output --web-root http://WEB_ROOT

應該會產生下述檔案:

output/
├── cat.txt
├── dog.txt
├── images
│   ├── 00f1acf458ae561796d9abf10e384f31.jpg
│   ├── ......
├── survey.tsv
└── urls.tsv

首先先將 images 的所有檔案上傳到自己指定的 web_root 網頁空間。 然後產生一個 Google Sheets,將 survey.tsvurls.tsv 分別匯入成為 surveyurls 兩個 tabs。範例可參考:〈Dogs vs. Cats Survey〉

撰寫問卷產生的程式

接下來進入該 Google Sheets,點擊 Tools -> Script editor...,編輯 Code.gs

新增一個函式抓取問卷資料:

function getSurveyData(sheetName) {
  var arrayOfArrays = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName || 'survey').getDataRange().getValues();
  var headers = arrayOfArrays.shift();
  return arrayOfArrays;
}

再新增一個函式抓取網址資料:

function getUrlData(sheetName) {
  var arrayOfArrays = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName || 'urls').getDataRange().getValues();
  var headers = arrayOfArrays.shift();
  var mapOfUrls = {};
  for (var i=0; i<arrayOfArrays.length; i+=1) {
    var h = arrayOfArrays[i][0];
    var url = arrayOfArrays[i][1];
    mapOfUrls[h] = url;
  }
  return mapOfUrls;
}

最後就是問卷產生了,為了避免產生太多 requests,造成連線逾時,也加了等待和重試的機制。

function makeOurForm() {
  var title = 'Dogs vs. Cats Survey';
  var desc = 'Please select the picture that looks like a cat.';
  var mapOfUrls = getUrlData();
  var surveyData = getSurveyData();

  var form = FormApp.create(title)
  form.setDescription(desc);
  for (var i=0; i<surveyData.length; i++) {
    Utilities.sleep(10);
    var item = form.addMultipleChoiceItem();
    item.setTitle('Q' + surveyData[i][0] + ': Which one looks like a cat?')
    .setChoices([
      item.createChoice('1'),
      item.createChoice('2')
    ])
    .setRequired(true);

    for (var j=1; j<surveyData[i].length; j++) {
      var name = '(' + j + ')';
      var h = surveyData[i][j];
      var img = UrlFetchApp.fetch(mapOfUrls[h]);
      Utilities.sleep(100);
      var imgItem = form.addImageItem();
      try {
        imgItem.setTitle(name).setImage(img);
      } catch (e) {
        Utilities.sleep(500);
        imgItem.setTitle(name).setImage(img);
      }
    }
  }
}

點擊 Run -> makeOurForm 熱騰騰的問卷就產生好了,可參考下圖。

Dogs vs. Cats Survey

程式碼

照慣例,程式碼放在下述地方供參考: