diff --git a/analytical/templatetags/google_analytics_gtag.py b/analytical/templatetags/google_analytics_gtag.py index f83f835..07c5a00 100644 --- a/analytical/templatetags/google_analytics_gtag.py +++ b/analytical/templatetags/google_analytics_gtag.py @@ -1,7 +1,9 @@ """ -Google Analytics template tags and filters, using the new analytics.js library. +Google Analytics template tags and filters, using the new gtag.js library. +https://developers.google.com/tag-platform/gtagjs/reference """ +import json import re from django.template import Library, Node, TemplateSyntaxError @@ -23,13 +25,10 @@ SETUP_CODE = """ function gtag(){{dataLayer.push(arguments);}} gtag('js', new Date()); - {extra} - gtag('config', '{property_id}'); + gtag('config', '{property_id}', {custom_dimensions}); """ -GTAG_SET_CODE = """gtag('set', {{'{key}': '{value}'}});""" - register = Library() @@ -59,21 +58,15 @@ class GoogleAnalyticsGTagNode(Node): ) def render(self, context): - other_fields = {} + custom_dimensions = context.get('google_analytics_custom_dimensions', {}) - identity = get_identity(context, 'google_analytics_gtag') + identity = get_identity(context, prefix='google_analytics_gtag') if identity is not None: - other_fields['user_id'] = identity + custom_dimensions['user_id'] = identity - extra = '\n'.join( - [ - GTAG_SET_CODE.format(key=key, value=value) - for key, value in other_fields.items() - ] - ) html = SETUP_CODE.format( property_id=self.property_id, - extra=extra, + custom_dimensions=json.dumps(custom_dimensions), ) if is_internal_ip(context, 'GOOGLE_ANALYTICS'): html = disable_html(html, 'Google Analytics') diff --git a/docs/services/google_analytics_gtag.rst b/docs/services/google_analytics_gtag.rst index a5f6f24..9760930 100644 --- a/docs/services/google_analytics_gtag.rst +++ b/docs/services/google_analytics_gtag.rst @@ -117,3 +117,52 @@ or in the template: {% endwith %} .. _`Google Analytics conditions`: https://developers.google.com/analytics/solutions/crm-integration#user_id + +.. _google-analytics-custom-dimensions: + +Custom dimensions +---------------- + +As described in the Google Analytics `custom dimensions`_ documentation +page, you can define custom dimensions which are variables specific to your +business needs. These variables can include both custom event parameters as +well as customer user properties. Using the template context variable +``google_analytics_custom_dimensions``, you can let the :ttag:`google_analytics_gtag` +pass custom dimensions to Google Analytics automatically. The ``google_analytics_custom_dimensions`` +variable must be set to a dictionary where the keys are the dimension names +and the values are the dimension values. You can set the context variable in your +view when you render a template containing the tracking code:: + +.. code-block:: python + + context = RequestContext({ + 'google_analytics_custom_dimensions': { + 'gender': 'female', + 'country': 'US', + 'user_properties': { + 'age': 25 + } + } + }) + return some_template.render(context) + +Note that the ``user_properties`` key is used to pass user properties to Google +Analytics. It's not necessary to always use this key, but that'd be the way of +sending user properties to Google Analytics automatically. + +You may want to set custom dimensions in a context processor that you add +to the :data:`TEMPLATE_CONTEXT_PROCESSORS` list in :file:`settings.py`:: + +.. code-block:: python + + def google_analytics_segment_language(request): + try: + return {'google_analytics_custom_dimensions': {'language': request.LANGUAGE_CODE}} + 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. + +.. _`custom dimensions`: https://support.google.com/analytics/answer/10075209 diff --git a/tests/unit/test_tag_google_analytics_gtag.py b/tests/unit/test_tag_google_analytics_gtag.py index 16c356f..c84c327 100644 --- a/tests/unit/test_tag_google_analytics_gtag.py +++ b/tests/unit/test_tag_google_analytics_gtag.py @@ -16,7 +16,7 @@ from analytical.utils import AnalyticalException @override_settings(GOOGLE_ANALYTICS_GTAG_PROPERTY_ID='UA-123456-7') class GoogleAnalyticsTagTestCase(TagTestCase): """ - Tests for the ``google_analytics_js`` template tag. + Tests for the ``google_analytics_gtag`` template tag. """ def test_tag(self): @@ -25,7 +25,7 @@ class GoogleAnalyticsTagTestCase(TagTestCase): '' ) in r assert "gtag('js', new Date());" in r - assert "gtag('config', 'UA-123456-7');" in r + assert "gtag('config', 'UA-123456-7', {});" in r def test_node(self): r = GoogleAnalyticsGTagNode().render(Context()) @@ -33,7 +33,7 @@ class GoogleAnalyticsTagTestCase(TagTestCase): '' ) in r assert "gtag('js', new Date());" in r - assert "gtag('config', 'UA-123456-7');" in r + assert "gtag('config', 'UA-123456-7', {});" in r @override_settings(GOOGLE_ANALYTICS_GTAG_PROPERTY_ID=None) def test_no_property_id(self): @@ -57,7 +57,7 @@ class GoogleAnalyticsTagTestCase(TagTestCase): @override_settings(ANALYTICAL_AUTO_IDENTIFY=True) def test_identify(self): r = GoogleAnalyticsGTagNode().render(Context({'user': User(username='test')})) - assert "gtag('set', {'user_id': 'test'});" in r + assert 'gtag(\'config\', \'UA-123456-7\', {"user_id": "test"});' in r def test_identity_context_specific_provider(self): """ @@ -68,12 +68,13 @@ class GoogleAnalyticsTagTestCase(TagTestCase): Context( { 'google_analytics_gtag_identity': 'foo_gtag_identity', - 'analytical_identity': 'bar_analytical_identity', 'user': User(username='test'), } ) ) - assert "gtag('set', {'user_id': 'foo_gtag_identity'});" in r + assert ( + 'gtag(\'config\', \'UA-123456-7\', {"user_id": "foo_gtag_identity"});' in r + ) def test_identity_context_general(self): """ @@ -87,7 +88,10 @@ class GoogleAnalyticsTagTestCase(TagTestCase): } ) ) - assert "gtag('set', {'user_id': 'bar_analytical_identity'});" in r + assert ( + 'gtag(\'config\', \'UA-123456-7\', {"user_id": "bar_analytical_identity"});' + in r + ) @override_settings(GOOGLE_ANALYTICS_GTAG_PROPERTY_ID='G-12345678') def test_tag_with_measurement_id(self): @@ -96,7 +100,7 @@ class GoogleAnalyticsTagTestCase(TagTestCase): '' ) in r assert "gtag('js', new Date());" in r - assert "gtag('config', 'G-12345678');" in r + assert "gtag('config', 'G-12345678', {});" in r @override_settings(GOOGLE_ANALYTICS_GTAG_PROPERTY_ID='AW-1234567890') def test_tag_with_conversion_id(self): @@ -105,7 +109,7 @@ class GoogleAnalyticsTagTestCase(TagTestCase): '' ) in r assert "gtag('js', new Date());" in r - assert "gtag('config', 'DC-12345678');" in r + assert "gtag('config', 'DC-12345678', {});" in r + + def test_tag_with_custom_dimensions(self): + r = GoogleAnalyticsGTagNode().render( + Context( + { + 'google_analytics_custom_dimensions': { + 'dimension_1': 'foo', + 'dimension_2': 'bar', + 'user_properties': { + 'user_property_1': True, + 'user_property_2': 'xyz', + }, + }, + } + ) + ) + assert ( + "gtag('config', 'UA-123456-7', {" + '"dimension_1": "foo", ' + '"dimension_2": "bar", ' + '"user_properties": {' + '"user_property_1": true, ' + '"user_property_2": "xyz"}});' in r + ) + + def test_tag_with_identity_and_custom_dimensions(self): + r = GoogleAnalyticsGTagNode().render( + Context( + { + 'google_analytics_gtag_identity': 'foo_gtag_identity', + 'google_analytics_custom_dimensions': { + 'dimension_1': 'foo', + 'dimension_2': 'bar', + }, + } + ) + ) + assert ( + "gtag('config', 'UA-123456-7', {" + '"dimension_1": "foo", ' + '"dimension_2": "bar", ' + '"user_id": "foo_gtag_identity"});' in r + )