Skip to content

Commit

Permalink
resolving conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
bashu committed Sep 19, 2015
2 parents 0b9e29a + 63129a5 commit 27b2280
Show file tree
Hide file tree
Showing 11 changed files with 360 additions and 32 deletions.
196 changes: 196 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,199 @@ Features
* display how many active users there are on your site
* determine how many active users are on the same page within your site

* Optional "Active Visitors Map" to see where visitors are in the world

Requirements
============

As far as I am aware, the only requirement for django-tracking to work is a
modern version of Django. I developed the project on Django 1.0 alpha 2 and
beta 1. It is designed to work with the newforms-admin functionality.

If you wish to use a Google Map to display where your visitors are probably at,
you must have a `Google Maps API key
<http://code.google.com/intl/ro/apis/maps/signup.html>`_, which is free. You
are required to have the `GeoIP C API library
<http://geolite.maxmind.com/download/geoip/api/c/GeoIP.tar.gz>`_ installed.
You might want to grab the `GeoLite City binary
<http://www.maxmind.com/app/geolitecity>`_ unless you are a paying MaxMind
customer. This is the data file that ``django-tracking`` uses to translate an
IP into a location on the planet. Configuring this feature is discussed later.

Installation
============

Download ``django-tracking`` using *one* of the following methods:

pip
---

You can download the package from the `CheeseShop
<http://pypi.python.org/pypi/django-tracking/>`_ or use::

pip install django-tracking

to download and install ``django-tracking``.

easy_install
------------

You can download the package from the `CheeseShop <http://pypi.python.org/pypi/django-tracking/>`_ or use::

easy_install django-tracking

to download and install ``django-tracking``.

Checkout from BitBucket/GitHub/Google Code
------------------------------------------

Use one of the following commands::

hg clone http://bitbucket.org/codekoala/django-tracking
git clone http://github.com/codekoala/django-tracking.git
hg clone http://django-tracking.googlecode.com/hg/ django-tracking

Package Download
================

Download the latest ``.tar.gz`` file from the downloads section and extract it
somewhere you'll remember.

Configuration
=============

First of all, you must add this project to your list of ``INSTALLED_APPS`` in
``settings.py``::

INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
...
'tracking',
...
)

Run ``manage.py syncdb``. This creates a few tables in your database that are
necessary for operation.

Depending on how you wish to use this application, you have a few options:

Visitor Tracking
----------------

Add ``tracking.middleware.VisitorTrackingMiddleware`` to your
``MIDDLEWARE_CLASSES`` in ``settings.py``. It must be underneath the
``AuthenticationMiddleware``, so that ``request.user`` exists.

Automatic Visitor Clean-Up
++++++++++++++++++++++++++

If you want to have Django automatically clean past visitor information out
your database, put ``tracking.middleware.VisitorCleanUpMiddleware`` in your
``MIDDLEWARE_CLASSES``.

IP Banning
----------

Add ``tracking.middleware.BannedIPMiddleware`` to your ``MIDDLEWARE_CLASSES``
in ``settings.py``. I would recommend making this the very first item in
``MIDDLEWARE_CLASSES`` so your banned users do not have to drill through any
other middleware before Django realizes they don't belong on your site.

Visitors on Page (template tag)
-------------------------------

Make sure that ``django.core.context_processors.request`` is somewhere in your
``TEMPLATE_CONTEXT_PROCESSORS`` tuple. This context processor makes the
``request`` object accessible to your templates. This application uses the
``request`` object to determine what page the user is looking at in a template
tag.

Active Visitors Map
===================

If you're interested in seeing where your visitors are at a given point in
time, you might enjoy the active visitor map feature. Be sure you have added a
line to your main URLconf, as follows::

from django.conf.urls.defaults import *

urlpatterns = patterns('',
....
(r'^tracking/', include('tracking.urls')),
....
)

Next, set a couple of settings in your ``settings.py``:

* ``GOOGLE_MAPS_KEY``: Your very own Google Maps API key
* ``TRACKING_USE_GEOIP``: set this to ``True`` if you want to see markers on
the map
* ``GEOIP_PATH``: set this to the absolute path on the filesystem of your
``GeoIP.dat`` or ``GeoIPCity.dat`` or whatever file. It's usually something
like ``/usr/local/share/GeoIP.dat`` or ``/usr/share/GeoIP/GeoIP.dat``.
* ``GEOIP_CACHE_TYPE``: The type of caching to use when dealing with GeoIP data:

* ``0``: read database from filesystem, uses least memory.
* ``1``: load database into memory, faster performance but uses more
memory.
* ``2``: check for updated database. If database has been updated, reload
filehandle and/or memory cache.
* ``4``: just cache the most frequently accessed index portion of the
database, resulting in faster lookups than ``GEOIP_STANDARD``, but less
memory usage than ``GEOIP_MEMORY_CACHE`` - useful for larger databases
such as GeoIP Organization and GeoIP City. Note, for GeoIP Country,
Region and Netspeed databases, ``GEOIP_INDEX_CACHE`` is equivalent to
``GEOIP_MEMORY_CACHE``. *default*

* ``DEFAULT_TRACKING_TEMPLATE``: The template to use when generating the
visitor map. Defaults to ``tracking/visitor_map.html``.

When that's done, you should be able to go to ``/tracking/map/`` on your site
(replacing ``tracking`` with whatever prefix you chose to use in your URLconf,
obviously). The default template relies upon jQuery for its awesomeness, but
you're free to use whatever you would like.

Usage
=====

To display the number of active users there are in one of your templates, make
sure you have ``{% load tracking_tags %}`` somewhere in your template and do
something like this::

{% visitors_on_site as visitors %}
<p>
{{ visitors }} active user{{ visitors|pluralize }}
</p>

If you also want to show how many people are looking at the same page::

{% visitors_on_page as same_page %}
<p>
{{ same_page }} of {{ visitors }} active user{{ visitors|pluralize }}
{% ifequal same_page 1 %}is{% else %}are{% endifequal %} reading this page
</p>

If you don't want particular areas of your site to be tracked, you may define a
list of prefixes in your ``settings.py`` using the ``NO_TRACKING_PREFIXES``. For
example, if you didn't want visits to the ``/family/`` section of your website,
set ``NO_TRACKING_PREFIXES`` to ``['/family/']``.

If you don't want to count certain user-agents, such as Yahoo!'s Slurp and
Google's Googlebot, you may add keywords to your visitor tracking in your
Django administration interface. Look for "Untracked User-Agents" and add a
keyword that distinguishes a particular user-agent. Any visitors with the
keyword in their user-agent string will not be tracked.

By default, active users include any visitors within the last 10 minutes. If
you would like to override that setting, just set ``TRACKING_TIMEOUT`` to however
many minutes you want in your ``settings.py``.

For automatic visitor clean-up, any records older than 24 hours are removed by
default. If you would like to override that setting, set
``TRACKING_CLEANUP_TIMEOUT`` to however many hours you want in your
``settings.py``.

2 changes: 1 addition & 1 deletion tracking/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION = (0, 4, 0)
VERSION = (0, 4, 1)

__version__ = '.'.join([str(n) for n in VERSION])

11 changes: 10 additions & 1 deletion tracking/listeners.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,21 @@

log = logging.getLogger('tracking.listeners')

importing_exceptions = (ImportError, )
#Try to have the ImproperlyConfigured error available to catch that error during package installation
#...looks like newer versions of Django might have broken only receiving an ImportError
try:
from django.core.exceptions import ImproperlyConfigured
importing_exceptions = (ImportError, ImproperlyConfigured)
except ImportError:
pass

try:
from django.core.cache import cache
from django.db.models.signals import post_save, post_delete

from tracking.models import UntrackedUserAgent, BannedIP
except ImportError:
except importing_exceptions:
pass
else:

Expand Down
29 changes: 9 additions & 20 deletions tracking/middleware.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from datetime import datetime, timedelta
from datetime import timedelta
from django.utils import timezone
import logging
import re
Expand Down Expand Up @@ -57,9 +57,6 @@ def prefixes(self):
return self._prefixes

def process_request(self, request):
# don't process AJAX requests
if request.is_ajax(): return

# create some useful variables
ip_address = utils.get_ip(request)
user_agent = unicode(request.META.get('HTTP_USER_AGENT', '')[:255], errors='ignore')
Expand All @@ -79,13 +76,9 @@ def process_request(self, request):
log.debug('Not tracking UA "%s" because of keyword: %s' % (user_agent, ua.keyword))
return

if hasattr(request, 'session') and request.session.session_key:
# use the current session key if we can
session_key = request.session.session_key
else:
# otherwise just fake a session key
session_key = '%s:%s' % (ip_address, user_agent)
session_key = session_key[:40]
if not request.session.session_key:
request.session.save()
session_key = request.session.session_key

# ensure that the request.path does not begin with any of the prefixes
for prefix in self.prefixes:
Expand All @@ -95,11 +88,7 @@ def process_request(self, request):

# if we get here, the URL needs to be tracked
# determine what time it is
now = datetime.now()
if getattr(settings, 'USE_TZ', False):
import pytz
tz = pytz.timezone(settings.TIME_ZONE)
now = tz.localize(now)
now = timezone.now()

attrs = {
'session_key': session_key,
Expand All @@ -124,9 +113,9 @@ def process_request(self, request):
visitor.session_key = session_key
log.debug('Using existing visitor for IP %s / UA %s: %s' % (ip_address, user_agent, visitor.id))
else:
# it's probably safe to assume that the visitor is brand new
visitor = Visitor(**attrs)
log.debug('Created a new visitor: %s' % attrs)
visitor, created = Visitor.objects.get_or_create(**attrs)
if created:
log.debug('Created a new visitor: %s' % attrs)
except:
return

Expand Down Expand Up @@ -165,7 +154,7 @@ def process_request(self, request):

if str(timeout).isdigit():
log.debug('Cleaning up visitors older than %s hours' % timeout)
timeout = timezone.now() - timedelta(hours=int(timeout)) # datetime.now()
timeout = timezone.now() - timedelta(hours=int(timeout))
Visitor.objects.filter(last_update__lte=timeout).delete()

class BannedIPMiddleware:
Expand Down
64 changes: 64 additions & 0 deletions tracking/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations
from django.conf import settings


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name='BannedIP',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('ip_address', models.IPAddressField(help_text='The IP address that should be banned', verbose_name=b'IP Address')),
],
options={
'ordering': ('ip_address',),
'verbose_name': 'Banned IP',
'verbose_name_plural': 'Banned IPs',
},
bases=(models.Model,),
),
migrations.CreateModel(
name='UntrackedUserAgent',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('keyword', models.CharField(help_text='Part or all of a user-agent string. For example, "Googlebot" here will be found in "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" and that visitor will not be tracked.', max_length=100, verbose_name='keyword')),
],
options={
'ordering': ('keyword',),
'verbose_name': 'Untracked User-Agent',
'verbose_name_plural': 'Untracked User-Agents',
},
bases=(models.Model,),
),
migrations.CreateModel(
name='Visitor',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('session_key', models.CharField(max_length=40)),
('ip_address', models.CharField(max_length=20)),
('user_agent', models.CharField(max_length=255)),
('referrer', models.CharField(max_length=255)),
('url', models.CharField(max_length=255)),
('page_views', models.PositiveIntegerField(default=0)),
('session_start', models.DateTimeField()),
('last_update', models.DateTimeField()),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, null=True)),
],
options={
'ordering': ('-last_update',),
},
bases=(models.Model,),
),
migrations.AlterUniqueTogether(
name='visitor',
unique_together=set([('session_key', 'ip_address')]),
),
]
25 changes: 25 additions & 0 deletions tracking/migrations/0002_auto_20141023_0732.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations
import datetime


class Migration(migrations.Migration):

dependencies = [
('tracking', '0001_initial'),
]

operations = [
migrations.AlterField(
model_name='visitor',
name='last_update',
field=models.DateTimeField(default=datetime.datetime(2014, 10, 23, 7, 32, 14, 787217)),
),
migrations.AlterField(
model_name='visitor',
name='session_start',
field=models.DateTimeField(default=datetime.datetime(2014, 10, 23, 7, 32, 14, 787119)),
),
]
Loading

0 comments on commit 27b2280

Please sign in to comment.