1

Is there is a fast way to programmatically export all responses from a Google Form to a csv? Something like "Export responses to csv" invoked via Scripts.

Right now I'm doing it in a rock art way:

  • Iterate over the forms I want to export (~75)
    • Open each form var form = FormApp.openById(formId);
    • Get responses: var formReponses = form.getResponses(); (from 0 to 700 responses each form)
    • Iterate over responses and get item responses: var preguntes = formReponses[r].getItemResponses();
      • For each itemResponse, convert it to csv/json
    • Export responses to a drive file

This is extremly slow and additionally it hangs over and over, so I had to export responses in chunks of 50 responses and save them in Drive separated files. On next execution (after letting servers to cool down for a while), I'm executing the script again, skipping the number of responses found on the chunk file.

Additionally I'm not sure that Google keeps the responses order when doing form.getResponses(); (actually I've found that if the form has been modified, the order is not the same)

Is there a better way to do it?

Miquel
  • 8,339
  • 11
  • 59
  • 82
  • I am assuming you save the responses to a spreadsheet, you might speed this up by getting the values from the sheet using [getValues](https://developers.google.com/apps-script/reference/spreadsheet/range#getValues()). Then create a CSV file like described in this [SO post](http://stackoverflow.com/questions/28327109/google-apps-script-convert-to-csv) – Jack Brown Apr 26 '17 at 19:32
  • Nope, the responses are not in a spreadsheet to speed up data introduction. Basically there is an Script app behind that filters and relates the different forms data and presents it to user – Miquel Apr 26 '17 at 20:04
  • 1
    You can download form responses by using this link: `https://docs.google.com/forms/d/{form ID here}/downloadresponses?tz_offset=-18000000`. By modifying the form ID, this works well in a browser but I have not been able to get it to work with UrlFetchApp (authorization fails)maybe you can figure that part out. – Jack Brown Apr 26 '17 at 20:46
  • Is it possible to access this link via google apis? I'm trying to automatically download responses from node.js – Miquel Apr 27 '17 at 07:34
  • I have not used it, but I would think the Form Class item getResponses()](https://developers.google.com/apps-script/reference/forms/form#getResponses()) which retrieves an array of the responses could be used instead of the spreadsheet class getValues(). So maybe a modification of the [SO post](http://stackoverflow.com/questions/28327109/google-apps-script-convert-to-csv) mentioned by @JackBrown using Form class items is possible? Maybe you mean you are doing that. If it is inefficient maybe post the code for assistance. – Karl_S Apr 27 '17 at 14:53

1 Answers1

1

Whith the help of @JackBrown I've managed to write a Chrome extension to download responses (maybe soon in github). This will wait for each download in the formIds object until finished and then prompt for the next one:

'use strict';

function startDownload() {
    const formIds = {
        'Downloads-subfolder-here': {
            'Download-filename-here': '1-cx-aSAMrTK0IHsQkE... {form-id here}',
            'Another-filename-here': '...-dnqdpnEso {form-id here}',
            // ...
        },
        'Another-subfolder-here': {
            'Download-filename-here': '1-cx-aSAMrTK0IHsQkE... {form-id here}',
            'Another-filename-here': '...-dnqdpnEso {form-id here}',
            // ...
        },
    };

    const destFolders = Object.keys(formIds);
    const downloads = [];

    for (let t = 0, tl = destFolders.length; t < tl; t += 1) {
        const destFolder = destFolders[t];
        const forms = Object.keys(formIds[destFolder]);

        for (let f = 0, fl = forms.length; f < fl; f += 1) {
            const formName = forms[f];
            downloads.push({
                destFolder,
                formName,
                url: `https://docs.google.com/forms/d/${formIds[destFolder][formName]}/downloadresponses?tz_offset=-18000000`,
                filename: `myfolder/${destFolder}/${formName.replace(/\//g, '_')}.csv`,
            });
        }
    }

    const event = new Event('finishedDownload');
    const eventInterrupt = new Event('interruptedDownload');
    let currId;

    chrome.downloads.onChanged.addListener((downloadDelta) => {
        if (downloadDelta.id === currId) {
            if (downloadDelta.state && downloadDelta.state.current === 'complete') {
                document.dispatchEvent(event);
            } else if (downloadDelta.state && downloadDelta.state.current === 'interrupted') {
                console.log(downloadDelta);
                document.dispatchEvent(eventInterrupt);
            }
        }
    });

    downloads.reduce((promise, actual) => {
        return promise.then((last) => (last ? new Promise((resolve) => {
            const { url, filename, destFolder, formName } = actual;
            function listener() {
                document.removeEventListener('finishedDownload', listener);
                document.removeEventListener('interruptedDownload', listener);
                resolve(true);
            };
            function interrupt() {
                document.removeEventListener('finishedDownload', listener);
                document.removeEventListener('interruptedDownload', listener);
                resolve(false);
            }
            console.log(`Processant ${destFolder}, ${formName}: ${url}`);
            document.addEventListener('finishedDownload', listener);
            document.addEventListener('interruptedDownload', interrupt);
            chrome.downloads.download({ url, filename }, (downloadId) => {
                currId = downloadId;
                if (!downloadId) {
                    console.log();
                    console.log('Error downloading...');
                    console.log(runtime.lastError);
                    resolve();
                }
            });
        }) : Promise.resolve(false)));
    }, Promise.resolve(true));
}

chrome.browserAction.onClicked.addListener((/*tab*/) => startDownload());
Miquel
  • 8,339
  • 11
  • 59
  • 82