I'm using an API script to pull in a single metric for Google Sheets in realtime. I can get one metrics to work, but I can't figure out how to pull in more than one and display it in the sheet.
// get time stamp of query run
function setTimeStamp(sheetName) {
SpreadsheetApp.getActive().getSheetByName(sheetName)
.getRange('C2').setValue(new Date())
}
// gaGet data
function gaGet(tableId, metrics, options) {
// Apply standard options
options = options || {};
options['max-results'] = options['max-results'] || '10000';
// If errors persist up to 5 times then terminate the program.
for (var i = 0; i < 5; i++) {
try {
return Analytics.Data.Realtime.get(tableId, metrics, options); // 503
} catch (err) {
// https://developers.google.com/analytics/devguides/reporting/core/v3/coreErrors
if (err.message.indexOf('a server error occurred') > -1) {
Logger.log('Backend Error');
// Note: Don't listen to Google's reply and retry request after 2 minutes
Utilities.sleep(2 * 60 * 1000);
} else if (err.message.indexOf('User Rate') > -1) {
Logger.log('Rate Limit Error');
// Exponential Backoff
Utilities.sleep(1000 * Math.pow((i + 1), 2));
} else if (err.message.indexOf('too many concurrent connections') > -1) {
Logger.log('Concurrent Connections Error');
// Exponential Backoff
Utilities.sleep(1000 * Math.pow((i + 1), 2));
} else {
Logger.log(err);
throw err;
}
}
}
throw 'Error. Max retries reached';
}
// rt pages query
function getRtPages(){
// set up the parameters and variables
var sheetName = 'web'; // The name of the sheet (not the Spreadsheet) we want to write the data e.g Sheet1
var tableId = 'ga:214164114'; // The id of the view to query the data from e.g ga:123456
//var startDate = 'yyyy-MM-dd'; // The start date of the query with the appropriate format e.g 2018-04-01 (1 April 2018)
//var endDate = 'yyyy-MM-dd'; // The end date of the query with the appropriate format e.g 2018-04-30 (30 April 2018)
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getSheetByName(sheetName);
// set Up the query arguments
var metrics = ['rt:pageviews'];
var options = {
'dimensions': 'rt:pagePath',
//'filters': '',
'sort': '-rt:pageviews',
//'segment': '',
'samplingLevel': 'HIGHER_PRECISION',
'max-results': '100' // To limit the results to 100. Maximum number of results: 10000
}
var metrics2 = ['rt:activeUsers'];
var options2 = {
'dimensions': 'rt:country',
//'filters': '',
'sort': '-rt:activeUsers',
//'segment': '',
'samplingLevel': 'HIGHER_PRECISION',
'max-results': '100' // To limit the results to 100. Maximum number of results: 10000
}
// fetch the report
var report = gaGet(tableId, metrics, options, metrics1, options1);
// clear current sheet data
var range = sheet.getRange("A2:D101");
range.clear();
// fetch the report
var report = gaGet(tableId, metrics, options);
// clear current sheet data
var range = sheet.getRange("A2:D101");
range.clear();
// set timestamp of query run
setTimeStamp(sheetName)
var data = report.rows;
// get the range to write and write the results
try {
var writeRange = sheet.getRange(2, 1, data.length, data[0].length) // Read reference for getRange arguments
writeRange.setValues(data);
}
catch(err) {
Logger.log(err);
}
}
// test ga query to return json to log
function test(){
var x = Analytics.Data.Realtime.get('ga:214164114', 'rt:pageviews')
Logger.log(x)
}
I've added the additional metrics variable and tried several ways try and bring them into the sheet, but I'm missing something.
Any thoughts are appreciated.
Related
I would like to get YouTube data for a few anime titles. For each title I put in the query, I would like to retrieve 100 records.
I've used the pageToken in the parameters but I keep getting the error in the below image. I only get this error when I put maxResults : 50 in the for loop and while loop. When the maxResults parameter is less than 50 for both loops, its fine.
I've observed that the script throws an error when the total records that would be outputted for each anime title exceeds 50.
I don't understand why this error shows up because I thought the page tokens were to allow users to get more than 50 results, no?
Below is my code. Any help would be massively appreciated!
function youTubeData() {
var ss = SpreadsheetApp.openById(<your sheet ID>);
var activeSheet = ss.getSheetByName("YouTube Results");
activeSheet.getRange("A2:G").clear(); // clear old data
let executionTime = Utilities.formatDate(new Date(), "GMT+1", "yyyy/MM/dd HH:mm:ss");
var arrSearchItems = ["attack on titan", "mob psycho 100", "demon slayer", "vinland saga"];
for (let i = 0; i < arrSearchItems.length; i++) {
if(i>0) {break;} // for testing purposes only display Attack on Titan results
var query = arrSearchItems[i];
Logger.log((i+1) + ') ' + query.toUpperCase())
var videos = YouTube.Search.list('snippet', {
q : query,
type : 'video',
maxResults : 50,
order : "viewCount",
});
var allVideos = videos.items;
var iteration = 1;
while (videos.nextPageToken && iteration == 1 /* only return one extra page of youtube results - we only want 100 records for each anime */) {
Logger.log('iteration = ' + iteration)
var videos = YouTube.Search.list('snippet', {
q : query,
type : 'video',
maxResults : 50,
order : "viewCount",
pageToken : videos.nextPageToken,
});
iteration = iteration + 1;
allVideos = allVideos.concat(videos.items);
};
var modRes = allVideos.map( function (v) { return [query.toUpperCase(), 'https://youtu.be/' + v.id.videoId, v.snippet.title, v.snippet.publishedAt]; } );
var ids = modRes.map( function (res) { return res[1].split("/")[3]; }); // get ID of videos in modRes
var stats = YouTube.Videos.list("statistics", {'id' : ids}); // get the stats for each video
// build the video stats array
var vidsStats = stats.items.map ( function (res) { return [res.statistics.viewCount, res.statistics.likeCount, executionTime] } );
var rowStart = activeSheet.getLastRow() + 1 // row start for the next search query when outputting to GSheets
// output YouTube data to GSheets
activeSheet.getRange(rowStart, 1, modRes.length, modRes[0].length).setValues(modRes);
activeSheet.getRange(rowStart, 5, vidsStats.length, vidsStats[0].length).setValues(vidsStats);
}
}
I believe your goal is as follows.
You want to search the values from arrSearchItems and retrieve videoId, title, and publishedAt. And also, you want to retrieve viewCount and likeCount from each video ID.
You want to retrieve the number of 100 from each value of arrSearchItems.
Modification points:
In your script, at 1st while loop, the length of allVideos is 100. And, 100 IDs are retrieved from the values. Using this IDs, you use YouTube.Videos.list("statistics", {'id' : ids}). In this case, there is the maximum number of IDs for {'id' : ids}. It's 50. The reason for your issue is due to this. This has already been mentioned in TheAddonDepot's answer. Ref
In your script, 2 setValues are used in a loop. In this case, the process cost will be high. Ref
In order to use pageToken, these threads might be useful. Ref1 and Ref2
When these points are reflected in your script, how about the following modification?
Modified script:
function youTubeData() {
var ss = SpreadsheetApp.openById("<your sheet ID>"); // Please set your Spreadsheet ID.
var activeSheet = ss.getSheetByName("YouTube Results");
activeSheet.getRange("A2:G").clear();
let executionTime = Utilities.formatDate(new Date(), "GMT+1", "yyyy/MM/dd HH:mm:ss");
var arrSearchItems = ["attack on titan", "mob psycho 100", "demon slayer", "vinland saga"];
// I modified below script.
var values = arrSearchItems.flatMap(query => {
var pageToken = "";
var ar = [];
do {
var videos = YouTube.Search.list('snippet', { q: query, type: 'video', maxResults: 50, order: "viewCount", pageToken, fields: "nextPageToken,items(id(videoId),snippet(title,publishedAt))" });
if (videos.items.length > 0) {
var { v1, ids } = videos.items.reduce((o, { id, snippet }) => {
o.v1.push([query.toUpperCase(), `https://youtu.be/${id.videoId}`, snippet.title, snippet.publishedAt]);
o.ids.push(id.videoId);
return o;
}, { v1: [], ids: [] });
var v = YouTube.Videos.list("statistics", { 'id': ids }).items.map(({ statistics }, i) => [...v1[i], statistics.viewCount, statistics.likeCount, executionTime]);
ar = [...ar, ...v];
}
pageToken = videos.nextPageToken;
} while (pageToken && ar.length < 100);
return ar;
});
activeSheet.getRange(1, 1, values.length, values[0].length).setValues(values);
}
When this script is run, the values of videoId, title, and publishedAt are retrieved using arrSearchItems with YouTube.Search.list. And, the values of viewCount and likeCount are retrieved using the retrieved video IDs with YouTube.Videos.list. And, the retrieved values are put on the sheet.
References:
Search: list
Videos: list
According to the documentation the max number of results you can set is 50.
Here's a direct quote:
The maxResults parameter specifies the maximum number of items that should be returned in the result set.
Note: This parameter is supported for use in conjunction with the
myRating parameter, but it is not supported for use in conjunction
with the id parameter. Acceptable values are 1 to 50, inclusive. The
default value is 5.
I would consider restructuring the first part of your code like this:
const ss = SpreadsheetApp.openById("ssid");
const sh = ss.getSheetByName("YouTube Results");
sh.getRange("A2:G" + sh.getLastRow()).clear();
const executionTime = Utilities.formatDate(new Date(), "GMT+1", "yyyy/MM/dd HH:mm:ss");
const sItems = ["attack on titan", "mob psycho 100", "demon slayer", "vinland saga"];
for (let i = 0; i < sItems.length; i++) {
let query = sItems[i];
let allVideos = [];
let ptkn = '';
do {
let options = { q: query, type: 'video', maxResults: 50, order: "viewCount", pageToken: ptkn }
let videos = YouTube.Search.list('snippet', options);
ptkn = videos.nextPageToken;
allVideos = allVideos.concat(videos.items);
} while (ptkn)
I have a pet project in google sheets that keeps track of the tv shows i've watched and syncs them up with the data in imdb (via some free api from www.omdbapi.com) to tell me what show I'm falling behind on, and should continue watching. I have a free account and I can only make 1k requests per day, so caching is an absolute MUST -- which is why there is caching EVERYWHERE. Issue I'm having is that onEdit runs, and sorts everything (expected), but re runs all the functions and I get these "Loading..." all over the place even though ALL of the functions are cached, I suspect google is making some of these fail?! But they don't "Error" (in the "Execution" tab) and they say "Completed" with no logs.
Does anyone know where the bottleneck is? And more importantly, how to fix it?
Here is the read only link to the sheet to try it out.
/**
* #OnlyCurrentDoc
*/
var APIKEY = 'xxxxxxx';
var CACHE_URL = 60 * 30; // 30 min
var CACHE_FUNCTION = 60 * 60 * 24; // 1 day
function onEdit(event) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var range = sheet.getRange("A3:K100");
range.sort([
{column: 9, ascending: false}, // continue watching
{column: 10, ascending: false}, // my ratings
{column: 8, ascending: true}, // days since
]);
ss.toast('Sort complete.');
}
function _cacheResult(key, callback, format, ttl) {
var cache = CacheService.getScriptCache();
var cached = cache.get(key);
if (cached != null) {
console.log(`found in cache: ${key}`, cached);
return format(cached);
}
var data = callback();
if (data == null) {
console.log(`cannot cache: ${key}`);
return data;
}
console.log(`cached!: ${key}`, data);
cache.put(key, data, ttl);
return format(data);
}
function _getJSON(url) {
var key = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, url)
.map(function(chr){return (chr+256).toString(16).slice(-2)})
.join('');
var callback = function() {
var response = UrlFetchApp.fetch(url)
var rawData = response.getContentText();
console.log(response.getResponseCode(), rawData);
if (response.getResponseCode() == 200) {
return rawData;
}
return null;
}
return _cacheResult(key, callback, JSON.parse, CACHE_URL)
}
function getSeasonDetail(imdbId, season) {
return _getJSON(`https://www.omdbapi.com/?i=${imdbId}&apikey=${APIKEY}&season=${season}`);
}
function _getValidEpisode(episodes) {
var currentDate = new Date();
console.log(episodes);
for (var i = episodes.length - 1; i >= 0; i--) {
if (episodes[i]['Released'] == 'N/A') {
console.log(`Skipping episode ${i+1}: unreleased`);
continue;
}
var dateSince = (currentDate - (new Date(episodes[i]['Released'])));
console.log(`Episode ${i+1}: ${dateSince}`);
if (dateSince > 0) {
console.log(`Episode ${i+1}: found!`);
return episodes[i];
}
}
return null;
}
function getIMDBRating(imdbId) {
if (!imdbId) {
return '';
}
var callback = function() {
var data = _getJSON(`https://www.omdbapi.com/?i=${imdbId}&apikey=${APIKEY}`);
return data['imdbRating'];
}
return _cacheResult(`rating:${imdbId}`, callback, parseFloat, CACHE_FUNCTION);
}
function getCurrentSeason(imdbId) {
if (!imdbId) {
console.log(`Ignoring.. bad id`, imdbId);
return '';
}
var callback = function() {
var data = _getJSON(`https://www.omdbapi.com/?i=${imdbId}&apikey=${APIKEY}`);
var season = data['totalSeasons'];
while (season) {
var seasonDetail = getSeasonDetail(imdbId, season);
var episode = _getValidEpisode(seasonDetail['Episodes']);
if (episode === null) {
console.log(`Season ${season} has no episodes ${imdbId}`);
season -= 1;
continue;
}
return season;
}
}
return _cacheResult(`current_season:${imdbId}`, callback, parseInt, CACHE_FUNCTION);
}
function getCurrentSeasonEpisode(imdbId, season) {
if (!imdbId) {
console.log(`Ignoring.. bad id`, imdbId);
return '';
} else if (!season || isNaN(parseInt(season))) {
console.log(`Ignoring ${imdbId}.. bad season id`, season);
return '?';
}
var callback = function() {
var data = getSeasonDetail(imdbId, season);
var episode = _getValidEpisode(data['Episodes']);
if (episode === null) {
console.log(`Ignoring.. bad episode response`, episode);
return '?';
}
return episode['Episode'];
}
return _cacheResult(`current_episode:${imdbId}:${season}`, callback, parseInt, CACHE_FUNCTION);
}
function getCurrentSeasonEpisodeReleased(imdbId, season) {
if (!imdbId) {
console.log(`Ignoring.. bad id`, imdbId);
return '';
} else if (!season || isNaN(parseInt(season))) {
console.log(`Ignoring ${imdbId}.. bad season id`, season);
return '?';
}
var callback = function() {
var data = getSeasonDetail(imdbId, season);
var episode = _getValidEpisode(data['Episodes']);
if (episode === null) {
console.log(`Ignoring.. bad episode reponse`, episode);
return '?';
}
return episode['Released'];
}
return _cacheResult(`current_episode_released:${imdbId}:${season}`, callback, String, CACHE_FUNCTION);
}
Issue and workaround:
I think that in your situation when onEdit is run, the rows are sorted. By this, the cell coordinates used by the formulas are also changed. By this, the formulas are recalculated. I think that this might be the reason for your issue.
If you don't want to recalculate the formulas, how about the following workaround?
Fix the values from the formulas, that the HTTP request is run, to the values.
In this case, even when the rows are sorted, UrlFetchApp.fetch(url) is not run.
When you want to update the values, the fixed values are updated by putting the formulas in the cells.
With this workaround, I thought that the quotas of the API might be able to be reduced.
In order to use this workaround, please add the following 2 functions.
Sample script:
Please copy and paste the following script to the script editor of your Spreadsheet.
// Values of columns E,F,G and K are fixed.
function fixValues() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
var lastRow = sheet.getLastRow();
sheet.getRangeList(["E3:G" + lastRow, "K3:K" + lastRow]).getRanges().forEach(r => r.copyTo(r, { contentsOnly: true }));
}
// In order to update values, formulas are put to columns E,F,G and K.
function updateValues() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
var lastRow = sheet.getLastRow();
var formulas = [
"=getCurrentSeason(R[0]C[-4])", // To column E
"=getCurrentSeasonEpisode(R[0]C[-5], R[0]C[-1])", // To column F
"=getCurrentSeasonEpisodeReleased(R[0]C[-6], R[0]C[-2])", // To column G
"=getIMDBRating(R[0]C[-10])", // To column K
];
sheet.getRangeList(["E3:E" + lastRow, "F3:F" + lastRow, "G3:G" + lastRow, "K3:K" + lastRow]).getRanges().forEach((r, i) => r.setFormulaR1C1(formulas[i]));
}
fixValues(): This function is used for fixing the values of columns E, F, G, and K.
updateValues(): This function is used for updating the values of columns E, F, G, and K using the formulas.
Testing:
In order to test this script, please do the following flow.
Please run fixValues() to your provided sample Spreadsheet. By this, the cells which have the formulas are fixed as the values.
Please edit the cells. By this, the rows are sorted. In this case, the formulas are not recalculated because of no formulas.
Please run updateValues(). By this, the values are updated by putting the formulas to the columns E, F, G, and K.
I thought that by this flow, your issue might be removed.
Note:
This sample script is for your provided sample Spreadsheet. So, when you change the structure of the Spreadsheet, this script might not be able to be used. Please be careful about this.
References:
getRangeList(a1Notations)
copyTo(destination, options)
setFormulaR1C1(formula)
Thank you for your help :)
I would like to wait until newSellPriceFunction and newBuyPriceFunction is finished loading before going to the next steps. As now sometimes it doesn't wait until the loading finished and return data with "Loading...".
function GoldPriceNotification() {
// get the Spreadsheet and sheet
var ss = SpreadsheetApp.openById("1oVMpP_MvwX2cUNviJc1NcubBj4ezR0qs2FjHEWfV8ps").getSheetByName("Gold")
// fetch data to get the price gold price
var newSellPriceFunction = ss.getRange("C6").setValue("=importxml(D2,E2)");
var newBuyPriceFunction = ss.getRange("C7").setValue("=importxml(D3,E3)");
// get value from the previous fetching gold price
var lastSellPrice = ss.getRange("C2").getDisplayValue();
var lastBuyPrice = ss.getRange("C3").getDisplayValue();
// get current date and time
var date = ss.getRange("B5").setValue("=now()")
var time = ss.getRange("B5").getDisplayValue();
// get value from the new gold price (newSellPrice is on the second because it creates span of time while its data is fetching)
var newBuyPrice = ss.getRange("C7").getDisplayValue();
var newSellPrice = ss.getRange("C6").getDisplayValue();
// if the data fetched get "#N/A, clear C6:C7
if (newSellPrice == "#N/A" || newBuyPrice == "#N/A") {
ss.getRange("C6:C7").clear()
// if the data fetched get "Loading...", clear C6:C7
} else if (newSellPrice == "Loading..." || newBuyPrice == "Loading...") {
ss.getRange("C6:C7").clear()
// Check if the last and new prices are different, send Line Notify
} else if (lastSellPrice != newSellPrice || lastBuyPrice != newBuyPrice) {
// Copy new Price to the old price area
var reNewOldPrice = ss.getRange("C6:C7").copyValuesToRange(ss, 3, 3, 2, 3)
// Send Line Notify to the group
var message = "\n🏅เอ็งฮงฮวด สวัสดีค่ะ🏅" + "\n😊🙏ขออนุญาตแจ้งราคาทอง🙏😊" + "\n⏳ณ " + time + "⏳" + "\nราคาซื้อ: " + newBuyPrice + "\nราคาขาย: " + newSellPrice + ""
sendLineNotify(message)
// Need to clear as sometime when duplicate function. it doesn't show the latest value
ss.getRange("C6:C7").clear()
// if the new and old price are the same, clear C6:C7
} else {
ss.getRange("C6:C7").clear()
}
}
You can fetch data directly in your code without using IMPORTXML formulas with the UrlFetchApp.fetch method. Try this code
function ImportXMLData() {
const url = 'https://www.goldtraders.or.th/default.aspx',
response = UrlFetchApp.fetch(url);
let content = {
Sell : '',
Buy : ''
}
if (response) {
let html = response.getContentText();
if (html) {
content.Sell = html.match(/<span id="DetailPlace_uc_goldprices1_lblBLSell"><b><font color="Red">(.*)<\/font><\/b><\/span>/i)[1];
content.Buy = html.match(/<span id="DetailPlace_uc_goldprices1_lblBLBuy"><b><font color="Red">(.*)<\/font><\/b><\/span>/i)[1];
}
}
return content;
}
I'd like to create a report of storage usage for all my users. To do so I use AdminReports app, like so (found in google example, somewhere. Just had to adapt the "parameters" and the "row" arrays) :
function generateUserUsageReport() {
var today = new Date();
var oneWeekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000);
var timezone = Session.getScriptTimeZone();
var date = Utilities.formatDate(oneWeekAgo, timezone, 'yyyy-MM-dd');
var parameters = [
'accounts:gmail_used_quota_in_mb',
'accounts:drive_used_quota_in_mb',
'accounts:total_quota_in_mb ',
'accounts:used_quota_in_percentage'
];
var rows = [];
var pageToken;
var page;
do {
page = AdminReports.UserUsageReport.get('all', date, {
parameters: parameters.join(','),
maxResults: 500,
pageToken: pageToken
});
if (page.warnings) {
for (var i = 0; i < page.warnings.length; i++) {
var warning = page.warnings[i];
Logger.log(warning.message);
}
}
var reports = page.usageReports;
if (reports) {
for (var i = 0; i < reports.length; i++) {
var report = reports[i];
var parameterValues = getParameterValues(report.parameters);
var row = [
report.date,
report.entity.userEmail,
parseInt(parameterValues['accounts:drive_used_quota_in_mb']),
parseInt(parameterValues['accounts:gmail_used_quota_in_mb']),
parseInt(parameterValues['accounts:total_quota_in_mb']),
((parseInt(parameterValues['accounts:gmail_used_quota_in_mb'])+parseInt(parameterValues['accounts:drive_used_quota_in_mb']))/parseInt(parameterValues['accounts:total_quota_in_mb']))*100
];
rows.push(row);
}
}
pageToken = page.nextPageToken;
} while (pageToken);
if (rows.length > 0) {
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getActiveSheet();
// Append the headers.
var headers = [['Date', 'User mail', 'Drive use','Gmail use', 'Total available',
'Total(%)']];
sheet.getRange(1, 1, 1, 6).setValues(headers);
// Append the results.
sheet.getRange(2, 1, rows.length, 6).setValues(rows);
Logger.log('Report spreadsheet created: %s', spreadsheet.getUrl());
} else {
Logger.log('No results returned.');
}
}
/**
* Gets a map of parameter names to values from an array of parameter objects.
* #param {Array} parameters An array of parameter objects.
* #return {Object} A map from parameter names to their values.
*/
function getParameterValues(parameters) {
return parameters.reduce(function(result, parameter) {
var name = parameter.name;
var value;
if (parameter.intValue !== undefined) {
value = parameter.intValue;
} else if (parameter.stringValue !== undefined) {
value = parameter.stringValue;
} else if (parameter.datetimeValue !== undefined) {
value = new Date(parameter.datetimeValue);
} else if (parameter.boolValue !== undefined) {
value = parameter.boolValue;
}
result[name] = value;
return result;
}, {});
}
The issue I have is that the parameters "accounts:drive_used_quota_in_mb" gives you the drive usage WITH the shared files (which is irrelevant to calculate the storage used by a user ( to determine whether he needs more space or not)).
I even tried to use 'accounts:used_quota_in_percentage' which seemed to be exactly what I need, but it calculate the percentage the same way i do : ((drive + mail)/total space)*100, and no way to ignore shared files to do so.
I'm working on the possibility to check every files of the drive, but you know the next problem : slowness.. (just for 1User with few docs, it take 1-2minutes)
Is there a way to do so by script, with another class, or something that is done for it in google that I didn't see ?
Thanks for your reading, forgive my english.
Ok, there is no issue, I just freaked out because a user was at 30Go consumption although he has his account for only 2month.
But after discussing with him, he did upload heavy files one week ago, and since, he deleted it.
And executing the script for only 2days ago gives the correct result, since he deleted these files between these two dates.
The reason of my mistake is that my script was providing stats that was 1 Week old (without me being conscious of that), and I was checking the veracity of theses stats on the web interface, that incidates nowadays stats.
So i have a script the gets some data from a server.
I want my script to publish the data to a sheet name "market items".
I got this working if I'm running the script directly from the sheet by using =getMarketItemsTrigger(1).
It posts all 11,669 items to my sheet.
The problem with this is that it refreshes every time the sheet is reloaded; I need it to only run once a month.
I've been trying to create a script which needs no reference in the given sheet but posts directly to a pre-named sheet but I can't figure out how I can get the data into the sheet
this is the script file i'm using
var version = '9a'
function getMarketItemsTrigger(refresh)
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Market Items");
Logger.log(sheet.getMaxColumns());
Logger.log(sheet.getMaxRows());
sheet.clear();
if(sheet.getMaxColumns()>2){
Logger.log('deleting colums');
sheet.deleteColumns(2, sheet.getMaxColumns()-2);
}
if(sheet.getMaxRows()>2){
Logger.log('deleting rows');
sheet.deleteRows(2,sheet.getMaxRows()-1);
}
var marketItemsEndpoint = 'https://crest-tq.eveonline.com/market/types/';
var marketItemsResponse = JSON.parse(fetchUrl(marketItemsEndpoint));
var totalPages = marketItemsResponse['pageCount'];
var itemList = [];
var headers = ['Item Name', 'ID'];
itemList.push(headers);
for (var currentPage = 1; currentPage <= totalPages; currentPage++)
{
Logger.log('Processing page ' + currentPage);
var marketItems = marketItemsResponse['items'];
for (var itemReference in marketItems)
{
var item = marketItems[itemReference];
itemList.push([item['type']['name'], item['id']]);
}
if (currentPage < totalPages)
{
var nextEndpoint = marketItemsResponse['next']['href'];
marketItemsResponse = JSON.parse(fetchUrl(nextEndpoint));
}
}
//sheet.insertRows(1,itemList.length+1);
// var range = sheet.getRange(1, 1,itemList.length+1,3);
// for(var i = 1;i<itemList.length;i++){
// range.getCell(i, 1).setValue([itemList]);
// range.getCell(1, i).setValue(itemList.);
// }
// Logger.log("don");
//sheet.getRange(1, 1, 1, itemList.length).setValues(itemList);
// sheet.getRange(itemList.length+1, 2).setValues(itemList);
// sheet.getDataRange().setValues([itemList]);
// sheet.appendRow(itemList);
// sheet.getRange(12+totalPages, 1, itemList.length, 1).setValues(itemList);
return itemList;
}
/**
* Private helper method that wraps the UrlFetchApp in a semaphore
* to prevent service overload.
*
* #param {url} url The URL to contact
* #param {options} options The fetch options to utilize in the request
*/
function fetchUrl(url)
{
if (gcsGetLock())
{
// Make the service call
headers = {"User-Agent": "Google Crest Script version " + version + " (/u/nuadi #Reddit.com)"}
params = {"headers": headers}
httpResponse = UrlFetchApp.fetch(url, params);
}
return httpResponse;
}
/**
* Custom implementation of a semaphore after LockService failed to support GCS properly.
* Hopefully this works a bit longer...
*
* This function searches through N semaphores, until it finds one that is not defined.
* Once it finds one, that n-th semaphore is set to TRUE and the function returns.
* If no semaphore is open, the function sleeps 0.1 seconds before trying again.
*/
function gcsGetLock()
{
var NLocks = 150;
var lock = false;
while (!lock)
{
for (var nLock = 0; nLock < NLocks; nLock++)
{
if (CacheService.getDocumentCache().get('GCSLock' + nLock) == null)
{
CacheService.getDocumentCache().put('GCSLock' + nLock, true, 1)
lock = true;
break;
}
}
}
return lock;
}
/**
* Private helper function that will check for a new version of GCS.
*/
function versionCheck()
{
var versionEndpoint = 'https://raw.githubusercontent.com/nuadi/googlecrestscript/master/version';
var newVersion = fetchUrl(versionEndpoint);
if (newVersion != null)
{
newVersion = newVersion.getContentText().trim();
Logger.log('Current version from Github: ' + newVersion);
var message = 'You are using the latest version of GCS. Fly safe. o7';
var title = 'No updates found';
if (newVersion > version)
{
message = 'A new version of GCS is available on GitHub.';
title = 'GCS version ' + newVersion + ' available!';
}
SpreadsheetApp.getActiveSpreadsheet().toast(message, title, 120);
}
}
All the code in the function getMarketItemsTrigger that's commented out is what I have tyred without luck .
The short version of this question is how can i post the values in itemList to column a and b in sheet market items
You can write the array itemList to the sheet by adding:
//your code
ss.getSheetByName('name_of_sheet_here')
.getRange(1, 1, itemList.length, itemList[0].length)
.setValues(itemList)
//more code (if needed)
} //end of code
-->> change sheet name and range to suit.
There are two ways to do this. If you did want it to run as a custom function those have access to the script property service. You could save a time stamp in the script properties and check it every time the custom function runs.
https://developers.google.com/apps-script/reference/properties/
https://developers.google.com/apps-script/guides/sheets/functions#using_apps_script_services
The second is to create a time trigger to run the code as a cron job every month.
https://developers.google.com/apps-script/guides/triggers/installable#time-driven_triggers
https://developers.google.com/apps-script/guides/triggers/installable#managing_triggers_manually