Reference data from cell then timestamp it - google-apps-script

I am currently using Sheetgo to timestamp data. Unfortunately my trial is about to expire. I'm assuming a script with a trigger would provide me with the same results, but I am unsure how to go about it.
I am pulling the value "A1" from an api:
Sheetgo then logs the value every hour on a separate sheet:
Any assistance with creating a script/trigger that could perform this task would be much appreciated. Thanks.

Logging a value every hour
function getA1() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Sheet0");//wherever the data is
const A1 = sh.getRange("A1").getValue();//wherever the data is
ss.getSheetByName("log").appendRow([A1,new Date()]);
}
Run the following atleast once
function ctrig() {
if(ScriptApp.getProjectTriggers().filter(t => t.getHandlerFunction() == "getA1").length == 0) {
ScriptApp.newTrigger("getA1").timeBased().everyHours(1).create();
}
}
Script App newTrigger

Related

LOCK column range at specific Date (month wise)

LINK TO THE SAMPLE FILE HERE I have a spreadsheet which contains multiple tabs in it on which data is filled by multiple users at a time which go direct to my master-sheet through IMPORT-range Function but this create a problem for me as a user can change Data or value at any time which further will reflect in master same which mean i have no control over the data as user can edit the values or the data according to the need.
i want to lock the value for the columns at every 15th of the EACH month
AS you can see in the Image which shown each Tab have month wise column which is to be filled by the user
Any help would be appreciated
any suggestions to it ?
Issue:
To do this, you'll have to use Google Apps Script.
A range cannot be protected for all users. At least the user executing user will still be able to edit this (this is not a feature of the solution proposed below; it's how Sheets work).
Solution:
Create a time-driven trigger that will fire the 15th of each month. This can be done manually or programmatically (by executing the function installTrigger below once, using onMonthDay). The triggered function (protectCurrentMonthColumn in the sample below) should do the following.
Get the month index and year of the current date (see Date).
Get a list of sheets to protect (retrieve all sheets via Spreadsheet.getSheets() and filter out the ones to ignore) and iterate through them.
For each sheet, get the column index of the header that contains current month date. You can compare monthIndex and year for that, and use findIndex to get the index.
Using the columnIndex, get the corresponding Range and protect it.
Code sample:
function protectCurrentMonthColumn() {
const SHEETS_TO_IGNORE = ["NDRX", "Verified NDRx"]; // Change according to your preferences
const now = new Date();
const monthIndex = now.getMonth(); // Current month
const year = now.getFullYear(); // Current year
const ss = SpreadsheetApp.getActive();
const sheetsToProtect = ss.getSheets().filter(s => !SHEETS_TO_IGNORE.includes(s.getSheetName())); // Filter out ignored sheets
sheetsToProtect.forEach(s => { // Iterate through sheets to protect
const headers = s.getRange("1:1").getValues()[0];
const columnIndex = headers.findIndex(header => { // Get index of the column to protect (header is current month and year)
return typeof header.getMonth === 'function' && header.getMonth() === monthIndex && header.getFullYear() === year;
});
if (columnIndex > -1) { // If header is not found, don't protect anything
const rangeToProtect = s.getRange(1,columnIndex+1,s.getLastRow()); // Column to protect
const protection = rangeToProtect.protect();
var me = Session.getEffectiveUser();
protection.addEditor(me);
protection.removeEditors(protection.getEditors());
if (protection.canDomainEdit()) {
protection.setDomainEdit(false);
}
}
});
}
function installTrigger() {
ScriptApp.newTrigger("protectCurrentMonthColumn")
.timeBased()
.onMonthDay(15)
.create();
}
Note:
You have to execute installTrigger once for this to work.

Google sheet script to shift content once a day

I would like to move the content of B2:F8 to C2:G8 (one column to the right) once a day, at 4am.
How can I achieve that?
Move Stuff one column to the right
function movestuff() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Enter you sheetname");
sh.getRange("B2:F8").moveTo(sh.getRange("C2:G8"));
}
Run the following function once to create the trigger
function createTrigger() {
if(ScriptApp.getProjectTriggers().filter(t => t.getHandlerFunction() == "moveStuff").length == 0) {
ScriptApp.newTrigger("moveStuff").timeBased().everyDays(1).atHour(4).create();
}
}
You can run AppScript triggers to run your custom function once a Day.
For example, you have a function to do this content shifting in your Apps Script.
Then go to your Triggers > Add Trigger > Choose your function to run once in a day > Select the Select event source to Time Driven > Select type of time-based trigger to Day Timer
That's it. It will run a function once a day or you can customize your event whatever you like
From #Cooper solution you can take his code & call it in a daily based event timer.
function contentShifter() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Enter you sheet tab name");
sh.getRange("B2:F8").moveTo(sh.getRange("C2:G8"));
}
Run the function contentShifter in daily event timer that's it. Hope it will help you.

Changing Cell Value based off of Date- App script build?

I was wondering if anyone could help build a script for a Macro Extension on my google sheet. We use sheets to track application statuses and would like an automation to be set.
My Goal:
I would like any application that is still in the "Pre-IC" agreement (column N) after 30 days of their application date(column H) to be automatically changed to "Delayed" in column N.
I have attached a screenshot to this, please let me know if further information is needed in order to build this script -- I'm a newbie at this I really need support
Description
You can use a Time Driven Trigger to run unattended periodically. You do this from the Script Editor Triggers.
The following script can be set to run daily or more frequently as needed.
I have created a simple mock up of your sheet with only 2 columns that you were interested in.
Code.gs
function statusTimer() {
try {
let sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet2");
let values = sheet.getRange(7,1,sheet.getLastRow()-6,sheet.getLastColumn()).getValues();
let today = new Date();
for( let i=0; i<values.length; i++ ) {
if( values[i][1] === "Pre IC Agreement" ) {
if( today.valueOf()-values[i][0].valueOf() > (30*24*60*60*1000) ) { // valueOf is in milliseconds
values[i][1] = "Delayed";
}
}
}
values = values.map( row => [row[1]] ); // extract only 2nd column
// Note my test sheet has only 2 columns. You need to adjust getRange() for your case
sheet.getRange(7,2,values.length,1).setValues(values);
}
catch(err) {
console.log(err);
}
}
Reference
Installable Trigger
Spreadsheet Service
Date Object
Array.map()
Try
function myFunction() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Sheet1');
var d = new Date();
var oneMonthAgo = new Date();
oneMonthAgo.setDate(d.getDate() - 30)
const date = sh.getRange('H7:H'+sh.getLastRow()).getValues()
const status = sh.getRange('N7:N'+sh.getLastRow()).getValues()
status.forEach(function(r,i){
if (r[0] == 'Pre-IC Agreement' && date[i][0] < oneMonthAgo){
r[0] = 'Delayed'
}
})
sh.getRange('N7:N'+sh.getLastRow()).setValues(status)
}

Google Apps Script takes very long (or doesn't run at all)

I have a simple script that checks the cell contents for how many dashes it contains and changes the cell colour accordingly. The function trigger is set to onEdit().
For some reason I don't know, this script takes at least a few seconds to run, if it even runs at all.
I would like to ask for help to explain why this takes very long to execute.
Thank you in advance.
function checkSix() {
var sheet = SpreadsheetApp.getActiveSheet();
var currentCell = sheet.getCurrentCell();
var contents = currentCell.getValue();
var amt = contents.replace(/[^-]/g, "").length;
if(amt >= 6)
currentCell.setBackground('red');
else if(amt == 5)
currentCell.setBackground('yellow');
else
currentCell.setBackground('white');
}
I thought that your script is correct. When I tested it, the process time was about 1 second. Although I'm not sure whether this is the direct solution of your issue, can you test the following modification?
Modification points:
In your script, the if statement of the multiple conditional branches is used.
From the benchmark, when the ternary operator is used, a process cost can be reduced a little.
Your function of checkSix() is executed by OnEdit trigger of onEdit.
From the benchmark, when the event object is used, a process cost can be reduced a little.
From or doesn't run at all, I thought that several users might use Google Spreadsheet.
In this case, I would like to propose to use LockService.
When above points are reflected to your script, it becomes as follows.
Modified script:
From your question, it supposes that in your situation, checkSix is called from onEdit as follows. Please be careful this. When the lock service is not used, the script becomes as follows.
function onEdit(e) {
checkSix(e);
}
function checkSix(e) {
var currentCell = e.range;
var amt = e.range.getValue().replace(/[^-]/g, "").length;
currentCell.setBackground(amt >= 6 ? 'red' : amt == 5 ? 'yellow' : 'white');
}
When the lock service is used, the script becomes as follows.
function onEdit(e) {
checkSix(e);
}
function checkSix(e) {
var lock = LockService.getDocumentLock();
if (lock.tryLock(10000)) {
try {
var currentCell = e.range;
var amt = e.range.getValue().replace(/[^-]/g, "").length;
currentCell.setBackground(amt >= 6 ? 'red' : amt == 5 ? 'yellow' : 'white');
} catch(er) {
throw new Error(er);
} finally {
lock.releaseLock();
}
}
}
Note:
In this answer, it supposes that your script is only the script shown in your question. If you run other functions when onEdit is run, my proposal might not be useful. Please be careful this.
If above modification was not the direct solution of your issue, as a test case, I would like to propose to try to create new Spreadsheet and test it again.
References:
Benchmark: Conditional Branch using Google Apps Script
Benchmark: Event Objects for Google Apps Script
Event Objects
function checkSix(e) {
const sh = e.range.getSheet();
if(sh.getName() == 'Your Sheet Name' && e.value ) {
let contents = e.value;
let l = contents.replace(/[^-]/g,'').length;
if(l>=6) {
e.range.setBackground('red');
}else if(l==5) {
e.range.setBackground('yellow');
} else {
e.range.setBackground('while');
}
}
Since you changed the name you will require an installable onEdit trigger. Or call it from onEdit(e) function.

How to Put Formula Count another sheet in App Script

my First ASK and last at 2020.
Im Newbie with google App Script, need some help
I had 2 Spreadsheet
Spreadsheet ssGETDATA with SHEET1 column(id,name,address,ts,visit)
Spreadsheet ssVERIFY with SHEET1 column (id,ts)
I send data from Android use this code :
function doGet(e) {
return message("Error: Please Try Again");
}
function doPost(e) {
var id= e.parameters.id[0];
var name= e.parameters.name[0];
var address= e.parameters.address[0];
var visit= e.parameters.visit[0];
var ts = Utilities.formatDate(new Date(), "GMT+8", "dd/MM/yyyy HH:mm:ss");
var ss = SpreadsheetApp.getActive();
var sh = ss.getSheets()[0];
sh.appendRow([id,name,address,ts,visit]);
return message("Data Saved");
} else {
return message("Error: Please Try Again");
}}}
function message(msg) {
return ContentService.createTextOutput(msg);
}
I want to Verify data from ssGETDATA but data (id) appeared several times.
so my idea every time append row executed it put formula in column (visit) with =count(id,ssVERIFYSheet1!id) to check it Verified or Not
how it applies in the google app script?
i hope when ssVERIFY changed then ssGETDATA column (visit) counting too.
thanks for your explanation. Happy new Year
The simplest way to check if a column has a value probably is using getValues(), and search for the row in the array:
const range = sheet.getRange('A2:A')
const hasBeenValidated = range.getValues()
.flat()
.some(value => value === id)
if (hasBeenValidated) {
// [...]
}
Notice that I assumed that id is on column A and that it has a header.
flat() is necessary because getValues() returns a 2D array (array of rows) and we only need an array of values.
With this snippet you can check for existing entries at ssGETDATA and/or ssVERIFY before making any changes. It should work for any column but you may need to change the value === id part depending on the type of it (dates, for example).
References
Range getValues() (Google Developers)
Array.prototype.flat() (MDN)
Array.prototype.some() (MDN)
SpreadsheetApp openByUrl(url) (Google Developers)