Thanks to everyone for their help in advance.
As a general novice to scripts and Installable Triggers specifically, I have searched all over for the technique of how to do this and haven't found what I need. I will try to break down what I am looking for as best I can.
1.) I am using a form service that is not through Google and I need this email script to trigger when a form is submitted via this service.
Currently I am using a Simple Trigger that runs every 5 minutes which is super annoying because it slows down the whole project and is obviously not very efficient.
2.) I don't know where the 'Trigger' function needs to be placed within the script so I will post the simple script that I am using and hopefully you guys can direct me to where it needs to be placed (and if it even matters?).
Finally, I have already visited all of the links both through Google and Formstack on this issue and have been unable to piece together the result I am looking for. Again, I am a novice at this trigger part so a walk-through is very much appreciated! :-)
Thanks again!
Edit: script
function Email1() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.setActiveSheet(ss.getSheetByName("Sheet3"));
var startRow = 2; // First row of data to process
var numRows = 18; // Number of rows to process
// Fetch the range of cells A2:B19
var dataRange = (ss.getSheetByName('Sheet3')).getRange(startRow, 1, numRows, 19)
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (var i = 0; i < data.length; ++i) {
var EMAIL_SENT = "EMAIL_SENT";
var row = data[i];
var emailAddress = row[0]; // First column
if(row[0]!="")
var message = row[1]; // Second column
var emailSent = row[2]; // Third column
var clientEmail = row[13]; // Thirteenth column
if (emailSent != EMAIL_SENT) { // Prevents sending duplicates
var subject = "APPOINTMENT SCHEDULED";
MailApp.sendEmail(emailAddress, subject, message, {noReply:true, cc:clientEmail, bcc:
'xxxxx.myemail.com'});
(ss.getSheetByName('Sheet3')).getRange(startRow + i, 3).setValue(EMAIL_SENT);
// Make sure the cell is updated right away in case the script is interrupted
SpreadsheetApp.flush();
}
}
}
Related
I'm working on a project that take "profiles" stored in a Google Sheet, makes a unique Google Doc for each profile, and then updates the unique Google Doc with any new information when you push a button on the Google Sheet.
I have some other automations built into my original code, but I simplified most of it to what's pertinent to the error I'm getting, which is this:
Exception: Document is missing (perhaps it was deleted, or you don't have read access?
It happens on Line 52 of my script in the fileUpdate funtion. Here's the appropriate line for reference:
var file = DocumentApp.openById(fileName);
And this is the rest of my code:
function manageFiles() {
//Basic setup. Defining the range and retrieving the spreadsheet to store as an array.
var date = new Date();
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var array = sheet.getDataRange().getValues();
var arrayL = sheet.getLastRow();
var arrayW = sheet.getLastColumn();
for (var i = 1; i < arrayL; i++) {
if (array[i][arrayW-2] == "") {
//Collect the data from the current sheet.
//Create the document and retrieve some information from it.
var docTitle = array[i , 0]
var doc = DocumentApp.create(docTitle);
var docBody = doc.getBody();
var docLink = doc.getUrl();
//Use a for function to collect the unique data from each cell in the row.
docBody.insertParagraph(0 , "Last Updated: "+date);
for (var j = 2; j <= arrayW; j++) {
var colName = array[0][arrayW-j];
var data = array[i][arrayW-j];
if (colName !== "Filed?") {
docBody.insertParagraph(0 , colName+": "+data);
}
}
//Insert a hyperlink to the file in the cell containing the SID
sheet.getRange(i+1 , 1).setFormula('=HYPERLINK("'+docLink+'", "'+SID+'")');
//Insert a checkbox and check it.
sheet.getRange(i+1 , arrayW-1).insertCheckboxes();
sheet.getRange(i+1 , arrayW-1).setFormula('=TRUE');
}
else if (array[i][arrayW-2] !== "") {
updateFiles(i);
}
}
sheet.getRange(1 , arrayW).setValue('Last Update: '+date);
}
//Note: I hate how cluttered updateFiles is. I'm going to clean it up later.
function fileUpdate(rowNum) {
//now you do the whole thing over again from createFiles()
//Basic setup. Defining the range and retrieving the spreadsheet to store as an array.
var date = new Date();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var array = sheet.getDataRange().getValues();
var arrayL = sheet.getLastRow();
var arrayW = sheet.getLastColumn();
//Collect the data from the current sheet.
var fileName = array[rowNum][0];
var file = DocumentApp.openById(fileName);
//retrieve the body of the document and clear the text, making it blank.
file.getBody().setText("");
//Use a for function to collect the the unique date from every non-blank cell in the row.
file.getBody().insertParagraph(0 , "Last Updated: "+date);
for (var j = 2; j <= arrayW; j++) {
var colName = array[0][arrayW-j];
var data = array[rowNum][arrayW-j];
file.getBody().insertParagraph(0 , colName+": "+data);
}
}
If you'd like to take a look at my sample spreadsheet, you can see it here. I suggest you make a copy though, because you won't have permissions to the Google Docs my script created.
I've looked at some other forums with this same error and tried several of the prescribed solutions (signing out of other Google Accounts, clearing my cookies, completing the URL with a backslash, widening permissions to everyone with the link), but to no avail.
**Note to anyone offended by my janky code or formatting: I'm self-taught, so I do apologize if my work is difficult to read.
The problem (in the updated code attached to your sheet) comes from your URL
Side Note:
In your initial question, you define DocumentApp.openById(fileName);
I assume your realized that this is not correct, since you updated
your code to DocumentApp.openByUrl(docURL);, so I will discuss the
problem of the latter in the following.
The URLs in your sheet are of the form
https://docs.google.com/open?id=1pT5kr7V11TMH0pJea281VhZg_1bOt8YDRrh9thrUV0w
while DocumentApp.openByUrl expects a URL of form
https://docs.google.com/document/d/1pT5kr7V11TMH0pJea281VhZg_1bOt8YDRrh9thrUV0w/
Just adding a / is not enough!
Either create the expected URL manually, or - much easier / use the method DocumentApp.openById(id) instead.
For this, you can extract the id from your URL as following:
var id = docURL.split("https://docs.google.com/open?id=")[1];
var file = DocumentApp.openById(id)
I have a sheet with rows I'd like to move to another sheet based on a cell value. I tried following this post's solution (refer below), but I'm having trouble editing the script towards what I want it to do.
I'm doing Check-Ins for an event. I would like to be able to change the value in Column F, populate Column G with the time the status changed, and for the row data to migrate to the Attendee Arrived sheet.
I believe the script already does this, but one has to run it manually. It also takes care of deleting the row data in A (Event) after migrating it to B (Attendee Arrived).
My question is could someone please help me set it up in order for script to run continuously (on edit), and also accomplish all of the above if I missed something?
I don't believe the script will respect the drop down format as it runs so I'm willing to manually type in something. It'd be cool if it could stay that way though - makes it easier for one.
Here's the sheet I'm testing on.
https://docs.google.com/spreadsheets/d/1HrFnV2gFKj1vkw_UpJN4tstHVPK6Y8XhHCIyna9TLJg/edit#gid=1517587380
Here's the solution I tried following. All credit to Jason P and Ritz for this.
Google App Script - Google Spreadsheets Move Row based on cell value efficiently
Thank you D:
function CheckIn() {
// How Many Columns over to copy
var columsCopyCount = 7; // A=1 B=2 C=3 ....
// What Column to Monitor
var columnsToMonitor = 6; // A=1 B=2 C=3 ....
//TARGET SPREAD SHEETS
var target1 = "Attendee Arrived";
//Target Value
var cellvalue = "Attendee Arrived";
//SOURCE SPREAD SHEET
var ss = SpreadsheetApp.openById('1HrFnV2gFKj1vkw_UpJN4tstHVPK6Y8XhHCIyna9TLJg');
var sourceSpreadSheetSheetID = ss.getSheetByName("Event");
var sourceSpreadSheetSheetID1 = ss.getSheetByName(target1);
var data = sourceSpreadSheetSheetID.getRange(2, 1, sourceSpreadSheetSheetID.getLastRow() - 1, sourceSpreadSheetSheetID.getLastColumn()).getValues();
var attendee = [];
for (var i = 0; i < data.length; i++) {
var rValue = data[i][6];
if (rValue == cellvalue) {
attendee.push(data[i]);
} else { //Fail Safe
attendee.push(data[i]);
}
}
if(attendee.length > 0){
sourceSpreadSheetSheetID1.getRange(sourceSpreadSheetSheetID1.getLastRow() + 1,
1, attendee.length, attendee[0].length).setValues(attendee);
}
//Will delete the rows of importdata once the data is copided to other
sheets
sourceSpreadSheetSheetID.deleteRows(2,
sourceSpreadSheetSheetID.getLastRow() - 1);
}
Try this:
I imagine that you already know that you'll need an installable onEdit Trigger and that you can't test a function of this nature by running it without the event object.
function checkIn(e) {
var sh=e.range.getSheet();
if(sh.getName()!="Event") return;
if(e.range.columnStart==6) {
if(e.value=="Attendee Arrived"){
e.range.offset(0,1).setValue(Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "M/d/yyyy HH:mm:ss"));
var row=sh.getRange(e.range.rowStart,1,1,sh.getLastColumn()).getValues()[0];
e.source.getSheetByName("Attendee Arrived").appendRow(row);
sh.deleteRow(e.range.rowStart);
}
}
}
I have searched and troubleshooted for many hours, but I can't get this to work.
I am trying send an email when a cell in Column C is marked Complete. The email will contain info from Column D as well as the sheet name. The code below works, but if Column C has more than one item marked Complete, it will send multiple emails.
How can I modify this code to only send an email once for newly marked items?
I'd rather avoid adding a column labeling the item as 'email sent'.
EDIT: For the avoidance of doubt, I wanted to note that I have this set up as an OnEdit Trigger, so that's not the issue.
function sendNotification() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
// figure out what the last row is
var lastRow = sheet.getLastRow();
// the rows are indexed starting at 1, and the first row
// is the headers, so start with row 2
var startRow = 2;
// grab column 3 (the Project Status column)
var range = sheet.getRange(2,3,lastRow-startRow+1,1 );
var numRows = range.getNumRows();
var projectStatus_values = range.getValues();
// grab column 4 (the Project Name column)
var namerange = sheet.getRange(2,4,lastRow-startRow+1,1 );
var nameNumRows = namerange.getNumRows();
var projectName_values = namerange.getValues();
// Get Active Sheet Name
var sheetname = SpreadsheetApp.getActiveSheet().getName();
// Loop over the Project Status values
for (var i = 0; i <= numRows - 1; i++) {
var projectStatus = projectStatus_values[i][0];
if(projectStatus == "Complete")
{var projectName = projectName_values[i][0];
//Define Notification Details
var recipients = "EMAIL#EMAIL.com";
var subject = sheetname + " has a new project marked Completed.";
var body = sheetname + " has a new project marked Completed:\n\n" + projectName + ".\n\nVisit " + ss.getUrl() + " to view the changes.";
//Send the Email
MailApp.sendEmail(recipients, subject, body);
}
}
//End sendNotification
Of the many threads I perused I found two approaches that seemed like they should work, but both didn't workfor me.
First, I tried Hyde's suggestions here---but I ran into an error ("TypeError: Cannot find function getvalue in object Range.") which I understand because I am using a custom function and not passing a range, but rather an object (probably butchered that).
I also tried this thread's suggestion to incorporate a background color check but haven't had any luck (script is just not sending emails at all despite complete lack of debug errors).
I'm making a task manager in Google Sheets that when a task is created it sends the task that was just made to a target Sheet, I was able to get it to send the data just fine to the target sheet but my problem lies with what i wanted to do with that edit made on the target sheet. On the target sheet I want it to grab that info that was just inputted and place it in the next available row, but when I test my code It doesn't wanna work correctly.
My Code(for the target sheet):
function onEdit (e) {
var ss = SpreadsheetApp.openById('SheetIdHere');
var sheet = ss.getSheets()[0];
var sheetRows = sheet.getMaxRows();
var openRow = 0;
for (var i =4;i < sheetRows;i++) {
var currentValue = sheet.getRange(i,2).getValue();
if (currentValue == "") {
openRow = i;
break;
}
}
var lastEdit = sheet.getRange(1000, 2, 1, 3);
var lastEditValues = lastEdit.getValues();
var openRange = sheet.getRange(openRow, 2, 1, 3);
var openA1 = openRange.getA1Notation();
sheet.getRange(openA1).setValues(lastEditValues);
}
So far from my tests, the for loop to find the next available row is working fine because when I make another change on the sheet afterward it will grab the edited information fine and input it into the next row, but on the initial onEdit fire it will just grab the values from the previous test at row 1000 and put it into the next row instead of the new values.
I feel like there is a mistiming perhaps on when it grabs the values because if I clear the sheets and send a new task nothing appears on the next available row but I might be mistaken on that feeling.
I have the following script in google sheets script which works fine.
function morningemail() {
var doc = SpreadsheetApp.getActiveSpreadsheet();
var sheet = doc.getSheetByName("clock script");
var startRow = 9; // First row of data to process
var numRows = 1; // Number of rows to process
var dataRange = sheet.getRange(startRow, 1, numRows, 4)
var data = dataRange.getValues();
for (i in data) {
var row = data[i]
var emailAddress = row[0]; // First column
var message = row[2] + row[3]; // Second & third column
var subject = row[1]; // First column
MailApp.sendEmail(emailAddress, subject, message);
}
}
I will set the project trigger to activate every 5 minutes but only want the script to send an email if cell "A20" (on the same spreadsheet) is ">10".
I've tried a few ways of doing this but can't get it to work. Any suggestions would be great.
Short answer
Add if( data[19][0]>10) return; between the following lines:
var data = dataRange.getValues();
for (i in data) {
Explanation
In order to minimize the number of calls to the API, and considering that your script already got the data range values, use data[19][0] to get the value of A20, then use if to test if the desired condition is met and in such case use return to finalize the execution of the script.
References
Early exit from function?