7
votes

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

2
So basically, you just need to know how to get the row number that the data was just written to?Alan Wells
Thanks for the reply, but that's not it. I already have the row, and all its data, that I emailed to the editor. Now I would like to store data into one extra column of that row.user3880146
If you know the row number and column number where you want the data written to, use the getRange() method first: Google Documentation - getRange Then set the value: Google Documentation - setValue() Write some code, and post it if you have a specific problem.Alan Wells
Thank you for the pointers. I'll try that.user3880146
Thanks, but. . . . Sorry to report that everything I found required such a script to be run from the sheet side, not from the form side. And some of the info that I need is available only in the context of the form. As I said, newbie at Google docs. How does one navigate from the form to the sheet or vice versa? From both directions, trying to get to the other one, XXXAp.getActiveXXX() returns null. We have a survivable workaround, but it contains a manual step we'd just as soon bypass.user3880146

2 Answers

6
votes

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.

1
votes

You can use the form submit range parameter to get the row / spreadsheet range of the form data being placed in the sheet. Then use the range offset method to push your data into the column after the last column of form data.

Notice if you use the HYPERLINK formula, you must escape the quotes that are passes as parameters.

e.g.

function formProcessing(e){
  var formData = e.values;
  var dataRange = e.range; // gets the range on the spreadsheet
  /*
   do all your processing


 */
  var url = "http://www.google.com"; // whatever url to put in spreadsheet

  // add the url value to the spreadsheet
   formRange.getCell(1,formRange.getLastColumn()).offset(0,1).setValue(url);
    // or if you want a named link
  //formRange.getCell(1,formRange.getLastColumn()).offset(0,1).setFormula("HYPERLINK(\"" + url + "\", \"Edit Form\")");
}

enter image description here

enter image description here