Adds Google Analytics Event Tracking to some API calls so that usage

of the CKAN API can be reported on via Google Analytics.
This commit is contained in:
Alex Sadleir 2013-10-20 02:04:28 +11:00
parent 6d7aa555c3
commit 56fbb853a4
3 changed files with 137 additions and 0 deletions

View File

@ -18,6 +18,9 @@ Features
resource downloads will be displayed as Events in the Google Analytics
reporting interface.
* Adds Google Analytics Event Tracking to some API calls so that usage of the
API can be reported on via Google Analytics.
* Adds Google Analytics Event Tracking to group links on the home page,
user profile links, editing and saving user profiles, etc.

View File

@ -2,6 +2,19 @@ import logging
from ckan.lib.base import BaseController, c, render
import dbutil
import urllib
from pprint import pprint
import logging
import ckan.logic as logic
import hashlib
import threading
from pylons import config
from webob.multidict import UnicodeMultiDict
from paste.util.multidict import MultiDict
from ckan.controllers.api import ApiController
log = logging.getLogger('ckanext.googleanalytics')
class GAController(BaseController):
@ -10,3 +23,71 @@ class GAController(BaseController):
c.top_packages = dbutil.get_top_packages(limit=10)
c.top_resources = dbutil.get_top_resources(limit=10)
return render('summary.html')
class GAApiController(ApiController):
# intercept API calls to record via google analytics
def _post_analytics(self,user,request_obj_type,request_function,request_id):
if (config.get('googleanalytics.id') != None):
data = urllib.urlencode({
"v":1,
"tid":config.get('googleanalytics.id'),
"cid":hashlib.md5(user).hexdigest(), #customer id should be obfuscated
"t":"event",
"dh":c.environ['HTTP_HOST'],
"dp":c.environ['PATH_INFO'],
"dr":c.environ.get('HTTP_REFERER',''),
"ec":"CKAN API Request",
"ea":request_obj_type+request_function,
"el":request_id,
})
log.debug("Sending API event to Google Analytics: "+data)
# send analytics asynchronously
threading.Thread(target=urllib.urlopen,args=("http://www.google-analytics.com/collect", data)).start()
def action(self, logic_function, ver=None):
try:
function = logic.get_action(logic_function)
except Exception,e:
log.debug(e)
pass
try:
side_effect_free = getattr(function, 'side_effect_free', False)
request_data = self._get_request_data(try_url_params=side_effect_free)
if isinstance(request_data, dict):
id = request_data.get('id','')
if 'q' in request_data.keys():
id = request_data['q']
if 'query' in request_data.keys():
id = request_data['query']
self._post_analytics(c.user,logic_function,'', id)
except Exception,e:
print log.debug(e)
pass
return ApiController.action(self,logic_function, ver)
def list(self, ver=None, register=None, subregister=None, id=None):
self._post_analytics(c.user,register+("_"+str(subregister) if subregister else ""),"list",id)
return ApiController.list(self,ver, register, subregister, id)
def show(self, ver=None, register=None, subregister=None, id=None, id2=None):
self._post_analytics(c.user,register+("_"+str(subregister) if subregister else ""),"show",id)
return ApiController.show(self,ver, register, subregister, id,id2)
def update(self, ver=None, register=None, subregister=None, id=None, id2=None):
self._post_analytics(c.user,register+("_"+str(subregister) if subregister else ""),"update",id)
return ApiController.update(self,ver, register, subregister, id,id2)
def delete(self, ver=None, register=None, subregister=None, id=None, id2=None):
self._post_analytics(c.user,register+("_"+str(subregister) if subregister else ""),"delete",id)
return ApiController.delete(self,ver, register, subregister, id,id2)
def search(self, ver=None, register=None):
id = None
try:
params = MultiDict(self._get_search_params(request.params))
if 'q' in params.keys():
id = params['q']
if 'query' in params.keys():
id = params['query']
except ValueError, e:
print str(e)
pass
self._post_analytics(c.user,register,"search",id)

View File

@ -8,6 +8,7 @@ import pylons
import ckan.lib.helpers as h
import ckan.plugins as p
import gasnippet
from routes.mapper import SubMapper, Mapper as _Mapper
log = logging.getLogger('ckanext.googleanalytics')
@ -67,6 +68,58 @@ class GoogleAnalyticsPlugin(p.SingletonPlugin):
else:
p.toolkit.add_template_directory(config, 'templates')
def before_map(self, map):
'''Add new routes that this extension's controllers handle.
See IRoutes.
'''
# Helpers to reduce code clutter
GET = dict(method=['GET'])
PUT = dict(method=['PUT'])
POST = dict(method=['POST'])
DELETE = dict(method=['DELETE'])
GET_POST = dict(method=['GET', 'POST'])
# intercept API calls that we want to capture analytics on
register_list = [
'package',
'dataset',
'resource',
'tag',
'group',
'related',
'revision',
'licenses',
'rating',
'user',
'activity'
]
register_list_str = '|'.join(register_list)
# /api ver 3 or none
with SubMapper(map, controller='ckanext.googleanalytics.controller:GAApiController', path_prefix='/api{ver:/3|}',
ver='/3') as m:
m.connect('/action/{logic_function}', action='action',
conditions=GET_POST)
# /api ver 1, 2, 3 or none
with SubMapper(map, controller='ckanext.googleanalytics.controller:GAApiController', path_prefix='/api{ver:/1|/2|/3|}',
ver='/1') as m:
m.connect('/search/{register}', action='search')
# /api/rest ver 1, 2 or none
with SubMapper(map, controller='ckanext.googleanalytics.controller:GAApiController', path_prefix='/api{ver:/1|/2|}',
ver='/1', requirements=dict(register=register_list_str)
) as m:
m.connect('/rest/{register}', action='list', conditions=GET)
m.connect('/rest/{register}', action='create', conditions=POST)
m.connect('/rest/{register}/{id}', action='show', conditions=GET)
m.connect('/rest/{register}/{id}', action='update', conditions=PUT)
m.connect('/rest/{register}/{id}', action='update', conditions=POST)
m.connect('/rest/{register}/{id}', action='delete', conditions=DELETE)
return map
def after_map(self, map):
'''Add new routes that this extension's controllers handle.