From 8f9b5104dac3a53f6f90cdd1f50039441e12007a Mon Sep 17 00:00:00 2001 From: Joost Cassee Date: Tue, 22 Feb 2011 10:44:53 +0100 Subject: [PATCH] Add Reinvigorate service This commit also contains a number of typo fixes in the documentation and bumps the version. --- analytical/__init__.py | 2 +- analytical/templatetags/reinvigorate.py | 80 +++++++++++ analytical/tests/__init__.py | 1 + analytical/tests/test_tag_reinvigorate.py | 65 +++++++++ analytical/utils.py | 11 +- docs/services/clicky.rst | 4 +- docs/services/crazy_egg.rst | 2 +- docs/services/google_analytics.rst | 2 +- docs/services/hubspot.rst | 2 +- docs/services/kiss_insights.rst | 2 +- docs/services/kiss_metrics.rst | 2 +- docs/services/mixpanel.rst | 2 +- docs/services/optimizely.rst | 2 +- docs/services/performable.rst | 6 +- docs/services/reinvigorate.rst | 157 ++++++++++++++++++++++ 15 files changed, 324 insertions(+), 16 deletions(-) create mode 100644 analytical/templatetags/reinvigorate.py create mode 100644 analytical/tests/test_tag_reinvigorate.py create mode 100644 docs/services/reinvigorate.rst diff --git a/analytical/__init__.py b/analytical/__init__.py index fff20fb..f63621b 100644 --- a/analytical/__init__.py +++ b/analytical/__init__.py @@ -10,6 +10,6 @@ Django_ project. See the ``docs`` directory for more information. __author__ = "Joost Cassee" __email__ = "joost@cassee.net" -__version__ = "0.5.1" +__version__ = "0.6.0" __copyright__ = "Copyright (C) 2011 Joost Cassee" __license__ = "MIT License" diff --git a/analytical/templatetags/reinvigorate.py b/analytical/templatetags/reinvigorate.py new file mode 100644 index 0000000..9d0889d --- /dev/null +++ b/analytical/templatetags/reinvigorate.py @@ -0,0 +1,80 @@ +""" +Reinvigorate 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'^[\w\d]{5}-[\w\d]{10}$') +TRACKING_CODE = """ + + +""" + + +register = Library() + + +@register.tag +def reinvigorate(parser, token): + """ + Reinvigorate tracking template tag. + + Renders Javascript code to track page visits. You must supply + your Reinvigorate tracking ID (as a string) in the + ``REINVIGORATE_TRACKING_ID`` setting. + """ + bits = token.split_contents() + if len(bits) > 1: + raise TemplateSyntaxError("'%s' takes no arguments" % bits[0]) + return ReinvigorateNode() + +class ReinvigorateNode(Node): + def __init__(self): + self.tracking_id = get_required_setting('REINVIGORATE_TRACKING_ID', + TRACKING_ID_RE, + "must be a string looking like XXXXX-XXXXXXXXXX") + + def render(self, context): + re_vars = {} + for dict_ in context: + for var, val in dict_.items(): + if var.startswith('reinvigorate_'): + re_vars[var[13:]] = val + if 'name' not in re_vars: + identity = get_identity(context, 'reinvigorate', + lambda u: u.get_full_name()) + if identity is not None: + re_vars['name'] = identity + if 'context' not in re_vars: + email = get_identity(context, 'reinvigorate', lambda u: u.email) + if email is not None: + re_vars['context'] = email + tags = " ".join("var re_%s_tag = %s;" % (tag, simplejson.dumps(value)) + for tag, value in re_vars.items()) + + html = TRACKING_CODE % {'tracking_id': self.tracking_id, + 'tags': tags} + if is_internal_ip(context, 'REINVIGORATE'): + html = disable_html(html, 'Reinvigorate') + return html + + +def contribute_to_analytical(add_node): + ReinvigorateNode() # ensure properly configured + add_node('body_bottom', ReinvigorateNode) diff --git a/analytical/tests/__init__.py b/analytical/tests/__init__.py index 4ac739e..d3eb93d 100644 --- a/analytical/tests/__init__.py +++ b/analytical/tests/__init__.py @@ -13,4 +13,5 @@ from analytical.tests.test_tag_kiss_metrics import * from analytical.tests.test_tag_mixpanel import * 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_utils import * diff --git a/analytical/tests/test_tag_reinvigorate.py b/analytical/tests/test_tag_reinvigorate.py new file mode 100644 index 0000000..3455a78 --- /dev/null +++ b/analytical/tests/test_tag_reinvigorate.py @@ -0,0 +1,65 @@ +""" +Tests for the Reinvigorate 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.reinvigorate import ReinvigorateNode +from analytical.tests.utils import TagTestCase +from analytical.utils import AnalyticalException + + +class ReinvigorateTagTestCase(TagTestCase): + """ + Tests for the ``reinvigorate`` template tag. + """ + + def setUp(self): + super(ReinvigorateTagTestCase, self).setUp() + self.settings_manager.set(REINVIGORATE_TRACKING_ID='12345-abcdefghij') + + def test_tag(self): + r = self.render_tag('reinvigorate', 'reinvigorate') + self.assertTrue('reinvigorate.track("12345-abcdefghij");' in r, r) + + def test_node(self): + r = ReinvigorateNode().render(Context({})) + self.assertTrue('reinvigorate.track("12345-abcdefghij");' in r, r) + + def test_no_tracking_id(self): + self.settings_manager.delete('REINVIGORATE_TRACKING_ID') + self.assertRaises(AnalyticalException, ReinvigorateNode) + + def test_wrong_tracking_id(self): + self.settings_manager.set(REINVIGORATE_TRACKING_ID='12345-abcdefghi') + self.assertRaises(AnalyticalException, ReinvigorateNode) + self.settings_manager.set(REINVIGORATE_TRACKING_ID='12345-abcdefghijk') + self.assertRaises(AnalyticalException, ReinvigorateNode) + + def test_identify(self): + self.settings_manager.set(ANALYTICAL_AUTO_IDENTIFY=True) + r = ReinvigorateNode().render(Context({'user': + User(username='test', first_name='Test', last_name='User', + email='test@example.com')})) + self.assertTrue('var re_name_tag = "Test User";' in r, r) + self.assertTrue('var re_context_tag = "test@example.com";' in r, r) + + def test_tags(self): + r = ReinvigorateNode().render(Context({'reinvigorate_var1': 'val1', + 'reinvigorate_var2': 2})) + self.assertTrue(re.search('var re_var1_tag = "val1";', r), r) + self.assertTrue(re.search('var re_var2_tag = 2;', r), r) + + def test_render_internal_ip(self): + self.settings_manager.set(ANALYTICAL_INTERNAL_IPS=['1.1.1.1']) + req = HttpRequest() + req.META['REMOTE_ADDR'] = '1.1.1.1' + context = Context({'request': req}) + r = ReinvigorateNode().render(context) + self.assertTrue(r.startswith( + ''), r) diff --git a/analytical/utils.py b/analytical/utils.py index 9e02ed4..71ed067 100644 --- a/analytical/utils.py +++ b/analytical/utils.py @@ -27,12 +27,14 @@ def get_required_setting(setting, value_re, invalid_msg): return value -def get_identity(context, prefix=None): +def get_identity(context, prefix=None, identity_func=None): """ Get the identity of a logged in user from a template context. The `prefix` argument is used to provide different identities to - different analytics services. + different analytics services. The `identity_func` argument is a + function that returns the identity of the user; by default the + identity is the username. """ if prefix is not None: try: @@ -51,7 +53,10 @@ def get_identity(context, prefix=None): request = context['request'] user = request.user if user.is_authenticated(): - return user.username + if identity_func is not None: + return identity_func(user) + else: + return user.username except (KeyError, AttributeError): pass return None diff --git a/docs/services/clicky.rst b/docs/services/clicky.rst index eb5208a..d6b2470 100644 --- a/docs/services/clicky.rst +++ b/docs/services/clicky.rst @@ -20,7 +20,7 @@ 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 Clicky template tags to your templates. This +Next you need to add the Clicky 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:`clicky-configuration`. @@ -104,7 +104,7 @@ 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. -Here is a table with the most important variables. All variable listed +Here is a table with the most important variables. All variables listed on the `customized tracking`_ documentation page can be set by replacing ``clicky_custom.`` with ``clicky_``. diff --git a/docs/services/crazy_egg.rst b/docs/services/crazy_egg.rst index def4213..a0a9cc7 100644 --- a/docs/services/crazy_egg.rst +++ b/docs/services/crazy_egg.rst @@ -19,7 +19,7 @@ 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 Crazy Egg template tags to your templates. +Next you need to add the Crazy Egg 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:`crazy-egg-configuration`. diff --git a/docs/services/google_analytics.rst b/docs/services/google_analytics.rst index 27ec705..e356340 100644 --- a/docs/services/google_analytics.rst +++ b/docs/services/google_analytics.rst @@ -20,7 +20,7 @@ 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 Google Analytics template tags to your +Next you need to add the Google Analytics 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:`google-analytics-configuration`. diff --git a/docs/services/hubspot.rst b/docs/services/hubspot.rst index bc7014e..5a51c1c 100644 --- a/docs/services/hubspot.rst +++ b/docs/services/hubspot.rst @@ -19,7 +19,7 @@ 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 HubSpot template tags to your templates. This +Next you need to add the HubSpot 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:`hubspot-configuration`. diff --git a/docs/services/kiss_insights.rst b/docs/services/kiss_insights.rst index 47c3f70..bec5923 100644 --- a/docs/services/kiss_insights.rst +++ b/docs/services/kiss_insights.rst @@ -19,7 +19,7 @@ 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 KISSinsights template tags to your templates. +Next you need to add the KISSinsights 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:`kiss-insights-configuration`. diff --git a/docs/services/kiss_metrics.rst b/docs/services/kiss_metrics.rst index 741e38e..c7e3bb0 100644 --- a/docs/services/kiss_metrics.rst +++ b/docs/services/kiss_metrics.rst @@ -20,7 +20,7 @@ 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 KISSmetrics template tags to your templates. +Next you need to add the KISSmetrics 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:`kiss-metrics-configuration`. diff --git a/docs/services/mixpanel.rst b/docs/services/mixpanel.rst index 0240931..4b6bd44 100644 --- a/docs/services/mixpanel.rst +++ b/docs/services/mixpanel.rst @@ -19,7 +19,7 @@ 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 Mixpanel template tags to your templates. This +Next you need to add the Mixpanel 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:`mixpanel-configuration`. diff --git a/docs/services/optimizely.rst b/docs/services/optimizely.rst index 39aa890..e726042 100644 --- a/docs/services/optimizely.rst +++ b/docs/services/optimizely.rst @@ -20,7 +20,7 @@ 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 Optimizely template tags to your templates. +Next you need to add the Optimizely 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:`optimizely-configuration`. diff --git a/docs/services/performable.rst b/docs/services/performable.rst index 87f09ae..43869c3 100644 --- a/docs/services/performable.rst +++ b/docs/services/performable.rst @@ -20,7 +20,7 @@ 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 Performable template tags to your templates. +Next you need to add the Performable 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:`performable-configuration`. @@ -69,8 +69,8 @@ Identifying authenticated users ------------------------------- If your websites identifies visitors, you can pass this information on -to Performable so that you can track individual users. By default, the -username of an authenticated user is passed to Performable +to Performable so that you can track individual users. By default, the +username of an authenticated user is passed to Performable automatically. See :ref:`identifying-visitors`. You can also send the visitor identity yourself by adding either the diff --git a/docs/services/reinvigorate.rst b/docs/services/reinvigorate.rst new file mode 100644 index 0000000..4770d2b --- /dev/null +++ b/docs/services/reinvigorate.rst @@ -0,0 +1,157 @@ +================================ +Reinvigorate -- visitor tracking +================================ + +Reinvigorate_ gives you real-time traffic analysis, visitor activity, +search and referrer information and click heatmaps. A system tray / +system status bar application for your desktop notifies you when +interesting events occur. + +.. _Reinvigorate: http://www.reinvigorate.com/ + + +.. reinvigorate-installation: + +Installation +============ + +To start using the Reinvigorate 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 Reinvigorate 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:`reinvigorate-configuration`. + +The Reinvigorate tracking code is inserted into templates using a +template tag. Load the :mod:`reinvigorate` template tag library and +insert the :ttag:`reinvigorate` 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 somewhere within the HTML body:: + + {% load reinvigorate %} + ... + {% reinvigorate %} + + + + +.. _reinvigorate-configuration: + +Configuration +============= + +Before you can use the Reinvigorate integration, you must first set your +tracking ID. You can also customize the data that Reinvigorate tracks. + + +.. _reinvigorate-tracking-id: + +Setting the tracking ID +----------------------- + +Every website you track with Reinvigorate gets a tracking ID, and the +:ttag:`reinvigorate` tag will include it in the rendered Javascript +code. You can find the tracking ID in the URL of your website report +pages. The URL looks like this: + + \https://report.reinvigorate.net/accounts/XXXXX-XXXXXXXXXX/ + +Here, ``XXXXX-XXXXXXXXXX`` is the tracking ID. Set +:const:`REINVIGORATE_TRACKING_ID` in the project :file:`settings.py` +file:: + + REINVIGORATE_TRACKING_ID = 'XXXXX-XXXXXXXXXX' + +If you do not set a tracking ID, the tracking code will not be rendered. + + +.. _reinvigorate-internal-ips: + +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:`REINVIGORATE_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. + + +.. _reinvigorate-tags: + +Reinvigorate tags +----------------- + +As described in the Reinvigorate *NameTags* and *Snoop* pages, +the data that is tracked by Reinvigorate can be customized by adding +*tags* to the Javascript tracking code. (These should not be confused +with Django template tags.) Using template context variables, you can +let the :ttag:`reinvigorate` template tag pass reinvigorate tags to +automatically. You can set the context variables in your view when your +render a template containing the tracking code:: + + context = RequestContext({'reinvigorate_purchase': True, + 'reinvigorate_comment': 'Got discount'}) + return some_template.render(context) + +If you have tags that are generated on every page, you may want to set +them in a context processor that you add to the +:data:`TEMPLATE_CONTEXT_PROCESSORS` list in :file:`settings.py`:: + + def reinvigorate_tags(request): + try: + return {'name': request.user.username} + except AttributeError: + 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. + +Here is a table with the most important tags. All tags listed on the +Reinvigorate pages can be set by replacing ``re_XXX_tag`` with +``reinvigorate_XXX``. + +========================= ============================================= +Context variable Description +========================= ============================================= +``reinvigorate_name`` The visitor name. +------------------------- --------------------------------------------- +``reinvigorate_context`` Some context information about the visitor, + e.g. an e-mail address. +------------------------- --------------------------------------------- +``reinvigorate_purchase`` A boolean indicating whether the visitor has + just made a purchase. Setting this variable + triggers an event in the Snoop notification + application. +------------------------- --------------------------------------------- +``reinvigorate_new_user`` A boolean indicating whether the visitor has + just registered as a new user. Setting this + variable triggers an event in the Snoop + notification application. +------------------------- --------------------------------------------- +``reinvigorate_comment`` A comment, which is included in a Snoop + event notification. +========================= ============================================= + + +.. _reinvigorate-identify-user: + +Identifying authenticated users +------------------------------- + +If you have not set the ``reinvigorate_name`` context variable +explicitly, the full name of an authenticated user is passed to +Reinvigorate automatically. Similarly, the e-mail address is passed +automatically in the ``context`` tag. See :ref:`identifying-visitors`. + + +---- + +Thanks go to Reinvigorate for their support with the development of this +application.