Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can an apps-script on a Form store extra data into the Sheet?

Q: How can an AppsScript attached to a Form store an extra piece of data into the Sheet?

Situation: We have a (long) Google Form that stores many pieces of data into a Google Sheet. Often the entries need to be edited, and it is much easier to edit using the original form than trying to edit directly into the sheet. (Some of the items are text, several paragraphs long.) I would like to store into the spreadsheet one additional piece of data, specifically the URL that an editor can use to edit the row entry using the form.

I can already get all the form data and I can get the right URL with formResponse.getEditResponseUrl(). And I can send all of that in an email to a user, usually the editor who is collecting all the form entries. (Thanks to many helpful answers in StackOverflow for getting me this far!) But the editor has to manually copy and paste the URL into an additional column in the proper row of the spreadsheet.

I see an interface in class Sheet to add a column to the spreadsheet, but I don't see how to populate that extra column for the particular row that the form just stored. We have added the column manually, and have verified that it is not overwritten by Google when editing via the form. How do I store that one little piece of data into the sheet?

What am I missing? Any help will be greatly appreciated. Thanks.

[added clarifications 2015-02-06]

  • We have a long form that some people submit and other people edit. Editing is to be done using the form, not editing directly in the spreadsheet, so we need the URL that permits the editors to re-edit the response.

  • I would like to store that URL into the spreadsheet during the form submission, so that the editors, who have access to the sheet, can find it.

  • In a script on the Form side, I can easily calculate that URL, but now how do I store it into the sheet in an extra column?

  • In my Form-side script at the moment, I get the URL and send it, along with all the form data, in an email to the editors' distribution list. One of the editors then copies the URL from the email and pastes it into the sheet. (Most of the time, into the correct row, even. :-) This is a potentially error-prone manual step.)

  • A secondary question: what is up with the row numbers in the sheet versus the response numbers in the form.getResponses()? The row numbers and response numbers seem to wander as new items are submitted (i.e., new rows), and old items are edited. Can one reasonably predict the sheet's row number in which the editor will find the form data?

Again, thanks for any help you can give me on this. We have a survivable interim solution. However, with a hundred or so form entries coming in the next couple months, I would love to error-proof the process as much as possible.

rick

like image 830
user3880146 Avatar asked Jan 25 '15 23:01

user3880146


1 Answers

So, I've just stumbled upon your questions and, hopefully, I've understood it correctly.

Possible problems:

  • the script is incorrectly bound to the spreadsheet attached to the form and not to the form itself (which is not the problem in your case as far as I understood from your description)

  • race conditions between submission insertion and additional column edit, or between simultaneous submissions (see lines 27-32 from code)

  • accessing the spreadsheet directly, without prior selecting a sheet from the spreadsheet, even if it spreadsheet contains only one sheet! (see lines 36-37 from code)

  • using the column numeric index, instead of the corresponding column letter as argument for getRange() method, which accepts only column letters AFAIK (see lines 42-43 from code)

Below you have the code which should address all these problems (I have not tested it, but it is an adaptation of a perfect working solution for a very similar scenario):

// Converts sheet column numeric index to corresponding column letter
function columnToLetter(column)
{
  var temp, letter = '';
  while (column > 0)
  {
    temp = (column - 1) % 26;
    letter = String.fromCharCode(temp + 65) + letter;
    column = (column - temp - 1) / 26;
  }
  return letter;
}

The following function must be registered to an "On form submit" event from form - not from the spreadsheet! (Script Toolbar -> Resources -> Current project's triggers -> Add a new trigger)

// Associated the sheet rows with response URLs in an additional column
function onFormSubmit(e)
{
  try
  {
    // Get the response Url, either from FormApp:
    var responseUrl = FormApp.getActiveForm().getEditResponseUrl();
    // Or alternatively get it from the event:
//  var responseUrl e.response.getId().getEditResponseUrl();
    // ....................
    // Other URL processing
    // ....................

    // Get a public lock on this script, because we're about to modify a shared resource.
    var lock = LockService.getPublicLock();
    // Wait for up to 30 seconds for other processes to finish.
    lock.waitLock(30000);
    // Wait for row insertion to finish, so that sheet.getLastRow() method gets the updated number of rows
    Utilities.sleep(1000); // 1 second

    // Here insert the URL to your spreadsheet
    var spreadsheetUrl = "https://docs.google.com/spreadsheets/d/YGUgHi28_gYUffGYGGH_78hkO1Pk/edit";
    // Gets the first sheet inside the spreadsheet (if you have multiple sheets, just change the value [0])
    var sheet = SpreadsheetApp.openByUrl(spreadsheetUrl).getSheets()[0];
    // Get updated number of rows and columns, after form submit inserted the new row
    var lastRow = sheet.getLastRow();
    var lastColumn = sheet.getLastColumn();

    // Get the exact cell, next to the right of the new row, by converting the column index to corresponding letter
    var lastCell = columnToLetter(lastColumn) + lastRow.toString();
    // Set the content of the cell with the new URL
    sheet.getRange(lastCell).setValue(responseUrl);

    // Release the lock so that other processes can continue.
    lock.releaseLock();    
  }
  catch (error)
  {
    // If there's an error, show the error message
    return error.toString();
  }
}

For any other questions, just write a comment. Hope it helps.

like image 175
Teodor Tite Avatar answered Sep 28 '22 06:09

Teodor Tite