From 2ed11a90ad36db0df704408882a7014c0264dfb6 Mon Sep 17 00:00:00 2001 From: Ricky Rosario Date: Mon, 13 Feb 2012 22:42:11 -0500 Subject: [PATCH] [bug 718696] Add CTR chart to KPI dashboard. --- apps/kpi/api.py | 14 +++---- apps/kpi/templates/kpi/dashboard.html | 12 +++--- apps/kpi/tests/test_api.py | 8 ++-- media/js/kpi.dashboard.js | 59 ++++++++++++++++++++++----- 4 files changed, 64 insertions(+), 29 deletions(-) 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(); } });