Merge branch '2551-feature-track-events'

This commit is contained in:
John Glover 2012-07-23 11:53:00 +01:00
commit 0cb3944ccd
4 changed files with 152 additions and 62 deletions

View File

@ -60,12 +60,10 @@ Installation
Finally, there are some optional configuration settings (shown here Finally, there are some optional configuration settings (shown here
with their default settings):: with their default settings)::
googleanalytics.show_downloads = true
googleanalytics.resource_prefix = /downloads/ googleanalytics.resource_prefix = /downloads/
googleanalytics.domain = auto googleanalytics.domain = auto
googleanalytics.show_downloads = true
If ``show_downloads`` is set, a download count for resources will googleanalytics.track_events = false
be displayed on individual package pages.
``resource_prefix`` is an arbitrary identifier so that we can query ``resource_prefix`` is an arbitrary identifier so that we can query
for downloads in Google Analytics. It can theoretically be any for downloads in Google Analytics. It can theoretically be any
@ -76,8 +74,16 @@ Installation
``domain`` allows you to specify a domain against which Analytics ``domain`` allows you to specify a domain against which Analytics
will track users. You will usually want to leave this as ``auto``; will track users. You will usually want to leave this as ``auto``;
if you are tracking users from multiple subdomains, you might want if you are tracking users from multiple subdomains, you might want
to specify something like ``.mydomain.com``. See `Google's to specify something like ``.mydomain.com``.
documentation <http://code.google.com/apis/analytics/docs/gaJS/gaJSApiDomainDirectory.html#_gat.GA_Tracker_._setDomainName>`_ for more info. See `Google's documentation
<http://code.google.com/apis/analytics/docs/gaJS/gaJSApiDomainDirectory.html#_gat.GA_Tracker_._setDomainName>`_
for more info.
If ``show_downloads`` is set, a download count for resources will
be displayed on individual package pages.
If ``track_events`` is set, Google Analytics event tracking will be
enabled.
5. Restart CKAN (e.g. by restarting Apache) 5. Restart CKAN (e.g. by restarting Apache)

View File

@ -1,4 +1,4 @@
gacode = """ header_code = """
<script type="text/javascript"> <script type="text/javascript">
var _gaq = _gaq || []; var _gaq = _gaq || [];
_gaq.push(['_setAccount', '%s']); _gaq.push(['_setAccount', '%s']);
@ -11,3 +11,15 @@ gacode = """
})(); })();
</script> </script>
""" """
footer_code = """
<script type="text/javascript" src="%s"></script>
"""
download_style = """
<style type="text/css">
span.downloads-count {
font-size: 0.9em;
}
</style>
"""

View File

@ -1,17 +1,15 @@
import logging import logging
import urllib import urllib
import os
from paste.deploy.converters import asbool from paste.deploy.converters import asbool
from genshi.filters import Transformer from genshi.filters import Transformer
from genshi import HTML from genshi import HTML
from genshi.core import START, TEXT from genshi.core import START, TEXT
from genshi.filters.transform import INSIDE, EXIT from genshi.filters.transform import INSIDE, EXIT
from pylons import config, request import pylons
from ckan.plugins import implements, SingletonPlugin import ckan.lib.helpers as h
from ckan.plugins import IGenshiStreamFilter, IConfigurable, IRoutes import ckan.plugins as p
from ckan.plugins import IConfigurer import gasnippet
from gasnippet import gacode import commands
from commands import DEFAULT_RESOURCE_URL_TAG
import dbutil import dbutil
log = logging.getLogger('ckanext.googleanalytics') log = logging.getLogger('ckanext.googleanalytics')
@ -21,44 +19,69 @@ class GoogleAnalyticsException(Exception):
pass pass
class GoogleAnalyticsPlugin(SingletonPlugin): class GoogleAnalyticsPlugin(p.SingletonPlugin):
implements(IConfigurable, inherit=True) p.implements(p.IConfigurable, inherit=True)
implements(IGenshiStreamFilter, inherit=True) p.implements(p.IGenshiStreamFilter, inherit=True)
implements(IRoutes, inherit=True) p.implements(p.IRoutes, inherit=True)
implements(IConfigurer, inherit=True) p.implements(p.IConfigurer, inherit=True)
def configure(self, config): def configure(self, config):
self.config = config
if (not 'googleanalytics.id' in config): if (not 'googleanalytics.id' in config):
msg = "Missing googleanalytics.id in config" msg = "Missing googleanalytics.id in config"
raise GoogleAnalyticsException(msg) raise GoogleAnalyticsException(msg)
def filter(self, stream): ga_id = config['googleanalytics.id']
log.info("Inserting GA code into template") ga_domain = config.get('googleanalytics.domain', 'auto')
ga_id = self.config['googleanalytics.id'] js_url = h.url_for_static('/scripts/ckanext-googleanalytics.js')
ga_domain = self.config.get('googleanalytics.domain', 'auto') self.resource_url = config.get('googleanalytics.resource_prefix',
code = HTML(gacode % (ga_id, ga_domain)) commands.DEFAULT_RESOURCE_URL_TAG)
stream = stream | Transformer('head').append(code) self.show_downloads = asbool(
resource_url = config.get('googleanalytics.resource_prefix', config.get('googleanalytics.show_downloads', True)
DEFAULT_RESOURCE_URL_TAG) )
self.track_events = asbool(
config.get('googleanalytics.track_events', False)
)
routes = request.environ.get('pylons.routes_dict') self.header_code = HTML(gasnippet.header_code % (ga_id, ga_domain))
action = routes.get('action') self.footer_code = HTML(gasnippet.footer_code % js_url)
def update_config(self, config):
p.toolkit.add_template_directory(config, 'templates')
p.toolkit.add_public_directory(config, 'public')
def after_map(self, map):
map.redirect("/analytics/package/top", "/analytics/dataset/top")
map.connect(
'analytics', '/analytics/dataset/top',
controller='ckanext.googleanalytics.controller:GAController',
action='view'
)
return map
def filter(self, stream):
log.info("Inserting Google Analytics code into template")
stream = stream | Transformer('head').append(self.header_code)
if self.track_events:
stream = stream | Transformer('body/div[@id="scripts"]')\
.append(self.footer_code)
routes = pylons.request.environ.get('pylons.routes_dict')
action = routes.get('action')
controller = routes.get('controller') controller = routes.get('controller')
if (controller == 'package' and \ if (controller == 'package' and \
action in ['search', 'read', 'resource_read']) or \ action in ['search', 'read', 'resource_read']) or \
(controller == 'group' and action == 'read'): (controller == 'group' and action == 'read'):
log.info("Tracking of resource downloads") log.info("Tracking of resource downloads")
show_downloads = (
asbool(config.get('googleanalytics.show_downloads', True)) and
action == 'read' and controller == 'package'
)
# add download tracking link # add download tracking link
def js_attr(name, event): def js_attr(name, event):
attrs = event[1][1] attrs = event[1][1]
href = attrs.get('href').encode('utf-8') href = attrs.get('href').encode('utf-8')
link = '%s%s' % (resource_url, urllib.quote(href)) link = '%s%s' % (self.resource_url, urllib.quote(href))
js = "javascript: _gaq.push(['_trackPageview', '%s']);" % link js = "javascript: _gaq.push(['_trackPageview', '%s']);" % link
return js return js
@ -77,38 +100,15 @@ class GoogleAnalyticsPlugin(SingletonPlugin):
yield INSIDE, (TEXT, HTML(download_html % count), pos) yield INSIDE, (TEXT, HTML(download_html % count), pos)
yield mark, (kind, data, pos) yield mark, (kind, data, pos)
# and some styling
download_style = '''
<style type="text/css">
span.downloads-count {
font-size: 0.9em;
}
</style>
'''
# perform the stream transform # perform the stream transform
stream = stream | Transformer('//a[contains(@class, "resource-url-analytics")]')\ stream = stream | Transformer('//a[contains(@class, "resource-url-analytics")]')\
.attr('onclick', js_attr) .attr('onclick', js_attr)
if show_downloads: if (self.show_downloads and action == 'read' and
controller == 'package'):
stream = stream | Transformer('//a[contains(@class, "resource-url-analytics")]')\ stream = stream | Transformer('//a[contains(@class, "resource-url-analytics")]')\
.apply(download_adder) .apply(download_adder)
stream = stream | Transformer('//head').append(HTML(download_style)) stream = stream | Transformer('//head')\
.append(HTML(gasnippet.download_style))
return stream return stream
def after_map(self, map):
map.redirect("/analytics/package/top", "/analytics/dataset/top")
map.connect('analytics', '/analytics/dataset/top',
controller='ckanext.googleanalytics.controller:GAController',
action='view')
return map
def update_config(self, config):
here = os.path.dirname(__file__)
rootdir = os.path.dirname(os.path.dirname(here))
template_dir = os.path.join(rootdir, 'ckanext',
'googleanalytics', 'templates')
config['extra_template_paths'] = ','.join(
[template_dir, config.get('extra_template_paths', '')]
)

View File

@ -0,0 +1,72 @@
(function ($) {
$(document).ready(function () {
// Google Analytics event tracking
// group links on home page
$('body.home div.group a').click(function() {
_gaq.push(['_trackEvent', 'Home', 'Click: Group Link', $(this).attr('href')]);
});
// clicking on user name (go to profile)
$('div.account span.ckan-logged-in a').first().click(function() {
_gaq.push(['_trackEvent', 'User', 'Click: User Name', $(this).attr('href')]);
});
// In user profile, clicking on Edit Profile
$('body.user div#minornavigation a')
.filter(function(index) {return $(this).text() === "Edit Profile";})
.click(function() {
_gaq.push(['_trackEvent', 'User', 'Click: Tab', 'Edit Profile']);
});
// Clicking Save Changes on Edit Profile page
$('body.user.edit input#save').click(function() {
_gaq.push(['_trackEvent', 'User', 'Click: Button', 'Save Profile Changes']);
});
// Clicking on any dataset link on User Profile page
$('body.user.read ul.datasets a').click(function() {
_gaq.push(['_trackEvent', 'User', 'Click: Dataset Link', $(this).attr('href')]);
});
// Compare Button on /dataset/history/X
$('body.package.history form#dataset-revisions input[name="diff"]').click(function() {
_gaq.push(['_trackEvent', 'Dataset', 'Click: Button', 'Compare History']);
});
// Tags on right hand sidebar of /dataset/X
$('body.package.read div#sidebar h3')
.filter(function(index) {return $(this).text().indexOf("Tags") != -1;})
.next('ul')
.find('a')
.click(function() {
_gaq.push(['_trackEvent', 'Dataset', 'Click: Tag', $(this).attr('href')]);
});
// Any of the group links on /group
$('body.group.index table.groups a').click(function() {
_gaq.push(['_trackEvent', 'Group', 'Click: Group Link', $(this).attr('href')]);
});
// Clicking any of the right hand sidebar tags on /group/X
$('body.group.read div#sidebar h2')
.filter(function(index) {return $(this).text().indexOf("Tags") != -1;})
.next('ul')
.find('a')
.click(function() {
_gaq.push(['_trackEvent', 'Group', 'Click: Tag', $(this).attr('href')]);
});
// Visiting /group/history/X
$('body.group div#minornavigation ul.nav a')
.filter(function(index) {return $(this).text().indexOf("History") != -1;})
.click(function() {
_gaq.push(['_trackEvent', 'Group', 'Click: History Tab', $(this).attr('href')]);
});
// Compare Button on /group/history/X
$('body.group.history form#group-revisions input[name="diff"]').click(function() {
_gaq.push(['_trackEvent', 'Group', 'Click: Button', 'Compare History']);
});
});
}(jQuery));