I was wondering if there was a way to create a function for the following. I am trying to update a cell, say G3 with a value in D3 but I want to do it on a certain date. I want for that cell to update on that date and then lock in with the value.
For example, if D3 has a value of 30, I want for my G3 cell to update with 30 on 10/12/2012. After that date I want that value to lock in as the data in D3 will change over time. This formula would need to be copied as I am trying to find a trend detail on a weekly basis.
Any help would be appreciated, thanks in advance!
If you don't mind doing this in Google Apps Script, you can achieve this easily via a time-driven trigger. Specifically, with atDate(year, month, day) you can make a function run at a certain date. To do this, you would first have to create the trigger. You can do this manually from the script editor, as explained here. You could also do this programmatically, by running a function that creates such trigger. The function would be, in this case:
function createTrigger() {
ScriptApp.newTrigger("setAndLock")
.timeBased()
.atDate(2012, 12, 10)
.create();
}
Running this function will create a trigger that will fire a function called setAndLock at date 2012/12/10, which should copy the value from D3 to G3 and protect this last cell. Such a function could be similar to this one:
function setAndLock() {
var sheet = SpreadsheetApp.getActiveSheet();
// Copying the value from D3 to G3:
var origin = sheet.getRange("D3").getValue();
var dest = sheet.getRange("G3");
dest.setValue(origin);
// Protecting G3:
var protection = dest.protect();
var me = Session.getEffectiveUser();
protection.addEditor(me);
protection.removeEditors(protection.getEditors());
if (protection.canDomainEdit()) {
protection.setDomainEdit(false);
}
}
Please take into account that the cell cannot be locked from the current user nor the owner of the spreadsheet, as mentioned here.
I hope this is of any help.
Related
[how to lock cell autometicly base on cell data & and time everyday?]https://docs.google.com/spreadsheets/d/1PQTpr9tes5_7c963nV_c0BSOdemYD6Vpyd1SB1elANE/edit#gid=1118459963
A possible solution for this is to use a time based trigger in Apps Script which will lock the cells you want.
You should start by writing a script similar to this:
function lockCells() {
let sheet = SpreadsheetApp.openById("SS_ID").getSheetByName("Attendance1");
let protection = sheet.getRange(3, 5, 15, 30).protect(); // CORRESPONDS TO E3:AH17
let editors = protection.getEditors();
protection.removeEditors(editors)
}
function timeTrigger() {
ScriptApp.newTrigger('lockCells')
.timeBased()
.atHour(11)
.create();
}
The code above consists in the two functions:
lockCells - which is used to protect the range by retrieving the editors and then removing them;
timeTrigger - which will trigger the lockCells function every day at 11:00.
Reference
Apps Script Range Class - protect();
Apps Script Protection Class - getEditors();
Apps Script Protection Class - removeEditors();
Installable Triggers.
I have a Google Sheets document that has 36 formulas across a row in the "Historical Data" tab. When I run the script each morning it populates the next empty row with the formulas in the previous row and then makes the previous row a value (so as to remove the formulas). I have an issue sometimes that the row with formulas loses the values and display "#N/A". I believe this is because the data is pulled from GoogleFinance.
Error in the image below.
I am curious if there is a method to do the following:
Define the formulas for each column in the code.
Run the script each morning and populate the next empty row with the values of the formulas.
The biggest item I need help with is adding the following formulas to the code so they will not be present in the worksheet. Additionally, some of the formulas rely on the value in a cell. For example the first formula is to create the date based on the date in the last row cell (A). The remaining formulas use the date from the new row columnm (a) and inputs from other columns in the new row. So when you see A2736 that is in last row and A2737 is the new row that was create when I last ran the script.
//DATE(A)=workday(A2736,1,'NYSE Holidays'!$A$2:$A$27)
//VIX9D(B)=INDEX(GOOGLEFINANCE("INDEXCBOE:VIX9D","close",$A2737),2,2)
//VIX1D(C)=INDEX(GOOGLEFINANCE("INDEXCBOE:VIX","close",$A2737),2,2)
//VIX3M(D)=index(GOOGLEFINANCE("INDEXCBOE:VIX3M","close",$A2737),2,2)
//VIX6M(E)=index(GOOGLEFINANCE("INDEXCBOE:VIX6M","close",$A2737),2,2)
//VIX1Y(F)=index(GOOGLEFINANCE("INDEXCBOE:VIX1Y","close",$A2737),2,2)
//VVIX(G)=index(GOOGLEFINANCE("INDEXCBOE:VVIX","close",$A2737),2,2)
//SPX(H)=index(GOOGLEFINANCE("INDEXSP:.INX","close",$A2737),2,2)
//VIX9D:VIX(I)=B2737/C2737
//VIX:VIX3M(J)=C2737/D2737
//VIX:VIX6M(K)=C2737/E2737
//VIX:VIX1Y(L)=C2737/F2737
//CORR, SPX , VVIX, 5(M)=correl(H2733:H2737,G2733:G2737)
//CORR, SPX , VIX, 10(N)=correl(H2728:H2737,C2728:C2737)
//Contango VIX2:VIX1(O) No formula yet
//Vix4:Vix7 Contango(P) No formula yet
//Log Ret(Q)=ln(H2737/H2736)
//V10(R)=stdev(Q2728:Q2737)
//V20(S)=stdev(Q2719:Q2737)
//HV10(T)=sqrt(252)*R2737
//HV20(U)=sqrt(252)*S2737
//VIX - HV10(V)=C2737-(T2737*100)
//VIX - hv20(W)=C2737-(U2737*100)
//VIX9D % Rank(X)=PERCENTRANK(B$2:B,B2737)
//VIX1D % Rank(Y)=PERCENTRANK(C$2:C,C2737)
//VIX3M % Rank(Z)=PERCENTRANK(D$2:D,D2737)
//VIX6M % Rank(AA)=PERCENTRANK(E$2:E,E2737)
//VIX1Y % Rank(AB)=PERCENTRANK(F$2:F,F2737)
//VVIX % Rank(AC)=PERCENTRANK(G$2:G,G2737)
//VIX9D Median(AD)=MEDIAN(B$2:B)
//VIX1D Median(AE)=MEDIAN(C$2:C)
//VIX3M Median(AF)=MEDIAN(D$2:D)
//VIX6M Median(AG)=MEDIAN(E$2:E)
//VIX1Y Median(AH)=MEDIAN(F$2:F)
//VVIX Median(AI)=MEDIAN(G$2:G)
//VDelta (VIX-VIX9D)(AJ)=C2737-B2737
Current code is below.
// Add Run button to menu
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu("Auto Trigger")
.addItem("Run","runAuto")
.addToUi();
}
// Define function to run with menu button
function runAuto() {
recordValue()
}
function createTimeDrivenTrigger() {
// Trigger every Weekday at 09:00.
ScriptApp.newTrigger('recordValue')
.timeBased()
.onWeekDay(ScriptApp.WeekDay)
.atHour(9)
.create();
}
// Record history from a cell and append to next available row
function recordValue() {
if (isNotHoliday()){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Historical_Data");
var lastRow = sheet.getLastRow();
var oldDate = sheet.getRange(lastRow,1).getValue();
var rng = sheet.getRange(lastRow,1,1,36);
rng.copyTo(sheet.getRange(lastRow+1,1,1,36));
rng.setValues(rng.getValues());
}
}
function isNotHoliday(){
var yesterday = new Date(new Date().getFullYear(),new Date().getMonth(),new Date().getDate()-1);
var formattedDate = Utilities.formatDate(yesterday, Session.getScriptTimeZone(), "M/d/yy")
var holidays = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('NYSE Holidays').getRange('A2:A').getDisplayValues().join().split(",");
return (! ~holidays.indexOf(formattedDate))
}
If you wish to run the script once every morning you could use programmatically triggers. This way you can easily set up a script run inside your desired time frame. Then you only would need to get the range of the last row and paste the formula there. I see that the script already gets the range, so you only need Range.setFormula() to drop the formula.
There is a much simpler way to do this - which is to flip time the other way.
You leave the google finance formulas in row 2 along with a =TODAY() in cell B2, with headers in row 1.
every night at 11pm, your sheet inserts a row above row 3, copies the values from row 2 into the new row 3 as values only.
That's it. super simple. you never need to copy formulas at all.
The newest prices are always at the top. This is how most people track portfolios.
I have a sheet "Sheet1" where a user inputs data in cells A3:E3. The goal is to then move that data into "Sheet2" where column A4:A34 is a numeric value for the day of the month. I want to move the data inputted on "Sheet1" into "Sheet2" on the row that corresponds with the current day of the month. Then "Sheet1" would clear itself for the next day ahead.
I'll be honest I'm not great with functions and I'm mainly looking for help on where to look to find a solution. I've tried different code that I do know how to use like Query but the issue is it doesn't lock the value in place and as soon as the values are wiped in Sheet1 they are then removed via the query.
You will need to run a function on a time-driven trigger. The function can use Range.getValues() to read the data, get the current date's day number with const dayNumber = new Date().getDate(), look up the day number in Sheet2 with Range.getValues().flat().indexOf(dayNumber), write the data with Range.setValues() and finally clear the source range with Range.clearContent().
It is unclear what the purpose the day numbers in Sheet2 serve, because the data would in any case be written once a day. It would seem simpler to always append the most recent data at the first available row and include a timestamp.
For an example of how to do this, see the appendRowsToArchiveSheet script.
Since the task is rather simple, here is the code that does the work:
// add menu Scripts
function onOpen() {
SpreadsheetApp.getUi().createMenu('🔨 Scripts')
.addItem('✅ Copy to archive', 'copy_range_to_archive')
.addItem('❌ Erase', 'erase_range')
.addToUi();
}
// erase the range A3:E3
function erase_range() {
SpreadsheetApp.getActiveSpreadsheet()
.getSheets()[0]
.getRange("A3:E3")
.clearContent();
}
// copy the range A3:E3 to Sheet 2
function copy_range_to_archive() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const src_range = ss.getSheets()[0].getRange("A3:E3");
const data = src_range.getValues();
const dest_sheet = ss.getSheets()[1];
const today = new Date().getDate();
const dest_range = dest_sheet.getRange(3 + today, 2, 1, data[0].length);
dest_range.clearContent();
dest_range.setValues(data);
}
It makes the menu 'Scripts' and two command 'Copy to archive' (the range A3:E3) and 'Erase' (the same range).
good morning
this script adds a cell below, it is wrong I would rather copy the value of E2 and paste it in E3, E4 etc etc every 1 hour.
Thanks to those who solve my problem.
var activeSheet = SpreadsheetApp.getActiveSheet();
SpreadsheetApp.getActiveSheet().getRange('E2').setValue(jsonResponse.price);
var sheet =SpreadsheetApp.getActiveSheet()
var range = sheet.getRange("E2:E2");
range.insertCells(SpreadsheetApp.Dimension.ROWS);
Time-driven trigger:
The script you shared will effectively shift the cells in column E down, with E3 containing the newest value (E2 will be blank). You want this function to run automatically each hour. For this, you need to install a time-driven trigger. The trigger needed in this case would be everyHours(n). You can install either manually, by following these steps, or programmatically, by running the following function once:
function createHourTrigger() {
var triggeredFunction = "shiftAndSet"; // Triggered function name, change accordingly
ScriptApp.newTrigger(triggeredFunction)
.timeBased()
.everyHours(1)
.create();
}
Note:
Your script has a certain amount of repetition and could be reduced to this (the code to retrieve jsonResponse is obviously missing):
function shiftAndSet() {
var range = SpreadsheetApp.getActiveSheet().getRange("E2");
range.setValue(jsonResponse.price);
range.insertCells(SpreadsheetApp.Dimension.ROWS);
}
Also, if you want E2 and not E3 to have the newest value at the end of each execution, you could change the order of actions, like this:
function shiftAndSet() {
var range = SpreadsheetApp.getActiveSheet().getRange("E2");
range.insertCells(SpreadsheetApp.Dimension.ROWS);
range.setValue(jsonResponse.price);
}
I have 2 lists with ranges C2:F21, which should be available to fill for all users in a certain period.
Range C2:F21 in the list A shouldn't be locked from 01.01.2020 to 05.01.2020, but in another time it should be locked for all users except me.
In the list B range C2:F21 shouldn't be locked only from 01.02.2020 to 05.02.2020.
I will be very grateful for any answer
The most "secure" way to handle this would be to actually save the data as a separate spreadsheet (only you have permission to edit) on the expiration date(s) which you could also automate with a script. You could then use a series of importrange function(s) to create a list of read only information if you need the users to see it in the master sheet. Anyone who is an editor of the spreadsheet has the ability (perhaps not the knowledge) to get around your cell protection in various creative ways.
If you still insist on using cell protection to accomplish this it is very simple, use the Protection class and add a trigger to the project using the little clock in the script editor, Select event source Time-driven and time based trigger Specific date and time.
First you would need to protect the ranges, something that you can do easily through the web UI by selecting the range you want to protect, right-clicking and selecting Protect range. You can do it too via Apps Script, by running a function like this one:
function protectRangeA() {
var ss = SpreadsheetApp.openById("your-spreadsheet-id"); // Change accordingly
var sheetA = ss.getSheetByName("your-sheet-name"); // Change accordingly
var protection = sheetA.getRange("C2:F21").protect();
var me = Session.getEffectiveUser();
protection.addEditor(me);
protection.removeEditors(protection.getEditors());
if (protection.canDomainEdit()) {
protection.setDomainEdit(false);
}
}
This function gets a sheet with a certain name from a spreadsheet with a certain id (as others said before me, for security reasons - to avoid people messing with your script - it would be better if it was a standalone script, not bound to your spreadsheet), and protects the range C2:F21, using protect(), and you will be the only editor.
Second, you want to unprotect this range at a certain date, an protect it again at another date. To do that, you could create time-driven triggers. You could, for example, use an atDate trigger, that will run the function you specify at the year, month and day you specify. So, if you want to unprotect a range at 01.01.2020, you could, for example, have this function:
function createUnprotectTriggerA() {
ScriptApp.newTrigger("unprotectRangeA")
.timeBased()
.atDate(2020, 1, 1)
.create();
}
This will fire a function called unprotectRangeA near midnight of 01.01.2020 (+/- 15 minutes). The function that will be run at this time, then, should unprotect the desired range. It could be something on the following lines:
function unprotectRangeA() {
var ss = SpreadsheetApp.openById("your-spreadsheet-id"); // Change accordingly
var sheetA = ss.getSheetByName("your-sheet-name"); // Change accordingly
var protections = sheetA.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
var protection = protections[i];
if (protection.canEdit()) {
protection.remove();
}
}
}
This function uses getProtections to get all range protections that exist in the sheet and, before checking if the current user can edit them via canEdit(), it removes these with the use of remove().
And to protect it again at 05.01.2020, have this trigger, which will fire the function protectRangeA (previously defined) at this date:
function createProtectTriggerA() {
ScriptApp.newTrigger("protectRangeA")
.timeBased()
.atDate(2020, 1, 5)
.create();
}
Finally, because you want to protect/unprotect several ranges and you would need several triggers, you could call all the functions that create triggers in the same function, which you would have to run once to set all the triggers you need (I didn't define createProtectTriggerB and createUnprotectTriggerB but it would be the same as with the other range):
function createTriggers() {
createProtectTriggerA();
createUnprotectTriggerA();
createProtectTriggerB();
createUnprotectTriggerB();
}
Please beware that, as CodeCamper said, if users have permission to edit the spreadsheet, they could potentially use a script to mess up with your protections. So, depending on the people who are editing this, maybe you should have a copy of the spreadsheet that only you can access.
I hope this is of any help.