Browse Source

More accurate online tracking #76

Ralfp 12 years ago
parent
commit
d6e829423d

+ 0 - 1
misago/apps/admin/online/forms.py

@@ -9,7 +9,6 @@ class SearchSessionsForm(Form):
     type = forms.ChoiceField(choices=(
                                       ('all', _("All types")),
                                       ('registered', _("Registered Members Sessions")),
-                                      ('hidden', _("Hidden Sessions")),
                                       ('guest', _("Guests Sessions")),
                                       ('crawler', _("Crawler Sessions")),
                                       ), required=False)

+ 16 - 11
misago/apps/index.py

@@ -36,27 +36,32 @@ def index(request):
             ranks_list.append(rank_entry)
             ranks_dict[rank.pk] = rank_entry
         if ranks_dict:
-            for session in Session.objects.select_related('user').filter(rank__in=ranks_dict.keys()).filter(last__gte=timezone.now() - timedelta(minutes=10)).filter(user__isnull=False):
+            for session in Session.objects.select_related('user').filter(rank__in=ranks_dict.keys()).filter(last__gte=timezone.now() - timedelta(seconds=request.settings['sessions_tracker_sync_frequency'])).filter(user__isnull=False):
                 if not session.user_id in users_list:
                     ranks_dict[session.user.rank_id]['online'].append(session.user)
                     users_list.append(session.user_id)
             # Assert we are on list
             if (request.user.is_authenticated() and request.user.rank_id in ranks_dict.keys()
-                and not request.user.id in users_list):
+                and not request.user.pk in users_list):
                     ranks_dict[request.user.rank_id]['online'].append(request.user)
+                    users_list.append(request.user.pk)
+            cache.set('team_users_online', users_list, request.settings['sessions_tracker_sync_frequency'])
             del ranks_dict
             del users_list
-        cache.set('ranks_online', ranks_list, 300)
+        cache.set('ranks_online', ranks_list, request.settings['sessions_tracker_sync_frequency'])
 
+    print cache.get('team_users_online')
     # Users online
-    users_online = cache.get('users_online', 'nada')
-    if users_online == 'nada':
-        users_online = Session.objects.filter(matched=True).filter(crawler__isnull=True).filter(last__gte=timezone.now() - timedelta(seconds=300)).count()
-        cache.set('users_online', users_online, 300)
-    if not users_online and not request.user.is_crawler():
-        # Cheatey trick to make sure we'll never display
-        # zero users online to human client
-        users_online = 1
+    users_online = {
+                    'members': request.onlines.members(),
+                    'all': request.onlines.all(),
+                   }
+    if not users_online['members'] and request.user.is_authenticated():
+        users_online['members'] += 1
+    if users_online['members'] > users_online['all']:
+        users_online['all'] = users_online['members']
+    if users_online['members'] >= users_online['all'] and request.user.is_anonymous():
+        users_online['all'] += 1
 
     # Load reads tracker and build forums list
     reads_tracker = ForumsTracker(request.user)

+ 6 - 0
misago/apps/signin/views.py

@@ -1,3 +1,4 @@
+from django.core.cache import cache
 from django.core.urlresolvers import reverse
 from django.shortcuts import redirect
 from django.template import RequestContext
@@ -106,4 +107,9 @@ def signout(request):
     request.messages.set_flash(Message(_("You have been signed out.")), 'info', 'security')
     if request.firewall.admin:
         return redirect(reverse(site.get_admin_index()))
+    else:
+        request.onlines.sign_out()
+        if user.pk in cache.get('team_users_online', []):
+            print 'SYNC TEH ONLINEZ'
+            cache.delete_many(['team_users_online', 'ranks_online'])
     return redirect(reverse('index'))

+ 2 - 1
misago/auth.py

@@ -120,4 +120,5 @@ def sign_user_in(request, user):
                         )
     user.save(force_update=True)
     request.session.set_user(user)
-    request.session.set_hidden(user.hide_activity > 0)
+    if not request.firewall.admin:
+        request.onlines.sign_in()

+ 2 - 1
misago/fixtures/accountssetings.py

@@ -8,9 +8,10 @@ settings_fixture = (
         'description': _("Those settings allow you to increase security of your members accounts."),
         'settings': (
             ('account_activation', {
+                'value':        "none",
                 'type':         "string",
                 'input':        "choice",
-                'extra':        {'choices': [('', _("No validation required")), ('user', _("Activation Token sent to User")), ('admin', _("Activation by Administrator")), ('block', _("Dont allow new registrations"))]},
+                'extra':        {'choices': [('none', _("No validation required")), ('user', _("Activation Token sent to User")), ('admin', _("Activation by Administrator")), ('block', _("Dont allow new registrations"))]},
                 'separator':    _("Users Registrations"),
                 'name':         _("New accounts validation"),
             }),

+ 10 - 0
misago/fixtures/onlinemonitor.py

@@ -0,0 +1,10 @@
+from misago.utils.fixtures import load_monitor_fixture
+
+monitor_fixture = {
+                   'online_members': 0,
+                   'online_all': 0,
+                  }
+
+
+def load():
+    load_monitor_fixture(monitor_fixture)

+ 8 - 0
misago/fixtures/signingsettings.py

@@ -15,6 +15,14 @@ settings_fixture = (
                 'name':         _("Check IP on session authorization"),
                 'description':  _("Makes sessions more secure, but can cause problems with proxies and VPN's."),
             }),
+            ('sessions_tracker_sync_frequency', {
+                'value':        300,
+                'type':         "integer",
+                'input':        "text",
+                'extra':         {'min': 15},
+                'name':         _("Online Tracker Updates Frequency"),
+                'description':  _("How often do you want online tracker to synchronize itself with database? Low numbers provide good accuracy at cost of database traffic while great number provides your users with general idea how many are currently online while at same time keeping stress off your database."),
+            }),
             ('remember_me_allow', {
                 'value':        True,
                 'type':         "boolean",

+ 0 - 3
misago/middleware/session.py

@@ -12,9 +12,6 @@ class SessionMiddleware(object):
             request.session = HumanSession(request)
             request.user = request.session.get_user()
 
-            if request.user.is_authenticated():
-                request.session.set_hidden(request.user.hide_activity > 0)
-
     def process_response(self, request, response):
         try:
             # Sync last visit date

+ 14 - 0
misago/middleware/user.py

@@ -2,6 +2,7 @@ from django.conf import settings
 from django.utils import timezone
 from django.utils.translation import ugettext_lazy as _
 from misago.messages import Message
+from misago.onlines import MembersOnline
 
 def set_timezone(new_tz):
     if settings.USE_TZ:
@@ -14,6 +15,11 @@ def set_timezone(new_tz):
 
 class UserMiddleware(object):
     def process_request(self, request):
+        request.onlines = MembersOnline(request.monitor, request.settings['sessions_tracker_sync_frequency'])
+
+        if request.session.created() and not request.firewall.admin:
+            request.onlines.new_session()
+
         if request.user.is_authenticated():
             # Set user timezone and rank
             request.session.rank = request.user.rank_id
@@ -21,8 +27,16 @@ class UserMiddleware(object):
 
             # Display "welcome back!" message
             if request.session.remember_me:
+                request.onlines.sign_in()
                 request.messages.set_message(Message(_("Welcome back, %(username)s! We've signed you in automatically for your convenience.") % {'username': request.user.username}), 'info')
         else:
             # Set guest's timezone and empty rank
             set_timezone(request.settings['default_timezone'])
             request.session.rank = None
+
+    def process_response(self, request, response):
+        try:
+            request.onlines.sync()
+        except AttributeError:
+            pass
+        return response

+ 0 - 1
misago/models/sessionmodel.py

@@ -13,7 +13,6 @@ class Session(models.Model):
     rank = models.ForeignKey('Rank', related_name='sessions', null=True, on_delete=models.SET_NULL)
     admin = models.BooleanField(default=False)
     matched = models.BooleanField(default=False)
-    hidden = models.BooleanField(default=False)
 
     class Meta:
         app_label = 'misago'

+ 2 - 3
misago/models/usermodel.py

@@ -358,11 +358,10 @@ class User(models.Model):
         self.password_date = tz_util.now()
         self.password = make_password(raw_password.strip())
 
-    def set_last_visit(self, ip, agent, hidden=False):
+    def set_last_visit(self, ip, agent):
         self.last_date = tz_util.now()
         self.last_ip = ip
         self.last_agent = agent
-        self.last_hide = hidden
 
     def check_password(self, raw_password, mobile=False):
         """
@@ -553,7 +552,7 @@ class Crawler(Guest):
         self.username = username
 
     def is_anonymous(self):
-        return True
+        return False
 
     def is_authenticated(self):
         return False

+ 4 - 0
misago/monitor.py

@@ -1,3 +1,4 @@
+from datetime import timedelta
 from django.core.cache import cache
 from django.utils import timezone
 from misago.models import MonitorItem
@@ -42,6 +43,9 @@ class Monitor(object):
             return self._items[key][1]
         return None
 
+    def expired(self, key, seconds=5):
+        return self._items[key][1] < (timezone.now() - timedelta(seconds=seconds))
+
     def has_key(self, key):
         return key in self._items
 

+ 38 - 0
misago/onlines.py

@@ -0,0 +1,38 @@
+from datetime import timedelta
+from django.utils import timezone
+from misago.models import Session
+
+class MembersOnline(object):
+    def __init__(self, monitor, frequency=180):
+        self.monitor = monitor
+        self.frequency = frequency
+        self._members = int(monitor['online_members'])
+        self._all = int(monitor['online_all'])
+        self._om = self._members
+        self._oa = self._all
+        if monitor.expired('online_all', frequency):
+            queryset = Session.objects.filter(matched=True).filter(crawler__isnull=True).filter(last__gte=timezone.now() - timedelta(seconds=frequency))
+            self._all = queryset.count()
+            self._members = queryset.filter(user__isnull=False).count()
+
+    def new_session(self):
+        self._all += 1
+
+    def sign_in(self):
+        self._members += 1
+
+    def sign_out(self):
+        if self._members:
+            self._members -= 1
+
+    def all(self):
+        return self._all
+
+    def members(self):
+        return self._members
+
+    def sync(self):
+        if self._members != self._om:
+            self.monitor['online_members'] = self._members
+        if self._all != self._oa:
+            self.monitor['online_all'] = self._all

+ 13 - 20
misago/sessions.py

@@ -41,6 +41,12 @@ class MisagoSession(SessionBase):
         """We use sessions to track onlines so sorry, only sessions cleaner may delete sessions"""
         pass
 
+    def created(self):
+        try:
+            return self.started
+        except AttributeError:
+            return False
+
     def flush(self):
         """We use sessions to track onlines so sorry, only sessions cleaner may delete sessions"""
         pass
@@ -51,12 +57,6 @@ class MisagoSession(SessionBase):
     def session_expired(self):
         return False
 
-    def get_hidden(self):
-        return False
-
-    def set_hidden(self, hidden=False):
-        pass
-
     def get_ip(self, request):
         return request.META.get('HTTP_X_FORWARDED_FOR', '') or request.META.get('REMOTE_ADDR')
 
@@ -83,7 +83,7 @@ class CrawlerSession(MisagoSession):
     Crawler Session controller
     """
     def __init__(self, request):
-        self.hidden = False
+        self.started = False
         self.team = False
         self._ip = self.get_ip(request)
         self._session_key = md5('%s-%s' % (request.user.username, self._ip)).hexdigest()
@@ -123,8 +123,8 @@ class HumanSession(MisagoSession):
     Human Session controller
     """
     def __init__(self, request):
+        self.started = False
         self.expired = False
-        self.hidden = False
         self.team = False
         self.rank = None
         self.remember_me = None
@@ -153,16 +153,18 @@ class HumanSession(MisagoSession):
                 self.expired = True
                 raise IncorrectSessionException()
             
-            # Change session to matched and extract session user and hidden flag
+            # Change session to matched and extract session user
+            if not self._session_rk.matched:
+                self.started = True
             self._session_rk.matched = True
             self._user = self._session_rk.user
-            self.hidden = self._session_rk.hidden
             self.team = self._session_rk.team
         except (Session.DoesNotExist, IncorrectSessionException):
             # Attempt autolog
             try:
                 self.remember_me = auth_remember(request, self.get_ip(request))
                 self.create(request, user=self.remember_me.user)
+                self.started = True
             except AuthException as e:
                 # Autolog failed
                 self.create(request)
@@ -194,8 +196,7 @@ class HumanSession(MisagoSession):
                     # Update user data
                     user.set_last_visit(
                                         self.get_ip(request),
-                                        request.META.get('HTTP_USER_AGENT', ''),
-                                        hidden=self.hidden
+                                        request.META.get('HTTP_USER_AGENT', '')
                                         )
                     user.save(force_update=True)
                 break
@@ -205,7 +206,6 @@ class HumanSession(MisagoSession):
 
     def save(self):
         self._session_rk.user = self._user
-        self._session_rk.hidden = self.hidden
         self._session_rk.team = self.team
         self._session_rk.rank_id = self.rank
         super(HumanSession, self).save()
@@ -233,18 +233,11 @@ class HumanSession(MisagoSession):
                         if len(request.COOKIES[cookie_token]) > 0:
                             Token.objects.filter(id=request.COOKIES[cookie_token]).delete()
                         request.cookiejar.delete('TOKEN')
-                self.hidden = False
                 self._user = None
                 request.user = Guest()
         except AttributeError:
             pass
 
-    def get_hidden(self):
-        return self.hidden
-
-    def set_hidden(self, hidden=False):
-        self.hidden = hidden
-
 
 class SessionMock(object):
     def get_ip(self, request):

+ 1 - 1
templates/cranefly/index.html

@@ -133,7 +133,7 @@
           <span class="tooltip-top" title="{% trans %}Members{% endtrans %}"><i class="icon-user"></i> {{ monitor.users|int|intcomma }}</span>
         </li>
         <li>
-          <span class="tooltip-top" title="{% trans %}Online{% endtrans %}"><i class="icon-map-marker"></i> {{ users_online|int|intcomma }}</span>
+          <span class="tooltip-top" title="{% trans %}Online{% endtrans %}"><i class="icon-map-marker"></i> {{ users_online.members|int|intcomma }} <span class="muted">{{ users_online.all|int|intcomma }}</spam></span>
         </li>
       </ul>
     </div>