I need the apps script to scan each column each day on incremental basis. If it scans one column today say A column, next day the next column B should be scanned.
To trigger the function, daily timer is used for a specific time in a day.
The below code scans all the columns at a time.
The problem here is, the apps script is triggered daily one time. So it starts from beginning. How to store which column it had scanned yesterday and increment it for today.
function AutoSend() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var list= new Array();
for(var j= 2; j<=29;j++){ // for Column
for(var i=5;i<=23;i++){ // for row
var value = sheet.getRange(i, j).getValue();
if(value !== "OFF"){ if(value !== "L") { if(value !== "COMP OFF") {
var EmpName= sheet.getRange(i,1).getValue();
list.push(EmpName);
}}}
}
}
Logger.log(list);
var subject= "sub";
var message = list;
MailApp.sendEmail('example#gmail.com',subject, message);
}
You'll probably need to store data in the document properties.
PropertiesService.getDocumentProperties().setProperty(key, value);
You may want to use the date as a string for the key, and the column number as the value.
var theColumn = 9; //To Do - get the correct column number
var todaysDate = new Date().toString();
Logger.log('todaysDate: ' + todaysDate);
todaysDate = todaysDate.slice(0,15); //Remove time off the end
Logger.log('todaysDate: ' + todaysDate);
PropertiesService.getDocumentProperties().setProperty(todaysDate, theColumn);
var theColumnForTheDate = PropertiesService.getDocumentProperties()
.getProperty(todaysDate);
Logger.log('theColumnForTheDate: ' + theColumnForTheDate);
Related
I have a model in Google Sheets that is set up with one column per day. It contains both actuals and forecasts, and every day I need to roll forward formulas to replace forecasts with actuals. I can't roll forward the whole column, only a segment of it (there are reference numbers above and below that shouldn't be changed).
I have tried to write a script to do this for me every day, but I don't know how to make getRange reference a dynamic range. This is my attempt:
function rollColumn() {
var ss2 = SpreadsheetApp.openById('<ID redacted>');
ss2.getRange("=index(Model!$7:$7,,match(today()-2,Model!$4:$4,0)):index(Model!$168:$168,,match(today()-2,Model!$4:$4,0))").copyTo(ss2.getRange("=index(Model!$7:$7,,match(today()-1,Model!$4:$4,0)):index(Model!$168:$168,,match(today()-1,Model!$4:$4,0))"))
};
The INDEX formulas work insofar as they reference the relevant ranges (I have tested them in the spreadsheet). But clearly getRange doesn't accept formulas as an input. It also seems that Google Sheets doesn't allow for a named range to be created with formulas (which is how I would solve this in Excel).
Can someone help me recreate this functionality with GAS?
This is the closest existing question I've found on Stack Overflow, but I haven't been able to make it work:
Google Apps Script performing Index & Match function between two separate Google Sheets
Thank you!
You should add {contentsOnly:false} parameter to your code. something like this:
TemplateSheet.getRange("S2:T2").copyTo(DestSheet.getRange("S2:T"+LRow2+""), {contentsOnly:false});
Getting a date from column's title, then pasting formulas to the row to the right:
// note: we assume that sheet is disposed as in the following document: https://docs.google.com/spreadsheets/d/1BU2rhAZGOLYgzgSAdEz4fJkxEcPRpwl_TZ1SR5F0y08/edit?ts=5a32fcc5#gid=0
function find_3formulas() {
var sheet = SpreadsheetApp.getActiveSheet(),
leftTitle, // this variable will stay unused because we do not need a vertical index
topTitle = todayMinus_xDays(2),
topTitlesRange = sheet.getRange("G3:T3"),
leftTitlesRange = sheet.getRange("A4:A8"); // this range will stay unused.
var coor = findCoordinates(leftTitlesRange, leftTitle, topTitlesRange, topTitle);
if (coor.row == null || coor.column == null) {
sheet.getRange("M12:M14").setFormula('="NULL: please check logs"');
return;
}
var rowAxis = 4 + coor.row;
var colAxis = 8 + coor.column;
var fromRange = sheet.getRange(rowAxis, colAxis, 3, 1);
var toRange = sheet.getRange(rowAxis, colAxis + 1, 3, 1);
Logger.log(fromRange.getA1Notation())
Logger.log(toRange.getA1Notation());
var threeFormulas = fromRange.getFormulas();
toRange.setFormulas(threeFormulas)
}
// unused in current script!
function findCoordinates(leftTitlesRange, leftTitle, topTitlesRange, topTitle) {
var formattedDate,
row = 0,
column = 0;
if (leftTitle) {
row = findRow(leftTitlesRange, leftTitle);
}
if (topTitle) {
column = findColumn(topTitlesRange, topTitle);
}
var array = {row:row, column:column}
return array;
}
// unused in current script!
function findRow(range, valueToSearch) {
var colRows = range.getValues();
for (i = 0; i < colRows.length; i++) {
if (valueToSearch == colRows[i][0]) {return i;}
}
// however, if found nothing:
Logger.log("the value " + valueToSearch + " could not be found in row titles");
return null;
}
// assumes that column titles are dates, therefore of type object.
function findColumn(range, valueToSearch) {
var colTitles = range.getValues();
for (i = 0; i < colTitles[0].length; i++) {
if (typeof colTitles[0][i] == "object") {
formattedDate = Utilities.formatDate(colTitles[0][i], "GMT", "yyyy-MM-dd")
};
if (valueToSearch === formattedDate) {return i;}
}
// however, if found nothing:
Logger.log("today's date, " + valueToSearch + ", could not be found in column titles");
return null;
}
// substracts 2 days from today, then returns the result in string format.
function todayMinus_xDays(x) {
var d = new Date();
d = new Date(d - x * 24 * 60 * 60 * 1000);
d = Utilities.formatDate(d, "GMT", "yyyy-MM-dd");
return d;
}
I'm using google sheets and have never run a "cron job" here.
To give credit where it's due, I found this awesome code to help me access the speed insights api here: https://statsravingmad.com/measure/page-speed-insights/
function speed(url,device,filter_third_party_resources,http_secure) {
url = url || 'www.statsravingmad.com';
strategy = 'desktop' || device;
filter_third_party_resources = 'true' || filter_third_party_resources;
http_secure = 'false' || http_secure ;
switch (http_secure) {
case 'false':
http_protocol = 'http://';
break;
case 'true':
http_protocol = 'https://';
break;
}
var key = 'api-key';
var api = 'https://www.googleapis.com/pagespeedonline/v2/runPagespeed?url=' + http_protocol + url
+ '&filter_third_party_resources=' + filter_third_party_resources +
'&strategy=' + strategy + '&key=' + key;
var response = UrlFetchApp.fetch(api, {muteHttpExceptions: true });
var result = JSON.parse(response.getContentText());
score = result.ruleGroups.SPEED.score;
return(score);
}
So I have this code in a function that is triggered every hour for my particular test sites in my google sheet.
But, the data is only filling one cell per site, the cell that the formula is assigned to.
When using google sheets, how can I modify this in order to have it fill a new cell in a column every hour? Do I modify this code, do I have to set up another function, or is there an option to fill the cells down a column?
This function can be modified to write, say, to column C of Sheet1. Here is how it would end, instead of return(score) (there is no need to return anything if the value is written to the spreadsheet directly; the function would not be invoked from the spreadsheet, but from a trigger).
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var values = sheet.getRange("C:C").getValues(); // using column C
var lastRow = 1;
for (var i = 0; i < values.length; i++) {
if (values[i][0] != "") {
lastRow = i + 1;
}
}
sheet.getRange(lastRow + 1, 3).setValue(score); // column = 3 because C
Here the loop finds the last row in column C that has data, and the values of score is placed under it.
I modified code from this question to suit my needs. I use Google Forms that fill into Google Sheets, and then the script inserts an event into Google Calendar. I have installed a "form submit" trigger to execute the script. The issue I have is that my code starts with the data in the first row of the sheet, adding the same events every time a new form response is added.
Here is my version:
//push new events to calendar
function pushToCalendar() {
//spreadsheet variables
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getLastRow();
var range = sheet.getRange(2,1,lastRow,16);
var values = range.getValues();
//calendar variables
var defaultCalendar = CalendarApp.getDefaultCalendar()
var numValues = 0;
for (var i = 0; i < values.length; i++) {
//check to see if Start DateTime and End DateTime are filled out
if ((values[i][3]) && (values[i][4])) {
//check if it's been entered before
if (values[i][6] != 'y') {
//create event https://developers.google.com/apps-script/class_calendarapp#createEvent
var newEventTitle = values[i][1] + ' - ' + values[i][2];
var startDay = Utilities.formatDate(new Date(values[i][3]), "GMT", "yyyy-MM-dd'T'HH:mm:ss'Z'");
var endDay = Utilities.formatDate(new Date(values[i][4]), "GMT", "yyyy-MM-dd'T'HH:mm:ss'Z'");
var newEvent = defaultCalendar.createEvent(newEventTitle, new Date(startDay), new Date(endDay), {location: values[i][2]});
//get ID
var newEventId = newEvent.getId();
//mark as entered, enter ID
sheet.getRange(i + 2, 6).setValue('y');
sheet.getRange(i + 2, 7).setValue(newEventId);
}
}
numValues++;
}
}
Found the solution by myself: on row 20 i changed the code
if (values[i][6] != 'y') {
to
if (!values[i][6]) {
No need to write that 'y' sign. I also changed
sheet.getRange(i+2,6).setValue('y');
to
sheet.getRange(i+2,6).setValue('');
And no duplicates anymore. The code works perfect!
The issue with the original code is that you are reading the wrong column - JavaScript arrays are 0-base, while the spreadsheet columns are 1-base. Column 6 (from getRange(i + 2, 6) is in the array index 5, i.e. values[i][5], not values[i][6]. So you were comparing the newEventId with the string 'y', which was never going to be the same.
As your solution indicates, removing the condition that values[i][6] (aka column 7, where your script writes the created event's ID) is not equal to 'y' (a condition that was always true), and instead testing for any value will appropriately guard the event creation code. Given the presence of the event ID column, the column in which 'y' was written is entirely unnecessary.
If you remove that column from your form response sheet, the code guard would then be:
//check to see if Start DateTime and End DateTime are filled out
if ((values[i][3]) && (values[i][4])) {
//check if it's been entered before, by looking in Column F:
var existingEventId = values[i][5]; // Will be "" (falsy) if not yet added
if (!existingEventId) {
...
// Log the event's ID so we don't make a duplicate:
sheet.getRange(i + 2, 6).setValue(newEventId);
...
I have created a script to send emails to a specific people with Birthday Reminder. This use to work till day before yesterday. I don't know why am I getting this error that Cannot find function getMonth, Can anyone tell where is the mistake
function emailAlert() {
// Short circuit if email notice is set to "No". This causes an error and the script stops.
if (turnOnEmailNotice.toLowerCase() == "no")
{
Logger.log("The Email Notification is NOT turned ON. System will Exit.");
exit
}
//Get the total number of filled row in the sheet.
var currentRowAT = 2;
var currentCellValueAT = "start";
while (currentCellValueAT != ""){
if (currentCellValueAT = birthdaysSheet.getRange("G" + currentRowAT).getValue() != ""){
currentRowAT = currentRowAT +1;
}
}
var birthdaysSheetLastRow = currentRowAT - 1;
// Get today's Date (with Month, Date and Year)
var today = new Date();
var todayMth = today.getMonth()+1;
var todayDate = today.getDate();
var todayYear = today.getFullYear();
// Check sheet cell for match to alertDate, k is the current row number being checked. Starting at 2 as the row #1 is for the headers
for (k=2; k < birthdaysSheetLastRow + 1; k++)
{
var targetBday = new Date();
targetBday = birthdaysSheet.getRange("P" + k).getValue();
// If Birthday is not speicified, continue with the next row
if (targetBday == ""){continue};
var unadjTargetBday = new Date();
var unadjTargetBdayMth = targetBday.getMonth()+1;
var unadjTargetBdayDate = targetBday.getDate();
var unadjTargetBdayYear = targetBday.getFullYear();
var unadjTargetBday = targetBday;
targetBday.setDate(targetBday.getDate()-daysInAdvance); // Calculating how many days in advance you want to trigger the notification. This is set in Settings Tab.
var targetBdayMth = targetBday.getMonth()+1;
var targetBdayDate = targetBday.getDate();
if (targetBdayMth + " " + targetBdayDate == todayMth + " " + todayDate)
{
var targetBirthDateYearsOld = (today.getYear() - unadjTargetBday.getYear())-1900;
prepareAndSendEmail(k, targetBirthDateYearsOld);
}
}
}
getValue will return the type string/date/number depending on the cell type in the spreadsheet (see menu Format -> Number). To be sure, just always convert to Date Type. This is the right way to convert it:
var targetBday = new Date(birthdaysSheet.getRange("P" + k).getValue());
Short answer
Check that the cell value is a valid date for Google Sheet.
Explanation
From https://developers.google.com/apps-script/reference/spreadsheet/range#getvalue (emphasis mine)
getValue()
Returns the value of the top-left cell in the range. The value may be
of type Number, Boolean, Date, or String depending on the value of the
cell. Empty cells will return an empty string.
To avoid this kind of problems, you may include some sort of data validation. You could use build-in features like conditional formatting, data validation, or something like a on edit script together with try / catch or a redundant validation in your emailAlert script.
Situation:
I have the following script, that run every 1 hours. This script insert "Update Age", I mean how many days past from DATE1 to DATE2.
Script 1: Script Link
Problem:
Column A: This column have an hyperlink. (Tickets# Numbers)
Column F & G: Date 1 and Date 2
Column H: The result of the Script 1
In the coulmn A, I have other script that insert Hyperlink... See Script Link 2.
This Script 2, run OnEdit but the Script 1 run every 1 hour.
When the script 1 run, the formula or Hyperlink is deleted only appear the data in cell.
Question:
Does exist any way that when the script 1 run, not delete the hyperlink or apply the hyperlink to all rows starting for the second 2 rows, the hyperlink.
SCRIPT FOR INSERT LINK:
function InsertLink(e)
{
var actSht = e.source.getActiveSheet();
if (actSht.getName() == ['ISP1']){
var activeCell = actSht.getActiveCell(); //Detec the ActiveCell
//var activeCell = event.range;
var activeCellValue = e.value;
var column = activeCell.getColumn();
var colNums = [1]; //Columns, whose edit is considered
if(colNums.indexOf(column) == -1) return; //If column other than considered then return
var row = activeCell.getRow();
if(row < 2) return; //If header row then return
var length = String(activeCellValue).length;
if (!e.value)
{
activeCell.setValue()
}
else if(length > 2)
{
activeCell.setValue('=HYPERLINK' + '("https://www.example.com/id='+activeCellValue+'";"'+activeCellValue+'")');
}
}
}
SCRIPT UPDATE:
// Create a timer trigger that will call "shellUpdateAge" every 30 minutes
// This function will run only for this particular sheets
function shellUpdateAge(){
var sheets = ['ISP1'];
for (var s in sheets){
toUpdateAge(sheets[s]);
}
}
function toUpdateAge(sheetName){
var ss = SpreadsheetApp.openById('1WmEwSLzqxOj7xkjokmor5B_HpMdabbEAGXiYeQwpIl8');
var sh = ss.getSheetByName(sheetName);
var data = sh.getRange(1,1,sh.getLastRow(),sh.getLastColumn()).getValues();
for(var n=0;n<data.length;++n){
if(typeof(data[n][6])=='object'){
data[n][7]=dayToToday(data[n][6])
}
}
sh.getRange(1,1,data.length,data[0].length).setValues(data)
}
function dayToToday(x){
var refcell = x;;// get value in column A to get the reference date
var refTime = new Date(refcell);
var ref = refTime.setHours(0,0,0,0)/(24*3600000);// set hours, minutes, seconds and milliseconds to 0 if necessary and get number of days
var today = new Date();
var TD = today.setHours(0,0,0,0)/(24*3600000);// set hours, minutes, seconds and milliseconds to 0 if necessary and get number of days
var day = parseInt(TD-ref);// get the difference in days (integer value )
return day ; // return result that will be in cell
}
DATA:
A2: =HYPERLINK("https://www.example.com/id=12345";"12345")
Note: The Hyperlink appear for every row in the column A when I ingress the Ticket ID.
F: 12/08/2014 18:08:00 (This Value is the DATA2)
G: 13/08/2014 18:08:00 (This Value is the DATE2)
H: The update age, insert the result from DATE2-DATE1. In this example the script will return 1 day.
Now the problem is when the script Update Age run, the script update the column G and delete the formula in column A.
I would just get and set the bare minimum, so you're not involving the HYPERLINK column:
function toUpdateAge(sheetName){
var ss = SpreadsheetApp.openById('1WmEwSLzqxOj7xkjokmor5B_HpMdabbEAGXiYeQwpIl8');
var sh = ss.getSheetByName(sheetName);
var range = sh.getRange(1,7,sh.getLastRow(),2);
var data = range.getValues();
for(var n=0;n<data.length;++n){
if(typeof(data[n][0])=='object'){
data[n][1]=dayToToday(data[n][0]);
}
}
range.setValues(data);
}
As an aside, you wouldn't consider a spreadsheet array formula for the date difference and HYPERLINK columns?