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 logging
 import datetime
 import datetime
 
 
-from flask import Flask, render_template
+from flask import Flask, render_template, request
 from flask.ext.login import current_user
 from flask.ext.login import current_user
 from flask_debugtoolbar import DebugToolbarExtension
 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,
 from flaskbb.helpers import (format_date, time_since, is_online,
                              perm_post_reply, perm_post_topic, perm_edit_post,
                              perm_post_reply, perm_post_topic, perm_edit_post,
                              perm_delete_topic, perm_delete_post, crop_title,
                              perm_delete_topic, perm_delete_post, crop_title,
-                             render_markup)
+                             render_markup, mark_online)
 
 
 
 
 DEFAULT_BLUEPRINTS = (
 DEFAULT_BLUEPRINTS = (
@@ -165,6 +165,13 @@ def configure_before_handlers(app):
     def get_user_permissions():
     def get_user_permissions():
         current_user.permissions = current_user.get_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):
 def configure_errorhandlers(app):
     """
     """

+ 1 - 1
flaskbb/configs/default.py

@@ -86,6 +86,6 @@ class DefaultConfig(object):
     USERS_PER_PAGE = 10
     USERS_PER_PAGE = 10
 
 
     # How long the use can be inactive before he is marked as offline
     # 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
     # The length of the topic title in characters on the index
     TITLE_LENGTH = 15
     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
     # How long (in minutes) a user needs to be inactive
     # to be shown as offline.
     # to be shown as offline.
-    LAST_SEEN = 15
+    ONLINE_LAST_MINUTES = 15
     # The length of the topic title in characters on the index
     # The length of the topic title in characters on the index
     TITLE_LENGTH = 15
     TITLE_LENGTH = 15

+ 4 - 0
flaskbb/extensions.py

@@ -8,6 +8,7 @@
     :copyright: (c) 2013 by the FlaskBB Team.
     :copyright: (c) 2013 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
     :license: BSD, see LICENSE for more details.
 """
 """
+from redis import Redis
 from flask.ext.sqlalchemy import SQLAlchemy
 from flask.ext.sqlalchemy import SQLAlchemy
 from flask.ext.login import LoginManager
 from flask.ext.login import LoginManager
 from flask.ext.mail import Mail
 from flask.ext.mail import Mail
@@ -26,5 +27,8 @@ mail = Mail()
 # Caching
 # Caching
 cache = Cache()
 cache = Cache()
 
 
+# Redis
+redis = Redis()
+
 # Debugtoolbar
 # Debugtoolbar
 #toolbar = DebugToolbarExtension()
 #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,
 from flaskbb.helpers import (time_diff, perm_post_reply, perm_post_topic,
                              perm_edit_post, perm_delete_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.models import Category, Forum, Topic, Post
 from flaskbb.forum.forms import QuickreplyForm, ReplyForm, NewTopicForm
 from flaskbb.forum.forms import QuickreplyForm, ReplyForm, NewTopicForm
 from flaskbb.user.models import User
 from flaskbb.user.models import User
@@ -37,14 +37,14 @@ def index():
     post_count = Post.query.count()
     post_count = Post.query.count()
     newest_user = User.query.order_by(User.id.desc()).first()
     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,
     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>")
 @forum.route("/category/<int:category_id>")
@@ -208,7 +208,7 @@ def delete_post(post_id):
 
 
 @forum.route("/who_is_online")
 @forum.route("/who_is_online")
 def 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)
     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.
     :license: BSD, see LICENSE for more details.
 """
 """
 import random
 import random
-import datetime
+import time
+from datetime import datetime, timedelta
 
 
 from flask import current_app
 from flask import current_app
 from sqlalchemy import types
 from sqlalchemy import types
@@ -17,8 +18,56 @@ from sqlalchemy.ext.mutable import Mutable
 from wtforms.widgets.core import Select, HTMLString, html_params
 from wtforms.widgets.core import Select, HTMLString, html_params
 from postmarkup import render_bbcode
 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):
 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):
     if can_moderate(user, forum):
         return True
         return True
     if post_user_id and user.is_authenticated():
     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):
 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:
     if user.permissions['mod'] and user.id in forum.moderators:
         return True
         return True
     return user.permissions['super_mod'] or user.permissions['admin']
     return user.permissions['super_mod'] or user.permissions['admin']
@@ -80,10 +134,6 @@ def crop_title(title):
     return 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):
 def render_markup(text):
     return render_bbcode(text)
     return render_bbcode(text)
 
 
@@ -93,8 +143,8 @@ def is_online(user):
 
 
 
 
 def time_diff():
 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
     return diff
 
 
 
 
@@ -119,7 +169,7 @@ def time_delta_format(dt, default=None):
     if default is None:
     if default is None:
         default = 'just now'
         default = 'just now'
 
 
-    now = datetime.datetime.utcnow()
+    now = datetime.utcnow()
     diff = now - dt
     diff = now - dt
 
 
     periods = (
     periods = (
@@ -198,7 +248,7 @@ class SelectDateWidget(object):
         '%Y': 'select_date_year'
         '%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__()
         super(SelectDateWidget, self).__init__()
         self.FORMAT_CHOICES['%Y'] = [(x, str(x)) for x in years]
         self.FORMAT_CHOICES['%Y'] = [(x, str(x)) for x in years]
 
 

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

@@ -21,14 +21,14 @@
     <tbody>
     <tbody>
         <tr>
         <tr>
             <td>
             <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>
             <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>
             </td>
         </tr>
         </tr>
 
 

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

@@ -5,7 +5,7 @@
 
 
 <legend>Online Users</legend>
 <legend>Online Users</legend>
 {% for user in online_users %}
 {% 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 %}
 {% endfor %}
 
 
 {% endblock %}
 {% endblock %}