From 1fa09bae92c4bc9700603a78ff317c472041d724 Mon Sep 17 00:00:00 2001 From: John Glover Date: Wed, 11 Jul 2012 10:38:47 +0100 Subject: [PATCH 1/4] Tidy up imports a bit in plugin.py --- ckanext/googleanalytics/plugin.py | 52 +++++++++++++------------------ 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/ckanext/googleanalytics/plugin.py b/ckanext/googleanalytics/plugin.py index 98dcb42..e8d0ae5 100644 --- a/ckanext/googleanalytics/plugin.py +++ b/ckanext/googleanalytics/plugin.py @@ -1,17 +1,14 @@ import logging import urllib -import os from paste.deploy.converters import asbool from genshi.filters import Transformer from genshi import HTML from genshi.core import START, TEXT from genshi.filters.transform import INSIDE, EXIT from pylons import config, request -from ckan.plugins import implements, SingletonPlugin -from ckan.plugins import IGenshiStreamFilter, IConfigurable, IRoutes -from ckan.plugins import IConfigurer -from gasnippet import gacode -from commands import DEFAULT_RESOURCE_URL_TAG +import ckan.plugins as p +import gasnippet +import commands import dbutil log = logging.getLogger('ckanext.googleanalytics') @@ -21,11 +18,11 @@ class GoogleAnalyticsException(Exception): pass -class GoogleAnalyticsPlugin(SingletonPlugin): - implements(IConfigurable, inherit=True) - implements(IGenshiStreamFilter, inherit=True) - implements(IRoutes, inherit=True) - implements(IConfigurer, inherit=True) +class GoogleAnalyticsPlugin(p.SingletonPlugin): + p.implements(p.IConfigurable, inherit=True) + p.implements(p.IGenshiStreamFilter, inherit=True) + p.implements(p.IRoutes, inherit=True) + p.implements(p.IConfigurer, inherit=True) def configure(self, config): self.config = config @@ -33,17 +30,27 @@ class GoogleAnalyticsPlugin(SingletonPlugin): msg = "Missing googleanalytics.id in config" raise GoogleAnalyticsException(msg) + def update_config(self, config): + p.toolkit.add_template_directory(config, 'templates') + + 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 GA code into template") ga_id = self.config['googleanalytics.id'] ga_domain = self.config.get('googleanalytics.domain', 'auto') - code = HTML(gacode % (ga_id, ga_domain)) + code = HTML(gasnippet.gacode % (ga_id, ga_domain)) stream = stream | Transformer('head').append(code) resource_url = config.get('googleanalytics.resource_prefix', - DEFAULT_RESOURCE_URL_TAG) + commands.DEFAULT_RESOURCE_URL_TAG) routes = request.environ.get('pylons.routes_dict') - action = routes.get('action') + action = routes.get('action') controller = routes.get('controller') if (controller == 'package' and \ action in ['search', 'read', 'resource_read']) or \ @@ -54,6 +61,7 @@ class GoogleAnalyticsPlugin(SingletonPlugin): asbool(config.get('googleanalytics.show_downloads', True)) and action == 'read' and controller == 'package' ) + # add download tracking link def js_attr(name, event): attrs = event[1][1] @@ -96,19 +104,3 @@ class GoogleAnalyticsPlugin(SingletonPlugin): stream = stream | Transformer('//head').append(HTML(download_style)) 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', '')] - ) From 03b4023676bea602895aea43cd5bea283a3d40c2 Mon Sep 17 00:00:00 2001 From: John Glover Date: Wed, 11 Jul 2012 14:34:18 +0100 Subject: [PATCH 2/4] Add 'track_events' config option, tidy up config reading. --- README.rst | 18 ++++-- ckanext/googleanalytics/gasnippet.py | 14 ++++- ckanext/googleanalytics/plugin.py | 62 +++++++++++-------- .../public/scripts/ckanext-googleanalytics.js | 4 ++ 4 files changed, 64 insertions(+), 34 deletions(-) create mode 100644 ckanext/googleanalytics/public/scripts/ckanext-googleanalytics.js diff --git a/README.rst b/README.rst index ddb0a89..a0d8f63 100644 --- a/README.rst +++ b/README.rst @@ -60,12 +60,10 @@ Installation Finally, there are some optional configuration settings (shown here with their default settings):: - googleanalytics.show_downloads = true googleanalytics.resource_prefix = /downloads/ googleanalytics.domain = auto - - If ``show_downloads`` is set, a download count for resources will - be displayed on individual package pages. + googleanalytics.show_downloads = true + googleanalytics.track_events = false ``resource_prefix`` is an arbitrary identifier so that we can query 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 will track users. You will usually want to leave this as ``auto``; if you are tracking users from multiple subdomains, you might want - to specify something like ``.mydomain.com``. See `Google's - documentation `_ for more info. + to specify something like ``.mydomain.com``. + See `Google's documentation + `_ + 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) diff --git a/ckanext/googleanalytics/gasnippet.py b/ckanext/googleanalytics/gasnippet.py index 54f63d6..8a12f38 100644 --- a/ckanext/googleanalytics/gasnippet.py +++ b/ckanext/googleanalytics/gasnippet.py @@ -1,4 +1,4 @@ -gacode = """ +header_code = """ """ + +footer_code = """ + +""" + +download_style = """ + +""" diff --git a/ckanext/googleanalytics/plugin.py b/ckanext/googleanalytics/plugin.py index e8d0ae5..352dec8 100644 --- a/ckanext/googleanalytics/plugin.py +++ b/ckanext/googleanalytics/plugin.py @@ -6,6 +6,7 @@ from genshi import HTML from genshi.core import START, TEXT from genshi.filters.transform import INSIDE, EXIT from pylons import config, request +import ckan.lib.helpers as h import ckan.plugins as p import gasnippet import commands @@ -25,48 +26,62 @@ class GoogleAnalyticsPlugin(p.SingletonPlugin): p.implements(p.IConfigurer, inherit=True) def configure(self, config): - self.config = config if (not 'googleanalytics.id' in config): msg = "Missing googleanalytics.id in config" raise GoogleAnalyticsException(msg) + ga_id = config['googleanalytics.id'] + ga_domain = config.get('googleanalytics.domain', 'auto') + js_url = h.url_for_static('/scripts/ckanext-googleanalytics.js') + self.resource_url = config.get('googleanalytics.resource_prefix', + commands.DEFAULT_RESOURCE_URL_TAG) + self.show_downloads = asbool( + config.get('googleanalytics.show_downloads', True) + ) + self.track_events = asbool( + config.get('googleanalytics.track_events', False) + ) + + self.header_code = HTML(gasnippet.header_code % (ga_id, ga_domain)) + 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') + map.connect( + 'analytics', '/analytics/dataset/top', + controller='ckanext.googleanalytics.controller:GAController', + action='view' + ) return map def filter(self, stream): - log.info("Inserting GA code into template") - ga_id = self.config['googleanalytics.id'] - ga_domain = self.config.get('googleanalytics.domain', 'auto') - code = HTML(gasnippet.gacode % (ga_id, ga_domain)) - stream = stream | Transformer('head').append(code) - resource_url = config.get('googleanalytics.resource_prefix', - commands.DEFAULT_RESOURCE_URL_TAG) + 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 = request.environ.get('pylons.routes_dict') action = routes.get('action') controller = routes.get('controller') + if (controller == 'package' and \ action in ['search', 'read', 'resource_read']) or \ (controller == 'group' and action == 'read'): 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 def js_attr(name, event): attrs = event[1][1] 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 return js @@ -85,22 +100,15 @@ class GoogleAnalyticsPlugin(p.SingletonPlugin): yield INSIDE, (TEXT, HTML(download_html % count), pos) yield mark, (kind, data, pos) - # and some styling - download_style = ''' - - ''' - # perform the stream transform stream = stream | Transformer('//a[contains(@class, "resource-url-analytics")]')\ .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")]')\ .apply(download_adder) - stream = stream | Transformer('//head').append(HTML(download_style)) + stream = stream | Transformer('//head')\ + .append(HTML(gasnippet.download_style)) return stream diff --git a/ckanext/googleanalytics/public/scripts/ckanext-googleanalytics.js b/ckanext/googleanalytics/public/scripts/ckanext-googleanalytics.js new file mode 100644 index 0000000..de1dbaf --- /dev/null +++ b/ckanext/googleanalytics/public/scripts/ckanext-googleanalytics.js @@ -0,0 +1,4 @@ +(function ($) { + $(document).ready(function () { + }); +}(jQuery)); From ffd28f4619e1d605ecf8de517eb6a21f6965c8a7 Mon Sep 17 00:00:00 2001 From: John Glover Date: Wed, 11 Jul 2012 16:40:17 +0100 Subject: [PATCH 3/4] Add first batch of Google Analytics track events. --- ckanext/googleanalytics/plugin.py | 4 +- .../public/scripts/ckanext-googleanalytics.js | 53 ++++++++++++++++++- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/ckanext/googleanalytics/plugin.py b/ckanext/googleanalytics/plugin.py index 352dec8..f131877 100644 --- a/ckanext/googleanalytics/plugin.py +++ b/ckanext/googleanalytics/plugin.py @@ -5,7 +5,7 @@ from genshi.filters import Transformer from genshi import HTML from genshi.core import START, TEXT from genshi.filters.transform import INSIDE, EXIT -from pylons import config, request +import pylons import ckan.lib.helpers as h import ckan.plugins as p import gasnippet @@ -67,7 +67,7 @@ class GoogleAnalyticsPlugin(p.SingletonPlugin): stream = stream | Transformer('body/div[@id="scripts"]')\ .append(self.footer_code) - routes = request.environ.get('pylons.routes_dict') + routes = pylons.request.environ.get('pylons.routes_dict') action = routes.get('action') controller = routes.get('controller') diff --git a/ckanext/googleanalytics/public/scripts/ckanext-googleanalytics.js b/ckanext/googleanalytics/public/scripts/ckanext-googleanalytics.js index de1dbaf..bdbdcdc 100644 --- a/ckanext/googleanalytics/public/scripts/ckanext-googleanalytics.js +++ b/ckanext/googleanalytics/public/scripts/ckanext-googleanalytics.js @@ -1,4 +1,55 @@ (function ($) { - $(document).ready(function () { + $(document).ready(function () { + // Google Analytics event tracking + + // alert($(this).attr('href')); + + // 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')]); + }); + + // 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() === "Tags";}) + .next('ul') + .find('a') + .click(function() { + _gaq.push(['_trackEvent', 'Group', 'Click: Tag', $(this).attr('href')]); + }); + + // Visiting /group/history/X + // Compare Button on /group/history/X + // Compare Button on /dataset/history/X + // Tags on right hand sidebar of /dataset/X + // Download button on any /dataset/X/resource[> page + // Data API button on any /dataset/X/resource[> page + }); }(jQuery)); From df3f575119dd619e1e82b1c1d67541cbac334e57 Mon Sep 17 00:00:00 2001 From: John Glover Date: Mon, 23 Jul 2012 11:48:59 +0100 Subject: [PATCH 4/4] Add remaining track events. --- .../public/scripts/ckanext-googleanalytics.js | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/ckanext/googleanalytics/public/scripts/ckanext-googleanalytics.js b/ckanext/googleanalytics/public/scripts/ckanext-googleanalytics.js index bdbdcdc..8e9047d 100644 --- a/ckanext/googleanalytics/public/scripts/ckanext-googleanalytics.js +++ b/ckanext/googleanalytics/public/scripts/ckanext-googleanalytics.js @@ -2,8 +2,6 @@ $(document).ready(function () { // Google Analytics event tracking - // alert($(this).attr('href')); - // group links on home page $('body.home div.group a').click(function() { _gaq.push(['_trackEvent', 'Home', 'Click: Group Link', $(this).attr('href')]); @@ -31,6 +29,20 @@ _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')]); @@ -38,7 +50,7 @@ // Clicking any of the right hand sidebar tags on /group/X $('body.group.read div#sidebar h2') - .filter(function(index) {return $(this).text() === "Tags";}) + .filter(function(index) {return $(this).text().indexOf("Tags") != -1;}) .next('ul') .find('a') .click(function() { @@ -46,10 +58,15 @@ }); // 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 - // Compare Button on /dataset/history/X - // Tags on right hand sidebar of /dataset/X - // Download button on any /dataset/X/resource[> page - // Data API button on any /dataset/X/resource[> page + $('body.group.history form#group-revisions input[name="diff"]').click(function() { + _gaq.push(['_trackEvent', 'Group', 'Click: Button', 'Compare History']); + }); }); }(jQuery));