Browse Source

Added online guest count and online user count

sh4nks 11 years ago
parent
commit
81b67edfb1

+ 9 - 2
flaskbb/app.py

@@ -12,7 +12,7 @@ import os
 import logging
 import datetime
 
-from flask import Flask, render_template
+from flask import Flask, render_template, request
 from flask.ext.login import current_user
 from flask_debugtoolbar import DebugToolbarExtension
 
@@ -33,7 +33,7 @@ from flaskbb.extensions import db, login_manager, mail, cache
 from flaskbb.helpers import (format_date, time_since, is_online,
                              perm_post_reply, perm_post_topic, perm_edit_post,
                              perm_delete_topic, perm_delete_post, crop_title,
-                             render_markup)
+                             render_markup, mark_online)
 
 
 DEFAULT_BLUEPRINTS = (
@@ -165,6 +165,13 @@ def configure_before_handlers(app):
     def get_user_permissions():
         current_user.permissions = current_user.get_permissions()
 
+    @app.before_request
+    def mark_current_user_online():
+        if current_user.is_authenticated():
+            mark_online(current_user.username)
+        else:
+            mark_online(request.remote_addr, guest=True)
+
 
 def configure_errorhandlers(app):
     """

+ 1 - 1
flaskbb/configs/default.py

@@ -86,6 +86,6 @@ class DefaultConfig(object):
     USERS_PER_PAGE = 10
 
     # How long the use can be inactive before he is marked as offline
-    LAST_SEEN = 15
+    ONLINE_LAST_MINUTES = 15
     # The length of the topic title in characters on the index
     TITLE_LENGTH = 15

+ 1 - 1
flaskbb/configs/production.py.example

@@ -82,6 +82,6 @@ class ProductionConfig(DefaultConfig):
 
     # How long (in minutes) a user needs to be inactive
     # to be shown as offline.
-    LAST_SEEN = 15
+    ONLINE_LAST_MINUTES = 15
     # The length of the topic title in characters on the index
     TITLE_LENGTH = 15

+ 4 - 0
flaskbb/extensions.py

@@ -8,6 +8,7 @@
     :copyright: (c) 2013 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
 """
+from redis import Redis
 from flask.ext.sqlalchemy import SQLAlchemy
 from flask.ext.login import LoginManager
 from flask.ext.mail import Mail
@@ -26,5 +27,8 @@ mail = Mail()
 # Caching
 cache = Cache()
 
+# Redis
+redis = Redis()
+
 # Debugtoolbar
 #toolbar = DebugToolbarExtension()

+ 9 - 9
flaskbb/forum/views.py

@@ -18,7 +18,7 @@ from flask.ext.login import login_required, current_user
 
 from flaskbb.helpers import (time_diff, perm_post_reply, perm_post_topic,
                              perm_edit_post, perm_delete_topic,
-                             perm_delete_post)
+                             perm_delete_post, get_online_users)
 from flaskbb.forum.models import Category, Forum, Topic, Post
 from flaskbb.forum.forms import QuickreplyForm, ReplyForm, NewTopicForm
 from flaskbb.user.models import User
@@ -37,14 +37,14 @@ def index():
     post_count = Post.query.count()
     newest_user = User.query.order_by(User.id.desc()).first()
 
-    online_users = User.query.filter(User.lastseen >= time_diff())
-
     return render_template("forum/index.html", categories=categories,
-                           stats={'user_count': user_count,
-                                  'topic_count': topic_count,
-                                  'post_count': post_count,
-                                  'newest_user': newest_user.username,
-                                  'online_users': online_users})
+                           user_count=user_count,
+                           topic_count=topic_count,
+                           post_count=post_count,
+                           newest_user=newest_user.username,
+                           online_users=len(get_online_users()),
+                           online_guests=len(get_online_users(guest=True)))
+
 
 
 @forum.route("/category/<int:category_id>")
@@ -208,7 +208,7 @@ def delete_post(post_id):
 
 @forum.route("/who_is_online")
 def who_is_online():
-    online_users = User.query.filter(User.lastseen >= time_diff()).all()
+    online_users=get_online_users()
     return render_template("forum/online_users.html", online_users=online_users)
 
 

+ 59 - 9
flaskbb/helpers.py

@@ -9,7 +9,8 @@
     :license: BSD, see LICENSE for more details.
 """
 import random
-import datetime
+import time
+from datetime import datetime, timedelta
 
 from flask import current_app
 from sqlalchemy import types
@@ -17,8 +18,56 @@ from sqlalchemy.ext.mutable import Mutable
 from wtforms.widgets.core import Select, HTMLString, html_params
 from postmarkup import render_bbcode
 
+from flaskbb.extensions import redis
+
+
+def mark_online(user_id, guest=False):
+    """
+    Source: http://flask.pocoo.org/snippets/71/
+    """
+    now = int(time.time())
+    expires = now + (current_app.config['ONLINE_LAST_MINUTES'] * 60) + 10
+    if guest:
+        all_users_key = 'online-guests/%d' % (now // 60)
+        user_key = 'guest-activity/%s' % user_id
+    else:
+        all_users_key = 'online-users/%d' % (now // 60)
+        user_key = 'user-activity/%s' % user_id
+    p = redis.pipeline()
+    p.sadd(all_users_key, user_id)
+    p.set(user_key, now)
+    p.expireat(all_users_key, expires)
+    p.expireat(user_key, expires)
+    p.execute()
+
+
+def get_last_user_activity(user_id, guest=False):
+    if guest:
+        last_active = redis.get('guest-activity/%s' % user_id)
+    else:
+        last_active = redis.get('user-activity/%s' % user_id)
+
+    if last_active is None:
+        return None
+    return datetime.utcfromtimestamp(int(last_active))
+
+
+def get_online_users(guest=False):
+    current = int(time.time()) // 60
+    minutes = xrange(current_app.config['ONLINE_LAST_MINUTES'])
+    if guest:
+        return redis.sunion(['online-guests/%d' % (current - x)
+                             for x in minutes])
+    return redis.sunion(['online-users/%d' % (current - x)
+                         for x in minutes])
+
 
 def check_perm(user, perm, forum, post_user_id=None):
+    """
+    Checks if the `user` has a specified `perm` in the `forum`
+    If post_user_id is provided, it will also check if the user
+    has created the post
+    """
     if can_moderate(user, forum):
         return True
     if post_user_id and user.is_authenticated():
@@ -27,6 +76,11 @@ def check_perm(user, perm, forum, post_user_id=None):
 
 
 def can_moderate(user, forum):
+    """
+    Checks if a user can moderate a forum
+    He needs to be super moderator or a moderator of the
+    specified `forum`
+    """
     if user.permissions['mod'] and user.id in forum.moderators:
         return True
     return user.permissions['super_mod'] or user.permissions['admin']
@@ -80,10 +134,6 @@ def crop_title(title):
     return title
 
 
-def generate_random_pass(length=8):
-    return "".join(chr(random.randint(33, 126)) for i in range(length))
-
-
 def render_markup(text):
     return render_bbcode(text)
 
@@ -93,8 +143,8 @@ def is_online(user):
 
 
 def time_diff():
-    now = datetime.datetime.utcnow()
-    diff = now - datetime.timedelta(minutes=current_app.config['LAST_SEEN'])
+    now = datetime.utcnow()
+    diff = now - timedelta(minutes=current_app.config['ONLINE_LAST_MINUTES'])
     return diff
 
 
@@ -119,7 +169,7 @@ def time_delta_format(dt, default=None):
     if default is None:
         default = 'just now'
 
-    now = datetime.datetime.utcnow()
+    now = datetime.utcnow()
     diff = now - dt
 
     periods = (
@@ -198,7 +248,7 @@ class SelectDateWidget(object):
         '%Y': 'select_date_year'
     }
 
-    def __init__(self, years=range(1930, datetime.datetime.utcnow().year+1)):
+    def __init__(self, years=range(1930, datetime.utcnow().year+1)):
         super(SelectDateWidget, self).__init__()
         self.FORMAT_CHOICES['%Y'] = [(x, str(x)) for x in years]
 

+ 6 - 6
flaskbb/templates/forum/index.html

@@ -21,14 +21,14 @@
     <tbody>
         <tr>
             <td>
-                Total number of registered users: <strong>{{ stats['user_count'] }}</strong> <br />
-                Total number of topics: <strong>{{ stats['topic_count'] }}</strong> <br />
-                Total number of posts: <strong>{{ stats['post_count'] }}</strong> <br />
+                Total number of registered users: <strong>{{ user_count }}</strong> <br />
+                Total number of topics: <strong>{{ topic_count }}</strong> <br />
+                Total number of posts: <strong>{{ post_count }}</strong> <br />
             </td>
             <td>
-                Newest registered user: <a href="{{ url_for('user.profile', username=stats['newest_user']) }}">{{ stats['newest_user'] }}</a> <br />
-                Registered users online: <strong>{{ stats['online_users'].count() }}</strong> <br />
-                <strong>#TODO</strong> Guests online: <strong>40</strong> <br />
+                Newest registered user: <a href="{{ url_for('user.profile', username=newest_user) }}">{{ newest_user }}</a> <br />
+                Registered users online: <strong>{{ online_users }}</strong> <br />
+                Guests online: <strong>{{ online_guests }}</strong> <br />
             </td>
         </tr>
 

+ 1 - 1
flaskbb/templates/forum/online_users.html

@@ -5,7 +5,7 @@
 
 <legend>Online Users</legend>
 {% for user in online_users %}
-    <a href="{{ url_for('user.profile', username=user.username) }}">{{ user.username }}<a>{% if not loop.last %}, {% endif %}
+    <a href="{{ url_for('user.profile', username=user) }}">{{ user }}<a>{% if not loop.last %}, {% endif %}
 {% endfor %}
 
 {% endblock %}