diff --git a/analytical/chartbeat/__init__.py b/analytical/chartbeat/__init__.py new file mode 100644 index 0000000..7fd9add --- /dev/null +++ b/analytical/chartbeat/__init__.py @@ -0,0 +1,145 @@ +""" +============================= +Chartbeat -- traffic analysis +============================= + +Chartbeat_ provides real-time analytics to websites and blogs. It shows +visitors, load times, and referring sites on a minute-by-minute basis. +The service also provides alerts the second your website crashes or +slows to a crawl. + +.. _Chartbeat: http://www.chartbeat.com/ + + +.. chartbeat-installation: + +Installation +============ + +You only need to do perform these steps if you are not using the +generic :ttag:`analytical.*` tags. If you are, skip to +:ref:`chartbeat-configuration`. + +In order to use the template tags, you need to add +:mod:`analytical.chartbeat` to the installed applications list in the +project :file:`settings.py` file:: + + INSTALLED_APPS = [ + ... + 'analytical.chartbeat', + ... + ] + +The Chartbeat tracking code is inserted into templates using template +tags. At the top of the template, load the :mod:`chartbeat` template +tag library. Then insert the :ttag:`chartbeat_top` tag at the top of +the head section, and the :ttag:`chartbeat_bottom` tag at the bottom of +the body section:: + + {% load chartbeat %} + +
+ {% chartbeat_top %} + + ... + + {% chartbeat_bottom %} + + + +Because these tags are used to measure page loading time, it is +important to place them as close as possible to the start and end of the +document. + +.. _chartbeat-configuration: + +Configuration +============= + +Before you can use the Chartbeat integration, you must first set your +User ID. + + +.. _chartbeat-user-id: + +Setting the User ID +------------------- + +Your Chartbeat account has a unique User ID. You can find your User ID +by visiting the Chartbeat `Add New Site`_ page. The second code snippet +contains a line that looks like this:: + + var _sf_async_config={uid:XXXXX,domain:"YYYYYYYYYY"}; + +Here, ``XXXXX`` is your User ID. Set :const:`CHARTBEAT_USER_ID` in the +project :file:`settings.py` file:: + + CHARTBEAT_SITE_ID = 'XXXXX' + +If you do not set a User ID, the tracking code will not be rendered. + +.. _`Add New Site`: http://chartbeat.com/code/ + + +.. _chartbeat-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:`INTERNAL_IPS` setting, the +tracking code is commented out. See :const:`ANALYTICAL_INTERNAL_IPS` +for important information about detecting the visitor IP address. + + +.. _chartbeat-domain: + +Setting the domain +------------------ + +The Javascript tracking code can send the website domain to Chartbeat. +If you use multiple subdomains this enables you to treat them as one +website in Chartbeat. If your project uses the sites framework, the +domain name of the current :class:`~django.contrib.sites.models.Site` +will be passed to Chartbeat automatically. You can modify this behavior +using the :const:`CHARTBEAT_AUTO_DOMAIN` setting:: + + CHARTBEAT_AUTO_DOMAIN = False + +Alternatively, you set the domain through the ``chartbeat_domain`` +context variable when you render the template:: + + context = RequestContext({'chartbeat_domain': 'example.com'}) + return some_template.render(context) + +It is annoying to do this for every view, so you may want to set it in +a context processor that you add to the +:data:`TEMPLATE_CONTEXT_PROCESSORS` list in :file:`settings.py`:: + + def chartbeat(request): + return {'chartbeat_domain': 'example.com'} + +The context domain overrides the domain from the current site. If no +domain is set, either explicitly or implicitly through the sites +framework, then no domain is sent, and Chartbeat will detect the domain +name from the URL. If your website uses just one domain, this will work +just fine. + + +---- + +Thanks go to Chartbeat for their support with the development of this +application. +""" + +chartbeat_service = { + 'head_top': ( + 'analytical.chartbeat.templatetags.chartbeat.ChartbeatTopNode', + 'top', + ), + 'body_bottom': ( + 'analytical.chartbeat.templatetags.chartbeat.ChartbeatBottomNode', + 'bottom', + ), +} diff --git a/analytical/chartbeat/templatetags/__init__.py b/analytical/chartbeat/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/analytical/chartbeat/templatetags/chartbeat.py b/analytical/chartbeat/templatetags/chartbeat.py new file mode 100644 index 0000000..cee59a2 --- /dev/null +++ b/analytical/chartbeat/templatetags/chartbeat.py @@ -0,0 +1,96 @@ +""" +Chartbeat template tags. +""" + +import re + +from django.conf import settings +from django.contrib.sites.models import Site +from django.core.exceptions import ImproperlyConfigured +from django.template import Library, Node, TemplateSyntaxError +from django.utils import simplejson + +from analytical.utils import is_internal_ip, disable_html + + +USER_ID_RE = re.compile(r'^\d{5}$') +INIT_CODE = """""" +SETUP_CODE = """ + +""" +DOMAIN_CONTEXT_KEY = 'chartbeat_domain' + + +register = Library() + + +@register.tag +def chartbeat_top(parser, token): + """ + Top Chartbeat template tag. + + Render the top Javascript code for Chartbeat. + """ + bits = token.split_contents() + if len(bits) > 1: + raise TemplateSyntaxError("'%s' takes no arguments" % bits[0]) + return ChartbeatTopNode() + +class ChartbeatTopNode(Node): + def render(self, context): + if is_internal_ip(context): + return disable_html(INIT_CODE, 'Chartbeat') + return INIT_CODE + + +@register.tag +def chartbeat_bottom(parser, token): + """ + Bottom Chartbeat template tag. + + Render the bottom Javascript code for Chartbeat. You must supply + your Chartbeat User ID (as a string) in the ``CHARTBEAT_USER_ID`` + setting. + """ + bits = token.split_contents() + if len(bits) > 1: + raise TemplateSyntaxError("'%s' takes no arguments" % bits[0]) + return ChartbeatBottomNode() + +class ChartbeatBottomNode(Node): + 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(self, context): + config = {'uid': self.user_id} + domain = context.get(DOMAIN_CONTEXT_KEY) + if domain is None and getattr(settings, 'CHARTBEAT_AUTO_DOMAIN', True): + try: + domain = Site.objects.get_current().domain + except (ImproperlyConfigured, Site.DoesNotExist): + pass + if domain is not None: + config['domain'] = domain + html = SETUP_CODE % {'config': simplejson.dumps(config)} + if is_internal_ip(context): + html = disable_html(html, 'Chartbeat') + return html diff --git a/analytical/clicky/__init__.py b/analytical/clicky/__init__.py index c4354af..a5dfb58 100644 --- a/analytical/clicky/__init__.py +++ b/analytical/clicky/__init__.py @@ -11,7 +11,7 @@ designed to be very easy to use. .. _Clicky: http://getclicky.com/ -.. clicky-template-tag: +.. clicky-installation: Installation ============ @@ -37,6 +37,7 @@ have the tag, it is useful to add it to your base template. Insert the tag at the bottom of the HTML body:: {% load clicky %} + ... {% clicky %} @@ -55,8 +56,8 @@ website Site ID. You can also customize the data that Clicky tracks. .. _clicky-site-id: -The Site ID ------------ +Setting the Site ID +------------------- Every website you track with Clicky gets its own Site ID, and the :ttag:`clicky` tag will include it in the rendered Javascript code. @@ -64,15 +65,21 @@ You can find the Site ID in the *Info* tab of the website *Preferences* page, in your Clicky account. Set :const:`CLICKY_SITE_ID` in the project :file:`settings.py` file:: - CLICKY_SITE_ID = '12345678' + CLICKY_SITE_ID = 'XXXXXXXX' If you do not set a Site ID, the tracking code will not be rendered. -Often you do not want to track clicks from your development or internal -IP addresses. By default, if the tag detects that the client comes from -any address in the :const:`INTERNAL_IPS` setting, the tracking code is -commented out. See :const:`ANALYTICAL_INTERNAL_IPS` for important -information about detecting the visitor IP address. + +.. _clicky-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:`INTERNAL_IPS` setting, the +tracking code is commented out. See :const:`ANALYTICAL_INTERNAL_IPS` +for important information about detecting the visitor IP address. .. _clicky-custom-data: @@ -106,34 +113,34 @@ Here is a table with the most important variables. All variable listed on the `customized tracking`_ documentation page can be set by replacing ``clicky_custom.`` with ``clicky_``. -================ =============== ===================================== -Context variable Clicky property Description -================ =============== ===================================== -clicky_session session_ Session data. A dictionary - containing ``username`` and/or - ``group`` keys. ----------------- --------------- ------------------------------------- -clicky_goal goal_ A succeeded goal. A dictionary - containing ``id`` and ``revenue`` - keys. ----------------- --------------- ------------------------------------- -clicky_href href_ The URL as tracked by Clicky. Default - is the page URL. ----------------- --------------- ------------------------------------- -clicky_title title_ The page title as tracked by Clicky. - Default is the HTML title. -================ =============== ===================================== +================== =============== =================================== +Context variable Clicky property Description +================== =============== =================================== +``clicky_session`` session_ Session data. A dictionary + containing ``username`` and/or + ``group`` keys. +------------------ --------------- ----------------------------------- +``clicky_goal`` goal_ A succeeded goal. A dictionary + containing ``id`` and ``revenue`` + keys. +------------------ --------------- ----------------------------------- +``clicky_href`` href_ The URL as tracked by Clicky. + Default is the page URL. +------------------ --------------- ----------------------------------- +``clicky_title`` title_ The page title as tracked by + Clicky. Default is the HTML title. +================== =============== =================================== + +By default, the username of an authenticated user is passed to Clicky +automatically in the session_ property, unless that property was set +explicitly. See :data:`ANALYTICAL_AUTO_IDENTIFY`. .. _`customized tracking`: http://getclicky.com/help/customization -.. _session: http://getclicky.com/help/customization#goal +.. _session: http://getclicky.com/help/customization#session .. _goal: http://getclicky.com/help/customization#goal .. _href: http://getclicky.com/help/customization#href .. _title: http://getclicky.com/help/customization#title -By default, the username of an authenticated user is passed to Clicky -automatically in the ``session.username`` property, unless that property -was set explicitly. See :data:`ANALYTICAL_AUTO_IDENTIFY`. - ---- diff --git a/analytical/services/chartbeat.py b/analytical/services/chartbeat.py deleted file mode 100644 index 278fbc4..0000000 --- a/analytical/services/chartbeat.py +++ /dev/null @@ -1,57 +0,0 @@ -""" -Chartbeat service. -""" - -import re - -from django.contrib.sites.models import Site, RequestSite -from django.core.exceptions import ImproperlyConfigured -from django.utils import simplejson - -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): - config = {'uid': self.user_id} - try: - config['domain'] = context[DOMAIN_CONTEXT_KEY] - except KeyError: - try: - config['domain'] = Site.objects.get_current().domain - except (ImproperlyConfigured, Site.DoesNotExist, AttributeError): - pass - return SETUP_CODE % {'config': simplejson.dumps(config)} diff --git a/analytical/templatetags/analytical.py b/analytical/templatetags/analytical.py index 7a4f5b8..19c0970 100644 --- a/analytical/templatetags/analytical.py +++ b/analytical/templatetags/analytical.py @@ -12,7 +12,7 @@ from django.utils.importlib import import_module DEFAULT_SERVICES = [ - 'analytical.chartbeat.ChartbeatService', + 'analytical.chartbeat.chartbeat_service', 'analytical.clicky.clicky_service', 'analytical.crazy_egg.CrazyEggService', 'analytical.google_analytics.GoogleAnalyticsService', diff --git a/docs/services/chartbeat.rst b/docs/services/chartbeat.rst index 907d3d7..6dd15b0 100644 --- a/docs/services/chartbeat.rst +++ b/docs/services/chartbeat.rst @@ -1,40 +1,3 @@ -Chartbeat -- traffic analysis -============================= +.. currentmodule:: analytical.chartbeat -Chartbeat_ provides real-time analytics to websites and blogs. It shows -visitors, load times, and referring sites on a minute-by-minute basis. -The service also provides alerts the second your website crashes or -slows to a crawl. - -.. _Chartbeat: http://www.chartbeat.com/ - -The Chartbeat service adds code both to the top of the head section and -the bottom of the body section. If the project uses the sites -framework, the domain name of the current website will be passed to -Chartbeat. Otherwise, Chartbeat will detect the domain name from the -URL. - - -Required settings ------------------ - -.. data:: CHARTBEAT_USER_ID - - The User ID:: - - CHARTBEAT_USER_ID = '12345' - - You can find the User ID by visiting the Chartbeat `Add New Site`_ - page. The second code snippet contains a line that looks like this:: - - var _sf_async_config={uid:XXXXX,domain:"YYYYYYYYYY"}; - - Here, ``XXXXX`` is your User ID. - -.. _`Add New Site`: http://chartbeat.com/code/ - - ----- - -Thanks go to Chartbeat for their support with the development of this -application. +.. automodule:: analytical.chartbeat