Send email based on cell change in google sheets - google-sheets-api

Please help me with a script which sends an email on change in a value of a cell, for example if the value is less than 1 in column F then it has to pick up the email id from column g and text from h (here the body of the text is same for all the mails) and send an email. So each time the value goes below 1 it has to send an email for the relevant row and it should not repeat the mail for the previous row where it has already been sent since that value of less than 1 will not change once the email has been sent.

First you will need to write a Script under "Tools->Script Editor"
The script will look something like this:
function sendEmail() {
// return all data in active spreadsheet
var values = SpreadsheetApp.getActiveSheet().getDataRange().getValues()
for (i in values.length) { //iterate over each row
// get value of column 6 (aka F) for this row
// this is your conditional cell for emailing
var data= values[i][6];
// get email address from column g (aka G)
var emailAddress = values[i][7];
var subject = "Value Less Than One"
var message = values[i][8];
// if data cell is less than one, send email for this row
if (data < 1) ;{
MailApp.sendEmail(emailAddress, subject, message);
}
}
}
Then in the Script Editor you will need to add a trigger in Resources->Current project's triggers that triggers sendEmail() "on change".
You can find more about Google Scripting in their documentation or in other SO questions:
Send Email when value changes in Google Spreadsheet
email notification if cell is changed

Related

Getting individual question scores(numbers) from Google forms to a google spreadsheet

I have a google form which is basically an assessment for students. Each question carries 1 point. When I connect my form to a specific spreadsheet, I get the total score of the student e.g 24/30
What I want to do:
Along with the total score, we want to get each question's score to go to the spreadsheet. Here is what we are trying to have:
I have no idea what to do. Please guide. Thanks
You can try with Apps Script. I tested the following script that can give you an idea on how to achieve this.
const TARGET_SPREADSHEET_ID = "spreadsheetID"; //Change according to your needs
const TARGET_SHEET_NAME = "sheetName"; //Change according to your needs
const SOURCE_FORM_ID = "formID"; //Change according to your needs
//Run this function only one time, this will create a trigger that will run function "onFormSubmitTrigger" whenever a student submits a response.
function installOnFormSubmitTrigger() {
const form = FormApp.openById(SOURCE_FORM_ID);
ScriptApp.newTrigger("onFormSubmitTrigger")
.forForm(form)
.onFormSubmit()
.create();
}
function onFormSubmitTrigger(e) {
const targetSpreadsheet = SpreadsheetApp.openById(TARGET_SPREADSHEET_ID);
const targetSheet = targetSpreadsheet.getSheetByName(TARGET_SHEET_NAME);
//GETTING RESPONSES
var itemResponses = e.response.getItemResponses();
var responses = itemResponses.map(itemResponse => itemResponse.getResponse()); //Get responses
var name = responses.splice(0,1); //extracting first element ("Full Name") of responses
//GETTING SCORES
var itemScores = e.response.getGradableItemResponses();
var scores = itemScores.map(itemScore => itemScore.getScore()); // Get response score
scores.splice(0,1); //removing score for the first element ("Full Name")
var newArr = name.concat(scores); //combining name and scores in one array
//newArr.splice (1,0,""); //you can add this line to insert blank between "Student Name" and "Q1"
targetSheet.appendRow(newArr); //Append scores to the sheet
}
Test the script by submitting a form and the target sheet should show students names and the score for each question they answered. If you have any questions let me know.

Time stamp a cell when multiple columns are updated

I have been checking out multiple codes on trying to update my Google Spreadsheet but have been unsuccessful when trying to do this with multiple cells. On my spreadsheet I have multiple tabs and when I update a row in column 2,3 or 4, I would like it to enter the date in column 5.
Thank you for your help.
Step 1.
In the Google Spreadsheet, click on "Script editor..." under the "Tools" menu.
Step 2.
Remove any sample script that might be in there and paste the following ...
// Sets the targetColumn on the edited row to the current date if the
// edited column in within columnBounds.
// Note: This will only handle single cell editing.
// Columns that need to be monitored for changes. Use CAPITAL letters.
var monitoredColumns = ['B', 'C', 'D'];
// Colum that will receive the date.
var targetColumn = 'E'
// To avoid adding the date in the title row, we need to consider the starting row.
var startingRow = 4
// onEdit() is a reserved function name that will be called every time the sheet will be edited.
function onEdit(e) {
var range = e.range;
// Row of the edited cell.
var row = range.getRow();
// Column of the edited cell.
var col = String.fromCharCode(64 + range.getColumn());
if (row < startingRow) {
// None of the monitored rows have been edited.
return;
}
if (monitoredColumns.indexOf(col) < 0) {
// Column B, C or D (2, 3 or 4) was not modified.
// Do not proceed any further.
return;
}
// Current spreadsheet.
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
// Date cell.
var dateCell = sheet.getRange(targetColumn + range.getRow());
// Set it to the current date.
dateCell.setValue(new Date());
}
Step 3
Adjust the values of monitoredColumns, targetColumn and startingRow
Step 4
Start entering some content in the cells.

Need a more efficient solution than looping

I am building a spreadsheet that tracks work in progress as it moves through steps of a manufacturing process.
Each step of the process has a column with the total parts moved to each stage. To the left of this column is a column for number of parts moved to the stage (parts move through a few at a time).
My scrpit then takes the values in the "add" column, adds them to the "total" column, then reset the "add" column to "".
Here's the code:
function addColumns() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
// ss is now the spreadsheet the script is associated with
var sheet = ss.getSheets()[0]; // sheets are counted starting from 0
// sheet is the first worksheet in the spreadsheet
for (var i=4; i<500; i++ ) {
if(sheet.getRange(i,1).getValue()>0){ //Only run if order number not empty
//Breakout Column
var add = sheet.getRange(i,6);
var total = sheet.getRange(i,7);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
//CNC Column
var add = sheet.getRange(i,8);
var total = sheet.getRange(i,9);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
//CutSand Column
var add = sheet.getRange(i,10);
var total = sheet.getRange(i,11);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
//Lasered Column
var add = sheet.getRange(i,12);
var total = sheet.getRange(i,13);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
//To Finishing Column
var add = sheet.getRange(i,14);
var total = sheet.getRange(i,15);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
// Defective Column
var add = sheet.getRange(i,17);
var total = sheet.getRange(i,18);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
//Etsy Column
var add = sheet.getRange(i,20);
var total = sheet.getRange(i,21);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
}
if(sheet.getRange(i,4).getValue()<1){i=500} //Once you find a blank order exit the loop
}
}
My code as written does accomplish this; it does exactly what I need. The problem is that since the code is accessing the spreadsheet on each loop it takes almost a full second per cell to run, and with 7 steps per order it can take minutes at a time to run through with lots of orders...
This is a pretty simple mathematical task, so there has to be a more efficient way of doing it, I just haven't been able to find the right keywords to describe what I need to do.
I am quite happy to learn whatever needs to be done, just need to know what direction to head.
Thanks in advance!
I would suggest to do something like this: (not tested)
function addColumns() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0]; // Refers to the first worksheet in the spreadsheet
var data = sheet.getDataRange().getValues(); // Acquires all values of the sheet
for (var i = 3; i < data.length; i++) { // Loop over every row
if (data[i][0].length > 0) { // Check if first column has a value
// Breakout
sheet.getRange(i+1,7).setValue(parseFloat(data[i][6]) + parseFloat(data[i][5]));
sheet.getRange(i+1,6).clear();
// Repeat code above for other columns
}
}
}
This code acquires all the data from the sheet instead of looping over a fixed amount of 500 rows. Assuming that your data starts at row 4, I've implemented this in the code above as well.
Variable data acquires all the data at one moment instead of trying to fetch values of every range (cell) all the time. I expect that this will save your script quite some time.
Because we acquire the data at once, the script sees the value as a string. Before we calculate the new value of the total column, we parse the value as a float (a number with decimals).
The code above is not tested as I don't have a sheet ready in the same format as you do but I think the logic is clear and if it doesn't work I suppose you should be able to adjust it to work for your sheet.

Incrementing a value saved in Google Sheets Cache Service

I'm trying to store a value using google cache services but it doesn't seem to increment like I want it to. Here's the code -
//Starts a new instance of cache
var cache = CacheService.getScriptCache();
//Puts the value 2 into foo key of Cache
cache.put('foo', 2);
//Grabs that value
var startMessageRow = cache.get('foo');
//stuff for an email I'm sending using this cached value
var messageDataRange = sheet.getRange(startMessageRow, 2)
var message = messageDataRange.getValues();
//Here's where I'm trying to increment it, but the value is staying at 2
cache.put('foo','startMessageRow'+1);
That last line of code is where I'm trying to increment the value by one each time this script runs, however the value is just stuck at two no matter what I try.
I found an easier way to do this. I just put a counter in a random cell and then I just add one to that value every time my script runs. Way easier than using the cache services!
var cellRange = s1.getRange("W726"); //set this string to be the corresponding cell for daily counter
var cell = cellRange.getValues(); //grabs the value from that cell
var startMessageRow = cell; //sets StartMessage Row to be equal to the value of counter
startMessageRow = parseInt(startMessageRow);//ints it
cell = parseInt(cell); //ints it
cell+=1; //increments
cell = cell+""; // strings it
cellRange.setValue(cell); // puts the new value in

How to get first row from Google Spreadsheets Data API

Here is an example: https://spreadsheets.google.com/feeds/list/1a2JzZzUjSIcpROgR5v_M-UmWyT-iokcegIxHJOopZWA/od6/public/full?alt=json
The returned JSON data doesn't contain the first row from the spreadsheet.
You can view the contents of the spreadsheet in HTML (https://docs.google.com/spreadsheets/d/1a2JzZzUjSIcpROgR5v_M-UmWyT-iokcegIxHJOopZWA/pubhtml) to verify that "first row" exists in the sheet.
How can I get the first row from the spreadsheet? There is a "openSearch$startIndex" = 1 in the returned JSON. Maybe if you could set this to 0, I could get the first row also.
1st Row - Cell feed
https://spreadsheets.google.com/feeds/cells/key/worksheetId/private/full?min-row=1&min-col=1&max-col=4
Docs: https://developers.google.com/google-apps/spreadsheets/#fetching_specific_rows_or_columns
Cell feed is better for almost everything. But append a data row is better in list feed, I think.
In C# you would do it like this:
var link = worksheet.Links.FindService(GDataSpreadsheetsNameTable.CellRel, null).HRef.ToString();
var cellQuery = new CellQuery(link)
{
MinimumRow = 1,
MaximumRow = 1,
MinimumColumn = 1
};
CellFeed header = spreadsheetService.Query(cellQuery);