mirror of
https://github.com/jazzband/django-analytical.git
synced 2026-05-11 00:33:13 +00:00
Merge branch 'master' of github.com:jcassee/django-analytical
This commit is contained in:
commit
782c8d90cf
17 changed files with 117 additions and 30 deletions
|
|
@ -1,6 +1,6 @@
|
|||
The django-analytical package was written by `Joost Cassee`_, with
|
||||
contributions from `Eric Davis`_, `Paul Oswald`_, `Uros Trebec`_,
|
||||
`Steven Skoczen`_ and others.
|
||||
`Steven Skoczen`_, `Piet Delport`_, `Sandra Mau`_, `Simon Ye`_ and others.
|
||||
|
||||
Included Javascript code snippets for integration of the analytics
|
||||
services were written by the respective service providers.
|
||||
|
|
@ -14,6 +14,9 @@ The work on Crazy Egg was made possible by `Bateau Knowledge`_.
|
|||
.. _`Eric Davis`: https://github.com/edavis
|
||||
.. _`Paul Oswald`: https://github.com/poswald
|
||||
.. _`Uros Trebec`: https://github.com/failedguidedog
|
||||
.. _`Steven Skoczen`: https://github.com/https://github.com/skoczen
|
||||
.. _`Steven Skoczen`: https://github.com/skoczen
|
||||
.. _`Piet Delport`: https://github.com/pjdelport
|
||||
.. _`Sandra Mau`: https://github.com/xthepoet
|
||||
.. _`Simon Ye`: https://github.com/yesimon
|
||||
.. _Analytical: https://github.com/jkrall/analytical
|
||||
.. _`Bateau Knowledge`: http://www.bateauknowledge.nl/
|
||||
|
|
|
|||
|
|
@ -1,3 +1,12 @@
|
|||
Version 0.14.0
|
||||
--------------
|
||||
* Update mixpanel integration to latest code (Simon Ye)
|
||||
|
||||
Version 0.13.0
|
||||
--------------
|
||||
* Add support for the KISSmetrics alias feature (Sandra Mau)
|
||||
* Update testing code for Django 1.4 (Piet Delport)
|
||||
|
||||
Version 0.12.0
|
||||
--------------
|
||||
* Add support for the UserVoice service.
|
||||
|
|
|
|||
|
|
@ -10,6 +10,6 @@ Django_ project. See the ``docs`` directory for more information.
|
|||
|
||||
__author__ = "Joost Cassee"
|
||||
__email__ = "joost@cassee.net"
|
||||
__version__ = "0.12.1"
|
||||
__copyright__ = "Copyright (C) 2011 Joost Cassee and others"
|
||||
__version__ = "0.14.0"
|
||||
__copyright__ = "Copyright (C) 2011-2012 Joost Cassee and others"
|
||||
__license__ = "MIT License"
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ ALLOW_LINKER_CODE = "_gaq.push(['_setAllowLinker', true]);"
|
|||
CUSTOM_VAR_CODE = "_gaq.push(['_setCustomVar', %(index)s, '%(name)s', " \
|
||||
"'%(value)s', %(scope)s]);"
|
||||
SITE_SPEED_CODE = "_gaq.push(['_trackPageLoadTime']);"
|
||||
ANONYMIZE_IP_CODE = "_gaq.push (['_gat._anonymizeIp']);"
|
||||
|
||||
|
||||
register = Library()
|
||||
|
|
@ -120,6 +121,8 @@ class GoogleAnalyticsNode(Node):
|
|||
commands = []
|
||||
if getattr(settings, 'GOOGLE_ANALYTICS_SITE_SPEED', False):
|
||||
commands.append(SITE_SPEED_CODE)
|
||||
if getattr(settings, 'GOOGLE_ANALYTICS_ANONYMIZE_IP', False):
|
||||
commands.append(ANONYMIZE_IP_CODE)
|
||||
return commands
|
||||
|
||||
def contribute_to_analytical(add_node):
|
||||
|
|
|
|||
|
|
@ -35,9 +35,11 @@ TRACKING_CODE = """
|
|||
IDENTIFY_CODE = "_kmq.push(['identify', '%s']);"
|
||||
EVENT_CODE = "_kmq.push(['record', '%(name)s', %(properties)s]);"
|
||||
PROPERTY_CODE = "_kmq.push(['set', %(properties)s]);"
|
||||
ALIAS_CODE = "_kmq.push(['alias', '%s', '%s']);"
|
||||
|
||||
EVENT_CONTEXT_KEY = 'kiss_metrics_event'
|
||||
PROPERTY_CONTEXT_KEY = 'kiss_metrics_properties'
|
||||
ALIAS_CONTEXT_KEY = 'kiss_metrics_alias'
|
||||
|
||||
register = Library()
|
||||
|
||||
|
|
@ -67,6 +69,12 @@ class KissMetricsNode(Node):
|
|||
identity = get_identity(context, 'kiss_metrics')
|
||||
if identity is not None:
|
||||
commands.append(IDENTIFY_CODE % identity)
|
||||
try:
|
||||
properties = context[ALIAS_CONTEXT_KEY]
|
||||
key, value = properties.popitem()
|
||||
commands.append(ALIAS_CODE % (key,value))
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
name, properties = context[EVENT_CONTEXT_KEY]
|
||||
commands.append(EVENT_CODE % {'name': name,
|
||||
|
|
|
|||
|
|
@ -16,14 +16,14 @@ from analytical.utils import is_internal_ip, disable_html, get_identity, \
|
|||
MIXPANEL_API_TOKEN_RE = re.compile(r'^[0-9a-f]{32}$')
|
||||
TRACKING_CODE = """
|
||||
<script type="text/javascript">
|
||||
var mpq = [];
|
||||
mpq.push(['init', '%(token)s']);
|
||||
%(commands)s
|
||||
(function(){var b,a,e,d,c;b=document.createElement("script");b.type="text/javascript";b.async=true;b.src=(document.location.protocol==="https:"?"https:":"http:")+"//api.mixpanel.com/site_media/js/api/mixpanel.js";a=document.getElementsByTagName("script")[0];a.parentNode.insertBefore(b,a);e=function(f){return function(){mpq.push([f].concat(Array.prototype.slice.call(arguments,0)))}};d=["init","track","track_links","track_forms","register","register_once","identify","name_tag","set_config"];for(c=0;c<d.length;c++){mpq[d[c]]=e(d[c])}})();
|
||||
(function(c,a){window.mixpanel=a;var b,d,h,e;b=c.createElement("script");b.type="text/javascript";b.async=!0;b.src=("https:"===c.location.protocol?"https:":"http:")+'//cdn.mxpnl.com/libs/mixpanel-2.1.min.js';d=c.getElementsByTagName("script")[0];d.parentNode.insertBefore(b,d);a._i=[];a.init=function(b,c,f){function d(a,b){var c=b.split(".");2==c.length&&(a=a[c[0]],b=c[1]);a[b]=function(){a.push([b].concat(Array.prototype.slice.call(arguments,0)))}}var g=a;"undefined"!==typeof f?
|
||||
g=a[f]=[]:f="mixpanel";g.people=g.people||[];h="disable track track_pageview track_links track_forms register register_once unregister identify name_tag set_config people.identify people.set people.increment".split(" ");for(e=0;e<h.length;e++)d(g,h[e]);a._i.push([b,c,f])};a.__SV=1.1})(document,window.mixpanel||[]);
|
||||
mixpanel.init('%(token)s');
|
||||
%(commands)s
|
||||
</script>
|
||||
"""
|
||||
IDENTIFY_CODE = "mpq.push(['identify', '%s']);"
|
||||
EVENT_CODE = "mpq.push(['track', '%(name)s', %(properties)s]);"
|
||||
IDENTIFY_CODE = "mixpanel.register_once({distinct_id: '%s'});"
|
||||
EVENT_CODE = "mixpanel.track('%(name)s', %(properties)s);"
|
||||
EVENT_CONTEXT_KEY = 'mixpanel_event'
|
||||
|
||||
register = Library()
|
||||
|
|
|
|||
|
|
@ -12,3 +12,5 @@ DATABASES = {
|
|||
INSTALLED_APPS = [
|
||||
'analytical',
|
||||
]
|
||||
|
||||
SECRET_KEY = 'testing'
|
||||
|
|
|
|||
|
|
@ -86,6 +86,15 @@ class GoogleAnalyticsTagTestCase(TagTestCase):
|
|||
'<!-- Google Analytics disabled on internal IP address'), r)
|
||||
self.assertTrue(r.endswith('-->'), r)
|
||||
|
||||
@override_settings(GOOGLE_ANALYTICS_ANONYMIZE_IP=True)
|
||||
def test_anonymize_ip(self):
|
||||
r = GoogleAnalyticsNode().render(Context())
|
||||
self.assertTrue("_gaq.push (['_gat._anonymizeIp']);" in r, r)
|
||||
|
||||
@override_settings(GOOGLE_ANALYTICS_ANONYMIZE_IP=False)
|
||||
def test_anonymize_ip_not_present(self):
|
||||
r = GoogleAnalyticsNode().render(Context())
|
||||
self.assertFalse("_gaq.push (['_gat._anonymizeIp']);" in r, r)
|
||||
|
||||
@without_apps('django.contrib.sites')
|
||||
@override_settings(GOOGLE_ANALYTICS_PROPERTY_ID='UA-123456-7',
|
||||
|
|
|
|||
|
|
@ -64,6 +64,12 @@ class KissMetricsTagTestCase(TagTestCase):
|
|||
self.assertTrue("_kmq.push(['set', "
|
||||
'{"prop1": "val1", "prop2": "val2"}]);' in r, r)
|
||||
|
||||
def test_alias(self):
|
||||
r = KissMetricsNode().render(Context({'kiss_metrics_alias':
|
||||
{'test': 'test_alias'}}))
|
||||
self.assertTrue("_kmq.push(['alias', 'test', 'test_alias']);" in r,r)
|
||||
|
||||
|
||||
@override_settings(ANALYTICAL_INTERNAL_IPS=['1.1.1.1'])
|
||||
def test_render_internal_ip(self):
|
||||
req = HttpRequest()
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@ class MixpanelTagTestCase(TagTestCase):
|
|||
def test_tag(self):
|
||||
r = self.render_tag('mixpanel', 'mixpanel')
|
||||
self.assertTrue(
|
||||
"mpq.push(['init', '0123456789abcdef0123456789abcdef']);" in r,
|
||||
"mixpanel.init('0123456789abcdef0123456789abcdef');" in r,
|
||||
r)
|
||||
|
||||
def test_node(self):
|
||||
r = MixpanelNode().render(Context())
|
||||
self.assertTrue(
|
||||
"mpq.push(['init', '0123456789abcdef0123456789abcdef']);" in r,
|
||||
"mixpanel.init('0123456789abcdef0123456789abcdef');" in r,
|
||||
r)
|
||||
|
||||
@override_settings(MIXPANEL_API_TOKEN=SETTING_DELETED)
|
||||
|
|
@ -44,18 +44,18 @@ class MixpanelTagTestCase(TagTestCase):
|
|||
@override_settings(ANALYTICAL_AUTO_IDENTIFY=True)
|
||||
def test_identify(self):
|
||||
r = MixpanelNode().render(Context({'user': User(username='test')}))
|
||||
self.assertTrue("mpq.push(['identify', 'test']);" in r, r)
|
||||
self.assertTrue("mixpanel.register_once({distinct_id: 'test'});" in r, r)
|
||||
|
||||
@override_settings(ANALYTICAL_AUTO_IDENTIFY=True)
|
||||
def test_identify_anonymous_user(self):
|
||||
r = MixpanelNode().render(Context({'user': AnonymousUser()}))
|
||||
self.assertFalse("mpq.push(['identify', " in r, r)
|
||||
self.assertFalse("mixpanel.register_once({distinct_id:" in r, r)
|
||||
|
||||
def test_event(self):
|
||||
r = MixpanelNode().render(Context({'mixpanel_event':
|
||||
('test_event', {'prop1': 'val1', 'prop2': 'val2'})}))
|
||||
self.assertTrue("mpq.push(['track', 'test_event', "
|
||||
'{"prop1": "val1", "prop2": "val2"}]);' in r, r)
|
||||
('test_event', {'prop1': 'val1', 'prop2': 'val2'})}))
|
||||
self.assertTrue("mixpanel.track('test_event', "
|
||||
'{"prop1": "val1", "prop2": "val2"});' in r, r)
|
||||
|
||||
@override_settings(ANALYTICAL_INTERNAL_IPS=['1.1.1.1'])
|
||||
def test_render_internal_ip(self):
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ pygments_style = 'sphinx'
|
|||
|
||||
intersphinx_mapping = {
|
||||
'http://docs.python.org/2.6': None,
|
||||
'http://docs.djangoproject.com/en/1.3': 'http://docs.djangoproject.com/en/1.3/_objects/',
|
||||
'http://docs.djangoproject.com/en/1.3': 'http://docs.djangoproject.com/en/1.4/_objects/',
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ Overview
|
|||
:start-after: Django_ project.
|
||||
:end-before: Currently supported services:
|
||||
|
||||
To get a feel of how django-analytics works, check out the
|
||||
To get a feel of how django-analytical works, check out the
|
||||
:doc:`tutorial`.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -160,6 +160,6 @@ settings required to enable each service are listed here:
|
|||
|
||||
----
|
||||
|
||||
The django-analytics application is now set-up to track visitors. For
|
||||
information about further configuration and customization, see
|
||||
:doc:`features`.
|
||||
The django-analytical application is now set-up to track visitors. For
|
||||
information about identifying users, further configuration and
|
||||
customization, see :doc:`features`.
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ Constant Value Description
|
|||
================= ====== =============================================
|
||||
``SCOPE_VISITOR`` 1 Distinguishes categories of visitors across
|
||||
multiple sessions.
|
||||
``SCOPE_SESSION`` 2 Ddistinguishes different visitor experiences
|
||||
``SCOPE_SESSION`` 2 Distinguishes different visitor experiences
|
||||
across sessions.
|
||||
``SCOPE_PAGE`` 3 Defines page-level activity.
|
||||
================= ====== =============================================
|
||||
|
|
@ -172,3 +172,21 @@ Just remember that if you set the same context variable in the
|
|||
context processor, the latter clobbers the former.
|
||||
|
||||
.. _`custom variables`: http://code.google.com/apis/analytics/docs/tracking/gaTrackingCustomVariables.html
|
||||
|
||||
|
||||
.. _google-analytics-anonimyze-ips:
|
||||
|
||||
Anonymize IPs
|
||||
----------------
|
||||
|
||||
You can enable the `IP anonymization`_ feature by setting the
|
||||
:const:`GOOGLE_ANALYTICS_ANONYMIZE_IP` configuration setting::
|
||||
|
||||
GOOGLE_ANALYTICS_ANONYMIZE_IP = True
|
||||
|
||||
This may be mandatory for deployments in countries that have a firm policies
|
||||
concerning data privacy (e.g. Germany).
|
||||
|
||||
By default, IPs are not anonymized.
|
||||
|
||||
.. _`IP anonymization`: https://support.google.com/analytics/bin/answer.py?hl=en&answer=2763052
|
||||
|
|
@ -108,7 +108,29 @@ Just remember that if you set the same context variable in the
|
|||
:class:`~django.template.context.RequestContext` constructor and in a
|
||||
context processor, the latter clobbers the former.
|
||||
|
||||
.. _kiss-metrics-event:
|
||||
|
||||
.. _kiss-metrics-alias:
|
||||
|
||||
Alias
|
||||
-----
|
||||
|
||||
Alias is used to associate one identity with another.
|
||||
This most likely will occur if a user is not signed in yet,
|
||||
you assign them an anonymous identity and record activity for them
|
||||
and they later sign in and you get a named identity.
|
||||
|
||||
For example::
|
||||
|
||||
context = RequestContext({
|
||||
'kiss_metrics_alias': {'my_registered@email' : 'my_user_id'},
|
||||
})
|
||||
return some_template.render(context)
|
||||
|
||||
The output script tag will then include the corresponding properties as
|
||||
documented in the `KISSmetrics alias API`_ docs.
|
||||
|
||||
.. _`KISSmetrics alias API`: http://support.kissmetrics.com/apis/common-methods#alias
|
||||
|
||||
|
||||
Recording events
|
||||
----------------
|
||||
|
|
|
|||
|
|
@ -124,8 +124,7 @@ notation, as described in the section titled
|
|||
`"Asynchronous Tracking with Javascript"`_ in the Mixpanel
|
||||
documentation. For example::
|
||||
|
||||
mpq.push(["track", "play-game", {"level": "12", "weapon": "sword", "character": "knight"}]);
|
||||
mixpanel.track("play-game", {"level": "12", "weapon": "sword", "character": "knight"});
|
||||
|
||||
.. _mixpanel-celery: http://github.com/winhamwr/mixpanel-celery
|
||||
.. _`"Asynchronous Tracking with Javascript"`: http://mixpanel.com/api/docs/guides/integration/js#async
|
||||
|
||||
|
|
|
|||
16
tox.ini
16
tox.ini
|
|
@ -1,7 +1,7 @@
|
|||
[tox]
|
||||
envlist =
|
||||
py2.6-django1.2,py2.6-django1.3,
|
||||
py2.7-django1.2,py2.7-django1.3
|
||||
py2.6-django1.2,py2.6-django1.3,py2.6-django1.4,
|
||||
py2.7-django1.2,py2.7-django1.3,py2.7-django1.4,
|
||||
|
||||
[testenv]
|
||||
commands = python -Wall setup.py test
|
||||
|
|
@ -12,7 +12,11 @@ deps = Django>=1.2,<1.3
|
|||
|
||||
[testenv:py2.6-django1.3]
|
||||
basepython = python2.6
|
||||
deps = Django>=1.3
|
||||
deps = Django>=1.3,<1.4
|
||||
|
||||
[testenv:py2.6-django1.4]
|
||||
basepython = python2.6
|
||||
deps = Django>=1.4,<1.5
|
||||
|
||||
[testenv:py2.7-django1.2]
|
||||
basepython = python2.7
|
||||
|
|
@ -20,4 +24,8 @@ deps = Django>=1.2,<1.3
|
|||
|
||||
[testenv:py2.7-django1.3]
|
||||
basepython = python2.7
|
||||
deps = Django>=1.3
|
||||
deps = Django>=1.3,<1.4
|
||||
|
||||
[testenv:py2.7-django1.4]
|
||||
basepython = python2.7
|
||||
deps = Django>=1.4,<1.5
|
||||
|
|
|
|||
Loading…
Reference in a new issue