mirror of
https://github.com/jazzband/django-analytical.git
synced 2026-03-16 22:20:25 +00:00
Merge pull request #40 from bittner/master
Added Piwik open source web analytics
This commit is contained in:
commit
bf0da79846
8 changed files with 266 additions and 3 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,3 +1,5 @@
|
|||
/*.geany
|
||||
/.idea
|
||||
/.tox
|
||||
|
||||
/build
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ Currently supported services:
|
|||
* `Olark`_ visitor chat
|
||||
* `Optimizely`_ A/B testing
|
||||
* `Performable`_ web analytics and landing pages
|
||||
* `Piwik`_ open source web analytics
|
||||
* `Reinvigorate`_ visitor tracking
|
||||
* `SnapEngage`_ live chat
|
||||
* `Spring Metrics`_ conversion tracking
|
||||
|
|
@ -67,6 +68,7 @@ an issue to discuss your plans.
|
|||
.. _`Olark`: http://www.olark.com/
|
||||
.. _`Optimizely`: http://www.optimizely.com/
|
||||
.. _`Performable`: http://www.performable.com/
|
||||
.. _`Piwik`: http://www.piwik.org/
|
||||
.. _`Reinvigorate`: http://www.reinvigorate.net/
|
||||
.. _`SnapEngage`: http://www.snapengage.com/
|
||||
.. _`Spring Metrics`: http://www.springmetrics.com/
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ TAG_MODULES = [
|
|||
'analytical.olark',
|
||||
'analytical.optimizely',
|
||||
'analytical.performable',
|
||||
'analytical.piwik',
|
||||
'analytical.reinvigorate',
|
||||
'analytical.snapengage',
|
||||
'analytical.spring_metrics',
|
||||
|
|
@ -37,7 +38,6 @@ TAG_MODULES = [
|
|||
'analytical.woopra',
|
||||
]
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
register = template.Library()
|
||||
|
||||
|
|
@ -48,8 +48,10 @@ def _location_tag(location):
|
|||
if len(bits) > 1:
|
||||
raise TemplateSyntaxError("'%s' tag takes no arguments" % bits[0])
|
||||
return AnalyticalNode(location)
|
||||
|
||||
return analytical_tag
|
||||
|
||||
|
||||
for loc in TAG_LOCATIONS:
|
||||
register.tag('analytical_%s' % loc, _location_tag(loc))
|
||||
|
||||
|
|
@ -64,9 +66,11 @@ class AnalyticalNode(Node):
|
|||
|
||||
def _load_template_nodes():
|
||||
template_nodes = dict((l, dict((p, []) for p in TAG_POSITIONS))
|
||||
for l in TAG_LOCATIONS)
|
||||
for l in TAG_LOCATIONS)
|
||||
|
||||
def add_node_cls(location, node, position=None):
|
||||
template_nodes[location][position].append(node)
|
||||
|
||||
for path in TAG_MODULES:
|
||||
module = _import_tag_module(path)
|
||||
try:
|
||||
|
|
@ -75,11 +79,13 @@ def _load_template_nodes():
|
|||
logger.debug("not loading tags from '%s': %s", path, e)
|
||||
for location in TAG_LOCATIONS:
|
||||
template_nodes[location] = sum((template_nodes[location][p]
|
||||
for p in TAG_POSITIONS), [])
|
||||
for p in TAG_POSITIONS), [])
|
||||
return template_nodes
|
||||
|
||||
|
||||
def _import_tag_module(path):
|
||||
app_name, lib_name = path.rsplit('.', 1)
|
||||
return import_module("%s.templatetags.%s" % (app_name, lib_name))
|
||||
|
||||
|
||||
template_nodes = _load_template_nodes()
|
||||
|
|
|
|||
78
analytical/templatetags/piwik.py
Normal file
78
analytical/templatetags/piwik.py
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
"""
|
||||
Piwik template tags and filters.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import re
|
||||
|
||||
from django.template import Library, Node, TemplateSyntaxError
|
||||
|
||||
from analytical.utils import is_internal_ip, disable_html, get_required_setting
|
||||
|
||||
|
||||
# domain name (characters separated by a dot), optional URI path, no slash
|
||||
DOMAINPATH_RE = re.compile(r'^(([^./?#@:]+\.)+[^./?#@:]+)+(/[^/?#@:]+)*$')
|
||||
|
||||
# numeric ID
|
||||
SITEID_RE = re.compile(r'^\d+$')
|
||||
|
||||
TRACKING_CODE = """
|
||||
<script type="text/javascript">
|
||||
var _paq = _paq || [];
|
||||
_paq.push(['trackPageView']);
|
||||
_paq.push(['enableLinkTracking']);
|
||||
(function() {
|
||||
var u=(("https:" == document.location.protocol) ? "https" : "http") + "://%(url)s/";
|
||||
_paq.push(['setTrackerUrl', u+'piwik.php']);
|
||||
_paq.push(['setSiteId', %(siteid)s]);
|
||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; g.type='text/javascript';
|
||||
g.defer=true; g.async=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
|
||||
})();
|
||||
</script>
|
||||
<noscript><p><img src="http://%(url)s/piwik.php?idsite=%(siteid)s" style="border:0;" alt="" /></p></noscript>
|
||||
""" # noqa
|
||||
|
||||
|
||||
register = Library()
|
||||
|
||||
|
||||
@register.tag
|
||||
def piwik(parser, token):
|
||||
"""
|
||||
Piwik tracking template tag.
|
||||
|
||||
Renders Javascript code to track page visits. You must supply
|
||||
your Piwik domain (plus optional URI path), and tracked site ID
|
||||
in the ``PIWIK_DOMAIN_PATH`` and the ``PIWIK_SITE_ID`` setting.
|
||||
"""
|
||||
bits = token.split_contents()
|
||||
if len(bits) > 1:
|
||||
raise TemplateSyntaxError("'%s' takes no arguments" % bits[0])
|
||||
return PiwikNode()
|
||||
|
||||
|
||||
class PiwikNode(Node):
|
||||
def __init__(self):
|
||||
self.domain_path = \
|
||||
get_required_setting('PIWIK_DOMAIN_PATH', DOMAINPATH_RE,
|
||||
"must be a domain name, optionally followed "
|
||||
"by an URI path, no trailing slash (e.g. "
|
||||
"piwik.example.com or my.piwik.server/path)")
|
||||
self.site_id = \
|
||||
get_required_setting('PIWIK_SITE_ID', SITEID_RE,
|
||||
"must be a (string containing a) number")
|
||||
|
||||
def render(self, context):
|
||||
html = TRACKING_CODE % {
|
||||
'url': self.domain_path,
|
||||
'siteid': self.site_id,
|
||||
}
|
||||
if is_internal_ip(context, 'PIWIK'):
|
||||
html = disable_html(html, 'Piwik')
|
||||
return html
|
||||
|
||||
|
||||
def contribute_to_analytical(add_node):
|
||||
PiwikNode() # ensure properly configured
|
||||
add_node('body_bottom', PiwikNode)
|
||||
|
|
@ -18,6 +18,7 @@ from analytical.tests.test_tag_mixpanel import *
|
|||
from analytical.tests.test_tag_olark import *
|
||||
from analytical.tests.test_tag_optimizely import *
|
||||
from analytical.tests.test_tag_performable import *
|
||||
from analytical.tests.test_tag_piwik import *
|
||||
from analytical.tests.test_tag_reinvigorate import *
|
||||
from analytical.tests.test_tag_snapengage import *
|
||||
from analytical.tests.test_tag_spring_metrics import *
|
||||
|
|
|
|||
69
analytical/tests/test_tag_piwik.py
Normal file
69
analytical/tests/test_tag_piwik.py
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
"""
|
||||
Tests for the Piwik template tags and filters.
|
||||
"""
|
||||
|
||||
from django.http import HttpRequest
|
||||
from django.template import Context
|
||||
|
||||
from analytical.templatetags.piwik import PiwikNode
|
||||
from analytical.tests.utils import TagTestCase, override_settings, \
|
||||
SETTING_DELETED
|
||||
from analytical.utils import AnalyticalException
|
||||
|
||||
|
||||
@override_settings(PIWIK_DOMAIN_PATH='example.com', PIWIK_SITE_ID='345')
|
||||
class PiwikTagTestCase(TagTestCase):
|
||||
"""
|
||||
Tests for the ``piwik`` template tag.
|
||||
"""
|
||||
|
||||
def test_tag(self):
|
||||
r = self.render_tag('piwik', 'piwik')
|
||||
self.assertTrue(' ? "https" : "http") + "://example.com/";' in r, r)
|
||||
self.assertTrue("_paq.push(['setSiteId', 345]);" in r, r)
|
||||
self.assertTrue('img src="http://example.com/piwik.php?idsite=345"'
|
||||
in r, r)
|
||||
|
||||
def test_node(self):
|
||||
r = PiwikNode().render(Context({}))
|
||||
self.assertTrue(' ? "https" : "http") + "://example.com/";' in r, r)
|
||||
self.assertTrue("_paq.push(['setSiteId', 345]);" in r, r)
|
||||
self.assertTrue('img src="http://example.com/piwik.php?idsite=345"'
|
||||
in r, r)
|
||||
|
||||
@override_settings(PIWIK_DOMAIN_PATH='example.com/piwik',
|
||||
PIWIK_SITE_ID='345')
|
||||
def test_domain_path_valid(self):
|
||||
r = self.render_tag('piwik', 'piwik')
|
||||
self.assertTrue(' ? "https" : "http") + "://example.com/piwik/";' in r,
|
||||
r)
|
||||
|
||||
@override_settings(PIWIK_DOMAIN_PATH=SETTING_DELETED)
|
||||
def test_no_domain(self):
|
||||
self.assertRaises(AnalyticalException, PiwikNode)
|
||||
|
||||
@override_settings(PIWIK_SITE_ID=SETTING_DELETED)
|
||||
def test_no_siteid(self):
|
||||
self.assertRaises(AnalyticalException, PiwikNode)
|
||||
|
||||
@override_settings(PIWIK_SITE_ID='x')
|
||||
def test_siteid_not_a_number(self):
|
||||
self.assertRaises(AnalyticalException, PiwikNode)
|
||||
|
||||
@override_settings(PIWIK_DOMAIN_PATH='http://www.example.com')
|
||||
def test_domain_protocol_invalid(self):
|
||||
self.assertRaises(AnalyticalException, PiwikNode)
|
||||
|
||||
@override_settings(PIWIK_DOMAIN_PATH='example.com/')
|
||||
def test_domain_slash_invalid(self):
|
||||
self.assertRaises(AnalyticalException, PiwikNode)
|
||||
|
||||
@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 = PiwikNode().render(context)
|
||||
self.assertTrue(r.startswith(
|
||||
'<!-- Piwik disabled on internal IP address'), r)
|
||||
self.assertTrue(r.endswith('-->'), r)
|
||||
|
|
@ -157,6 +157,11 @@ settings required to enable each service are listed here:
|
|||
|
||||
PERFORMABLE_API_KEY = '123abc'
|
||||
|
||||
* :doc:`Piwik <services/piwik>`::
|
||||
|
||||
PIWIK_DOMAIN_PATH = 'your.piwik.server/optional/path'
|
||||
PIWIK_SITE_ID = '123'
|
||||
|
||||
* :doc:`Reinvigorate <services/reinvigorate>`::
|
||||
|
||||
REINVIGORATE_TRACKING_ID = '12345-abcdefghij'
|
||||
|
|
|
|||
100
docs/services/piwik.rst
Normal file
100
docs/services/piwik.rst
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
==================================
|
||||
Piwik -- open source web analytics
|
||||
==================================
|
||||
|
||||
Piwik_ is an open analytics platform currently used by individuals,
|
||||
companies and governments all over the world. With Piwik, your data
|
||||
will always be yours, because you run your own analytics server.
|
||||
|
||||
.. _Piwik: http://www.piwik.org/
|
||||
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
To start using the Piwik 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 Piwik 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:`piwik-configuration`.
|
||||
|
||||
The Piwik tracking code is inserted into templates using a template
|
||||
tag. Load the :mod:`piwik` template tag library and insert the
|
||||
:ttag:`piwik` 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 bottom of the HTML body as recommended by the
|
||||
`Piwik best practice for Integration Plugins`_::
|
||||
|
||||
{% load piwik %}
|
||||
...
|
||||
{% piwik %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
.. _`Piwik best practice for integration`: http://piwik.org/integrate/how-to/
|
||||
|
||||
|
||||
|
||||
.. _piwik-configuration:
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
Before you can use the Piwik integration, you must first define
|
||||
domain name and optional URI path to your Piwik server, as well as
|
||||
the Piwik ID of the website you're tracking with your Piwik server,
|
||||
in your project settings.
|
||||
|
||||
|
||||
Setting the domain
|
||||
------------------
|
||||
|
||||
Your Django project needs to know where your Piwik server is located.
|
||||
Typically, you'll have Piwik installed on a subdomain of its own
|
||||
(e.g. ``piwik.example.com``), otherwise it runs in a subdirectory of
|
||||
a website of yours (e.g. ``www.example.com/piwik``). Set
|
||||
:const:`PIWIK_DOMAIN_PATH` in the project :file:`settings.py` file
|
||||
accordingly::
|
||||
|
||||
PIWIK_DOMAIN_PATH = 'piwik.example.com'
|
||||
|
||||
If you do not set a domain the tracking code will not be rendered.
|
||||
|
||||
|
||||
Setting the site ID
|
||||
-------------------
|
||||
|
||||
Your Piwik server can track several websites. Each website has its
|
||||
site ID (this is the ``idSite`` parameter in the query string of your
|
||||
browser's address bar when you visit the Piwik Dashboard). Set
|
||||
:const:`PIWIK_SITE_ID` in the project :file:`settings.py` file to
|
||||
the value corresponding to the website your tracking::
|
||||
|
||||
PIWIK_SITE_ID = '4'
|
||||
|
||||
If you do not set the site ID the tracking code will not be rendered.
|
||||
|
||||
|
||||
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` by
|
||||
default (which takes the value of :const:`INTERNAL_IPS` by default).
|
||||
See :ref:`identifying-visitors` for important information about
|
||||
detecting the visitor IP address.
|
||||
|
||||
|
||||
----
|
||||
|
||||
Thanks go to Piwik for providing an excellent web analytics platform
|
||||
entirely for free! Consider donating_ to ensure that they continue
|
||||
their development efforts in the spirit of open source and freedom
|
||||
for our personal data.
|
||||
|
||||
.. _donating: http://piwik.org/donate/
|
||||
Loading…
Reference in a new issue