Pages

Tuesday, 30 August 2022

Bulk export Qualtrics survey results 2.0

The following tool is designed to connect to your Qualtrics account and export the results data for multiple surveys that you specify, in one go. The results will be appended to the Google Sheet using the survey-name to identify each sheet of data. The data itself is exported as CSV.

This post is an updated version of the previous tool that has become redundant due to changes in the Qualtrics API.

Screenshot of instructional sheet requesting Qualtrics information
Screenshot of instructional sheet requesting Qualtrics information

Download

Bulk export Qualtrics survey results 2.0 download (please use 'File' > 'Make a copy' for your own version).

 

/*
Get values from the Spreadsheet to pass to other Functions.
*/
function getSpreadsheetData() {
logEvent('Starting script.');
// update status cell
statusUpdate('Process is running', '#ff9900');
// get current spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// get Config sheet
var configSheet = ss.getSheetByName('Config');
// get Data Center ID
var dataCenterId = configSheet.getRange(3, 2).getValue();
Logger.log('Data Center ID is: ' + dataCenterId);
// get Sleep time and convert to seconds
var sleepTime = configSheet.getRange(5, 2).getValue();
var sleepTimeSecs = sleepTime * 1000;
Logger.log('Sleep time in secs is: ' + sleepTimeSecs);
// get Welcome sheet
var welcomeSheet = ss.getSheetByName('Welcome');
// 1) Qualtrics API Token
var apiToken = welcomeSheet.getRange(8, 2).getValue().trim();
Logger.log('apiToken is: ' + apiToken);
// 2) labels instead of recode value for answer choice Q's
var labels = welcomeSheet.getRange(13, 2).getValue().trim();
if (labels == 'Yes') {
labels = true;
}
else if (labels == 'No') {
labels = false;
}
else {
labels = '';
}
Logger.log('labels is: ' + labels);
// 3) 'SurveyIDs'
var surveyIds = welcomeSheet.getRange(18, 2).getValue().trim();
// run Function to check no blank values
var blanks = popupBox(apiToken, labels, surveyIds);
if (blanks == false) {
// get array from comma-separated 'SurveyIDs'
var surveyIdsArray = surveyIds.split(', ');
Logger.log(surveyIdsArray);
// create name:value pairs array to pass data to next Function
var allData = {ss:ss, apiToken:apiToken, labels:labels, surveyIdsArray:surveyIdsArray,
dataCenterId:dataCenterId, sleepTimeSecs:sleepTimeSecs};
// run Function to loop through each Survey
var loopSuccess = loopSurveys(allData);
// check process ran correctly to provide relevant status updates
if (loopSuccess) {
// create Toast popup to inform user
ss.toast("Process has now finished.", "Complete");
// update status cell
statusUpdate('Successfully completed', '#00ff00');
logEvent('Script completed successfully.');
}
else {
// create Toast popup to inform user
ss.toast("Problem with survey - check your details otherwise contact IT Support.", "Error");
// process did not complete successfully
logEvent('Script terminated with errors.');
// update status cell
statusUpdate('Problem with survey - check your details otherwise contact IT Support.', '#ff0000');
}
}
else {
// there are blank values in the Sheet
logEvent('Terminated due to blank values in Sheet.');
// update status cell
statusUpdate('Missing information - check boxes', '#ff0000');
}
}
/*
Get Survey Name.
*/
function getSurveyName(apiToken, surveyId, dataCenterId) {
// Qualtrics API Url
var baseUrl = 'https://' + dataCenterId + '.qualtrics.com/API/v3/surveys/' + surveyId;
// authentication headers
var headers = {
"x-api-token": apiToken
};
// compile the above options
var options = {
'method' : 'get',
'headers' : headers
};
// get response from API
try {
var request = UrlFetchApp.fetch(baseUrl, options);
// extract API response data
var resultText = request.getContentText();
var responseCode = request.getResponseCode();
logEvent("Response code for 'getSurveyName' is: " + responseCode);
var resultObj = JSON.parse(resultText);
// extract Survey Name from data
var surveyName = resultObj['result']['name'];
Logger.log('surveyName is: ' + surveyName);
// return Survey Name to Parent Function
return surveyName;
}
catch(e) {
// return Flag as false if problem so script terminates cleanly
logEvent("Problem with 'getSurveyName' Fetch: " + e);
var surveyName = false;
return surveyName;
}
}
/*
Get data for Survey.
*/
function getSurveyData(ss, attempt, apiToken, surveyId, labels, dataCenterId, sleepTimeSecs) {
// file format for output data
var fileFormat = 'csv';
// Qualtrics API Url
var baseUrl = 'https://' + dataCenterId + '.qualtrics.com/API/v3/surveys/' + surveyId + '/export-responses/';
Logger.log('baseUrl is: ' + baseUrl);
// authentication headers
var headers = {
"content-type": "application/json",
"x-api-token": apiToken,
};
// compile JSON
var requestPayload = JSON.stringify({ format: fileFormat, useLabels: labels });
// compile the above options
var options = {
'method': 'post',
'payload': requestPayload,
'headers': headers
};
// get response from API
try {
var request = UrlFetchApp.fetch(baseUrl, options);
// Add 10sec sleep here to allow time for Qualtrics to prepare export file
Utilities.sleep(sleepTimeSecs);
// extract API response data
var resultText = request.getContentText();
var responseCode = request.getResponseCode();
logEvent("Response code for 'getSurveyData' is: " + responseCode);
var resultObj = JSON.parse(resultText);
// run Function to extract survey data
var surveyResultsData = extractSurveyData(headers, baseUrl, resultObj);
}
catch (e) {
// output error message into Log sheet
logEvent("Problem with 'getSurveyData' Fetch: " + e);
// extract message part of error
var error = e.message;
// look for text string match
var errorMessage = error.match(/Export file not found/gm);
// if error message matches and no re-attempts have been made
if ((errorMessage) && (attempt == false)) {
Logger.log('errorMessage is True and attempt is False');
// create Toast popup to inform user
ss.toast("Attempt 2", "Export file not found, retrying in 60 seconds.");
logEvent("Re-running 'getSurveyData' attempt 2 in 60 seconds");
// pause for 60 seconds to allow Qualtrics time to prep export file
Utilities.sleep(60000);
// set attempt value so only tries re-running once
var attempt = true;
// re-run this Function
var surveyResultsData = getSurveyData(ss, attempt, apiToken,
surveyId, labels, dataCenterId, sleepTimeSecs);
}
else {
Logger.log('errorMessage is False, attempt is true');
// reset attempt value so next Survey continues as normal
var attempt = false;
// return Flag as false if problem so script terminates cleanly
var surveyResultsData = false;
return surveyResultsData;
}
}
// return Survey results data to Parent Function after successful 'try'
return surveyResultsData;
}
/*
Extract results data for Survey.
*/
function extractSurveyData(headers, baseUrl, resultObj) {
// get 'results' from data
var dataResults = resultObj['result'];
// get 'progress id' from 'results'
var progressId = dataResults['progressId']
// create Url to check status of download request
var requestCheckUrl = baseUrl + progressId;
Logger.log('requestCheckUrl is: ' + requestCheckUrl);
// create the options for authentication
var options = {
'method': 'get',
'headers': headers
};
// get response from API
var requestCheckResponse = UrlFetchApp.fetch(requestCheckUrl, options);
// extract API response data
var requestCheckResponseText = requestCheckResponse.getContentText();
var requestCheckResponseObj = JSON.parse(requestCheckResponseText);
// get percent complete & fileId from 'results'
var responseResults = requestCheckResponseObj['result'];
var percentComplete = responseResults['percentComplete'];
var fileId = responseResults['fileId'];
Logger.log('percentComplete is: ' + percentComplete);
Logger.log('fileId is: ' + fileId);
// create download Url
var requestDownloadUrl = baseUrl + fileId + '/file';
// get the file
var blob = UrlFetchApp.fetch(requestDownloadUrl, options).getBlob();
// unzip the file
var unzippedBlob = Utilities.unzip(blob)[0];
// get raw data from unzipped file
var csvText = unzippedBlob.getDataAsString();
//Logger.log(csvText);
// parse data as an array for entering into spreadsheet
var csvArray = Utilities.parseCsv(csvText);
// return Survey results data to Parent Function
return csvArray;
}
/*
Loop through each 'SurveyID'.
*/
function loopSurveys(allData) {
// extract values from name:value pairs array
var ss = allData['ss'];
var apiToken = allData['apiToken'];
var labels = allData['labels'];
var surveyIdsArray = allData['surveyIdsArray'];
var dataCenterId = allData['dataCenterId'];
var sleepTimeSecs = allData['sleepTimeSecs'];
// get length of array for loop
var surveyIdsArrayLength = surveyIdsArray.length;
// loop through each 'SurveyID' in array **************************************
for (var i = 0; i < surveyIdsArrayLength; i++) {
// extract individual 'SurveyID'
var surveyId = surveyIdsArray[i];
Logger.log('surveyId is: ' + surveyId);
// run Function to get Survey Name
var surveyName = getSurveyName(apiToken, surveyId, dataCenterId);
// create Toast popup to inform user
ss.toast(surveyName, "Starting Survey:");
// check Function ran successfully before proceeding
if (surveyName) {
logEvent('Starting to export data for Survey: ' + surveyName);
// set attempt value to false as this is first try
var attempt = false;
// run Function to get Survey results data
var surveyResultsData = getSurveyData(ss, attempt, apiToken,
surveyId, labels, dataCenterId, sleepTimeSecs);
// check Function ran successfully before proceeding
if (surveyResultsData) {
// workout dimensions of Survey results data array
var width = surveyResultsData[0].length;
var depth = surveyResultsData.length;
// create new Sheet using Survey Name
var newSheet = ss.insertSheet(surveyName);
// insert survey data into sheet
newSheet.getRange(1, 2, depth, width).setValues(surveyResultsData);
// delete empty first column
newSheet.deleteColumns(1, 1);
// log success
logEvent('Completed export data for Survey: ' + surveyName);
}
else {
// break out of loop to stop script proceeding
return false;
}
}
else {
// break out of loop to stop script proceeding
return false;
}
}
// loop through each 'SurveyID' in array **************************************
// return true as script completed successfully
return true;
}
/*
Output log information to spreadsheet.
*/
function logEvent(action) {
// get the user running the script
var theUser = Session.getActiveUser().getEmail();
// get the relevant spreadsheet to output log details
var ss = SpreadsheetApp.getActiveSpreadsheet();
var logSheet = ss.getSheetByName('Log');
// create and format a timestamp
var dateTime = new Date();
var timeZone = ss.getSpreadsheetTimeZone();
var niceDateTime = Utilities.formatDate(dateTime, timeZone, "dd/MM/yy @ HH:mm:ss");
// create array of data for pasting into log sheet
var logData = [niceDateTime, theUser, action];
// append details into next row of log sheet
logSheet.appendRow(logData);
}
/*
create popup box for user confirmation
*/
function popupBox(apiToken, labels, surveyIds) {
// check relevant boxes are not blank before rest of script can proceed
if (apiToken == '' || labels == '' || surveyIds == '') {
// get Spreadsheet UI
var ui = SpreadsheetApp.getUi();
// create Alert popup box
var result = ui.alert(
'Missing Information',
'Please check you have completed all relevant boxes on the Welcome page.',
ui.ButtonSet.OK);
// set true Flag as values are missing
var blanks = true;
}
else {
// set false Flag as no values are missing
var blanks = false;
}
// return Flag to Parent Function
return blanks;
}
/*
Update the message in the Status cell on the Welcome sheet.
*/
function statusUpdate(message, colour) {
// get current spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// get Welcome sheet
var welcomeSheet = ss.getSheetByName('Welcome');
// get status cell
var statusCell = welcomeSheet.getRange(22, 3);
// update message in status cell
statusCell.setValue(message);
// update colour of status cell
statusCell.setFontColor(colour);
}
/*
Reset spreadsheet to start again.
*/
function reset() {
logEvent("Running 'Reset' Function");
// get current spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// get Welcome sheet
var welcomeSheet = ss.getSheetByName('Welcome');
// clear Qualtrics API Token box
var apiToken = welcomeSheet.getRange(8, 2).clearContent();
// clear labels box
var labels = welcomeSheet.getRange(13, 2).clearContent();
// clear 'SurveyIDs' box
var surveyIds = welcomeSheet.getRange(18, 2).clearContent();
// update status cell
statusUpdate('Ready', '#00ff00');
logEvent("Completed 'Reset' Function.");
}

No comments:

Post a Comment