Automate dynamic IMPORTRANGE in google sheets - google-apps-script

I am building a multi-section questionnaire (3 in total) and I want to have 4 sheets to hold the data (one master and one for each section).
How would I send data to another sheet from the master spreadsheet when a new row is added to the Master Sheet and make it dynamic so it does not pull the same row everytime?
I have found this script online:
script link
but it is for moving data between sheets unfortunately. Could it be remodeled?
Thanks!

Perhaps the setFormula class can help you, it can be dynamic & you can update it with a trigger
sheet.getRange(1,1,1,1).setFormula('=IMPORTRANGE("SPREADSHEET_URL", "SHEET_NAME!'+rangevariable1inA1notation+':'+rangevariable2inA1notation+'")');
Make sure you get the range you want, and the notation of those 2 variables to get the dynamic range:
var rangevariable1inA1notation = sheet.getRange(1,1).getA1Notation(); // for example, instead of 1,1 it could be your own variable
var rangevariable2inA1notation = sheet.getRange(2,2).getA1Notation(); //for example

To send values to another sheet (besides from your main one), you can use the onFormSubmit function, it will be triggered every time the form is submitted and with its event object, which contains the information from your form, you will be able to pass those values into the other sheets as you want.
// This will be triggered every time the form is submitted
function onFormSubmit(e) {
// Get all sheets in the active spreadsheet
var sheetsArr = SpreadsheetApp.getActiveSpreadsheet().getSheets();
// Get the second sheet
var slaveSheet1 = sheetsArr[1];
// Get the row where the values will be inserted
var rowVals = slaveSheet1.getLastRow() + 1;
// The number of cols where you will puth the values
var numberOfCols = e.values.length;
// Set values that came from the form
slaveSheet1.getRange(rowVals, 1, 1, numberOfCols).setValues([e.values]);
}
Be careful with the installable triggers Restrictions.

Related

is there a way to get form responses and place them in specific cell in another sheet

I have set up a script that creates a Google form and links it to a spreadsheet, is there a way to collect the responses and place them in certain cells in another sheet then unlinks and deletes the form response sheet.
the script I need will have to be flexible enough that it won't matter what the response sheet is named as I will be making multiple one use forms hence why I would also like to delete the response sheet after the answer is moved
for example say the answer is A (a1), B (b1) and C (c1) and I want to move it to sheet 'C' and into columns F, G and H after that's done i would like the response sheet to unlink and be deleted
any help would be greatly appreciated
Issue:
You want form response data to be submitted to a sheet of your choice, not the one that is created when linking the form to the spreadsheet.
Solution:
In that case, I'd suggest not linking the form to the spreadsheet at all, and use an onFormSubmit trigger to write the submitted data to your desired sheet.
Workflow:
Install an onFormSubmit trigger. You can do that manually, following these steps, or programmatically, by executing this function once:
const SOURCE_FORM_ID = "YOUR_FORM_ID"; // Change according to your needs
function installOnFormSubmitTrigger() {
const form = FormApp.openById(SOURCE_FORM_ID);
ScriptApp.newTrigger("onFormSubmitTrigger")
.forForm(form)
.onFormSubmit()
.create();
}
Once the trigger is installed, a function named onFormSubmitTrigger (it doesn't have to be named that way) will execute every time someone submits a response to the form. This function should append the response data to your desired sheet. It could be something like this (check inline comments):
const TARGET_SPREADSHEET_ID = "YOUR_SPREADSHEET_ID"; // Change according to your needs
const TARGET_SHEET_NAME = "Sheet1"; // Change according to your needs
function onFormSubmitTrigger(e) {
const targetSpreadsheet = SpreadsheetApp.openById(TARGET_SPREADSHEET_ID);
const targetSheet = targetSpreadsheet.getSheetByName(TARGET_SHEET_NAME);
if (targetSheet.getLastRow() === 0) { // Add headers if they don't exist yet
const itemTitles = e.source.getItems().map(item => item.getTitle()); // Get item titles
itemTitles.unshift("Timestamp"); // Append "Timestamp" to the sheet (if desired)
targetSheet.appendRow(itemTitles); // Append form item titles to the sheet
}
const itemResponses = e.response.getItemResponses();
const responses = itemResponses.map(itemResponse => itemResponse.getResponse()); // Get user responses
responses.unshift(new Date()); // Add today's date to the responses (if desired)
targetSheet.appendRow(responses); // Append responses to the sheet
}
Note:
If you don't want to submit to the first columns in the spreadsheet, simply add empty strings to the responses array, or use Range.setValues instead.
Reference:
Installable Triggers
appendRow

Google Sheets: Action Based on Birthday

I'm trying to send myself either an email or copy the row to a new sheet when it's someone's birthday or hire date anniversary. Copying the line to a new sheet would allow me to use zapier to notify me of the update. Either would work. The sheet uses a form to collect data.
I've built a few scripts but nothing that had to do with dates. I'm just struggling with this one and have tried a few examples I could find with no luck.
Here is this sheet. It's view only so just let me know if you need more access.
I understand that you want to replicate your form responses Sheet in another Sheet (let's call it Zapier Sheet) automatically each time that a new form response is added. You can achieve that goal developing an Apps Script code that runs at each form response. In that case you can use a code similar to this one:
function so62400514() {
var formSheet = SpreadsheetApp.openById(
'{FORM SHEET ID}').getSheets()[0];
var zapierSheet = SpreadsheetApp.openById(
'{ZAPIER SHEET ID}').getSheets()[0];
var formData = formSheet.getRange(1, 1, formSheet.getLastRow(), formSheet
.getLastColumn()).getValues();
var zapierData = zapierSheet.getRange(1, 1, zapierSheet.getLastRow(),
formSheet.getLastColumn()).getValues();
var recorded = false;
for (var fr = 0; fr < formData.length; fr++) {
for (var zr = 0; zr < zapierData.length; zr++) {
if (formData[fr].toLocaleString() == zapierData[zr].toLocaleString()) {
recorded = true;
}
}
if (recorded == false) {
zapierSheet.appendRow(formData[fr]);
} else {
recorded = false;
}
}
}
This code will first open both sheets (using SpreadsheetApp.openById() and Spreadsheet.getSheets()) to select the data with Sheet.getRange (setting boundaries with Sheet.getLastRow() and Sheet.getLastColumn()) and reading it using Range.getValues(). After that operation the data will get iterated using the property Array.length as the perimeter. The iteration compares each row from the form Sheet to every row of the zapier sheet (to accomplish that, I first parsed the row as a string with Date.toLocaleString()). If the form row is found in the zapier sheet, the boolean recorded will flag to true. After every row on the zapier sheet gets compared to the form row, the code will write it down on the zapier sheet based on the boolean flag.
As explained in the previous paragraph, this code will take the form sheet rows not present in the zapier sheet; and paste them on the zapier sheet. I used this approach to prevent missing any row (as it could happen when simultaneous users answer the form all at once). To make this fire automatically you'll need to set up an installable trigger with these settings:
As an example, let's say that we have these form responses:
And our initial sample zapier sheet looks like this one below. Please, notice how several past rows are missing;
After running the script (as it will do automatically) this would be the result:
I suggest running the script manually for an initial setup. If the timestamps diverge, please check if both spreadsheets share time zones. Don't hesitate to ask me further questions to clarify my answer.

Is it possible to place form responses with sections to different tabs?

was wondering if anyone knows whether it's possible or not to direct form responses to different tabs but within the same spreadsheet?
I have form with different sections, Main, Drop-Off/PickUp, and Meeting. In the Main forms, there is a drop down which has option for either DO/PU, or Meeting. If DO/PU is chosen, they will be directed to that section of the form.
Both forms are quite similar and only has a slight difference. However in the main response sheet, even the same information is divided into different column. eg Name for DO/PU is in column B, while Name for Meeting is in column C.
Therefore, I was wondering if it's possible to direct each section responses to its own tabs.
Will I have to use App Script?
I've only tried using IMPORTRANGE, and QUERY function but the information isn't sorted as how I want it to be (some columns are imported with empty column in between).
Without using Apps Script there isn't a way to segregate and organise form responses into multiple sheets the way you describe unfortunately, as Google Forms will always submit all responses to the same single sheet on each sumbit.
Using Apps Script this can be done quite simply though, if you know how many responses need to be put on each page, you can write an onFormSubmit() function bound to the form which opens the Sheet directly using the Sheet's ID and moves the data from the default sheet into the sheets you would like.
function onFormSubmit(){
// Open the spreadsheet and each of the sheets to copy from and to
var spreadsheet = SpreadsheetApp.openById("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
var tempSheet = spreadsheet.getSheetByName("DefaultSheetTheFormSumbitsTo")
var page1 = spreadsheet.getSheetByName("Responses 1");
var page2 = spreadsheet.getSheetByName("Responses 2");
var page3 = spreadsheet.getSheetByName("Responses 3");
// Get the first free row in each of the segregated sheets to copy from and to
var page1Range = page1.getRange((page1.getLastRow() + 1), 1, 1, 4);
var page2Range = page2.getRange((page2.getLastRow() + 1), 1, 1, 4);
var page3Range = page3.getRange((page3.getLastRow() + 1), 1, 1, 4);
// Get the values of the data from the default sheet that you want
// to copy to each of the sheets
var page1Data = tempSheet.getRange(tempSheet.getLastRow(), 1, 1, 4);
var page2Data = tempSheet.getRange(tempSheet.getLastRow(), 5, 1, 4);
var page3Data = tempSheet.getRange(tempSheet.getLastRow(), 10, 1, 4);
// Copy the data into the right sheet
page1Range.setValues(page1Data.getValues());
page2Range.setValues(page2Data.getValues());
page3Range.setValues(page3Data.getValues());
// Uncomment this if you want to keep the default sheet clean, though
// this code will run either way
// tempSheet.clear();
}
You need to make sure that the .getRanges() point to the correct range of responses each time that the form is submitted. If you want to see the questions in the first row you will need to make sure that each of the individual sheets are populated in row 1 as this script only gets the latest response.
After this has been done, make sure that you have the Sheets API Advanced Google service enabled under Resources -> Advanced google Services, and set up an installable trigger on your G Suite Developer Hub Triggers page so that the onFormSubmit() function runs on event type 'On form submit'.

How to merge a large number of form response sheets into one master sheet. (Google Sheets)

I have a spreadsheet in which I have the form response sheets from a large number of forms (quizzes). As all the forms contain the same format, number and order of questions I want to collect them all into one sheet.
I know this can be done using the query({sheet1!range,sheet2!range...}) But the size of this query would be huge (have over 25 forms!) and it would require me to fiddle around with this formula every time I add a new form.
What i initially investigated was creating a list of sheets in a range and then tried to get arrayformula query to run through that list using indirect. This however did not work and after asking on this forum have been told that that cannot be done.
I was advised to look into scripts and have spent all weekend trying to find a script that can do this. I have however failed.
I need the script to copy the last row of a form response sheet to the bottom of a master sheet. I would like the script to do this to all response sheets (I have a naming format for sheets that would allow the script to easily see which sheets to incorporate). I would imagine I need to use the onSubmit() function but not sure.
Overview
I don't think that using a on submit trigger is a good idea because there are several forms involved and a large number of responses. By the other hand could be conflicts if several responses are submitted very close. IMHO a better approach is to run the script by demand (manually) or by using a time-driven trigger.
As Google Apps Script execution time should not exceed six minutes1, instead of checking each sheet for new responses I think that a better approach is to clear the master sheet and append all the sheet responses at once. This could work if there will no be notes, comments, custom formats, data validations or data changes applied directly on the master sheet.
Script
Below is a script that joins the content of form responses sheets that are in the same spreadsheet to a sheet named 'Master'. It could be easily adapted, I think.
It's assumed that the spreadsheet only contains form responses sheets and the 'Master' sheet.
/**
* Joins the data from form responses sheets on a master responses sheet.
* Assumes that the master sheet has the form response headers and
* that there aren't extra columns.
*/
function joinSheetsData() {
// Initialize loop variables
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var masterName = 'Master'; // Change this.
var masterSheet = ss.getSheetByName(masterName);
var headers = [masterSheet.getDataRange().getValues()[0]];
var masterdata = [];
// Loop to append form responses from all response sheets to one array.
for(var i = 0; i < sheets.length; i++){
if(sheets[i].getName() != masterSheet.getName()){
var data = sheets[i].getDataRange().getValues();
data.splice(0,1);
masterdata = append(masterdata,data);
}
}
// Clear the master sheet.
masterSheet.clear();
// Add the headers to the master sheet.
var masterheadersRow = masterSheet.getRange(1, 1, 1,
masterdata[0].length);
masterheadersRow.setValues(headers);
// Send the resulting array to the master sheet.
var masterdataRange = masterSheet.getRange(2, 1, masterdata.length,
masterdata[0].length);
masterdataRange.setValues(masterdata);
}
/*
* Auxiliary function to append 2D arrays.
*/
function append(a,b){
for(var i = 0; i<b.length; i++){
a.push(b[i]);
}
return a;
}

google sheets form function not updating with data input from the script

I'm having some trouble getting a function in a google sheet to calculate using values inserted by a script.
I have a google script that is adding data to a sheet based on user-inputted data from a form that the script has created. So, in the form, a user inputs their name, selects a product and some options, and the script adds this to a sheet named 'Client Data Sheet'.
I then have a different sheet which is supposed to do the math to calculate the price. My script copies all the functions from a hidden template row into the next-available row, so, for example, cell D7 contains ='Client Data Sheet'!A4, D8 contains ='Client Data Sheet'!B4 etc... These all display the correct values.
The problem is the price calculation function, in that same calculation sheet, which has a rather complex function that a previous coder wrote. This function should calculate the price of the product and options based on the data in the same sheet. It does so without calling a script, just pulling data from cells in other sheets, running some if() checks to decide whether or not to add extra costs to the total price, and adding it all up.
Problem is, it doesn't update based on the new data. It just shows 0, as though the other cells were empty, even though they now contain data updated by pulling data from another sheet which was edited by a script. If I go in to edit the function and just press enter, it re-calculates correctly, so I basically just need the function to re-evaluate based on the new data that is in the cells it's dependent on.
My theory is that it's not updating the function since I didn't directly edit the cells it's dependent on. I could try to change the awkward, huge function this other coder wrote so it pulls from the spreadsheet my script edits, rather than from cells that copy in that data, but that seems like a workaround, and is unsatisfying.
TL;DR: a function isn't updating based on data that changes in cells that copy the data from other cells which are filled by a script. Anybody have any advice for how to get the function to update?
EDIT: Ok, so if I make sure that the function that isn't updating pulls at least some data from cells that the script updates, it works. It seems that it doesn't recognize that the cell once removed has updated as well. It would be better, though if it was able to pull from the cells that pull their data from the cells the script updates. This would let other users of the sheet change the products if a customer requested a change later on without having to edit my hidden sheets that the data is pulled from.
Code:
This copies a template row in my spreadsheet that contains the math functions:
function new_client(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Liquidation');
if(ss.getName()=="Data" || ss.getName().indexOf("Liq")==0){
var last_row = ss.getLastRow();
var last_col = ss.getLastColumn();
ss.insertRowsAfter(last_row, 1);
var template_row = 4;
var copy_range = ss.getRange(template_row, 1, 1, last_col);
var paste_range = ss.getRange(last_row+1, 1, 1, last_col);
copy_range.copyTo(paste_range, {contentsOnly:false});
paste_range.getCell(1,1).setValue("NO");
SpreadsheetApp.flush();
}
}
This copies data from the form into a different spreadsheet, which the sheet edited above pulls data from:
var clientSheet = SpreadsheetApp.getActive().getSheetByName('Client Data Sheet');
clientSheet.insertRowsAfter(clientSheet.getMaxRows(), 1)
var lastRow = clientSheet.getLastRow() + 1;
var lastColumn = clientSheet.getLastColumn();
var destRow = clientSheet.getRange(lastRow, 1, 1, lastColumn);
var column = 1;
for (var key in user) {
if (user.hasOwnProperty(key)) {
destRow.getCell(1, column).setValue(user[key]);
column++;
}
}
So, for example, after this code is run, the Client Data Sheet contains a cell, B4, which now contains the name of the chosen product, "foo". The Liquidation sheet has a cell let's call it A5 that contains the function ='Client Data Sheet'!B4, as well as another cell which has the price calculation function: =if(A5="foo", 100, 0)
When the script above inserts the values from the form, the cell B4 in Client Sheet and the cell A5 in the Liquidation sheet will contain the right value, but the cell with the calculation function =if(B4="foo", 100, 0) will not update.