As a test, I have entered the following formula in cell K2 of my spreadsheet: =IF($M2=today(),"Today"). This acheives the desired effect and I would like this to be applied to all the rows below (with m3 referring to k3 , m4 to k4 etc.) , HOWEVER, this sheet is updated via Google Form so I cannot leave a formula in these cells as it will be overwritten.
Therefore I need to write and run the formula in apps script but, whilst I have enough knowledge of script language to do write basic If functions, this one is beyond my skills.
I have referred to this: How to get range and then set value in Google Apps Script and tried to adapt it to my purposes but to no avail.
Could someone please enlighten me?
Try this script:
function isMToday() {
sheet = SpreadsheetApp.getActiveSheet();
lastRow = sheet.getLastRow();
// get M2:M range
mRange = sheet.getRange(2, 13, lastRow - 1, 1);
// get display values instead to avoid timezone issues
mValues = mRange.getDisplayValues();
today = new Date();
// check every mValue, if today, return today, else false
output = mValues.map(mValue => {
mValueDate = new Date(mValue);
if (mValueDate.getDate() == today.getDate() &&
mValueDate.getMonth() == today.getMonth() &&
mValueDate.getFullYear() == today.getFullYear())
return ["Today"];
else
return [false];
});
// write output to K2:K
mRange.offset(0, -2).setValues(output);
}
After execution:
You do not need AppScript to do this even though it is on a Form Responses tab.
Instead of this formula in cell K2 (as you described):
=IF($M2=today(),"Today")
Use this formula in cell K1, and delete all the other formulas in column K:
=ARRAYFORMULA(IF(ROW(M:M)=1,"Today?",IF(M:M=TODAY(),"Today")))
Related
I'm completely new to writing app scripts. Have been trying to find an answer to my question online but unfortunately without any success. Therefore I'm reaching out to the community for help.
In my g sheet, I have two tabs one is an input tab (X) and the other one is an output (Y). What I am trying to accomplish is to send an email notification to a given email address every time a cell has been edited in the output tab (Y). The output tab has an editable range from B2:Y61. I have found plenty of stuff online on the "on edit" feature. However, non of them was close to my example mostly because they have been referring to getActivetSheet and not a specific one. The simpler code the better.
For instance, I found the following post very interesting. Unfortunately have no idea how to make it work with a specific tab name. Furthermore, if copied and pasted exactly, the formula returns an error, that row reference has not been declared.
https://spreadsheet.dev/send-email-when-google-sheet-is-edited
Look forward to hearing from you!
Best,
D
You can use this:
function onEdit(e) {
var editedSheet = e.source.getActiveSheet();
var editedCell = e.range;
var row = editedCell.getRow();
var col = editedCell.getColumn();
//Check if B2:Y61 was modified in Sheet "Y" based on its row and column index
if(row>=2 && row<=61 && col>=2 && col<=25 && editedSheet.getName()=="Y"){
//Send email
var subject = "Sheet: "+editedSheet.getName();
var message = "Cell "+editedCell.getA1Notation()+" was modified from '"+e.oldvalue+"' to '"+e.value+"'";
MailApp.sendEmail("email address",subject,message);
}
}
What it does?
Using the Google Sheets events, you can get the active sheet that is being modified using e.source, get the cell being modified using e.range and even get the old and the new value of the cell using e.oldValue and e.value
You just need to include a condition to check if the cell modified is within the range that you prefer B2:Y61 which should have a min row = 2, max row = 61, min col = 2 and max col = 25 and sheet name should be "Y".
Send an email using MailApp.sendEmail() if all the conditions were met.
Note:
You need to create this as an installable trigger since it will use mail service which requires authentication.
To manually create an installable trigger in the script editor, follow these steps:
OUTPUT:
(UPDATE)
This code will copy changes done in X_input sheet to Y_input sheet and check if email should be sent when B2:Y61 range was modified.
function onEdit(e) {
var editedSheet = e.source.getActiveSheet();
var editedCell = e.range;
var row = editedCell.getRow();
var col = editedCell.getColumn();
var ySheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Y_output");
//Check if edit is done in Sheet "X_input"
if(editedSheet.getName()=="X_input"){
//copy modified cell to Y_output sheet
ySheet.getRange(row,col).setValue(e.value);
//check if modified cell is within B2:Y61 range and send an email
if(row>=2 && row<=61 && col>=2 && col<=25){
//Send email
var subject = "Sheet: "+editedSheet.getName();
var message = "Cell "+editedCell.getA1Notation()+" was modified from '"+e.oldvalue+"' to '"+e.value+"'";
MailApp.sendEmail("ronoel#google.com",subject,message);
}
}
}
I'm using work tracker in Google Sheets. I have columns from A to U. I have "Status" in "L" column with drop down options - Inquiry Status Pending, Reconciliation Done, Waiting for IM Response. When I set the Status to "Inquiry Status Pending" I want timestamp to be populated in "N" column and it should be static. Like wise - for Reconciliation Done, timestamp should be in "E" column. For "Waiting for IM Response" status, timestamp should be in "R" column. All timestamps should be static
The best way to insert a timestamp on edit event is a Google Apps Script
Apps Script offers many useful functions to work with Google spreadsheets. It also contains triggers like onEdit that fire when an edit takes place in your spreadsheet and allow you retrieve the event objects (useful for determining e.g. the edited column). The best way to become familiar with Apps Script is following the official tutorial.
How to implement your request in Apps Script:
Go from your spreadsheet on Tools -> Script editor
Paste the script bellow
Save the script and click on the "play button" (to run the script)
Ignore the error message you receive. You script now works correctly.
function onEdit(e) {
var range = e.range;
var column = range.getColumn();
Logger.log('edited');
if (column == 12){
var timestamp = new Date();
var row = range.getRow();
var value = e.value;
var sheet = e.source.getActiveSheet();
Logger.log("edited cell: "+range.getA1Notation());
if( value == "Inquiry Status Pending"){
sheet.getRange(row, 14).setValue(timestamp);
} else if(value == "Reconciliation Done"){
sheet.getRange(row, 5).setValue(timestamp);
} else if(value == "Waiting for IM Response"){
sheet.getRange(row, 18).setValue(timestamp);
}
}
}
I recommend you to spend some time to study Apps Script so you understand how the provided code works and can adapt it to your needs.
The script has the purpose of writing the datetimestamp to a specific cell the moment when a cell in column 4 has the text Assign.
I have a script that theoretical works but I'm getting my data from Appsheet. The problem with this is that Appsheet writes data into my sheet but the script wont see it as edited cells, so it wont write the time stamp.
But my knowledge about Apps Scripts is pretty bad. And I am getting errors with the source line and the col, val lines.
function onEdit(e) {
var sh = e.source.getActiveSheet();
var row = e.range.getRow();
var col = e.range.getColumn();
var val = sh.getRange(row, 4).getValue();
//check if sheet is 'Blad1' & value is 'Assign'
if (sh.getSheetName() === 'Blad1' && val == 'Assign') {
var tz = e.source.getSpreadsheetTimeZone();
var date = Utilities.formatDate(new Date(), tz, 'dd-MM-yyyy hhmmss');
//set date in column 14 for same row
sh.getRange(row, 14).setValue(date);
}
}
I want to convert my script to a manually run script with a time-based trigger of 1 min. That way I hope the script will see the changed cell to Assign.
The problem is rooted in apps script triggers restrictions, specifically in their inability to listen to script-based events. I would suggest deploying your script as a WebApp with doGet() / doPost() functions to listen to API requests and, if I am correct in assuming that AppSheet works similarly to Zapier, etc., add a step that calls your WebApp after making changes to your Spreadsheet.
Please, see this guide on WebApps, its pretty straightforward to follow.
P.s. Btw, refrain from creating triggers acting as event listeners, you will easily cap your quotas!
I attached a copy of the spreadsheet I run in google. The concept is when someone begins picking an order, they will for instance type their name in I3 and their time will start. once complete they will type their name in J3 , each causing a time stamp below leading to a total duration time. later it will factor percentages.
The problem is the time stamps seem to randomly update without prompting to do so. it seems to be when it is printed or reopened. This will cause inaccuracies in the times and percentages. Any assistance would be greatly appreciated
It would appear that I should write a script to accommodate this need, but I haven't the slightest on how to do this. I was directed to this forum from a reply in google docs help forum
enter link description here
I don't think you can choose on which cell you update or recalculate (Tell me if I'm wrong).
A non-optimized workaround (Maybe some expert have got better solution):
function onEdit(e){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var i = 3;
while(i <= sheet.getLastRow())
{
var data = sheet.getRange(i, 9, 3, 2).getValues();
if(data[1][0].length == 0)
{
if(data[0][0] != "")
{
var d = new Date();
sheet.getRange(i+1, 9).setValue(d);
}
}
if(data[1][1].length == 0)
{
if(data[0][1] != "")
{
var d = new Date()
sheet.getRange(i+1, 10).setValue(d);
}
}
i=i+3;
}
}
The onEdit(e) function is a simple trigger implement in Google Spreadsheet. It will trigger each time a cell is edit.
On this code above, we iterate to check on each element if a name was added or not. If so, the code will set the date and time below the name just added. Since you set the value on one single moment, no auto update will modify those time.
for this workaround, you need to remove the formula you put on the cell where you want your timestamp (i.e I4 and J4). You can keep the cells which calculate the difference between time, they should work with no problem.
Paste below script in the script editor and save it.
function onEdit(e) {
if (e.range.rowStart % 3 != 0 || [9, 10].indexOf(e.range.columnStart) == -1) return;
var o = e.range.offset(1, 0);
if (!o.getValue()) o.setValue(new Date())
}
Don't run the script by clicking the play button in the script editor. Instead to back to any of the tabs, clear all cells where you now have the =now() formula and enter a name. See if the timestamp appears.
I am using ImportXML in a google docs sheet to aqcuire data from the sistrix api. It works fine but I encountered the limitation of 50 ImportXML commands in one sheet. So I used a script that writes the ImportXML command to a cell (temporary) formula and takes back the resulting value of the cell and copies it to the destination cell. So you can do as much ImportXML queries as you need, as they only appear in one temporary cell in the sheet.
The problem here is, that the ImportXML query SOMETIMES takes very long or returns with N/A.
Is it possible that my script sometimes doesnt wait for the ImportXML query to return and so the result is corrupted? I am currently doing it in this way:
function GetFormulaData(formula, sheet, row, col)
{
// write the formula (ImportXML(...)) to the specified cell
sheet.getRange(row, col).setFormula(formula);
// return the value of this cell resulting from the formula
return sheet.getRange(row, col).getValue();
}
So this can obviously only work if the formula (the ImportXML query) is done and has written the return value into the cell, so I can read afterwards.
Does anybody have experience or alternatives with calling ImportXML from a script?
I have solved this now in a different way. It is more common to use UrlFetchapp() within google doc scripts than ImportXML. But you have to gain the xml data yourself from the http response.
I do it in this way now:
var response = UrlFetchApp.fetch(apiString).getContentText();
var xmlContent = Xml.parse(response, true);
var answer = xmlContent.response.answer;
// get what you need from the XML answer
if (answer != null)
{
var element = answer.getElement('foo');
if (element != null)
{
var attrib = element.getAttribute('bar');
if (attrib != null)
value = attrib.getValue(); // the value you want
}
}