How to get Google Analytics credentials without gflags - using run_flow() instead?
A

3

24

This may take a second to explain so please bear with me:

I'm working on a project for work that requires me to pull in google analytics data. I originally did this following this link, so after installing the API client pip install --upgrade google-api-python-client and setting things up like the client_secrets.json, it wanted gflags to be installed in order to execute the run() statement. (i.e credentials = run(FLOW, storage))

Now, I was getting the error message to install gflags or better to use run_flow() (exact error message was this):

NotImplementedError: The gflags library must be installed to use tools.run(). Please install gflags or preferably switch to using tools.run_flow().

I originally used gflags (a few months ago), but it wasn't compatible with our framework (pyramid), so we removed it until we could figure out what the issue was. And the reason why it's preferable to switch from gflags to run_flow() is because gflags has been deprecated, so I don't want to use it like I had. What I'm trying to do now is switch over to using run_flow()

The issue with this is run_flow() expects a command line argument to be sent to it and this is not a command line application. I found some documentation that was helpful but I'm stuck on building the flags for the run_flow() function.

Before showing code one more thing to explain.

run_flow() takes three arguments (documentation here). It takes the flow and storage just like run() does, but it also takes a flags object. the gflags library built a flags ArgumentParser object that was used in the oauth2client execution method.

a few other links that were helpful in building the argumentParser object:

The second link is very helpful to see how it would be executed, so now when I try to do something similar, sys.argv pulls in the location of my virtual environment that is running aka pserve and also pulls in my .ini file (which stores credentials for my machine to run the virtual environment). But that throws an error because its expecting something else, and this is where I'm stuck.

  • I don't know what flags object I need to build to send through run_flow()
  • I don't know what argv arguments I need passed in order for the statement flags = parser.parse_args(argv[1:]) to retrieve the correct information (I don't know what the correct information is supposed to be)

Code:

CLIENT_SECRETS = client_file.uri
MISSING_CLIENT_SECRETS_MESSAGE = '%s is missing' % CLIENT_SECRETS
FLOW = flow_from_clientsecrets(
    CLIENT_SECRETS,
    scope='https://www.googleapis.com/auth/analytics.readonly',
    message=MISSING_CLIENT_SECRETS_MESSAGE
)
TOKEN_FILE_NAME = 'analytics.dat'

def prepare_credentials(self, argv):
    storage = Storage(self.TOKEN_FILE_NAME)
    credentials = storage.get()

    if credentials is None or credentials.invalid:
        parser = argparse.ArgumentParser(description=__doc__,
            formatter_class=argparse.RawDescriptionHelpFormatter,
            parents=[tools.argparser])
        flags = parser.parse_args(argv[1:]) # i could also do just argv, both error
        credentials = run_flow(self.FLOW, storage, flags) 
    return credentials

def initialize_service(self, argv):
    http = httplib2.Http()
    credentials = self.prepare_credentials(self, argv)
    http = credentials.authorize(http)
    return build('analytics', 'v3', http=http)

I call a main function passing sys.argv that calls the initialize_service

def main(self, argv):
    service = self.initialize_service(self, argv)

    try:
        #do a query and stuff here

I knew this wouldn't work because my application is not a command line application but rather a full integrated service but I figured it was worth a shot. Any thoughts on how to build the flags object correctly?

Asymmetric answered 22/7, 2014 at 14:29 Comment(8)
I did something similar for InAppPurchases for playstore, the scope of the authentication is nearly the same. All you need is the authentication token?Culbertson
@RafaelBarros yes I need that authentication token to query google analytics.. I'm thinking about creating my own whole analytics object where I build the credentials from oauth... if i can't get a suitable answer here :)Asymmetric
Ok, I'll write an answer about using the oauth to get a refresh token and then using the token to do the API calls with requests. You will need the terminal once to craft a token but after that you can just put that in a config file and be done with it.Culbertson
@RafaelBarros doesn't that token expire after some time?Asymmetric
The refresh token is just a way to recreate the oauth token that does expire. The refresh token is "permanent".Culbertson
Actually, before I write an entire answer with all the flows necessary for this answer, can you check this: developers.google.com/analytics/solutions/articles/…? And are you available to chat?Culbertson
If you wanna chat: chat.stackoverflow.com/rooms/58198/google-oauth-api-flowCulbertson
that link is one of the links in my post :)Asymmetric
A
17
from oauth2client import tools

flags = tools.argparser.parse_args(args=[])
credentials = tools.run_flow(flow, storage, flags)

Took a bit of mucking about but climbed my way out the two traps it dropped me into:

  1. have to use the argparser provided in tools
  2. I had to feed args an empty list to prevent it from reading args off the command line, which was a problem because I'm running it from inside a unittest (so different cmdline args).
Above answered 20/11, 2014 at 10:28 Comment(1)
I come to the same solution with you. But I still facing webbrowser.open() termination issue for unittesting code. Any idea? #31879881Graph
A
0

This piece of code works for me for the gmail api.

(Also this link helped. Command-line tools)

import argparse
import httplib2
from oauth2client.tools import run_flow
from oauth2client.tools import argparser
from oauth2client.file import Storage

CLIENT_SECRETS_FILE = "your_file.json"
OAUTH_SCOPE = 'https://www.googleapis.com/auth/gmail.readonly'

STORAGE = Storage("storage")

flow = flow_from_clientsecrets(CLIENT_SECRETS_FILE, scope=OAUTH_SCOPE)
http = httplib2.Http()

credentials = STORAGE.get()

if credentials is None or credentials.invalid:
    #magic 
    parser = argparse.ArgumentParser(parents=[argparser])
    flags = parser.parse_args()
    credentials = run_flow(flow, STORAGE, flags, http=http)


http = credentials.authorize(http)
gmApi = build('gmail', 'v1', http=http)
# ...
Anima answered 22/11, 2014 at 7:56 Comment(0)
T
-2

The flags that can be passed can be found here and are:

  --auth_host_name: Host name to use when running a local web server
    to handle redirects during OAuth authorization.
    (default: 'localhost')

  --auth_host_port: Port to use when running a local web server to handle
    redirects during OAuth authorization.;
    repeat this option to specify a list of values
    (default: '[8080, 8090]')
    (an integer)

  --[no]auth_local_webserver: Run a local web server to handle redirects
    during OAuth authorization.
    (default: 'true')

I have yet to work out how exactly to parse them, I have tried passing through various different values for each of these three flags but nothing works for me. I have a bountied question here that may be of use for you when answered.

Thies answered 27/8, 2014 at 14:28 Comment(3)
You probably should have just put the bounty on this question instead of posting another one since they are basically identical.. but in any case... i found a way to get it working... when I have time I'll post it so you can see :)Asymmetric
Aye, I'm surprised I didn't find this question in my searches.. Sorry! I hope you find the time to share what you found :)Thies
no problem. I used a built in method in our service to connect to gmail. I'll dig into it and post the relevant stuff.Asymmetric

© 2022 - 2024 — McMap. All rights reserved.