create individualized google forms based on google sheets information - google-apps-script

Say I have a spreadsheet in google sheets with three columns EMAIL, TOPIC and TIME where I´d like to send an email to each of the EMAILs in the spreadsheet containing a link to a google form that has a question asking the preferred TIME for the given TOPIC in the spreadsheet. Is it doable to create such individualized google forms based on sheets?

The first thing that should be done is creating the forms and sending the emails. To do that, I wrote a function that loops through all the rows in your sheet (called "Sheet1", change it according to your preferences), creates a form for each row and sends it to the emails found in column A (in the sheet I've been working on, data starts at row 2 and columns are: A - email / B - topic / C - time):
function sendMails() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var values = ss.getSheetByName("Sheet1").getDataRange().getValues();
for(var i = 1; i < values.length; i++) {
var email = values[i][0];
var topic = values[i][1]
var formName = email + " - " + topic;
var form = FormApp.create(formName);
var url = form.getPublishedUrl();
form.setTitle(formName);
form.setDestination(FormApp.DestinationType.SPREADSHEET, ss.getId()); // This line bounds the created form to your spreadsheet so that responses will be written in here, in an automatically created sheet
// You can set another destination spreadsheet if you don't want all these sheets created in your spreadsheet
var question = form.addDateTimeItem();
question.setTitle("Please provide a preferred time for your assigned lecture on " + topic);
var userEmail = form.addCheckboxItem();
userEmail.setChoices([userEmail.createChoice(email)]);
var topicName = form.addCheckboxItem();
topicName.setChoices([topicName.createChoice(topic)]);
var checkBoxValidation = FormApp.createCheckboxValidation()
.requireSelectExactly(1)
.build();
userEmail.setValidation(checkBoxValidation);
topicName.setValidation(checkBoxValidation);
MailApp.sendEmail(email, topic, url);
}
}
Next, you need to install an onFormSubmit trigger in your spreadsheet. You can do this to run a function that will write the preferred TIME the user chose in the form every time a form is submitted. To create the trigger, run this function in your script, only once:
function createOnFormSubmitTrigger() {
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger('writeTime')
.forSpreadsheet(ss)
.onFormSubmit()
.create();
}
Finally, below is the function that the trigger trigs when a form is submitted. It looks for the row where topic and email match the ones coming from the form, and sets the time:
function writeTime(e) {
var response = e.values;
var time = response[1];
var email = response[2];
var topic = response[3];
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var values = sheet.getDataRange().getValues();
for(var i = 1; i < values.length; i++) {
if(values[i][0] == email && values[i][1] == topic) {
sheet.getRange(i + 1, 3).setValue(time);
}
}
}
I hope this is useful to you.

Related

getSheetByName takes too long to process during onFormSubmit trigger [duplicate]

This question already has answers here:
Formulas make SpreadsheetApp work slower in Google Sheets Script
(3 answers)
Very slow execution of for...in loop
(2 answers)
Google Apps Script Iterating over Spreadsheets very slow
(2 answers)
Closed 3 months ago.
I'm creating a spreadsheet wherein all projects were entered thru Google Form linked to a Google Spreadsheet and then retrieving the information to a specific Sheet intended for each Teams thru onFormSubmit Trigger. I am able to get what I want but I came to a point where my script takes too long to complete. And I figure out which part of the script is the culprit.
I'm getting around 20s just to return
var sheet = e.source.getSheetByName(TeamSheetNo);
Here is the script for the onFormSubmit function.
function onFormSubmit(e) {
Logger.log("onFormSubmit has been ran.");
var itemResponses = e.values;
var Rteam = itemResponses[7];
var Rjobno = itemResponses[1];
var Rprojectname = itemResponses[2];
var Rclientname = itemResponses[3];
var Rother = itemResponses[6];
var Rjobmanager = itemResponses[4];
var Rcomponent = itemResponses[5];
Logger.log("Item Response Lists - " + [Rteam, Rjobno, Rprojectname, Rclientname, Rother, Rjobmanager, Rcomponent]);
console.time('GetInsertJobIndex');
var Teams = JSON.parse(userproperties.getProperty('IndexTeams'));
Logger.log(Teams);
var search = Rteam
var TeamIndex = Teams.indexOf(search) + 1;
Logger.log("Insert Job at Sheet No. " + TeamIndex);
console.timeEnd('GetInsertJobIndex');
console.time('Insert_Response');
Logger.log("Start Insert Project");
// Get Team Sheet Tab
//var TeamSheetNo = userproperties.getProperty("TeamSheetNo");
var TeamSheetNo = TeamIndex;
Logger.log("Go to Sheet No. " + TeamSheetNo);
console.time('ActiveSheet');
var sheet = e.source.getSheetByName(TeamSheetNo);
//var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(TeamSheetNo); //this take too long to run
console.timeEnd('ActiveSheet');
//Logger.log("Get " + sheet.getRange("C1:C1").getValue());
var ColumnToCheck = sheet.getRange("A:A").getValues();
Logger.log("Get " + ColumnToCheck);
var LastRow = getLastRowSpecial(ColumnToCheck);
Logger.log("Get Last Row = " + LastRow);
// Get the ID Number for the Submitted Project
// var formId = '1_il_J6Hm_3D-ASB8cKVXYiLZVtVJcBdB0hfPRMQazEA';
var form = FormApp.openById(formId);
var IDnumber = form.getResponses().length;
Logger.log(IDnumber + " - ID Number for the Submitted Project")
// Get Form Responses 1 Tab
var FormSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Form Responses 1');
var FormColumntoCheck = FormSheet.getRange("A:A").getValues(); //Get all values from sheet
var FormLastRow = getLastRowSpecial(FormColumntoCheck);
// Check whether Submitted Project exists on the Existing Data
let found = false;
FormColumntoCheck.forEach(r => {
if (~r.indexOf(IDnumber)) {
Logger.log("Submitted Project Found on the Existing Data");
found = true;
};
});
if (found) {
Logger.log("Exit Function");
return;
} else {
Logger.log("Insert New Job then")
//SpreadsheetApp.getActiveSpreadsheet().toast("Consolidating New Project Data...", "💡",3); <----- Not working on OnFormSubmit Trigger
// Insert New Project
// Insert New Project to Form Responses 1 Tab
FormSheet.getRange(FormLastRow + 1, 1).setValue(IDnumber);
Logger.log("Insert New Project to Form Responses 1 Tab");
// Insert New Project to Team Sheet Tab
var value = [IDnumber]; // inserted to Team Sheet Tab
for (var i = 1; i <= 4; i = i + 1) {
var row_number = LastRow + i
// Value has been inserted
sheet.getRange(row_number, 1).setValues([value]);
};
};
SpreadsheetApp.flush();
Logger.log("Insert New Project to Task Tab");
//UpdateFilterRange(sheet, row_number);
//SpreadsheetApp.flush();
console.timeEnd('Insert_Response');
getEditResponseUrls(e);
SpreadsheetApp.flush();
//SpreadsheetApp.getActiveSpreadsheet().toast("Update Complete", "💡",3); <----- Not working on OnFormSubmit Trigger
}
Here is the logs for
console.time('ActiveSheet');
Logs
The script is too long because I tried making it work on a single function first and wishing it will help. Unfortunately, it did not. Actually, these are made from multiple functions called under onFormSubmit function.
Are there any solution/s on my problems?
console.time('ActiveSheet');
var sheet = e.source.getSheetByName(TeamSheetNo);
console.timeEnd('ActiveSheet');
(20060 ms)
The getSheetByName() call should not take that long unless the spreadsheet is on the heavy side. Chances are that you can improve the loading time by optimizing the spreadsheet. See these optimization tips.

Google Sheets Email Script - Sending different sheet tabs based on values of Column on main sheet

I'm trying to write a script that will allow me to email a list of 150 employees with their individual sales data for the week.
At the moment I have a main front sheet with a Column for Email, Subject, and Store number. Each store number correlates to a Sheet (tab) with the same name, for example joe#gmail.com at store number 5070 has a tab named '5070' with changing data.
The problem I'm having is referencing the changing variable sheet name.
function sendEmail() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet1=ss.getSheetByName('Sheet1');
var n=sheet1.getLastRow();
for (var i = 2; i < n+1 ; i++ ) {
var emailAddress = sheet1.getRange(i,1).getValue();
var subject = sheet1.getRange(i,2).getValue();
var message = sheet1.getRange(i,3).getValue();
MailApp.sendEmail(emailAddress, subject, message);
}
}
I am very new to the whole thing and have been searching around but have not had much luck. Thank you in advance!
You can't send a sheet. You can send only a link to the sheet.
If you replace this:
var message = sheet1.getRange(i,3).getValue();
with this:
var sheet_name = sheet1.getRange(i,3).getValue();
var sheet = ss.getSheetByName(sheet_name);
var message = sheet.getUrl();
Your recipients will get the link to the spreadsheet (a whole sheet, not even to the particular sheet).
To send a link to a particular sheet of the spreadsheet you need a bit more complicated solution:
var sheet_name = sheet1.getRange(i,3).getValue();
var sheet = ss.getSheetByName(sheet_name);
var message = getSheetUrl(sheet);
function getSheetUrl(sht) {
// credits: https://webapps.stackexchange.com/questions/93305/
var ss = SpreadsheetApp.getActive();
var url = '';
sht = sht ? sht : ss.getActiveSheet();
url = (ss.getUrl() + '#gid=' + ss.getSheetId());
return url;
}
But all your recipients will see all the spreadsheet anyway with all its sheets. In case this is not the thing you want you have three options:
Option 1 -- Make a new spreadsheet, copy the data into it and send the link to this new spreadsheet.
Option 2 -- Make PDF from the sheet and send it. Actually you will need to perform Option 1 first, convert the new spreadsheet to PDF, and delete the new spreadsheet after you send it (as PDF).
Option 3 -- make a HTML table (or text table, why not?) from the data of the sheet and send the table.

How to send emails in google sheets based on the following conditions?

https://docs.google.com/spreadsheets/d/1tf99kQdDSGxvj8f9B383W1NZRHRsd-b6bUcl9FJAOmQ/edit?usp=sharing
I want to send email from google sheets based on the criteria in column D and subject and messages choose as per criteria.I lost it in the line to choose the three different criteria(Yes,No,Confirm) and couldn't loop it.The email will be sent to different ids in sheet1 table.
How can i achieve this in google sheets scripts and how to send emails when new entry are made into the sheet?
Any solutions would be highly helpful for my project.
Googlesheet newbie.
function checkValue(e)
{
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName(“Sheet1”);
var valueToCheck = sheet.getRange(“D2:D”).getValue();
var rangeEdit = e.range.getA1Notation();
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet1=ss.getSheetByName('Sheet1');
var n=sheet1.getLastRow();
if(rangeEdit == “D2:D”)
{
if(valueToCheck = Yes )
{
function sendEmail() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet1=ss.getSheetByName('Sheet1');
var n=sheet1.getLastRow();
for (var i = 2; i < n+1 ; i++ ) {
var emailAddress = sheet1.getRange(i,1).getValue();
var subject = sheet1.getRange(i,2).getValue();
var message = sheet1.getRange(i,3).getValue();
MailApp.sendEmail(emailAddress, subject, message);
}
}
You can add Google Forms as a way to populate the data in your sheet. Using forms will give you access to new entry data which you can use to send an email.
How to add Google Forms to Google Sheet?
In your spreadsheet, click on Insert and select Form
How to determine if new entry is added and how to access the data?
You can use Installable Triggers. Installable Triggers run a function automatically when a certain event, such as submitting a form, occurs. When a trigger fires, Apps Script passes the function an event object as an argument, typically called e. The event object contains information about the context that caused the trigger to fire. You can follow the steps here on how to create Installable Trigger.
Note: For this project, make sure to use On form submit as event type.
Example:
Form:
Code:
function sendEmail(e) {
var data = e.namedValues;
var msgContent = {
'Yes':{
'Subject': 'Receipt of your goods',
'Message': 'We acknowledge receipt of goods.',
},
'No':{
'Subject': 'Non delivery',
'Message': 'We have not received the goods.',
},
'Confirm':{
'Subject': 'Confirm',
'Message': 'Confirm your message.',
}
}
var emailAddress = data['Email'][0];
var criteria = msgContent[data['Criteria'][0]];
var subject = criteria['Subject'];
var body = criteria['Message'];
MailApp.sendEmail(emailAddress, subject, body);
}
Output:

Fetch data from source sheet based on emails provided in the Emails tab in the target spreadsheet

I have a problem where I have two sheets. one sheet is the source spreadsheet and another is a target spreadsheet. The source spreadsheet has a source sheet has which is the master database and the target spreadsheet has the target where we want to fetch data from source sheet based on emails provided in the Emails tab in the target spreadsheet.
I want the following things to happen with a script and not with IMPORTRANGE or QUERY:
The target spreadsheet will have multiple copies so I want to connect the target spreadsheet with the source spreadsheet based on the source spreadsheet's id.
I want the email matches to be case insensitive so that the users of the target spreadsheet can type emails in any case.
The Emails can go up to 50 or let's say get the last row for that column.
It will be great if the script shows a pop up saying updated after it has fetched the data.
The source sheet might have data up to 15000 rows so I am thinking about speed too.
I have shared both of the spreadsheets with hyperlinks to their names. I am not really great at scripts so it will be helpful if you can leave comments in it wherever you feel like. I would truly appreciate your help.
Thanks in advance!
Script here:
function fetch() {
//get the sheets
var source_Ssheet = SpreadsheetApp.openById('19FkL3rsh5sxdujb6x00BUPvXEEhiXfAeURTeQi3YWzo');
var target_Ssheet = SpreadsheetApp.getActiveSpreadsheet();
//get the tabs
var email_sheet = target_Ssheet.getSheetByName("Emails");
var target_sheet = target_Ssheet.getSheetByName("Target Sheet");
var source_sheet = source_Ssheet.getSheetByName("Source Sheet");
//get ranges
var email_list = email_sheet.getRange("B2:B");
var target_sheet_range = target_sheet.getRange("A1:F100");
var source_sheet_range = source_sheet.getRange("A1:F100");
//get last rows
var last_email_name = email_list.getLastRow();
var last_target_sheet_range = target_sheet_range.getLastRow();
var last_source_sheet_range = source_sheet_range.getLastRow();
//start searching for emails
for (var i=3; i < last_email_name.length+1; i++)
{
for(varj=3; j< last_source_sheet_range.length+1; j++ )
{
if(source_sheet_range[j][3].getValue() == email_list[i][3].getValue())
{
//copy matches to target sheet
target_sheet.getRange((last_target_sheet_range + 1),1,1,10).setValues(master_sheet_range[j].getValues());
}
}
}
}
Several things
last_email_name and last_source_sheet_range are numbers - they do not have any length, this is why your first forloops are not working
You are missing a space in varj=3;
email_list[i][3].getValue() does not exist because email_list only includes B - that only one column. I assume you meant email_list[i][0].getValue()
ranges cannot be addressed with the indices [][], you need to retrieve the values first to have a 2D value range.
You email values in the different sheets do not follow the same case. Apps Script is case sensitive, to suee the == comparison you need to use the toLowerCase() method.
Also mind that defining getRange("B2:B") will include many empty rows that you don't need and will make your code very slow. Replace it through getRange("B2:B" + email_sheet.getLastRow());
Have a look here at the debugged code - keep in mind that there is still much room for improvement.
function fetch() {
//get the sheets
var source_Ssheet = SpreadsheetApp.openById('19FkL3rsh5sxdujb6x00BUPvXEEhiXfAeURTeQi3YWzo');
var target_Ssheet = SpreadsheetApp.getActiveSpreadsheet();
//get the tabs
var email_sheet = target_Ssheet.getSheetByName("Emails");
var target_sheet = target_Ssheet.getSheetByName("Target Sheet");
var source_sheet = source_Ssheet.getSheetByName("Source Sheet");
//get ranges
var email_list = email_sheet.getRange("B2:B" + email_sheet.getLastRow()).getValues();
var target_sheet_range = target_sheet.getRange("A1:F100").getValues();
var source_sheet_range = source_sheet.getRange("A1:F100").getValues();
var last_target_sheet_range = target_sheet.getLastRow();
//start searching for emails
for (var i=1; i < email_list.length; i++)
{
for(var j=1; j< source_sheet_range.length; j++ )
{
if(source_sheet_range[j][0].toLowerCase() == email_list[i][0].toLowerCase())
{
target_sheet.getRange((last_target_sheet_range + 1),1,1,6).setValues([source_sheet_range[j]]);
}
}
}
}

Is there a way to submit google forms responses to different spreadshseets, based on user dropdown choice?

I have many users who submit workload data via a google from. These include a field that designates the site they are reporting from.
I now have 1000s of responses that I query > filter from the main response submission sheet, into separate spreadsheets for each site. The data load time for the query is getting longer and longer with the growing responses.
[FORM] --> [MASTER SHEET WITH RESPONSES]
|
|
/ | \
QUERY FILTER INTO SEPARATE SHEETS
I'd like to, instead, force the form to submit the responses directly into the appropriate site spreadsheet based on which site is selected in the form. Is there a way to do this?
[FORM: USER SITE SELECTION] --> SCRIPT SENDS DATA DIRECTLY TO SHEET FOR THAT SITE
You asked whether there is a way to force a form to submit responses directly into a spreadsheet based on a form selection?
In comments TheMaster and DimuDesigns referred to i) using a script bound to the form, not the spreadsheet, and ii) using onFormSubmit as an installable trigger. The following answer is an example of those principles.
In testing, I found that it was essential that the form should be named onFormSubmit(e). If the code is named as a custom function, then I found it gave an error "You do not have permission to call SpreadsheetApp.openByUrl."
Possibly I might/could/should have used switch instead of if...else. But there are many ways of realising this outcome, and this code should be regarded as just one example.
function onFormSubmit(e) {
//so5745108802
var form = FormApp.getActiveForm();
//Logger.log(JSON.stringify(e));
var resp = e.response.getItemResponses();
//Logger.log(resp);
//Logger.log("DEBUG: resp = "+resp+", and length = "+resp.length);
var dateStamp = Utilities.formatDate(new Date(), "GMT+10:00", "dd-MM-yyyy");
var output = [];
var temp=[];
temp.push(dateStamp);
for (var j = 0; j < resp.length; j++) {
var itemResponse = resp[j];
// Logger.log('DEBUG: Response to the question "%s" was "%s"',itemResponse.getItem().getTitle(),itemResponse.getResponse());
// Logger.log("DEBUG: the response = "+ itemResponse.getResponse())
temp.push(itemResponse.getResponse());
}
output.push(temp);
// Logger.log(output);
var target = resp[2].getResponse();
// Logger.log("DEBUG: the target sheet is "+target)
// Note: be sure to edit the urls for your own spreadsheets
if (target === "A"){
// target = A
var url = "https://docs.google.com/spreadsheets/d/url1/edit" // Spreadsheet=so_57451088_A
}
else if (target === "B"){
// target = B
var url = "https://docs.google.com/spreadsheets/d/url2/edit" // Spreadsheet=so_57451088_B
}
else {
// target = C
var url = "https://docs.google.com/spreadsheets/d/url3/edit" // Spreadsheet=so_57451088_C
}
var ss = SpreadsheetApp.openByUrl(url);
Logger.log(ss.getName());
var sheetname = "Sheet1";
var sheet = ss.getSheetByName(sheetname);
var sheetLR = sheet.getLastRow();
var targetRange = sheet.getRange(sheetLR+1,1, 1, resp.length+1);
// Logger.log("DEBUG: the target range = "+targetRange.getA1Notation());
targetRange.setValues(output);
}
Form screenshot
Example Spreadsheet screenshot