From 128014db994759bfda18b9f6f56da0bddc5a3d61 Mon Sep 17 00:00:00 2001 From: Joost Cassee Date: Sun, 30 Oct 2011 16:51:26 +0100 Subject: [PATCH] Add support for the Spring Metrics service --- CHANGELOG.rst | 3 +- README.rst | 2 + analytical/templatetags/analytical.py | 1 + analytical/templatetags/spring_metrics.py | 84 ++++++++++++ analytical/tests/__init__.py | 1 + analytical/tests/test_tag_spring_metrics.py | 59 +++++++++ docs/services/spring_metrics.rst | 134 ++++++++++++++++++++ 7 files changed, 283 insertions(+), 1 deletion(-) create mode 100644 analytical/templatetags/spring_metrics.py create mode 100644 analytical/tests/test_tag_spring_metrics.py create mode 100644 docs/services/spring_metrics.rst diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f7bf710..044aabe 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,5 +1,6 @@ Version 0.11.0 -------------- +* Added support for the Spring Metrics service. * Allow sending events and properties to KISSmetrics (Paul Oswald). Version 0.10.0 @@ -9,7 +10,7 @@ Version 0.10.0 Version 0.9.2 ------------- -* Added suppert for the SnapEngage service. +* Added support for the SnapEngage service. * Updated Mixpanel code (Julien Grenier). Version 0.9.1 diff --git a/README.rst b/README.rst index dcd191f..cec30b6 100644 --- a/README.rst +++ b/README.rst @@ -33,6 +33,7 @@ Currently supported services: * `Performable`_ web analytics and landing pages * `Reinvigorate`_ visitor tracking * `SnapEngage`_ live chat +* `Spring Metrics`_ conversion tracking * `Woopra`_ web analytics The documentation can be found in the ``docs`` directory or `read @@ -61,6 +62,7 @@ an issue to discuss your plans. .. _Performable: http://www.performable.com/ .. _Reinvigorate: http://www.reinvigorate.com/ .. _SnapEngage: http://www.snapengage.com/ +.. _`Spring Metrics`: http://www.springmetrics.com/ .. _Woopra: http://www.woopra.com/ .. _`read online`: http://packages.python.org/django-analytical/ .. _`hosted by GitHub`: http://github.com/jcassee/django-analytical diff --git a/analytical/templatetags/analytical.py b/analytical/templatetags/analytical.py index a3d7581..aa40099 100644 --- a/analytical/templatetags/analytical.py +++ b/analytical/templatetags/analytical.py @@ -29,6 +29,7 @@ TAG_MODULES = [ 'analytical.performable', 'analytical.reinvigorate', 'analytical.snapengage', + 'analytical.spring_metrics', 'analytical.woopra', ] diff --git a/analytical/templatetags/spring_metrics.py b/analytical/templatetags/spring_metrics.py new file mode 100644 index 0000000..26633eb --- /dev/null +++ b/analytical/templatetags/spring_metrics.py @@ -0,0 +1,84 @@ +""" +Spring Metrics template tags and filters. +""" + +from __future__ import absolute_import + +import re + +from django.template import Library, Node, TemplateSyntaxError +from django.utils import simplejson + +from analytical.utils import get_identity, is_internal_ip, disable_html, \ + get_required_setting + + +TRACKING_ID_RE = re.compile(r'^[0-9a-f]+$') +TRACKING_CODE = """ + +""" + + +register = Library() + + +@register.tag +def spring_metrics(parser, token): + """ + Spring Metrics tracking template tag. + + Renders Javascript code to track page visits. You must supply + your Spring Metrics Tracking ID in the + ``SPRING_METRICS_TRACKING_ID`` setting. + """ + bits = token.split_contents() + if len(bits) > 1: + raise TemplateSyntaxError("'%s' takes no arguments" % bits[0]) + return SpringMetricsNode() + +class SpringMetricsNode(Node): + def __init__(self): + self.tracking_id = get_required_setting('SPRING_METRICS_TRACKING_ID', + TRACKING_ID_RE, "must be a hexadecimal string") + + def render(self, context): + custom = {} + for dict_ in context: + for var, val in dict_.items(): + if var.startswith('spring_metrics_'): + custom[var[15:]] = val + if 'email' not in custom: + identity = get_identity(context, 'spring_metrics', + lambda u: u.email) + if identity is not None: + custom['email'] = identity + + html = TRACKING_CODE % {'tracking_id': self.tracking_id, + 'custom_commands': self._generate_custom_javascript(custom)} + if is_internal_ip(context, 'SPRING_METRICS'): + html = disable_html(html, 'Spring Metrics') + return html + + def _generate_custom_javascript(self, vars): + commands = ("_springMetq.push(['%s', '%s']);" % (var, val) + for var, val in vars.items()) + return " ".join(commands) + + +def contribute_to_analytical(add_node): + SpringMetricsNode() # ensure properly configured + add_node('head_bottom', SpringMetricsNode) diff --git a/analytical/tests/__init__.py b/analytical/tests/__init__.py index aa6f6df..71a93b6 100644 --- a/analytical/tests/__init__.py +++ b/analytical/tests/__init__.py @@ -17,5 +17,6 @@ from analytical.tests.test_tag_optimizely import * from analytical.tests.test_tag_performable import * from analytical.tests.test_tag_reinvigorate import * from analytical.tests.test_tag_snapengage import * +from analytical.tests.test_tag_spring_metrics import * from analytical.tests.test_tag_woopra import * from analytical.tests.test_utils import * diff --git a/analytical/tests/test_tag_spring_metrics.py b/analytical/tests/test_tag_spring_metrics.py new file mode 100644 index 0000000..0de0804 --- /dev/null +++ b/analytical/tests/test_tag_spring_metrics.py @@ -0,0 +1,59 @@ +""" +Tests for the Spring Metrics template tags and filters. +""" + +import re + +from django.contrib.auth.models import User +from django.http import HttpRequest +from django.template import Context + +from analytical.templatetags.spring_metrics import SpringMetricsNode +from analytical.tests.utils import TagTestCase, override_settings, \ + SETTING_DELETED +from analytical.utils import AnalyticalException + + +@override_settings(SPRING_METRICS_TRACKING_ID='12345678') +class SpringMetricsTagTestCase(TagTestCase): + """ + Tests for the ``spring_metrics`` template tag. + """ + + def test_tag(self): + r = self.render_tag('spring_metrics', 'spring_metrics') + self.assertTrue("_springMetq.push(['id', '12345678']);" in r, r) + + def test_node(self): + r = SpringMetricsNode().render(Context({})) + self.assertTrue("_springMetq.push(['id', '12345678']);" in r, r) + + @override_settings(SPRING_METRICS_TRACKING_ID=SETTING_DELETED) + def test_no_site_id(self): + self.assertRaises(AnalyticalException, SpringMetricsNode) + + @override_settings(SPRING_METRICS_TRACKING_ID='123xyz') + def test_wrong_site_id(self): + self.assertRaises(AnalyticalException, SpringMetricsNode) + + @override_settings(ANALYTICAL_AUTO_IDENTIFY=True) + def test_identify(self): + r = SpringMetricsNode().render(Context({'user': + User(email='test@test.com')})) + self.assertTrue("_springMetq.push(['email', 'test@test.com']);" in r, r) + + def test_custom(self): + r = SpringMetricsNode().render(Context({'spring_metrics_var1': 'val1', + 'spring_metrics_var2': 'val2'})) + self.assertTrue("_springMetq.push(['var1', 'val1']);" in r, r) + self.assertTrue("_springMetq.push(['var2', 'val2']);" in r, r) + + @override_settings(ANALYTICAL_INTERNAL_IPS=['1.1.1.1']) + def test_render_internal_ip(self): + req = HttpRequest() + req.META['REMOTE_ADDR'] = '1.1.1.1' + context = Context({'request': req}) + r = SpringMetricsNode().render(context) + self.assertTrue(r.startswith( + ''), r) diff --git a/docs/services/spring_metrics.rst b/docs/services/spring_metrics.rst new file mode 100644 index 0000000..c9937a3 --- /dev/null +++ b/docs/services/spring_metrics.rst @@ -0,0 +1,134 @@ +===================================== +Spring Metrics -- conversion tracking +===================================== + +`Spring Metrics`_ is a convesions analysis tool. It shows you the top +converting sources, search keywords ande landing pages. The real-time +dashboard shows you how customers interacted with your website and how +to increase conversion. + +.. _`Spring Metrics`: http://www.springmetrics.com/ + + +Installation +============ + +To start using the Spring Metrics integration, you must have installed +the django-analytical package and have added the ``analytical`` +application to :const:`INSTALLED_APPS` in your project +:file:`settings.py` file. See :doc:`../install` for details. + +Next you need to add the Spring Metrics template tag to your templates. +This step is only needed if you are not using the generic +:ttag:`analytical.*` tags. If you are, skip to +:ref:`spring-metrics-configuration`. + +The Spring Metrics tracking code is inserted into templates using a +template tag. Load the :mod:`spring_metrics` template tag library and +insert the :ttag:`spring_metrics` tag. Because every page that you +want to track must have the tag, it is useful to add it to your base +template. Insert the tag at the bottom of the HTML head:: + + {% load spring_metrics %} + + + ... + {% spring_metrics %} + + ... + + +.. _spring-metrics-configuration: + +Configuration +============= + +Before you can use the Spring Metrics integration, you must first set +your website Tracking ID. You can also customize the data that Spring +Metrics tracks. + + +Setting the Tracking ID +----------------------- + +Every website you track with Spring Metrics gets its own Tracking ID, +and the :ttag:`spring_metrics` tag will include it in the rendered +Javascript code. You can find the Tracking ID in the `manage page`_ +of your Spring Metrics account. Set :const:`SPRING_METRICS_TRACKING_ID` +in the project :file:`settings.py` file:: + + SPRING_METRICS_TRACKING_ID = 'XXXXXXXXXX' + +If you do not set a Tracking ID, the tracking code will not be rendered. + +.. _`manage page`: https://app.springmetrics.com/manage/ + + +Internal IP addresses +--------------------- + +Usually you do not want to track clicks from your development or +internal IP addresses. By default, if the tags detect that the client +comes from any address in the :const:`SPRING_METRICS_INTERNAL_IPS` +setting, the tracking code is commented out. It takes the value of +:const:`ANALYTICAL_INTERNAL_IPS` by default (which in turn is +:const:`INTERNAL_IPS` by default). See :ref:`identifying-visitors` for +important information about detecting the visitor IP address. + + +Tracking revenue +---------------- + +The most important value tracked by Spring Metrics is that of revenue. +Using the :data:`spring_metrics_revenue` template context variable, you +can let the :ttag:`spring_metrics` tag pass earned revenue to Spring +Metrics. You can set the context variable in your view when you render +a template containing thetracking code:: + + context = RequestContext({'spring_metrics_revenue': '30.53'}) + return some_template.render(context) + + +Custom data +----------- + +Spring Metrics can also track other data. Interesting examples would be +transaction IDs or e-mail addresses from logged in users. By setting +any :data:`spring_metrics_X` template context variable, Spring Metrics +will track a variable named :data:`X`. For example:: + + context = RequestContext({ + 'spring_metrics_revenue': '30.53', + 'spring_metrics_order_id': '15445', + }) + return some_template.render(context) + +Some variables should be passed on every page and can be computed from +the request object. In such cases you will want to set custom +variables in a context processor that you add to the +:data:`TEMPLATE_CONTEXT_PROCESSORS` list in :file:`settings.py`:: + + def spring_metrics_global_variables(request): + try: + profile = request.user.get_profile() + return {'spring_metrics_city': profile.address.city} + except (AttributeError, ObjectDoesNotExist): + return {} + +Just remember that if you set the same context variable in the +:class:`~django.template.context.RequestContext` constructor and in a +context processor, the latter clobbers the former. + + +Identifying authenticated users +------------------------------- + +If you have not set the :data:`spring_metrics_email` property +explicitly, the e-mail address of an authenticated user is passed to +Spring Metrics automatically. See :ref:`identifying-visitors`. + + +---- + +Thanks go to Spring Metrics for their support with the development of +this application.