mirror of
https://github.com/jazzband/django-analytical.git
synced 2026-03-16 22:20:25 +00:00
Initial Google Gtag Analytics
This commit is contained in:
parent
5487fd677b
commit
adee3f722f
4 changed files with 381 additions and 0 deletions
|
|
@ -24,6 +24,7 @@ TAG_MODULES = [
|
|||
'analytical.gauges',
|
||||
'analytical.google_analytics',
|
||||
'analytical.google_analytics_js',
|
||||
'analytical.google_gtag_js',
|
||||
'analytical.gosquared',
|
||||
'analytical.hotjar',
|
||||
'analytical.hubspot',
|
||||
|
|
|
|||
129
analytical/templatetags/google_gtag_js.py
Normal file
129
analytical/templatetags/google_gtag_js.py
Normal file
|
|
@ -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 = """
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id={property_id}"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){{dataLayer.push(arguments);}}
|
||||
gtag('js', new Date());
|
||||
{set_commands}
|
||||
gtag('config', '{property_id}', {config_parameters_json});
|
||||
</script>
|
||||
"""
|
||||
|
||||
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)
|
||||
107
analytical/tests/test_tag_google_gtag_js.py
Normal file
107
analytical/tests/test_tag_google_gtag_js.py
Normal file
|
|
@ -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("""<script async src="https://www.googletagmanager.com/gtag/js?id=UA-123456-7"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());""" in r, r)
|
||||
self.assertTrue("gtag('config', 'UA-123456-7'" in r, r)
|
||||
|
||||
def test_node(self):
|
||||
r = GoogleGTagJsNode().render(Context())
|
||||
self.assertTrue("""<script async src="https://www.googletagmanager.com/gtag/js?id=UA-123456-7"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());""" in r, r)
|
||||
self.assertTrue("gtag('config', 'UA-123456-7'" in r, r)
|
||||
|
||||
@override_settings(GOOGLE_GTAG_JS_PROPERTY_ID=None)
|
||||
def test_no_property_id(self):
|
||||
self.assertRaises(AnalyticalException, GoogleGTagJsNode)
|
||||
|
||||
def test_custom_set_vars(self):
|
||||
context = Context({
|
||||
'google_gtag_js_set1': ('test1', 'foo'),
|
||||
'google_gtag_js_set2': ('test2', 'bar'),
|
||||
'google_gtag_js_set4': ('test4', 1),
|
||||
'google_gtag_js_set5': ('test5', 2.2),
|
||||
})
|
||||
r = GoogleGTagJsNode().render(context)
|
||||
self.assertTrue("""gtag('set', 'test1', "foo");""" in r, r)
|
||||
self.assertTrue("""gtag('set', 'test2', "bar");""" in r, r)
|
||||
self.assertTrue("""gtag('set', 'test4', 1);""" in r, r)
|
||||
self.assertTrue("""gtag('set', 'test5', 2.2);""" in r, r)
|
||||
|
||||
def test_custom_set_data(self):
|
||||
context = Context({
|
||||
'google_gtag_js_set_data': {'test1': 'foo'},
|
||||
})
|
||||
r = GoogleGTagJsNode().render(context)
|
||||
self.assertTrue("""gtag('set', {"test1": "foo"});""" in r, r)
|
||||
|
||||
def test_custom_set_data_not_ignored(self):
|
||||
context = Context({
|
||||
'google_gtag_js_set_data': {'test1': 'foo'},
|
||||
'google_gtag_js_set2': ('test2', 'bar'),
|
||||
})
|
||||
r = GoogleGTagJsNode().render(context)
|
||||
self.assertTrue("""gtag('set', {"test1": "foo"});""" in r, r)
|
||||
self.assertTrue("""gtag('set', 'test2', "bar");""" in r, r)
|
||||
|
||||
def test_custom_config_context_dic(self):
|
||||
context = Context({
|
||||
'google_gtag_js_config_data': {'test1': True},
|
||||
'google_gtag_js_set1': ('shouldnt_affect', 'config'),
|
||||
})
|
||||
r = GoogleGTagJsNode().render(context)
|
||||
self.assertTrue("""gtag('config', 'UA-123456-7', {"test1": true});""" in r, r)
|
||||
|
||||
@override_settings(
|
||||
GOOGLE_GTAG_JS_DEFAULT_CONFIG={'test1': True},
|
||||
)
|
||||
def test_custom_config_defaults_dic(self):
|
||||
context = Context({
|
||||
'google_gtag_js_config_data': {},
|
||||
})
|
||||
r = GoogleGTagJsNode().render(context)
|
||||
self.assertTrue("""gtag('config', 'UA-123456-7', {"test1": true});""" in r, r)
|
||||
|
||||
@override_settings(
|
||||
GOOGLE_GTAG_JS_DEFAULT_CONFIG={'test1': True},
|
||||
)
|
||||
def test_custom_config_context_overrides_defaults(self):
|
||||
context = Context({
|
||||
'google_gtag_js_config_data': {'test1': False},
|
||||
})
|
||||
r = GoogleGTagJsNode().render(context)
|
||||
self.assertTrue("""gtag('config', 'UA-123456-7', {"test1": false});""" in 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 = GoogleGTagJsNode().render(context)
|
||||
self.assertTrue(r.startswith(
|
||||
'<!-- Google Analytics disabled on internal IP address'), r)
|
||||
self.assertTrue(r.endswith('-->'), r)
|
||||
144
docs/services/google_gtag_js.rst
Normal file
144
docs/services/google_gtag_js.rst
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
======================================
|
||||
Google Analytics (gtag.js) -- traffic analysis
|
||||
======================================
|
||||
|
||||
`Google Analytics`_ is the well-known web analytics service from
|
||||
Google. The product is aimed more at marketers than webmasters or
|
||||
technologists, supporting integration with AdWords and other e-commence
|
||||
features.
|
||||
This uses the gtags.js version of google analytics.
|
||||
|
||||
.. _`Google Analytics`: https://developers.google.com/analytics/
|
||||
|
||||
|
||||
.. google-analytics-installation:
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
To start using the Google Analytics 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 Google Analytics 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:`google-analytics-configuration`.
|
||||
|
||||
The Google Analytics tracking code is inserted into templates using a
|
||||
template tag. Load the :mod:`google_gtag_js` template tag library and
|
||||
insert the :ttag:`google_gtag_js` 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 top of the HTML head::
|
||||
|
||||
{% load google_gtag_js %}
|
||||
<html>
|
||||
<head>
|
||||
{% google_gtag_js %}
|
||||
...
|
||||
|
||||
</head>
|
||||
...
|
||||
|
||||
|
||||
.. _google-analytics-configuration:
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
Before you can use the Google Analytics integration, you must first set
|
||||
your website property ID. Then you can set any analytics config variables
|
||||
you wish Google Analytics to track.
|
||||
|
||||
|
||||
.. _google-analytics-property-id:
|
||||
|
||||
Setting the property ID
|
||||
-----------------------
|
||||
|
||||
Every website you track with Google Analytics gets its own property ID,
|
||||
and the :ttag:`google_gtag_js` tag will include it in the rendered
|
||||
Javascript code. You can find the web property ID on the overview page
|
||||
of your account. Set :const:`GOOGLE_GTAG_JS_PROPERTY_ID` in the
|
||||
project :file:`settings.py` file::
|
||||
|
||||
GOOGLE_GTAG_JS_PROPERTY_ID = 'UA-XXXXXX-X'
|
||||
|
||||
If you do not set a property ID, the tracking code will not be rendered.
|
||||
|
||||
|
||||
Google Analytics Tracking Config
|
||||
---------------------
|
||||
|
||||
:ttag:`google_gtag_js` works by letting you use gtag.js 's
|
||||
'set' and 'config' `javascript commands
|
||||
<https://developers.google.com/gtagjs/reference/api>`_
|
||||
.
|
||||
The 'set' gtag commands are inserted before the 'config' commands.
|
||||
|
||||
You are given the option to 'set' gtag values for a request
|
||||
via context variable ``google_gtag_js_set_data``. If used, this should be
|
||||
a json serializable object, to be used like ``gtag('set', <value>)``.
|
||||
|
||||
Additionally, you can use ``google_gtag_js_set1`` though
|
||||
``google_gtag_js_set5``, which should each be key value pairs, to be
|
||||
used like ``gtag('set', <key>, <value>)``.
|
||||
Key should be a string, and value a json serializable object (which includes strings).
|
||||
|
||||
|
||||
At the present, :ttag:`google_gtag_js` only supports
|
||||
configuring one 'GA_MEASUREMENT_ID' property.
|
||||
The options for this config can be set in the following ways:
|
||||
|
||||
1. Via setting: :const:`GOOGLE_GTAG_JS_DEFAULT_CONFIG` - which should
|
||||
be a json serializable dictionary of all default config
|
||||
options. This value will be used as the default config on all pages.
|
||||
2. Via the context variable ``google_gtag_js_config_data``, again a
|
||||
json serializable dictionary.
|
||||
The resultant config options will be made from the default config
|
||||
updated with the values of this 'per request' config.
|
||||
|
||||
Note re config options:
|
||||
Provided you find the right key name, you should be able to configure
|
||||
the gtag tracking however you need it.
|
||||
You can use :file:`settings.py` options such as::
|
||||
|
||||
GOOGLE_GTAG_JS_DEFAULT_CONFIG = {
|
||||
'anonymize_ip': True,
|
||||
'send_page_view': False,
|
||||
'custom_map': {
|
||||
'dimension<Index>': 'dimension_name',
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
||||
You may also like to create a context processor for setting the gtag
|
||||
'set' and 'config' options per request, that you add to the
|
||||
:data:`TEMPLATE_CONTEXT_PROCESSORS` list in :file:`settings.py`, eg::
|
||||
|
||||
def process_google_gtag_options(request):
|
||||
google_gtag_options = {}
|
||||
google_gtag_options['google_gtag_js_set1'] = ('dimension1', request.some_data)
|
||||
google_gtag_options['google_gtag_js_config_data'] = {
|
||||
'currency': 'USD',
|
||||
'country': 'US',
|
||||
'custom_map': {'metric5': 'avg_page_load_time'},
|
||||
}
|
||||
return google_gtag_options
|
||||
|
||||
|
||||
|
||||
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:`GOOGLE_ANALYTICS_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.
|
||||
|
||||
|
||||
Loading…
Reference in a new issue