Skip to content

Commit

Permalink
[Bug 663241] KB Helpfulvotes Metrics Frontend Graphing
Browse files Browse the repository at this point in the history
  • Loading branch information
tgavankar committed Jul 26, 2011
1 parent 1b070f3 commit 05e3d84
Show file tree
Hide file tree
Showing 9 changed files with 285 additions and 1 deletion.
2 changes: 2 additions & 0 deletions apps/wiki/templates/wiki/history.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{% extends "wiki/base.html" %}
{% from "wiki/includes/sidebar_modules.html" import document_tabs %}
{% from "layout/errorlist.html" import errorlist %}
{% set scripts = ('wiki', 'libs/jqueryui', 'wiki.history') %}
{% set title = _('Revision History | {document}')|fe(document=document.title) %}
{% set crumbs = [(url('wiki.category', document.category), document.get_category_display()),
(document.get_absolute_url(), document.title),
Expand All @@ -23,6 +24,7 @@ <h1 class="title">{{ _('History of {title}')|fe(title=document.title) }}</h1>
</li>
</ul>
</div>
<div id="helpful-chart" data-url="{{ url('wiki.get_helpful_votes_async', document.slug) }}"></div>
{% include 'wiki/includes/revision_list.html' %}

{% include 'wiki/includes/ready_for_l10n_modal.html' %}
Expand Down
5 changes: 4 additions & 1 deletion apps/wiki/templates/wiki/includes/revision_list.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
{% if revisions.count() > 1 %}
<div class="compare top">
<input type="submit" class="link-btn" value="{% if document.parent %}{{ _('Compare Selected Translations') }}{% else %}{{ _('Compare Selected Revisions') }}{% endif %}" />
{% if not waffle.switch('hide-helpful-chart') %}
<a id="show-chart">{{ _('Show Helpfulness Votes Chart') }}</a>
{% endif %}
</div>
{% endif %}
{% set reached_current = False %}
Expand Down Expand Up @@ -33,7 +36,7 @@
{% endif %}
</li>
{% endif %}
<li class="{% if document.current_revision == rev %}current{% endif %}{% if rev.reviewed and not rev.is_approved %}rejected{% endif %}">
<li id="rev-list-{{ rev.id }}" class="{% if document.current_revision == rev %}current{% endif %}{% if rev.reviewed and not rev.is_approved %}rejected{% endif %}">
<div class="radio">
{% if not loop.first %}<input type="radio" name="from" value="{{ rev.id }}" {% if loop.index == 2 %}checked="checked"{% endif %} />{% endif %}
</div>
Expand Down
44 changes: 44 additions & 0 deletions apps/wiki/tests/test_templates.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from datetime import datetime, timedelta
import difflib
import json

from django.conf import settings
from django.contrib.auth.models import User
Expand Down Expand Up @@ -1764,6 +1765,49 @@ def test_vote_ajax(self):
eq_(1, votes.count())
assert votes[0].helpful

def test_helpfulvotes_graph_async_yes(self):
r = self.document.current_revision
response = post(self.client, 'wiki.document_vote',
{'helpful': 'Yes', 'revision_id': r.id}, args=[self.document.slug])
eq_(200, response.status_code)

resp = get(self.client, 'wiki.get_helpful_votes_async', args=[r.document.slug])
eq_(200, resp.status_code)
data = json.loads(resp.content)
eq_(2, len(data['data']))
eq_('Yes', data['data'][0]['name'])
eq_(1, len(data['data'][0]['data']))
eq_('No', data['data'][1]['name'])
eq_(1, len(data['data'][1]['data']))

eq_(1, len(data['date_to_rev_id']))

def test_helpfulvotes_graph_async_no(self):
r = self.document.current_revision
response = post(self.client, 'wiki.document_vote',
{'helpful': 'Yes', 'revision_id': r.id}, args=[self.document.slug])
eq_(200, response.status_code)

resp = get(self.client, 'wiki.get_helpful_votes_async', args=[r.document.slug])
eq_(200, resp.status_code)
data = json.loads(resp.content)
eq_(2, len(data['data']))
eq_('Yes', data['data'][0]['name'])
eq_(1, len(data['data'][0]['data']))
eq_('No', data['data'][1]['name'])
eq_(1, len(data['data'][1]['data']))

eq_(1, len(data['date_to_rev_id']))

def test_helpfulvotes_graph_async_no_votes(self):
r = self.document.current_revision

resp = get(self.client, 'wiki.get_helpful_votes_async', args=[r.document.slug])
eq_(200, resp.status_code)
data = json.loads(resp.content)
eq_(0, len(data['data']))

eq_(0, len(data['date_to_rev_id']))

class SelectLocaleTests(TestCaseBase):
"""Test the locale selection page"""
Expand Down
3 changes: 3 additions & 0 deletions apps/wiki/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
# Vote helpful/not helpful
url(r'^/vote', 'helpful_vote', name="wiki.document_vote"),

# Get helpful votes data
url(r'^/get-votes-async', 'get_helpful_votes_async', name="wiki.get_helpful_votes_async"),

# KB discussion forums
(r'^/discuss', include(doc_discuss_patterns)),

Expand Down
81 changes: 81 additions & 0 deletions apps/wiki/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
import json
import logging
from string import ascii_letters
import time

from django.conf import settings
from django.contrib import messages
from django.contrib.auth.models import User
from django.core.exceptions import PermissionDenied
from django.db import connection
from django.http import (HttpResponse, HttpResponseRedirect,
Http404, HttpResponseBadRequest)
from django.shortcuts import get_object_or_404
Expand Down Expand Up @@ -709,6 +711,85 @@ def helpful_vote(request, document_slug):
return HttpResponseRedirect(revision.document.get_absolute_url())


@require_GET
def get_helpful_votes_async(request, document_slug):
document = get_object_or_404(
Document, locale=request.locale, slug=document_slug)

yes_data = []
no_data = []
date_to_rev_id = {}
flag_data = []
revisions = set([])

start = time.time()
cursor = connection.cursor()

cursor.execute('SELECT wiki_helpfulvote.revision_id, '
'SUM(wiki_helpfulvote.helpful), '
'SUM(NOT(wiki_helpfulvote.helpful)), '
'wiki_helpfulvote.created '
'FROM wiki_helpfulvote '
'INNER JOIN wiki_revision ON '
'wiki_helpfulvote.revision_id=wiki_revision.id '
'WHERE wiki_revision.document_id=%s '
'GROUP BY DATE(wiki_helpfulvote.created)',
[document.id])

results = cursor.fetchall()
for res in results:
created = 1000 * int(time.mktime(res[3].timetuple()))
yes_data.append([created, int(res[1])])
no_data.append([created, int(res[2])])
date_to_rev_id[created] = res[0]
revisions.add(int(res[0]))

# Drop the first revision marker to make the graph cleaner
revisions = sorted(revisions)[1:]

for rev in revisions:
r = get_object_or_404(Revision, id=rev)
rdate = r.created

flag_data.append({
'x': 1000 * int(time.mktime(rdate.timetuple())),
'title': _('Revision'),
'text': unicode(_('Revision %s')) % rdate
#'url': 'http://www.google.com/' # Not supported yet
})

end = time.time()

send = {'data': [{
'name': _('Yes'),
'id': 'yes_data',
'data': yes_data
},
{
'name': _('No'),
'id': 'no_data',
'data': no_data
},
{
'type': 'flags',
'data': flag_data,
'shape': 'squarepin'
}],
'date_to_rev_id': date_to_rev_id,
'query': end - start
}

if len(send['data'][2]['data']) == 0:
send['data'].pop(2)
if len(send['data'][1]['data']) == 0:
send['data'].pop(1)
if len(send['data'][0]['data']) == 0:
send['data'].pop(0)

return HttpResponse(json.dumps(send),
mimetype='application/json')


@login_required
@permission_required('wiki.delete_revision')
def delete_revision(request, document_slug, revision_id):
Expand Down
15 changes: 15 additions & 0 deletions docs/licenses.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
========================
Licenses
========================

The license for Kitsune's codebase can be found in LICENSE.


License Exceptions
==================

The charting library included with the Wiki (HighCharts) is for non-commercial
use only. It is not free for commercial use. For more information regarding
commercial usage, visit `http://www.highcharts.com
<http://www.highcharts.com/>`_.

25 changes: 25 additions & 0 deletions media/css/wiki.css
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,18 @@ details.h2 > summary {
font-weight: normal;
}

#helpful-chart {
display: none;
height: 400px;
width: 100%;
}

#chart-footnote {
color: #ABABAB;
font-size: 10px;
text-align: right;
}

#revision-list form {
margin: 10px 0;
}
Expand Down Expand Up @@ -572,6 +584,10 @@ details.h2 > summary {
font-weight: bold;
}

#revision-list li.graph-hover {
background: #f6f6ad;
}

#revision-list li.rejected *,
#revision-list li.rejected div.creator {
color: #999;
Expand Down Expand Up @@ -705,6 +721,15 @@ details.h2 > summary {
text-align: right;
}

#show-chart {
color: #447BC4;
cursor: pointer;
float: right;
font-family: Trebuchet MS, Helvetica, sans-serif;
font-size: 13px;
margin-top: 1px;
}

/* Revision page */
div.revision-info li {
list-style-position: inside;
Expand Down
107 changes: 107 additions & 0 deletions media/js/charts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* charts.js
* Scripts to support charts.
*/

(function ($) {
function init() {
$('#show-chart').unbind('click');
$('#show-chart').html(gettext('Loading...'));
$('#show-chart').css('color', '#333333').css('cursor', 'auto').css('text-decoration', 'none');
initChart();
}

function initChart() {
var data, dateToRevID;
$.ajax({
type: "GET",
url: $('#helpful-chart').data('url'),
data: null,
success: function (response) {
data = response['data'];
dateToRevID = response['date_to_rev_id'];
if(data.length > 0) {
$('#helpful-chart').show('fast', function () {
stockChart(data, dateToRevID);
$('#helpful-chart').append('<div id="chart-footnote">' + gettext('Query took: ') + response['query'] + gettext(' seconds') + '</div>')
});
}
else {
$('#show-chart').html(gettext('No votes data'));
$('#show-chart').unbind('click');
}
},
error: function () {
$('#show-chart').html(gettext('Error loading chart'));
$('#show-chart').unbind('click');
}
});
}

/*
* stockChart()
* Creates the StockChart object with the defined options.
* Requires data, plotLines, and dateToRevID to be set prior via AJAX.
* The graph is drawn upon creation of the StockChart object.
* Returns: nothing
*/
function stockChart(data, dateToRevID) {
chart = new Highcharts.StockChart({
chart: {
renderTo: 'helpful-chart'
},
rangeSelector: {
selected: 1
},

title: {
text: gettext('Helpfulness Votes')
},
xAxis: {
type: 'datetime',
maxZoom: 14 * 24 * 3600000, // fourteen days
title: {
text: null
}
},
yAxis: {
title: {
text: gettext('Votes')
}
},
tooltip: {
style: {
width: 200
}
},
credits: {
enabled: false
},
plotOptions: {
series: {
cursor: 'pointer',
point: {
events: {
mouseOver: function () {
$('#rev-list-' + dateToRevID[this.x]).addClass('graph-hover');
},
mouseOut: function () {
$('#rev-list-' + dateToRevID[this.x]).removeClass('graph-hover');
}
}
},
marker: {
lineWidth: 1
},
stickyTracking: true,
}
},
series: data
}, function () {
$('#show-chart').hide(); // loading complete callback
});
}

$('#show-chart').click(init);

}(jQuery));
4 changes: 4 additions & 0 deletions settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,10 @@ def JINJA_CONFIG():
'js/dashboards.js',
'js/editable.js',
),
'wiki.history': (
'js/charts.js',
'js/libs/highcharts-2.1.4/highcharts.src.js',
),
'customercare': (
'js/libs/jquery.NobleCount.js',
'js/libs/jquery.cookie.js',
Expand Down

0 comments on commit 05e3d84

Please sign in to comment.