I have a google Slides presentation containing a chart tied to a google Spreadsheets Sheet. I created a plug-in (google script), using which I am able to paste the sheets url and have all available charts by id.
I am unable to overcome the issue of identifying the chart I need solely using its id which is generated.
Right now I can do this:
var spreadSheetContainingChartAndData = Sheets.Spreadsheets.get(sheetsId)
var allSheets = spreadSheetContainingChartAndData.sheets
var allChartsOnTheOnlyExistingSheet = dataSheet.sheets[0].charts
var firstChart = allChartsOnTheOnlyExistingSheet[0]
var chartId = firstChart.chartId
Ideally I would want to find the chart by some form of user defined identifier like this:
var chartId = findByAnythingElse(allChartsOnTheOnlyExistingSheet, 'user-defined-tag')
The usecase is to have a spreadsheet template containing client data and a number of charts visualising said data.
Whenever someone wants to make a presentation, he can click on the plugin menu and have certain slides to be populated with specific charts which will be cropped and resized to a predefined format.
You want to retrieve the chartId from all charts in a Spreadsheet.
You want to search the chartId using the specific tag.
You want to achieve this using Google Apps Script.
If my understanding is correct, how about this sample script? Please think of this as just one of several answers.
In this answer, the chart's title, subtitle and alt title are used as the search tags.
Sample script:
Before you use this script, please enable Sheets API at Advanced Google Services. And when you test this script, please set the variables for searching the chart and run the function of run().
function findByAnythingElse(spreadsheetId, searchObj) {
var obj = Sheets.Spreadsheets.get(spreadsheetId, {fields: "sheets(charts(chartId,spec(altText,subtitle,title)))"});
var chartIds = [];
for (var i = 0; i < obj.sheets.length; i++) {
var charts = obj.sheets[i].charts;
if (charts) {
for (var j = 0; j < charts.length; j++) {
var title = charts[j].spec.title;
var subTitle = charts[j].spec.subtitle;
var altText = charts[j].spec.altText;
if (title == searchObj.searchTitle || subTitle == searchObj.searchSubTitle || altText == searchObj.searchAltText) {
chartIds.push(charts[j].chartId);
}
}
}
}
return chartIds;
}
// Please run this fnuction for testing.
function run() {
var searchObj = { // Please set search values.
searchTitle: "sample1",
searchSubTitle: "",
searchAltText: "",
}
var spreadsheetId = "###"; // Please set Spreadsheet ID here.
var res = findByAnythingElse(spreadsheetId, searchObj);
Logger.log(res)
}
In this sample script, when run() is run, one of searchTitle, searchSubTitle and searchAltText of searchObj is matched, the chart ID is retrieved.
For example, the chart ID can be also retrieved from only searchTitle.
When there are several charts with the same title, several chart IDs are returned.
Note:
This is a simple sample script. So please modify it for your situation.
References:
Advanced Google services
Charts
If I misunderstood your question and this was not the direction you want, I apologize.
Related
We have switched from Microsoft to Google Workspace.
We have a Sheet with multiple tabs with a table and graph in each tab. I would like to automate inserting these graphs (as images) at specific points into a preformated Google Doc. I am sure this is something very routinely done.
I am told this can be done with Google Apps Script, but I have never used it.
Could someone point me to some good examples to get me started?
Thanks
Description
Here is a sample example script to get a chart from Google Sheet and place in a Google Doc. I use as a place holder the paragraph "Chart1".
Spreadsheet
Doc (Before)
Doc (After)
Code.gs
function testChart() {
try {
let charts = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Chart").getCharts();
// In this case there is only 1
let chart = charts[0];
chart = chart.getAs("image/png");
let doc = DocumentApp.openById("1A........................ds");
let body = doc.getBody();
let paras = body.getParagraphs();
for( let i=0; i<paras.length; i++ ) {
let para = paras[i];
let text = para.getText();
if( text === "Chart1" ) {
para.addPositionedImage(chart);
}
}
}
catch(err) {
console.log(err);
}
}
Reference
Sheet.getCharts()
DocumentApp.openById()
Document.getBody()
PositionImage
This question already has an answer here:
Replacing =ImportHTML with URLFetchApp [duplicate]
(1 answer)
Closed 1 year ago.
Hoping someone in here can help,
I have been pulling player prop data for the last couple years and it has worked fine, but this year they added more selections to the drop down and I get the error "Resource at url contents exceeded maximum size" anytime there are more than 5 games.
The function we use is =importhtml("https://www.scoresandodds.com/nba/props", "table",1)
Hoping somebody can point me in the right direction to get this working again, if possible at all. I am not very familiar with scripts and from what I've gathered that is the only way to work around this error.
Thank you for your time!
In your situation, how about using Sheets API with Google Apps Script? When the pasteData request of Sheets API is used, the HTML table can be parsed and put to the Spreadsheet. When this is reflected to the script, it becomes as follows.
Sample script:
Please copy and paste the following script to the script editor of Google Spreadsheet, and please enable Sheets API at Advanced Google services. And, please run myFunction at the script editor. By this, the retrieved table is put to the sheet.
function myFunction() {
const sheetName = "Sheet1"; // Please set the destination sheet name.
const url = "https://www.scoresandodds.com/nba/props";
const html = UrlFetchApp.fetch(url).getContentText();
const table = html.match(/<table[\s\S\w]+?<\/table>/);
if (table) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const requests = { requests: [{ pasteData: { html: true, data: table[0], coordinate: { sheetId: ss.getSheetByName(sheetName).getSheetId() } } }] };
Sheets.Spreadsheets.batchUpdate(requests, ss.getId());
return;
}
throw new Error("Table cannot be retrieved.");
}
References:
Method: spreadsheets.batchUpdate
PasteDataRequest
Try this script
function importTableHTML(url) {
var html = '<table' + UrlFetchApp.fetch(url).getContentText().match(/(?<=\<table).*(?=\<\/table)/g) + '</table>';
var trs = [...html.matchAll(/<tr[\s\S\w]+?<\/tr>/g)];
var data = [];
for (var i=0;i<trs.length;i++){
var tds = [...trs[i][0].matchAll(/<(td|th)[\s\S\w]+?<\/(td|th)>/g)];
var prov = [];
for (var j=0;j<tds.length;j++){
donnee=tds[j][0].match(/(?<=\>).*(?=\<\/)/g)[0].replace(/ /g,' ');
if(donnee.indexOf("</a>")>-1){
prov.push(donnee.match(/(?<=\>).*(?=\<\/)/g)[0]);
}else{
prov.push(donnee);
}
}
data.push(prov);
}
return(data);
}
I'm looking for a simple script for a Google Docs add-on that will highlight words held in an array. I want the script to auto-highlight the words as I type. Is this simple enough?
Thanks!
Based on Can I color certain words in Google Document using Google Apps Script?
function highlight_words() {
var doc = DocumentApp.getActiveDocument();
var words = ['one','two','three'];
var style = { [DocumentApp.Attribute.BACKGROUND_COLOR]:'#FFFF00' };
var pgfs = doc.getParagraphs();
for (var word of words) for (var pgf of pgfs) {
var location = pgf.findText(word);
if (!location) continue;
var start = location.getStartOffset();
var end = location.getEndOffsetInclusive();
location.getElement().setAttributes(start, end, style);
}
}
I am trying to create a function (ultimately a menu button) that automatically resizes all the charts in a Sheet to a defined render width. I want to keep the other chart options (which might have changed from their default values before running this function) intact.
My code so far:
function resizeCharts() {
var newWidth = 1000;
var ss = SpreadsheetApp.getActiveSpreadsheet();
var currentSheet = ss.getActiveSheet();
var sheetCharts = currentSheet.getCharts();
for (var i = 0; i < sheetCharts.length; i++) {
var modifiedChart = sheetCharts[i].modify();
modifiedChart = modifiedChart.setOption('width', newWidth).build();
currentSheet.updateChart(modifiedChart);
}
}
This code will adjust the width of the chart, but it changes the other options (e.g. series colors, axis formats), which I don't want.
Is there a way to preserve all the existing options of a chart except for the one I want to change?
In case you just need to update chart's size or position, there is Advanced Sheets Service and specific UpdateEmbeddedObjectPosition request for that (which is not available via built-in Spreadsheet Service).
Example:
// your spreadsheet id
let ssId = SpreadsheetApp.getActiveSpreadsheet().getId();
// get all charts from sheet (replace 'YOUR_SHEET_TITLE')
let charts = Sheets.Spreadsheets
.get(ssId)
.sheets
.filter(sheet => sheet.properties.title === 'YOUR_SHEET_TITLE')[0]
.charts;
// array of requests
let requests = [];
// loop charts array
charts.forEach(chart => {
// create request for each chart
let request = {
updateEmbeddedObjectPosition: {
objectId: chart.chartId,
newPosition: {
overlayPosition: {
widthPixels: 1000 // specify your width
}
},
fields: 'widthPixels' // list fields you want to update
}
};
// push request to array
requests.push(request);
});
// send requests at once
// this may allow to improve script performance
Sheets.Spreadsheets.batchUpdate({'requests': requests}, ssId);
References:
Updating Spreadsheets - Apps Script
Updating Spreadsheets - Field masks
UpdateEmbeddedObjectPositionRequest
EmbeddedObjectPosition
OverlayPosition
This question already has answers here:
Scraping data to Google Sheets from a website that uses JavaScript
(2 answers)
Closed last month.
I'm attempting to scrape options pricing data from Yahoo Finance in Google Sheets. Although I'm able to pull the options chain just fine, i.e.
=IMPORTHTML("https://finance.yahoo.com/quote/TCOM/options?date=1610668800","table",2)
I find that it's returning results that don't completely match what's actually shown on Yahoo Finance. Specifically, the scraped results are incomplete - they're missing some strikes. i.e. the first 5 rows of the chart may match, but then it will start returning only every other strike (aka skipping every other strike).
Why would IMPORTHTML be returning "abbreviated" results, which don't match what's actually shown on the page? And more importantly, is there some way to scrape complete data (i.e. that doesn't skip some portion of the available strikes)?
In Yahoo finance, all data are available in a big json called root.App.main. So to get the complete set of data, proceed as following
var source = UrlFetchApp.fetch(url).getContentText()
var jsonString = source.match(/(?<=root.App.main = ).*(?=}}}})/g) + '}}}}'
var data = JSON.parse(jsonString)
You can then choose to fetch the informations you need. Take a copy of this example https://docs.google.com/spreadsheets/d/1sTA71PhpxI_QdGKXVAtb0Rc3cmvPLgzvXKXXTmiec7k/copy
edit
if you want to get a full list of available data, you can retrieve it by this simple script
// mike.steelson
let result = [];
function getAllDataJSON(url = 'https://finance.yahoo.com/quote/TCOM/options?date=1610668800') {
var source = UrlFetchApp.fetch(url).getContentText()
var jsonString = source.match(/(?<=root.App.main = ).*(?=}}}})/g) + '}}}}'
var data = JSON.parse(jsonString)
getAllData(eval(data),'data')
var sh = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet()
sh.getRange(1, 1, result.length, result[0].length).setValues(result);
}
function getAllData(obj,id) {
const regex = new RegExp('[^0-9]+');
for (let p in obj) {
var newid = (regex.test(p)) ? id + '["' + p + '"]' : id + '[' + p + ']';
if (obj[p]!=null){
if (typeof obj[p] != 'object' && typeof obj[p] != 'function'){
result.push([newid, obj[p]]);
}
if (typeof obj[p] == 'object') {
getAllData(obj[p], newid );
}
}
}
}
Here's a simpler way to get the last market price of a given option. Add this function to you Google Sheets Script Editor.
function OPTION(ticker) {
var ticker = ticker+"";
var URL = "finance.yahoo.com/quote/"+ticker;
var html = UrlFetchApp.fetch(URL).getContentText();
var count = (html.match(/regularMarketPrice/g) || []).length;
var query = "regularMarketPrice";
var loc = 0;
var n = parseInt(count)-2;
for(i = 0; i<n; i++) {
loc = html.indexOf(query,loc+1);
}
var value = html.substring(loc+query.length+9, html.indexOf(",", loc+query.length+9));
return value*100;
}
In your google sheets input the Yahoo Finance option ticker like below
=OPTION("AAPL210430C00060000")
I believe your goal as follows.
You want to retrieve the complete table from the URL of https://finance.yahoo.com/quote/TCOM/options?date=1610668800, and want to put it to the Spreadsheet.
Issue and workaround:
I could replicate your issue. When I saw the HTML data, unfortunately, I couldn't find the difference of HTML between the showing rows and the not showing rows. And also, I could confirm that the complete table is included in the HTML data. By the way, when I tested it using =IMPORTXML(A1,"//section[2]//tr"), the same result of IMPORTHTML occurs. So I thought that in this case, IMPORTHTML and IMPORTXML might not be able to retrieve the complete table.
So, in this answer, as a workaround, I would like to propose to put the complete table parsed using Sheets API. In this case, Google Apps Script is used. By this, I could confirm that the complete table can be retrieved by parsing the HTML table with Sheet API.
Sample script:
Please copy and paste the following script to the script editor of Spreadsheet, and please enable Sheets API at Advanced Google services. And, please run the function of myFunction at the script editor. By this, the retrieved table is put to the sheet of sheetName.
function myFunction() {
// Please set the following variables.
const url ="https://finance.yahoo.com/quote/TCOM/options?date=1610668800";
const sheetName = "Sheet1"; // Please set the destination sheet name.
const sessionNumber = 2; // Please set the number of session. In this case, the table of 2nd session is retrieved.
const html = UrlFetchApp.fetch(url).getContentText();
const section = [...html.matchAll(/<section[\s\S\w]+?<\/section>/g)];
if (section.length >= sessionNumber) {
if (section[sessionNumber].length == 1) {
const table = section[sessionNumber][0].match(/<table[\s\S\w]+?<\/table>/);
if (table) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const body = {requests: [{pasteData: {html: true, data: table[0], coordinate: {sheetId: ss.getSheetByName(sheetName).getSheetId()}}}]};
Sheets.Spreadsheets.batchUpdate(body, ss.getId());
}
} else {
throw new Error("No table.");
}
} else {
throw new Error("No table.");
}
}
const sessionNumber = 2; means that 2 of =IMPORTHTML("https://finance.yahoo.com/quote/TCOM/options?date=1610668800","table",2).
References:
Method: spreadsheets.batchUpdate
PasteDataRequest