I have never written in JavaScript before, and I'm sure it shows. This will likely make some of you cringe but its the best way I know how to parse and check the spreadsheet data. I need an email alert to go out when the date of calibration is less than a specified cutoff date. I've tried a few configurations, some send emails for every item regardless of the date comparison and some send nothing such as the following script. Please help me make this work! I'm sure its mostly syntax or lack of specification for certain data structures.
'''
function checkCal(){
// Fetch the equipment name, calibration date, and today's date
var ss = SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheets/d/1xKzfW5vX3-eDWKuKmc3R_xin4FMFKhDHaDAd7vdoaLE/edit#gid=0');
SpreadsheetApp.setActiveSpreadsheet(ss);
var calibrationDateRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Equipment").getRange("H2:H12").getValues();
var vendList = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Equipment").getRange("C2:C12").getValues();
var modelList = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Equipment").getRange("D2:D12").getValues();
var cutoffDate = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Emails").getRange("D1").getValue();
var shipped = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Equipment").getRange("I2:I12").getValues();
var notes = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Equipment").getRange("J2:J12").getValues();
var sn = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Equipment").getRange("E2:E12").getValues();
var loc = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Equipment").getRange("A2:A12").getValues();
// Check date
for (var i = 0; i < calibrationDateRange.length; i++){
var calDate = Date(calibrationDateRange[i].getValue());
//console.log('comparing' + calDate + ' to ' + cutoffDate);
if (calDate.getTime() <= cutoffDate.getTime() + shipped[i] == false){
// Fetch the email address
var emailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Emails").getRange("B2");
var emailAddress = emailRange.getValue();
// Send Alert Email.
var message = 'Calibration due date is approaching for ' + vendList[i] + ' ' + modelList[i] + ' S/N: ' + sn[i] + ', located at ' + loc[i] + ', on ' + calDate[i] + '. Please reference the equipment spreadsheet to verify this date and the serial number of the referenced equipment. Note: ' + notes[i]; // body of email using associated variables
var subject = 'Equipment Calibration Alert';
MailApp.sendEmail(emailAddress, subject, message);
}
}
}
'''
Try it this way:
function checkCal() {
var ss = SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheets/d/1xKzfW5vX3-eDWKuKmc3R_xin4FMFKhDHaDAd7vdoaLE/edit#gid=0');
const msh = ss.getSheetByName("Emails");
const esh = ss.getSheetByName("Equipment");
const evs = esh.getRange(2, 1, esh.getLastRow() - 1, esh.getLastColumn()).getValues();
const codt = new Date(msh.getRange("D1").getValue());
const emailAddress = msh.getRange(msh.getRange("B2").getValue()).getValue();
evs.forEach((r, i) => {
let calDate = new Date(r[7]);
if (calDate.valueOf() <= codt.valueOf() && !r[8]) {
let message = 'Calibration due date is approaching for ' + r[2] + ' ' + modelList[i] + ' S/N: ' + r[4] + ', located at ' + r[0] + ', on ' + calDate + '. Please reference the equipment spreadsheet to verify this date and the serial number of the referenced equipment. Note: ' + r[9];
var subject = 'Equipment Calibration Alert';
MailApp.sendEmail(emailAddress, subject, message);
}
});
}
I am a high school teacher writing a Google Apps Script against Google Classroom. I want to create a spreadsheet like view of my students grades that my students can access with their credentials.
I have successfully written the code so that I can run it with my privileges by explicitly placing the student's Id in the code. Additionally, I have successfully written the code in Python where I can explicitly set just the two scopes a student needs to access this (and only this) information. However, Google Apps Scripts automatic scope generation has me stymied because I can't explicitly ask for only the 2 scopes I want.
Here are the two scopes that worked when I wrote it in python:
SCOPES = ['https://www.googleapis.com/auth/classroom.coursework.me.readonly https://www.googleapis.com/auth/classroom.student-submissions.me.readonly']
And here are the scopes that are automatically generated by Google Apps Scripts.
5 OAuth Scopes required by the script:
https://www.googleapis.com/auth/classroom.courses
https://www.googleapis.com/auth/classroom.coursework.students
https://www.googleapis.com/auth/classroom.profile.emails
https://www.googleapis.com/auth/classroom.profile.photos
https://www.googleapis.com/auth/classroom.rosters
Here is my code:
function doGet() {
return HtmlService.createHtmlOutputFromFile('Index');
}
function getMyGrades() {
var pageToken;
var studentSubmissionsArray = [];
//Get Student Submissions for the logged in student that is running this app
do {
var optionalArgs = {
'pageToken': pageToken,
'userId' : 'me',
};
var response = Classroom.Courses.CourseWork.StudentSubmissions.list(courseId='7131560586', courseWorkId='-', optionalArgs);
var studentSubmissions = response.studentSubmissions;
if (studentSubmissions && studentSubmissions.length > 0) {
for (i = 0; i < studentSubmissions.length; i++) {
var studentSubmission = studentSubmissions[i];
var courseworkResponse = Classroom.Courses.CourseWork.get(courseId = '7131560586', id = studentSubmission.courseWorkId)
var studentSubmissionArray = [courseworkResponse.title, courseworkResponse.maxPoints];
studentSubmissionArray.push(studentSubmission.assignedGrade, studentSubmission.courseWorkType, studentSubmission.late, studentSubmission.state, studentSubmission.courseWorkId);
studentSubmissionsArray.push(studentSubmissionArray);
}
} else {
studentSubmissionsTable = "No Students Found";
}
pageToken = response.nextPageToken;
} while (pageToken);
studentSubmissionsArray.sort()
var studentSubmissionsTable = "<table border = 1, cellpadding = 8><tr><th>#</th><th>Title</th><th>Max Points</th><th>Assigned Grade</th><th>Type</th><th>late</th><th>State</th><th>Coursework ID</th></tr>"
if (studentSubmissionsArray && studentSubmissionsArray.length > 0) {
for (i = 0; i < studentSubmissionsArray.length; i++) {
c = i + 1;
studentSubmissionArray = studentSubmissionsArray[i];
studentSubmissionsTable = studentSubmissionsTable + '<tr><td>'+c+'</td><td>'+ studentSubmissionArray[0] + '</td><td>' + studentSubmissionArray[1] + '</td><td>' + studentSubmissionArray[2] + '</td><td>' + studentSubmissionArray[3] + '</td><td>' + studentSubmissionArray[4] + '</td><td>' + studentSubmissionArray[5] + '</td><td>' + studentSubmissionArray[6] + '</td></tr>'
}
studentSubmissionsTable = studentSubmissionsTable + '</table>'
}
return studentSubmissionsTable
}
...because I can't explicitly ask for only the 2 scopes I want.
For this, please navigate to View > Show project manifest from your Apps Script editor -
...and then specify the auth scopes as required. You may find more information about the same here and refer to the entire manifest structure as needed. Hope this helps.
EDIT#2:
I've decided to try and batch the copy-paste operations that run after all the emails are fired. In previous posted code, in the last lines of every loop, I copied and pasted the items from each purchase order ("Email" sheet) to another ("AsignRec"). Now, what I want to do is store the items from "Email" sheet in every loop to a Javascript array, and paste all together into "AsignRec" at the end, just once.
However, I'm still not doing it right. I'm stuck at the final pasting/setValues(). I believe the array is correctly formed, as it has a length of 49, which is the number of unique SKUs to be sent out to suppliers. Still, at the setValues([OCitems]) line 185, I get the error "Incorrect range height, was 1 but should be 49 (line 185, file "TestArrayMultiple4")".
I assume this means the destination/output range is not the same size as array/input (called OCitems). I don't see why though, since I defined the length of the output range using OCitems.length. I am missing something, and not sure what.
This is the important bit of the code, and full code below. Same GDocs link as before, script file TestArrayMultiple4, lines 160-185.
https://docs.google.com/spreadsheets/d/1yzvMTh0VYhRhiexNzQPIjTwz1FCMq4XnbpGvCF1FYu8/edit#gid=436022027
/// Get Range we want to change to creating Javascript array and paste at end only
var OcNoHeader = sheet.getRange("B9:J" + MaxTableRow).getValues(); // get items to send to supplier from "Email Sheet"
// if supplier number is 1, create array "OCitems" by storing OcNoHeader. If not supplier #1, then append to existing array "OCitems"
if(y == 1){
var OCitems = OcNoHeader}
else
{for(j=0;j<OcNoHeader.length;j++){
OCitems.push(OcNoHeader[j]);
}}
Logger.log(OCitems);
Logger.log("OCitems length = " + OCitems.length)
debugger;
i++; // after firing email, y+1 to go to next supplier
Logger.log("i++ IF =" + 1);
Logger.log("new i ELSE =" + 1)
debugger;
} // only do while x = max number of suppliers reached
while (i < x);
sheet3.getRange(3, 3, OCitems.length, 9).setValues([OCitems]); // paste operation, NumRows set equal to length of array
=========================
EDIT#1: Worked on improving performance using getValues of several cells instead of doing individual getValue() several times over. Unfortunately, this hasn't reduced the execution time in a stable or noticeable manner (sometimes it finished before 6min, sometimes not).
Posting code below (you can access it in script file "TestArrayMultiple2" in new Sheet shared below):
Using Execution transcript, I see that although the execution time of the getValue() lines that were previously taking very long has basically been reduced to zero, other lines of code are now taking more time and killing the gain achieved from the batching other getValue().
There are still 4-5 "individual" getValue() on specific cells (much less than before), but I don't understand why they would take so long. So it seems even if I removed the remaining "individual" getValue(), if only one remained, it would take even longer.
Seems to me it has something to do with caching (and I'm sure I don't fully understand this concept), for the following reasons:
1) It is always the first getValue() in the loop which takes the longest.
2) I tried to go down a different route, by changing the code for the copy/paste operation which occurrs after all emails are sent (line 150 in "TestArrayMultiple2" script file). I basically try to create an array which gets fed with more data at every loop (append/push method) but doesn't paste within every loop - the idea is to paste all the data at the end, after finishing looping. I still don't have it right (this second script file is the last one, "TestArrayMultiple3"), but I can see the emails get fired off much faster.
Once again, your help would be much appreciated.
> // version with 1 getDataRange array which stores for supplier ID, name, email, MaxTableRow for PO email, email subject all from Dashboard sheet
function TestArrayMultiple2() {
var ss = SpreadsheetApp.getActiveSpreadsheet ();
var sheet = ss.getSheetByName("Email");
var sheet1 = ss.getSheetByName("Pedido email");
var sheet2 = ss.getSheetByName("Dashboard");
var sheet3 = ss.getSheetByName("AsignRec");
var sheet4 = ss.getSheetByName("ListadoProductos");
var sheet5 = ss.getSheetByName("Registro-Consolid");
var sheet6 = ss.getSheetByName("Registro-Unico");
var x = ss.getSheetByName("Dashboard").getRange("C4").getValue();
Logger.log("x = " + x)
var offsetV = 4; // number of rows of offset for email status to be inserted in Dashboard sheet
var OffSetColProv = 1; // column in Dashboard sheet with supplier name
var OffsetColMaxPOrows = 3; // Number of unique SKUs or rows in PO. Replace MaxTableRow formula in Email Sheet
var OffSetColPzas = 4; // column in Dashboard sheet with number of items in supplier purchase order.
var OffSetColEmail = 5; // column in Dashboard sheet with supplier email
var OffSetColCC = 13; // column in Dashboard sheet with supplier email CC
var OffSetColSubject = 14; // column in Dashboard sheet with supplier email Subject
var colStatus = 11; // column in Dashboard sheet where send status of email inserted -----> LEAVE AS IS FOR NOW, not an offset, is fixed, col. K = 11
var OffsetEmailRows = 8 // number of rows in Email sheet before the items in PO are shown
var ProvNumEmail = sheet.getRange(1,2); // Supplier number in email sheet used to refresh products in purchase order email via FILTER formula
var StatusRange = sheet2.getRange("K5:K100");
Logger.log("StatusRange = " + StatusRange)
var currentTime = new Date();
var timestamp = Utilities.formatDate(currentTime,'GMT-0600','dd/MM/yyyy HH:mm:ss');
Logger.log("timestamp = " + timestamp);
var ProvArray = sheet2.getRange("E5:S100");
var DashValues = ProvArray.getValues();
i = 0;
do {
var y = DashValues[i][0];
Logger.log("y = " + y)
ProvNumEmail.setValue(y); // set value of next supplier in Email sheet to load next purchase order products
// emails var here in order to update email value in IF email = ERROR condition and skip to else
var Prov = DashValues[i][OffSetColProv];
Logger.log("Prov = " + Prov);
var EmailSubject = DashValues[i][OffSetColSubject];
Logger.log("EmailSubject = " + EmailSubject)
var MaxTableRow = DashValues[i][OffsetColMaxPOrows] + OffsetEmailRows;
Logger.log("MaxTableRow = " + MaxTableRow)
var EmailTo = DashValues[i][OffSetColEmail];
Logger.log("EmailTo = " + EmailTo)
var EmailCC = DashValues[i][OffSetColCC];
Logger.log("EmailCC = " + EmailCC)
var Piezas = DashValues[i][OffSetColPzas];
Logger.log("Piezas = " + Piezas)
SpreadsheetApp.flush();
var name = "Petsy Compras - Juan Carlos León";
var ReplyToEmail = "compras#petsy.mx";
var email = EmailTo;
var subject = EmailSubject;
var name = name;
var replyTo = ReplyToEmail;
var Emailcc = EmailCC;
var schedRange = sheet.getRange("B7:J"+MaxTableRow);
var body = '<div>';
body += "Estimados," +'<br>' + '<br>';
body += "Envío la orden de compra, por un total de " + '<b>' + Piezas + " piezas." + '</b>' +'<br>' + '<br>';
body += "Favor de confirmar las existencias lo más rápidamente posible, dentro del mismo correo y"+ '<b><a style="color:#FF0000">'+ " enviar factura a: "+ '</a></b>' + "facturasproveedores#petsy.mx." +'<br>' + '<br>';
body += "Al dar " +'<b><a style="color:#FF0000"> '+ "RESPONDER A TODOS" + '</a></b>' +" la tabla con los productos pedidos se hace editable: favor de marcar por cada item si será faltante." +'<br>' + '<br>';
body += "Cualquier duda avísenme por favor." +'<br>' + '<br>';
body += "Un saludo" +'<br>' + '<br>';
body += '<b>' + "Juan Carlos León" + '<b>' + '<br>';
body += "Petsy Compras"+'<br>';
body += "Mapa aquí: "+'<br>';
body += "Fijo directo 1: (55) 68 12 07 97 / Fijo directo 2: (55) 68 12 07 99 / Cel y Whatsapp: 55 32 23 57 17"+'<br>' + '<br>';
body += "" +'<br>' + '<br>';
body += getHtmlTable(schedRange);
body += '</div>';
// variables for error email
var emailERR = 'oscialom#petsy.mx'
var subjectERR = 'ERROR ENVIO OC' + ' // ' + Prov + ' ' + timestamp
if(email == 'ERROR' || MaxTableRow == 0) // skip condition to go begin loop with y+1
{
// if above skip condition is true, y+1 to move to next purchase order
Logger.log("y = " + y);
Logger.log("IF");
sheet2.getRange(y + offsetV,colStatus).setValue('NOT_SENT'); // set email send status next to supplier in Dashboard sheet
{
GmailApp.sendEmail(emailERR, subjectERR, "Requires HTML",
{
'name':name,
'replyTo':replyTo,
'htmlBody':'',
'cc':''});
}
i++;
Logger.log("new i IF =" + 1);
continue
}
else
{
// if skip condition is false, fire current supplier purchase order email email
Logger.log("i = " + i);
Logger.log("y = " + y);
Logger.log("ELSE");
GmailApp.sendEmail(email, subject, "Requires HTML",
{
'name':name,
'replyTo':replyTo,
'htmlBody':body,
'cc':EmailCC});
}
sheet2.getRange(y + offsetV,colStatus).setValue('OK'); // set email send status next to supplier in Dashboard sheet
// START copy-paste Asign-Rec
var MaxTableRowASIGN = sheet3.getRange("A1").getValue();
Logger.log("MaxTableRowASIGN = " + MaxTableRowASIGN)
/// Get Range we want to change to creating Javascript array and paste at end only
debugger; // stop debugger at this point !! REMOVE OR PLACE AT CORRECT LINE IF USING DEBUGGER
var OcNoHeader = sheet.getRange("B9:J" + MaxTableRow);
var ConsolAsignRec = sheet3.getRange("B3:K" + MaxTableRow);
var ProvOC = sheet.getRange("B2").getValue();
Logger.log("ProvOC = " + ProvOC)
var MaxRowB = sheet3.getRange("C1").getValue() + 1;
Logger.log("MaxRowB = " + MaxRowB);
var NextRowB = MaxRowB + 1;
Logger.log("NextRowB = " + NextRowB);
OcNoHeader.copyTo(sheet3.getRange(MaxTableRowASIGN + 1,3),{contentsOnly:true});
var NumRowsProv = OcNoHeader.getNumRows();
var ProvOCcolumn = sheet3.getRange(MaxRowB, 2, NumRowsProv)
Logger.log("ProvOCcolumn = " + ProvOCcolumn);
ProvOCcolumn.setValue(ProvOC);
// END copy-paste Asign-Rec
i++; // after firing email, y+1 to go to next supplier
Logger.log("i++ IF =" + 1);
Logger.log("new i ELSE =" + 1)
} // only do while x = max number of suppliers reached
while (y<x);
// set y = 1 to reset value again after finishing loop
sheet.getRange(1,2).setValue(1); // reset ProvNumber = 1 to start again next time script is fired.
var EmailsSent = sheet2.getRange("C10").getValue(); // set values
Logger.log("EmailsSent = " + EmailsSent)
var EmailErrors = sheet2.getRange("C11").getValue();
Logger.log("EmailErrors = " + EmailErrors)
var MaxTableRowEND = sheet2.getRange("C9").getValue();
var schedRange = sheet2.getRange("E4:K" + MaxTableRowEND);
var emailEND = "oscialom#petsy.mx";
var subjectEND = 'OCs Inbound enviadas' + ' ' + timestamp + " (errores " + EmailErrors + " / enviados " + EmailsSent + ")";
var EmailCCEND = "";
var bodyEND = getHtmlTable(schedRange);
GmailApp.sendEmail(emailEND, subjectEND, "Requires HTML",
{
'name':name,
'replyTo':replyTo,
'htmlBody':bodyEND,
'cc':EmailCCEND});
StatusRange.clearContent();
/// START RecordTimestamp code
var Avals = sheet4.getRange("A1:A").getValues();
var lastrow1 = Avals.filter(String).length;
Logger.log('lastrow1 =' + lastrow1)
var Avals2 = sheet5.getRange("A1:A").getValues();
var lastrow2 = Avals2.filter(String).length;
Logger.log('lastrow2 =' + lastrow2)
sheet4.getRange("B2:B" + lastrow1).copyTo(sheet5.getRange(lastrow2 + 1, 1)) // copy order-items to Registro sheet, after last filled row
sheet4.getRange("K2:K" + lastrow1).copyTo(sheet5.getRange(lastrow2 + 1, 2)) // copy Prov1 to Registro sheet, after last filled row
var Avals3 = sheet5.getRange("C1:C").getValues();
var lastrow2c = Avals3.filter(String).length;
Logger.log('lastrow2c =' + lastrow2c);
if(lastrow2 == 1)
{ sheet5.getRange(lastrow2c + 1, 3, lastrow1 - 1).setValue(timestamp)
Logger.log('IF')
}
else
{
sheet5.getRange(lastrow2c + 1, 3, lastrow1 - 1).setValue(timestamp)
Logger.log('ELSE')
}
var MaxTableRowEMAIL = sheet6.getRange("G5").getValue()
var subject = "Items pedidos en OC automatizada " + timestamp
var email = "oscialom#petsy.mx";
var EmailCC = "";
var EmailBCC;
var name = "Petsy Compras";
var ReplyToEmail = "compras#petsy.mx"
var schedRange = sheet6.getRange("A1:C" + MaxTableRowEMAIL);
var body = getHtmlTable(schedRange);
{
GmailApp.sendEmail(email, subject, "Requires HTML",
{
'name':name,
'replyTo':ReplyToEmail,
'htmlBody':body,
'cc':''});
}
/// END RecordTimestamp code
Logger.log("MaxTableRowASIGN " + MaxTableRowASIGN);
var endtime = new Date();
Logger.log("timestamp end " + timestamp);
Logger.log("endtime " + Utilities.formatDate(endtime,'GMT-0600','dd/MM/yyyy HH:mm:ss'));
var scripttime = (endtime - currentTime);
Logger.log("scripttime original" + scripttime);
// strip the ms
scripttime /= 1000;
Logger.log("scripttime / 1000" + scripttime);
// get seconds (Original had 'round' which incorrectly counts 0:28, 0:29, 1:30 ... 1:59, 1:0)
var seconds = Math.round(scripttime % 60);
Logger.log("scripttime % 60" + scripttime);
// remove seconds from the date
scripttime = Math.floor(scripttime / 60);
Logger.log("scripttime / 60" + scripttime);
// Browser.msgBox("Script completado en " + seconds + " segundos",Browser.Buttons.OK_CANCEL); // removed MsgBox to measure real execution time
Logger.log("seconds " + seconds)
}
=====================
ORIGINAL POST
I wrote a Google script to automate the purchase order process to several suppliers. The process takes a list of products (from sheet ListaProductos), formats the product info into an email format (sheet "Email"), sends the emails, and does some copy/pasting into other sheets of the same spreadsheet. However, I always run into the execution time at around 75% of the script. I'm fairly new to this, have been reading up but frankly don't know what to try next.
I see the problem in this part of code:
do {
// read info from the sheet
range.getValue();
// more code here...
} // only do while x = max number of suppliers reached
while (y<x)
Operation getValue takes much time to run. Best practice is to use the whole range:
var data = sheet.getDataRange().getValuses();
and then use data as source for further calculations.
See more info here:
https://developers.google.com/apps-script/best_practices
Right now I am trying to set up a function that could help my colleague to update the stocking. I need a program that as soon as the stock update is done, an email of stock notice will be sent to manager.
I tried to run the program without trigger and I received a perfect email for stocking, but now I need to apply a trigger to the program so when my colleague update stock she does not need to run the program to send the email.
I just want to know how could I get a better way to trigger the email sending instead of every update it trigger the program running and sending multiple emails.Also please give me some idea about how to set range 'e' and how the trigger works
Thanks
here is the code
function onEdit(e) {
//if (activeSheet.getName() !== "inventory") return;
//var sheet = SpreadsheetApp.getActiveSheet();
var sheet = e.source;
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var workRows = numRows -8;
var dataRange = sheet.getRange(8,2, workRows, 13)
// Fetch values for each row in the Range.
var data = dataRange.getValues();
//email information and content
var emailAddress = 'xxx#gmail.com';
var bodyrefill = '';
var bodyset = '';
var bodysend = '';
var subject = '';
var check1 = 0;
var check2 = 0;
/*
var range = e.range;
var columnOfCellEdited = range.getColumn();
if (columnOfCellEdited==14){
columnofCellEdited.setNote('Last modified: ' + new Date());
}
*/
for (var row in data){
if(data[row][12]!=0){
if (data[row][11]<=data[row][12]){
check1 += 1;
var num = data[row][12]-data[row][11];
bodyrefill = bodyrefill + "The " + row + " line, " + data[row][2] + " needs refill product for" + num + "\n";
}
}
else{
check2 += 1;
bodyset = bodyset + "The " + row + " line, product " + data[row][2] + " needs to set up a product limit." + "\n";
}
}
if (check1 > 0){
if (check2 > 0){
subject = "Google sheet has products need to refill and set up alart limit.";
bodysend = bodyrefill + bodyset;
}
else{
subject = "Google sheet has products need to refill.";
bodysend = bodyrefill;
}
}
else{
subject = "Google sheet has products need to set up alart limit.";
bodysend = bodyset;
}
MailApp.sendEmail(emailAddress, subject, bodysend);
SpreadsheetApp.flush();
}
I searched and tried to use what I could find, but I'm stuck. I am attempting to create a script that scans a Google sheet (with about 30 sheets) for values that are newly inputted and are below 60%. I am then looking to have the script e-mail me the data that is in the 2nd column (Student's name), 4th column (teacher's name) of the current row of the data as well as the percentage (test score)that was below 60% (ex. Clarke,John Mrs. Brown 56.64%) . I am new to Java script but have been trying to learn.
Here is the script that I have so far. It finds the correct sheet without a problem. It will also email me as well (with the wrong message, but it works none the less). But there are certain things that I am stuck on. Like I know that I have to set up some sort of onEdit trigger so that it only sends the newly inputted score, but I don't know where to put it or how to do that. I also know that the dataRange code that I have seems wrong to me and the if statement also seems wrong. I am not sure how to specify "below 60%".
I am a technology teacher and set up a massive data system for our school using Google Drive. I want to have the master data sheet automatically email my principal whenever a student needs help on a specific assessment. Any help you could give me would be much appreciated!
Thank you guys so much for all of your time I love spending my time on here learning from everyone.
Brandon
Here is a link to an example of the data spreadsheet we use
Choppy Script Below
function sendEmail() {
var spreadsheet = SpreadsheetApp.openById('spreadsheet ID');
/// The ID of the data spreadshfeet
var sheet = spreadsheet.getSheets(); // gets all sheets
var startRow = 3; // Third row of data to process
var numRows = 40; // Number of rows to process
var dataRange = sheet.getRange(startRow, 10, numRows, 50); // Start row 3 column 10, and stop row 40, column 50
// Fetch values for each row in the Range.
var data = dataRange.getValues();
//Browser.msgBox(data)
for (i in data) {
var row = data[i];
if (dataRange.getValues() <= .60); {
var emailAddress = "my email";
var message = row[2]; // Second column
var subject = "ALERT - Assessment score below 60% inputted.";
MailApp.sendEmail(emailAddress, subject, message);
// Browser.msgBox(emailAddress)
}
}
}
function emailAlert(e) {
var range = e.range;
if (range.getColumn() >= 10) { // Only check column I and up
var editedSheet = e.source.getActiveSheet();
var editedRow = range.getRow();
var value = range.getValue();
if (value !== 'undefined') {
if (value < 0.6) {
var studentData = editedSheet.getRange(editedRow, 1, 1, 9).getValues();
Logger.log(
'StudentId: ' + studentData[0][0] +
'\n Name: ' + studentData[0][1] +
'\n HR: ' + studentData[0][2] +
'\n Teacher: ' + studentData[0][3] +
'\n Grade: ' + studentData[0][4] +
'\n Race: ' + studentData[0][5] +
'\n G: ' + studentData[0][6] +
'\n Ed: ' + studentData[0][7] +
'\n AVG: ' + studentData[0][8]);
var emailAddress = "brandon.mause#sleschool.org";
var message = Test
var subject = "ALERT - Assessment score below 60% inputted.";
MailApp.sendEmail(emailAddress, subject, message);
// Send email..
}
}
}
}
I added the new code above with the installable trigger active. I still can't get the email function to work properly. Any Ideas?
Thanks...
It sounds like your problem can be solved using the onEdit trigger. However, since you want to send an email you'll need to add the trigger manually because the function requires authorization to send emails.
Try this:
function assessmentOnEdit(e) {
var range = e.range;
if (range.getColumn() >= 10) { // Only check column I and up
var editedSheet = e.source.getActiveSheet();
var editedRow = range.getRow();
var value = range.getValue();
if (typeof value === 'number') {
if (value < 0.6) {
var studentData = editedSheet.getRange(editedRow, 1, 1, 9).getValues();
var message = 'Assessment score: ' + Math.round((value * 100) * 10) / 10 + ' %' +
'\nStudentId: ' + studentData[0][0] +
'\nName: ' + studentData[0][1] +
'\nHR: ' + studentData[0][2] +
'\nTeacher: ' + studentData[0][3] +
'\nGrade: ' + studentData[0][4] +
'\nRace: ' + studentData[0][5] +
'\nG: ' + studentData[0][6] +
'\nEd: ' + studentData[0][7] +
'\nAVG: ' + Math.round((studentData[0][8] * 100) * 10) / 10 + ' %';
var emailAddress = 'john.doe#example.com';
var subject = 'ALERT - Assessment score below 60% inputted.';
MailApp.sendEmail(emailAddress, subject, message);
}
}
}
}
Check the google referance pages for more info on triggers: https://developers.google.com/apps-script/guides/triggers/events
EDIT
Since you want to use IMPORTRANGE the onEdit trigger will not fire. Try using the function below and use this function with onChange instead of onEdit.
function assessmentOnChange(e) {
var editedSheet = e.source.getActiveSheet();
var scriptProperties = PropertiesService.getScriptProperties();
var propertyKey = 'currentColumn';
var currentColumn = scriptProperties.getProperty(propertyKey);
if (currentColumn === null) {
currentColumn = 14; //Fisrt Empty column is N
}
var table = editedSheet.getRange(3, Number(currentColumn), editedSheet.getLastRow(), editedSheet.getLastColumn()).getValues(),
rowNumber = 3,
valuesToProcess = [];
table.forEach(function(row) {
var column = 0;
row.forEach(function(cell) {
if (typeof cell === 'number') {
valuesToProcess.push({
value: cell,
row: rowNumber,
column: Number(currentColumn) + column
})
}
column++;
});
rowNumber++;
});
var maxColumn = Number(currentColumn);
for (var i in valuesToProcess) {
assessmentOnEdit({
source: e.source,
range: {
getRow: function() {
return valuesToProcess[i].row
},
getValue: function() {
return valuesToProcess[i].value
},
getColumn: function() {
return valuesToProcess[i].column
}
}
});
if (valuesToProcess[i].column > maxColumn) {
maxColumn = valuesToProcess[i].column;
}
}
scriptProperties.setProperty(propertyKey, maxColumn + 1);
}