Implement oauth2 for accessing google analytics
- Provided a command (getauthtoken) to go through the auth flow and have
the user's oauth credentials written to local disk.
- Make sure loadanalytics command uses the token file to authenticate on
the request
- Added documentation on how to get the credentials (from google) to allow
for the oauth2 flow.
- Added google-api-client to the requirements
--- a/.gitignore
+++ b/.gitignore
@@ -15,6 +15,10 @@
develop-eggs
.installed.cfg
+# Private info
+credentials.json
+token.dat
+
# Installer logs
pip-log.txt
--- a/README.rst
+++ b/README.rst
@@ -46,12 +46,43 @@
ckan.plugins = ga-report
+Authorization
+--------------
+
+Before you can access the data, you need to set up the OAUTH details which you can do by following the `instructions <https://developers.google.com/analytics/resources/tutorials/hello-analytics-api>`_ the outcome of which will be a file called credentials.json which should look like credentials.json.template with the relevant fields completed. These steps are below for convenience:
+
+1. Visit the `Google APIs Console <https://code.google.com/apis/console>`_
+
+2. Sign-in and create a project or use an existing project.
+
+3. In the `Services pane <https://code.google.com/apis/console#:services>`_ , activate Analytics API for your project. If prompted, read and accept the terms of service.
+
+4. Go to the `API Access pane <https://code.google.com/apis/console/#:access>`_
+
+5. Click Create an OAuth 2.0 client ID....
+
+6. Fill out the Branding Information fields and click Next.
+
+7. In Client ID Settings, set Application type to Installed application.
+
+8. Click Create client ID
+
+9. The details you need below are Client ID, Client secret, and Redirect URIs
+
+
+Once you have set up your credentials.json file you can generate an oauth token file by using the
+following command, which will store your oauth token in a file called token.dat once you have finished
+giving permission in the browser.
+
+ $ paster getauthtoken --config=../ckan/development.ini
+
+
Tutorial
--------
-Download some GA data and store it in CKAN's db. (Ensure your CKAN pyenv is still activated, run the command from ``src/ckanext-ga-report``, alter the ``--config`` option to point to your site config file)::
+Download some GA data and store it in CKAN's db. (Ensure your CKAN pyenv is still activated, run the command from ``src/ckanext-ga-report``, alter the ``--config`` option to point to your site config file) and specifying the name of your auth file (token.dat by default) from the previous step::
- $ paster loadanalytics latest --config=../ckan/development.ini
+ $ paster loadanalytics token.dat latest --config=../ckan/development.ini
Software Licence
--- a/ckanext/ga_report/command.py
+++ b/ckanext/ga_report/command.py
@@ -23,13 +23,31 @@
ga_model.init_tables()
log.info("DB tables are setup")
+
+class GetAuthToken(CkanCommand):
+ """ Get's the Google auth token
+ """
+ summary = __doc__.split('\n')[0]
+ usage = __doc__
+ max_args = 0
+ min_args = 0
+
+ def command(self):
+ from ga_auth import initialize_service
+ initialize_service('token.dat',
+ self.args[0] if self.args
+ else 'credentials.json')
+
class LoadAnalytics(CkanCommand):
"""Get data from Google Analytics API and save it
in the ga_model
- Usage: paster loadanalytics <time-period>
+ Usage: paster loadanalytics <tokenfile> <time-period>
- Where <time-period> is:
+ Where <tokenfile> is the name of the auth token file from
+ the getauthtoken step.
+
+ And where <time-period> is:
all - data for all time
latest - (default) just the 'latest' data
YYYY-MM-DD - just data for all time periods going
@@ -37,16 +55,24 @@
"""
summary = __doc__.split('\n')[0]
usage = __doc__
- max_args = 1
- min_args = 0
+ max_args = 2
+ min_args = 1
def command(self):
self._load_config()
+ from ga_auth import initialize_service
+ try:
+ svc = initialize_service(self.args[0], None)
+ except TypeError:
+ print 'Have you correctly run the getauthtoken task and specified the correct file here'
+ return
+
from download_analytics import DownloadAnalytics
- downloader = DownloadAnalytics()
-
- time_period = self.args[0] if self.args else 'latest'
+ from ga_auth import get_profile_id
+ downloader = DownloadAnalytics(svc, profile_id=get_profile_id(svc))
+
+ time_period = self.args[1] if self.args and len(self.args) > 1 else 'latest'
if time_period == 'all':
downloader.all_()
elif time_period == 'latest':
--- a/ckanext/ga_report/download_analytics.py
+++ b/ckanext/ga_report/download_analytics.py
@@ -4,7 +4,8 @@
from pylons import config
import ga_model
-from ga_client import GA
+
+#from ga_client import GA
log = logging.getLogger('ckanext.ga-report')
@@ -12,12 +13,16 @@
class DownloadAnalytics(object):
'''Downloads and stores analytics info'''
- def __init__(self):
+
+ def __init__(self, service=None, profile_id=None):
self.period = config['ga-report.period']
-
+ self.service = service
+ self.profile_id = profile_id
+
+
def all_(self):
- pass
-
+ self.since_date(datetime.datetime(2010, 1, 1))
+
def latest(self):
if self.period == 'monthly':
# from first of this month to today
@@ -49,8 +54,8 @@
break
elif first_of_the_month < first_of_this_month:
in_the_next_month = first_of_the_month + datetime.timedelta(40)
- last_of_the_month == datetime.datetime(in_the_next_month.year,
- in_the_next_month.month, a)\
+ last_of_the_month = datetime.datetime(in_the_next_month.year,
+ in_the_next_month.month, 1)\
- datetime.timedelta(1)
periods.append((now.strftime(FORMAT_MONTH), 0,
first_of_the_month, last_of_the_month))
@@ -71,7 +76,7 @@
return period_name + ' (up to %ith)' % period_complete_day
else:
return period_name
-
+
def download_and_store(self, periods):
for period_name, period_complete_day, start_date, end_date in periods:
@@ -84,8 +89,8 @@
self.get_full_period_name(period_name, period_complete_day))
self.store(period_name, period_complete_day, data)
- @classmethod
- def download(cls, start_date, end_date):
+
+ def download(self, start_date, end_date):
'''Get data from GA for a given time period'''
start_date = start_date.strftime('%Y-%m-%d')
end_date = end_date.strftime('%Y-%m-%d')
@@ -93,22 +98,45 @@
#query = 'ga:pagePath=~^%s,ga:pagePath=~^%s' % \
# (PACKAGE_URL, self.resource_url_tag)
query = 'ga:pagePath=~^/dataset/'
+ #query = 'ga:pagePath=~^/User/'
metrics = 'ga:uniquePageviews'
sort = '-ga:uniquePageviews'
- for entry in GA.ga_query(query_filter=query,
- from_date=start_date,
+
+ # Supported query params at
+ # https://developers.google.com/analytics/devguides/reporting/core/v3/reference
+ results = self.service.data().ga().get(
+ ids='ga:' + self.profile_id,
+ filters=query,
+ start_date=start_date,
metrics=metrics,
sort=sort,
- to_date=end_date):
- print entry
- import pdb; pdb.set_trace()
- for dim in entry.dimension:
- if dim.name == "ga:pagePath":
- package = dim.value
- count = entry.get_metric(
- 'ga:uniquePageviews').value or 0
- packages[package] = int(count)
- return packages
+ end_date=end_date).execute()
+ self.print_results(results)
+
+# for entry in GA.ga_query(query_filter=query,
+# from_date=start_date,
+# metrics=metrics,
+# sort=sort,
+# to_date=end_date):
+# print entry, type(entry)
+# import pdb; pdb.set_trace()
+# for dim in entry.dimension:
+# if dim.name == "ga:pagePath":
+# package = dim.value
+# count = entry.get_metric(
+# 'ga:uniquePageviews').value or 0
+# packages[package] = int(count)
+ return []
+
+ def print_results(self, results):
+ import pprint
+ pprint.pprint(results)
+ if results:
+ print 'Profile: %s' % results.get('profileInfo').get('profileName')
+ print 'Total results: %s' % results.get('totalResults')
+ print 'Total Visits: %s' % results.get('rows', [[-1]])[0][0]
+ else:
+ print 'No results found'
def store(self, period_name, period_complete_day, data):
if 'url' in data:
--- /dev/null
+++ b/ckanext/ga_report/ga_auth.py
@@ -1,1 +1,45 @@
+import httplib2
+from apiclient.discovery import build
+from oauth2client.client import flow_from_clientsecrets
+from oauth2client.file import Storage
+from oauth2client.tools import run
+from pylons import config
+
+
+def _prepare_credentials( token_filename, credentials_filename ):
+ storage = Storage( token_filename )
+ credentials = storage.get()
+
+ if credentials is None or credentials.invalid:
+ flow = flow_from_clientsecrets(credentials_filename,
+ scope='https://www.googleapis.com/auth/analytics.readonly',
+ message="Can't find the credentials file")
+ credentials = run(flow, storage)
+
+ return credentials
+
+def initialize_service( token_file, credentials_file ):
+ http = httplib2.Http()
+
+ credentials = _prepare_credentials(token_file, credentials_file)
+ http = credentials.authorize(http) # authorize the http object
+
+ return build('analytics', 'v3', http=http)
+
+def get_profile_id(service):
+ # Get a list of all Google Analytics accounts for this user
+ accounts = service.management().accounts().list().execute()
+
+ if accounts.get('items'):
+ firstAccountId = accounts.get('items')[0].get('id')
+ webPropertyId = config.get('googleanalytics.id')
+ profiles = service.management().profiles().list(
+ accountId=firstAccountId,
+ webPropertyId=webPropertyId).execute()
+
+ if profiles.get('items'):
+ # return the first Profile ID
+ return profiles.get('items')[0].get('id')
+
+ return None
--- /dev/null
+++ b/credentials.json.template
@@ -1,1 +1,11 @@
+{
+ "installed": {
+ "client_id": "",
+ "client_secret": "",
+ "redirect_uris": [""],
+ "auth_uri": "https://accounts.google.com/o/oauth2/auth",
+ "token_uri": "https://accounts.google.com/o/oauth2/token"
+ }
+}
+
--- a/setup.py
+++ b/setup.py
@@ -20,7 +20,8 @@
include_package_data=True,
zip_safe=False,
install_requires=[
- 'gdata'
+ 'gdata',
+ 'google-api-python-client'
],
entry_points=\
"""