From c05e294250f3dc4626825cd8c4f1c1545e0a17a9 Mon Sep 17 00:00:00 2001 From: Peter Bittner Date: Sun, 17 Apr 2016 11:51:27 +0200 Subject: [PATCH 01/29] Add "Python versions" badge (README) --- README.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index f2a1180..bb404cf 100644 --- a/README.rst +++ b/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 From 1bc53c3cc57a28c1d74e4ef2d526371fc63e4143 Mon Sep 17 00:00:00 2001 From: Peter Bittner Date: Sun, 17 Apr 2016 18:46:01 +0200 Subject: [PATCH 02/29] Pin pip version compatible with Python 3.2 See problems in issue #84 --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index 3f6996d..448715d 100644 --- a/tox.ini +++ b/tox.ini @@ -9,6 +9,7 @@ commands = coverage run setup.py test sh -c 'coveralls | true' deps = + pip<8.0.0 coverage==3.7.1 coveralls django17: Django>=1.7,<1.8 From 7243f34243959703bf3658ece058913c32249ddf Mon Sep 17 00:00:00 2001 From: Peter Bittner Date: Sun, 17 Apr 2016 19:10:50 +0200 Subject: [PATCH 03/29] Pin pip version compatible with Python 3.2 (2nd try) See problems in issue #84 --- .travis.yml | 2 ++ tox.ini | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d1cc97e..1a414d2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ language: python python: "3.5" install: + # continue to support Python 3.2 (see issue #84) + - pip install --upgrade "pip<8.0.0" - pip install coveralls tox script: - tox diff --git a/tox.ini b/tox.ini index 448715d..3f6996d 100644 --- a/tox.ini +++ b/tox.ini @@ -9,7 +9,6 @@ commands = coverage run setup.py test sh -c 'coveralls | true' deps = - pip<8.0.0 coverage==3.7.1 coveralls django17: Django>=1.7,<1.8 From f04ba7d12503bc1c3ec52e9b02e31e893069adc1 Mon Sep 17 00:00:00 2001 From: Peter Bittner Date: Sun, 17 Apr 2016 23:12:29 +0200 Subject: [PATCH 04/29] Pin pip version compatible with Python 3.2 (3rd try) See problems in issue #84 --- .travis.yml | 2 +- tox.ini | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1a414d2..fd6bcb2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: python python: "3.5" install: # continue to support Python 3.2 (see issue #84) - - pip install --upgrade "pip<8.0.0" + - pip install pip==7.1.2 - pip install coveralls tox script: - tox diff --git a/tox.ini b/tox.ini index 3f6996d..761724e 100644 --- a/tox.ini +++ b/tox.ini @@ -9,6 +9,7 @@ commands = coverage run setup.py test sh -c 'coveralls | true' deps = + pip==7.1.2 coverage==3.7.1 coveralls django17: Django>=1.7,<1.8 From f35511b33e33d47ce3c66cf9ab45dff7083f552c Mon Sep 17 00:00:00 2001 From: Peter Bittner Date: Sun, 15 May 2016 17:51:42 +0200 Subject: [PATCH 05/29] Pin virtualenv version for Python 3.2 support --- .travis.yml | 2 +- tox.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index fd6bcb2..ec2f064 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: python python: "3.5" install: # continue to support Python 3.2 (see issue #84) - - pip install pip==7.1.2 + - pip install "virtualenv<14.0.0" - pip install coveralls tox script: - tox diff --git a/tox.ini b/tox.ini index 761724e..5f71e15 100644 --- a/tox.ini +++ b/tox.ini @@ -9,11 +9,11 @@ commands = coverage run setup.py test sh -c 'coveralls | true' deps = - pip==7.1.2 coverage==3.7.1 coveralls 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 From ea6b49f354584069e15c3f8b3f7151dacf7354cf Mon Sep 17 00:00:00 2001 From: Peter Bittner Date: Sat, 23 Jan 2016 00:04:16 +0100 Subject: [PATCH 06/29] [Crazy Egg] Fix code smell "redefine built-in" and flake8 complaints --- analytical/templatetags/crazy_egg.py | 31 ++++++++++++++++++---------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/analytical/templatetags/crazy_egg.py b/analytical/templatetags/crazy_egg.py index 5f0d830..99a58c5 100644 --- a/analytical/templatetags/crazy_egg.py +++ b/analytical/templatetags/crazy_egg.py @@ -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 = """""" +SETUP_CODE = ''.\ + 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' \ - % (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' \ + '' % (html, js) if is_internal_ip(context, 'CRAZY_EGG'): html = disable_html(html, 'Crazy Egg') return html From 54b7dfce2c8cf1a2766609a43984041301414e46 Mon Sep 17 00:00:00 2001 From: Peter Bittner Date: Sat, 23 Jan 2016 00:13:20 +0100 Subject: [PATCH 07/29] [Google Analytics] Fix code smell "redefine built-in" and flake8 complaints --- analytical/templatetags/google_analytics.py | 24 ++++++++------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/analytical/templatetags/google_analytics.py b/analytical/templatetags/google_analytics.py index d19e6ca..ab6e890 100644 --- a/analytical/templatetags/google_analytics.py +++ b/analytical/templatetags/google_analytics.py @@ -13,14 +13,6 @@ 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 - - TRACK_SINGLE_DOMAIN = 1 TRACK_MULTIPLE_SUBDOMAINS = 2 TRACK_MULTIPLE_DOMAINS = 3 @@ -98,14 +90,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: @@ -113,11 +106,12 @@ 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: From 56bcb46bb252e75c94935cbfdb0d0b1c24f929ee Mon Sep 17 00:00:00 2001 From: Peter Bittner Date: Sat, 23 Jan 2016 00:30:05 +0100 Subject: [PATCH 08/29] [GoSquared] Fix code smell "unused import" and flake8 complaints --- analytical/templatetags/gosquared.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/analytical/templatetags/gosquared.py b/analytical/templatetags/gosquared.py index b159d9c..72edf6c 100644 --- a/analytical/templatetags/gosquared.py +++ b/analytical/templatetags/gosquared.py @@ -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): From 9e6f7c8723b03ddebf1cb347658c8d5d27442e02 Mon Sep 17 00:00:00 2001 From: Peter Bittner Date: Sat, 23 Jan 2016 00:40:58 +0100 Subject: [PATCH 09/29] [Intercom] Fix code smell "redefine built-in" and flake8 complaints --- analytical/templatetags/intercom.py | 36 ++++++++++++++++------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/analytical/templatetags/intercom.py b/analytical/templatetags/intercom.py index 5b88586..b9e7dd7 100644 --- a/analytical/templatetags/intercom.py +++ b/analytical/templatetags/intercom.py @@ -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 From a1d3bbc09fa2cb3c791041004920ba0e198fda45 Mon Sep 17 00:00:00 2001 From: Peter Bittner Date: Sat, 23 Jan 2016 00:47:40 +0100 Subject: [PATCH 10/29] [Spring Metrics] Fix code smell "redefine built-in" and flake8 complaints --- analytical/templatetags/spring_metrics.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/analytical/templatetags/spring_metrics.py b/analytical/templatetags/spring_metrics.py index 74e2767..ed2f063 100644 --- a/analytical/templatetags/spring_metrics.py +++ b/analytical/templatetags/spring_metrics.py @@ -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) From 14c1772641c211e9b06f6bb3eab11f68442de9b1 Mon Sep 17 00:00:00 2001 From: Peter Bittner Date: Sat, 23 Jan 2016 00:51:56 +0100 Subject: [PATCH 11/29] [Woopra] Fix code smell "redefine built-in" and flake8 complaints --- analytical/templatetags/woopra.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/analytical/templatetags/woopra.py b/analytical/templatetags/woopra.py index e5e2ad4..5aec22c 100644 --- a/analytical/templatetags/woopra.py +++ b/analytical/templatetags/woopra.py @@ -50,7 +50,8 @@ def woopra(parser, token): class WoopraNode(Node): def __init__(self): - self.domain = get_required_setting('WOOPRA_DOMAIN', DOMAIN_RE, + self.domain = get_required_setting( + 'WOOPRA_DOMAIN', DOMAIN_RE, "must be a domain name") def render(self, context): @@ -74,19 +75,19 @@ class WoopraNode(Node): return vars 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() From f832ec61795d0c371f5e50d0584a2179f4a0ecfa Mon Sep 17 00:00:00 2001 From: Peter Bittner Date: Wed, 18 May 2016 14:51:52 +0200 Subject: [PATCH 12/29] Remove dead code (unsupported Python/Django versions) --- analytical/tests/test_utils.py | 23 ++++++++--------------- analytical/utils.py | 5 +---- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/analytical/tests/test_utils.py b/analytical/tests/test_utils.py index 4a4ba6a..0f4ad58 100644 --- a/analytical/tests/test_utils.py +++ b/analytical/tests/test_utils.py @@ -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') diff --git a/analytical/utils.py b/analytical/utils.py index da9413a..d915cf4 100644 --- a/analytical/utils.py +++ b/analytical/utils.py @@ -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 From 709b8edcf48fee7a31dc67c4287d251b5db87935 Mon Sep 17 00:00:00 2001 From: Peter Bittner Date: Wed, 18 May 2016 14:58:41 +0200 Subject: [PATCH 13/29] [GoSquared] Test user is None in _identify --- .travis.yml | 4 ++-- analytical/tests/test_tag_gosquared.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index ec2f064..989706d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,9 +6,9 @@ install: - 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 diff --git a/analytical/tests/test_tag_gosquared.py b/analytical/tests/test_tag_gosquared.py index 9b946c0..83607f4 100644 --- a/analytical/tests/test_tag_gosquared.py +++ b/analytical/tests/test_tag_gosquared.py @@ -48,7 +48,7 @@ class GoSquaredTagTestCase(TagTestCase): })) self.assertTrue('GoSquared.UserName = "test_identity";' in r, r) - @override_settings(ANALYTICAL_AUTO_IDENTIFY=True) + @override_settings(ANALYTICAL_AUTO_IDENTIFY=True, USER_ID=None) def test_identify_anonymous_user(self): r = GoSquaredNode().render(Context({'user': AnonymousUser()})) self.assertFalse('GoSquared.UserName = ' in r, r) From 7847d22f95f44792e35108af24267161411c5bf1 Mon Sep 17 00:00:00 2001 From: Peter Bittner Date: Wed, 18 May 2016 15:34:18 +0200 Subject: [PATCH 14/29] [GoSquared] Remove settings override with no effect --- analytical/tests/test_tag_gosquared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/analytical/tests/test_tag_gosquared.py b/analytical/tests/test_tag_gosquared.py index 83607f4..9b946c0 100644 --- a/analytical/tests/test_tag_gosquared.py +++ b/analytical/tests/test_tag_gosquared.py @@ -48,7 +48,7 @@ class GoSquaredTagTestCase(TagTestCase): })) self.assertTrue('GoSquared.UserName = "test_identity";' in r, r) - @override_settings(ANALYTICAL_AUTO_IDENTIFY=True, USER_ID=None) + @override_settings(ANALYTICAL_AUTO_IDENTIFY=True) def test_identify_anonymous_user(self): r = GoSquaredNode().render(Context({'user': AnonymousUser()})) self.assertFalse('GoSquared.UserName = ' in r, r) From 66f4aea54ac20b353a3538b98a13a2287e9a2d3f Mon Sep 17 00:00:00 2001 From: Peter Bittner Date: Wed, 18 May 2016 19:20:37 +0200 Subject: [PATCH 15/29] Fix W0612 (unused-variable) x3, correct white space --- analytical/templatetags/google_analytics.py | 27 ++++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/analytical/templatetags/google_analytics.py b/analytical/templatetags/google_analytics.py index ab6e890..80c04d7 100644 --- a/analytical/templatetags/google_analytics.py +++ b/analytical/templatetags/google_analytics.py @@ -9,9 +9,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 - +from analytical.utils import ( + AnalyticalException, + disable_html, + get_domain, + get_required_setting, + is_internal_ip, +) TRACK_SINGLE_DOMAIN = 1 TRACK_MULTIPLE_SUBDOMAINS = 2 @@ -41,7 +45,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']);" DEFAULT_SOURCE = ("'https://ssl' : 'http://www'", "'.google-analytics.com/ga.js'") @@ -68,8 +72,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) @@ -97,8 +101,8 @@ class GoogleAnalyticsNode(Node): domain = get_domain(context, 'google_analytics') if domain is None: raise AnalyticalException( - "tracking multiple domains with Google Analytics" - " requires a domain name") + "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: @@ -118,7 +122,12 @@ class GoogleAnalyticsNode(Node): 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): From 0a4a9b7220a94819aa9d2f387f58c265ab29da43 Mon Sep 17 00:00:00 2001 From: Peter Bittner Date: Wed, 18 May 2016 23:13:03 +0200 Subject: [PATCH 16/29] Fix W0622 (redefined-builtin), correct white space --- analytical/templatetags/woopra.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/analytical/templatetags/woopra.py b/analytical/templatetags/woopra.py index 5aec22c..00548c7 100644 --- a/analytical/templatetags/woopra.py +++ b/analytical/templatetags/woopra.py @@ -10,9 +10,13 @@ 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 = """ @@ -30,7 +34,6 @@ TRACKING_CODE = """ """ - register = Library() @@ -51,8 +54,8 @@ def woopra(parser, token): class WoopraNode(Node): def __init__(self): self.domain = get_required_setting( - 'WOOPRA_DOMAIN', DOMAIN_RE, - "must be a domain name") + 'WOOPRA_DOMAIN', DOMAIN_RE, + "must be a domain name") def render(self, context): settings = self._get_settings(context) @@ -67,12 +70,12 @@ 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): params = {} @@ -84,7 +87,7 @@ class WoopraNode(Node): user = get_user_from_context(context) if user is not None and user.is_authenticated(): params['name'] = get_identity( - context, 'woopra', self._identify, user) + context, 'woopra', self._identify, user) if user.email: params['email'] = user.email return params From 83312e01f9a0a1983c2db2abb1b8692b12c2e377 Mon Sep 17 00:00:00 2001 From: Peter Bittner Date: Wed, 18 May 2016 23:41:58 +0200 Subject: [PATCH 17/29] Remove Reinvigorate template tag, tests and docs Closes #82 --- README.rst | 1 - analytical/templatetags/analytical.py | 1 - analytical/templatetags/reinvigorate.py | 81 ----------- analytical/tests/test_tag_reinvigorate.py | 67 --------- docs/install.rst | 4 - docs/services/reinvigorate.rst | 157 ---------------------- 6 files changed, 311 deletions(-) delete mode 100644 analytical/templatetags/reinvigorate.py delete mode 100644 analytical/tests/test_tag_reinvigorate.py delete mode 100644 docs/services/reinvigorate.rst diff --git a/README.rst b/README.rst index bb404cf..47accee 100644 --- a/README.rst +++ b/README.rst @@ -90,7 +90,6 @@ Currently Supported Services .. _`Optimizely`: http://www.optimizely.com/ .. _`Performable`: http://www.performable.com/ .. _`Piwik`: http://www.piwik.org/ -.. _`Reinvigorate`: http://www.reinvigorate.net/ .. _`SnapEngage`: http://www.snapengage.com/ .. _`Spring Metrics`: http://www.springmetrics.com/ .. _`UserVoice`: http://www.uservoice.com/ diff --git a/analytical/templatetags/analytical.py b/analytical/templatetags/analytical.py index c6ba6d6..68e0d6b 100644 --- a/analytical/templatetags/analytical.py +++ b/analytical/templatetags/analytical.py @@ -34,7 +34,6 @@ TAG_MODULES = [ 'analytical.optimizely', 'analytical.performable', 'analytical.piwik', - 'analytical.reinvigorate', 'analytical.snapengage', 'analytical.spring_metrics', 'analytical.uservoice', diff --git a/analytical/templatetags/reinvigorate.py b/analytical/templatetags/reinvigorate.py deleted file mode 100644 index 4f577bc..0000000 --- a/analytical/templatetags/reinvigorate.py +++ /dev/null @@ -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 = """ - - -""" - - -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) diff --git a/analytical/tests/test_tag_reinvigorate.py b/analytical/tests/test_tag_reinvigorate.py deleted file mode 100644 index c23febd..0000000 --- a/analytical/tests/test_tag_reinvigorate.py +++ /dev/null @@ -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( - ''), r) diff --git a/docs/install.rst b/docs/install.rst index b29fec3..37db7a0 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -162,10 +162,6 @@ settings required to enable each service are listed here: PIWIK_DOMAIN_PATH = 'your.piwik.server/optional/path' PIWIK_SITE_ID = '123' -* :doc:`Reinvigorate `:: - - REINVIGORATE_TRACKING_ID = '12345-abcdefghij' - * :doc:`Woopra `:: WOOPRA_DOMAIN = 'abcde.com' diff --git a/docs/services/reinvigorate.rst b/docs/services/reinvigorate.rst deleted file mode 100644 index 44051d7..0000000 --- a/docs/services/reinvigorate.rst +++ /dev/null @@ -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 %} - - - - -.. _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. From 849c551d553a768254a570b235ec0e57264df3d4 Mon Sep 17 00:00:00 2001 From: Peter Bittner Date: Thu, 19 May 2016 00:04:15 +0200 Subject: [PATCH 18/29] Remove dead code (unsupported Python 2.6) --- analytical/templatetags/analytical.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/analytical/templatetags/analytical.py b/analytical/templatetags/analytical.py index 68e0d6b..a6c1e31 100644 --- a/analytical/templatetags/analytical.py +++ b/analytical/templatetags/analytical.py @@ -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 From bf96a8fe50c88aa86cd326dfcc299d0882412939 Mon Sep 17 00:00:00 2001 From: Peter Bittner Date: Thu, 19 May 2016 00:14:08 +0200 Subject: [PATCH 19/29] Fix some white space and highlighting in docs --- docs/install.rst | 61 +++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/docs/install.rst b/docs/install.rst index 37db7a0..14fe1d1 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -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:: + + $ 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 %} @@ -101,27 +111,27 @@ settings required to enable each service are listed here: * :doc:`Chartbeat `:: - CHARTBEAT_USER_ID = '12345' + CHARTBEAT_USER_ID = '12345' * :doc:`Clickmap `:: - CLICKMAP_TRACKER_CODE = '12345678....912' + CLICKMAP_TRACKER_CODE = '12345678....912' * :doc:`Clicky `:: - CLICKY_SITE_ID = '12345678' + CLICKY_SITE_ID = '12345678' * :doc:`Crazy Egg `:: - CRAZY_EGG_ACCOUNT_NUMBER = '12345678' + CRAZY_EGG_ACCOUNT_NUMBER = '12345678' * :doc:`Gaug.es `:: - GAUGES_SITE_ID = '0123456789abcdef0123456789abcdef' + GAUGES_SITE_ID = '0123456789abcdef0123456789abcdef' * :doc:`Google Analytics `:: - GOOGLE_ANALYTICS_PROPERTY_ID = 'UA-1234567-8' + GOOGLE_ANALYTICS_PROPERTY_ID = 'UA-1234567-8' * :doc:`HubSpot `:: @@ -134,16 +144,16 @@ settings required to enable each service are listed here: * :doc:`KISSinsights `:: - KISS_INSIGHTS_ACCOUNT_NUMBER = '12345' - KISS_INSIGHTS_SITE_CODE = 'abc' + KISS_INSIGHTS_ACCOUNT_NUMBER = '12345' + KISS_INSIGHTS_SITE_CODE = 'abc' * :doc:`KISSmetrics `:: - KISS_METRICS_API_KEY = '0123456789abcdef0123456789abcdef01234567' + KISS_METRICS_API_KEY = '0123456789abcdef0123456789abcdef01234567' * :doc:`Mixpanel `:: - MIXPANEL_API_TOKEN = '0123456789abcdef0123456789abcdef' + MIXPANEL_API_TOKEN = '0123456789abcdef0123456789abcdef' * :doc:`Olark `:: @@ -151,7 +161,7 @@ settings required to enable each service are listed here: * :doc:`Optimizely `:: - OPTIMIZELY_ACCOUNT_NUMBER = '1234567' + OPTIMIZELY_ACCOUNT_NUMBER = '1234567' * :doc:`Performable `:: @@ -166,7 +176,6 @@ settings required to enable each service are listed here: WOOPRA_DOMAIN = 'abcde.com' - ---- The django-analytical application is now set-up to track visitors. For From fa034b0b1071c823e5dc487b011e761dce336d5a Mon Sep 17 00:00:00 2001 From: Nikolay Korotkiy Date: Sun, 3 Apr 2016 12:12:19 +0300 Subject: [PATCH 20/29] Add support for the Yandex.Metrica service --- README.rst | 2 + analytical/templatetags/analytical.py | 1 + analytical/templatetags/yandex_metrica.py | 93 +++++++++++++++++++++ analytical/tests/test_tag_yandex_metrica.py | 47 +++++++++++ docs/install.rst | 4 + docs/services/yandex_metrica.rst | 84 +++++++++++++++++++ 6 files changed, 231 insertions(+) create mode 100644 analytical/templatetags/yandex_metrica.py create mode 100644 analytical/tests/test_tag_yandex_metrica.py create mode 100644 docs/services/yandex_metrica.rst diff --git a/README.rst b/README.rst index 47accee..a824e1a 100644 --- a/README.rst +++ b/README.rst @@ -73,6 +73,7 @@ Currently Supported Services * `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/ @@ -94,6 +95,7 @@ Currently Supported Services .. _`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 ------------------------- diff --git a/analytical/templatetags/analytical.py b/analytical/templatetags/analytical.py index a6c1e31..3ab7c52 100644 --- a/analytical/templatetags/analytical.py +++ b/analytical/templatetags/analytical.py @@ -36,6 +36,7 @@ TAG_MODULES = [ 'analytical.spring_metrics', 'analytical.uservoice', 'analytical.woopra', + 'analytical.yandex_metrica', ] logger = logging.getLogger(__name__) diff --git a/analytical/templatetags/yandex_metrica.py b/analytical/templatetags/yandex_metrica.py new file mode 100644 index 0000000..e2ec20b --- /dev/null +++ b/analytical/templatetags/yandex_metrica.py @@ -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 = """ + + +""" + + +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) diff --git a/analytical/tests/test_tag_yandex_metrica.py b/analytical/tests/test_tag_yandex_metrica.py new file mode 100644 index 0000000..f85924a --- /dev/null +++ b/analytical/tests/test_tag_yandex_metrica.py @@ -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( + ''), r) diff --git a/docs/install.rst b/docs/install.rst index 14fe1d1..77517c4 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -176,6 +176,10 @@ settings required to enable each service are listed here: WOOPRA_DOMAIN = 'abcde.com' +* :doc:`Yandex.Metrica `:: + + YANDEX_METRICA_COUNTER_ID = '12345678' + ---- The django-analytical application is now set-up to track visitors. For diff --git a/docs/services/yandex_metrica.rst b/docs/services/yandex_metrica.rst new file mode 100644 index 0000000..d9861bb --- /dev/null +++ b/docs/services/yandex_metrica.rst @@ -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 %} + + + ... + {% yandex_metrica %} + + ... + + +.. _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. From 20e6a381630695bb16e2797b894a8588f0127d04 Mon Sep 17 00:00:00 2001 From: Aleck Landgraf Date: Tue, 24 May 2016 14:37:54 -0700 Subject: [PATCH 21/29] updates to the latest woopra snippet which adds woopra to the window for tracking custom events, like SPA routing --- analytical/templatetags/woopra.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/analytical/templatetags/woopra.py b/analytical/templatetags/woopra.py index 00548c7..2269cb9 100644 --- a/analytical/templatetags/woopra.py +++ b/analytical/templatetags/woopra.py @@ -23,14 +23,15 @@ TRACKING_CODE = """ """ From 3c118b2f37f5b8737ab9d064a6670e37f2344fa6 Mon Sep 17 00:00:00 2001 From: Peter Bittner Date: Wed, 25 May 2016 03:18:33 +0200 Subject: [PATCH 22/29] Remove Reinvigorate from README (slipped through PR #91) --- README.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/README.rst b/README.rst index 47accee..aa45a15 100644 --- a/README.rst +++ b/README.rst @@ -68,7 +68,6 @@ Currently Supported Services * `Optimizely`_ A/B testing * `Performable`_ web analytics and landing pages * `Piwik`_ open source web analytics -* `Reinvigorate`_ visitor tracking * `SnapEngage`_ live chat * `Spring Metrics`_ conversion tracking * `UserVoice`_ user feedback and helpdesk From 8b232d06cbfed41ae3a6f687ef5443392777daf1 Mon Sep 17 00:00:00 2001 From: Nikolay Korotkiy Date: Sun, 24 Apr 2016 12:15:36 +0300 Subject: [PATCH 23/29] Add support for the Raiting@Mail.ru service --- README.rst | 2 + analytical/templatetags/analytical.py | 1 + analytical/templatetags/rating_mailru.py | 71 +++++++++++++++++++++ analytical/tests/test_tag_rating_mailru.py | 47 ++++++++++++++ docs/install.rst | 4 ++ docs/services/rating_mailru.rst | 73 ++++++++++++++++++++++ 6 files changed, 198 insertions(+) create mode 100644 analytical/templatetags/rating_mailru.py create mode 100644 analytical/tests/test_tag_rating_mailru.py create mode 100644 docs/services/rating_mailru.rst diff --git a/README.rst b/README.rst index 68004b1..4056e17 100644 --- a/README.rst +++ b/README.rst @@ -68,6 +68,7 @@ Currently Supported Services * `Optimizely`_ A/B testing * `Performable`_ web analytics and landing pages * `Piwik`_ open source web analytics +* `Rating@Mail.ru`_ web analytics * `SnapEngage`_ live chat * `Spring Metrics`_ conversion tracking * `UserVoice`_ user feedback and helpdesk @@ -90,6 +91,7 @@ Currently Supported Services .. _`Optimizely`: http://www.optimizely.com/ .. _`Performable`: http://www.performable.com/ .. _`Piwik`: http://www.piwik.org/ +.. _`Rating@Mail.ru`: http://top.mail.ru/ .. _`SnapEngage`: http://www.snapengage.com/ .. _`Spring Metrics`: http://www.springmetrics.com/ .. _`UserVoice`: http://www.uservoice.com/ diff --git a/analytical/templatetags/analytical.py b/analytical/templatetags/analytical.py index 3ab7c52..ad4fba7 100644 --- a/analytical/templatetags/analytical.py +++ b/analytical/templatetags/analytical.py @@ -32,6 +32,7 @@ TAG_MODULES = [ 'analytical.optimizely', 'analytical.performable', 'analytical.piwik', + 'analytical.rating_mailru', 'analytical.snapengage', 'analytical.spring_metrics', 'analytical.uservoice', diff --git a/analytical/templatetags/rating_mailru.py b/analytical/templatetags/rating_mailru.py new file mode 100644 index 0000000..d495824 --- /dev/null +++ b/analytical/templatetags/rating_mailru.py @@ -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 = """ + + +""" + + +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) diff --git a/analytical/tests/test_tag_rating_mailru.py b/analytical/tests/test_tag_rating_mailru.py new file mode 100644 index 0000000..2e91a60 --- /dev/null +++ b/analytical/tests/test_tag_rating_mailru.py @@ -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( + ''), r) diff --git a/docs/install.rst b/docs/install.rst index 77517c4..192617f 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -172,6 +172,10 @@ settings required to enable each service are listed here: PIWIK_DOMAIN_PATH = 'your.piwik.server/optional/path' PIWIK_SITE_ID = '123' +* :doc:`Rating@Mail.ru `:: + + RATING_MAILRU_COUNTER_ID = '1234567' + * :doc:`Woopra `:: WOOPRA_DOMAIN = 'abcde.com' diff --git a/docs/services/rating_mailru.rst b/docs/services/rating_mailru.rst new file mode 100644 index 0000000..c278622 --- /dev/null +++ b/docs/services/rating_mailru.rst @@ -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 %} + + + ... + {% rating_mailru %} + + ... + + +.. _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. From 826725f2ced82872fb2b5205b41a70b8795d39b4 Mon Sep 17 00:00:00 2001 From: Joost Cassee Date: Wed, 25 May 2016 10:43:57 +0200 Subject: [PATCH 24/29] Fix Sphinx warning about code-block in install.rst --- docs/install.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/install.rst b/docs/install.rst index 192617f..f6695e9 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -39,7 +39,7 @@ get the development code: Then install the package by running the setup script: -.. code-block:: +.. code-block:: bash $ cd django-analytical $ python setup.py install From 0da24961a60e03d8f021e3a0cb507640c87a0c24 Mon Sep 17 00:00:00 2001 From: Joost Cassee Date: Wed, 25 May 2016 10:44:18 +0200 Subject: [PATCH 25/29] Update changelog and bump version to 2.1.0 --- AUTHORS.rst | 5 ++++- CHANGELOG.rst | 7 +++++++ analytical/__init__.py | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index b9e4f4d..d76e06a 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -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`_ and others. Included Javascript code snippets for integration of the analytics services were written by the respective service providers. @@ -33,6 +34,8 @@ 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 .. _`Analytical`: https://github.com/jkrall/analytical .. _`Bateau Knowledge`: http://www.bateauknowledge.nl/ .. _`GreenKahuna`: http://www.greenkahuna.com/ diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f08386d..75b02b7 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,10 @@ +Version 2.1.0 +------------- +* Support Rating@Mmail.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) diff --git a/analytical/__init__.py b/analytical/__init__.py index f3a35e8..ae45761 100644 --- a/analytical/__init__.py +++ b/analytical/__init__.py @@ -10,6 +10,6 @@ Django_ project. See the ``docs`` directory for more information. __author__ = "Joost Cassee" __email__ = "joost@cassee.net" -__version__ = "2.0.0" +__version__ = "2.1.0" __copyright__ = "Copyright (C) 2011-2016 Joost Cassee and others" __license__ = "MIT License" From 109de4c1d738571c1934df9ce80af7bf37a30c79 Mon Sep 17 00:00:00 2001 From: Aleck Landgraf Date: Wed, 25 May 2016 10:43:11 -0700 Subject: [PATCH 26/29] removes crud per PR comments --- analytical/templatetags/woopra.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/analytical/templatetags/woopra.py b/analytical/templatetags/woopra.py index 2269cb9..a18a71d 100644 --- a/analytical/templatetags/woopra.py +++ b/analytical/templatetags/woopra.py @@ -24,13 +24,8 @@ TRACKING_CODE = """ var woo_settings = %(settings)s; var woo_visitor = %(visitor)s; !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 """ From eadd182fd02015ed76ca62c094dd9ae429ed13ae Mon Sep 17 00:00:00 2001 From: Joost Cassee Date: Wed, 25 May 2016 20:22:15 +0200 Subject: [PATCH 27/29] Update changelog and bump version to 2.2.0 --- AUTHORS.rst | 3 ++- CHANGELOG.rst | 4 ++++ analytical/__init__.py | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index d76e06a..9c8b416 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -4,7 +4,7 @@ contributions from `Eric Davis`_, `Paul Oswald`_, `Uros Trebec`_, `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`_, -`Nikolay Korotkiy`_, `Steve Schwarz`_ and others. +`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. @@ -36,6 +36,7 @@ The work on Intercom was made possible by `GreenKahuna`_. .. _`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/ diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 75b02b7..f7b483c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,7 @@ +Version 2.2.0 +------------- +* Update Woopra JavaScript snippet (Aleck Landgraf) + Version 2.1.0 ------------- * Support Rating@Mmail.ru (Nikolay Korotkiy) diff --git a/analytical/__init__.py b/analytical/__init__.py index ae45761..1c312c3 100644 --- a/analytical/__init__.py +++ b/analytical/__init__.py @@ -10,6 +10,6 @@ Django_ project. See the ``docs`` directory for more information. __author__ = "Joost Cassee" __email__ = "joost@cassee.net" -__version__ = "2.1.0" +__version__ = "2.2.0" __copyright__ = "Copyright (C) 2011-2016 Joost Cassee and others" __license__ = "MIT License" From 1cb458fe7c66732c5f47b77a51faa97602b37219 Mon Sep 17 00:00:00 2001 From: Joost Cassee Date: Wed, 25 May 2016 20:26:59 +0200 Subject: [PATCH 28/29] Fix links in Google Analytics documentation --- docs/services/google_analytics.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/services/google_analytics.rst b/docs/services/google_analytics.rst index 7add8b7..c34ba36 100644 --- a/docs/services/google_analytics.rst +++ b/docs/services/google_analytics.rst @@ -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 From 71e8538451bd7cff565044ac213c226917ac6934 Mon Sep 17 00:00:00 2001 From: Nikolay Korotkiy Date: Thu, 26 May 2016 01:47:04 +0300 Subject: [PATCH 29/29] Escape the '@' symbol in doc --- CHANGELOG.rst | 2 +- README.rst | 4 ++-- docs/install.rst | 2 +- docs/services/rating_mailru.rst | 16 ++++++++-------- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f7b483c..bd88d91 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,7 +4,7 @@ Version 2.2.0 Version 2.1.0 ------------- -* Support Rating@Mmail.ru (Nikolay Korotkiy) +* 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) diff --git a/README.rst b/README.rst index 4056e17..5ce9748 100644 --- a/README.rst +++ b/README.rst @@ -68,7 +68,7 @@ Currently Supported Services * `Optimizely`_ A/B testing * `Performable`_ web analytics and landing pages * `Piwik`_ open source web analytics -* `Rating@Mail.ru`_ web analytics +* `Rating\@Mail.ru`_ web analytics * `SnapEngage`_ live chat * `Spring Metrics`_ conversion tracking * `UserVoice`_ user feedback and helpdesk @@ -91,7 +91,7 @@ Currently Supported Services .. _`Optimizely`: http://www.optimizely.com/ .. _`Performable`: http://www.performable.com/ .. _`Piwik`: http://www.piwik.org/ -.. _`Rating@Mail.ru`: http://top.mail.ru/ +.. _`Rating\@Mail.ru`: http://top.mail.ru/ .. _`SnapEngage`: http://www.snapengage.com/ .. _`Spring Metrics`: http://www.springmetrics.com/ .. _`UserVoice`: http://www.uservoice.com/ diff --git a/docs/install.rst b/docs/install.rst index f6695e9..0b25521 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -172,7 +172,7 @@ settings required to enable each service are listed here: PIWIK_DOMAIN_PATH = 'your.piwik.server/optional/path' PIWIK_SITE_ID = '123' -* :doc:`Rating@Mail.ru `:: +* :doc:`Rating\@Mail.ru `:: RATING_MAILRU_COUNTER_ID = '1234567' diff --git a/docs/services/rating_mailru.rst b/docs/services/rating_mailru.rst index c278622..e905e4a 100644 --- a/docs/services/rating_mailru.rst +++ b/docs/services/rating_mailru.rst @@ -1,10 +1,10 @@ =================================== -Rating@Mail.ru -- traffic analysis +Rating\@Mail.ru -- traffic analysis =================================== -`Rating@Mail.ru`_ is an analytics tool like as google analytics. +`Rating\@Mail.ru`_ is an analytics tool like as google analytics. -.. _`Rating@Mail.ru`: http://top.mail.ru/ +.. _`Rating\@Mail.ru`: http://top.mail.ru/ .. rating-mailru-installation: @@ -12,17 +12,17 @@ Rating@Mail.ru -- traffic analysis Installation ============ -To start using the Rating@Mail.ru integration, you must have installed the +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 +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 +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 @@ -42,7 +42,7 @@ the tag at the bottom of the HTML head:: Configuration ============= -Before you can use the Rating@Mail.ru integration, you must first set +Before you can use the Rating\@Mail.ru integration, you must first set your website counter ID. @@ -51,7 +51,7 @@ your website counter ID. Setting the counter ID ---------------------- -Every website you track with Rating@Mail.ru gets its own 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