8

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

user3880146
  • 81
  • 1
  • 5
  • So basically, you just need to know how to get the row number that the data was just written to? – Alan Wells Jan 26 '15 at 03:11
  • 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 Jan 27 '15 at 17:17
  • If you know the row number and column number where you want the data written to, use the `getRange()` method first: [Google Documentation - getRange](https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet#getRange(String)) Then set the value: [Google Documentation - setValue()](https://developers.google.com/apps-script/reference/spreadsheet/range#setValue(Object)) Write some code, and post it if you have a specific problem. – Alan Wells Jan 27 '15 at 17:38
  • 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 Feb 06 '15 at 20:55

2 Answers2

6

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.

Teodor Tite
  • 1,855
  • 3
  • 24
  • 31
1

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

Cyrus Loree
  • 837
  • 6
  • 7
  • Thanks for the reply. Can I ask for some clarifications? – user3880146 Jan 27 '15 at 17:32
  • Thanks for the reply. Can I ask for some clarifications? 1. In an apps-script running on the Form, how do I get the sheet? I tried SpreadsheetApp.getActiveSheet() but that returned null. 2. In the code example you gave, is "dataRange" a typo for "formRange"? If not, where does formRange come from. Sorry, I am a real beginner at Google Forms and Sheets. 3. What is the "e" with which many example functions are called. I have seen it many times but not uncovered the reason in a number of tutorials. If you can give me a pointer to a tutorial, I would be most grateful. Thanks. – user3880146 Jan 27 '15 at 17:37
  • You want to run your code from the Spreadsheet where the data is stored. The Spreadsheet Apps Script has a container based trigger 'onFormSubmit' which captures the event of the form submission. – Cyrus Loree Jan 27 '15 at 22:52
  • In essence it's a reactive process, when the data has been stored in the spreadsheet. That is how e.range (in the code snippet above) knows what row (range) the data has been stored in the spreadsheet. -- If you paste the code snippet above into the script editor on your spreadsheet, setup the trigger for 'On form submit', it will shove a link to Google in the first column after your form data into your spreadsheet. – Cyrus Loree Jan 27 '15 at 23:01
  • Thank you for the details. I will try this. There might be one glitch: to get hold of the EditResponseURL, I currently have code that starts from the form, to the responses, to the response items, and then to the URL. If I can get the sheet from the form script, then that will integrate more easily with what I already have running. I trigger from the same event, onFormSubmit. I will post the code when I get it running. – user3880146 Jan 28 '15 at 01:41
  • Thanks, but. . . . (1) I have not found a way to calculate the data I need (the Response.getEditResponseURL() ) from the sheet side of the conversation. (2) On the sheet side, the range seems to include only the changed fields *if* one is editing a previously stored entry. So offset(0,1) gets me to the cell to the right of the last changed value, which is usually in the middle of the row. And 80% of all our transactions are edits to previously stored items. Thanks for assistance. – user3880146 Feb 06 '15 at 21:04