I have the script below which is importing some XML data in a google sheet called prices. Everything works fine except that I have set up a time driven trigger to run every minute but the data won't get updated.
The trigger seems to work fine, as I can see the last run time being updated every minute.
The script calling the XML data works fine as I can see the data being populated in the spreadsheet.
The XML feed works fine too, as I can see the time being updated every minute, also have a cron job.
I only have this function as a project.
function getData() {
var sheetName = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("prices");
var queryString = Math.random();
var cellFunction = '=ImportXML("http://myxmldata.com/data-xml.php","//data/date")';
sheetName.getRange('A2').setValue(cellFunction);
}
So what's wrong?
Here is how I solved my problem:
On your spreadsheet go to the top menu > click Tool > then Script Editor and add the following scripts:
This is the script to call your data e.g. XML. Please update the script with your own information. YOUR-SHEET-NAME, is the tab name e.g. "prices".
function getData() {
var sheetName = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("YOUR-
SHEET-NAME");
var queryString = Math.random();
var cellFunction = '=ImportXML("https://yoururl-xml.php","//trade/price")';
var range = sheetName.getRange('A2');
range.clearContent(); // You can also use range.setFormula("");
SpreadsheetApp.flush();
range.setFormula(cellFunction);
}
Below that script, add the following script, more information on this page: Periodically refresh IMPORTXML() spreadsheet function
YOUR-SHEET-ID is the long number in the spreadsheet url e.g. 1YTB12xSTMSNdoT_S1U67MtOUDTf6n4OL2tJLnTNAXYZ
function RefreshImports() {
var lock = LockService.getScriptLock();
if (!lock.tryLock(5000)) return; // Wait up to 5s for previous refresh to end.
var id = "YOUR-SHEET-ID";
var ss = SpreadsheetApp.openById(id);
var sheet = ss.getSheetByName("YOUR-SHEET-NAME");
var dataRange = sheet.getDataRange();
var formulas = dataRange.getFormulas();
var content = "";
var now = new Date();
var time = now.getTime();
var re = /.*[^a-z0-9]import(?:xml|data|feed|html|range)\(.*/gi;
var re2 = /((\?|&)(update=[0-9]*))/gi;
var re3 = /(",)/gi;
for (var row=0; row<formulas.length; row++) {
for (var col=0; col<formulas[0].length; col++) {
content = formulas[row][col];
if (content != "") {
var match = content.search(re);
if (match !== -1 ) {
// import function is used in this cell
var updatedContent = content.toString().replace(re2,"$2update=" + time);
if (updatedContent == content) {
// No querystring exists yet in url
updatedContent = content.toString().replace(re3,"?update=" + time + "$1");
}
// Update url in formula with querystring param
sheet.getRange(row+1, col+1).setFormula(updatedContent);
}
}
}
}
// Done refresh; release the lock.
lock.releaseLock();
}
Here is a screenshot of both scripts:
Then add the timer, go to the top menu click on the clock and add trigger. Make sure to select the right function i.e. RefreshImports.
Done!
Related
I have a sheet that imports data from a site, there are about 10 imports all slightly different. I want a way that I could update that every five minutes or so. I know there are scrips to do it but when I tried to use one it would just put ?update at the end and mess up the entire import. I can show the script I am using. It is from 4 years ago and maybe it is outdated. Any help would be appreciated.
Also just having a script that changes the = to nothing then adds it back again or something would work I guess.
Here is the script I am using
function RefreshImports() {
var lock = LockService.getScriptLock();
if (!lock.tryLock(5000)) return; // Wait up to 5s for previous refresh to end.
var id = "Sheets ID";
var ss = SpreadsheetApp.openById(id);
var sheet = ss.getSheetByName("Sheet Name");
var dataRange = sheet.getDataRange();
var formulas = dataRange.getFormulas();
var content = "";
var now = new Date();
var time = now.getTime();
var re = /.*[^a-z0-9]import(?:xml|data|feed|html|range)\(.*/gi;
var re2 = /((\?|&)(update=[0-9]*))/gi;
var re3 = /(",)/gi;
for (var row=0; row<formulas.length; row++) {
for (var col=0; col<formulas[0].length; col++) {
content = formulas[row][col];
if (content != "") {
var match = content.search(re);
if (match !== -1 ) {
// import function is used in this cell
var updatedContent = content.toString().replace(re2,"$2update=" + time);
if (updatedContent == content) {
// No querystring exists yet in url
updatedContent = content.toString().replace(re3,"?update=" + time + "$1");
}
// Update url in formula with querystring param
sheet.getRange(row+1, col+1).setFormula(updatedContent);
}
}
}
}
// Done refresh; release the lock.
lock.releaseLock();
// Show last updated time on sheet somewhere
sheet.getRange(12,2).setValue("Rates were last updated at " + now.toLocaleTimeString())
}
You can write a script that retrieves the formula from the specified cell and sets it back to the same cell on a timer
For this you need:
Retrieve the formula with getFormula()
Set the formula back into the same cell with getFormula()
Bind an installable time-driven trigger to your function specifying the desired interval
Sample:
function setMeOnTrigger() {
var sheet = SpreadsheetApp.getActive().getActiveSheet();
var cell = sheet.getRange("C1");
cell.setFormula(cell.getFormula())
}
I have two scripts:
One which adds an IMPORTJSON sheets function from bradjasper, works great.
The second one is that I want to refresh the scripts automatically, that does not work. The script does work when I ran it manually, and also the trigger does work according to the logs, but the data does not get refreshed.
I have tried all the different scripts, they do work, but the data imported on the sheet is not actually updated.
function RefreshImports() {
var lock = LockService.getScriptLock();
if (!lock.tryLock(5000)) return; // Wait up to 5s for previous refresh to end.
var id = "1Vt-rqQZ7iXsui8Nrr2XABusd4lUbGqxj4HkzsRkZFNA";
var ss = SpreadsheetApp.openById(id);
var sheet = ss.getSheetByName("Blad2");
var dataRange = sheet.getDataRange();
var formulas = dataRange.getFormulas();
var content = "";
var now = new Date();
var time = now.getTime();
var re = /.*[^a-z0-9]import(?:xml|data|feed|html|json|range)\(.*/gi;
var re2 = /((\?|&)(update=[0-9]*))/gi;
var re3 = /(",)/gi;
for (var row=0; row<formulas.length; row++) {
for (var col=0; col<formulas[0].length; col++) {
content = formulas[row][col];
if (content != "") {
var match = content.search(re);
if (match !== -1 ) {
// import function is used in this cell
var updatedContent = content.toString().replace(re2,"$2update=" + time);
if (updatedContent == content) {
// No querystring exists yet in url
updatedContent = content.toString().replace(re3,"?update=" + time + "$1");
}
// Update url in formula with querystring param
sheet.getRange(row+1, col+1).setFormula(updatedContent);
}
}
}
}
// Done refresh; release the lock.
lock.releaseLock();
// Show last updated time on sheet somewhere
sheet.getRange(1,1).setValue("Rates were last updated at " + now.toLocaleTimeString())
}
This script does run, as there are no errors in the logs. However, the data shown on the sheet does not change to reflect the current information from the API / JSON file.
This question already has answers here:
Periodically refresh IMPORTXML() spreadsheet function
(4 answers)
Closed last month.
I'm trying to get a Google sheets apps script to work for an IMPORTXML I'm using.
A1
=importxml("http://www.nfl.com/liveupdate/scorestrip/ss.xml","//#q")
A2
=importxml("http://www.nfl.com/liveupdate/scorestrip/ss.xml","//#h")
The data fills from A1:B16
According to a script I found on web to have it auto refresh:
function getData() {
var queryString = Math.random();
var cellFunction1 = '=IMPORTXML("' + SpreadsheetApp.getActiveSheet().getRange('A1').getValue() + '?' + queryString + '","'+ SpreadsheetApp.getActiveSheet().getRange('A2').getValue() + '")';
SpreadsheetApp.getActiveSheet().getRange('C1').setValue(cellFunction1);
var cellFunction2 = '=IMPORTXML("' + SpreadsheetApp.getActiveSheet().getRange('A4').getValue() + '?' + queryString + '","'+ SpreadsheetApp.getActiveSheet().getRange('A5').getValue() + '")';
SpreadsheetApp.getActiveSheet().getRange('C2').setValue(cellFunction2);
}
I don't know what I'm supposed to be putting/replacing in that code with mine. If someone could help me to explain what I'm supposed to be changing to get it to work in my sheet/provide some examples of how one might look that would be a huge help.
I appreciate
You can update the function by using getFormula() then setFormula() in a time-driven trigger function. Here is a code snippet from a related SO post:
/**
* Go through all sheets in a spreadsheet, identify and remove all spreadsheet
* import functions, then replace them a while later. This causes a "refresh"
* of the "import" functions. For periodic refresh of these formulas, set this
* function up as a time-based trigger.
*
* Caution: Formula changes made to the spreadsheet by other scripts or users
* during the refresh period COULD BE OVERWRITTEN.
*
* From: https://stackoverflow.com/a/33875957/1677912
*/
function RefreshImports() {
var lock = LockService.getScriptLock();
if (!lock.tryLock(5000)) return; // Wait up to 5s for previous refresh to end.
// At this point, we are holding the lock.
var id = "YOUR-SHEET-ID";
var ss = SpreadsheetApp.openById(id);
var sheets = ss.getSheets();
for (var sheetNum=0; sheetNum<sheets.length; sheetNum++) {
var sheet = sheets[sheetNum];
var dataRange = sheet.getDataRange();
var formulas = dataRange.getFormulas();
var tempFormulas = [];
for (var row=0; row<formulas.length; row++) {
for (col=0; col<formulas[0].length; col++) {
// Blank all formulas containing any "import" function
// See https://regex101.com/r/bE7fJ6/2
var re = /.*[^a-z0-9]import(?:xml|data|feed|html|range)\(.*/gi;
if (formulas[row][col].search(re) !== -1 ) {
tempFormulas.push({row:row+1,
col:col+1,
formula:formulas[row][col]});
sheet.getRange(row+1, col+1).setFormula("");
}
}
}
// After a pause, replace the import functions
Utilities.sleep(5000);
for (var i=0; i<tempFormulas.length; i++) {
var cell = tempFormulas[i];
sheet.getRange( cell.row, cell.col ).setFormula(cell.formula)
}
// Done refresh; release the lock.
lock.releaseLock();
}
}
Hope this helps.
I have been working on something related and finally solved it by using code from this StackOverFlow Post, but I needed to add some extra bits.
It wasn't working well for me, so I made some changes and added extra logging to make it understandable for me. Here is goes:
function RefreshImports() {
var lock = LockService.getScriptLock();
if (!lock.tryLock(5000)) return; // Wait up to 5s for previous refresh to end.
var now = new Date();
// Show start time on log
Logger.log("Starting Running at " + now.toLocaleTimeString());
var url = "URL OF YOUR SHEET";
var sheetName = "NAME OF YOUR SHEET";
var ss = SpreadsheetApp.openByUrl(url);
var sheet = ss.getSheetByName(sheetName);
var dataRange = sheet.getDataRange();
var formulas = dataRange.getFormulas();
var tempFormulas = [];
for (var row=0; row<formulas.length; row++) {
for (var col=0; col<formulas[0].length; col++) {
// Blank all formulas containing any "import" function
// See https://regex101.com/r/bE7fJ6/2
var re = /.*[^a-z0-9]import(?:xml|data|feed|html|range)\(.*/gi;
if (formulas[row][col].search(re) !== -1 ) {
tempFormulas.push({row:row+1,
col:col+1,
formula:formulas[row][col]});
sheet.getRange(row+1, col+1).setFormula(""); //cleans up the formula
}
}
}
// After a pause, replace the import functions
Utilities.sleep(500);
for (var i=0; i<tempFormulas.length; i++) {
var cell = tempFormulas[i];
sheet.getRange( cell.row, cell.col ).setFormula(cell.formula);
var nowLogger = new Date();
Logger.log("Update import from row " + cell.row + " col " + cell.col + " done at " + nowLogger.toLocaleTimeString());
Utilities.sleep(1000); //adding to try to control the amount of parallel connections from the Sheet
}
// Show Finished time on log
var now = new Date();
Logger.log("Sources from URLs were last updated at " + now.toLocaleTimeString());
// Done refresh; release the lock.
lock.releaseLock();
}
Then I added the Time-Based trigger from my project in https://script.google.com/home/triggers, and that one handles the automatic update execution.
I hope this helps!
It is actually not working. I mean it really does not refresh the cell whereas using a random number it works:
function getData() {
var queryString = Math.random();
var Xpath_1 = "/html/body/text()";
var importXpath_1 = '=IMPORTXML("' + 'http://www.pde-racing.com/trams/tram.php?id=999&valor=1&manega=Entrenament+1&dbTemps=Event999Ex.scdb&dbInscrits=Event999.scdb&name=&_=1616318271525' + '?' + queryString + '";"'+ Xpath_1 + '")';
SpreadsheetApp.getActiveSheet().getRange('B39').setValue(importXpath_1);
}
I'm getting this error while running this sheet.
Cell reference out of range (line 81, file "genreportSE")
I don't know why it says it's 'out of range'.
I tried to used 'copyvalues'. I saw a script where you can't really "print" a range, but you can create another spreadsheet, copy that range, then print that sheet and delete it.
How should I accomplish this?
function genreportSE() { // This function let us read the value of a cell from a sheet and change the value of another cell in a different sheet
var ss = SpreadsheetApp.getActive(); //ss stands for spreadsheet, this is the active spreadsheet
var clientsheet = ss.getSheetByName('Clientes SE');
var gensheet = ss.getSheetByName('Generador SE');
var clienttable = clientsheet.getDataRange();
var numberofservices = clienttable.getNumRows(); //The number of services in the Clientes sheet
var error1;
var error2;
var rangetocheck1;
var rangetocheck2;
var client;
var clientname;
var i=0;
var reportswitherrors = []; //Array for faulty reports
var email ='jvaldez#galt.mx';
var subject = "Reporte de producción y consumo - " + (new Date()).toString();
var body = "TEXT" ;
for (i=0;i<=2;i++){
gensheet.getRange('B2').setValue(clientsheet.getRange(i+2,1).getValue()); //This will change the cell "B2" in "Generador SE" to the current service number for the report generation
Utilities.sleep(3000); //A timer to let the importdata function get the data from the SE server in miliseconds
client = gensheet.getRange('B4').getValue;
clientname = String(client);
rangetocheck1 = gensheet.getRange('B8:C14').getValues(); //Data range that could present calculation errors ********
rangetocheck2 = gensheet.getRange('H8:H14').getValues(); //Data range that could present calculation errors ********
if(String(rangetocheck1).indexOf('#N/A') == -1) { //This checks if there are any errors in rangetocheck1
error1 = false;
} else {
error1 = true;
};
if(String(rangetocheck2).indexOf('#N/A') == -1) { //This checks if there are any errors in rangetocheck2
error2 = false;
} else{
error2 = true;
};
if(error1||error2){
reportswitherrors.push(clientsheet.getRange(i+2,1).getValue()); //This appends the current service number to the faulty services array
} else {
// Convert individual worksheets to PDF
var newSpreadsheet = SpreadsheetApp.create("Spreadsheet to export",15,60);
newSpreadsheet.getSheetByName('Sheet1').activate();
var newsheet = newSpreadsheet.getSheetByName('Sheet1');
var genRange = gensheet.getRange('A1:H50').copyValuesToRange(newsheet,0,10,0,55)
var pdf = DriveApp.getFileById(newSpreadsheet.getId()).getAs('application/pdf').getBytes();
var attach = {fileName:'Weekly Status.pdf',content:pdf, mimeType:'application/pdf'};
MailApp.sendEmail(email, subject, body, {attachments:[attach]});
DriveApp.getFileById(newSpreadsheet.getId()).setTrashed(true);
}
};
Logger.log(reportswitherrors);
}
It appears that you've got your row & column dimensions flipped between function calls. (Because Google decided to be inconsistent with the order of them...)
This line calls create(name, rows, columns):
var newSpreadsheet = SpreadsheetApp.create("Spreadsheet to export",15,60);
You've created a spreadsheet with 15 rows and 60 columns.
A bit further along, probably on line 81, copyValuesToRange(sheet, column, columnEnd, row, rowEnd) gets invoked:
var genRange = gensheet.getRange('A1:H50').copyValuesToRange(newsheet,0,10,0,55)
I am trying to download the data from a website every hour using Google script. The code is shown as below. When I manually run the function CSV_sgj, I can receive the desired information in email. But when I set the function as time driven (every hour), what I get are all #VALUE!.
I found a similar question and tried to change
var sheet = SpreadsheetApp.getActiveSheet();
to
var sheet = SpreadsheetApp.openById("0AtAYfCLk3-h7dDBnckdSZkNXbkZBLXBHV200SGtuZnc");
but it still does not work.
Many thanks in advance for help!
The full code is below.
function readRows() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
for (var i = 0; i <= numRows - 1; i++) {
var row = values[i];
Logger.log(row);
}
};
/**
* Adds a custom menu to the active spreadsheet, containing a single menu item
* for invoking the readRows() function specified above.
* The onOpen() function, when defined, is automatically invoked whenever the
* spreadsheet is opened.
* For more information on using the Spreadsheet API, see
* https://developers.google.com/apps-script/service_spreadsheet
*/
function CSV_sgj() {
readRows();
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange("A1:N32");
var data = range.getValues();
var csv = "";
for (var i = 0; i < data.length; ++i) {
csv += data[i].join(",") + "\r\n";
}
var csvFiles = [{fileName:"PSI5.csv", content:csv}]
MailApp.sendEmail("xxxxxx#gmail.com", "CSV", "", {attachments: csvFiles});
}
When you use
var sheet = SpreadsheetApp.openById("0AtAYfCLk3-h7dDBnckdSZkNXbkZBLXBHV200SGtuZnc");
the returned value is a spreadsheet object and what you want is a sheet object...
So you have to get the sheet by its name or by its index number. I suggest you change the variable names accordingly to keep things clear :
var ss= SpreadsheetApp.openById("0AtAYfCLk3-h7dDBnckdSZkNXbkZBLXBHV200SGtuZnc");
var sheet = ss.openByName('sheet1');// or any name you use (a string)
//or by its index
var sheet = ss.getSheets()[0] ;// 0 is the index of the first sheet in the spreadsheet (integer)