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.