mirror of
https://github.com/jazzband/django-analytical.git
synced 2026-03-16 22:20:25 +00:00
Merge fafe73a2a1 into e6f12719cc
This commit is contained in:
commit
c836e48947
4 changed files with 219 additions and 0 deletions
|
|
@ -25,6 +25,7 @@ TAG_MODULES = [
|
|||
'analytical.gosquared',
|
||||
'analytical.heap',
|
||||
'analytical.hotjar',
|
||||
'analytical.contentsquare',
|
||||
'analytical.hubspot',
|
||||
'analytical.intercom',
|
||||
'analytical.kiss_insights',
|
||||
|
|
|
|||
63
analytical/templatetags/contentsquare.py
Normal file
63
analytical/templatetags/contentsquare.py
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
"""
|
||||
Contentsquare template tags and filters.
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from django.template import Library, Node, TemplateSyntaxError
|
||||
|
||||
from analytical.utils import disable_html, get_required_setting, is_internal_ip
|
||||
|
||||
CONTENTSQUARE_TRACKING_CODE = """\
|
||||
<script>
|
||||
(function(c,s,q,u,a,r,e){
|
||||
c.hj=c.hj||function(){(c.hj.q=c.hj.q||[]).push(arguments)};
|
||||
c._hjSettings={hjid:a};
|
||||
r=s.getElementsByTagName('head')[0];
|
||||
e=s.createElement('script');
|
||||
e.async=true;
|
||||
e.src=q+c._hjSettings.hjid+u;
|
||||
r.appendChild(e);
|
||||
})(window,document,'https://static.hj.contentsquare.net/c/csq-','.js',%(CONTENTSQUARE_SITE_ID)s);
|
||||
</script>
|
||||
"""
|
||||
|
||||
register = Library()
|
||||
|
||||
|
||||
def _validate_no_args(token):
|
||||
bits = token.split_contents()
|
||||
if len(bits) > 1:
|
||||
raise TemplateSyntaxError("'%s' takes no arguments" % bits[0])
|
||||
|
||||
|
||||
@register.tag
|
||||
def contentsquare(parser, token):
|
||||
"""
|
||||
Contentsquare template tag.
|
||||
"""
|
||||
_validate_no_args(token)
|
||||
return ContentsquareNode()
|
||||
|
||||
|
||||
class ContentsquareNode(Node):
|
||||
|
||||
def __init__(self):
|
||||
self.site_id = get_required_setting(
|
||||
'CONTENTSQUARE_SITE_ID',
|
||||
re.compile(r'^\d+$'),
|
||||
"must be (a string containing) a number",
|
||||
)
|
||||
|
||||
def render(self, context):
|
||||
html = CONTENTSQUARE_TRACKING_CODE % {'CONTENTSQUARE_SITE_ID': self.site_id}
|
||||
if is_internal_ip(context, 'CONTENTSQUARE'):
|
||||
return disable_html(html, 'Contentsquare')
|
||||
else:
|
||||
return html
|
||||
|
||||
|
||||
def contribute_to_analytical(add_node):
|
||||
# ensure properly configured
|
||||
ContentsquareNode()
|
||||
add_node('head_bottom', ContentsquareNode)
|
||||
74
docs/services/content_square.rst
Normal file
74
docs/services/content_square.rst
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
=====================================
|
||||
Contentsquare -- enterprise digital experience analytics
|
||||
=====================================
|
||||
|
||||
`Contentsquare`_ is an enterprise digital experience analytics platform that provides comprehensive insights into user behavior across web, mobile, and other digital touchpoints.
|
||||
|
||||
.. _`Contentsquare`: https://contentsquare.com/
|
||||
|
||||
|
||||
.. contentsquare-installation:
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
To start using the Contentsquare 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 Contentsquare 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:`contentsquare-configuration`.
|
||||
|
||||
The Contentsquare code is inserted into templates using template tags.
|
||||
Because every page that you want to track must have the tag,
|
||||
it is useful to add it to your base template.
|
||||
At the top of the template, load the :mod:`contentsquare` template tag library.
|
||||
Then insert the :ttag:`contentsquare` tag at the bottom of the head section::
|
||||
|
||||
{% load contentsquare %}
|
||||
<html>
|
||||
<head>
|
||||
...
|
||||
{% contentsquare %}
|
||||
</head>
|
||||
...
|
||||
</html>
|
||||
|
||||
|
||||
.. _contentsquare-configuration:
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
Before you can use the Contentsquare integration, you must first set your Site ID.
|
||||
|
||||
|
||||
.. _contentsquare-id:
|
||||
|
||||
Setting the Contentsquare Site ID
|
||||
----------------------------------
|
||||
|
||||
You can find the Contentsquare Site ID in the "Sites & Organizations" section of your Contentsquare account.
|
||||
Set :const:`CONTENTSQUARE_SITE_ID` in the project :file:`settings.py` file::
|
||||
|
||||
CONTENTSQUARE_SITE_ID = 'XXXXXXXXX'
|
||||
|
||||
If you do not set a Contentsquare Site ID, the tracking code will not be rendered.
|
||||
|
||||
|
||||
.. _contentsquare-internal-ips:
|
||||
|
||||
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:`CONTENTSQUARE_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.
|
||||
|
||||
81
tests/unit/test_tag_content_square.py
Normal file
81
tests/unit/test_tag_content_square.py
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
"""
|
||||
Tests for the Contentsquare template tags.
|
||||
"""
|
||||
import pytest
|
||||
from django.http import HttpRequest
|
||||
from django.template import Context, Template, TemplateSyntaxError
|
||||
from django.test import override_settings
|
||||
from utils import TagTestCase
|
||||
|
||||
from analytical.templatetags.analytical import _load_template_nodes
|
||||
from analytical.templatetags.contentsquare import ContentsquareNode
|
||||
from analytical.utils import AnalyticalException
|
||||
|
||||
expected_html = """\
|
||||
<script>
|
||||
(function(c,s,q,u,a,r,e){
|
||||
c.hj=c.hj||function(){(c.hj.q=c.hj.q||[]).push(arguments)};
|
||||
c._hjSettings={hjid:a};
|
||||
r=s.getElementsByTagName('head')[0];
|
||||
e=s.createElement('script');
|
||||
e.async=true;
|
||||
e.src=q+c._hjSettings.hjid+u;
|
||||
r.appendChild(e);
|
||||
})(window,document,'https://static.hj.contentsquare.net/c/csq-','.js',123456789);
|
||||
</script>
|
||||
"""
|
||||
|
||||
@override_settings(CONTENTSQUARE_SITE_ID='123456789')
|
||||
class ContentsquareTagTestCase(TagTestCase):
|
||||
|
||||
maxDiff = None
|
||||
|
||||
def test_tag(self):
|
||||
html = self.render_tag('contentsquare', 'contentsquare')
|
||||
assert expected_html == html
|
||||
|
||||
def test_node(self):
|
||||
html = ContentsquareNode().render(Context({}))
|
||||
assert expected_html == html
|
||||
|
||||
def test_tags_take_no_args(self):
|
||||
with pytest.raises(TemplateSyntaxError, match="'contentsquare' takes no arguments"):
|
||||
Template('{% load contentsquare %}{% contentsquare "arg" %}').render(Context({}))
|
||||
|
||||
@override_settings(CONTENTSQUARE_SITE_ID=None)
|
||||
def test_no_id(self):
|
||||
with pytest.raises(AnalyticalException, match="CONTENTSQUARE_SITE_ID setting is not set"):
|
||||
ContentsquareNode()
|
||||
|
||||
@override_settings(CONTENTSQUARE_SITE_ID='invalid')
|
||||
def test_invalid_id(self):
|
||||
expected_pattern = (
|
||||
r"^CONTENTSQUARE_SITE_ID setting: must be \(a string containing\) a number: 'invalid'$")
|
||||
with pytest.raises(AnalyticalException, match=expected_pattern):
|
||||
ContentsquareNode()
|
||||
|
||||
@override_settings(ANALYTICAL_INTERNAL_IPS=['1.1.1.1'])
|
||||
def test_render_internal_ip(self):
|
||||
request = HttpRequest()
|
||||
request.META['REMOTE_ADDR'] = '1.1.1.1'
|
||||
context = Context({'request': request})
|
||||
|
||||
actual_html = ContentsquareNode().render(context)
|
||||
disabled_html = '\n'.join([
|
||||
'<!-- Contentsquare disabled on internal IP address',
|
||||
expected_html,
|
||||
'-->',
|
||||
])
|
||||
assert disabled_html == actual_html
|
||||
|
||||
def test_contribute_to_analytical(self):
|
||||
"""
|
||||
`contentsquare.contribute_to_analytical` registers the head and body nodes.
|
||||
"""
|
||||
template_nodes = _load_template_nodes()
|
||||
assert template_nodes == {
|
||||
'head_top': [],
|
||||
'head_bottom': [ContentsquareNode],
|
||||
'body_top': [],
|
||||
'body_bottom': [],
|
||||
}
|
||||
Loading…
Reference in a new issue