mirror of
https://github.com/jazzband/django-analytical.git
synced 2026-03-16 22:20:25 +00:00
Simplify analytics tag loading
The general-purpose tag loading code was much too generic and complex, so this commit hard-codes the supported services. Also adds a tutorial to the documentation. This commit is not tested.
This commit is contained in:
parent
a7db456359
commit
3db3bf4b0e
18 changed files with 269 additions and 182 deletions
|
|
@ -10,7 +10,7 @@ Django_ project. See the ``docs`` directory for more information.
|
|||
|
||||
__author__ = "Joost Cassee"
|
||||
__email__ = "joost@cassee.net"
|
||||
__version__ = "0.1.0"
|
||||
__version__ = "0.2.0alpha"
|
||||
__copyright__ = "Copyright (C) 2011 Joost Cassee"
|
||||
__license__ = "MIT License"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
"""
|
||||
Analytical template tags.
|
||||
Analytical template tags and filters.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
|
@ -10,34 +10,46 @@ from django.core.exceptions import ImproperlyConfigured
|
|||
from django.template import Node, TemplateSyntaxError
|
||||
from django.utils.importlib import import_module
|
||||
|
||||
|
||||
DEFAULT_SERVICES = [
|
||||
'analytical.templatetags.chartbeat.service',
|
||||
'analytical.templatetags.clicky.service',
|
||||
'analytical.templatetags.crazy_egg.service',
|
||||
'analytical.templatetags.google_analytics.service',
|
||||
'analytical.templatetags.hubspot.service',
|
||||
'analytical.templatetags.kiss_insights.service',
|
||||
'analytical.templatetags.kiss_metrics.service',
|
||||
'analytical.templatetags.mixpanel.service',
|
||||
'analytical.templatetags.optimizely.service',
|
||||
]
|
||||
LOCATIONS = ['head_top', 'head_bottom', 'body_top', 'body_bottom']
|
||||
from analytical.templatetags import chartbeat, clicky, crazy_egg, \
|
||||
google_analytics, hubspot, kiss_insights, kiss_metrics, mixpanel, \
|
||||
optimizely
|
||||
|
||||
|
||||
_log = logging.getLogger(__name__)
|
||||
TAG_NODES = {
|
||||
'head_top': [
|
||||
chartbeat.ChartbeatTopNode, # Chartbeat should come first
|
||||
kiss_metrics.KissMetricsNode,
|
||||
optimizely.OptimizelyNode,
|
||||
],
|
||||
'head_bottom': [
|
||||
google_analytics.GoogleAnalyticsNode,
|
||||
mixpanel.MixpanelNode,
|
||||
],
|
||||
'body_top': [
|
||||
kiss_insights.KissInsightsNode,
|
||||
],
|
||||
'body_bottom': [
|
||||
clicky.ClickyNode,
|
||||
crazy_egg.CrazyEggNode,
|
||||
hubspot.HubSpotNode,
|
||||
chartbeat.ChartbeatBottomNode, # Chartbeat should come last
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
register = template.Library()
|
||||
|
||||
|
||||
def _location_tag(location):
|
||||
def tag(parser, token):
|
||||
def analytical_tag(parser, token):
|
||||
bits = token.split_contents()
|
||||
if len(bits) > 1:
|
||||
raise TemplateSyntaxError("'%s' tag takes no arguments" % bits[0])
|
||||
return AnalyticalNode(location)
|
||||
return tag
|
||||
return analytical_tag
|
||||
|
||||
for loc in LOCATIONS:
|
||||
for loc in TAG_NODES.keys():
|
||||
register.tag('analytical_%s' % loc, _location_tag(loc))
|
||||
|
||||
|
||||
|
|
@ -50,64 +62,16 @@ class AnalyticalNode(Node):
|
|||
|
||||
|
||||
def _load_template_nodes():
|
||||
try:
|
||||
service_paths = settings.ANALYTICAL_SERVICES
|
||||
autoload = False
|
||||
except AttributeError:
|
||||
service_paths = DEFAULT_SERVICES
|
||||
autoload = True
|
||||
services = _get_services(service_paths)
|
||||
location_nodes = dict((loc, []) for loc in LOCATIONS)
|
||||
for location in LOCATIONS:
|
||||
node_tuples = []
|
||||
for service in services:
|
||||
node_tuple = service.get(location)
|
||||
if node_tuple is not None:
|
||||
if not isinstance(node_tuple, tuple):
|
||||
node_tuple = (node_tuple, None)
|
||||
node_tuples[location].append(node_tuple)
|
||||
location_nodes[location] = _get_nodes(node_tuples, autoload)
|
||||
location_nodes = {}
|
||||
for location, node_classes in TAG_NODES.items():
|
||||
location_nodes[location] = []
|
||||
for node_class in node_classes:
|
||||
try:
|
||||
node = node_class()
|
||||
except ImproperlyConfigured, e:
|
||||
logger.debug("not loading analytical service '%s': %s",
|
||||
node_class.name, e)
|
||||
location_nodes.append(node)
|
||||
return location_nodes
|
||||
|
||||
def _get_nodes(node_tuples, autoload):
|
||||
nodes = []
|
||||
node_sort_key = lambda n: {'first': -1, None: 0, 'last': 1}[n[1]]
|
||||
for node_tuple in sorted(node_tuples, key=node_sort_key):
|
||||
node_cls = node_tuple[0]
|
||||
try:
|
||||
nodes.append(node_cls())
|
||||
except ImproperlyConfigured, e:
|
||||
if autoload:
|
||||
_log.debug("not loading analytical service '%s': %s",
|
||||
node_cls.__module__, e)
|
||||
continue
|
||||
else:
|
||||
raise
|
||||
return nodes
|
||||
|
||||
def _get_services(paths, autoload):
|
||||
services = []
|
||||
for path in paths:
|
||||
mod_name, attr_name = path.rsplit('.', 1)
|
||||
try:
|
||||
mod = import_module(mod_name)
|
||||
except ImportError, e:
|
||||
if autoload:
|
||||
_log.exception(e)
|
||||
continue
|
||||
else:
|
||||
raise
|
||||
try:
|
||||
service = getattr(mod, attr_name)
|
||||
except AttributeError, e:
|
||||
if autoload:
|
||||
_log.debug("not loading analytical service '%s': "
|
||||
"module '%s' does not provide attribute '%s'",
|
||||
path, mod_name, attr_name)
|
||||
continue
|
||||
else:
|
||||
raise
|
||||
services.append(service)
|
||||
return services
|
||||
|
||||
template_nodes = _load_template_nodes()
|
||||
|
|
|
|||
|
|
@ -56,9 +56,11 @@ def chartbeat_top(parser, token):
|
|||
return ChartbeatTopNode()
|
||||
|
||||
class ChartbeatTopNode(Node):
|
||||
name = 'Chartbeat top code'
|
||||
|
||||
def render(self, context):
|
||||
if is_internal_ip(context):
|
||||
return disable_html(INIT_CODE, 'Chartbeat')
|
||||
return disable_html(INIT_CODE, self.name)
|
||||
return INIT_CODE
|
||||
|
||||
|
||||
|
|
@ -77,6 +79,8 @@ def chartbeat_bottom(parser, token):
|
|||
return ChartbeatBottomNode()
|
||||
|
||||
class ChartbeatBottomNode(Node):
|
||||
name = 'Chartbeat bottom code'
|
||||
|
||||
def __init__(self):
|
||||
self.user_id = self.get_required_setting(
|
||||
'CHARTBEAT_USER_ID', USER_ID_RE,
|
||||
|
|
@ -94,11 +98,5 @@ class ChartbeatBottomNode(Node):
|
|||
config['domain'] = domain
|
||||
html = SETUP_CODE % {'config': simplejson.dumps(config)}
|
||||
if is_internal_ip(context):
|
||||
html = disable_html(html, 'Chartbeat')
|
||||
html = disable_html(html, self.name)
|
||||
return html
|
||||
|
||||
|
||||
service = {
|
||||
'head_top': (ChartbeatTopNode, 'first'),
|
||||
'body_bottom': (ChartbeatBottomNode, 'last'),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ def clicky(parser, token):
|
|||
return ClickyNode()
|
||||
|
||||
class ClickyNode(Node):
|
||||
name = 'Clicky'
|
||||
|
||||
def __init__(self):
|
||||
self.site_id = get_required_setting('CLICKY_SITE_ID', SITE_ID_RE,
|
||||
"must be a string containing an eight-digit number")
|
||||
|
|
@ -66,10 +68,5 @@ class ClickyNode(Node):
|
|||
html = TRACKING_CODE % {'site_id': self.site_id,
|
||||
'custom': simplejson.dumps(custom)}
|
||||
if is_internal_ip(context):
|
||||
html = disable_html(html, 'Clicky')
|
||||
html = disable_html(html, self.name)
|
||||
return html
|
||||
|
||||
|
||||
service = {
|
||||
'body_bottom': ClickyNode,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ def crazy_egg(parser, token):
|
|||
return CrazyEggNode()
|
||||
|
||||
class CrazyEggNode(Node):
|
||||
name = 'Crazy Egg'
|
||||
|
||||
def __init__(self):
|
||||
self.account_nr = self.get_required_setting('CRAZY_EGG_ACCOUNT_NUMBER',
|
||||
ACCOUNT_NUMBER_RE,
|
||||
|
|
@ -50,10 +52,5 @@ class CrazyEggNode(Node):
|
|||
html = '%s\n<script type="text/javascript">%s</script>' \
|
||||
% (html, js)
|
||||
if is_internal_ip(context):
|
||||
html = disable_html(html, 'Crazy Egg')
|
||||
html = disable_html(html, self.name)
|
||||
return html
|
||||
|
||||
|
||||
service = {
|
||||
'body_bottom': CrazyEggNode,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,6 +52,8 @@ def google_analytics(parser, token):
|
|||
return GoogleAnalyticsNode()
|
||||
|
||||
class GoogleAnalyticsNode(Node):
|
||||
name = 'Google Analytics'
|
||||
|
||||
def __init__(self):
|
||||
self.property_id = self.get_required_setting(
|
||||
'GOOGLE_ANALYTICS_PROPERTY_ID', PROPERTY_ID_RE,
|
||||
|
|
@ -62,7 +64,7 @@ class GoogleAnalyticsNode(Node):
|
|||
html = SETUP_CODE % {'property_id': self.property_id,
|
||||
'commands': " ".join(commands)}
|
||||
if is_internal_ip(context):
|
||||
html = disable_html(html, 'Google Analytics')
|
||||
html = disable_html(html, self.name)
|
||||
return html
|
||||
|
||||
def _get_custom_var_commands(self, context):
|
||||
|
|
@ -79,8 +81,3 @@ class GoogleAnalyticsNode(Node):
|
|||
scope = SCOPE_PAGE
|
||||
commands.append(CUSTOM_VAR_CODE % locals())
|
||||
return commands
|
||||
|
||||
|
||||
service = {
|
||||
'head_bottom': GoogleAnalyticsNode,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ def hubspot(parser, token):
|
|||
return HubSpotNode()
|
||||
|
||||
class HubSpotNode(Node):
|
||||
name = 'HubSpot'
|
||||
|
||||
def __init__(self):
|
||||
self.site_id = get_required_setting('HUPSPOT_PORTAL_ID',
|
||||
PORTAL_ID_RE, "must be a (string containing a) number")
|
||||
|
|
@ -51,10 +53,5 @@ class HubSpotNode(Node):
|
|||
html = TRACKING_CODE % {'portal_id': self.portal_id,
|
||||
'domain': self.domain}
|
||||
if is_internal_ip(context):
|
||||
html = disable_html(html, 'HubSpot')
|
||||
html = disable_html(html, self.name)
|
||||
return html
|
||||
|
||||
|
||||
service = {
|
||||
'body_bottom': HubSpotNode,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ def kiss_insights(parser, token):
|
|||
return KissInsightsNode()
|
||||
|
||||
class KissInsightsNode(Node):
|
||||
name = 'KISSinsights'
|
||||
|
||||
def __init__(self):
|
||||
self.account_number = self.get_required_setting(
|
||||
'KISS_INSIGHTS_ACCOUNT_NUMBER', ACCOUNT_NUMBER_RE,
|
||||
|
|
@ -59,8 +61,3 @@ class KissInsightsNode(Node):
|
|||
html = SETUP_CODE % {'account_number': self.account_number,
|
||||
'site_code': self.site_code, 'commands': " ".join(commands)}
|
||||
return html
|
||||
|
||||
|
||||
service = {
|
||||
'body_top': KissInsightsNode,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ def kiss_metrics(parser, token):
|
|||
return KissMetricsNode()
|
||||
|
||||
class KissMetricsNode(Node):
|
||||
name = 'KISSmetrics'
|
||||
|
||||
def __init__(self):
|
||||
self.api_key = self.get_required_setting('KISS_METRICS_API_KEY',
|
||||
API_KEY_RE,
|
||||
|
|
@ -72,10 +74,5 @@ class KissMetricsNode(Node):
|
|||
html = TRACKING_CODE % {'api_key': self.api_key,
|
||||
'commands': " ".join(commands)}
|
||||
if is_internal_ip(context):
|
||||
html = disable_html(html, 'Mixpanel')
|
||||
html = disable_html(html, self.name)
|
||||
return html
|
||||
|
||||
|
||||
service = {
|
||||
'head_top': KissMetricsNode,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ def mixpanel(parser, token):
|
|||
return MixpanelNode()
|
||||
|
||||
class MixpanelNode(Node):
|
||||
name = 'Mixpanel'
|
||||
|
||||
def __init__(self):
|
||||
self.token = self.get_required_setting('MIXPANEL_TOKEN',
|
||||
MIXPANEL_TOKEN_RE,
|
||||
|
|
@ -65,10 +67,5 @@ class MixpanelNode(Node):
|
|||
html = TRACKING_CODE % {'token': self.token,
|
||||
'commands': " ".join(commands)}
|
||||
if is_internal_ip(context):
|
||||
html = disable_html(html, 'Mixpanel')
|
||||
html = disable_html(html, self.name)
|
||||
return html
|
||||
|
||||
|
||||
service = {
|
||||
'head_bottom': MixpanelNode,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ def optimizely(parser, token):
|
|||
return OptimizelyNode()
|
||||
|
||||
class OptimizelyNode(Node):
|
||||
name = 'Optimizely'
|
||||
|
||||
def __init__(self):
|
||||
self.account_number = self.get_required_setting(
|
||||
'OPTIMIZELY_ACCOUNT_NUMBER', ACCOUNT_NUMBER_RE,
|
||||
|
|
@ -41,10 +43,5 @@ class OptimizelyNode(Node):
|
|||
def render(self, context):
|
||||
html = SETUP_CODE % {'account_number': self.account_number}
|
||||
if is_internal_ip(context):
|
||||
html = disable_html(html, 'Optimizely')
|
||||
html = disable_html(html, self.name)
|
||||
return html
|
||||
|
||||
|
||||
service = {
|
||||
'head_top': OptimizelyNode,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,24 @@
|
|||
===================
|
||||
History and credits
|
||||
===================
|
||||
|
||||
Changelog
|
||||
---------
|
||||
=========
|
||||
|
||||
0.2.0
|
||||
-----
|
||||
* Added support for the HubSpot service.
|
||||
* Added template tags for individual services.
|
||||
|
||||
0.1.0
|
||||
First project release.
|
||||
-----
|
||||
* First project release.
|
||||
|
||||
Credits
|
||||
-------
|
||||
=======
|
||||
|
||||
django-analytical was written by `Joost Cassee`_. The project source
|
||||
code is hosted generously hosted by GitHub_.
|
||||
code is generously hosted by GitHub_.
|
||||
|
||||
This application was inspired by and uses ideas from Analytical_,
|
||||
Joshua Krall's all-purpose analytics front-end for Rails. The work on
|
||||
|
|
|
|||
|
|
@ -16,20 +16,18 @@ Overview
|
|||
|
||||
If your want to integrating an analytics service into a Django project,
|
||||
you need to add Javascript tracking code to the project templates.
|
||||
Unfortunately, every services has its own specific installation
|
||||
instructions. Furthermore, you need to specify your unique identifiers
|
||||
which would end up in templates. This application hides the details of
|
||||
the different analytics services behind a generic interface. It is
|
||||
designed to make the common case easy while allowing advanced users to
|
||||
customize tracking.
|
||||
Of course, every service has its own specific installation instructions.
|
||||
Furthermore, you need to include your unique identifiers, which then end
|
||||
up in the templates. This application hides the details of the
|
||||
different analytics services behind a generic interface, and keeps
|
||||
personal information and configuration out of the templates. Its goal
|
||||
is to make basic usage set-up very simple, while allowing advanced users
|
||||
to customize tracking. Each service is set-up as recommended by the
|
||||
services themselves, using an asynchronous version of the Javascript
|
||||
code if possible.
|
||||
|
||||
The application provides four generic template tags that are added to
|
||||
the top and bottom of the head and body section of the base template.
|
||||
Configured services will be enabled automatically by adding Javascript
|
||||
code at these locations. The installation will follow the
|
||||
recommendations from the analytics services, using an asynchronous
|
||||
version of the code if possible. See :doc:`services/index` for detailed
|
||||
information about each individual analytics service.
|
||||
To get a feel of how django-analytics works, first check out the
|
||||
:doc:`tutorial`.
|
||||
|
||||
|
||||
Contents
|
||||
|
|
@ -38,7 +36,27 @@ Contents
|
|||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
tutorial
|
||||
install
|
||||
services/index
|
||||
services
|
||||
settings
|
||||
history
|
||||
|
||||
|
||||
Helping out
|
||||
===========
|
||||
|
||||
If you want to help out with development of django-analytical, by
|
||||
posting detailed bug reports, suggesting new features or other analytics
|
||||
services to support, or doing some development work yourself, please use
|
||||
the `GitHub project page`_:
|
||||
|
||||
* Use the `issue tracker`_ to discuss bugs and features.
|
||||
* If you want to do the work yourself, great! Clone the repository, make
|
||||
changes and send a pull request. Please create a new issue first so
|
||||
that we can discuss it, and keep people from stepping on each others
|
||||
toes.
|
||||
* Of course, you can always send ideas and patches to joost@cassee.net.
|
||||
|
||||
.. _`GitHub project page`: http://github.com/jcassee/django-analytical
|
||||
.. _`issue tracker`: http://github.com/jcassee/django-analytical/issues
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ Installation and configuration
|
|||
|
||||
Integration of your analytics service is very simple. There are four
|
||||
steps: installing the package, adding it to the list of installed Django
|
||||
applications, adding the template tags to your base template, and adding
|
||||
the identifiers for the services you use to the project settings.
|
||||
applications, adding the template tags to your base template, and
|
||||
configuring the services you use in the project settings.
|
||||
|
||||
#. `Installing the Python package`_
|
||||
#. `Installing the Django application`_
|
||||
|
|
@ -13,6 +13,8 @@ the identifiers for the services you use to the project settings.
|
|||
#. `Configuring the application`_
|
||||
|
||||
|
||||
.. _installing-the-package:
|
||||
|
||||
Installing the Python package
|
||||
=============================
|
||||
|
||||
|
|
@ -31,16 +33,18 @@ get the development code::
|
|||
.. _PyPI: http://pypi.python.org/pypi/django-analytical/
|
||||
.. _GitHub: http://github.com/jcassee/django-analytical
|
||||
|
||||
Then install by running the setup script::
|
||||
Then install the package by running the setup script::
|
||||
|
||||
$ cd django-analytical
|
||||
$ python setup.py install
|
||||
|
||||
|
||||
.. _installing-the-application:
|
||||
|
||||
Installing the Django application
|
||||
=================================
|
||||
|
||||
After you install django-analytical, add the ``analytical`` Django
|
||||
After you installed django-analytical, add the ``analytical`` Django
|
||||
application to the list of installed applications in the ``settings.py``
|
||||
file of your project::
|
||||
|
||||
|
|
@ -51,14 +55,16 @@ file of your project::
|
|||
]
|
||||
|
||||
|
||||
.. _adding-the-template-tags:
|
||||
|
||||
Adding the template tags to the base template
|
||||
=============================================
|
||||
|
||||
Because every analytics service has uses own specific Javascript code
|
||||
that should be added to the top or bottom of either the head or body
|
||||
of every HTML page, the django-analytical provides four general-purpose
|
||||
tags that will render the code needed for the services you are using.
|
||||
Your base template should look like this::
|
||||
Because every analytics service uses own specific Javascript code that
|
||||
should be added to the top or bottom of either the head or body of the
|
||||
HTML page, django-analytical provides four general-purpose template tags
|
||||
that will render the code needed for the services you are using. Your
|
||||
base template should look like this::
|
||||
|
||||
{% load analytical %}
|
||||
<!DOCTYPE ... >
|
||||
|
|
@ -79,6 +85,12 @@ Your base template should look like this::
|
|||
</body>
|
||||
</html>
|
||||
|
||||
Instead of using the general-purpose tags, you can also just use the
|
||||
tags for the analytics service(s) you are using. See :ref:`services`
|
||||
for documentation on using individual services.
|
||||
|
||||
|
||||
.. _configuration:
|
||||
|
||||
Configuring the application
|
||||
===========================
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
.. _services:
|
||||
|
||||
========
|
||||
Services
|
||||
========
|
||||
|
||||
|
|
@ -7,4 +10,4 @@ A number of analytics services is supported.
|
|||
:maxdepth: 1
|
||||
:glob:
|
||||
|
||||
*
|
||||
services/*
|
||||
|
|
@ -99,11 +99,12 @@ be computed from the HTTP request, you can also set them in a context
|
|||
processor that you add to the :data:`TEMPLATE_CONTEXT_PROCESSORS` list
|
||||
in :file:`settings.py`::
|
||||
|
||||
def segment_on_ip_proto(request):
|
||||
addr = request.META.get('HTTP_X_FORWARDED_FOR',
|
||||
request.META.get('REMOTE_ADDR', ''))
|
||||
proto = 'ipv6' if ':' in addr else 'ipv4'
|
||||
return {'crazy_egg_var3': proto}
|
||||
def track_admin_role(request):
|
||||
if request.user.is_staff():
|
||||
role = 'staff'
|
||||
else:
|
||||
role = 'visitor'
|
||||
return {'crazy_egg_var3': role}
|
||||
|
||||
Just remember that if you set the same context variable in the
|
||||
:class:`~django.template.context.RequestContext` constructor and in a
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
.. _identifying-visitors:
|
||||
|
||||
========
|
||||
Settings
|
||||
========
|
||||
|
|
@ -6,6 +8,7 @@ Here's a full list of all available settings, in alphabetical order, and
|
|||
their default values.
|
||||
|
||||
|
||||
|
||||
.. data:: ANALYTICAL_AUTO_IDENTIFY
|
||||
|
||||
Default: ``True``
|
||||
|
|
@ -50,20 +53,3 @@ their default values.
|
|||
``'django.core.context_processors.request'`` to the list of
|
||||
context processors in the ``TEMPLATE_CONTEXT_PROCESSORS``
|
||||
setting.
|
||||
|
||||
|
||||
.. data:: ANALYTICAL_SERVICES
|
||||
|
||||
Default: all included services that have been configured correctly
|
||||
|
||||
A list or tuple of analytics services to use. If this setting is
|
||||
used and one of the services is not configured correctly, an
|
||||
:exc:`ImproperlyConfigured` exception is raised when the services
|
||||
are first loaded.
|
||||
|
||||
Example::
|
||||
|
||||
ANALYTICAL_SERVICES = [
|
||||
'analytical.services.crazy_egg.CrazyEggService',
|
||||
'analytical.services.google_analytics.GoogleAnalyticsService',
|
||||
]
|
||||
|
|
|
|||
122
docs/tutorial.rst
Normal file
122
docs/tutorial.rst
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
.. _tutorial:
|
||||
|
||||
========
|
||||
Tutorial
|
||||
========
|
||||
|
||||
In this tutorial you will learn how to install and configure
|
||||
django-analytical for basic tracking. Suppose you want to use two
|
||||
different analytics services on your Django website:
|
||||
|
||||
* :doc:`Clicky <services/clicky>` for detailed traffic analysis
|
||||
* :doc:`Crazy Egg <services/crazy_egg>` to see where visitors click on your pages
|
||||
|
||||
At the end of this tutorial, the project will track visitors on both
|
||||
Clicky and Crazy Egg, identify authenticated users and add extra
|
||||
tracking data to segment mouse clicks on Crazy Egg based on whether
|
||||
visitors are using IPv4 or IPv6.
|
||||
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
To get started with django-analytical, the package must first be
|
||||
installed. You can download and install the latest stable package from
|
||||
the Python Package Index automatically by using ``easy_install``::
|
||||
|
||||
$ easy_install django-analytical
|
||||
|
||||
For more ways to install django-analytical, see
|
||||
:ref:`installing-the-package`.
|
||||
|
||||
After you install django-analytical, you need to add it to the list of
|
||||
installed applications in the ``settings.py`` file of your project::
|
||||
|
||||
INSTALLED_APPS = [
|
||||
...
|
||||
'analytical',
|
||||
...
|
||||
]
|
||||
|
||||
Now add the general-purpose django-analytical template tags to your base
|
||||
template::
|
||||
|
||||
{% load analytical %}
|
||||
<!DOCTYPE ... >
|
||||
<html>
|
||||
<head>
|
||||
{% analytical_head_top %}
|
||||
|
||||
...
|
||||
|
||||
{% analytical_head_bottom %}
|
||||
</head>
|
||||
<body>
|
||||
{% analytical_body_top %}
|
||||
|
||||
...
|
||||
|
||||
{% analytical_body_bottom %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Finally, you need to configure the Clicky Site ID and the Crazy Egg
|
||||
account number. Add the following to your project :file:`settings.py`
|
||||
file::
|
||||
|
||||
CLICKY_SITE_ID = 'xxxxxxxx'
|
||||
CRAZY_EGG_ACCOUNT_NUMBER = 'xxxxxxxx'
|
||||
|
||||
The analytics services are now installed. If you run Django with these
|
||||
changes, both Clicky and Crazy Egg will be tracking your visitors.
|
||||
|
||||
|
||||
Identifying authenticated users
|
||||
===============================
|
||||
|
||||
Some analytics services, such as Clicky, can identify and track
|
||||
individual visitors. If django-analytical tags detect that the current
|
||||
user is authenticated, they will automatically include code to send the
|
||||
username to services that support this feature. This only works if the
|
||||
template context has the current user in the ``user`` or
|
||||
``request.user`` context variable. If you use a
|
||||
:class:`~django.template.RequestContext` to render templates (which is
|
||||
recommended anyway) and have the
|
||||
:class:`django.contrib.auth.context_processors.auth` context processor
|
||||
in the :data:`TEMPLATE_CONTEXT_PROCESSORS` setting (which is default),
|
||||
then this identification works without having to make any changes.
|
||||
|
||||
For more detailed information on automatic identification, and how to
|
||||
disable or override it, see :ref:`identifying-visitors`.
|
||||
|
||||
|
||||
Adding custom tracking data
|
||||
===========================
|
||||
|
||||
You want to track whether visitors are using IPv4 or IPv6. (Maybe you
|
||||
are running a website on the IPv6 transition?) This means including
|
||||
the visitor IP protocol version as custom data with the tracking code.
|
||||
The easiest way to do this is by using a context processor::
|
||||
|
||||
def track_ip_proto(request):
|
||||
addr = request.META.get('HTTP_X_FORWARDED_FOR', '')
|
||||
if not addr:
|
||||
addr = request.META.get('REMOTE_ADDR', '')
|
||||
if ':' in addr:
|
||||
proto = 'ipv6'
|
||||
else:
|
||||
proto = 'ipv4' # assume IPv4 if no information
|
||||
return {'crazy_egg_var1': proto}
|
||||
|
||||
Use a :class:`~django.template.RequestContext` when rendering templates
|
||||
and add the ``'track_ip_proto'`` to :data:`TEMPLATE_CONTEXT_PROCESSORS`.
|
||||
In Crazy Egg, you can now select *User Var1* in the overlay or confetti
|
||||
views to see whether visitors using IPv4 behave differently from those
|
||||
using IPv6.
|
||||
|
||||
|
||||
----
|
||||
|
||||
This concludes the tutorial. For information about setting up,
|
||||
configuring and customizing the different analytics services, see
|
||||
:ref:`services`.
|
||||
Loading…
Reference in a new issue