How to update Chart Title via Google Sheet API (python) - python

I have a google chart whose ID I have already extracted via .get method.
I now need to change the title of the chart (and nothing else). The charts come from external template, so I do not know anything about them, other than the name.
I tried the following request:
body = {
"requests": [
{
"updateChartSpec": {
"chartId": 1944564251,
"spec": {
"title": "NEW NAME OF CHART"}
}}]
}
r = sheet.batchUpdate(spreadsheetId=DESTINATION_SPREADSHEET_ID, body=body).execute()
but I am getting this error:
"Invalid requests[0].updateChartSpec: One of basicChart, pieChart, bubbleChart, candelstickChart, histogramChart, or orgChart must be set on chartSpec."
I don't want to change anything in the chart other than the title. How can I go about it?

I believe your goal and your current situation as follows.
You want to modify the title of the chart on Google Spreadsheet.
You want to achieve this using googleapis for Python.
You have already been able to get and put values for Google Spreadsheet using Sheets API.
Modification points:
In the current stage, it seems that UpdateChartSpecRequest of Sheets API cannot still update the chart title using only the property of title. I thought that the reason of your issue is due to this. This has already been reported to the issue tracker as a Feature Request. Ref
I think that if fields is added to this request, your script might work. But, in the current stage, it is required to use the workaround.
The workaround is as follows.
Retrieve the chart object using the chart ID and spreadsheets.get method in Sheets API.
Modify the chart object.
Using UpdateChartSpecRequest, the modified chart object is used. By this, the chart is updated.
I have already answered this workaround at here. But, this is for Google Apps Script. When I searched this workaround using googleapis for Python, I couldn't find it. So I answerd this using googleapis for Python.
When above workaround is reflected to the script of Python, it becomes as follows.
Modified script:
DESTINATION_SPREADSHEET_ID = "###" # Please set the Spreadsheet ID.
chartId = 1944564251 # Please set the chart ID.
newTitle = "NEW NAME OF CHART"
service = build('sheets', 'v4', credentials=creds)
res = service.spreadsheets().get(spreadsheetId=DESTINATION_SPREADSHEET_ID, fields='sheets(charts)').execute()
chart = None
for s in res.get('sheets'):
charts = s.get('charts')
if charts:
for c in charts:
if c.get('chartId') == chartId:
chart = c
if chart:
chart.pop('position')
chart['spec']['title'] = newTitle
body = {"requests": [{"updateChartSpec": chart}]}
r = service.spreadsheets().batchUpdate(spreadsheetId=DESTINATION_SPREADSHEET_ID, body=body).execute()
print(r)
else:
print("Chart cannot be found.")
References:
Method: spreadsheets.get
Method: spreadsheets.batchUpdate
UpdateChartSpecRequest
Issue tracker.
Add fields property to UpdateChartSpecRequest of batchUpdate method in Sheets API
Added:
From your following replying,
Thank you for your response. I tried the code above and getting this error: Invalid requests[0].updateChartSpec: chartSpec.basicChart.lineSmoothing not supported when chartSpec.basicChart.chartType is COMBO
I could replicate your error. I confirmed that in the current stage, it seems that "lineSmoothing": true cannot be used with COMBO chart using UpdateChartSpecRequest. So in the current stage, when Sheets API is used, it is required to remove the property of "lineSmoothing": true as follows. In this case, please modify above sample script as follows.
From:
if chart:
chart.pop('position')
To:
if chart:
chart.pop('position')
if chart['spec'].get('basicChart') and chart['spec']['basicChart']['chartType'] == 'COMBO' and chart['spec']['basicChart'].get('lineSmoothing') and chart['spec']['basicChart']['lineSmoothing'] == True:
chart['spec']['basicChart'].pop('lineSmoothing')
By above modification, the error can be removed. But in this case, unfortunately, the chart is changed. Because "lineSmoothing": true is not used. So, in this case, how about reporting this issue as the issue tracker as the future request? Ref
So, as a workaround, how about the following method? In this workaround, Web Apps created by Google Apps Script is used as the wrapper API. When Google Apps Script is used, the chart title can be directly modified.
Usage:
1. Create new project of Google Apps Script.
Sample script of Web Apps is a Google Apps Script. So please create a project of Google Apps Script.
If you want to directly create it, please access to https://script.new/. In this case, if you are not logged in Google, the log in screen is opened. So please log in to Google. By this, the script editor of Google Apps Script is opened.
2. Sample script.
Please copy and paste the following script to the created Google Apps Script project and save it.
function doGet(e) {
const {spreadsheetId, chartId, newTitle} = e.parameter;
const sheets = SpreadsheetApp.openById(spreadsheetId).getSheets();
let done = false;
for (let i = 0; i < sheets.length; i++) {
const charts = sheets[i].getCharts();
for (let j = 0; j < charts.length; j++) {
if (charts[j].getChartId() == chartId) {
const chart = charts[j].modify().setOption("title", newTitle).build();
sheets[i].updateChart(chart);
done = true;
break;
}
}
if (done) break;
}
return ContentService.createTextOutput("Done.");
}
3. Deploy Web Apps.
The detail information can be seen at the official document.
On the script editor, at the top right of the script editor, please click "click Deploy" -> "New deployment".
Please click "Select type" -> "Web App".
Please input the information about the Web App in the fields under "Deployment configuration".
Please select "Me" for "Execute as".
This is the important of this workaround.
Please select "Anyone" for "Who has access".
In this case, the user is not required to use the access token. So please use this as a test case.
Of course, you can also access to your Web Apps using the access token. Please check this report.
Please click "Deploy" button.
Copy the URL of Web App. It's like https://script.google.com/macros/s/###/exec.
When you modified the Google Apps Script, please modify the deployment as new version. By this, the modified script is reflected to Web Apps. Please be careful this.
You can see the detail of this at the report of "Redeploying Web Apps without Changing URL of Web Apps for new IDE".
4. Testing.
When above Web Apps is tested, the sample python script is as follows. Please set your URL of Web Apps.
import requests
url = "https://script.google.com/macros/s/###/exec"
url += "?spreadsheetId=###&chartId=###&newTitle=###"
res = requests.get(url)
print(res.text)
When an error occurs, please confirm the settings of Web Apps again.
References:
Web Apps
Taking advantage of Web Apps with Google Apps Script

Related

How to add a list of hyperlinks in a CSV field to a cell in Google Sheets?

I'm generating a csv file (with thousands of rows) on my local PC. From a Google account, I would like to do a manual Google Sheets>Import to upload the file for my book club group. The data is collected from HTML tables on multiple pages, if that matters.
One of the fields is named "shelves" is essentially tags, and it contains a list of (name, url) tuples. I'd like to modify my Python program to make a list along the lines of
[=HYPERLINK(url, name), =HYPERLINK(url, name), ..., =HYPERLINK(url, name)]
but I can't find any syntax clues. I also tried
['=HYPERLINK("url", "name"), =HYPERLINK("url", "name")', '=HYPERLINK("url", "name"), =HYPERLINK("url", "name")', ...]
Can something like this via importing a CSV file from Google Sheets work or not, in Aug 2022?
Here's a sample CSV row:
,title,title_url,author,author_url,shelves,date_started,date_finished,member_name,member_url,date_added,group_activity,group_book_id_url'
'29,"Luck in the Shadows (Nightrunner, #1)",http://goodreads.com/book/show/74270.Luck_in_the_Shadows,"Flewelling, Lynn",http://goodreads.com/author/show/42110.Lynn_Flewelling,"[('http://goodreads.com/group/bookshelf/group?shelf=read', 'read'), ('http://goodreads.com/group/bookshelf/group?shelf=1-book-of-the-month', '1-book-of-the-month'), ('http://goodreads.com/group/bookshelf/group?shelf=char-royalty-nobility', 'char-royalty-nobi...'), ('http://goodreads.com/group/bookshelf/group?shelf=genre-action-adventure', 'genre-action-adve...'), ('http://goodreads.com/group/bookshelf/group?shelf=genre-epic', 'genre-epic'), ('http://goodreads.com/group/bookshelf/group?shelf=genre-fantasy', 'genre-fantasy'), ('http://goodreads.com/group/bookshelf/group?shelf=genre-romance', 'genre-romance'), ('http://goodreads.com/group/bookshelf/group?shelf=profession-mage-witch-wizard', 'profession-mage-w...'), ('http://goodreads.com/group/bookshelf/group?shelf=theme-cross-dressing', 'theme-cross-dressing'), ('http://goodreads.com/group/bookshelf/group?shelf=theme-nautical', 'theme-nautical'), ('http://goodreads.com/group/bookshelf/group?shelf=theme-on-the-run', 'theme-on-the-run'), ('http://goodreads.com/group/bookshelf/group?shelf=time-historical', 'time-historical')]",1/1/2021,1/31/2021,Marianne ,http://goodreads.com/user/show/marianne,"group activity for 536628',http://goodreads.com/group/show_book/group?group_book_id=536628
So shelves is the field I'm working on. As you can see it has a long list (and edited for brevity):
[('http://goodreads.com/group/bookshelf/group?shelf=read', 'read'), ('http://goodreads.com/group/bookshelf/group?shelf=genre-action-adventure', 'genre-action-adve...'), ('http://goodreads.com/group/bookshelf/group?shelf=genre-epic', 'genre-epic'), ('http://goodreads.com/group/bookshelf/group?shelf=genre-fantasy', 'genre-fantasy'), ('http://goodreads.com/group/bookshelf/group?shelf=genre-romance', 'genre-romance'), ('http://goodreads.com/group/bookshelf/group?shelf=profession-mage-witch-wizard', 'profession-mage-w...'), ('http://goodreads.com/group/bookshelf/group?shelf=theme-on-the-run', 'theme-on-the-run'), ('http://goodreads.com/group/bookshelf/group?shelf=time-historical', 'time-historical')]
I would like to have a csv-type file that can be manually imported into Google Sheets and have a single cell contain the shelves list in the following fashion:
`[=HYPERTEXT('http://goodreads.com/group/bookshelf/group?shelf=read', 'read'), =HYPERTEXT('http://goodreads.com/group/bookshelf/group?shelf=genre-action-adventure', 'genre-action-adve...')]
So that when it is uploaded to Google it displays similarly to an html table cell:
Before I go through a ton of iterations of that, I wanted to see if that would even work. All the research I've done has come up with mostly 2020 info about only being able to do this in the Google Apps environment, or to possibly write a function for the spreadsheet. I did sign up and try the Google Apps environment, but got stuck in setting up credentials.
If not, is there a best approach to somehow accomplish this?
If it is possible, I could use some help on the syntax. Thank you!
I believe your goal is as follows.
You want to upload CSV data of your local PC to your Google Drive.
Here, from your question, the uploading data is as follows.
[('http://goodreads.com/group/bookshelf/group?shelf=read', 'read'), ('http://goodreads.com/group/bookshelf/group?shelf=genre-action-adventure', 'genre-action-adve...'), ('http://goodreads.com/group/bookshelf/group?shelf=genre-epic', 'genre-epic'), ('http://goodreads.com/group/bookshelf/group?shelf=genre-fantasy', 'genre-fantasy'), ('http://goodreads.com/group/bookshelf/group?shelf=genre-romance', 'genre-romance'), ('http://goodreads.com/group/bookshelf/group?shelf=profession-mage-witch-wizard', 'profession-mage-w...'), ('http://goodreads.com/group/bookshelf/group?shelf=theme-on-the-run', 'theme-on-the-run'), ('http://goodreads.com/group/bookshelf/group?shelf=time-historical', 'time-historical')]
You want to create a new Spreadsheet and put the formulas like =HYPERLINK("url", "name") to the column "A" of the new Spreadsheet.
You want to achieve this using a python script. And, when the data is uploaded, you don't want to authorize the scopes.
Unfortunately, when the data is uploaded to Google Drive, authorization is required to be used. So, in this answer, in order to achieve your goal, as a workaround, I used Web Apps as a wrapper API. When Web Apps is used, the authorization can be done when the Web Apps is deployed. By this, when the script accesses the Web Apps, the data can be uploaded without authorization. In this case, how about the following method?
Usage:
1. Create a Google Apps Script project.
In order to use Web Apps, please create a new Google Apps Script project. When you access https://script.google.com/home and create a new project. You can create a new Google Apps Script project.
2. Sample script.
Please copy and paste the following script to the script editor of the created Google Apps Script project.
function doPost(e) {
const data = JSON.parse(e.postData.contents);
const newSS = SpreadsheetApp.create("sample");
const formulas = data.map(([a, b]) => [`=HYPERLINK("${a}", "${b}")`]);
newSS.getSheets()[0].getRange(1, 1, formulas.length, 1).setFormulas(formulas);
return ContentService.createTextOutput(newSS.getUrl());
}
In this case, as a sample, a new Spreadsheet is created to the root folder.
If you want to achieve your goal without using =HYPERLINK(), please modify as follows.
From
const formulas = data.map(([a, b]) => [`=HYPERLINK("${a}", "${b}")`]);
newSS.getSheets()[0].getRange(1, 1, formulas.length, 1).setFormulas(formulas);
To
const rValues = data.map(([a, b]) => [SpreadsheetApp.newRichTextValue().setText(b).setLinkUrl(a).build()]);
newSS.getSheets()[0].getRange(1, 1, rValues.length).setRichTextValues(rValues);
3. Deploy Web Apps.
The detailed information can be seen at the official document.
Please set this using the new IDE of the script editor.
On the script editor, at the top right of the script editor, please click "click Deploy" -> "New deployment".
Please click "Select type" -> "Web App".
Please input the information about the Web App in the fields under "Deployment configuration".
Please select "Me" for "Execute as".
Please select "Anyone" for "Who has access".
Please click "Deploy" button.
Copy the URL of the Web App. It's like https://script.google.com/macros/s/###/exec.
When you modified the Google Apps Script, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful about this.
You can see the detail of this in the report "Redeploying Web Apps without Changing URL of Web Apps for new IDE".
4. Testing:
In order to test the above Web Apps, in this case, from your question, a python script and your showing data are used. The sample script is as follows. Before you use this, please set url.
import json
import requests
url = "https://script.google.com/macros/s/###/exec" # Please replace this with your Web Apps URL.
# This data is from your question.
data = [('http://goodreads.com/group/bookshelf/group?shelf=read', 'read'), ('http://goodreads.com/group/bookshelf/group?shelf=genre-action-adventure', 'genre-action-adve...'), ('http://goodreads.com/group/bookshelf/group?shelf=genre-epic', 'genre-epic'), ('http://goodreads.com/group/bookshelf/group?shelf=genre-fantasy', 'genre-fantasy'), ('http://goodreads.com/group/bookshelf/group?shelf=genre-romance', 'genre-romance'), ('http://goodreads.com/group/bookshelf/group?shelf=profession-mage-witch-wizard', 'profession-mage-w...'), ('http://goodreads.com/group/bookshelf/group?shelf=theme-on-the-run', 'theme-on-the-run'), ('http://goodreads.com/group/bookshelf/group?shelf=time-historical', 'time-historical')]
res = requests.post(url, json.dumps(data))
print(res.text)
From your question, I understood that you have already had data in the above script. So, I used it.
When this script is run, your data is sent to Web Apps without the authorization. Because the authorization has already been done when Web Apps is deployed. The uploaded data is parsed and set the values as the formula. And, the URL of the created new Spreadsheet is returned.
Note:
When you modified the Google Apps Script, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful about this.
You can see the detail of this in the report "Redeploying Web Apps without Changing URL of Web Apps for new IDE".
References:
Web Apps
Taking advantage of Web Apps with Google Apps Script
Google's Import function cannot automatically translate a field containing a list of URLs into a single field as a list of Hyperlinks, regardless of the URL format used in the CSV field.
The user must write a Google Sheet Extension using Extensions > Apps Scripts in the relevant Sheet. Thanks to Tanaike's Answer that supplies comprehensive directions.
The user can then copy that script to their other Google Sheets that Import similarly formatted files.

Facebook Graph API: Cannot pass categories array on Python

I'm trying to use Facebook's Graph API to get all the restaurants within a certain location. I'm doing this on Python and my url is
url ="https://graph.facebook.com/search?type=place&center=14.6091,121.0223,50000&categories=[\"FOOD_BEVERAGE\"]&fields=name,location,category,fan_count,rating_count,overall_star_rating&limit=100&access_token=" + access
However, this is the error message I get.
{
"error": {
"message": "(#100) For field 'placesearch': param categories must be an array.",
"code": 100,
"type": "OAuthException",
"fbtrace_id": "EGQ8YdwnzUT"
}
}
But when I paste the URL on the Graph explorer (linked below), it works. I can't do this exhaustively on the explorer because I need to collect restaurant data from all the next pages. Can someone help me explain why this is happening and how to fix it so that I can access it through Python?
Example Link
You did not specify an API version, so this will fall back to the lowest version your app can use.
I can reproduce the error for API versions <= v2.8 in Graph API Explorer - for v2.9 and above it works.
So use https://graph.facebook.com/v2.9/search?… (at least, or a higher version, up to you.)

Determine display columns from Saved Search via SuiteTalk / NetSuite?

I'm using Python 2.7 and Zeep to call SuiteTalk v2017_2_0, the SOAP-based NetSuite web service API. The command I'm running is search like so:
from zeep import Client
netsuite = Client(WSDL)
TransactionSearchAdvanced = netsuite.get_type(
'ns19:TransactionSearchAdvanced')
TransactionSearchRow = netsuite.get_type('ns19:TransactionSearchRow')
# login removed for brevity
r = netsuite.service.search(TransactionSearchAdvanced(
savedSearchId=search, columns=TransactionSearchRow()))
Now the results of this include all the data I want but I can't figure out how (if at all) I can determine the display columns that the website would show for this saved search and the order they go in.
I figure I could probably netsuite.service.get() and pass the internalId of the saved search but what type do I specify? Along those lines, has anyone found a decent reference for all the objects, type enumerations, etc.?
https://stackoverflow.com/a/50257412/1807800
Check out the above link regarding Search Preferences. It explains how to limit columns returned to those only in the search.

Re-accessing python variables AFTER initial html get() call

I am making a data-plotting tool using google app engine and google docs/gdata and have run into a python/js/html communication issue.
I want a very simple layout for the webpage -- just a series of dropdown menu sections and then the final graphs. My current dilemma involves the dropdown menus.
There are total of 4 dropdown menus, and the fields of each one depends on the previous selection. I have found JS/HTML code that has all of the needed logic, but it assumes that you have pre-loaded all of the selection fields. Due to the scale of the project, it is impossible to load everything on start-up, and the data is dynamic/changing, so I can't just declare static files with the menu selection data contained.
Currently, I have methods to get all of the menu selections/data I need from google docs (using gdata). Ideally, I would like to just call each one in series (html selection1 -> python method1 to get next selection set -> html selection2 -> python method2 to get next selection set-> etc) but from what I can gather it seems this is not possible (return to python, ie 'post', without reloading the entire page).
So, is there a way to access python variables/methods in a google-app-engine JS code AFTER the initial template render or without 'posting'? Or, more open-endedly, if anyone has insight/suggestions/ideas I'd appreciate it.
Thanks
Two step process:
Create some server side code to return your data as JSON - the defacto method of transferring data on the web.
In your javascript code, call your server (from step 1) and then parse the result. Use the result to populate your dropdown.
The good news is that Google already provides json as an alternate format for their gdata APIs. See this link for a primer on how to use gdata and return JSON.
For the second part, almost all javascript client side libraries (and you really should be using one) provide an easy way to request data using ajax. As this is a common task, there are plenty of questions on StackOverflow on this - this one in particular has an example that shows you how to populate a dropdown using jquery and ajax.
Assuming you can use javascript and replicate whatever functionality your Python code is providing, the above will work for you. If you have some specific logic that you don't want to transfer to the client side, then your Python code will have to return json (in effect, you would be acting like the gdata api for your javascript code).
Since you didn't highlight which Python framework you are using - here is an example using flask, a simple framework for Python web development:
from flask import Flask, jsonify
app = Flask(__name__)
#app.route('/the-question')
def the_answer():
return jsonify(answer=42)
A more comprehensive example is available in the documentation under AJAX with jQuery.
If you are using webapp2, Google provides excellent documentation to do the same thing.

How can I edit a secondary calendar using google python API

I wonder if it's possible to create and delete events with Google API in a secondary calendar. I know well how to do it in main calendar so I only ask, how to change calendar_service to read and write to other calendar.
I've tried loging with secondary calendar email, but that's not possible with BadAuthentication Error. The URL was surely correct, becouse it was read by API.
Waiting for your help.
A'm answering my own question so I can finally accept this one. The problem has been solved some time ago.
The most important answer is in this documentation.
Each query can be run with uri as argument. For example "InsertEvent(event, uri)". Uri can be set manually (from google calendar settings) or automatically, as written in post below. Note, that CalendarEventQuery takes only username, not the whole url.
The construction of both goes this way:
user = "abcd1234#group.calendar.google.com"
uri = "http://www.google.com/calendar/feeds/{{ user }}/private/full-noattendees"
What's useful, is that you can run queries with different uri and add/delete events to many different calendars in one script.
Hope someone finds it helpful.
I got the same issue but I found this solution (I do not remember where)
This solution is to extract the secondary calendar user from its src url provided by google
This is probably not the better one but it's a working one
Note:the code is extracted from a real project [some part has been removed] and must be adapted to your particular case and is provide as sample just to have a support for explaination (It will not work as is)
# 1 - Connect using the main user email address in a classical way
cal_client = gdata.calendar.service.CalendarService()
# insert here after connection stuff
# 2 - For each existing calendars
feed = cal_client.GetAllCalendarsFeed():
# a loop the find the calendar by it's title (cal_title)
for a_calendar in feed.entry:
if cal_title in a_calendar.title.text:
cal_user = a_calendar.content.src.split('/')[5].replace('%40','#')
# If you print a_calendar.content.src.split you will see that the url
# contains an email like definition. This is the one to used to work
# with the calendar
Then you just have to replace the default user by the cal_user in the api to work on the secondary calendar.
Replace call is required because google api function are doing internal conversion on special characters like '%'
I hope this will help you.

Categories

Resources