diff --git a/analytical/templatetags/analytical.py b/analytical/templatetags/analytical.py index f74684e..72b3234 100644 --- a/analytical/templatetags/analytical.py +++ b/analytical/templatetags/analytical.py @@ -24,6 +24,7 @@ 'analytical.gauges', 'analytical.google_analytics', 'analytical.google_analytics_js', + 'analytical.google_gtag_js', 'analytical.gosquared', 'analytical.hotjar', 'analytical.hubspot', diff --git a/analytical/templatetags/google_gtag_js.py b/analytical/templatetags/google_gtag_js.py new file mode 100644 index 0000000..a8c31f5 --- /dev/null +++ b/analytical/templatetags/google_gtag_js.py @@ -0,0 +1,129 @@ +""" +Google Analytics template tags and filters, using the new gtag.js library. + +gtag.js documentation found at: https://developers.google.com/analytics/devguides/collection/gtagjs/ +API reference at: https://developers.google.com/gtagjs/reference/api +""" + +from __future__ import absolute_import + +import re +import json +from django.conf import settings +from django.template import Library, Node, TemplateSyntaxError + +from analytical.utils import ( + disable_html, + get_required_setting, + is_internal_ip, +) + +GA_MEASUREMENT_ID_RE = re.compile(r'[a-zA-Z\d_\-]+') + +SETUP_CODE = """ + + +""" + +CUSTOM_SET_KV_CODE = "gtag('set', '{key}', {value_json});" +CUSTOM_SET_DATA_CODE = "gtag('set', {value_json});" + +# You are allowed to config more than one GA_MEASUREMENT_ID on a page. +# This could be used, but for now is not. +CUSTOM_CONFIG_CODE = "gtag('config', '{property_id}', {config_parameters_json});" + +register = Library() + + +@register.tag +def google_gtag_js(parser, token): + """ + Google Analytics Global Site Tag tracking template tag. + + Renders Javascript code to track page visits. You must supply + your website property ID (as a string) in the + ``GOOGLE_GTAG_JS_PROPERTY_ID`` setting. + """ + bits = token.split_contents() + if len(bits) > 1: + raise TemplateSyntaxError("'%s' takes no arguments" % bits[0]) + return GoogleGTagJsNode() + + +class GoogleGTagJsNode(Node): + def __init__(self): + self.property_id = get_required_setting( + 'GOOGLE_GTAG_JS_PROPERTY_ID', GA_MEASUREMENT_ID_RE, + "must be a string like a slug") + + def render(self, context): + config_parameters = self._get_config_parameters(context) + config_parameters_json = self._to_json(config_parameters) + + set_commands = self._get_set_commands(context) + + html = SETUP_CODE.format( + property_id=self.property_id, + config_parameters_json=config_parameters_json, + set_commands=" ".join(set_commands), + ) + if is_internal_ip(context, 'GOOGLE_ANALYTICS'): + html = disable_html(html, 'Google Analytics') + return html + + def _get_config_parameters(self, context): + config_data = getattr( + settings, 'GOOGLE_GTAG_JS_DEFAULT_CONFIG', {}, + ) + + config_data.update(context.get('google_gtag_js_config_data', {})) + + return config_data + + def _to_json(self, data, default="{}"): + try: + return json.dumps(data) + except ValueError: + return default + except TypeError: + return default + + def _get_set_commands(self, context): + commands = [] + + if 'google_gtag_js_set_data' in context: + try: + commands.append(CUSTOM_SET_DATA_CODE.format( + value_json=json.dumps(context['google_gtag_js_set_data']), + )) + except ValueError: + pass + + values = ( + context.get('google_gtag_js_set%s' % i) for i in range(1, 6) + ) + params = [(i, v) for i, v in enumerate(values, 1) if v is not None] + + for _, var in params: + key_name = var[0] + value = var[1] + try: + value_json = json.dumps(value) + except ValueError: + value_json = json.dumps(str(value)) + commands.append(CUSTOM_SET_KV_CODE.format( + key=key_name, + value_json=value_json, + )) + return commands + + +def contribute_to_analytical(add_node): + GoogleGTagJsNode() # ensure properly configured + add_node('head_top', GoogleGTagJsNode) diff --git a/analytical/tests/test_tag_google_gtag_js.py b/analytical/tests/test_tag_google_gtag_js.py new file mode 100644 index 0000000..1a80d26 --- /dev/null +++ b/analytical/tests/test_tag_google_gtag_js.py @@ -0,0 +1,107 @@ +""" +Tests for the Google Analytics template tags and filters, using the new analytics.js library. +""" + +from django.http import HttpRequest +from django.template import Context +from django.test.utils import override_settings + +from analytical.templatetags.google_gtag_js import GoogleGTagJsNode +from analytical.tests.utils import TagTestCase +from analytical.utils import AnalyticalException + + +@override_settings(GOOGLE_GTAG_JS_PROPERTY_ID='UA-123456-7') +class GoogleGTagJsTagTestCase(TagTestCase): + """ + Tests for the ``google_gtag_js`` template tag. + """ + + def test_tag(self): + r = self.render_tag('google_gtag_js', 'google_gtag_js') + self.assertTrue(""" + +