Merge remote-tracking branch 'jcassee/master' into gaflags_fix

This commit is contained in:
Steve Schwarz 2016-05-26 21:39:19 -05:00
commit 2f22762010
26 changed files with 605 additions and 451 deletions

View file

@ -1,12 +1,14 @@
language: python
python: "3.5"
install:
# continue to support Python 3.2 (see issue #84)
- pip install "virtualenv<14.0.0"
- pip install coveralls tox
script:
- tox
# NOTE: To generate (update) the env list run
# $ tox -l | sort | xargs -I ITEM echo " - TOXENV="ITEM
env:
# NOTE: To generate (update) the env list run
# $ tox -l | sort | xargs -I ITEM echo " - TOXENV="ITEM
- TOXENV=py27-django17
- TOXENV=py27-django18
- TOXENV=py27-django19

View file

@ -3,7 +3,8 @@ contributions from `Eric Davis`_, `Paul Oswald`_, `Uros Trebec`_,
`Steven Skoczen`_, `Piet Delport`_, `Sandra Mau`_, `Simon Ye`_,
`Tinnet Coronam`_, `Philippe O. Wagner`_, `Max Arnold`_ , `Martín
Gaitán`_, `Craig Bruce`_, `Peter Bittner`_, `Scott Adams`_, `Eric Amador`_,
`Alexandre Pocquet`_, `Brad Pitcher`_, `Hugo Osvaldo Barrera`_ and others.
`Alexandre Pocquet`_, `Brad Pitcher`_, `Hugo Osvaldo Barrera`_,
`Nikolay Korotkiy`_, `Steve Schwarz`_, `Aleck Landgraf`_ and others.
Included Javascript code snippets for integration of the analytics
services were written by the respective service providers.
@ -33,6 +34,9 @@ The work on Intercom was made possible by `GreenKahuna`_.
.. _`Alexandre Pocquet`: https://github.com/apocquet
.. _`Brad Pitcher`: https://github.com/brad
.. _`Hugo Osvaldo Barrera`: https://github.com/hobarrera
.. _`Nikolay Korotkiy`: https://github.com/sikmir
.. _`Steve Schwarz`: https://github.com/saschwarz
.. _`Aleck Landgraf`: https://github.com/alecklandgraf
.. _`Analytical`: https://github.com/jkrall/analytical
.. _`Bateau Knowledge`: http://www.bateauknowledge.nl/
.. _`GreenKahuna`: http://www.greenkahuna.com/

View file

@ -1,3 +1,14 @@
Version 2.2.0
-------------
* Update Woopra JavaScript snippet (Aleck Landgraf)
Version 2.1.0
-------------
* Support Rating\@mail.ru (Nikolay Korotkiy)
* Support Yandex.Metrica (Nikolay Korotkiy)
* Add support for extra Google Analytics variables (Steve Schwarz)
* Remove support for Reinvigorate (service shut down)
Version 2.0.0
-------------
* Support Django 1.9, drop support for Django < 1.7 (Hugo Osvaldo Barrera)

View file

@ -1,7 +1,7 @@
django-analytical |latest-version|
==================================
|travis-ci| |coveralls| |health| |downloads| |license| |gitter|
|travis-ci| |coveralls| |health| |python-support| |downloads| |license| |gitter|
The django-analytical application integrates analytics services into a
Django_ project.
@ -35,6 +35,9 @@ an asynchronous version of the Javascript code if possible.
.. |health| image:: https://landscape.io/github/jcassee/django-analytical/master/landscape.svg?style=flat
:target: https://landscape.io/github/jcassee/django-analytical/master
:alt: Code health
.. |python-support| image:: https://img.shields.io/pypi/pyversions/django-analytical.svg
:target: https://pypi.python.org/pypi/django-analytical
:alt: Python versions
.. |downloads| image:: https://img.shields.io/pypi/dm/django-analytical.svg
:alt: Monthly downloads from PyPI
:target: https://pypi.python.org/pypi/django-analytical
@ -65,11 +68,12 @@ Currently Supported Services
* `Optimizely`_ A/B testing
* `Performable`_ web analytics and landing pages
* `Piwik`_ open source web analytics
* `Reinvigorate`_ visitor tracking
* `Rating\@Mail.ru`_ web analytics
* `SnapEngage`_ live chat
* `Spring Metrics`_ conversion tracking
* `UserVoice`_ user feedback and helpdesk
* `Woopra`_ web analytics
* `Yandex.Metrica`_ web analytics
.. _`Chartbeat`: http://www.chartbeat.com/
.. _`Clickmap`: http://getclickmap.com/
@ -87,11 +91,12 @@ Currently Supported Services
.. _`Optimizely`: http://www.optimizely.com/
.. _`Performable`: http://www.performable.com/
.. _`Piwik`: http://www.piwik.org/
.. _`Reinvigorate`: http://www.reinvigorate.net/
.. _`Rating\@Mail.ru`: http://top.mail.ru/
.. _`SnapEngage`: http://www.snapengage.com/
.. _`Spring Metrics`: http://www.springmetrics.com/
.. _`UserVoice`: http://www.uservoice.com/
.. _`Woopra`: http://www.woopra.com/
.. _`Yandex.Metrica`: http://metrica.yandex.com
Documentation and Support
-------------------------

View file

@ -10,6 +10,6 @@ Django_ project. See the ``docs`` directory for more information.
__author__ = "Joost Cassee"
__email__ = "joost@cassee.net"
__version__ = "2.0.0"
__version__ = "2.2.0"
__copyright__ = "Copyright (C) 2011-2016 Joost Cassee and others"
__license__ = "MIT License"

View file

@ -8,10 +8,8 @@ import logging
from django import template
from django.template import Node, TemplateSyntaxError
try:
from importlib import import_module
except ImportError: # Python 2.6
from django.utils.importlib import import_module
from importlib import import_module
from analytical.utils import AnalyticalException
@ -34,11 +32,12 @@ TAG_MODULES = [
'analytical.optimizely',
'analytical.performable',
'analytical.piwik',
'analytical.reinvigorate',
'analytical.rating_mailru',
'analytical.snapengage',
'analytical.spring_metrics',
'analytical.uservoice',
'analytical.woopra',
'analytical.yandex_metrica',
]
logger = logging.getLogger(__name__)

View file

@ -12,7 +12,10 @@ from analytical.utils import is_internal_ip, disable_html, get_required_setting
ACCOUNT_NUMBER_RE = re.compile(r'^\d+$')
SETUP_CODE = """<script type="text/javascript" src="//dnn506yrbagrg.cloudfront.net/pages/scripts/%(account_nr_1)s/%(account_nr_2)s.js"></script>"""
SETUP_CODE = '<script type="text/javascript" src="{placeholder_url}">' \
'</script>'.\
format(placeholder_url='//dnn506yrbagrg.cloudfront.net/pages/scripts/'
'%(account_nr_1)s/%(account_nr_2)s.js')
USERVAR_CODE = "CE2.set(%(varnr)d, '%(value)s');"
@ -36,19 +39,25 @@ def crazy_egg(parser, token):
class CrazyEggNode(Node):
def __init__(self):
self.account_nr = get_required_setting('CRAZY_EGG_ACCOUNT_NUMBER',
ACCOUNT_NUMBER_RE, "must be (a string containing) a number")
self.account_nr = get_required_setting(
'CRAZY_EGG_ACCOUNT_NUMBER',
ACCOUNT_NUMBER_RE, "must be (a string containing) a number"
)
def render(self, context):
html = SETUP_CODE % {'account_nr_1': self.account_nr[:4],
'account_nr_2': self.account_nr[4:]}
html = SETUP_CODE % {
'account_nr_1': self.account_nr[:4],
'account_nr_2': self.account_nr[4:],
}
values = (context.get('crazy_egg_var%d' % i) for i in range(1, 6))
vars = [(i, v) for i, v in enumerate(values, 1) if v is not None]
if vars:
js = " ".join(USERVAR_CODE % {'varnr': varnr, 'value': value}
for (varnr, value) in vars)
html = '%s\n<script type="text/javascript">%s</script>' \
% (html, js)
params = [(i, v) for i, v in enumerate(values, 1) if v is not None]
if params:
js = " ".join(USERVAR_CODE % {
'varnr': varnr,
'value': value,
} for (varnr, value) in params)
html = '%s\n' \
'<script type="text/javascript">%s</script>' % (html, js)
if is_internal_ip(context, 'CRAZY_EGG'):
html = disable_html(html, 'Crazy Egg')
return html

View file

@ -10,17 +10,13 @@ import re
from django.conf import settings
from django.template import Library, Node, TemplateSyntaxError
from analytical.utils import is_internal_ip, disable_html, \
get_required_setting, get_domain, AnalyticalException
def enumerate(sequence, start=0):
"""Copy of the Python 2.6 `enumerate` builtin for compatibility."""
n = start
for elem in sequence:
yield n, elem
n += 1
from analytical.utils import (
AnalyticalException,
disable_html,
get_domain,
get_required_setting,
is_internal_ip,
)
TRACK_SINGLE_DOMAIN = 1
TRACK_MULTIPLE_SUBDOMAINS = 2
@ -50,7 +46,7 @@ DOMAIN_CODE = "_gaq.push(['_setDomainName', '%s']);"
NO_ALLOW_HASH_CODE = "_gaq.push(['_setAllowHash', false]);"
ALLOW_LINKER_CODE = "_gaq.push(['_setAllowLinker', true]);"
CUSTOM_VAR_CODE = "_gaq.push(['_setCustomVar', %(index)s, '%(name)s', " \
"'%(value)s', %(scope)s]);"
"'%(value)s', %(scope)s]);"
SITE_SPEED_CODE = "_gaq.push(['_trackPageLoadTime']);"
ANONYMIZE_IP_CODE = "_gaq.push (['_gat._anonymizeIp']);"
SAMPLE_RATE_CODE = "_gaq.push (['_gat._setSampleRate', '%s']);"
@ -84,8 +80,8 @@ def google_analytics(parser, token):
class GoogleAnalyticsNode(Node):
def __init__(self):
self.property_id = get_required_setting(
'GOOGLE_ANALYTICS_PROPERTY_ID', PROPERTY_ID_RE,
"must be a string looking like 'UA-XXXXXX-Y'")
'GOOGLE_ANALYTICS_PROPERTY_ID', PROPERTY_ID_RE,
"must be a string looking like 'UA-XXXXXX-Y'")
def render(self, context):
commands = self._get_domain_commands(context)
@ -106,14 +102,15 @@ class GoogleAnalyticsNode(Node):
def _get_domain_commands(self, context):
commands = []
tracking_type = getattr(settings, 'GOOGLE_ANALYTICS_TRACKING_STYLE',
TRACK_SINGLE_DOMAIN)
TRACK_SINGLE_DOMAIN)
if tracking_type == TRACK_SINGLE_DOMAIN:
pass
else:
domain = get_domain(context, 'google_analytics')
if domain is None:
raise AnalyticalException("tracking multiple domains with"
" Google Analytics requires a domain name")
raise AnalyticalException(
"tracking multiple domains with Google Analytics"
" requires a domain name")
commands.append(DOMAIN_CODE % domain)
commands.append(NO_ALLOW_HASH_CODE)
if tracking_type == TRACK_MULTIPLE_DOMAINS:
@ -121,18 +118,24 @@ class GoogleAnalyticsNode(Node):
return commands
def _get_custom_var_commands(self, context):
values = (context.get('google_analytics_var%s' % i)
for i in range(1, 6))
vars = [(i, v) for i, v in enumerate(values, 1) if v is not None]
values = (
context.get('google_analytics_var%s' % i) for i in range(1, 6)
)
params = [(i, v) for i, v in enumerate(values, 1) if v is not None]
commands = []
for index, var in vars:
for index, var in params:
name = var[0]
value = var[1]
try:
scope = var[2]
except IndexError:
scope = SCOPE_PAGE
commands.append(CUSTOM_VAR_CODE % locals())
commands.append(CUSTOM_VAR_CODE % {
'index': index,
'name': name,
'value': value,
'scope': scope,
})
return commands
def _get_other_commands(self, context):

View file

@ -6,10 +6,9 @@ from __future__ import absolute_import
import re
from django.conf import settings
from django.template import Library, Node, TemplateSyntaxError
from analytical.utils import get_identity, get_user_from_context, \
from analytical.utils import get_identity, \
is_internal_ip, disable_html, get_required_setting
@ -51,7 +50,8 @@ def gosquared(parser, token):
class GoSquaredNode(Node):
def __init__(self):
self.site_token = get_required_setting('GOSQUARED_SITE_TOKEN', TOKEN_RE,
self.site_token = get_required_setting(
'GOSQUARED_SITE_TOKEN', TOKEN_RE,
"must be a string looking like XXX-XXXXXX-X")
def render(self, context):

View file

@ -9,8 +9,8 @@ import re
from django.template import Library, Node, TemplateSyntaxError
from analytical.utils import disable_html, get_required_setting, is_internal_ip,\
get_user_from_context, get_identity
from analytical.utils import disable_html, get_required_setting, \
is_internal_ip, get_user_from_context, get_identity
APP_ID_RE = re.compile(r'[\da-f]+$')
TRACKING_CODE = """
@ -51,33 +51,37 @@ class IntercomNode(Node):
return name
def _get_custom_attrs(self, context):
vars = {}
params = {}
for dict_ in context:
for var, val in dict_.items():
if var.startswith('intercom_'):
vars[var[9:]] = val
params[var[9:]] = val
user = get_user_from_context(context)
if user is not None and user.is_authenticated():
if 'name' not in vars:
vars['name'] = get_identity(context, 'intercom', self._identify, user)
if 'email' not in vars and user.email:
vars['email'] = user.email
if 'name' not in params:
params['name'] = get_identity(
context, 'intercom', self._identify, user)
if 'email' not in params and user.email:
params['email'] = user.email
vars['created_at'] = int(time.mktime(user.date_joined.timetuple()))
params['created_at'] = int(time.mktime(
user.date_joined.timetuple()))
else:
vars['created_at'] = None
params['created_at'] = None
return vars
return params
def render(self, context):
html = ""
user = get_user_from_context(context)
vars = self._get_custom_attrs(context)
vars["app_id"] = self.app_id
html = TRACKING_CODE % {"settings_json": json.dumps(vars, sort_keys=True)}
params = self._get_custom_attrs(context)
params["app_id"] = self.app_id
html = TRACKING_CODE % {
"settings_json": json.dumps(params, sort_keys=True)
}
if is_internal_ip(context, 'INTERCOM') or not user or not user.is_authenticated():
if is_internal_ip(context, 'INTERCOM') \
or not user or not user.is_authenticated():
# Intercom is disabled for non-logged in users.
html = disable_html(html, 'Intercom')
return html

View file

@ -0,0 +1,71 @@
"""
Rating@Mail.ru template tags and filters.
"""
from __future__ import absolute_import
import json
import re
from django.conf import settings
from django.template import Library, Node, TemplateSyntaxError
from analytical.utils import is_internal_ip, disable_html, \
get_required_setting
COUNTER_ID_RE = re.compile(r'^\d{7}$')
COUNTER_CODE = """
<script type="text/javascript">
var _tmr = window._tmr || (window._tmr = []);
_tmr.push({id: "%(counter_id)s", type: "pageView", start: (new Date()).getTime()});
(function (d, w, id) {
if (d.getElementById(id)) return;
var ts = d.createElement("script"); ts.type = "text/javascript"; ts.async = true; ts.id = id;
ts.src = (d.location.protocol == "https:" ? "https:" : "http:") + "//top-fwz1.mail.ru/js/code.js";
var f = function () {var s = d.getElementsByTagName("script")[0]; s.parentNode.insertBefore(ts, s);};
if (w.opera == "[object Opera]") { d.addEventListener("DOMContentLoaded", f, false); } else { f(); }
})(document, window, "topmailru-code");
</script>
<noscript><div style="position:absolute;left:-10000px;">
<img src="//top-fwz1.mail.ru/counter?id=%(counter_id)s;js=na" style="border:0;" height="1" width="1" alt="Rating@Mail.ru" />
</div></noscript>
"""
register = Library()
@register.tag
def rating_mailru(parser, token):
"""
Rating@Mail.ru counter template tag.
Renders Javascript code to track page visits. You must supply
your website counter ID (as a string) in the
``RATING_MAILRU_COUNTER_ID`` setting.
"""
bits = token.split_contents()
if len(bits) > 1:
raise TemplateSyntaxError("'%s' takes no arguments" % bits[0])
return RatingMailruNode()
class RatingMailruNode(Node):
def __init__(self):
self.counter_id = get_required_setting(
'RATING_MAILRU_COUNTER_ID', COUNTER_ID_RE,
"must be (a string containing) a number'")
def render(self, context):
html = COUNTER_CODE % {
'counter_id': self.counter_id,
}
if is_internal_ip(context, 'RATING_MAILRU_METRICA'):
html = disable_html(html, 'Rating@Mail.ru')
return html
def contribute_to_analytical(add_node):
RatingMailruNode() # ensure properly configured
add_node('head_bottom', RatingMailruNode)

View file

@ -1,81 +0,0 @@
"""
Reinvigorate template tags and filters.
"""
from __future__ import absolute_import
import json
import re
from django.template import Library, Node, TemplateSyntaxError
from analytical.utils import get_identity, is_internal_ip, disable_html, \
get_required_setting
TRACKING_ID_RE = re.compile(r'^[\w\d]+-[\w\d]+$')
TRACKING_CODE = """
<script type="text/javascript">
document.write(unescape("%%3Cscript src='" + (("https:" == document.location.protocol) ? "https://ssl-" : "http://") + "include.reinvigorate.net/re_.js' type='text/javascript'%%3E%%3C/script%%3E"));
</script>
<script type="text/javascript">
try {
%(tags)s
reinvigorate.track("%(tracking_id)s");
} catch(err) {}
</script>
"""
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, json.dumps(value, sort_keys=True))
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)

View file

@ -52,8 +52,9 @@ def spring_metrics(parser, token):
class SpringMetricsNode(Node):
def __init__(self):
self.tracking_id = get_required_setting('SPRING_METRICS_TRACKING_ID',
TRACKING_ID_RE, "must be a hexadecimal string")
self.tracking_id = get_required_setting(
'SPRING_METRICS_TRACKING_ID',
TRACKING_ID_RE, "must be a hexadecimal string")
def render(self, context):
custom = {}
@ -63,23 +64,25 @@ class SpringMetricsNode(Node):
custom[var[15:]] = val
if 'email' not in custom:
identity = get_identity(context, 'spring_metrics',
lambda u: u.email)
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)}
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):
def _generate_custom_javascript(self, params):
commands = []
convert = vars.pop('convert', None)
convert = params.pop('convert', None)
if convert is not None:
commands.append("_springMetq.push(['convert', '%s'])" % convert)
commands.extend("_springMetq.push(['setdata', {'%s': '%s'}]);"
% (var, val) for var, val in vars.items())
% (var, val) for var, val in params.items())
return " ".join(commands)

View file

@ -10,27 +10,26 @@ import re
from django.conf import settings
from django.template import Library, Node, TemplateSyntaxError
from analytical.utils import get_identity, get_user_from_context, \
is_internal_ip, disable_html, get_required_setting
from analytical.utils import (
disable_html,
get_identity,
get_required_setting,
get_user_from_context,
is_internal_ip,
)
DOMAIN_RE = re.compile(r'^\S+$')
TRACKING_CODE = """
<script type="text/javascript">
var woo_settings = %(settings)s;
var woo_visitor = %(visitor)s;
(function(){
var wsc=document.createElement('script');
wsc.type='text/javascript';
wsc.src=document.location.protocol+'//static.woopra.com/js/woopra.js';
wsc.async=true;
var ssc = document.getElementsByTagName('script')[0];
ssc.parentNode.insertBefore(wsc, ssc);
})();
!function(){var a,b,c,d=window,e=document,f=arguments,g="script",h=["config","track","trackForm","trackClick","identify","visit","push","call"],i=function(){var a,b=this,c=function(a){b[a]=function(){return b._e.push([a].concat(Array.prototype.slice.call(arguments,0))),b}};for(b._e=[],a=0;a<h.length;a++)c(h[a])};for(d.__woo=d.__woo||{},a=0;a<f.length;a++)d.__woo[f[a]]=d[f[a]]=d[f[a]]||new i;b=e.createElement(g),b.async=1,b.src="//static.woopra.com/js/w.js",c=e.getElementsByTagName(g)[0],c.parentNode.insertBefore(b,c)}("woopra");
woopra.config(woo_settings);
woopra.identify(woo_visitor);
woopra.track();
</script>
"""
register = Library()
@ -50,8 +49,9 @@ def woopra(parser, token):
class WoopraNode(Node):
def __init__(self):
self.domain = get_required_setting('WOOPRA_DOMAIN', DOMAIN_RE,
"must be a domain name")
self.domain = get_required_setting(
'WOOPRA_DOMAIN', DOMAIN_RE,
"must be a domain name")
def render(self, context):
settings = self._get_settings(context)
@ -66,27 +66,27 @@ class WoopraNode(Node):
return html
def _get_settings(self, context):
vars = {'domain': self.domain}
variables = {'domain': self.domain}
try:
vars['idle_timeout'] = str(settings.WOOPRA_IDLE_TIMEOUT)
variables['idle_timeout'] = str(settings.WOOPRA_IDLE_TIMEOUT)
except AttributeError:
pass
return vars
return variables
def _get_visitor(self, context):
vars = {}
params = {}
for dict_ in context:
for var, val in dict_.items():
if var.startswith('woopra_'):
vars[var[7:]] = val
if 'name' not in vars and 'email' not in vars:
params[var[7:]] = val
if 'name' not in params and 'email' not in params:
user = get_user_from_context(context)
if user is not None and user.is_authenticated():
vars['name'] = get_identity(context, 'woopra',
self._identify, user)
params['name'] = get_identity(
context, 'woopra', self._identify, user)
if user.email:
vars['email'] = user.email
return vars
params['email'] = user.email
return params
def _identify(self, user):
name = user.get_full_name()

View file

@ -0,0 +1,93 @@
"""
Yandex.Metrica template tags and filters.
"""
from __future__ import absolute_import
import json
import re
from django.conf import settings
from django.template import Library, Node, TemplateSyntaxError
from analytical.utils import is_internal_ip, disable_html, \
get_required_setting
COUNTER_ID_RE = re.compile(r'^\d{8}$')
COUNTER_CODE = """
<script type="text/javascript">
(function (d, w, c) {
(w[c] = w[c] || []).push(function() {
try {
w.yaCounter%(counter_id)s = new Ya.Metrika(%(options)s);
} catch(e) { }
});
var n = d.getElementsByTagName("script")[0],
s = d.createElement("script"),
f = function () { n.parentNode.insertBefore(s, n); };
s.type = "text/javascript";
s.async = true;
s.src = "https://mc.yandex.ru/metrika/watch.js";
if (w.opera == "[object Opera]") {
d.addEventListener("DOMContentLoaded", f, false);
} else { f(); }
})(document, window, "yandex_metrika_callbacks");
</script>
<noscript><div><img src="https://mc.yandex.ru/watch/%(counter_id)s" style="position:absolute; left:-9999px;" alt="" /></div></noscript>
"""
register = Library()
@register.tag
def yandex_metrica(parser, token):
"""
Yandex.Metrica counter template tag.
Renders Javascript code to track page visits. You must supply
your website counter ID (as a string) in the
``YANDEX_METRICA_COUNTER_ID`` setting.
"""
bits = token.split_contents()
if len(bits) > 1:
raise TemplateSyntaxError("'%s' takes no arguments" % bits[0])
return YandexMetricaNode()
class YandexMetricaNode(Node):
def __init__(self):
self.counter_id = get_required_setting(
'YANDEX_METRICA_COUNTER_ID', COUNTER_ID_RE,
"must be (a string containing) a number'")
def render(self, context):
options = {
'id': int(self.counter_id),
'clickmap': True,
'trackLinks': True,
'accurateTrackBounce': True
}
if getattr(settings, 'YANDEX_METRICA_WEBVISOR', False):
options['webvisor'] = True
if getattr(settings, 'YANDEX_METRICA_TRACKHASH', False):
options['trackHash'] = True
if getattr(settings, 'YANDEX_METRICA_NOINDEX', False):
options['ut'] = 'noindex'
if getattr(settings, 'YANDEX_METRICA_ECOMMERCE', False):
options['ecommerce'] = 'dataLayer'
html = COUNTER_CODE % {
'counter_id': self.counter_id,
'options': json.dumps(options),
}
if is_internal_ip(context, 'YANDEX_METRICA'):
html = disable_html(html, 'Yandex.Metrica')
return html
def contribute_to_analytical(add_node):
YandexMetricaNode() # ensure properly configured
add_node('head_bottom', YandexMetricaNode)

View file

@ -0,0 +1,47 @@
"""
Tests for the Rating@Mail.ru template tags and filters.
"""
import re
from django.contrib.auth.models import User, AnonymousUser
from django.http import HttpRequest
from django.template import Context
from django.test.utils import override_settings
from analytical.templatetags.rating_mailru import RatingMailruNode
from analytical.tests.utils import TagTestCase
from analytical.utils import AnalyticalException
@override_settings(RATING_MAILRU_COUNTER_ID='1234567')
class RatingMailruTagTestCase(TagTestCase):
"""
Tests for the ``rating_mailru`` template tag.
"""
def test_tag(self):
r = self.render_tag('rating_mailru', 'rating_mailru')
self.assertTrue("counter?id=1234567;js=na" in r, r)
def test_node(self):
r = RatingMailruNode().render(Context({}))
self.assertTrue("counter?id=1234567;js=na" in r, r)
@override_settings(RATING_MAILRU_COUNTER_ID=None)
def test_no_site_id(self):
self.assertRaises(AnalyticalException, RatingMailruNode)
@override_settings(RATING_MAILRU_COUNTER_ID='1234abc')
def test_wrong_site_id(self):
self.assertRaises(AnalyticalException, RatingMailruNode)
@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 = RatingMailruNode().render(context)
self.assertTrue(r.startswith(
'<!-- Rating@Mail.ru disabled on internal IP address'), r)
self.assertTrue(r.endswith('-->'), r)

View file

@ -1,67 +0,0 @@
"""
Tests for the Reinvigorate template tags and filters.
"""
import re
from django.contrib.auth.models import User, AnonymousUser
from django.http import HttpRequest
from django.template import Context
from django.test.utils import override_settings
from analytical.templatetags.reinvigorate import ReinvigorateNode
from analytical.tests.utils import TagTestCase
from analytical.utils import AnalyticalException
@override_settings(REINVIGORATE_TRACKING_ID='12345-abcdefghij')
class ReinvigorateTagTestCase(TagTestCase):
"""
Tests for the ``reinvigorate`` template tag.
"""
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)
@override_settings(REINVIGORATE_TRACKING_ID=None)
def test_no_tracking_id(self):
self.assertRaises(AnalyticalException, ReinvigorateNode)
@override_settings(REINVIGORATE_TRACKING_ID='123abc')
def test_wrong_tracking_id(self):
self.assertRaises(AnalyticalException, ReinvigorateNode)
@override_settings(ANALYTICAL_AUTO_IDENTIFY=True)
def test_identify(self):
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)
@override_settings(ANALYTICAL_AUTO_IDENTIFY=True)
def test_identify_anonymous_user(self):
r = ReinvigorateNode().render(Context({'user': AnonymousUser()}))
self.assertFalse('var re_name_tag = ' in r, r)
self.assertFalse('var re_context_tag = ' 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)
@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 = ReinvigorateNode().render(context)
self.assertTrue(r.startswith(
'<!-- Reinvigorate disabled on internal IP address'), r)
self.assertTrue(r.endswith('-->'), r)

View file

@ -0,0 +1,47 @@
"""
Tests for the Yandex.Metrica template tags and filters.
"""
import re
from django.contrib.auth.models import User, AnonymousUser
from django.http import HttpRequest
from django.template import Context
from django.test.utils import override_settings
from analytical.templatetags.yandex_metrica import YandexMetricaNode
from analytical.tests.utils import TagTestCase
from analytical.utils import AnalyticalException
@override_settings(YANDEX_METRICA_COUNTER_ID='12345678')
class YandexMetricaTagTestCase(TagTestCase):
"""
Tests for the ``yandex_metrica`` template tag.
"""
def test_tag(self):
r = self.render_tag('yandex_metrica', 'yandex_metrica')
self.assertTrue("w.yaCounter12345678 = new Ya.Metrika" in r, r)
def test_node(self):
r = YandexMetricaNode().render(Context({}))
self.assertTrue("w.yaCounter12345678 = new Ya.Metrika" in r, r)
@override_settings(YANDEX_METRICA_COUNTER_ID=None)
def test_no_site_id(self):
self.assertRaises(AnalyticalException, YandexMetricaNode)
@override_settings(YANDEX_METRICA_COUNTER_ID='1234abcd')
def test_wrong_site_id(self):
self.assertRaises(AnalyticalException, YandexMetricaNode)
@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 = YandexMetricaNode().render(context)
self.assertTrue(r.startswith(
'<!-- Yandex.Metrica disabled on internal IP address'), r)
self.assertTrue(r.endswith('-->'), r)

View file

@ -1,29 +1,23 @@
"""
Tests for the analytical.utils module.
"""
import django
# import django
from django.conf import settings
from django.contrib.auth.models import AbstractBaseUser
from django.db import models
from django.http import HttpRequest
from django.template import Context
from django.test.utils import override_settings
from analytical.utils import (
get_domain, get_identity, is_internal_ip, get_required_setting,
AnalyticalException)
AnalyticalException,
get_domain,
get_identity,
get_required_setting,
is_internal_ip,
)
from analytical.tests.utils import TestCase
try:
from unittest import skipIf
except ImportError: # Python 2.6 fallback
from unittest2 import skipIf
try:
from django.contrib.auth.models import AbstractBaseUser
except ImportError: # Django < 1.5 fallback
AbstractBaseUser = models.Model
class SettingDeletedTestCase(TestCase):
@ -52,7 +46,6 @@ class MyUser(AbstractBaseUser):
class GetIdentityTestCase(TestCase):
@skipIf(django.VERSION < (1, 5,), 'Custom usernames not supported in Django < 1.5')
def test_custom_username_field(self):
get_id = get_identity(Context({}), user=MyUser(identity='fake_id'))
self.assertEqual(get_id, 'fake_id')

View file

@ -75,10 +75,7 @@ def get_identity(context, prefix=None, identity_func=None, user=None):
if identity_func is not None:
return identity_func(user)
else:
try:
return user.get_username()
except AttributeError: # Django < 1.5 fallback
return user.username
return user.get_username()
except (KeyError, AttributeError):
pass
return None

View file

@ -20,23 +20,29 @@ Installing the Python package
To install django-analytical the ``analytical`` package must be added to
the Python path. You can install it directly from PyPI using
``easy_install``::
``easy_install``:
$ easy_install django-analytical
.. code-block:: bash
$ easy_install django-analytical
You can also install directly from source. Download either the latest
stable version from PyPI_ or any release from GitHub_, or use Git to
get the development code::
get the development code:
$ git clone https://github.com/jcassee/django-analytical.git
.. code-block:: bash
$ git clone https://github.com/jcassee/django-analytical.git
.. _PyPI: http://pypi.python.org/pypi/django-analytical/
.. _GitHub: http://github.com/jcassee/django-analytical
Then install the package by running the setup script::
Then install the package by running the setup script:
$ cd django-analytical
$ python setup.py install
.. code-block:: bash
$ cd django-analytical
$ python setup.py install
.. _installing-the-application:
@ -46,13 +52,15 @@ Installing the Django application
After you installed django-analytical, add the ``analytical`` Django
application to the list of installed applications in the ``settings.py``
file of your project::
file of your project:
INSTALLED_APPS = [
...
'analytical',
...
]
.. code-block:: python
INSTALLED_APPS = [
...
'analytical',
...
]
.. _adding-the-template-tags:
@ -64,7 +72,9 @@ 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::
base template should look like this:
.. code-block:: html
{% load analytical %}
<!DOCTYPE ... >
@ -101,27 +111,27 @@ settings required to enable each service are listed here:
* :doc:`Chartbeat <services/chartbeat>`::
CHARTBEAT_USER_ID = '12345'
CHARTBEAT_USER_ID = '12345'
* :doc:`Clickmap <services/clickmap>`::
CLICKMAP_TRACKER_CODE = '12345678....912'
CLICKMAP_TRACKER_CODE = '12345678....912'
* :doc:`Clicky <services/clicky>`::
CLICKY_SITE_ID = '12345678'
CLICKY_SITE_ID = '12345678'
* :doc:`Crazy Egg <services/crazy_egg>`::
CRAZY_EGG_ACCOUNT_NUMBER = '12345678'
CRAZY_EGG_ACCOUNT_NUMBER = '12345678'
* :doc:`Gaug.es <services/gauges>`::
GAUGES_SITE_ID = '0123456789abcdef0123456789abcdef'
GAUGES_SITE_ID = '0123456789abcdef0123456789abcdef'
* :doc:`Google Analytics <services/google_analytics>`::
GOOGLE_ANALYTICS_PROPERTY_ID = 'UA-1234567-8'
GOOGLE_ANALYTICS_PROPERTY_ID = 'UA-1234567-8'
* :doc:`HubSpot <services/hubspot>`::
@ -134,16 +144,16 @@ settings required to enable each service are listed here:
* :doc:`KISSinsights <services/kiss_insights>`::
KISS_INSIGHTS_ACCOUNT_NUMBER = '12345'
KISS_INSIGHTS_SITE_CODE = 'abc'
KISS_INSIGHTS_ACCOUNT_NUMBER = '12345'
KISS_INSIGHTS_SITE_CODE = 'abc'
* :doc:`KISSmetrics <services/kiss_metrics>`::
KISS_METRICS_API_KEY = '0123456789abcdef0123456789abcdef01234567'
KISS_METRICS_API_KEY = '0123456789abcdef0123456789abcdef01234567'
* :doc:`Mixpanel <services/mixpanel>`::
MIXPANEL_API_TOKEN = '0123456789abcdef0123456789abcdef'
MIXPANEL_API_TOKEN = '0123456789abcdef0123456789abcdef'
* :doc:`Olark <services/olark>`::
@ -151,7 +161,7 @@ settings required to enable each service are listed here:
* :doc:`Optimizely <services/optimizely>`::
OPTIMIZELY_ACCOUNT_NUMBER = '1234567'
OPTIMIZELY_ACCOUNT_NUMBER = '1234567'
* :doc:`Performable <services/performable>`::
@ -162,14 +172,17 @@ settings required to enable each service are listed here:
PIWIK_DOMAIN_PATH = 'your.piwik.server/optional/path'
PIWIK_SITE_ID = '123'
* :doc:`Reinvigorate <services/reinvigorate>`::
* :doc:`Rating\@Mail.ru <services/rating_mailru>`::
REINVIGORATE_TRACKING_ID = '12345-abcdefghij'
RATING_MAILRU_COUNTER_ID = '1234567'
* :doc:`Woopra <services/woopra>`::
WOOPRA_DOMAIN = 'abcde.com'
* :doc:`Yandex.Metrica <services/yandex_metrica>`::
YANDEX_METRICA_COUNTER_ID = '12345678'
----

View file

@ -220,7 +220,7 @@ You can configure the `Sample Rate`_ feature by setting the
The value is a percentage and can be between 0 and 100 and can be a string or
decimal value of with up to two decimal places.
.. _`Sample Rate`_: https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiBasicConfiguration#_setsamplerate
.. _`Sample Rate`: https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiBasicConfiguration#_setsamplerate
.. _google-analytics-site-speed-sample-rate:
@ -236,7 +236,7 @@ You can configure the `Site Speed Sample Rate`_ feature by setting the
The value is a percentage and can be between 0 and 100 and can be a string or
decimal value of with up to two decimal places.
.. _`Site Speed Sample Rate`_: https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiBasicConfiguration#_setsitespeedsamplerate
.. _`Site Speed Sample Rate`: https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiBasicConfiguration#_setsitespeedsamplerate
.. _google-analytics-session-cookie-timeout:
@ -251,7 +251,7 @@ You can configure the `Session Cookie Timeout`_ feature by setting the
The value is the session cookie timeout in milliseconds or 0 to delete the cookie when the browser is closed.
.. _`Session Cookie Timeout`_: https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiBasicConfiguration#_setsessioncookietimeout
.. _`Session Cookie Timeout`: https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiBasicConfiguration#_setsessioncookietimeout
.. _google-analytics-visitor-cookie-timeout:
@ -266,4 +266,4 @@ You can configure the `Visitor Cookie Timeout`_ feature by setting the
The value is the visitor cookie timeout in milliseconds or 0 to delete the cookie when the browser is closed.
.. _`Visitor Cookie Timeout`_: https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiBasicConfiguration#_setvisitorcookietimeout
.. _`Visitor Cookie Timeout`: https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiBasicConfiguration#_setvisitorcookietimeout

View file

@ -0,0 +1,73 @@
===================================
Rating\@Mail.ru -- traffic analysis
===================================
`Rating\@Mail.ru`_ is an analytics tool like as google analytics.
.. _`Rating\@Mail.ru`: http://top.mail.ru/
.. rating-mailru-installation:
Installation
============
To start using the Rating\@Mail.ru 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 Rating\@Mail.ru 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:`rating-mailru-configuration`.
The Rating\@Mail.ru counter code is inserted into templates using a template
tag. Load the :mod:`rating_mailru` template tag library and insert the
:ttag:`rating_mailru` 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 rating_mailru %}
<html>
<head>
...
{% rating_mailru %}
</head>
...
.. _rating-mailru-configuration:
Configuration
=============
Before you can use the Rating\@Mail.ru integration, you must first set
your website counter ID.
.. _rating-mailru-counter-id:
Setting the counter ID
----------------------
Every website you track with Rating\@Mail.ru gets its own counter ID,
and the :ttag:`rating_mailru` tag will include it in the rendered
Javascript code. You can find the web counter ID on the overview page
of your account. Set :const:`RATING_MAILRU_COUNTER_ID` in the
project :file:`settings.py` file::
RATING_MAILRU_COUNTER_ID = '1234567'
If you do not set a counter ID, the counter code will not be rendered.
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:`RATING_MAILRU_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.

View file

@ -1,157 +0,0 @@
================================
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.net/
.. 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 %}
</body>
</html>
.. _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.

View file

@ -0,0 +1,84 @@
==================================
Yandex.Metrica -- traffic analysis
==================================
`Yandex.Metrica`_ is an analytics tool like as google analytics.
.. _`Yandex.Metrica`: http://metrica.yandex.com/
.. yandex-metrica-installation:
Installation
============
To start using the Yandex.Metrica 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 Yandex.Metrica 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:`yandex-metrica-configuration`.
The Yandex.Metrica counter code is inserted into templates using a template
tag. Load the :mod:`yandex_metrica` template tag library and insert the
:ttag:`yandex_metrica` 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 yandex_metrica %}
<html>
<head>
...
{% yandex_metrica %}
</head>
...
.. _yandex-metrica-configuration:
Configuration
=============
Before you can use the Yandex.Metrica integration, you must first set
your website counter ID.
.. _yandex-metrica-counter-id:
Setting the counter ID
----------------------
Every website you track with Yandex.Metrica gets its own counter ID,
and the :ttag:`yandex_metrica` tag will include it in the rendered
Javascript code. You can find the web counter ID on the overview page
of your account. Set :const:`YANDEX_METRICA_COUNTER_ID` in the
project :file:`settings.py` file::
YANDEX_METRICA_COUNTER_ID = '12345678'
If you do not set a counter ID, the counter code will not be rendered.
You can set additional options to tune your counter:
============================ ============= =============================================
Constant Default Value Description
============================ ============= =============================================
``YANDEX_METRICA_WEBVISOR`` False Webvisor, scroll map, form analysis.
``YANDEX_METRICA_TRACKHASH`` False Hash tracking in the browser address bar.
``YANDEX_METRICA_NOINDEX`` False Stop automatic page indexing.
``YANDEX_METRICA_ECOMMERCE`` False Dispatch ecommerce data to Metrica.
============================ ============= =============================================
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:`YANDEX_METRICA_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.

View file

@ -14,5 +14,6 @@ deps =
django17: Django>=1.7,<1.8
django18: Django>=1.8,<1.9
django19: Django>=1.9,<1.10
virtualenv<14.0.0
passenv = TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH
whitelist_externals = sh