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:
parent
6d7aa555c3
commit
56fbb853a4
|
@ -18,6 +18,9 @@ Features
|
||||||
resource downloads will be displayed as Events in the Google Analytics
|
resource downloads will be displayed as Events in the Google Analytics
|
||||||
reporting interface.
|
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,
|
* Adds Google Analytics Event Tracking to group links on the home page,
|
||||||
user profile links, editing and saving user profiles, etc.
|
user profile links, editing and saving user profiles, etc.
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,19 @@ import logging
|
||||||
from ckan.lib.base import BaseController, c, render
|
from ckan.lib.base import BaseController, c, render
|
||||||
import dbutil
|
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')
|
log = logging.getLogger('ckanext.googleanalytics')
|
||||||
|
|
||||||
class GAController(BaseController):
|
class GAController(BaseController):
|
||||||
|
@ -10,3 +23,71 @@ class GAController(BaseController):
|
||||||
c.top_packages = dbutil.get_top_packages(limit=10)
|
c.top_packages = dbutil.get_top_packages(limit=10)
|
||||||
c.top_resources = dbutil.get_top_resources(limit=10)
|
c.top_resources = dbutil.get_top_resources(limit=10)
|
||||||
return render('summary.html')
|
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)
|
||||||
|
|
|
@ -8,6 +8,7 @@ import pylons
|
||||||
import ckan.lib.helpers as h
|
import ckan.lib.helpers as h
|
||||||
import ckan.plugins as p
|
import ckan.plugins as p
|
||||||
import gasnippet
|
import gasnippet
|
||||||
|
from routes.mapper import SubMapper, Mapper as _Mapper
|
||||||
|
|
||||||
log = logging.getLogger('ckanext.googleanalytics')
|
log = logging.getLogger('ckanext.googleanalytics')
|
||||||
|
|
||||||
|
@ -67,6 +68,58 @@ class GoogleAnalyticsPlugin(p.SingletonPlugin):
|
||||||
else:
|
else:
|
||||||
p.toolkit.add_template_directory(config, 'templates')
|
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):
|
def after_map(self, map):
|
||||||
'''Add new routes that this extension's controllers handle.
|
'''Add new routes that this extension's controllers handle.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue