Redesign editor performance page (bug 541156)

This commit is contained in:
Gregory Koberger 2011-04-22 14:46:43 -04:00
Родитель 57861d8419
Коммит 0d59e31647
8 изменённых файлов: 248 добавлений и 4 удалений

Просмотреть файл

@ -189,6 +189,31 @@ class ViewPreliminaryQueue(VersionSpecificQueue):
return q
class PerformanceGraph(ViewQueue):
id = models.IntegerField()
yearmonth = models.CharField(max_length=7)
approval_created = models.DateTimeField()
user_id = models.IntegerField()
total = models.IntegerField()
def base_query(self):
return {
'select': SortedDict([
('yearmonth', "DATE_FORMAT(`approvals`.`created`, '%%Y-%%m')"),
('approval_created', '`approvals`.`created`'),
('user_id', '`users`.`id`'),
('total', 'COUNT(*)')]),
'from': [
'approvals',
'LEFT JOIN `users` ON (`users`.`id`=`approvals`.`user_id`)',
"""INNER JOIN `groups_users` ON
(`users`.`id` = `groups_users`.`user_id` AND
`groups_users`.`group_id` = 2)"""],
'where': [],
'group_by': 'yearmonth, user_id'
}
class EditorSubscription(amo.models.ModelBase):
user = models.ForeignKey(UserProfile)
addon = models.ForeignKey(Addon)

Просмотреть файл

@ -0,0 +1,46 @@
{% extends "editors/base.html" %}
{% block breadcrumbs %}
{{ editors_breadcrumbs(items=[(None, _('Performance'))]) }}
{% endblock %}
{% block content %}
<h2>{{ _('Add-on Editor Performance', 'editorcp_reviewlog_page_heading') }}</h2>
<div class="featured">
<div class="featured-inner">
<table class="data-grid">
<tr class="listing-header">
<th>{{ _('Range') }}</th>
<th>{{ _('My Reviews') }}</th>
<th>{{ _('Total Reviews') }}</th>
{# <th>{{ _('Team Average') }}</th> #}
<th>{{ _('Active Contributors') }}</th>
</tr>
<tr>
<td><strong>{{ _('This Month') }}</strong></td>
<td>{{ performance_month['usercount'] }}</td>
<td>{{ performance_month['teamcount'] }}</td>
<td>{{ performance_month['teamamt'] }}</td>
</tr>
<tr>
<td><strong>{{ _('Past Year') }}</strong></td>
<td>{{ performance_year['usercount'] }}</td>
<td>{{ performance_year['teamcount'] }}</td>
<td>{{ performance_year['teamamt'] }}</td>
</tr>
</table>
</div>
</div>
<div class="featured">
<div class="featured-inner listing">
<div class="listing-header"><span>{{ _('Monthly Performance') }}</span></div>
<div id="monthly" class="highcharts-container"
data-chart="{{ monthly_data }}"></div>
</div>
</div>
</div>
{% endblock %}

Просмотреть файл

@ -743,6 +743,25 @@ class TestModeratedQueue(QueueTest):
eq_(doc('.review-saved button').length, 1) # Only show one button.
class TestPerformance(QueueTest):
fixtures = ('base/users', 'editors/pending-queue', 'base/approvals')
"""Test the page at /editors/performance."""
def setUp(self):
super(TestPerformance, self).setUp()
self.url_performance = reverse('editors.performance')
def test_performance_chart(self):
r = self.client.get(self.url_performance)
doc = pq(r.content)
data = {u'2010-08': {u'teamcount': 1, u'teamavg': u'1.0',
u'usercount': 1, u'teamamt': 1,
u'label': u'Aug 2010'}}
eq_(json.loads(doc('#monthly').attr('data-chart')), data)
class SearchTest(EditorTest):
def setUp(self):

Просмотреть файл

@ -20,6 +20,7 @@ urlpatterns = patterns('',
url(r'^log/(\d+)$', views.eventlog_detail, name='editors.eventlog.detail'),
url(r'^reviewlog$', views.reviewlog, name='editors.reviewlog'),
url(r'^review/(?P<version_id>\d+)$', views.review, name='editors.review'),
url(r'^performance$', views.performance, name='editors.performance'),
url(r'^motd$', views.motd, name='editors.motd'),
url(r'^motd/save$', views.save_motd, name='editors.save_motd'),
url(r'^$', views.home, name='editors.home'),

Просмотреть файл

@ -1,9 +1,12 @@
from datetime import date
from datetime import date, datetime
import functools
import json
import time
from django import http
from django.conf import settings
from django.shortcuts import redirect, get_object_or_404
from django.utils.datastructures import SortedDict
import jingo
from tower import ugettext as _
@ -18,7 +21,7 @@ from devhub.models import ActivityLog
from editors import forms
from editors.models import (EditorSubscription, ViewPendingQueue,
ViewFullReviewQueue, ViewPreliminaryQueue,
EventLog, CannedResponse)
EventLog, CannedResponse, PerformanceGraph)
from editors.helpers import (ViewPendingQueueTable, ViewFullReviewQueueTable,
ViewPreliminaryQueueTable)
from files.models import Approval
@ -113,6 +116,82 @@ def _editor_progress():
return (progress, percentage)
@editor_required
def performance(request):
monthly_data = _performanceByMonth(request.amo_user.id)
performance_total = _performance_total(monthly_data)
data = context(monthly_data=json.dumps(monthly_data),
performance_month=performance_total['month'],
performance_year=performance_total['year'])
return jingo.render(request, 'editors/performance.html', data)
def _performance_total(data):
# TODO(gkoberger): Fix this so it's the past X, rather than this X to date.
# (ex: March 15-April 15, not April 1 - April 15)
total_yr = dict(usercount=0, teamamt=0, teamcount=0, teamavg=0)
total_month = dict(usercount=0, teamamt=0, teamcount=0, teamavg=0)
current_year = datetime.now().year
for k, val in data.items():
if k.startswith(str(current_year)):
total_yr['usercount'] = total_yr['usercount'] + val['usercount']
total_yr['teamamt'] = total_yr['teamamt'] + val['teamamt']
total_yr['teamcount'] = total_yr['teamcount'] + val['teamcount']
current_label_month = datetime.now().isoformat()[:7]
if current_label_month in data:
total_month = data[current_label_month]
return dict(month=total_month, year=total_yr)
def _performanceByMonth(user_id, months=12, end_month=None, end_year=None):
monthly_data = SortedDict()
now = datetime.now()
if not end_month:
end_month = now.month
if not end_year:
end_year = now.year
end_time = time.mktime((end_year, end_month + 1, 1, 0, 0, 0, 0, 0, -1))
start_time = time.mktime((end_year, end_month + 1 - months,
1, 0, 0, 0, 0, 0, -1))
sql = (PerformanceGraph.objects
.filter_raw('approvals.created >=',
date.fromtimestamp(start_time).isoformat())
.filter_raw('approvals.created <',
date.fromtimestamp(end_time).isoformat())
)
for row in sql.all():
label = row.approval_created.isoformat()[:7]
if not label in monthly_data:
xaxis = row.approval_created.strftime('%b %Y')
monthly_data[label] = dict(teamcount=0, usercount=0,
teamamt=0, label=xaxis)
monthly_data[label]['teamamt'] = monthly_data[label]['teamamt'] + 1
monthly_data_count = monthly_data[label]['teamcount']
monthly_data[label]['teamcount'] = monthly_data_count + row.total
if row.user_id == user_id:
user_count = monthly_data[label]['usercount']
monthly_data[label]['usercount'] = user_count + row.total
# Calculate averages
for i, vals in monthly_data.items():
average = round(vals['teamcount'] / float(vals['teamamt']), 1)
monthly_data[i]['teamavg'] = str(average) # floats aren't valid json
return monthly_data;
@editor_required
def motd(request):
form = None

Просмотреть файл

@ -193,7 +193,7 @@ ul.tabnav a:hover {
border-bottom-width: 0px;
}
#editors_main .listing .listing-header {
.listing .listing-header {
text-align: center;
font-weight: bold;
}

Просмотреть файл

@ -3,7 +3,6 @@ $(function() {
initDailyMessage();
}
var show_comments = function(e) {
e.preventDefault()
var me = e.target;
@ -31,6 +30,10 @@ $(function() {
if($('#review-actions').length > 0) {
initReviewActions();
}
if($('#monthly').exists()) {
initPerformanceStats();
}
});
function initReviewActions() {
@ -177,3 +180,73 @@ function initQueueSearch(doc) {
});
});
}
function initPerformanceStats() {
var container = $('#monthly'),
groups = {'usercount': gettext('Your Reviews'),
'teamavg': gettext('Average Reviews')}
createChart(container, groups, JSON.parse(container.attr('data-chart')));
function createChart(container, groups, data) {
var labels = [],
data_points = {},
chart_series = [];
$.each(groups, function(key, name){
data_points[key] = {'name': name, 'data':[]};
});
$.each(data, function(k, vals) {
labels.push(vals['label']);
$.each(vals, function(group, amount){
if(groups[group]){
data_points[group]['data'].push(parseFloat(amount));
}
});
});
$.each(data_points, function(k, vals){
chart_series.push(vals);
});
var $c = container,
chart = new Highcharts.Chart({
chart: {
renderTo: container[0],
defaultSeriesType: 'line',
marginRight: 130,
marginBottom: 25
},
title: {
text: ' ',
x: 0 //center
},
xAxis: {
categories: labels
},
yAxis: {
title: { text: gettext('Number of Reviews') },
plotLines: [{value: 0, width: 1, color: '#808080'}]
},
tooltip: {
formatter: function() {
return '<b>'+ this.series.name +'</b><br/>' + this.x +': '+ this.y;
}
},
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'top',
x: -10,
y: 100,
borderWidth: 0
},
series: chart_series
});
}
}

Просмотреть файл

@ -487,6 +487,7 @@ MINIFY_BUNDLES = {
),
'zamboni/editors': (
'js/zamboni/editors.js',
'js/lib/highcharts.src.js'
),
'zamboni/files': (
'js/diff/diff_match_patch_uncompressed.js',