mirror of
https://github.com/jazzband/django-analytical.git
synced 2026-03-16 22:20:25 +00:00
Merge remote-tracking branch 'jcassee/master' into gaflags_fix
This commit is contained in:
commit
2f22762010
26 changed files with 605 additions and 451 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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/
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
11
README.rst
11
README.rst
|
|
@ -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
|
||||
-------------------------
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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__)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
71
analytical/templatetags/rating_mailru.py
Normal file
71
analytical/templatetags/rating_mailru.py
Normal 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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
93
analytical/templatetags/yandex_metrica.py
Normal file
93
analytical/templatetags/yandex_metrica.py
Normal 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)
|
||||
47
analytical/tests/test_tag_rating_mailru.py
Normal file
47
analytical/tests/test_tag_rating_mailru.py
Normal 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)
|
||||
|
|
@ -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)
|
||||
47
analytical/tests/test_tag_yandex_metrica.py
Normal file
47
analytical/tests/test_tag_yandex_metrica.py
Normal 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)
|
||||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
||||
----
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
73
docs/services/rating_mailru.rst
Normal file
73
docs/services/rating_mailru.rst
Normal 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.
|
||||
|
|
@ -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.
|
||||
84
docs/services/yandex_metrica.rst
Normal file
84
docs/services/yandex_metrica.rst
Normal 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.
|
||||
1
tox.ini
1
tox.ini
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue