When sheet is opened, would like to auto-jump to certain cell based on date - google-apps-script

UPDATE: Based on earlier answers, I joined the chorus of people asking Google to re-enable the ability to do this, and they have apparently complied.
Now to figure out how to implement it since I know nothing about scripting in Google Sheets.
ORIGINAL QUESTION BELOW
I have a spreadsheet with a number of different sheets that require daily entry of data. For example, there's a sheet called US-Sales, with a row for each day of the year. There are also several other identical sheets, like UK-Sales, US-Rentals, etc.
I would like focus to auto-jump to the proper row, based on the current date, whenever one of these sheets is opened.
Possible? If so, how?

This could normally be achieved by a simple script with an onOpen() function (that executes on spreadsheet opening) and activates the desired cell (or sheet)...
but, due to a change in the new version of spreadsheets (that you are most probably using) this is not possible anymore (for now), see this issue (3928) and feel free to star it so that hopefully Google team will change their mind and make it possible again ;-)
edit : this simple code works in old version of spreadsheets, it does not in new version.
function onOpen() {
SpreadsheetApp.getActive().getSheets()[0].getRange('B6').activate();// an arbitrary cell
// not worth trying more complex cell selection (on date or anything else) while this is blocked by design .
}
Details on this change here and below.
EDIT 2 :
On october 16 2014 this issue is now fixed and the code above works also in new version of spreadsheets. details of issue here
to automatically activate the row corresponding to the day of the year you can use a code like below :
function onOpen() {
var day = new Date().getDOY();// this uses a custom date method that returns the day of the year and is defined below
SpreadsheetApp.getActive().getSheets()[0].getRange(day,1).activate();// in cloumn 1 for example. Add an offset if necessary (if headers...)
}
Date.prototype.getDOY = function() {
var onejan = new Date(this.getFullYear(),0,1);
return Math.ceil((this - onejan) / 86400000);
}

Related

Adding Date+Time without 12AM moving to the next day

I am using the attached to automate a time entry to go along with the date for a calendar import. The entries don't have times, and the staff will not enter them try as I might. I need to automate them to simplify the entry procedure.
The issue I am facing is that the Calendar API needs the data to be in DATE/TIME format. To do this I need to use the =DATE+TIME formula. When I do so and the time reaches 12:00AM, the dates thereafter change to the following day.
Essentially I need to either override the logic that makes it move into the next day after midnight appears, or I need to tell either the function in column B-C that it can never roll to midnight. I am trying to think of perhaps a way that I can tell the function to reset the time if the date in column A changes to a new day, and if it doesn't change to a new day go ahead and use the existing function and add 5 minutes to the time that is shown previously to it.
I am stumped, any help would be greatly appreciated.
Here is a sheet to show you the issue
Here is the formula I tried, which worked to sort out the problem but did not work with the Calendar API requirements to format at DATE/TIME. Even when using the importrange formula to move the data into a new sheet with the cells formatted as DATE/TIME it still recognizes it as TEXT as this is what the formula prescribes.
=IF(A2<>"",(CONCATENATE(TEXT(A2,"MM/DD/YYYY")&" "&TEXT(B2,"HH:MM:SS"))),"")
I need this to work in both the sheet and in the import to Calendar using the Calendar API requirements through APPScript.
If I understood correctly your question, here a suggestion with a custom Apps Script function called like a normal Google Sheet function.
Open Apps Script Editor and paste the function below
Call the function rebuildDateTime(COL1, COL2) inside your spreadsheet
Spreadsheet:
Code:
function rebuildDateTime(arg0, arg1) {
var date = new Date(arg0);
var str = arg1.toString().split(':');
date.setHours(str[0]);
date.setMinutes(str[1]);
return date;
}
Warning :
Your COL2 (which contains only the time), must be forced to TEXT FORMAT
References :
Create a custom function

Automate copying a value from one cell to another on a specific date

I'm looking for more of a pointer to some documentation of a method here rather than an exact solution, I'm happy with JavaScript, haven't done much Apps Script and I'm moderate to OK familiar with Google Sheets functions but far from expert.
I have part of a Google Sheet with some date specific data on it like this:
Date
Some-Value
1 Jan 2023
123
15 Jan 2023
456
... etc
In another part of a sheet I have a cell with the current value of Some-Value. This cell in fact contains a formula that totals a column on another sheet for the values. In case it's not blindingly obvious, these dates are in fact sprint end dates and the Some-Value is a count of story points extracted from JIRA. So sort of like this:
Current Value of Some-Value
345
On exactly the date in the Date column I want to copy the value from the "Current Value of Some Value" cell into the cell in the "Some Value" column adjacent to that date. I don't care if that copy happens only once on that day, or several times on that day, or every hour on that day, or whatever. I just want it to happen at least once, automatically.
Should I be looking at an Apps Script function to do this, and roughly how should I do that? Or is there a simpler way of using some Google Sheets function to copy that cell?
You could potentially do this with formulas if you are willing to enable iterative calculation for your sheet. In that case, you could then write something like =if(A2=today(),currentValueCell,B2) (I'm assuming your Date/Some-value table is in A1:Bx of a sheet and you are placing the above in B2). This will return the current value only if the date matches, and then when the dates no longer match will just maintain whatever value is already present in the cell.
EDIT
Ah yes, I forgot that the initial state of a self-referencing IF is zero (rather than null) until a TRUE occurs. Try =if(A2=today(),currentValueCell,if(B2<>0,B2,)) to hide the initial zero generated when the date in A2 is not equal to TODAY().
If you need to copy a value than then it's going to be changed or erased, yes, you'll need an AppScript. If you already know something, you'll more than able to set a simple function to do it.
You can look into Installable Triggers that will help you to set when and how frequently you want your script to be fired; and it will notify you if there are errors in any of that executions.
Sometimes, matching dates can be tricky, more if you have them previously written. If you have to do a timestamp, then you'll probably succeed at first. Just for have it handy: https://developers.google.com/google-ads/scripts/docs/features/dates
Remember to always use Logger.log() to track the progress of your code and what it's returning. Good luck and here you can always ask for specific struggles you may find!
(I'm no expert at programming by far, since it's copying only a value, you may find useful to record macros and watch them to learn how to copy in the formats you need - only values, paste format, etc. - if you don't copy, you can also research in .getValue() and .setValue() )
In case anyone else comes across this in future, here is the somewhat creaky Apps Script function I developed to do this. It's my first Google Apps Script function so feel free to tear it to shreds.
I then added a trigger to run this function weekly, which was easier than I had thought -- a bit of google searching for Apps Script time based triggers found me the answer.
Nonetheless thanks to the people who answered earlier because the clues you gave me helped me find the answer in the end.
function updateUnderTest() {
var activeSheet = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = activeSheet.getSheetByName('Supporting Data');
// I12 is the cell that gets updated daily with the story point count
var value = sourceSheet.getRange('I12').getValue();
// D3:E27 is the range containing date : value pairs
var data = sourceSheet.getRange("D3:E27").getValues();
// epochNow will be the current epoch time, in milliseconds
var epochNow = new Date().valueOf();
// Look through the data range to find a matching date
for (nn=0;nn<data.length;++nn) {
// If a match is found to the nearest day, break the loop
if ((data[nn][0].valueOf() < epochNow) && ((epochNow - data[nn][0].valueOf()) < (24*3600*1000) )) {break};
}
// only update if we found a match
if (nn < 24) {
sourceSheet.getRange(nn+3, 5).setValue(value);
}
}

How to change background color of a cell if it is edited after a specific duration?

I have a google spreadsheet which we use to feed inventory purchase and issue data. The only problem is my staff can manipulate purchased quantity, prices and other variables at a later date. I want that if they enter the data in a cell and try to edit it whenever after 12 hours of entering the data, the cell should get highlighted. If possible, the cell should not highlight if I edit the data.
A simple solution is to have a column when this data is entered (if the data is added with a google form, then you are already set to go on that front). At that point all you need is a onEdit triggered function that fetches that timestamp, does var curTime = new Date() and then check what is the difference between them, and if it's greater than 12 hours you do a e.range.setBackground('red') or whatever color you wish (remember that the function must have e defined like function checker(e)).
Having it ignore edits done by you is also simple, just have an a simple
var editUser = Session.getEffectiveUser().getEmail()
if (editUser == 'myEmail#gmail.com`)
return 1;
and it will stop the script if the session users email matches your own.
For future reference please also provide what code you have so far, as currently we don't know what research you have done so far, what worked and what did not. Remember — here you won't get someone to write the program for you, only solve issues you have with code you already have.

Time/Date-Based Rule for Conditional Formatting via Google Apps Scripting

Alright, I'm stuck.
I've got a sheet that an entire team of people use to communicate with each other and sign up for timeslots for internal logins.
I've got the sheet formatting performing the way I want to, but, it's too easily edited by people, because they have to be able to edit the values in the cells.
It's currently running Conditional Formatting rules, based upon time/date. Basically, if the cell's timeslot has passed, then it's blacked out. It is also blacked out if the cell isn't associated with the current day.
https://docs.google.com/spreadsheets/d/1TP5iJ9AA_xqDQPiyl89ntZehd7aqDPELcN0bRMGP-SU/edit?usp=sharing
I'm trying to create a script that "onEdit" will restore any formatting (not the value) that had been changed during the edit.
Got it this far, but I'm struggling to make it conditional like the UI rules allow me to do.
function myFunction() {
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
var range1 = sheet.getRange('A1:B10');
range1.mergeAcross()
range1.setBackground('white');
range1.setBorder(true,true,true,true,true,true);
range1.setFontColor('black');
range1.setFontFamily('Arial');
range1.setFontSize(10);
range1.setFontWeight("normal");
range1.setFontStyle("normal");
range1.setHorizontalAlignment("left");
range1.setVerticalAlignment("center");
range1.setWrap(false);
}
Here's where I'm struggling (notes inserted)
function myFunction() {
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
var range1 = sheet.getRange('A1:B10'); // I'd have this repeated for multiple ranges
range1.mergeAcross()
range1.setBackground('white'); // Need an "IF" argument for this, such as if today is Wednesday and time is before 2:30pm, then white, otherwise black. Could probably put this at a different point in the script.
range1.setBorder(true,true,true,true,true,true); // All the following attributes would simply restore defaults
range1.setFontColor('black');
range1.setFontFamily('Arial');
range1.setFontSize(10);
range1.setFontWeight("normal");
range1.setFontStyle("normal");
range1.setHorizontalAlignment("left");
range1.setVerticalAlignment("center");
range1.setWrap(false);
}
Thanks in advance for any help you might offer!
Adam
I've actually been trying to deal with this same problem.
Sadly, google scripts doesn't seem to have a way programmatically set the conditional programming. (Here's a link to the issue. You can cast a vote at the bottom on to get them to implement it)
However, I think you might be able to implement a solution to your problem it by scratch.
The onEdit trigger passes a variable to the function it's assigned to.
function testOnEdit(e) {
Logger.log(JSON.stringify(e));
}
Here is an example of the JSON from the event object for onEdit.
{"range":{"columnStart":3,"rowStart":2,"rowEnd":2,"columnEnd":3},"source":{},"value":"I typed this into B3!","authMode":{},"triggerUid":*********}
You might be able to use this to directly reset the format of the cells they edit. BUT... Unfortunately, onEdit is only fired when a range's values are changed--not when formatting is changed, not when data validation is changed, not when conditional formatting is changed--so to catch the cases that some rouge user decides that purple looks nice, you may want to also make the script run every minute using a timed trigger (but you need to make sure it doesn't take too long to run since it will be going off every minute and google puts a limit on total amount of time triggers can run).
As for comparing dates, these functions may be useful, but be warned that they return the local time (if you have users from multiple time zones you may need to convert to UTC).
function myFunction() {
var date = new Date();
date.getDay(); // the value returned by getDay() is the index of the day of the week, where 0 is Sunday, 1 is Monday...
date.getHours(); // the value returned by getHours() is an integer between 0 and 23
date.getMinutes(); // the value returned by getMinutes() is the current minute in the range 0-60
}
Since I too am dealing with this problem, I plan to add more insight to this solution later. Let me know if you have any insights, Adam.

How to write an onOpen triggered script without using Activate() methods

Good morning,
I am having an issue with a piece of script which will open the spreadsheet at the first available row after a data table. I am fairly new to this, but as I understand it from research, the onOpen trigger will not work for scripts featuring Activate() methods (which I assume are things like getActiveRange() and setActiveRange() etc.).
I'd really like for this to work, but I can only do it currently by placing a button at the top of the sheet which the user has to click to run the script. I'm at a loss as to how to write in a different way, and have spent ages googling but only get things which don't help me much at all. My code is as below, any help you can give in writing it without using Activate() methods would be very much appreciated. Thanks, MB
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var range = ss.getRange("A2");
var rowCount = range.getValue() + 3;
var newRange = ss.getRange("A" + rowCount);
SpreadsheetApp.setActiveRange(newRange); };
The value on line 4 refers to a value in a cell which is counting the number of rows in the dataset (I had issues trying to count this using script because there are other columns in the dataset which throw functions like getLastRow()).
As mentioned in issue 3928 there is no way (as of now, september 2014) to set a selection on a cell/range/sheet from a triggered function.
This issue was closed as "wont fix" but recently reopened - probably because a lot of people were very disappointed (to use a polite word) - so I suggest you star this issue to increase its visibility and its priority.
In the mean time I'm afraid there is not much we can do except continue to use old version spreadsheet in which this works without problem.
About what you said about counting the last row in a single column, this has been the subject of one of the most popular thread on this forum, look at the suggested solutions there (and also in other similar threads).