function chk(){
//PDD data setting
var today=new Date();
var Date = Utilities.formatDate(new Date(today.getFullYear(),today.getMonth(),today.getDate()), Session.getScriptTimeZone(), "yy-MM-dd");
[{
sheetName: "Sheet1" ,
folderID: '(google drive Folder)',
sheetId: '(sheetId1)',
tab: ['1', '2', '3', '4'],
conver: Date
},{
sheetName: "Sheet2" ,
folderID: '(google drive Folder)',
sheetId: '(sheetId1)',
tab: ['1', '2', '3', '4'],
conver: Date
}{
sheetName: "Sheet3" ,
folderID: '(google drive Folder)',
sheetId: '(sheetId1)',
tab: ['1', '2', '3', '4'],
conver: Date
}].map(obj => cloneValues(obj))
}
function cloneValues(obj){
let string = obj.conver;
let A = obj.sheetName;
let B = DriveApp.getFolderById(obj.folderID);
let C = DriveApp.getFileById(obj.sheetId);
var totalurl = PastetoDrive.makeCopy('Date '.concat( obj.conver , ' ',obj.sheetName ), dstFolder).getUrl();
var copy_url = totalurl.split("/");
let linkSheet = SpreadsheetApp.openById('(Backup Link sheetId)').getSheetByName('LINK');
linkSheet.appendRow([ string , A , totalurl ]);
for(let i = 0; i < obj.tab.length ; i++ ){
let fromSheet = SpreadsheetApp.openById(obj.sheetId).getSheetByName(obj.tab[i]);
let toSheet = SpreadsheetApp.openById(copy_url[5]).getSheetByName(obj.tab[i]);
setValues(fromSheet,toSheet)
}
}
function setValues(fromSheet,toSheet){
let maxCols = fromSheet.getMaxColumns();
let maxRows = fromSheet.getMaxRows();
let frvalues = fromSheet.getRange(1,1,maxRows,maxCols).getValues();
toSheet.getRange(1,1,maxRows,maxCols).setValues(frvalues);
}
Exception: Service error: Spreadsheets
I am writing and using Google AppScript, a work sheet that performs daily backups.
Exception: Service error: Spreadsheets error started to occur about a month ago in a script that I have been using well.
Looking at the log, there was a problem with setValues.
It seems that the ranges of fromSheet and toSheet don't match.
Daily backups should be stored as values in fromSheet > toSheet .
function setValues(fromSheet,toSheet){
const range = fromSheet.getDataRange().getValues();
return toSheet.range;
}
Once I changed the function setValues syntax and used it, I tried using setValues(range) to save it as a value, but it was not enough.
I'm a script beginner. I am testing and using various articles I need on stackoverflow and GitHub by combining and changing them.
Can anyone help me again this time?
function setValues(fromSheet,tosheet){
try {
const range = fromSheet.getRange(1, 1, tosheet.getMaxRows(), tosheet.getMaxColumns());
const values = range.getValues();
const toRange = tosheet.getRange(1, 1, tosheet.getMaxRows(), tosheet.getMaxColumns()).getA1Notation();
return tosheet.getRange(toRange).setValues(values);
} catch (e) {
Logger.log(e);
}
}
to-be
as-is
As a result, it was successful.
Posting a question and waiting for help from many people, I tried various methods, but nothing improved and I kept getting Service Error: Spreadsheet text.
In the end, I changed some of the setValues syntax and
I tried setting the range again, but the same error occurred.
After confirming that the result of the clone sheet is the result I want,
Decided to avoid this issue.
catch(e)
Thank you for your interest.
Related
I have a dataset which contains images in col C loaded via formula =IMAGE("") and the need is to refresh the data and have these formulas load the images at destination.
I tried the Spreadsheet API, but handling the data the way it's needed it still far to me - knowledge wise.
I try with the script below, but the column C shows as blank at destination:
function getOrdersData() {
const srcFile = SpreadsheetApp.openById('XXXXXXXXXXX');
const srcSht = srcFile.getSheetByName('Orders');
let srcData = srcSht.getRange(1, 1, srcSht.getLastRow(),
srcSht.getLastColumn()).getValues();
const orderHeaders = srcData[4]; //Colunm headers are actually in row 05
const imgCol = orderHeaders.indexOf('Image');//Whish is where the formulas loading the imgs are
const imgFormulas = srcSht.getRange(1, imgCol + 1, srcSht.getLastRow(), 1).getFormulas();
srcData.forEach(function (row) {
row.splice(imgCol, 1, imgFormulas);
});
const dstFile = SpreadsheetApp.openById('XXXXXXXXXXXXXXX');
const dstSht = dstFile.getSheetByName('Orders v2');
const dstShtLr = dstSht.getLastRow();
if (dstShtLr > 0) {
dstSht.getRange(1, 1, dstShtLr, dstSht.getLastColumn()).clearContent();
}
dstSht.getRange(1, 1, srcData.length, srcData[0].length).setValues(srcData);
}
What can I try next?
In your script, imgFormulas is a 2-dimensional array. In this case, by srcData.forEach(,,,), srcData is not 2 dimensional array. I thought that this might be the reason for your issue. When your script is modified, how about the following modification?
From:
srcData.forEach(function (row) {
row.splice(imgCol, 1, imgFormulas);
});
To:
srcData.forEach(function (row, i) {
if (i > 4) row.splice(imgCol, 1, imgFormulas[i][0]);
});
if (i > 4) was used for considering Colunm headers are actually in row 05.
Note:
In your situation, when Sheets API is used, the sample script is as follows. In this case, please enable Sheets API at Advanced Google services. When the number of cells are large, this might be useful.
function sample() {
const srcSSId = '###'; // Please set source Spreadsheet ID.
const dstSSId = '###'; // Please set destination Spreadsheet ID.
const srcSheetName = 'Orders';
const dstSheetName = 'Orders v2';
const srcValues = Sheets.Spreadsheets.Values.get(srcSSId, srcSheetName).values;
const srcFormulas = Sheets.Spreadsheets.Values.get(srcSSId, srcSheetName, { valueRenderOption: "FORMULA" }).values;
const data = [{ range: dstSheetName, values: srcValues }, { range: dstSheetName, values: srcFormulas }];
Sheets.Spreadsheets.Values.batchUpdate({ valueInputOption: "USER_ENTERED", data }, dstSSId);
}
References:
Method: spreadsheets.values.get
Method: spreadsheets.values.batchUpdate
Continuing my previous question, #Tanaike proposed a solution to extract performance score from the following page:
This is the code snippet to get around it:
function CheckPageSpeed(url) {
const apiKey = "###"; // Please set your API key.
const apiEndpoint = `https://www.googleapis.com/pagespeedonline/v5/runPagespeed?key=${apiKey}&url=${encodeURIComponent(url)}&category=performance`;
const strategy = ["mobile"];
const res = UrlFetchApp.fetchAll(strategy.map(e => ({ url: `${apiEndpoint}&strategy=${e}`, muteHttpExceptions: true })));
const values = res.reduce((o, r, i) => {
if (r.getResponseCode() == 200) {
const obj = JSON.parse(r.getContentText());
o[strategy[i]] = obj.lighthouseResult.categories.performance.score * 100;
} else {
o[strategy[i]] = null;
}
return o;
}, {});
return values.mobile;
}
As I am using it in Google sheets as custom formula, sometimes it takes so much time that the sheet throws the following error:
Is there any way that we can counter this error so that it starts calculating the score again instead of throwing an error? Thank you.
Issue and workaround:
From your showing image, your error of Exceed maximum execution time and your updated script, in this case, it is considered that the execution time of the script is over 30 seconds. (In the current stage, the maximum execution time of the custom function is 30 seconds. Ref) In this case, when the error of Exceed maximum execution time occurs, unfortunately, this cannot be used as the trigger. And also, in the current stage, UrlFetchApp cannot be stopped over time. And, for example, even when all URLs are retrieved and each value is retrieved from the API, I'm not sure whether the processing time is over 6 minutes. I'm worried about this.
From the above situation, how about manually rerunning only the custom functions which occur the error?
Sample script:
Before you use this script, please enable Sheets API at Advanced Google services. How about executing this function by a button on Spreadsheet and/or the custom menu?
function reCalculation() {
const sheetName = "Sheet1"; // Please set sheet name.
const formula = "=CheckPageSpeed"; // Please set the function name of your custom function.
const dummy = "=sample";
const ss = SpreadsheetApp.getActiveSpreadsheet();
const ssId = ss.getId();
const sheet = ss.getSheetByName(sheetName);
const sheetId = sheet.getSheetId();
const values = sheet.getRange("B1:B" + sheet.getLastRow()).getDisplayValues();
const requests = values.reduce((ar, [a], i) => {
if (a == "#ERROR!") {
ar.push({ findReplace: { range: { sheetId, startRowIndex: i, endRowIndex: i + 1, startColumnIndex: 1, endColumnIndex: 2 }, find: `^${formula}`, replacement: dummy, includeFormulas: true, searchByRegex: true } }); // Modified
}
return ar;
}, []);
if (requests.length == 0) return;
Sheets.Spreadsheets.batchUpdate({ requests }, ssId);
SpreadsheetApp.flush();
requests.forEach(r => {
r.findReplace.find = dummy;
r.findReplace.replacement = formula;
r.findReplace.searchByRegex = false;
});
Sheets.Spreadsheets.batchUpdate({ requests }, ssId);
}
When this script is run, only the cells of #ERROR! in the column "B" are recalculated.
Note:
I thought that in this case, this function might be able to be executed by the time-driven trigger. But, in that case, it might affect the quotas (maximum execution time is 90 minutes/day) of the time-driven trigger. So, in this answer, I proposed to run this function using manual operation.
References:
Method: spreadsheets.batchUpdate
FindReplaceRequest
Added:
For example, in your situation, how about directly requesting the API endpoint using fetchAll method? The sample script is as follows. In this case, the URLs are retrieved from the column "A" and the values are retrieved and put to the column "C" in your sample Spreadsheet.
Sample script:
Please set your API key. And, please run this script with the script editor. By this, the values are retrieved using the API.
function reCalculation2() {
const apiKey = "###"; // Please set your API key.
const sheetName = "Sheet1"; // Please set sheet name.
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName(sheetName);
const values = sheet.getRange("A2:A" + sheet.getLastRow()).getValues();
const requests = values.map(([url]) => {
const apiEndpoint = `https://www.googleapis.com/pagespeedonline/v5/runPagespeed?key=${apiKey}&url=${encodeURIComponent(url)}&category=performance&strategy=mobile`;
return { url: apiEndpoint, muteHttpExceptions: true };
});
const res = UrlFetchApp.fetchAll(requests);
const v = res.map(r => {
if (r.getResponseCode() == 200) {
const obj = JSON.parse(r.getContentText());
return [obj.lighthouseResult.categories.performance.score * 100];
}
return [null];
});
sheet.getRange(2, 3, v.length).setValues(v);
}
In this case, fetchAll method is used. By this, I thought that the error of Exceeded maximum execution might be able to be avoided.
I am very new to programming in general, but excited about what I've been able to do so far for work projects. I'm wondering why in the program below, the variable companyID is not populating into the template literal string. I've tried putting it inside and outside the loop, and while I am not getting an error message, it just comes out as a space.
let activeSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('foreachloop');
let range = activeSheet.getRange(1, 1, 5, 4);
let values = range.getValues();
//FOR EACH LOOP
values.forEach(function(value, index){
let companyID = activeSheet.getRange(1, 9).getValue();
if (index ===0) return;
let descriptionVariable = `Employee Number ${companyID} ${value[0]} ${value[1]} has a current status of ${value[2]}`
activeSheet.getRange(index + 1, 4).setValue(descriptionVariable);
})
}```
Try it this way:
function lfunko() {
const sh = SpreadsheetApp.getActive().getSheetByName('foreachloop');
const values = sh.getRange(1, 1, 5, 4).getValues();
const companyID = sh.getRange(1, 9).getValue();
values.forEach(function (value, index) {
if (index > 0) {
sh.getRange(index + 1, 4).setValue(`Employee Number ${companyID} ${value[0]} ${value[1]} has a current status of ${value[2]}`);
}
});
}
check your runtime environment. It must be set to V8 if you want to use this feature. See https://developers.google.com/apps-script/guides/v8-runtime
check the content of the cell you are pulling companyID from. Is it empty? You can console.log(companyID) or run a debugger to check
I am trying to write code in Google Apps Script that will dump the data shown on the url https://coinmarketcap.com/ into a Google Sheet (say starting in A1). Not just data for one symbol, but all the symbols shown on this page. Specifically I am looking for the data for 'symbol' 'name' 'price' 'market_cap' .
The API documentation is here: https://coinmarketcap.com/api/documentation/v1/#operation/getV1CryptocurrencyListingsLatest
I used to use an API connector to do this, but would rather a couple of lines of code. I spent a couple of hours reading about different approaches from search results, but they were either appropriate for a single symbol, or involved too many requests.
My code is below. I am not getting error, but it isn't returning any data either. I believe I need to tweak 'setValue' but am not sure how to do it.
Would appreciate any help. Thank you!
function coin_price() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Coins')
var requestOptions = {
method: 'GET',
uri: 'https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?aux=cmc_rank',
qs: {
start: '1',
limit: '5000',
convert: 'USD',
},
headers: {
'X-CMC_PRO_API_KEY': 'MY API KEY'
},
json: true,
gzip: true,
};
var url = `https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?aux=cmc_rank`;
var result = UrlFetchApp.fetch(url, requestOptions);
var txt = result.getContentText()
var d = JSON.parse(txt);
sheet.getRange(100,1).setValue(d.data.market_cap)
}
Suggestion
Perhaps you can try this tweaked script below:
Script:
function coin_price() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Coins')
var requestOptions = {
method: 'GET',
uri: 'https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?aux=cmc_rank',
qs: {
start: '1',
limit: '5000',
convert: 'USD',
},
headers: {
'X-CMC_PRO_API_KEY': 'API_Key'
},
json: true,
gzip: true,
};
var url = `https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?aux=cmc_rank`;
var result = UrlFetchApp.fetch(url, requestOptions);
var txt = result.getContentText()
var d = JSON.parse(txt);
var dSymbol = [];
var name = [];
var price = [];
var marketCap = [];
for(i=0; i<d.data.length; i++){
marketCap.push([d.data[i].quote.USD.market_cap]);
dSymbol.push([d.data[i].symbol]);
name.push([d.data[i].name]);
price.push([d.data[i].quote.USD.price])
}
//getRange structure (starting row, start col, total number of rows,total number of cols)
sheet.getRange(1,1,dSymbol.length,1).setValues(dSymbol); //Symbol in Col A (start col #1)
sheet.getRange(1,2,name.length,1).setValues(name); //Name in Col B (start col #2)
sheet.getRange(1,3,price.length,1).setValues(price); //Price in Col C (start col #3)
sheet.getRange(1,4,marketCap.length,1).setValues(marketCap); //Market Cap in Col D (start col #4)
}
Sample Result:
Reference:
getRange(row, column, numRows, numColumns)
setValues(values)
What I have done:
I have Google Analytics Premium
I have authorized OAuth2 for Apps Script by following this instruction: https://github.com/googlesamples/apps-script-oauth2
I have enabled Google Analytics API and Drive API on Advanced Google Services and on the Developers Console.
I'm trying to follow this instruction to request the unsampled report: https://developers.google.com/analytics/devguides/config/mgmt/v3/mgmtReference/management/unsampledReports/insert#request
My problem:
I've written the code below on Google Apps Script editor to request unsampled report from Google Analytics API. As far as I know, if it works correctly, it's supposed to trigger the unsampled request on Google Analytics interface. However, I don't see it in the Pending or Completed section in the interface. And when I ran the code, nothing happened. I didn't even see any error. Could you please help on what I have done wrong? Thank you in advance.
Here is my code:
function insertView()
var request = gapi.client.analytics.management.unsampledReports.insert(
{
'accountId': 'XXXXXX',
'webPropertyId': 'UA-XXXXXX-XX',
'profileId': 'XXXXXXXX',
'resource': {
'title': 'A test Report',
'start-date': '2016-03-31',
'end-date': '2016-04-04',
'metrics': 'ga:itemRevenue',
'dimensions': 'ga:date'
}
});
return request;
}
}
function outputToSpreadsheetNext(request) {
var sheetId = '1RSkx8n-YRMq7Cnco-mvC83bJPKSnsb3QPx3BItAWmN8';
var sheetPrevious= SpreadsheetApp.openById(sheetId).getSheets()[0];
var headerNamesPrevious = []
for (var i = 0, header; header = request.getColumnHeaders()[i]; ++i) {
headerNamesPrevious.push(header.getName());
}
sheetPrevious.getRange(1, 1, 1, headerNamesPrevious.length)
.setValues([headerNamesPrevious]);
// Print the rows of data.
sheetPrevious.getRange(2, 1,request.getRows().length,headerNamesPrevious.length)
.setValues(request.getRows());
}
}
I have written instructions on how to do it here: http://sophearychiv.com/how-to-pull-and-automate-unsampled-reports-from-google-analytics-into-google-spreadsheet/
Here's a working version you might want to try.
Instructions
Create a new Google Spreadsheet.
Copy the content bellow into a new script
Go into Resources > Advanced Google Services
Enable the Google Analytics API toggling it to ON
Click the Google Developer Console link still on the Advanced Google Services dialog
From the Cloud API Manager find and Enable the Analytics API
Now you can run the function insertReport(), this will insert an Unsampled Report using the API. Remember that just like I told you in the previous question, these may take a few hours to process.
Run the updateAllReports() function after a while and it should try to get updated status for the reports.
As a bonus, if the status is complete it will give you the link to the file on Google Drive and also import the data from the CSV into a second sheet.
var LOG_SHEET_NAME = 'Unsampled Report Logs';
var ss = SpreadsheetApp.getActive();
var ui = SpreadsheetApp.getUi();
function insertReport() {
var resource = {
'title': 'A test Report',
'start-date': '2016-03-31',
'end-date': '2016-04-04',
'metrics': 'ga:itemRevenue',
'dimensions': 'ga:date'
};
var accountId = 'XXXXXXXX';
var webPropertyId = 'UA-XXXXXXXX-1';
var profileId = 'YYYYYYYY';
try {
var request = Analytics.Management.UnsampledReports.insert(resource, accountId, webPropertyId, profileId);
} catch (error) {
ui.alert('Error Performing Unsampled Report Query', error.message, ui.ButtonSet.OK);
return;
}
var sheet = ss.getSheetByName(LOG_SHEET_NAME);
if (!sheet) {
sheet = ss.insertSheet(LOG_SHEET_NAME);
sheet.appendRow(['User', 'Account', 'Web Property', 'View', 'Title', 'Inserted Time', 'Updated Time', 'Status', 'Id', 'File']);
sheet.getRange(1, 1, 1, 10).setFontWeight('bold');
}
sheet.appendRow([
Session.getEffectiveUser().getEmail(),
request.accountId,
request.webPropertyId,
request.profileId,
request.title,
request.created,
request.updated,
request.status,
request.id
]);
}
// Scans LOG_SHEET_NAME and tries to update any report that is PENDING
function updateAllReports() {
var sheet = ss.getSheetByName(LOG_SHEET_NAME);
var lastRow = sheet.getLastRow();
var dataRange = sheet.getRange(2,1, lastRow, 10);
var data = dataRange.getValues();
for (var i=0; i<data.length; i++) {
// If data is PENDING let's try to update it's status. Hopefully it's complete now
// but it may take up to 24h to process an Unsampled Reprot
if (data[i][0] == Session.getEffectiveUser().getEmail() && data[i][7] == 'PENDING') {
try {
var request = Analytics.Management.UnsampledReports.get(data[i][1], data[i][2], data[i][3], data[i][8]);
} catch (error) {
ui.alert('Error Performing Unsampled Report Query', error.message, ui.ButtonSet.OK);
return;
}
data[i] = [
Session.getEffectiveUser().getEmail(),
request.accountId,
request.webPropertyId,
request.profileId,
request.title,
request.created,
request.updated,
request.status,
request.id,
request.status == 'COMPLETED' ? DriveApp.getFileById(request.driveDownloadDetails.documentId).getUrl() : ''
];
// If data is Complete let's import it into a new sheet
if (request.status == 'COMPLETED') {
importReportFromDrive(request.title, request.driveDownloadDetails.documentId);
}
}
}
// Write only once to the spreadsheet this is faster
dataRange.setValues(data);
}
function importReportFromDrive(title, fileId) {
var file = DriveApp.getFileById(fileId);
var csvString = file.getBlob().getDataAsString();
var data = Utilities.parseCsv(csvString);
// Find a suitable name for the new sheet
var i=1;
var sheetName = title;
while (ss.getSheetByName(sheetName)) {
sheetName = title + ' ('+ i++ +')';
}
var sheet = ss.insertSheet(sheetName);
var range = sheet.getRange(1, 1, data.length, data[0].length);
range.setValues(data);
}
PS: I work for Google Analytics support, as "Zig Mandel" said in the comments feel free to reach out to Google Analytics Premium Support and we're happy to help. We're very friendly.