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.