Google Sheets: Cache IMPORTXML data - google-apps-script

So I am trying to just create a simple Movie list for myself and want to keep the IMDB/Meta/RT scores in a cell.
I have it working fine with something like this:
=IF(ISBLANK(A2),,IFERROR(importxml(("http://www.omdbapi.com/?apikey=**MYKEY**&t="&A2&"&r=xml&tomatoes=true&y=2018"),"root/movie/#imdbRating")/10))
This will return a value that I can keep in the cell. The PROBLEM is that it doesn't always refresh. Either google sheets is bugged, importxml, or the omdbapi. I have read others having the same issue.
A typical row looks like this:
[Black Panther] [February 16, 2018] [86%] [74%] [97%] [88%]
Since my data does not change much I wanted to somehow cache it. So if the importxml fails it won't blank out the cell, it will just keep whatever the last value was. Maybe I can do this by referring to another tab's cell? I did try that, but as soon as the other tab cell gets blanked out(because the importxml fails) so does the main tab's cell.
Thoughts?

I believe this requires an apps script that will add each change you make to row 1, to a corresponding target column 2 - if not empty/NA:
function onEdit(e) {
var sh = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
if(sh.getActiveCell().getColumn() == 1) {
if(e.value && e.value != '#N/A')
sh.getRange(e.range.rowStart, 2).setValue(e.value);
}
}
Goto Tools, Script Editor, from the menu and add the script. Make sure to run it once in the debugger and grant the required permissions.
Also, take note of this references:
Refresh data retrieved by a custom function in Google Sheet
How to debug Google Apps Script (aka where does Logger.log log to?)
https://webapps.stackexchange.com/questions/42204/how-do-i-automatically-update-a-cell-in-google-spreadsheets-to-the-most-recently

Related

Apps Script: How to capture oldValue of a cell after a change made by sheets api?

I am running a python script that updates my google sheet (named 'Notifications') through the sheets api every 5 minutes. The python script first clears and resizes the sheet to only two rows and then adds the new data on top and the sheet's size gets automatically expanded. I've done that not to have empty rows if my new data is smaller in size than the old one.
So the sheet is updated every 5 minutes but if nothing new happened in my data source, the new values are going to be the same as the old. In particular, the value in 'A2' is going to be the same as before the update. If something new has happened, the value in 'A2' is going to be different. I want to track that and get a pop-up message that notifies me that something new has happened after the update.
So as far as I understand I have to use onChange because onEdit only works if the changes are made by a human and cannot track changes made by the api. I'm uncertain how to do that though.
With onEdit I've achieved it and get a pop-up if I modify the content of 'A2' manually but nothing happens when python updates my sheet.
Here's the code with onEdit:
function onEdit(e) {
if (e.range.getA1Notation() === 'A2') {
let oldValue = e.oldValue
let newValue = e.newValue
if (oldValue != newValue) {
Browser.msgBox('The value in A2 changed from ' + oldValue + ' to ' + newValue);
}
}
}
So the end goal is to get a pop-up not every time python updates my sheet, but only if there's a change in the value of 'A2' after the update.
How can I do this?
The only way to get a pop-up when a a change is made through an API is by using polling function from client-side code. One way to do this is by using a sidebar to hold the client-side code.
You should store somewhere the old values then compare with the current values.
Related
How do I make a Sidebar display values from cells?
How to get all old values for a mulitple cell range with onEdit trigger
onEdit(e) not generating trigger event when cell value changes due to inbuilt function
You can use onChange trigger with a "ValueInputOption"="USER_ENTERED" to trigger a function when a script edits a sheet. But you can't show anything in the UI from the triggered context so getting a pop up will be impossible.

Simple `onEdit` Function Not Running for Google Sheets

I have a simple onEdit function that I use on other sheets. I have had no issue when using this in the past. As of late, this function does not run.
function onEdit(e) {
var sheet = e.range.getSheet();
var name = sheet.getSheetName()
Logger.log(name)
if(e.range.getA1Notation() === "C3" && name == 'Main') {
sheet.getRange("C4").clearContent()
}
}
I even added the Logger.log() call to check and make sure I wasn't insane. But nope, it won't run.
I have even tried making a test sheet with no additional code to interfere with the onEdit call... no dice.
Any ideas? I'm pretty lost on this one.
Thanks,
Marcus
Based on the script you posted, the function would only be applicable to a sheet named "Main" and would only affect Cell C4 if Cell C3 is edited.
First of all, Logger.log would not show anything if the script was not initiated from the script code itself, therefore, it would not be possible to check that way. Therefore, I checked using var ui = SpreadsheetApp.getUi() and creating an alert. everytime i made an edit.
Secondly, based on that check, the script that you provided did indeed run. Therefore, this leaves 4 possible sources of error. 1) the sheet that you are making the edit on is not named "Main". 2)the cell you are editing is not cell C3, 3) the cell C4 is empty and there is nothing to clear 4) You did not make the edit, i.e. the change in C3 was not yet input into the cell because you have not keyed in enter or selected a place outside of the cell.

Is it possible to set Notes with automatically inserted values using google sheets?

I was trying to figure out a way to insert Notes automatically, when i enter something in a specific cell. This thread was suggested to me and tried it out: https://support.google.com/docs/thread/13317657?hl=en. I changed the function mentioned a littlebit, to fit my project:
function onEdit(e) {
if(e.source.getActiveSheet().getName() === 'Testsheet') {
if(e.range.columnStart === 8 && (e.range.rowStart > 12 && e.range.rowStart < 21)) {
return e.range.offset(0, -6).setNote(e.value);
}
}
}
This way when I enter values in the range H13:H20, the same values get inserted as Notes on the corresponding (0, -6) offset cells. Which is a step in the right direction, but not yet what I'm truly trying to achieve. The problem is, this only works, when the values are entered manually. But for my project, I need the notes also to be set, when values are filled in automatically, using a function that fills in certain information in a row, depending on the selected dropdown choice.
In my sheet for example:
=if(B17="Test1",Testsheet!C10:Testsheet!H10)
When the Information is filled in that way, it doesn't give me the Note in the offset cell. Is there a way, so that it does?
Here is a link to my Testsheet, where you can see what I'm trying to do : https://docs.google.com/spreadsheets/d/16c2hVDM_FTnHWerKjnOauCopwVZSnKCwAMVi6-Z8vBo/edit?usp=sharing
Thanks for any tips and help in advance!
As you can see in the documentation:
Script executions and API requests do not cause triggers to run. For
example, calling Range.setValue() to edit a cell does not cause the
spreadsheet's onEdit trigger to run.
The only option would be to directly run the notes function with the values as parameter instead of inserting the "row 16" values, as updating the sheet by a script or formula won't trigger the function onEdit().

CopyTo: How to push data/copy a cell from one google spreadsheet to another google spreadsheet using google apps script?

I look for a solution to copy a specific cell value in the Source spreadsheet from tab "Sum all" to another Spreadsheet to the Target Spreadsheet Tab "Copy all". It should work every time i change the Value of Cell G10. Access to the Target sheet is granted before i enter any Value to G10.
(Source Tab Name is "Sum all:G10" - Sheet has 10 different Tabs)
(Target Tab Name is "CopyData:T12" - Sheet has 10 different Tabs)
Easy way
Use the built-in IMPORTRANGE() function in Google Apps:
In your Target cell, type the following formula
=IMPORTRANGE("FILE_ID_HERE","Sum all!G10:G10")
The syntax for this function is
=IMPORTRANGE("FILE_ID","SHEET_NAME!RANGE_START:RANGE_END")
When you first type in this function, you'll get an error in the cell. Simply click on it and select "Allow" to link the two sheets together. This error will occur even if it is the same spreadsheet. This function can link two separate spreadsheets, too, as long as you have edit access to both.
Hard Way
I'm assuming from your question that you want to copy values to and from the same spreadsheet document, but to different cells that are located on different sheets of the spreadsheet. The Google Apps Script API calls tabs "sheets" and the overall document "spreadsheet".
First, open the script editor
Open your spreadsheet that you'd like to make this script for.
Select "Tools" in the toolbar, then "Script Editor"
Second, make a function for onEdit.
Making a function named onEdit will create a function that runs every time the edit trigger is fired, using a no-authorization "simple trigger". Google Sheets automatically sends this event every time a cell is edited by a user. The argument e for the function is the event passed by the trigger.
function onEdit(e) {
// Get the sheet named "Sum all" from the active spreadsheet (i.e. the one you are editing)
var source = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sum all");
// Returns the active cell
var cell = source .getActiveCell();
// Compare to see if its the right cell you're looking for
// getRow and getColumn methods return integers for the row and column of the cell
// A = 1, B = 2, ... G = 7
if (cell.getRow() == 10 && cell.getColumn() == 7) {
// If its the right cell, copy to the other cell
var target = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("CopyData");
// set the value of the desired cell in the target sheet
target.getRange("T12").setValue(cell.getValue());
}
}
Third, save the script
Save the script, reload the file, and test it out.
If your tabs are on different spreadsheets
Change this line:
var target = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("CopyData");
to this:
var target = SpreadsheetApp.openById("FILE_ID").getSheetByName("CopyData");
and insert the file ID for the target spreadsheet where I've written FILE_ID.
You will also need to use an "installed trigger", since a simple trigger cannot open a remote spreadsheet. To do this, change the name (so it is no longer a simple trigger function), and follow the steps here

Custom function throws a "You do not have the permission required to setValue" error

I am trying to set some value to a cell in a Google Spreadsheet:
function exampleFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var range1 = sheet.getRange("A1");
var value1 = range1.getValue();
value1+=1;
range1.setValue(2);
return value1;
}
If I am trying to affect a cell with this function, this error appears:
You do not have the permission required to setValue. (line 10, file "ddd")
Do you know how I could make that possible? I actually want the affected cell to take the value of the cell A1 and increase the value of A1 by 1.
from the documentation :
Custom functions return values, but they cannot set values outside the cells they are in. In most circumstances, a custom function in cell A1 cannot modify cell A5. However, if a custom function returns a double array, the results overflow the cell containing the function and fill the cells below and to the right of the cell containing the custom function. You can test this with a custom function containing return [[1,2],[3,4]];.
reference : Custom Functions in Spreadsheets
It looks that you are using the above function as a custom function, in other words, it is called by cell formula on the Google Sheets UI, in the following way:
=exampleFunction()
Custom functions in Google Sheets have limitations like they can't be used to call Google Apps Script services that require permissions. The workaround is to use another mean to call the function:
Run it from the Google Apps Script Editor
Use a custom menu
Use a trigger
Also they could be called from dialogs and sidebars, Google Apps Script Web apps and by using the Google Apps Script execution API
It's just a little different than what we programmers think.
You can use setFormula in a Macro but not in a custom function.
Just create a simple macro from Tools > Macros > Record Macro, and then open the Script editor and change the Macro's code to your code...
Here is my Macro's code:
function SetFormula() {
var spreadsheet = SpreadsheetApp.getActive();
var formulaValue = spreadsheet.getRange('formulaText').getValue().toString();
spreadsheet.getRange('total').setFormula(formulaValue);
return formulaValue;
};
Then, to run your macro automatically (you can run that manually from Tools > Macros > YOUR-MACRO-NAME), just create a trigger as follows:
Open the Script Editor:
Then go to Triggers from the left side panel and tap on Add Trigger button:
Finally, create the trigger, select your Macro from the list (mine is SetFormula), select the Event Source as From SpreadSheet, the Event Type to On Edit, and save it.
That's it!
I named my ranges as FormulaText and total to be more flexible.
you can do that from here:
Custom functions do have permission limitations as noted above. They can run with a custom menu or you can insert an image and assign a custom script to it to use it like a button.
Using a Trigger is another way to accomplish something like this example, which makes it automatic.
A simple trigger in an App Script such as onSelectionChange(e) works without running into the permissions issue of putting a custom function into a cell. This trigger is newer than what was available in the original post. In the simple example below, cell A1 will turn white with an even integer and red with anything else. Granted, the speed at which the triggers fire may vary. It's not always as instantaneous as one might expect.
function onSelectionChange(e) {
const sheet = SpreadsheetApp.getActive()
var value1 = sheet.getRange("A1").getValue()
if(value1 % 2 == 0) {
sheet.getRange("A1").setBackground("#FFFFFF") //white
} else {
sheet.getRange("A1").setBackground("#FF0000") //red
}
}