diff --git a/README.rst b/README.rst
index d045c3e..f54b586 100644
--- a/README.rst
+++ b/README.rst
@@ -4,6 +4,7 @@ django-analytical
The django-analytical application integrates various analytics services
into a Django_ project. Currently supported services:
+* `Chartbeat`_ -- traffic analysis
* `Clicky`_ -- traffic analysis
* `Crazy Egg`_ -- visual click tracking
* `Google Analytics`_ traffic analysis
@@ -23,6 +24,7 @@ Joshua Krall's all-purpose analytics front-end for Rails. The work on
Crazy Egg was made possible by `Bateau Knowledge`_.
.. _Django: http://www.djangoproject.com/
+.. _Chartbeat: http://www.chartbeat.com/
.. _Clicky: http://getclicky.com/
.. _`Crazy Egg`: http://www.crazyegg.com/
.. _`Google Analytics`: http://www.google.com/analytics/
diff --git a/analytical/__init__.py b/analytical/__init__.py
index f687ea4..4fc48e4 100644
--- a/analytical/__init__.py
+++ b/analytical/__init__.py
@@ -1,10 +1,11 @@
"""
-========================================
Analytics service integration for Django
========================================
-The django-clicky application integrates Clicky_ analytics into a
-Django_ project.
+The django-analytical application integrates analytics services into a
+Django_ project. See the ``docs`` directory for more information.
+
+.. _Django: http://www.djangoproject.com/
"""
__author__ = "Joost Cassee"
@@ -12,3 +13,10 @@ __email__ = "joost@cassee.net"
__version__ = "0.1.0alpha"
__copyright__ = "Copyright (C) 2011 Joost Cassee"
__license__ = "MIT License"
+
+try:
+ from collections import namedtuple
+except ImportError:
+ namedtuple = lambda name, fields: lambda *values: values
+
+Property = namedtuple('Property', ['num', 'name', 'value'])
diff --git a/analytical/context_processors.py b/analytical/context_processors.py
new file mode 100644
index 0000000..ccd09d8
--- /dev/null
+++ b/analytical/context_processors.py
@@ -0,0 +1,33 @@
+"""
+Context processors for django-analytical.
+"""
+
+from django.conf import settings
+
+
+IMPORT_SETTINGS = [
+ 'ANALYTICAL_INTERNAL_IPS',
+ 'ANALYTICAL_SERVICES',
+ 'CHARTBEAT_USER_ID',
+ 'CLICKY_SITE_ID',
+ 'CRAZY_EGG_ACCOUNT_NUMBER',
+ 'GOOGLE_ANALYTICS_PROPERTY_ID',
+ 'KISS_INSIGHTS_ACCOUNT_NUMBER',
+ 'KISS_INSIGHTS_SITE_CODE',
+ 'KISS_METRICS_API_KEY',
+ 'MIXPANEL_TOKEN',
+ 'OPTIMIZELY_ACCOUNT_NUMBER',
+]
+
+
+def settings(request):
+ """
+ Import all django-analytical settings into the template context.
+ """
+ vars = {}
+ for setting in IMPORT_SETTINGS:
+ try:
+ vars[setting] = getattr(settings, setting)
+ except AttributeError:
+ pass
+ return vars
diff --git a/analytical/services/__init__.py b/analytical/services/__init__.py
index 5031eec..5f7d3e0 100644
--- a/analytical/services/__init__.py
+++ b/analytical/services/__init__.py
@@ -12,20 +12,21 @@ from django.core.exceptions import ImproperlyConfigured
_log = logging.getLogger(__name__)
DEFAULT_SERVICES = [
+ 'analytical.services.chartbeat.ChartbeatService',
'analytical.services.clicky.ClickyService',
- 'analytical.services.crazyegg.CrazyEggService',
+ 'analytical.services.crazy_egg.CrazyEggService',
'analytical.services.google_analytics.GoogleAnalyticsService',
- 'analytical.services.kissinsights.KissInsightsService',
- 'analytical.services.kissmetrics.KissMetricsService',
+ 'analytical.services.kiss_insights.KissInsightsService',
+ 'analytical.services.kiss_metrics.KissMetricsService',
'analytical.services.mixpanel.MixpanelService',
'analytical.services.optimizely.OptimizelyService',
]
enabled_services = None
-def get_enabled_services(reload=False):
+def get_enabled_services():
global enabled_services
- if enabled_services is None or reload:
+ if enabled_services is None:
enabled_services = load_services()
return enabled_services
@@ -39,19 +40,7 @@ def load_services():
autoload = True
for path in service_paths:
try:
- module, attr = path.rsplit('.', 1)
- try:
- mod = import_module(module)
- except ImportError, e:
- raise ImproperlyConfigured(
- 'error importing analytical service %s: "%s"'
- % (module, e))
- try:
- service = getattr(mod, attr)()
- except AttributeError:
- raise ImproperlyConfigured(
- 'module "%s" does not define service "%s"'
- % (module, attr))
+ service = _load_service(path)
enabled_services.append(service)
except ImproperlyConfigured, e:
if autoload:
@@ -60,3 +49,18 @@ def load_services():
else:
raise
return enabled_services
+
+def _load_service(path):
+ module, attr = path.rsplit('.', 1)
+ try:
+ mod = import_module(module)
+ except ImportError, e:
+ raise ImproperlyConfigured(
+ 'error importing analytical service %s: "%s"' % (module, e))
+ try:
+ service = getattr(mod, attr)()
+ except (AttributeError, TypeError):
+ raise ImproperlyConfigured(
+ 'module "%s" does not define callable service "%s"'
+ % (module, attr))
+ return service
diff --git a/analytical/services/base.py b/analytical/services/base.py
index 856632e..0f47f50 100644
--- a/analytical/services/base.py
+++ b/analytical/services/base.py
@@ -5,6 +5,12 @@ from django.core.exceptions import ImproperlyConfigured
from django.conf import settings
+
+HTML_COMMENT = ""
+JS_COMMENT = "*/ %(message):%(sep)s%(html)s%(sep)s*/"
+IDENTITY_CONTEXT_KEY = 'analytical_identity'
+
+
class AnalyticalService(object):
"""
Analytics service.
@@ -14,8 +20,6 @@ class AnalyticalService(object):
func_name = "render_%s" % location
func = getattr(self, func_name)
html = func(context)
- if self.is_initialized(context):
- pass
return html
def render_head_top(self, context):
@@ -30,6 +34,9 @@ class AnalyticalService(object):
def render_body_bottom(self, context):
return ""
+ def render_event(self, name, properties):
+ return ""
+
def get_required_setting(self, setting, value_re, invalid_msg):
try:
value = getattr(settings, setting)
@@ -37,5 +44,45 @@ class AnalyticalService(object):
raise ImproperlyConfigured("%s setting: not found" % setting)
value = str(value)
if not value_re.search(value):
- raise ImproperlyConfigured("%s setting: %s" % (value, invalid_msg))
+ raise ImproperlyConfigured("%s setting: %s: '%s'"
+ % (setting, invalid_msg, value))
return value
+
+ def get_identity(self, context):
+ try:
+ return context[IDENTITY_CONTEXT_KEY]
+ except KeyError:
+ pass
+ if getattr(settings, 'ANALYTICAL_AUTO_IDENTIFY', True):
+ try:
+ try:
+ user = context['user']
+ except KeyError:
+ request = context['request']
+ user = request.user
+ if user.is_authenticated():
+ return user.username
+ except (KeyError, AttributeError):
+ pass
+ return None
+
+ def get_events(self, context):
+ return context.get('analytical_events', {})
+
+ def get_properties(self, context):
+ return context.get('analytical_properties', {})
+
+ def _html_comment(self, html, message=""):
+ return self._comment(HTML_COMMENT, html, message)
+
+ def _js_comment(self, html, message=""):
+ return self._comment(JS_COMMENT, html, message)
+
+ def _comment(self, format, html, message):
+ if not message:
+ message = "Disabled"
+ if message.find('\n') > -1:
+ sep = '\n'
+ else:
+ sep = ' '
+ return format % {'message': message, 'html': html, 'sep': sep}
diff --git a/analytical/services/chartbeat.py b/analytical/services/chartbeat.py
new file mode 100644
index 0000000..22885af
--- /dev/null
+++ b/analytical/services/chartbeat.py
@@ -0,0 +1,66 @@
+"""
+Chartbeat service.
+"""
+
+import re
+
+from django.contrib.sites.models import Site, RequestSite
+from django.core.exceptions import ImproperlyConfigured
+
+from analytical.services.base import AnalyticalService
+
+
+USER_ID_RE = re.compile(r'^\d{5}$')
+INIT_CODE = """"""
+SETUP_CODE = """
+
+"""
+DOMAIN_CONTEXT_KEY = 'chartbeat_domain'
+
+
+class ChartbeatService(AnalyticalService):
+ def __init__(self):
+ self.user_id = self.get_required_setting(
+ 'CHARTBEAT_USER_ID', USER_ID_RE,
+ "must be a string containing an five-digit number")
+
+ def render_head_top(self, context):
+ return INIT_CODE
+
+ def render_body_bottom(self, context):
+ return SETUP_CODE % {'user_id': self.user_id,
+ 'domain': self._get_domain(context)}
+
+ def _get_domain(self, context):
+ try:
+ return context[DOMAIN_CONTEXT_KEY]
+ except KeyError:
+ pass
+ try:
+ return Site.objects.get_current().domain
+ except ImproperlyConfigured:
+ pass
+ try:
+ request = context['request']
+ return RequestSite(request).domain
+ except (KeyError, AttributeError):
+ raise KeyError("could not find access either '%s' or 'request' "
+ "in the template context and 'django.contrib.sites' is "
+ "not in INSTALLED_APPS" % DOMAIN_CONTEXT_KEY)
diff --git a/analytical/services/clicky.py b/analytical/services/clicky.py
index 617af41..acc06b4 100644
--- a/analytical/services/clicky.py
+++ b/analytical/services/clicky.py
@@ -4,14 +4,17 @@ Clicky service.
import re
+from django.utils import simplejson
+
from analytical.services.base import AnalyticalService
SITE_ID_RE = re.compile(r'^\d{8}$')
-TRACKING_CODE = """
+SETUP_CODE = """
"""
+CUSTOM_CONTEXT_KEY = 'clicky_custom'
class ClickyService(AnalyticalService):
- KEY = 'clicky'
-
def __init__(self):
self.site_id = self.get_required_setting('CLICKY_SITE_ID', SITE_ID_RE,
"must be a string containing an eight-digit number")
def render_body_bottom(self, context):
- return TRACKING_CODE % {'site_id': self.site_id}
+ custom = {
+ 'session': {
+ 'username': self.get_identity(context),
+ }
+ }
+ custom.update(context.get(CUSTOM_CONTEXT_KEY, {}))
+ return SETUP_CODE % {'site_id': self.site_id,
+ 'custom': simplejson.dumps(custom)}
+
+ def _convert_properties(self):
+ pass
diff --git a/analytical/services/console.py b/analytical/services/console.py
index 634b49c..7bde9f4 100644
--- a/analytical/services/console.py
+++ b/analytical/services/console.py
@@ -8,23 +8,35 @@ from analytical.services.base import AnalyticalService
DEBUG_CODE = """
"""
+LOG_CODE_ANONYMOUS = """
+ console.log('Analytical: rendering analytical_%(location)s tag');
+"""
+LOG_CODE_IDENTIFIED = """
+ console.log('Analytical: rendering analytical_%(location)s tag for user %(identity)s');
+"""
class ConsoleService(AnalyticalService):
- KEY = 'console'
-
def render_head_top(self, context):
- return DEBUG_CODE % {'location': 'head_top'}
+ return self._render_code('head_top', context)
def render_head_bottom(self, context):
- return DEBUG_CODE % {'location': 'head_bottom'}
+ return self._render_code('head_bottom', context)
def render_body_top(self, context):
- return DEBUG_CODE % {'location': 'body_top'}
+ return self._render_code('body_top', context)
def render_body_bottom(self, context):
- return DEBUG_CODE % {'location': 'body_bottom'}
+ return self._render_code('body_bottom', context)
+
+ def _render_code(self, location, context):
+ vars = {'location': location, 'identity': self.get_identity(context)}
+ if vars['identity'] is None:
+ debug_code = DEBUG_CODE % LOG_CODE_ANONYMOUS
+ else:
+ debug_code = DEBUG_CODE % LOG_CODE_IDENTIFIED
+ return debug_code % vars
diff --git a/analytical/services/crazy_egg.py b/analytical/services/crazy_egg.py
index 7ec85af..22a629e 100644
--- a/analytical/services/crazy_egg.py
+++ b/analytical/services/crazy_egg.py
@@ -8,17 +8,24 @@ from analytical.services.base import AnalyticalService
ACCOUNT_NUMBER_RE = re.compile(r'^\d{8}$')
-TRACK_CODE = """"""
+USERVAR_CODE = "CE2.set(%(varnr)d, '%(value)s');"
+USERVAR_CONTEXT_VAR = 'crazy_egg_uservars'
class CrazyEggService(AnalyticalService):
- KEY = 'crazy_egg'
-
def __init__(self):
self.account_nr = self.get_required_setting('CRAZY_EGG_ACCOUNT_NUMBER',
ACCOUNT_NUMBER_RE,
"must be a string containing an eight-digit number")
def render_body_bottom(self, context):
- return TRACK_CODE % {'account_nr_1': self.account_nr[:4],
+ html = SETUP_CODE % {'account_nr_1': self.account_nr[:4],
'account_nr_2': self.account_nr[4:]}
+ uservars = context.get(USERVAR_CONTEXT_VAR, {})
+ if uservars:
+ js = "".join(USERVAR_CODE % {'varnr': varnr, 'value': value}
+ for (varnr, value) in uservars.items())
+ html = '%s\n' \
+ % (html, js)
+ return html
diff --git a/analytical/services/google_analytics.py b/analytical/services/google_analytics.py
index f104795..851c57f 100644
--- a/analytical/services/google_analytics.py
+++ b/analytical/services/google_analytics.py
@@ -8,12 +8,12 @@ from analytical.services.base import AnalyticalService
PROPERTY_ID_RE = re.compile(r'^UA-\d+-\d+$')
-TRACKING_CODE = """
+SETUP_CODE = """
"""
-
+TRACK_CODE = "_gaq.push(['_trackPageview']);"
+CUSTOM_VARS_CONTEXT_KEY = "google_analytics_custom_vars"
+CUSTOM_VAR_CODE = "_gaq.push(['_setCustomVar', %(index)d, '%(name)s', " \
+ "'%(value)s', %(scope)d]);"
class GoogleAnalyticsService(AnalyticalService):
- KEY = 'google_analytics'
-
def __init__(self):
self.property_id = self.get_required_setting(
'GOOGLE_ANALYTICS_PROPERTY_ID', PROPERTY_ID_RE,
"must be a string looking like 'UA-XXXXXX-Y'")
def render_head_bottom(self, context):
- return TRACKING_CODE % {'property_id': self.property_id}
+ commands = self._get_custom_var_commands(context)
+ commands.append(TRACK_CODE)
+ return SETUP_CODE % {'property_id': self.property_id,
+ 'commands': " ".join(commands)}
+
+ def _get_custom_var_commands(self, context):
+ commands = []
+ vardefs = context.get(CUSTOM_VARS_CONTEXT_KEY, [])
+ for vardef in vardefs:
+ index = vardef[0]
+ if not 1 <= index <= 5:
+ raise ValueError("Google Analytics custom variable index must "
+ "be between 1 and 5: %s" % index)
+ name = vardef[1]
+ value = vardef[2]
+ if len(vardef) >= 4:
+ scope = vardef[3]
+ else:
+ scope = 2
+ commands.append(CUSTOM_VAR_CODE % locals())
+ return commands
diff --git a/analytical/services/kiss_insights.py b/analytical/services/kiss_insights.py
new file mode 100644
index 0000000..7143021
--- /dev/null
+++ b/analytical/services/kiss_insights.py
@@ -0,0 +1,39 @@
+"""
+KISSinsights service.
+"""
+
+import re
+
+from analytical.services.base import AnalyticalService
+
+
+ACCOUNT_NUMBER_RE = re.compile(r'^\d{5}$')
+SITE_CODE_RE = re.compile(r'^[\d\w]{3}$')
+SETUP_CODE = """
+
+
+"""
+IDENTIFY_CODE = "_kiq.push(['identify', '%s']);"
+SHOW_SURVEY_CODE = "_kiq.push(['showSurvey', %s]);"
+SHOW_SURVEY_CONTEXT_KEY = 'kiss_insights_show_survey'
+
+class KissInsightsService(AnalyticalService):
+ def __init__(self):
+ self.account_number = self.get_required_setting(
+ 'KISS_INSIGHTS_ACCOUNT_NUMBER', ACCOUNT_NUMBER_RE,
+ "must be a string containing an five-digit number")
+ self.site_code = self.get_required_setting('KISS_INSIGHTS_SITE_CODE',
+ SITE_CODE_RE, "must be a string containing three characters")
+
+ def render_body_top(self, context):
+ commands = []
+ identity = self.get_identity(context)
+ if identity is not None:
+ commands.append(IDENTIFY_CODE % identity)
+ try:
+ commands.append(SHOW_SURVEY_CODE
+ % context[SHOW_SURVEY_CONTEXT_KEY])
+ except KeyError:
+ pass
+ return SETUP_CODE % {'account_number': self.account_number,
+ 'site_code': self.site_code, 'commands': " ".join(commands)}
diff --git a/analytical/services/kissmetrics.py b/analytical/services/kiss_metrics.py
similarity index 56%
rename from analytical/services/kissmetrics.py
rename to analytical/services/kiss_metrics.py
index bc5c0e3..2868560 100644
--- a/analytical/services/kissmetrics.py
+++ b/analytical/services/kiss_metrics.py
@@ -4,13 +4,16 @@ KISSmetrics service.
import re
+from django.utils import simplejson
+
from analytical.services.base import AnalyticalService
API_KEY_RE = re.compile(r'^[0-9a-f]{40}$')
-TRACKING_CODE = """
+SETUP_CODE = """
"""
+IDENTIFY_CODE = "_kmq.push(['identify', '%s']);"
+JS_EVENT_CODE = "_kmq.push(['record', '%(name)s', %(properties)s]);"
class KissMetricsService(AnalyticalService):
- KEY = 'kissmetrics'
-
def __init__(self):
- self.api_key = self.get_required_setting('KISSMETRICS_API_KEY',
+ self.api_key = self.get_required_setting('KISS_METRICS_API_KEY',
API_KEY_RE,
"must be a string containing a 40-digit hexadecimal number")
def render_head_top(self, context):
- return TRACKING_CODE % {'api_key': self.api_key}
+ commands = []
+ identity = self.get_identity(context)
+ if identity is not None:
+ commands.append(IDENTIFY_CODE % identity)
+ return SETUP_CODE % {'api_key': self.api_key,
+ 'commands': commands}
+
+ def render_event(self, name, properties):
+ return JS_EVENT_CODE % {'name': name,
+ 'properties': simplejson.dumps(properties)}
diff --git a/analytical/services/kissinsights.py b/analytical/services/kissinsights.py
deleted file mode 100644
index 15ac87d..0000000
--- a/analytical/services/kissinsights.py
+++ /dev/null
@@ -1,30 +0,0 @@
-"""
-KISSinsights service.
-"""
-
-import re
-
-from analytical.services.base import AnalyticalService
-
-
-ACCOUNT_NUMBER_RE = re.compile(r'^\d{5}$')
-SITE_CODE_RE = re.compile(r'^[\d\w]{3}$')
-TRACKING_CODE = """
-
-
-"""
-
-
-class KissInsightsService(AnalyticalService):
- KEY = 'kissinsights'
-
- def __init__(self):
- self.account_number = self.get_required_setting(
- 'KISSINSIGHTS_ACCOUNT_NUMBER', ACCOUNT_NUMBER_RE,
- "must be a string containing an five-digit number")
- self.site_code = self.get_required_setting('KISSINSIGHTS_SITE_CODE',
- SITE_CODE_RE, "must be a string containing three characters")
-
- def render_body_top(self, context):
- return TRACKING_CODE % {'account_number': self.account_number,
- 'site_code': self.site_code}
diff --git a/analytical/services/mixpanel.py b/analytical/services/mixpanel.py
index c20e147..82d5c06 100644
--- a/analytical/services/mixpanel.py
+++ b/analytical/services/mixpanel.py
@@ -4,35 +4,42 @@ Mixpanel service.
import re
+from django.utils import simplejson
+
from analytical.services.base import AnalyticalService
MIXPANEL_TOKEN_RE = re.compile(r'^[0-9a-f]{32}$')
-TRACKING_CODE = """
-
-
"""
+IDENTIFY_CODE = "mpq.push(['identify', '%s']);"
+EVENT_CODE = "mpq.push(['track', '%(name)s', %(properties)s]);"
class MixpanelService(AnalyticalService):
- KEY = 'mixpanel'
-
def __init__(self):
self.token = self.get_required_setting('MIXPANEL_TOKEN',
MIXPANEL_TOKEN_RE,
"must be a string containing a 32-digit hexadecimal number")
- def render_body_bottom(self, context):
- return TRACKING_CODE % {'token': self.token}
+ def render_head_bottom(self, context):
+ commands = []
+ identity = self.get_identity(context)
+ if identity is not None:
+ commands.append(IDENTIFY_CODE % identity)
+ return SETUP_CODE % {'token': self.token,
+ 'commands': " ".join(commands)}
+
+ def render_event(self, name, properties):
+ return EVENT_CODE % {'name': name,
+ 'properties': simplejson.dumps(properties)}
diff --git a/analytical/services/optimizely.py b/analytical/services/optimizely.py
index 3209ec0..e4b9ae0 100644
--- a/analytical/services/optimizely.py
+++ b/analytical/services/optimizely.py
@@ -8,16 +8,14 @@ from analytical.services.base import AnalyticalService
ACCOUNT_NUMBER_RE = re.compile(r'^\d{7}$')
-TRACKING_CODE = """"""
+SETUP_CODE = """"""
class OptimizelyService(AnalyticalService):
- KEY = 'optimizely'
-
def __init__(self):
self.account_number = self.get_required_setting(
'OPTIMIZELY_ACCOUNT_NUMBER', ACCOUNT_NUMBER_RE,
"must be a string containing an seven-digit number")
def render_head_top(self, context):
- return TRACKING_CODE % {'account_number': self.account_number}
+ return SETUP_CODE % {'account_number': self.account_number}
diff --git a/analytical/templatetags/analytical.py b/analytical/templatetags/analytical.py
index f4f9479..9d25a8f 100644
--- a/analytical/templatetags/analytical.py
+++ b/analytical/templatetags/analytical.py
@@ -5,12 +5,14 @@ from __future__ import absolute_import
from django import template
from django.conf import settings
-from django.template import Node, TemplateSyntaxError
+from django.template import Node, TemplateSyntaxError, Variable
from analytical.services import get_enabled_services
-DISABLE_CODE = ""
+HTML_COMMENT_CODE = ""
+JS_COMMENT_CODE = "/* %s */"
+SCRIPT_CODE = """"""
register = template.Library()
@@ -25,7 +27,7 @@ def _location_tag(location):
return tag
for l in ['head_top', 'head_bottom', 'body_top', 'body_bottom']:
- register.tag('analytical_%s' % l, _location_tag(l))
+ register.tag('analytical_setup_%s' % l, _location_tag(l))
class AnalyticalNode(Node):
@@ -36,13 +38,13 @@ class AnalyticalNode(Node):
getattr(settings, 'INTERNAL_IPS', ()))
def render(self, context):
- html = "".join([self._render_service(service, context)
+ result = "".join([self._render_service(service, context)
for service in get_enabled_services()])
- if not html:
+ if not result:
return ""
-# if self._is_internal_ip(context):
-# return DISABLE_CODE % html
- return html
+ if self._is_internal_ip(context):
+ return HTML_COMMENT_CODE % result
+ return result
def _render_service(self, service, context):
func = getattr(service, self.render_func_name)
@@ -56,3 +58,42 @@ class AnalyticalNode(Node):
return remote_ip in self.internal_ips
except KeyError, AttributeError:
return False
+
+
+def event(parser, token):
+ bits = token.split_contents()
+ if len(bits) < 2:
+ raise TemplateSyntaxError("'%s' tag takes at least one argument"
+ % bits[0])
+ properties = _parse_properties(bits[0], bits[2:])
+ return EventNode(bits[1], properties)
+
+register.tag('event', event)
+
+
+class EventNode(Node):
+ def __init__(self, name, properties):
+ self.name = name
+ self.properties = properties
+
+ def render(self, context):
+ props = dict((var, Variable(val).resolve(context))
+ for var, val in self.properties)
+ result = "".join([service.render_js_event(props)
+ for service in get_enabled_services()])
+ if not result:
+ return ""
+ if self._is_internal_ip(context):
+ return JS_COMMENT_CODE % result
+ return result
+
+
+def _parse_properties(tag_name, bits):
+ properties = []
+ for bit in bits:
+ try:
+ properties.append(bit.split('=', 1))
+ except IndexError:
+ raise TemplateSyntaxError("'%s' tag argument must be of the form "
+ " property=value: '%s'" % (tag_name, bit))
+ return properties
diff --git a/analytical/tests/services/__init__.py b/analytical/tests/services/__init__.py
index c146338..6907280 100644
--- a/analytical/tests/services/__init__.py
+++ b/analytical/tests/services/__init__.py
@@ -2,11 +2,13 @@
Tests for the Analytical analytics services.
"""
+from analytical.tests.services.test_base import *
+from analytical.tests.services.test_chartbeat import *
from analytical.tests.services.test_clicky import *
from analytical.tests.services.test_console import *
from analytical.tests.services.test_crazy_egg import *
from analytical.tests.services.test_google_analytics import *
-from analytical.tests.services.test_kissinsights import *
-from analytical.tests.services.test_kissmetrics import *
+from analytical.tests.services.test_kiss_insights import *
+from analytical.tests.services.test_kiss_metrics import *
from analytical.tests.services.test_mixpanel import *
from analytical.tests.services.test_optimizely import *
diff --git a/analytical/tests/services/test_base.py b/analytical/tests/services/test_base.py
new file mode 100644
index 0000000..23dd147
--- /dev/null
+++ b/analytical/tests/services/test_base.py
@@ -0,0 +1,74 @@
+"""
+Tests for the base service.
+"""
+
+import re
+
+from django.contrib.auth.models import User, AnonymousUser
+from django.core.exceptions import ImproperlyConfigured
+from django.http import HttpRequest
+from django.test import TestCase
+
+from analytical.services.base import AnalyticalService
+from analytical.tests.utils import TestSettingsManager
+
+
+class DummyService(AnalyticalService):
+ def render_test(self, context):
+ return context
+
+
+class BaseServiceTestCase(TestCase):
+ """
+ Tests for the base service.
+ """
+
+ def setUp(self):
+ self.settings_manager = TestSettingsManager()
+ self.service = DummyService()
+
+ def tearDown(self):
+ self.settings_manager.revert()
+
+ def test_render(self):
+ r = self.service.render('test', 'foo')
+ self.assertEqual('foo', r)
+
+ def test_get_required_setting(self):
+ self.settings_manager.set(TEST='test')
+ r = self.service.get_required_setting('TEST', re.compile('es'), 'fail')
+ self.assertEqual('test', r)
+
+ def test_get_required_setting_missing(self):
+ self.settings_manager.delete('TEST')
+ self.assertRaises(ImproperlyConfigured,
+ self.service.get_required_setting, 'TEST', re.compile('es'),
+ 'fail')
+
+ def test_get_required_setting_wrong(self):
+ self.settings_manager.set(TEST='test')
+ self.assertRaises(ImproperlyConfigured,
+ self.service.get_required_setting, 'TEST', re.compile('foo'),
+ 'fail')
+
+ def test_get_identity_none(self):
+ context = {}
+ self.assertEqual(None, self.service.get_identity(context))
+
+ def test_get_identity_authenticated(self):
+ context = {'user': User(username='test')}
+ self.assertEqual('test', self.service.get_identity(context))
+
+ def test_get_identity_authenticated_request(self):
+ req = HttpRequest()
+ req.user = User(username='test')
+ context = {'request': req}
+ self.assertEqual('test', self.service.get_identity(context))
+
+ def test_get_identity_anonymous(self):
+ context = {'user': AnonymousUser()}
+ self.assertEqual(None, self.service.get_identity(context))
+
+ def test_get_identity_non_user(self):
+ context = {'user': object()}
+ self.assertEqual(None, self.service.get_identity(context))
diff --git a/analytical/tests/services/test_chartbeat.py b/analytical/tests/services/test_chartbeat.py
new file mode 100644
index 0000000..3f31fbb
--- /dev/null
+++ b/analytical/tests/services/test_chartbeat.py
@@ -0,0 +1,66 @@
+"""
+Tests for the Chartbeat service.
+"""
+
+from django.conf import settings
+from django.contrib.sites.models import Site
+from django.core.exceptions import ImproperlyConfigured
+from django.http import HttpRequest
+from django.test import TestCase
+
+from analytical.services.chartbeat import ChartbeatService
+from analytical.tests.utils import TestSettingsManager
+
+
+class ChartbeatTestCase(TestCase):
+ """
+ Tests for the Chartbeat service.
+ """
+
+ def setUp(self):
+ self.settings_manager = TestSettingsManager()
+ self.settings_manager.set(CHARTBEAT_USER_ID='12345')
+ self.service = ChartbeatService()
+
+ def tearDown(self):
+ self.settings_manager.revert()
+
+ def test_empty_locations(self):
+ self.assertEqual(self.service.render_head_bottom({}), "")
+ self.assertEqual(self.service.render_body_top({}), "")
+
+ def test_no_user_id(self):
+ self.settings_manager.delete('CHARTBEAT_USER_ID')
+ self.assertRaises(ImproperlyConfigured, ChartbeatService)
+
+ def test_wrong_user_id(self):
+ self.settings_manager.set(CHARTBEAT_USER_ID='1234')
+ self.assertRaises(ImproperlyConfigured, ChartbeatService)
+ self.settings_manager.set(CHARTBEAT_USER_ID='123456')
+ self.assertRaises(ImproperlyConfigured, ChartbeatService)
+
+ def test_rendering_init(self):
+ r = self.service.render_head_top({})
+ self.assertTrue('var _sf_startpt=(new Date()).getTime()' in r, r)
+
+ def test_rendering_setup(self):
+ r = self.service.render_body_bottom({'chartbeat_domain': "test.com"})
+ self.assertTrue('var _sf_async_config={uid:12345,domain:"test.com"};'
+ in r, r)
+
+ def test_rendering_setup_request_domain(self):
+ req = HttpRequest()
+ req.META['HTTP_HOST'] = 'test.com'
+ r = self.service.render_body_bottom({'request': req})
+ self.assertTrue('var _sf_async_config={uid:12345,domain:"test.com"};'
+ in r, r)
+
+ def test_rendering_setup_site(self):
+ installed_apps = list(settings.INSTALLED_APPS)
+ installed_apps.append('django.contrib.sites')
+ self.settings_manager.set(INSTALLED_APPS=installed_apps)
+ site = Site.objects.create(domain="test.com", name="test")
+ self.settings_manager.set(SITE_ID=site.id)
+ r = self.service.render_body_bottom({})
+ self.assertTrue('var _sf_async_config={uid:12345,domain:"test.com"};'
+ in r, r)
diff --git a/analytical/tests/services/test_clicky.py b/analytical/tests/services/test_clicky.py
index d50387d..68e6bf7 100644
--- a/analytical/tests/services/test_clicky.py
+++ b/analytical/tests/services/test_clicky.py
@@ -2,6 +2,9 @@
Tests for the Clicky service.
"""
+import re
+
+from django.contrib.auth.models import User
from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase
@@ -31,7 +34,7 @@ class ClickyTestCase(TestCase):
self.settings_manager.delete('CLICKY_SITE_ID')
self.assertRaises(ImproperlyConfigured, ClickyService)
- def test_wrong_id(self):
+ def test_wrong_site_id(self):
self.settings_manager.set(CLICKY_SITE_ID='1234567')
self.assertRaises(ImproperlyConfigured, ClickyService)
self.settings_manager.set(CLICKY_SITE_ID='123456789')
@@ -42,3 +45,16 @@ class ClickyTestCase(TestCase):
self.assertTrue('var clicky_site_id = 12345678;' in r, r)
self.assertTrue('src="http://in.getclicky.com/12345678ns.gif"' in r,
r)
+
+ def test_identify(self):
+ self.settings_manager.set(ANALYTICAL_AUTO_IDENTIFY=True)
+ r = self.service.render_body_bottom({'user': User(username='test')})
+ self.assertTrue(
+ 'var clicky_custom = {"session": {"username": "test"}};' in r,
+ r)
+
+ def test_custom(self):
+ custom = {'var1': 'val1', 'var2': 'val2'}
+ r = self.service.render_body_bottom({'clicky_custom': custom})
+ self.assertTrue(re.search('var clicky_custom = {.*'
+ '"var1": "val1", "var2": "val2".*};', r), r)
diff --git a/analytical/tests/services/test_console.py b/analytical/tests/services/test_console.py
index 24068a3..d9e5eb5 100644
--- a/analytical/tests/services/test_console.py
+++ b/analytical/tests/services/test_console.py
@@ -2,6 +2,7 @@
Tests for the console debugging service.
"""
+from django.contrib.auth.models import User
from django.test import TestCase
from analytical.services.console import ConsoleService
@@ -18,15 +19,27 @@ class ConsoleTestCase(TestCase):
def test_render_head_top(self):
r = self.service.render_head_top({})
self.assertTrue('rendering analytical_head_top tag' in r, r)
+ r = self.service.render_head_top({'user': User(username='test')})
+ self.assertTrue('rendering analytical_head_top tag for user test'
+ in r, r)
def test_render_head_bottom(self):
r = self.service.render_head_bottom({})
self.assertTrue('rendering analytical_head_bottom tag' in r, r)
+ r = self.service.render_head_bottom({'user': User(username='test')})
+ self.assertTrue('rendering analytical_head_bottom tag for user test'
+ in r, r)
def test_render_body_top(self):
r = self.service.render_body_top({})
self.assertTrue('rendering analytical_body_top tag' in r, r)
+ r = self.service.render_body_top({'user': User(username='test')})
+ self.assertTrue('rendering analytical_body_top tag for user test'
+ in r, r)
def test_render_body_bottom(self):
r = self.service.render_body_bottom({})
self.assertTrue('rendering analytical_body_bottom tag' in r, r)
+ r = self.service.render_body_bottom({'user': User(username='test')})
+ self.assertTrue('rendering analytical_body_bottom tag for user test'
+ in r, r)
diff --git a/analytical/tests/services/test_crazy_egg.py b/analytical/tests/services/test_crazy_egg.py
index 9dc1b17..9663440 100644
--- a/analytical/tests/services/test_crazy_egg.py
+++ b/analytical/tests/services/test_crazy_egg.py
@@ -31,7 +31,7 @@ class CrazyEggTestCase(TestCase):
self.settings_manager.delete('CRAZY_EGG_ACCOUNT_NUMBER')
self.assertRaises(ImproperlyConfigured, CrazyEggService)
- def test_wrong_id(self):
+ def test_wrong_account_number(self):
self.settings_manager.set(CRAZY_EGG_ACCOUNT_NUMBER='1234567')
self.assertRaises(ImproperlyConfigured, CrazyEggService)
self.settings_manager.set(CRAZY_EGG_ACCOUNT_NUMBER='123456789')
@@ -40,3 +40,9 @@ class CrazyEggTestCase(TestCase):
def test_rendering(self):
r = self.service.render_body_bottom({})
self.assertTrue('/1234/5678.js' in r, r)
+
+ def test_uservars(self):
+ context = {'crazy_egg_uservars': {1: 'foo', 2: 'bar'}}
+ r = self.service.render_body_bottom(context)
+ self.assertTrue("CE2.set(1, 'foo');" in r, r)
+ self.assertTrue("CE2.set(2, 'bar');" in r, r)
diff --git a/analytical/tests/services/test_google_analytics.py b/analytical/tests/services/test_google_analytics.py
index 37cb9d0..03c01b1 100644
--- a/analytical/tests/services/test_google_analytics.py
+++ b/analytical/tests/services/test_google_analytics.py
@@ -31,10 +31,26 @@ class GoogleAnalyticsTestCase(TestCase):
self.settings_manager.delete('GOOGLE_ANALYTICS_PROPERTY_ID')
self.assertRaises(ImproperlyConfigured, GoogleAnalyticsService)
- def test_wrong_id(self):
+ def test_wrong_property_id(self):
self.settings_manager.set(GOOGLE_ANALYTICS_PROPERTY_ID='wrong')
self.assertRaises(ImproperlyConfigured, GoogleAnalyticsService)
def test_rendering(self):
r = self.service.render_head_bottom({})
self.assertTrue("_gaq.push(['_setAccount', 'UA-123456-7']);" in r, r)
+ self.assertTrue("_gaq.push(['_trackPageview']);" in r, r)
+
+ def test_custom_vars(self):
+ context = {'google_analytics_custom_vars': [
+ (1, 'test1', 'foo'),
+ (5, 'test2', 'bar', 1),
+ ]}
+ r = self.service.render_head_bottom(context)
+ self.assertTrue("_gaq.push(['_setCustomVar', 1, 'test1', 'foo', 2]);"
+ in r, r)
+ self.assertTrue("_gaq.push(['_setCustomVar', 5, 'test2', 'bar', 1]);"
+ in r, r)
+ self.assertRaises(ValueError, self.service.render_head_bottom,
+ {'google_analytics_custom_vars': [(0, 'test', 'test')]})
+ self.assertRaises(ValueError, self.service.render_head_bottom,
+ {'google_analytics_custom_vars': [(6, 'test', 'test')]})
diff --git a/analytical/tests/services/test_kissinsights.py b/analytical/tests/services/test_kiss_insights.py
similarity index 57%
rename from analytical/tests/services/test_kissinsights.py
rename to analytical/tests/services/test_kiss_insights.py
index e89c781..8ac4da4 100644
--- a/analytical/tests/services/test_kissinsights.py
+++ b/analytical/tests/services/test_kiss_insights.py
@@ -2,10 +2,11 @@
Tests for the KISSinsights service.
"""
+from django.contrib.auth.models import User
from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase
-from analytical.services.kissinsights import KissInsightsService
+from analytical.services.kiss_insights import KissInsightsService
from analytical.tests.utils import TestSettingsManager
@@ -16,8 +17,8 @@ class KissInsightsTestCase(TestCase):
def setUp(self):
self.settings_manager = TestSettingsManager()
- self.settings_manager.set(KISSINSIGHTS_ACCOUNT_NUMBER='12345')
- self.settings_manager.set(KISSINSIGHTS_SITE_CODE='abc')
+ self.settings_manager.set(KISS_INSIGHTS_ACCOUNT_NUMBER='12345')
+ self.settings_manager.set(KISS_INSIGHTS_SITE_CODE='abc')
self.service = KissInsightsService()
def tearDown(self):
@@ -29,25 +30,34 @@ class KissInsightsTestCase(TestCase):
self.assertEqual(self.service.render_body_bottom({}), "")
def test_no_account_number(self):
- self.settings_manager.delete('KISSINSIGHTS_ACCOUNT_NUMBER')
+ self.settings_manager.delete('KISS_INSIGHTS_ACCOUNT_NUMBER')
self.assertRaises(ImproperlyConfigured, KissInsightsService)
def test_no_site_code(self):
- self.settings_manager.delete('KISSINSIGHTS_SITE_CODE')
+ self.settings_manager.delete('KISS_INSIGHTS_SITE_CODE')
self.assertRaises(ImproperlyConfigured, KissInsightsService)
def test_wrong_account_number(self):
- self.settings_manager.set(KISSINSIGHTS_ACCOUNT_NUMBER='1234')
+ self.settings_manager.set(KISS_INSIGHTS_ACCOUNT_NUMBER='1234')
self.assertRaises(ImproperlyConfigured, KissInsightsService)
- self.settings_manager.set(KISSINSIGHTS_ACCOUNT_NUMBER='123456')
+ self.settings_manager.set(KISS_INSIGHTS_ACCOUNT_NUMBER='123456')
self.assertRaises(ImproperlyConfigured, KissInsightsService)
def test_wrong_site_id(self):
- self.settings_manager.set(KISSINSIGHTS_SITE_CODE='ab')
+ self.settings_manager.set(KISS_INSIGHTS_SITE_CODE='ab')
self.assertRaises(ImproperlyConfigured, KissInsightsService)
- self.settings_manager.set(KISSINSIGHTS_SITE_CODE='abcd')
+ self.settings_manager.set(KISS_INSIGHTS_SITE_CODE='abcd')
self.assertRaises(ImproperlyConfigured, KissInsightsService)
def test_rendering(self):
r = self.service.render_body_top({})
self.assertTrue("//s3.amazonaws.com/ki.js/12345/abc.js" in r, r)
+
+ def test_identify(self):
+ self.settings_manager.set(ANALYTICAL_AUTO_IDENTIFY=True)
+ r = self.service.render_body_top({'user': User(username='test')})
+ self.assertTrue("_kiq.push(['identify', 'test']);" in r, r)
+
+ def test_show_survey(self):
+ r = self.service.render_body_top({'kiss_insights_show_survey': 1234})
+ self.assertTrue("_kiq.push(['showSurvey', 1234]);" in r, r)
diff --git a/analytical/tests/services/test_kissmetrics.py b/analytical/tests/services/test_kiss_metrics.py
similarity index 53%
rename from analytical/tests/services/test_kissmetrics.py
rename to analytical/tests/services/test_kiss_metrics.py
index a6e78b3..5829e23 100644
--- a/analytical/tests/services/test_kissmetrics.py
+++ b/analytical/tests/services/test_kiss_metrics.py
@@ -2,10 +2,11 @@
Tests for the KISSmetrics service.
"""
+from django.contrib.auth.models import User
from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase
-from analytical.services.kissmetrics import KissMetricsService
+from analytical.services.kiss_metrics import KissMetricsService
from analytical.tests.utils import TestSettingsManager
@@ -16,8 +17,8 @@ class KissMetricsTestCase(TestCase):
def setUp(self):
self.settings_manager = TestSettingsManager()
- self.settings_manager.set(KISSMETRICS_API_KEY='0123456789abcdef0123456'
- '789abcdef01234567')
+ self.settings_manager.set(KISS_METRICS_API_KEY='0123456789abcdef012345'
+ '6789abcdef01234567')
self.service = KissMetricsService()
def tearDown(self):
@@ -29,18 +30,29 @@ class KissMetricsTestCase(TestCase):
self.assertEqual(self.service.render_body_bottom({}), "")
def test_no_api_key(self):
- self.settings_manager.delete('KISSMETRICS_API_KEY')
+ self.settings_manager.delete('KISS_METRICS_API_KEY')
self.assertRaises(ImproperlyConfigured, KissMetricsService)
def test_wrong_api_key(self):
- self.settings_manager.set(KISSMETRICS_API_KEY='0123456789abcdef0123456'
- '789abcdef0123456')
+ self.settings_manager.set(KISS_METRICS_API_KEY='0123456789abcdef012345'
+ '6789abcdef0123456')
self.assertRaises(ImproperlyConfigured, KissMetricsService)
- self.settings_manager.set(KISSMETRICS_API_KEY='0123456789abcdef0123456'
- '789abcdef012345678')
+ self.settings_manager.set(KISS_METRICS_API_KEY='0123456789abcdef012345'
+ '6789abcdef012345678')
self.assertRaises(ImproperlyConfigured, KissMetricsService)
def test_rendering(self):
r = self.service.render_head_top({})
self.assertTrue("//doug1izaerwt3.cloudfront.net/0123456789abcdef012345"
"6789abcdef01234567.1.js" in r, r)
+
+ def test_identify(self):
+ self.settings_manager.set(ANALYTICAL_AUTO_IDENTIFY=True)
+ r = self.service.render_head_top({'user': User(username='test')})
+ self.assertTrue("_kmq.push(['identify', 'test']);" in r, r)
+
+ def test_event(self):
+ r = self.service.render_event('test_event', {'prop1': 'val1',
+ 'prop2': 'val2'})
+ self.assertEqual(r, "_kmq.push(['record', 'test_event', "
+ '{"prop1": "val1", "prop2": "val2"}]);')
diff --git a/analytical/tests/services/test_mixpanel.py b/analytical/tests/services/test_mixpanel.py
index 1d2ae68..92226c7 100644
--- a/analytical/tests/services/test_mixpanel.py
+++ b/analytical/tests/services/test_mixpanel.py
@@ -2,6 +2,7 @@
Tests for the Mixpanel service.
"""
+from django.contrib.auth.models import User
from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase
@@ -25,8 +26,8 @@ class MixpanelTestCase(TestCase):
def test_empty_locations(self):
self.assertEqual(self.service.render_head_top({}), "")
- self.assertEqual(self.service.render_head_bottom({}), "")
self.assertEqual(self.service.render_body_top({}), "")
+ self.assertEqual(self.service.render_body_bottom({}), "")
def test_no_token(self):
self.settings_manager.delete('MIXPANEL_TOKEN')
@@ -41,6 +42,18 @@ class MixpanelTestCase(TestCase):
self.assertRaises(ImproperlyConfigured, MixpanelService)
def test_rendering(self):
- r = self.service.render_body_bottom({})
- self.assertTrue("MixpanelLib('0123456789abcdef0123456789abcdef')" in r,
+ r = self.service.render_head_bottom({})
+ self.assertTrue(
+ "mpq.push(['init', '0123456789abcdef0123456789abcdef']);" in r,
r)
+
+ def test_identify(self):
+ self.settings_manager.set(ANALYTICAL_AUTO_IDENTIFY=True)
+ r = self.service.render_head_bottom({'user': User(username='test')})
+ self.assertTrue("mpq.push(['identify', 'test']);" in r, r)
+
+ def test_event(self):
+ r = self.service.render_event('test_event', {'prop1': 'val1',
+ 'prop2': 'val2'})
+ self.assertEqual(r, "mpq.push(['track', 'test_event', "
+ '{"prop1": "val1", "prop2": "val2"}]);')
diff --git a/analytical/tests/test_services.py b/analytical/tests/test_services.py
index d11dbf6..beb7187 100644
--- a/analytical/tests/test_services.py
+++ b/analytical/tests/test_services.py
@@ -18,30 +18,18 @@ class GetEnabledServicesTestCase(TestCase):
def setUp(self):
services.enabled_services = None
- services.load_services = self._dummy_load_services_1
+ services.load_services = lambda: 'test'
def tearDown(self):
+ services.enabled_services = None
services.load_services = load_services
- def test_no_reload(self):
+ def test_get_enabled_services(self):
result = services.get_enabled_services()
- self.assertEqual(result, 'test1')
- services.load_services = self._dummy_load_services_2
+ self.assertEqual(result, 'test')
+ services.load_services = lambda: 'test2'
result = services.get_enabled_services()
- self.assertEqual(result, 'test1')
-
- def test_reload(self):
- result = services.get_enabled_services()
- self.assertEqual(result, 'test1')
- services.load_services = self._dummy_load_services_2
- result = services.get_enabled_services(reload=True)
- self.assertEqual(result, 'test2')
-
- def _dummy_load_services_1(self):
- return 'test1'
-
- def _dummy_load_services_2(self):
- return 'test2'
+ self.assertEqual(result, 'test')
class LoadServicesTestCase(TestCase):
@@ -53,6 +41,7 @@ class LoadServicesTestCase(TestCase):
self.settings_manager = TestSettingsManager()
self.settings_manager.delete('ANALYTICAL_SERVICES')
self.settings_manager.delete('CLICKY_SITE_ID')
+ self.settings_manager.delete('CHARTBEAT_USER_ID')
self.settings_manager.delete('CRAZY_EGG_ACCOUNT_NUMBER')
self.settings_manager.delete('GOOGLE_ANALYTICS_PROPERTY_ID')
self.settings_manager.delete('KISSINSIGHTS_ACCOUNT_NUMBER')
@@ -60,9 +49,11 @@ class LoadServicesTestCase(TestCase):
self.settings_manager.delete('KISSMETRICS_API_KEY')
self.settings_manager.delete('MIXPANEL_TOKEN')
self.settings_manager.delete('OPTIMIZELY_ACCOUNT_NUMBER')
+ services.enabled_services = None
def tearDown(self):
self.settings_manager.revert()
+ services.enabled_services = None
def test_no_services(self):
self.assertEqual(load_services(), [])
diff --git a/analytical/tests/test_template_tags.py b/analytical/tests/test_template_tags.py
index de21617..f6a9d8e 100644
--- a/analytical/tests/test_template_tags.py
+++ b/analytical/tests/test_template_tags.py
@@ -2,8 +2,11 @@
Tests for the template tags.
"""
+from django.http import HttpRequest
+from django import template
from django.test import TestCase
+from analytical import services
from analytical.tests.utils import TestSettingsManager
@@ -14,6 +17,74 @@ class TemplateTagsTestCase(TestCase):
def setUp(self):
self.settings_manager = TestSettingsManager()
+ self.settings_manager.set(ANALYTICAL_SERVICES=[
+ 'analytical.services.console.ConsoleService'])
+ services.enabled_services = None
def tearDown(self):
self.settings_manager.revert()
+ services.enabled_services = None
+
+ def render_location_tag(self, location, context=None):
+ if context is None: context = {}
+ t = template.Template(
+ "{%% load analytical %%}{%% analytical_setup_%s %%}"
+ % location)
+ return t.render(template.Context(context))
+
+ def test_location_tags(self):
+ for l in ['head_top', 'head_bottom', 'body_top', 'body_bottom']:
+ r = self.render_location_tag(l)
+ self.assertTrue('rendering analytical_%s tag' % l in r, r)
+
+ def test_render_internal_ip(self):
+ self.settings_manager.set(ANALYTICAL_INTERNAL_IPS=['1.1.1.1'])
+ req = HttpRequest()
+ req.META['REMOTE_ADDR'] = '1.1.1.1'
+ for l in ['head_top', 'head_bottom', 'body_top', 'body_bottom']:
+ r = self.render_location_tag(l, {'request': req})
+ self.assertTrue('