diff --git a/apps/kpi/api.py b/apps/kpi/api.py
index fea8674af6d..9250b278e86 100644
--- a/apps/kpi/api.py
+++ b/apps/kpi/api.py
@@ -98,11 +98,7 @@ def obj_get(self, request=None, **kwargs):
"""Fetch a particular ratio by start date."""
raise NotImplementedError
- def get_object_list(self, request):
- """Return the authZ-limited set of ratios"""
- return self.obj_get_list(request)
-
- def obj_get_list(self, request=None, **kwargs):
+ def get_object_list(self, request=None, **kwargs):
"""Return all the ratios.
Or, if a ``min_start`` query param is present, return the (potentially
@@ -112,13 +108,13 @@ def obj_get_list(self, request=None, **kwargs):
If, somehow, half a ratio is missing, that ratio is not returned.
"""
- # Make min_start either a date or None:
+ # Get min_start from query string and validate it.
min_start = request.GET.get('min_start')
if min_start:
try:
- min_start = _parse_date()
+ _parse_date(min_start)
except (ValueError, TypeError):
- pass
+ min_start = None
# I'm not sure you can join a table to itself with the ORM.
cursor = _cursor()
@@ -132,7 +128,7 @@ def obj_get_list(self, request=None, **kwargs):
(self.clicks_kind, self.searches_kind) +
((min_start,) if min_start else ()))
return [Struct(start=s, clicks=n, searches=d) for
- s, n, d in cursor.fetchall()]
+ s, n, d in reversed(cursor.fetchall())]
def obj_create(self, bundle, request=None, **kwargs):
def create_metric(kind, value_field, data):
diff --git a/apps/kpi/templates/kpi/dashboard.html b/apps/kpi/templates/kpi/dashboard.html
index 1869e1aef52..e41d40488f8 100644
--- a/apps/kpi/templates/kpi/dashboard.html
+++ b/apps/kpi/templates/kpi/dashboard.html
@@ -8,11 +8,13 @@
{{ title }}
+ data-solved-url="{{ url('api_dispatch_list', resource_name='kpi_solution', api_name ='v1') }}"
+ data-fastresponse-url="{{ url('api_dispatch_list', resource_name='kpi_fast_response', api_name ='v1') }}"
+ data-active-kb-contributors-url="{{ url('api_dispatch_list', resource_name='kpi_active_kb_contributors', api_name ='v1') }}"
+ data-active-answerers-url="{{ url('api_dispatch_list', resource_name='kpi_active_answerers', api_name ='v1') }}"
+ data-sphinx-ctr-url="{{ url('api_dispatch_list', resource_name='sphinx-clickthrough-rate', api_name ='v1') }}"
+ data-elastic-ctr-url="{{ url('api_dispatch_list', resource_name='elastic-clickthrough-rate', api_name ='v1') }}"
+ data-vote-url="{{ url('api_dispatch_list', resource_name='kpi_vote', api_name ='v1') }}">
diff --git a/apps/kpi/tests/test_api.py b/apps/kpi/tests/test_api.py
index 7828a73fcce..06a96d99bda 100644
--- a/apps/kpi/tests/test_api.py
+++ b/apps/kpi/tests/test_api.py
@@ -159,10 +159,10 @@ def test_sphinx_clickthrough_get(self):
response = self.client.get(url + '?format=json')
self.assertContains( # Beware of dict order changes someday.
response,
- '"objects": [{"clicks": 1, "resource_uri": "", "searches": 10, '
- '"start": "2000-01-01"}, '
- '{"clicks": 2, "resource_uri": "", "searches": 20, '
- '"start": "2000-01-09"}]')
+ '"objects": [{"clicks": 2, "resource_uri": "", "searches": 20, '
+ '"start": "2000-01-09"}, '
+ '{"clicks": 1, "resource_uri": "", "searches": 10, '
+ '"start": "2000-01-01"}]')
# Test filtering by start date:
response = self.client.get(url + '?format=json&min_start=2000-01-09')
diff --git a/media/js/kpi.dashboard.js b/media/js/kpi.dashboard.js
index 8b360690845..18ce2ae024d 100644
--- a/media/js/kpi.dashboard.js
+++ b/media/js/kpi.dashboard.js
@@ -23,11 +23,11 @@ window.ChartModel = Backbone.Model.extend({
/*
* Views
*/
-window.MonthlyChartView = Backbone.View.extend({
+window.BasicChartView = Backbone.View.extend({
tagName: 'section',
className: 'graph',
- initialize: function(s) {
+ initialize: function() {
_.bindAll(this, 'render');
this.model.bind('change', this.render);
@@ -83,12 +83,21 @@ window.MonthlyChartView = Backbone.View.extend({
}
},
- render: function() {
+ addModel: function(model) {
+ model.bind('change', this.render);
+ },
+
+ render: function(model) {
var self = this,
- data = this.model.get('objects');
+ data = model && model.get('objects');
if(data) {
- self.chart = new Highcharts.Chart(self.chartOptions);
+
+ // Create the chart if we haven't yet.
+ if (!self.chart) {
+ self.chart = new Highcharts.Chart(self.chartOptions);
+ }
+
_.each(this.options.series, function(series) {
var mapper = series.mapper,
seriesData;
@@ -106,7 +115,7 @@ window.MonthlyChartView = Backbone.View.extend({
self.chart.addSeries({
data: seriesData,
- name: series.name
+ name: series.name || model.name
});
});
}
@@ -119,7 +128,7 @@ window.StockChartView = Backbone.View.extend({
tagName: 'section',
className: 'graph',
- initialize: function(s) {
+ initialize: function() {
_.bindAll(this, 'render');
this.model.bind('change', this.render);
@@ -290,6 +299,16 @@ window.KpiDashboard = Backbone.View.extend({
url: $(this.el).data('active-answerers-url')
});
+ this.sphinxCtrChart = new ChartModel([], {
+ url: $(this.el).data('sphinx-ctr-url')
+ });
+ this.sphinxCtrChart.name = 'Sphinx';
+
+ this.elasticCtrChart = new ChartModel([], {
+ url: $(this.el).data('elastic-ctr-url')
+ });
+ this.elasticCtrChart.name = 'Elastic';
+
// Create the views.
this.solvedChartView = new StockChartView({
model: this.solvedChart,
@@ -302,7 +321,7 @@ window.KpiDashboard = Backbone.View.extend({
}]
});
- this.voteChartView = new MonthlyChartView({
+ this.voteChartView = new BasicChartView({
model: this.voteChart,
title: gettext('Helpful Votes'),
percent: true,
@@ -337,7 +356,7 @@ window.KpiDashboard = Backbone.View.extend({
}]
});
- this.activeKbContributorsView = new MonthlyChartView({
+ this.activeKbContributorsView = new BasicChartView({
model: this.activeKbContributorsChart,
title: gettext('Active KB Contributors'),
series: [{
@@ -359,7 +378,7 @@ window.KpiDashboard = Backbone.View.extend({
}]
});
- this.activeAnswerers = new MonthlyChartView({
+ this.activeAnswerersView = new BasicChartView({
model: this.activeAnswerersChart,
title: gettext('Active Support Forum Contributors'),
series: [{
@@ -373,13 +392,29 @@ window.KpiDashboard = Backbone.View.extend({
}]
});
+ this.ctrView = new BasicChartView({
+ model: this.sphinxCtrChart,
+ title: gettext('Search Clickthrough Rate'),
+ percent: true,
+ series: [{
+ mapper: function(o) {
+ return {
+ x: Date.parse(o['start']),
+ y: o['clicks'] / o['searches'] * 100
+ };
+ }
+ }]
+ });
+ this.ctrView.addModel(this.elasticCtrChart);
+
// Render the views.
$(this.el)
.append(this.solvedChartView.render().el)
.append(this.fastResponseView.render().el)
.append(this.voteChartView.render().el)
.append(this.activeKbContributorsView.render().el)
- .append(this.activeAnswerers.render().el);
+ .append(this.activeAnswerersView.render().el)
+ .append(this.ctrView.render().el);
// Load up the models.
@@ -388,6 +423,8 @@ window.KpiDashboard = Backbone.View.extend({
this.activeKbContributorsChart.fetch();
this.activeAnswerersChart.fetch();
this.voteChart.fetch();
+ this.sphinxCtrChart.fetch();
+ this.elasticCtrChart.fetch();
}
});