We built a survey/quiz using Vue.js and Google Sheets

Hui Shun Chua
4 min readSep 24, 2020

Amidst the COVID-19 lockdown period, my sister and I created a circuit breaker personality quiz to spread some joy and fun. It’s truly in the Singapore spirit — you’ll see Singlish terms scattered throughout the quiz.

Try it out here!

A personality quiz about attitudes and feelings about the Circuit Breaker.

How it works

This application retrieves responses from users and stores the results in Google Sheets.

There are two parts to this application:

  1. Frontend (website) built using Vue.js, to collect data from users and send them to the API created at backend
  2. Backend built using Flask, to receive data from the frontend and send them to Google Sheets using Google Sheets API

The frontend code can be found here and the backend here. For a quick start, I suggest to clone the repositories and adapt it to your use case.

Frontend (website)

The design for this is adapted from a Codepen by Shameem A.R. I started my project with Vue CLI, and integrated the Codepen with the Vue boilerplate. However, the logic behind the the Codepen quiz and my personality quiz is different, so I had to rewrite that part.

The quiz logic

The quiz questions can be edited in the config.js file. Each answer has one or more types of personality it corresponds to. These information are stored in an object format.

The user response types are initialised as an array containing null values, and then passed to the Vue instance as userTypes.

const userResponseTypesSkeleton = Array(quiz.questions.length).fill(null);

In the Vue instance,

data() { 
return {
quiz: quiz,
questionIndex: -1,
userResponses: userResponseSkeleton,
userTypes: userResponseTypesSkeleton,
isActive: false
};
},

Every time an option is selected, the array of userType(s) corresponding to the answer will be added to the userTypes array.

selectOption: function(index) { 
Vue.set(this.userResponses, this.questionIndex, index);
Vue.set(this.userTypes, this.questionIndex, this.quiz.questions[this.questionIndex].responses[index].type);
},

At the end of the quiz, the 2-dimension userTypes array is flattened into 1-dimension to facilitate counting.

The returned result is the user type with the largest number of occurrences. In case of a tie, the result is the user type with a smaller index in the order array.

shorthandResult: function() { // flatten userTypes array 
const collateTypes = Array.prototype.concat.apply([], this.userTypes);
// count number of occurrences for each user type
let result = {};
for (let i = 0; i < collateTypes.length; i++) {
if (!result[collateTypes[i]]) {
result[collateTypes[i]] = 0;
}
++result[collateTypes[i]];
}
// return result with largest number of occurrences. If tie, return result based on specified order.
const order = ["TWGA", "TSGW", "TPCB", "TSSW", "TCLG", "TRC"];
return Object.keys(result).reduce((a, b) => {
if (result[a] > result[b]) {
return a;
}
if (result[a] < result[b]) {
return b;
}
if (result[a] == result[b]) {
return order.indexOf(a) < order.indexOf(b) ? a : b;
}
});
},

This result, along with the user’s answers, is then sent to the backend.

Backend

Results stored in Google Sheets

I built the API endpoints using Flask and created a new Cloud Platform project and automatically enabled the Google Sheets API following the instructions here. For security purposes, I stored my credentials in environment variables and then retrieved them when initialising the creds_json variable. The newline character is escaped when the private key is being stored, so I had to add .replace(‘\\n’, ‘\n’) when retrieving my private key.

The code to set up Flask API endpoints and enable Google Sheets API is as follows. It can also be found here.

from flask import Flask, request, jsonify
from os import getenv
from pprint import pprint
from datetime import datetime
from config import NUM_OF_QUESTIONS, HEADER, RESULTS_SHEET_NAME, SPREADSHEET_NAME, HEADER, NUM_OF_QUESTIONS
from flask_cors import cross_originimport gspread
from oauth2client.service_account import ServiceAccountCredentials
import json

# Set up Flask app
app = Flask(__name__)

# API Routes
@app.route('/', methods=['POST'])
@cross_origin(['http://localhost:8080/', getenv('APP_HOST_ONE', None), getenv('APP_HOST_TWO', None)])
def update_results():
# parsing received data
results = []
params = request.json
for i in range(NUM_OF_QUESTIONS):
key = 'q' + str(i + HEADER)
results.append(params.get(key, None))
results.append(params.get('result', None))

now = datetime.now()
dt_string = now.strftime("%Y%m%d %H:%M:%S")
results.append(dt_string)
add_results(results)
return 'Success'

# Enable Google Sheets API
with app.app_context():
scope = [“https://spreadsheets.google.com/feeds",
“https://www.googleapis.com/auth/spreadsheets",
“https://www.googleapis.com/auth/drive.file",
“https://www.googleapis.com/auth/drive"
]
# Get environment variables
creds_json = {
“type”: getenv(‘type’),
“project_id”: getenv(‘project_id’),
“private_key_id”: getenv(‘private_key_id’),
“private_key”: getenv(‘private_key’).replace(‘\\n’, ‘\n’),
“client_email”: getenv(‘client_email’),
“client_id”: getenv(‘client_id’),
“auth_uri”: getenv(‘auth_uri’),
“token_uri”: getenv(‘token_uri’),
“auth_provider_x509_cert_url”: getenv(‘auth_provider_x509_cert_url’),
“client_x509_cert_url”: getenv(‘client_x509_cert_url’)
}
creds = ServiceAccountCredentials.from_json_keyfile_dict(
keyfile_dict=creds_json, scopes=scope
)
client = gspread.authorize(creds)
spreadsheet = client.open(SPREADSHEET_NAME)

def add_results(insertRow):
sheet = spreadsheet.worksheet(RESULTS_SHEET_NAME)
sheet.insert_row(insertRow, index=2)

You may edit the number of questions, spreadsheet name, results sheet name and number of options in the quiz in the configuration file.

Conclusion

This application is not limited to personality quizzes. It can be adapted for surveys and forms as well, especially if you need something more than what Google Forms can provide.

--

--