Update 2nd column based on access from first column - google-apps-script

I need to put the current date & time (on an individual row basis) in column K of my google docs spreadsheet, if either of column I or J is changed. This is my first attempt at using google scripts.
I've located a function that will do the time (and I'm hoping I can figure out how to do the date), but I don't know how to check for changes in cells, and update other cells. So for example if row 10 column I or J changes, row 10 column K should get the current date & time.
function myFunction() {
var d = new Date();
var timeStamp = d.getTime(); // Number of ms since Jan 1, 1970
// OR:
var currentTime = d.toLocaleTimeString(); // "12:35 PM", for instance
}

You can give this a shot - it uses string formatting to put the current date in the proper format (you can adjust as you need). One important thing to note: regardless of what you do here, the value will be displayed according to whatever formatting you have specified in the spreadsheet itself. Therefore if you don't do anything, the result is going to look like the traditional datetime representation. If you want it to appear in the format you specify, you can do two things: 1.) convert the entire column to text using the spreadsheet's Format menu; or 2.) (my choice) prefix the value you write with a ', which will essentially turn it into a string (that is what is done in the code below). Hope this helps.
function onEdit() {
var s = SpreadsheetApp.getActiveSheet();
// This was "MySheet" for me - change to whichever sheet you want to test
if( s.getName() == "MySheet" ) {
var r = s.getActiveCell();
// Check if the column is either I or J
var currColumn = r.getColumn();
if( currColumn == 9 || currColumn == 10) {
// If so, set dateCell = the current row at column J
var dateCell = s.getRange(r.getRow(), 11);
// Format the current date into datetime format (adjust to display how you want)
var dateTime = Utilities.formatDate(new Date(), "GMT-8", "MMM-dd-yyyy h:mm a");
// Set the cell
dateCell.setValue("'" + dateTime);
}
}
};

Related

Google Sheets: Apps Script: Is there a way to automatically highlights and adjust the width of a column that matches this week's date?

I have a project roadmap tracker where each of the columns corresponds to the first date of the week. I have it automatically highlight the column if that date matches any date of the current week but the columns are so narrow that it's not easily legible. I want to expand the column that contains the a date that matches the current week when the sheet is opened.
I am not familiar with Google Apps script and have no idea how to approach this problem. Plus, hobbling together other example scripts have proven fruitless since I hardly know what these functions do with each other.
For highlighting the column of the current date/week, I'm using this conditional formatting formula that I would ideally prefer to be integrated into a script.
=AND(WEEKNUM(TODAY(),2)=WEEKNUM($2:$2,2),YEAR(TODAY())=YEAR($2:$2))
Where the formula looks for the date of each column in row 2 and checks if the date matches any date within this current week.
In short, I trying to write a script that:
On open
The script looks for the column that matches a date that exists within the current week
Automatically adjusts the column to be a specific width (like 200)
Automatically highlight that column a specific color (like pale red)
Basically it could be something like this:
function main() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
// get the dates from the first row of the sheet
var dates = sheet.getRange(1,1,1,sheet.getLastColumn()).getValues().flat();
// get current week and year
var today = new Date();
var current_week = Utilities.formatDate(today, ss.getSpreadsheetTimeZone(), 'w');
var current_year = today.getFullYear();
// get indexes of the columns with dates belong current week and year
var columns = [];
for (let i in dates) {
var date = new Date(dates[i]);
var date_week = Utilities.formatDate(date, ss.getSpreadsheetTimeZone(), 'w');
var date_year = date.getFullYear();
if (date_year == current_year && date_week == current_week) columns.push(+i + 1);
}
// change width of the columns to 200
columns.forEach(col => sheet.setColumnWidth(col, 200));
// change backgrounds of the columns to pale red
var last_row = sheet.getLastRow()
columns.forEach(col => sheet.getRange(1,col,last_row,1).setBackground('#ffaaaa'));
}

Copy and pasting data between cells with Google Scripts

Photo of Spreadsheet
Newbie Question
I am guessing this is any easy question for most people here but I can't figure out what I am missing and I've spent quite a while trying to figure it out.
I have a script that is supposed to time stamp different columns. All the if functions work correctly for stamping columns A, E, and F by placing input in columns in B, C, and G respectively.
The one issue I having is getting column E to show input when column d is checked. I've tried a number of different ways(which are commented out in the code.)
I am not sure what I am missing but the difference between the code that is working and the code that isn't is that code that is working uses an offset function to place a date stamp of the current time while the code that isn't working is supposed to copy the data from one row up and two columns over. Essentially this means in an example that when column D10 is checked it should copy the time value in F9 to E10(I have corrected this example since my original post. I was a bit tired when I first wrote it)
Here is the example of my code. I have also attached a picture of the spreadsheet for clarity. I suspect the issue that I using a function wrong but I don't have enough experience to understand why.
function onEdit(e){
//CORE VARIABLES
// The columns that trigger datestamps in other columns
var COLUMNB = 2;
var COLUMNC = 3;
var COLUMND = 4;
var COLUMNG = 7;
// Where you want the date time stamp offset from the input location. [row, column]
// DTL = Date Time Location
//Intiates a series of variables to hold the offset values for datestamping the row entry
//Instiates a variable which controls the offset values for column B name entry
//The date stamp currently corresponds to column A and is triggered by selecting a client in column B
var DTLColB = [0,-1];
//Intiates a variable to hold the offset values for datestamping the start time column with the current time
//The date stamp currently corresponds to the time value in column E and the trigger is in column C
var DTLColCCTstart = [0, 2];
//Intiates a variable to hold the offset values for datestamping the start time column with the previous row entry time
//The date stamp currently corresponds to the time value in column E(offset(-1,2))and the trigger is in column D,-,-
//It copies the previous end time into the current start time
var DTLColDLTstart = [0, 1];
//Intiates a variable to hold the offset values for datestamping the current time in the end time col for for a row
//The date stamp currently corresponds to the time value in column F and the trigger is in column G
var DTLColGCTend = [0, -1];
var LastStampCoord = [-1, 2];
//Intiates a variable that controls which sheets this function is active on
var SHEETNAME = 'Cont Log'
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
//checks that we're on the correct sheet.
if( sheet.getSheetName() == SHEETNAME ) {
var selectedCell = ss.getActiveCell();
//checks the column to ensure it is on the one we want to cause the date to appear.
if( selectedCell.getColumn() == COLUMNB) {
var dateTimeCellB = selectedCell.offset(DTLColB[0],DTLColB[1]);
dateTimeCellB.setValue(new Date());
}
if( selectedCell.getColumn() == COLUMNC || selectedCell.getColumn == true ) {
var dateTimeCellC = selectedCell.offset(DTLColCCTstart[0],DTLColCCTstart[1]);
dateTimeCellC.setValue(new Date());
}
if( selectedCell.getColumn() == COLUMND || selectedCell.getColumn == true) {
var dateTimeCellD = selectedCell.offset(DTLColDLTstart[0],DTLColDLTstart[1]);
// var oldtimevalue = selectedCell.offset(LastStampCoord[0],LastStampCoord[1]);
// var oldtimevalue = range.selectedCell.offset(LastStampCoord[0],LastStampCoord[1]));
//var oldtimevalue = selectedCell.offset(LastStampCoord[0],LastStampCoord[1]);
//var oldtimevalue = selectedCell.getrange(selectedCell.offset(LastStampCoord[0], LastStampCoord[1])).getvalues;
//var oldtimerecord = sheet.getrange(selectedCell.offset(LastStampCoord[0],LastStampCoord[1])).getvalue();
//var oldtimevalue = oldtimerecord.getvalue();
// var LastStampLookup = selectedCell.offset(LastStampCoord[0],LastStampCoord[1]).getvalue();
// var LastStampTime = LastStampLookup.getvalue()
// range1.offset(2, 0).setValue("Order complete");
// dateTimeCellD.offset(LastStampCoord[0], LastStampCoord[1]).setvalue(timelocation);
//dateTimeCellD.setValue(new oldtimevalue.getvalue());
dateTimeCellD.setValue(new date());
}
if( selectedCell.getColumn() == COLUMNG || selectedCell.getColumn == true) {
var dateTimeCellG = selectedCell.offset(DTLColGCTend[0],DTLColGCTend[1]);
dateTimeCellG.setValue(new Date());
}
}
}
[1]: https://i.stack.imgur.com/85DwP.png
I am not sure what I am missing but the difference between the code
that is working and the code that isn't
You have a small typing mistake - change
dateTimeCellD.setValue(new date());
to
dateTimeCellD.setValue(new Date());
However, if your goal is to set the value of dateTimeCellD to the one of the cell 1 to the right, one to the top - you can do it with
dateTimeCellD.setValue(dateTimeCellD.offset(-1,1).getValue());
See the method descriptions here and here.
Try this:
function onEdit(e){
var sh=e.range.getSheet();
if( sh.getName()=="Cont Log" ) {
var ts=Utilities.formatDate(new Date(),Session.getScriptTimeZone(),"E MM/dd/yyyy HH:mm:ss");
if(e.range.columnStart==2) {e.range.offset(0,-1).setValue(ts);}
if(e.range.columnStart==3) {e.range.offset(0,2).setValue(ts);}
if(e.range.columnStart==4) {e.range.offset(0,1).setValue(ts);}
if(e.range.columnStart==7) {e.range.offset(0,-1).setValue(ts);}
}
}

Lookup value from cell on specific date, then keep that value indefinitely

I am looking for a formula that: on 6/14/19, lookups up "Ige" in another column and grabs the numerical value. Once numerical value found, keep ONLY that value indefinitely. These are odds for betting that are constantly changing, so I want to grab only those specific odds on that specific day and put it into the box next to the date.
I know how to setup vlookup, but not sure how to do it on a specific date and only keep the value from that date.
Requirement:
Copy data matching dates to another range.
Solution:
function copyData() {
//spreadsheet variables
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getActiveSheet();
//data variables
var raw = sh.getRange('W30').getDisplayValue();
var range = sh.getRange(71, 17, 6, 2); //row 71, column R, for 6 rows and 2 columns
var data = range.getDisplayValues();
var date = Utilities.formatDate(new Date(), ss.getSpreadsheetTimeZone(), 'M/dd/yy');
//loop through range
for (i = 0; i < data.length; i++) {
//if date on sheet matches today's date
if (data[i][0] === date) {
var val = raw.toString().replace(/\+|-/g,''); //remove '+' and '-' from data
sh.getRange(71+i, 18).setValue(val); //set value
}
}
}
Explanation:
The script will loop through your range and compare the date in the column to the current date, then if they match it'll copy the value to the correct row in your range.
You will need to change var range if you want to record any more than 6 days worth of data, this is easy enough though, just change the '6' to however many rows worth of data you want to keep.

range.getValues() With specific Date in Specific Cell

We are using a Google Script to import a Range from other Spreadsheet to another.
This helped us in the past but now the data is growing and we need to reduce the data that we import. (timeout problems)
We need to import the rows with a specific date on a specific column.
In this case, as you can see in the script below, we are importing cells from 'A1' to 'N last row' in the range variable.
What we need is that in the column 'H' from that range date is checked with something like "Date in column K >= Today()-90"
// iterate all the sheets
sourceSheetNames.forEach(function(sheetName, index) {
if (EXCLUDED_SHEETS.indexOf(sheetName) == -1) {
// get the sheet
var sheet = sourceSpreadSheet.getSheetByName(sheetName);
// selects the range of data that we want to pick. We know that row 1 is the header of the table,
// but we need to calculate which is the last row of the sheet. For that we use getLastRow() function
var lastRow = sheet.getLastRow();
// N is because we want to copy to the N column
var range = sheet.getRange('A1:N' + lastRow);
// get the values
var data = range.getValues();
data.forEach(function(value) {
value.unshift(sheetName);
});
}
});
To conditionally copy the only the rows that meet a criteria, you will want to push them to a new array if they qualify. This push would be added to your existing data.forEach() call:
...
var now = new Date();
var today = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
var kept = [];
var data = range.getValues();
// Add qualifying rows to kept
data.forEach(function(row) {
var colHvalue = row[7];
var colKvalue = row[10];
if( /* your desired test */) {
// Add the name of the sheet as the row's 1st column value.
row.unshift(sheetName);
// Keep this row
kept.push(row);
}
});
/* other stuff with `kept`, like writing it to the new sheet */
You'll have to implement your specific test as you have not shared how time is stored in column H or K (e.g. days since epoch, iso time string, etc). Be sure to review the Date reference.
I've solved this in the past by adding a new column in the spreadsheet which calculates n days past an event.
=ARRAYFORMULA(IF(ISBLANK(K2:K),"",ROUNDDOWN(K2:K - NOW())))
The core of the function is the countdown calculation. For instance, today is Thursday, March 1. Subtracting it from a date in the future like Sunday, March 4, returns a whole integer: 3. I can test for that integer (or any integer) in a simple script.
In your script, add a conditional statement before executing the rest of the function:
// ...
if(someDate === -90) {
// do something...
}
This way, you're just checking the value of a cell rather than doing a calculation in a helper function. The only change (if you want a longer or shorter interval) is in the conditional test.

Sheets function to add content to another sheet

I've been making slow but steady progress on this app that creates the daily bulletin for the school where I teach.
Data is submitted by staff via a form, and is then naturally in a sheet. I already created a script to purge old data from the sheet, thanks in part to help I've gotten here. An additional script orders content on the data sheet by bulletin category, creates a copy of a template sheet, names it by the desired date, puts the date at the top. That's about as far as I've gotten. It also adds the first category heading by default, which is mostly a test.
What I'm attempting to do now is loop through each row of the data sheet to determine if any of the three date columns contains the desired date (entered via a dialog box earlier in the script). If any of them match today's date, we then will check to see if the current category and the category in the row are the same. If they are not, we change the current category and add a new heading to the bulletin sheet. If they are the same, we get the announcement itself and add that to the bulletin sheet. I suspect I'll use embedded functions for these two purposes.
Right now I'm stuck on the loop portion. Again, this should cycle through each row of the data sheet. There are three columns containing the dates (C, D, E). If I can get it to recognize date matches from one of the cells in this range, I can move forward with the rest.
function writeBulletin() {
//get the bulletin date
var bullSheet = todayDay;
//make the bulletin sheet active
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.setActiveSheet(ss.getSheetByName(todayDate));
//set var for needed sheets
var responses = ss.getSheetByName("Form Responses 1")
var bulletin = ss.getSheetByName(todayDate)
//get the date from the sheet title and apply it to the date range
var dateCell = bulletin.getRange(3,1);
var sheetDate = bulletin.getName();
dateCell.setValue(sheetDate);
//works
//Now we start building the bulletin
//currentDataRow is a reference to the Responses sheet. Used in later for loop
var currentDataRow = 2;
var currentBulletinRow = 11;
var catCurrent = "01 Administration";
var catCurrentSS=catCurrent.substring(3,30);
var lastRow = responses.getLastRow(); //get last row of data sheet
var lastBull = bulletin.getLastRow(); //get last row of bulletin sheet
var nextBullRow = lastBull+2;
var testOutput = bulletin.getRange(6,3);
var nextBullItem = bulletin.getRange(nextBullRow,1);
nextBullItem.setValue(catCurrentSS);
//testOutput.setValue("dude"); //this works
if(responses.getRange(2,3).getValue()==todayDate) {
testOutput.setValue("dude");
}
//bulletin.getRange(2,3).setValue("dude"); //test row
for(var i = 2; i<=lastRow; i++) {
if(5>3) {
//if(responses.getRange(i,3).getValue()==sheetDate||responses.getRange(i,4).getValue()==sheetDate||responses.getRange(i,5).getValue()==sheetDate){
//bulletin.getRange(nextBullRow,3).setValue("dude");//works
bulletin.getRange(nextBullRow,1).setValue(responses.getRange(i,7).getValue());
nextBullRow+=2;
}
}
}
I did notice that my loop condition statement had a reversed inequality sign; however, fixing this did not seem to help.
jdv: Good point. fixed it now
Aside from the issue of repeatedly interacting with the Spreadsheet interface (the alternative being to read values from the Spreadsheet once, then work with the resulting javascript Array object), the issue is that you are comparing a Range object with a String:
var sheetDate = bulletin.getName();
...
if(responses.getRange(i, 3) == sheetDate || ..... ) {
This will not work :) You need to access the value of the Range:
if(responses.getRange(i, 3).getValue() == sheetDate || ... ) {
edit: as mentioned in comments, the values in these responses cells are interpreted as Date objects. Date comparisons are fun, because you get to play with time zones and/or format strings. I recommend avoiding needing to use dates in this manner, especially when starting out with scripts.
One possible fix for this new issue is to use the value from dateCell.getValue() after calling SpreadsheetApp.flush() (to ensure the writing of sheetDate is performed first). This will let the spreadsheet do the nasty work making the correct date:
dateCell.setValue(sheetDate);
SpreadsheetApp.flush();
// Construct the proper Date object from the sheetDate value
var compareDate = dateCell.getValue();
...
for(var i = 2; i <= lastRow; ++i) {
// Read into an array [[ 0: elem#(i,3), 1: elem#(i,4), 2: elem#(i,5), 3: elem#(i,6), 4: elem#(i,7) ]]
var row = responses.getRange(i, 3, 1, 5).getValues();
if(row[0][0] == compareDate || row[0][1] == compareDate || row[0][2] == compareDate) {
...