diff --git a/analytical/templatetags/google_analytics.py b/analytical/templatetags/google_analytics.py index 80c04d7..f3a9f57 100644 --- a/analytical/templatetags/google_analytics.py +++ b/analytical/templatetags/google_analytics.py @@ -4,6 +4,7 @@ from __future__ import absolute_import +import decimal import re from django.conf import settings @@ -48,9 +49,16 @@ "'%(value)s', %(scope)s]);" SITE_SPEED_CODE = "_gaq.push(['_trackPageLoadTime']);" ANONYMIZE_IP_CODE = "_gaq.push (['_gat._anonymizeIp']);" +SAMPLE_RATE_CODE = "_gaq.push (['_gat._setSampleRate', '%s']);" +SITE_SPEED_SAMPLE_RATE_CODE = "_gaq.push (['_gat._setSiteSpeedSampleRate', '%s']);" +SESSION_COOKIE_TIMEOUT_CODE = "_gaq.push (['_gat._setSessionCookieTimeout', '%s']);" +VISITOR_COOKIE_TIMEOUT_CODE = "_gaq.push (['_gat._setVisitorCookieTimeout', '%s']);" DEFAULT_SOURCE = ("'https://ssl' : 'http://www'", "'.google-analytics.com/ga.js'") DISPLAY_ADVERTISING_SOURCE = ("'https://' : 'http://'", "'stats.g.doubleclick.net/dc.js'") +ZEROPLACES = decimal.Decimal('0') +TWOPLACES = decimal.Decimal('0.01') + register = Library() @@ -134,8 +142,37 @@ def _get_other_commands(self, context): 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) + + sampleRate = getattr(settings, 'GOOGLE_ANALYTICS_SAMPLE_RATE', False) + if sampleRate is not False: + value = decimal.Decimal(sampleRate) + if not 0 <= value <= 100: + raise AnalyticalException("'GOOGLE_ANALYTICS_SAMPLE_RATE' must be >= 0 and <= 100") + commands.append(SAMPLE_RATE_CODE % value.quantize(TWOPLACES)) + + siteSpeedSampleRate = getattr(settings, 'GOOGLE_ANALYTICS_SITE_SPEED_SAMPLE_RATE', False) + if siteSpeedSampleRate is not False: + value = decimal.Decimal(siteSpeedSampleRate) + if not 0 <= value <= 100: + raise AnalyticalException("'GOOGLE_ANALYTICS_SITE_SPEED_SAMPLE_RATE' must be >= 0 and <= 100") + commands.append(SITE_SPEED_SAMPLE_RATE_CODE % value.quantize(TWOPLACES)) + + sessionCookieTimeout = getattr(settings, 'GOOGLE_ANALYTICS_SESSION_COOKIE_TIMEOUT', False) + if sessionCookieTimeout is not False: + value = decimal.Decimal(sessionCookieTimeout) + if value < 0: + raise AnalyticalException("'GOOGLE_ANALYTICS_SESSION_COOKIE_TIMEOUT' must be >= 0") + commands.append(SESSION_COOKIE_TIMEOUT_CODE % value.quantize(ZEROPLACES)) + + visitorCookieTimeout = getattr(settings, 'GOOGLE_ANALYTICS_VISITOR_COOKIE_TIMEOUT', False) + if visitorCookieTimeout is not False: + value = decimal.Decimal(visitorCookieTimeout) + if value < 0: + raise AnalyticalException("'GOOGLE_ANALYTICS_VISITOR_COOKIE_TIMEOUT' must be >= 0") + commands.append(VISITOR_COOKIE_TIMEOUT_CODE % value.quantize(ZEROPLACES)) return commands diff --git a/analytical/tests/test_tag_google_analytics.py b/analytical/tests/test_tag_google_analytics.py index 74ce256..d2714af 100644 --- a/analytical/tests/test_tag_google_analytics.py +++ b/analytical/tests/test_tag_google_analytics.py @@ -104,6 +104,82 @@ def test_anonymize_ip_not_present(self): r = GoogleAnalyticsNode().render(Context()) self.assertFalse("_gaq.push (['_gat._anonymizeIp']);" in r, r) + @override_settings(GOOGLE_ANALYTICS_SAMPLE_RATE=0.0) + def test_set_sample_rate_min(self): + r = GoogleAnalyticsNode().render(Context()) + self.assertTrue("_gaq.push (['_gat._setSampleRate', '0.00']);" in r, r) + + @override_settings(GOOGLE_ANALYTICS_SAMPLE_RATE='100.00') + def test_set_sample_rate_max(self): + r = GoogleAnalyticsNode().render(Context()) + self.assertTrue("_gaq.push (['_gat._setSampleRate', '100.00']);" in r, r) + + @override_settings(GOOGLE_ANALYTICS_SAMPLE_RATE=-1) + def test_exception_whenset_sample_rate_too_small(self): + context = Context() + self.assertRaises(AnalyticalException, GoogleAnalyticsNode().render, + context) + + @override_settings(GOOGLE_ANALYTICS_SAMPLE_RATE=101) + def test_exception_when_set_sample_rate_too_large(self): + context = Context() + self.assertRaises(AnalyticalException, GoogleAnalyticsNode().render, + context) + + @override_settings(GOOGLE_ANALYTICS_SITE_SPEED_SAMPLE_RATE=0.0) + def test_set_site_speed_sample_rate_min(self): + r = GoogleAnalyticsNode().render(Context()) + self.assertTrue("_gaq.push (['_gat._setSiteSpeedSampleRate', '0.00']);" in r, r) + + @override_settings(GOOGLE_ANALYTICS_SITE_SPEED_SAMPLE_RATE='100.00') + def test_set_site_speed_sample_rate_max(self): + r = GoogleAnalyticsNode().render(Context()) + self.assertTrue("_gaq.push (['_gat._setSiteSpeedSampleRate', '100.00']);" in r, r) + + @override_settings(GOOGLE_ANALYTICS_SITE_SPEED_SAMPLE_RATE=-1) + def test_exception_whenset_site_speed_sample_rate_too_small(self): + context = Context() + self.assertRaises(AnalyticalException, GoogleAnalyticsNode().render, + context) + + @override_settings(GOOGLE_ANALYTICS_SITE_SPEED_SAMPLE_RATE=101) + def test_exception_when_set_site_speed_sample_rate_too_large(self): + context = Context() + self.assertRaises(AnalyticalException, GoogleAnalyticsNode().render, + context) + + @override_settings(GOOGLE_ANALYTICS_SESSION_COOKIE_TIMEOUT=0) + def test_set_session_cookie_timeout_min(self): + r = GoogleAnalyticsNode().render(Context()) + self.assertTrue("_gaq.push (['_gat._setSessionCookieTimeout', '0']);" in r, r) + + @override_settings(GOOGLE_ANALYTICS_SESSION_COOKIE_TIMEOUT='10000') + def test_set_session_cookie_timeout_as_string(self): + r = GoogleAnalyticsNode().render(Context()) + self.assertTrue("_gaq.push (['_gat._setSessionCookieTimeout', '10000']);" in r, r) + + @override_settings(GOOGLE_ANALYTICS_SESSION_COOKIE_TIMEOUT=-1) + def test_exception_when_set_session_cookie_timeout_too_small(self): + context = Context() + self.assertRaises(AnalyticalException, GoogleAnalyticsNode().render, + context) + + @override_settings(GOOGLE_ANALYTICS_VISITOR_COOKIE_TIMEOUT=0) + def test_set_visitor_cookie_timeout_min(self): + r = GoogleAnalyticsNode().render(Context()) + self.assertTrue("_gaq.push (['_gat._setVisitorCookieTimeout', '0']);" in r, r) + + @override_settings(GOOGLE_ANALYTICS_VISITOR_COOKIE_TIMEOUT='10000') + def test_set_visitor_cookie_timeout_as_string(self): + r = GoogleAnalyticsNode().render(Context()) + self.assertTrue("_gaq.push (['_gat._setVisitorCookieTimeout', '10000']);" in r, r) + + @override_settings(GOOGLE_ANALYTICS_VISITOR_COOKIE_TIMEOUT=-1) + def test_exception_when_set_visitor_cookie_timeout_too_small(self): + context = Context() + self.assertRaises(AnalyticalException, GoogleAnalyticsNode().render, + context) + @override_settings(GOOGLE_ANALYTICS_PROPERTY_ID='UA-123456-7', GOOGLE_ANALYTICS_TRACKING_STYLE=TRACK_MULTIPLE_DOMAINS, diff --git a/docs/services/google_analytics.rst b/docs/services/google_analytics.rst index 1afa354..7add8b7 100644 --- a/docs/services/google_analytics.rst +++ b/docs/services/google_analytics.rst @@ -1,6 +1,6 @@ -==================================== -Google Analytics -- traffic analysis -==================================== +====================================== + Google Analytics -- traffic analysis +====================================== `Google Analytics`_ is the well-known web analytics service from Google. The product is aimed more at marketers than webmasters or @@ -192,7 +192,7 @@ context processor, the latter clobbers the former. .. _google-analytics-anonimyze-ips: Anonymize IPs ----------------- +------------- You can enable the `IP anonymization`_ feature by setting the :const:`GOOGLE_ANALYTICS_ANONYMIZE_IP` configuration setting:: @@ -205,3 +205,65 @@ 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 + + +.. _google-analytics-sample-rate: + +Sample Rate +----------- + +You can configure the `Sample Rate`_ feature by setting the +:const:`GOOGLE_ANALYTICS_SAMPLE_RATE` configuration setting:: + + GOOGLE_ANALYTICS_SAMPLE_RATE = 10 + +The value is a percentage and can be between 0 and 100 and can be a string or +decimal value of with up to two decimal places. + +.. _`Sample Rate`_: https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiBasicConfiguration#_setsamplerate + + +.. _google-analytics-site-speed-sample-rate: + +Site Speed Sample Rate +---------------------- + +You can configure the `Site Speed Sample Rate`_ feature by setting the +:const:`GOOGLE_ANALYTICS_SITE_SPEED_SAMPLE_RATE` configuration setting:: + + GOOGLE_ANALYTICS_SITE_SPEED_SAMPLE_RATE = 10 + +The value is a percentage and can be between 0 and 100 and can be a string or +decimal value of with up to two decimal places. + +.. _`Site Speed Sample Rate`_: https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiBasicConfiguration#_setsitespeedsamplerate + + +.. _google-analytics-session-cookie-timeout: + +Session Cookie Timeout +---------------------- + +You can configure the `Session Cookie Timeout`_ feature by setting the +:const:`GOOGLE_ANALYTICS_SESSION_COOKIE_TIMEOUT` configuration setting:: + + GOOGLE_ANALYTICS_SESSION_COOKIE_TIMEOUT = 3600000 + +The value is the session cookie timeout in milliseconds or 0 to delete the cookie when the browser is closed. + +.. _`Session Cookie Timeout`_: https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiBasicConfiguration#_setsessioncookietimeout + + +.. _google-analytics-visitor-cookie-timeout: + +Visitor Cookie Timeout +---------------------- + +You can configure the `Visitor Cookie Timeout`_ feature by setting the +:const:`GOOGLE_ANALYTICS_VISITOR_COOKIE_TIMEOUT` configuration setting:: + + GOOGLE_ANALYTICS_VISITOR_COOKIE_TIMEOUT = 3600000 + +The value is the visitor cookie timeout in milliseconds or 0 to delete the cookie when the browser is closed. + +.. _`Visitor Cookie Timeout`_: https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiBasicConfiguration#_setvisitorcookietimeout