changeset 227:423c39ee44e0

Rework the who's online middleware and template tag for #87.
author Brian Neal <bgneal@gmail.com>
date Tue, 06 Jul 2010 03:02:20 +0000
parents 405468b8e3b9
children d77e0dc772ad
files gpp/core/functions.py gpp/core/middleware.py gpp/core/models.py gpp/core/templatetags/core_tags.py gpp/forums/middleware.py gpp/forums/templatetags/forum_tags.py gpp/settings.py gpp/templates/core/whos_online_tag.html gpp/templates/forums/index.html gpp/templates/forums/whos_online_tag.html
diffstat 10 files changed, 143 insertions(+), 86 deletions(-) [+]
line wrap: on
line diff
--- a/gpp/core/functions.py	Thu Jun 10 03:31:57 2010 +0000
+++ b/gpp/core/functions.py	Tue Jul 06 03:02:20 2010 +0000
@@ -1,5 +1,6 @@
 """This file houses various core utility functions for GPP"""
 import datetime
+import re
 
 import django.core.mail
 from django.contrib.sites.models import Site
@@ -65,3 +66,17 @@
         year_range = "%d - %d" % (BASE_YEAR, curr_year)
 
     return 'Copyright (C) %s, SurfGuitar101.com' % year_range
+
+
+IP_PAT = re.compile('(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})')
+
+def get_ip(request):
+    """Returns the IP from the request or None if it cannot be retrieved."""
+    ip = request.META.get('HTTP_X_FORWARDED_FOR',
+            request.META.get('REMOTE_ADDR'))
+
+    if ip:
+        match = IP_PAT.match(ip)
+        ip = match.group(1) if match else None
+
+    return ip
--- a/gpp/core/middleware.py	Thu Jun 10 03:31:57 2010 +0000
+++ b/gpp/core/middleware.py	Tue Jul 06 03:02:20 2010 +0000
@@ -1,5 +1,13 @@
 """Common middleware for the entire project."""
+import datetime
+
 from django.contrib.auth import logout
+from django.conf import settings
+
+from core.models import UserLastVisit
+from core.models import AnonLastVisit
+from core.functions import get_ip
+
 
 class InactiveUserMiddleware(object):
     """
@@ -10,7 +18,59 @@
     Idea taken from: http://djangosnippets.org/snippets/1105/
     """
 
-    def process_request(self, request):
+    def process_view(self, request, view_func, view_args, view_kwargs):
         if request.user.is_authenticated() and not request.user.is_active:
             logout(request)
 
+
+ONLINE_COOKIE = 'sg101_online'  # online cookie name
+ONLINE_TIMEOUT = 10 * 60        # online cookie lifetime in seconds
+
+class WhosOnline(object):
+    """
+    This middleware class keeps track of which registered users have
+    been seen recently, and the number of unique unregistered users.
+    This middleware should come after the authentication middleware,
+    as we count on the user attribute being attached to the request.
+    """
+
+    def process_response(self, request, response):
+        """
+        Keep track of who is online.
+        """
+        if request.is_ajax():
+            return
+
+        if request.user.is_authenticated():
+            if request.COOKIES.get(ONLINE_COOKIE) is None:
+                # update the last seen timestamp
+                try:
+                    ulv = UserLastVisit.objects.get(user=request.user)
+                except UserLastVisit.DoesNotExist:
+                    ulv = UserLastVisit(user=request.user)
+
+                ulv.last_visit = datetime.datetime.now()
+                ulv.save()
+
+                # set a cookie to expire in 10 minutes or so
+                response.set_cookie(ONLINE_COOKIE, '1', max_age=ONLINE_TIMEOUT)
+        else:
+            if request.COOKIES.get(settings.CSRF_COOKIE_NAME) is not None:
+                # We have a non-authenticated user that has cookies enabled. This
+                # means we can track them.
+                if request.COOKIES.get(ONLINE_COOKIE) is None:
+                    # update the timestamp for this anonymous visitor
+                    ip = get_ip(request)
+                    if ip:
+                        try:
+                            alv = AnonLastVisit.objects.get(ip=ip)
+                        except AnonLastVisit.DoesNotExist:
+                            alv = AnonLastVisit(ip=ip)
+
+                        alv.last_visit = datetime.datetime.now()
+                        alv.save()
+
+                        # set a cookie to expire in 10 minutes or so
+                        response.set_cookie(ONLINE_COOKIE, '1', max_age=ONLINE_TIMEOUT)
+
+        return response
--- a/gpp/core/models.py	Thu Jun 10 03:31:57 2010 +0000
+++ b/gpp/core/models.py	Tue Jul 06 03:02:20 2010 +0000
@@ -1,5 +1,24 @@
 """
 This file contains the core Models used in gpp
-(None at this time).
 """
+from django.db import models
+from django.contrib import auth
 
+
+class UserLastVisit(models.Model):
+    """
+    This model represents timestamps indicating a user's last visit to the
+    site.
+    """
+    user = models.ForeignKey(auth.models.User, unique=True)
+    last_visit = models.DateTimeField(db_index=True)
+
+
+class AnonLastVisit(models.Model):
+    """
+    This model represents timestamps for the last visit from non-authenticated
+    users.
+    """
+    ip = models.CharField(max_length=16, db_index=True, unique=True)
+    last_visit = models.DateTimeField(db_index=True)
+
--- a/gpp/core/templatetags/core_tags.py	Thu Jun 10 03:31:57 2010 +0000
+++ b/gpp/core/templatetags/core_tags.py	Tue Jul 06 03:02:20 2010 +0000
@@ -1,8 +1,14 @@
 """
 Miscellaneous/utility template tags.
 """
+import datetime
+
 from django import template
 from django.conf import settings
+from django.core.cache import cache
+
+from core.models import UserLastVisit, AnonLastVisit
+
 
 register = template.Library()
 
@@ -21,3 +27,30 @@
 @register.inclusion_tag('core/comment_dialogs.html')
 def comment_dialogs():
     return {'MEDIA_URL': settings.MEDIA_URL}
+
+
+@register.inclusion_tag('core/whos_online_tag.html')
+def whos_online():
+    """
+    Displays a list of who is online.
+    """
+    info = cache.get('whos_online')
+    if info:
+        return info
+
+    now = datetime.datetime.now()
+    cutoff = now - datetime.timedelta(minutes=10, seconds=5)
+
+    info = {}
+    users = UserLastVisit.objects.filter(
+            last_visit__gte=cutoff).values_list('user__username', flat=True)
+    num_users = len(users)
+    info['num_users'] = num_users
+    info['users'] = sorted(users)
+
+    num_guests = AnonLastVisit.objects.filter(last_visit__gte=cutoff).count()
+    info['num_guests'] = num_guests
+    info['total'] = num_users + num_guests
+
+    cache.set('whos_online', info, 300)
+    return info
--- a/gpp/forums/middleware.py	Thu Jun 10 03:31:57 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-"""
-Middleware for the forums application.
-"""
-import datetime
-
-from django.core.cache import cache
-from django.conf import settings
-
-
-USER_ONLINE_TIMEOUT = datetime.timedelta(minutes=15)
-USERS_ONLINE_KEY = 'users_online'
-GUESTS_ONLINE_KEY = 'guests_online'
-USERS_CACHE_TIMEOUT = 60 * 60 * 24      # units are seconds
-
-
-class WhosOnline(object):
-    """
-    This middleware class keeps track of which registered users have
-    been seen recently, and the number of unique unregistered users.
-    We use the Django cache system to store this information.
-    This middleware should come after the authentication middleware,
-    as we count on the user attribute being attached to the request.
-    """
-
-    def process_request(self, request):
-        """
-        Keep track of who is online.
-        """
-        now = datetime.datetime.now()
-        cutoff = now - USER_ONLINE_TIMEOUT
-        users_online = cache.get(USERS_ONLINE_KEY, {})
-        guests_online = cache.get(GUESTS_ONLINE_KEY, {})
-
-        # update timestamp for user
-        if request.user.is_authenticated():
-            users_online[request.user.username] = now
-        else:
-            sid = request.COOKIES.get(settings.SESSION_COOKIE_NAME, '')
-            guests_online[sid] = now
-
-        # expire old records
-        for username, timestamp in users_online.items():
-            if timestamp < cutoff:
-                del users_online[username]
-
-        for sid, timestamp in guests_online.items():
-            if timestamp < cutoff:
-                del guests_online[sid]
-
-        cache.set(USERS_ONLINE_KEY, users_online, USERS_CACHE_TIMEOUT)
-        cache.set(GUESTS_ONLINE_KEY, guests_online, USERS_CACHE_TIMEOUT)
--- a/gpp/forums/templatetags/forum_tags.py	Thu Jun 10 03:31:57 2010 +0000
+++ b/gpp/forums/templatetags/forum_tags.py	Tue Jul 06 03:02:20 2010 +0000
@@ -128,26 +128,6 @@
     }
 
 
-@register.inclusion_tag('forums/whos_online_tag.html')
-def whos_online():
-    """
-    Displays a list of who is online.
-    """
-    users_online = cache.get('users_online', {})
-    guests_online = cache.get('guests_online', {})
-    num_users = len(users_online)
-    num_guests = len(guests_online)
-    total = num_users + num_guests
-    users = sorted(users_online.keys())
-
-    return {
-        'num_users': num_users,
-        'num_guests': num_guests,
-        'total': total,
-        'users': users,
-    }
-
-
 @register.inclusion_tag('forums/forum_stats_tag.html')
 def forum_stats():
     """
--- a/gpp/settings.py	Thu Jun 10 03:31:57 2010 +0000
+++ b/gpp/settings.py	Tue Jul 06 03:02:20 2010 +0000
@@ -82,7 +82,7 @@
         'debug_toolbar.middleware.DebugToolbarMiddleware',
         'django.contrib.auth.middleware.AuthenticationMiddleware',
         'gpp.core.middleware.InactiveUserMiddleware',
-        'gpp.forums.middleware.WhosOnline',
+        'gpp.core.middleware.WhosOnline',
         'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
     )
 else:
@@ -93,7 +93,7 @@
         'django.contrib.messages.middleware.MessageMiddleware',
         'django.contrib.auth.middleware.AuthenticationMiddleware',
         'gpp.core.middleware.InactiveUserMiddleware',
-        'gpp.forums.middleware.WhosOnline',
+        'gpp.core.middleware.WhosOnline',
         'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
     )
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gpp/templates/core/whos_online_tag.html	Tue Jul 06 03:02:20 2010 +0000
@@ -0,0 +1,11 @@
+<div id="whos-online">
+There {{ total|pluralize:"is,are"}} {{ total }} user{{ total|pluralize }} online: {{ num_users }} registered user{{ num_users|pluralize }} and {{ num_guests }} guest{{ num_guests|pluralize }}.
+{% if num_users %}
+Registered users:
+<ul class="inline-list">
+{% for user in users %}
+<li><a href="{% url bio-view_profile username=user %}">{{ user }}</a></li>
+{% endfor %}
+</ul>
+{% endif %}
+</div>
--- a/gpp/templates/forums/index.html	Thu Jun 10 03:31:57 2010 +0000
+++ b/gpp/templates/forums/index.html	Tue Jul 06 03:02:20 2010 +0000
@@ -1,6 +1,7 @@
 {% extends 'base.html' %}
 {% load cache %}
 {% load forum_tags %}
+{% load core_tags %}
 {% block custom_head %}
 {% for feed in feeds %}
 <link rel="alternate" type="application/rss+xml" title="{{ feed.name }}" href="{{ feed.feed }}" />
--- a/gpp/templates/forums/whos_online_tag.html	Thu Jun 10 03:31:57 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-<div id="whos-online">
-There {{ total|pluralize:"is,are"}} {{ total }} user{{ total|pluralize }} online: {{ num_users }} registered user{{ num_users|pluralize }} and {{ num_guests }} guest{{ num_guests|pluralize }}.
-{% if num_users %}
-Registered users:
-<ul class="inline-list">
-{% for user in users %}
-<li><a href="{% url bio-view_profile username=user %}">{{ user }}</a></li>
-{% endfor %}
-</ul>
-{% endif %}
-</div>