Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added some silly statistics to the user pages.

Also cleaned up the styles slightly.
  • Loading branch information...
commit 20a21e15e267a3fc3bab4ad5b75da1de3f1482be 1 parent 3d1a65a
@jacobian jacobian authored
View
18 django_website/accounts/views.py
@@ -1,3 +1,5 @@
+from __future__ import absolute_import
+
import hashlib
import json
from django.shortcuts import render, get_object_or_404
@@ -5,6 +7,7 @@
from django.conf import settings
from django.core import cache
from django.http import HttpResponse
+from ..trac import stats as trac_stats
from ..cla.models import find_agreements
def user_profile(request, username):
@@ -14,6 +17,7 @@ def user_profile(request, username):
'email_hash': hashlib.md5(u.email).hexdigest(),
'user_can_commit': u.has_perm('auth.commit'),
'clas': find_agreements(u),
+ 'stats': get_user_stats(u),
}
return render(request, "accounts/user_profile.html", ctx)
@@ -57,6 +61,20 @@ def get_user_info(username):
c.set(key, info, 60*60)
return info
+def get_user_stats(user):
+ c = cache.get_cache('default')
+ key = 'user_vital_status:%s' % hashlib.md5(user.username).hexdigest()
+ info = c.get(key)
+ if info is None:
+ info = trac_stats.get_user_stats(user.username)
+ # Hide any stat with a value = 0 so that we don't accidentally insult
+ # non-contributors.
+ for k, v in info.items():
+ if v == 0:
+ info.pop(k)
+ c.set(key, info, 60*60)
+ return info
+
class JSONResponse(HttpResponse):
def __init__(self, obj):
super(JSONResponse, self).__init__(
View
1  django_website/settings/www.py
@@ -76,6 +76,7 @@
'registration',
'south',
'djangosecure',
+ 'gunicorn',
]
CACHE_MIDDLEWARE_SECONDS = 60 * 5 # 5 minutes
View
58 django_website/templates/accounts/user_profile.html
@@ -4,45 +4,51 @@
{% block extrahead %}
<style type="text/css">
#avatar { float: left; margin-top: 16px; }
- #user-info { padding-left: 150px; }
- #user-info ul { padding-left: 48px; }
+ #user-info { padding-left: 180px; }
+ #user-info ul li { margin-left: 1em; }
+ h1 span.badge {
+ font-size: 12px;
+ background-color: #FFC757;
+ border-radius: 4px;
+ padding: 2px 6px;
+ position: relative;
+ bottom: 0.3em;
+ }
</style>
{% endblock extrahead %}
{% block content-related %}{% endblock %}
{% block content %}
+ {% load humanize %}
<img id='avatar' width='150' height='150'
src="http://robohash.org/{{ email_hash }}?gravatar=hashed&amp;set=set3">
<div id="user-info">
- <h1>{% firstof user.get_full_name user.username %}</h1>
- <p>
- Member since {{ user.date_joined.date }}.
- </p>
- {% if user_can_commit %}<p>Committer!</p>{% endif %}
- {% if clas %}
- {% for cla in clas %}
- {% if cla.ccla %}
- {# it's a corporate cla #}
- <p>
- Contributions covered by CLA on file for {{ cla.ccla.company_name }}
- (signed {{ cla.ccla.date_signed }}).
- </p>
- {% else %}
- <p>
- CLA on file (signed {{ cla.date_signed }}).
- </p>
- {% endif %}
- {% endfor %}
+ <h1>
+ {% firstof user.get_full_name user.username %}
+ {% if user_can_commit %}<span class="badge" title="Core committer.">core</span>{% endif %}
+ {% if clas %}<span class="badge" title="Contributor License Agreement on file.">cla</span>{% endif %}
+ </h1>
+
+ <h3 class="deck">
+ Contributor since {{ user.date_joined.date }}.
+ </h2>
+
+ {% if stats %}
+ <h2>Lies, damned lies, and statistics:</h2>
+ <ul>
+ {% for stat, value in stats.items %}
+ <li>{{ stat }}: {{ value|intcomma }}.</li>
+ {% endfor %}
+ </ul>
{% endif %}
+
{% with user.owned_feeds.all as feeds %}
{% if feeds %}
<h2>Community feeds:</h2>
<ul>
- <li>
- {% for f in feeds %}
- <li><a href="{{ f.public_url }}">{{ f.title }}</a></li>
- {% endfor %}
- </li>
+ {% for f in feeds %}
+ <li><a href="{{ f.public_url }}">{{ f.title }}</a></li>
+ {% endfor %}
</ul>
{% endif %}
{% endwith %}
View
60 django_website/trac/stats.py
@@ -0,0 +1,60 @@
+"""
+Various queries for grabbing interesting user stats from Trac.
+"""
+
+from __future__ import absolute_import
+import operator
+import django.db
+from .models import Revision, Ticket, TicketChange
+
+_statfuncs = []
+
+def stat(title):
+ """
+ Register a function as a "stat"
+
+ The function should take a username and return a number.
+ """
+
+ def _inner(f):
+ _statfuncs.append(f)
+ f.title = title
+ return f
+ return _inner
+
+def get_user_stats(username):
+ stats = {}
+ for func in sorted(_statfuncs, key=operator.attrgetter('title')):
+ stats[func.title] = func(username)
+ return stats
+
+@stat('Commits')
+def commit_count(username):
+ return Revision.objects.filter(author=username).count()
+
+@stat('Tickets closed')
+def tickets_closed(username):
+ # Raw query so that we can do COUNT(DISTINCT ticket).
+ q = """SELECT COUNT(DISTINCT ticket) FROM ticket_change
+ WHERE author = %s AND field = 'status' AND newvalue = 'closed';"""
+ return run_single_value_query(q, username)
+
+@stat('Tickets opened')
+def tickets_opened(username):
+ return Ticket.objects.filter(reporter=username).count()
+
+@stat('New tickets reviewed')
+def new_tickets_reviewed(username):
+ # We don't want to de-dup as for tickets_closed: multiple reviews of the
+ # same ticket should "count" as a review.
+ qs = TicketChange.objects.filter(author=username, field='stage', oldvalue='Unreviewed')
+ qs = qs.exclude(newvalue='Unreviewed')
+ return qs.count()
+
+def run_single_value_query(query, *params):
+ """
+ Helper: run a query returning a single value (e.g. a COUNT) and return the value.
+ """
+ c = django.db.connections['trac'].cursor()
+ c.execute(query, params)
+ return c.fetchone()[0]
Please sign in to comment.
Something went wrong with that request. Please try again.