ImportXML with Google Sheet and Auto Refresh Every Minute - google-apps-script

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

Auto Refreshing ImportXML Google Sheets

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())
}

Google Apps Script function works when run manually, but fails when run as a trigger

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.

IMPORTXML into a Google Apps Script with Automatic Update [duplicate]

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);
}

Cell reference out of range, google sheets

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)

Time driven function in Google script

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)