Browse Source

#424: revamped active posters lists

Rafał Pitoń 10 years ago
parent
commit
b4d6ffedc3

+ 6 - 0
docs/developers/settings.rst

@@ -254,6 +254,12 @@ Some lists act as rankings, displaying users in order of certain scoring criteri
 This setting controls maximum age in days of items that should count to ranking.
 
 
+MISAGO_RANKING_SIZE
+-------------------
+
+Maximum number of items on ranking page.
+
+
 MISAGO_READ_RECORD_LENGTH
 -------------------------
 Controls amount of data used in resolving read/unread states of threads and forums. Any activity older than number of days specified in this setting is assumed to be read and not tracked anymore. Active forums can try lowering this value while less active ones may wish to increase this number.

+ 2 - 0
misago/conf/defaults.py

@@ -278,6 +278,8 @@ MISAGO_AVATAR_SERVER_PATH = '/user-avatar'
 # You don't have to be overzelous with this as user rankings are cached for 24h
 MISAGO_RANKING_LENGTH = 30
 
+# Controls max number of items displayed on ranked lists
+MISAGO_RANKING_SIZE = 30
 
 # Controls amount of data used in resolving read/unread states of threads and
 # forums. Any activity older than number of days below is assumed to be read

+ 1 - 0
misago/static/misago/admin/css/style.css

@@ -6413,6 +6413,7 @@ body {
 }
 .yes-no-switch {
   margin-top: 5px;
+  margin-bottom: 0px;
   cursor: pointer;
 }
 .yes-no-switch .fa {

+ 32 - 0
misago/static/misago/css/misago/userslists.less

@@ -77,3 +77,35 @@
     }
   }
 }
+
+
+// Users ranking
+//
+//==
+.users-ranking {
+  &>tbody {
+    &>tr {
+      &>td {
+        vertical-align: middle;
+
+        .avatar {
+          border-radius: @border-radius-base;
+        }
+
+        .item-title {
+          font-size: @font-size-large;
+        }
+      }
+
+      &.highlight {
+        color: @brand-accent;
+
+        .item-title {
+          &:link, &:visited {
+            color: @brand-accent;
+          }
+        }
+      }
+    }
+  }
+}

+ 56 - 29
misago/templates/misago/userslists/active_posters.html

@@ -1,5 +1,5 @@
 {% extends "misago/userslists/base.html" %}
-{% load humanize i18n %}
+{% load humanize i18n misago_avatars %}
 
 
 {% block meta-description %}{% blocktrans trimmed with days=tracked_period %}
@@ -7,33 +7,60 @@ List of users that have posted new messages during last {{ days }} days.
 {% endblocktrans %}{% endblock meta-description %}
 
 
-{% block users-list %}
-<p class="lead">
-  {% blocktrans trimmed with posters=users.paginator.count|intcomma days=tracked_period count counter=users.paginator.count %}
-  {{ posters }} user has posted new messages posting during last {{ days }} days.
-  {% plural %}
-  {{ posters }} users posted new messages during last {{ days }} days.
-  {% endblocktrans %}
-</p>
+{% block users %}
+{% if users_count %}
+  <p class="lead">
+    {% blocktrans trimmed with posters=users_count|intcomma days=tracked_period count counter=users_count %}
+    {{ posters }} user has posted new messages posting during last {{ days }} days.
+    {% plural %}
+    {{ posters }} users posted new messages during last {{ days }} days.
+    {% endblocktrans %}
+  </p>
 
-{{ block.super }}
-{% endblock users-list %}
+  <table class="table users-ranking">
+    <thead>
+      <tr>
+        <th colspan="2">{% trans "User" %}</th>
+        <th class="text-center">{% trans "Rank" %}</th>
+        <th class="text-center">{% trans "Posts" %}</th>
+        <th class="text-center">{% trans "Total" %}</th>
+      </tr>
+    </thead>
+    <tbody>
+      {% for ranked in users %}
+      {% url USER_PROFILE_URL user_slug=ranked.slug user_id=ranked.id as user_url %}
+      <tr {% if ranked.pk == user.pk %}class="highlight"{% endif %}>
+        <td style="width: 1%;">
+          <a href="{{ user_url }}">
+            <img src="{{ ranked|avatar:30 }}" alt="{% trans "Avatar" %}"class="avatar">
+          </a>
+        </td>
+        <td>
+          <a href="{{ user_url }}" class="item-title">{{ ranked.username }}</a>
+        </td>
+        <td class="lead text-center">
+          #{{ forloop.counter }}
+        </td>
+        <td class="lead text-center">
+          {{ ranked.num_posts }}
+        </td>
+        <td class="lead text-center">
+          {% if ranked.num_posts > ranked.posts %}
+          {{ ranked.num_posts }}
+          {% else %}
+          {{ ranked.posts }}
+          {% endif %}
+        </td>
+      </tr>
+      {% endfor %}
+    </tbody>
+  </table>
+{% else %}
+  <p class="lead">
+    {% blocktrans trimmed with days=tracked_period %}
+    No users have posted any new messages during last {{ days }} days.
+    {% endblocktrans %}
+  </p>
+{% endif %}
+{% endblock users %}
 
-
-{% block user-card-footer %}
-{{ block.super }}
-<small>
-  {% blocktrans trimmed with posts=user.followers|intcomma count counter=user.followers %}
-  {{ posts }} post
-  {% plural %}
-  {{ posts }} posts
-  {% endblocktrans %}
-</small>
-{% endblock user-card-footer %}
-
-
-{% block empty-list %}
-{% blocktrans trimmed with days=tracked_period %}
-No users have posted any new messages during last {{ days }} days.
-{% endblocktrans %}
-{% endblock empty-list %}

+ 31 - 29
misago/templates/misago/userslists/base.html

@@ -31,40 +31,42 @@
 </div>
 
 <div class="container">
-  {% if users.object_list %}
-    {% block users-list %}
-    <div class="users-cards">
-      {% for row in users.object_list|batch:6 %}
-      <div class="row">
-        {% for card in row %}
-        <div class="col-md-2">
+  {% block users %}
+    {% if users.object_list %}
+      {% block users-list %}
+      <div class="users-cards">
+        {% for row in users.object_list|batch:6 %}
+        <div class="row">
+          {% for card in row %}
+          <div class="col-md-2">
 
-          {% block user-card %}
-          <a href="{% url USER_PROFILE_URL user_slug=card.slug user_id=card.id %}" class="user-card {% if card.rank.css_class %}card-{{ card.rank.css_class }}{% endif %}">
-            <img src="{{ card|avatar:400 }}" alt="{% trans "Avatar" %}">
-            <div class="card-footer">
-              {% block user-card-footer %}
-              <h4 class="user-name">{{ card.username }}</h4>
-              {% endblock user-card-footer %}
-            </div>
-          </a>
-          {% endblock user-card %}
+            {% block user-card %}
+            <a href="{% url USER_PROFILE_URL user_slug=card.slug user_id=card.id %}" class="user-card {% if card.rank.css_class %}card-{{ card.rank.css_class }}{% endif %}">
+              <img src="{{ card|avatar:400 }}" alt="{% trans "Avatar" %}">
+              <div class="card-footer">
+                {% block user-card-footer %}
+                <h4 class="user-name">{{ card.username }}</h4>
+                {% endblock user-card-footer %}
+              </div>
+            </a>
+            {% endblock user-card %}
 
+          </div>
+          {% endfor %}
         </div>
         {% endfor %}
       </div>
-      {% endfor %}
-    </div>
-    {% endblock users-list %}
+      {% endblock users-list %}
 
-    {% block list-pagination %}
-      {% pagination users "misago/userslists/pagination.html" active_page.link %}
-    {% endblock list-pagination %}
-  {% else %}
-    <p class="lead">
-      {% block empty-list %}
-      {% endblock empty-list %}
-    </p>
-  {% endif %}
+      {% block list-pagination %}
+        {% pagination users "misago/userslists/pagination.html" active_page.link %}
+      {% endblock list-pagination %}
+    {% else %}
+      <p class="lead">
+        {% block empty-list %}
+        {% endblock empty-list %}
+      </p>
+    {% endif %}
+  {% endblock users %}
 </div>
 {% endblock content%}

+ 1 - 1
misago/threads/migrations/0001_initial.py

@@ -40,7 +40,7 @@ class Migration(migrations.Migration):
                 ('checksum', models.CharField(max_length=64)),
                 ('has_attachments', models.BooleanField(default=False)),
                 ('pickled_attachments', models.TextField(null=True, blank=True)),
-                ('posted_on', models.DateTimeField()),
+                ('posted_on', models.DateTimeField(db_index=True)),
                 ('updated_on', models.DateTimeField()),
                 ('edits', models.PositiveIntegerField(default=0)),
                 ('last_editor_name', models.CharField(max_length=255, null=True, blank=True)),

+ 1 - 1
misago/threads/models/post.py

@@ -20,7 +20,7 @@ class Post(models.Model):
                                       related_name="mention_set")
     has_attachments = models.BooleanField(default=False)
     pickled_attachments = models.TextField(null=True, blank=True)
-    posted_on = models.DateTimeField()
+    posted_on = models.DateTimeField(db_index=True)
     updated_on = models.DateTimeField()
     edits = models.PositiveIntegerField(default=0)
     last_editor = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='+',

+ 19 - 7
misago/users/views/lists.py

@@ -1,8 +1,9 @@
-import warnings
+from datetime import timedelta
 
 from django.conf import settings
 from django.contrib.auth import get_user_model
 from django.core.urlresolvers import reverse
+from django.db.models import Count
 from django.shortcuts import redirect, render as django_render
 from django.utils import timezone
 from django.views.decorators.cache import cache_page
@@ -62,18 +63,29 @@ def list_view(request, template, queryset, page, context=None):
     return render(request, template, context)
 
 
+def ranking_view(request, template, queryset, context=None):
+    context = context or {}
+    context.update({
+        'users': queryset[:settings.MISAGO_RANKING_SIZE],
+        'users_count': queryset.count()
+    })
+    return render(request, template, context)
+
+
+#@cache_page(18 * 3600)
 @allow_see_list()
-@cache_page(24 * 3600)
 def active_posters(request, page=0):
-    warnings.warn("Not implemented yet! See #404 for details.",
-                  FutureWarning)
-
     tracked_period = settings.MISAGO_RANKING_LENGTH
+    tracked_since = timezone.now() - timedelta(days=tracked_period)
+
     User = get_user_model()
-    queryset = User.objects.all().select_related('user__rank')
+    queryset = User.objects.filter(post__posted_on__gte=tracked_since)
+    queryset = queryset.annotate(num_posts=Count('post'))
+    queryset = queryset.select_related('user__rank')
+    queryset = queryset.order_by('-num_posts')
 
     template = "misago/userslists/active_posters.html"
-    return list_view(request, template, queryset, page, {
+    return ranking_view(request, template, queryset, {
         'tracked_period': tracked_period
     })