Added Heap integration (#194)

Heap integration
This commit is contained in:
Garrett Coakley 2021-11-26 13:13:59 +00:00 committed by GitHub
parent aa29a051bd
commit 58aa95df34
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 169 additions and 1 deletions

View file

@ -57,6 +57,7 @@ Currently Supported Services
* `Gaug.es`_ real time web analytics
* `Google Analytics`_ traffic analysis
* `GoSquared`_ traffic monitoring
* `Heap`_ analytics and events tracking
* `Hotjar`_ analytics and user feedback
* `HubSpot`_ inbound marketing
* `Intercom`_ live chat and support
@ -83,6 +84,7 @@ Currently Supported Services
.. _`Gaug.es`: http://get.gaug.es/
.. _`Google Analytics`: http://www.google.com/analytics/
.. _`GoSquared`: http://www.gosquared.com/
.. _`Heap`: https://heapanalytics.com/
.. _`Hotjar`: https://www.hotjar.com/
.. _`HubSpot`: http://www.hubspot.com/
.. _`Intercom`: http://www.intercom.io/

View file

@ -23,6 +23,7 @@ TAG_MODULES = [
'analytical.google_analytics_js',
'analytical.google_analytics_gtag',
'analytical.gosquared',
'analytical.heap',
'analytical.hotjar',
'analytical.hubspot',
'analytical.intercom',

View file

@ -0,0 +1,57 @@
"""
Heap 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
HEAP_TRACKER_ID_RE = re.compile(r'^\d+$')
TRACKING_CODE = """
<script type="text/javascript">
window.heap=window.heap||[],heap.load=function(e,t){window.heap.appid=e,window.heap.config=t=t||{};var r=document.createElement("script");r.type="text/javascript",r.async=!0,r.src="https://cdn.heapanalytics.com/js/heap-"+e+".js";var a=document.getElementsByTagName("script")[0];a.parentNode.insertBefore(r,a);for(var n=function(e){return function(){heap.push([e].concat(Array.prototype.slice.call(arguments,0)))}},p=["addEventProperties","addUserProperties","clearEventProperties","identify","resetIdentity","removeEventProperty","setEventProperties","track","unsetEventProperty"],o=0;o<p.length;o++)heap[p[o]]=n(p[o])};
heap.load("%(tracker_id)s");
</script>
""" # noqa
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 heap(parser, token):
"""
Heap tracker template tag.
Renders Javascript code to track page visits. You must supply
your heap tracker ID (as a string) in the ``HEAP_TRACKER_ID``
setting.
"""
_validate_no_args(token)
return HeapNode()
class HeapNode(Node):
def __init__(self):
self.tracker_id = get_required_setting('HEAP_TRACKER_ID',
HEAP_TRACKER_ID_RE,
"must be an numeric string")
def render(self, context):
html = TRACKING_CODE % {'tracker_id': self.tracker_id}
if is_internal_ip(context, 'HEAP'):
html = disable_html(html, 'Heap')
return html
def contribute_to_analytical(add_node):
HeapNode() # ensure properly configured
add_node('head_bottom', HeapNode)

View file

@ -10,7 +10,6 @@ sys.path.append(os.path.dirname(os.path.abspath('.')))
import analytical # noqa
# -- General configuration --------------------------------------------------
project = 'django-analytical'

59
docs/services/heap.rst Normal file
View file

@ -0,0 +1,59 @@
=====================================
Heap -- analytics and events tracking
=====================================
`Heap`_ automatically captures all user interactions on your site, from the moment of installation forward.
.. _`Heap`: https://heap.io/
.. heap-installation:
Installation
============
To start using the Heap 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.
.. _heap-configuration:
Configuration
=============
Before you can use the Heap integration, you must first get your
Heap Tracker ID. If you don't have a Heap account yet,
`sign up`_ to get your Tracker ID.
.. _`sign up`: https://heap.io/
.. _heap-tracker-id:
Setting the Tracker ID
----------------------
Heap gives you a unique ID. You can find this ID on the Projects page
of your Heap account. Set :const:`HEAP_TRACKER_ID` in the project
:file:`settings.py` file::
HEAP_TRACKER_ID = 'XXXXXXXX'
If you do not set an Tracker ID, the tracking code will not be
rendered.
The tracking code will be added just before the closing head tag.
.. _heap-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:`ANALYTICAL_INTERNAL_IPS` setting
(which is :const:`INTERNAL_IPS` by default,) the tracking code is
commented out. See :ref:`identifying-visitors` for important information
about detecting the visitor IP address.

View file

@ -0,0 +1,50 @@
"""
Tests for the Heap template tags and filters.
"""
import pytest
from django.http import HttpRequest
from django.template import Context, Template, TemplateSyntaxError
from django.test.utils import override_settings
from utils import TagTestCase
from analytical.templatetags.heap import HeapNode
from analytical.utils import AnalyticalException
@override_settings(HEAP_TRACKER_ID='123456789')
class HeapTagTestCase(TagTestCase):
"""
Tests for the ``heap`` template tag.
"""
def test_tag(self):
r = self.render_tag('heap', 'heap')
assert "123456789" in r
def test_node(self):
r = HeapNode().render(Context({}))
assert "123456789" in r
def test_tags_take_no_args(self):
with pytest.raises(TemplateSyntaxError, match="'heap' takes no arguments"):
Template('{% load heap %}{% heap "arg" %}').render(Context({}))
@override_settings(HEAP_TRACKER_ID=None)
def test_no_site_id(self):
with pytest.raises(AnalyticalException):
HeapNode()
@override_settings(HEAP_TRACKER_ID='abcdefg')
def test_wrong_site_id(self):
with pytest.raises(AnalyticalException):
HeapNode()
@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 = HeapNode().render(context)
assert r.startswith('<!-- Heap disabled on internal IP address')
assert r.endswith('-->')