From 259b939baf4fbbde02b12c31d422af9b87f32aaa Mon Sep 17 00:00:00 2001 From: William Lachance Date: Tue, 22 Dec 2015 14:28:00 -0500 Subject: [PATCH 1/2] Bug 1234621 - Add basic client-side filtering to perfherder alerts --- .../webapp/api/performance_serializers.py | 9 +- ui/js/controllers/perf/alerts.js | 105 +++++-- ui/js/perfapp.js | 2 +- ui/partials/perf/alertsctrl.html | 280 ++++++++++-------- 4 files changed, 234 insertions(+), 162 deletions(-) diff --git a/treeherder/webapp/api/performance_serializers.py b/treeherder/webapp/api/performance_serializers.py index bbecb2185..02133c630 100644 --- a/treeherder/webapp/api/performance_serializers.py +++ b/treeherder/webapp/api/performance_serializers.py @@ -21,6 +21,9 @@ class TestOptionsSerializer(serializers.JSONField): class PerformanceSignatureSerializer(serializers.ModelSerializer): + framework_id = serializers.SlugRelatedField( + slug_field="id", source="framework", + queryset=PerformanceFramework.objects.all()) option_collection_hash = serializers.SlugRelatedField( read_only=True, slug_field="option_collection_hash", source="option_collection") @@ -32,9 +35,9 @@ class PerformanceSignatureSerializer(serializers.ModelSerializer): class Meta: model = PerformanceSignature - fields = ['signature_hash', 'machine_platform', 'suite', 'test', - 'lower_is_better', 'option_collection_hash', - 'test_options'] + fields = ['framework_id', 'signature_hash', 'machine_platform', + 'suite', 'test', 'lower_is_better', + 'option_collection_hash', 'test_options'] class PerformanceDecimalField(serializers.DecimalField): diff --git a/ui/js/controllers/perf/alerts.js b/ui/js/controllers/perf/alerts.js index 3c8b54537..c06ef5f94 100644 --- a/ui/js/controllers/perf/alerts.js +++ b/ui/js/controllers/perf/alerts.js @@ -84,15 +84,16 @@ perf.controller( perf.controller('AlertsCtrl', [ '$state', '$stateParams', '$scope', '$rootScope', '$http', '$q', '$modal', 'thUrl', 'ThRepositoryModel', 'ThOptionCollectionModel', 'ThResultSetModel', - 'thDefaultRepo', 'PhSeries', 'PhAlerts', 'phTimeRanges', 'phDefaultTimeRangeValue', - 'phAlertResolutionMap', 'dateFilter', 'thDateFormat', + 'thDefaultRepo', 'PhFramework', 'PhSeries', 'PhAlerts', 'phTimeRanges', + 'phDefaultTimeRangeValue', 'phAlertResolutionMap', 'dateFilter', + 'thDateFormat', function AlertsCtrl($state, $stateParams, $scope, $rootScope, $http, $q, $modal, thUrl, ThRepositoryModel, ThOptionCollectionModel, - ThResultSetModel, thDefaultRepo, PhSeries, PhAlerts, - phTimeRanges, phDefaultTimeRangeValue, - phAlertResolutionMap, dateFilter, thDateFormat) { - + ThResultSetModel, thDefaultRepo, PhFramework, + PhSeries, PhAlerts, phTimeRanges, + phDefaultTimeRangeValue, phAlertResolutionMap, + dateFilter, thDateFormat) { $scope.alertSummaries = []; $scope.getMoreAlertSummariesHref = null; $scope.getCappedMagnitude = function(percent) { @@ -110,6 +111,41 @@ perf.controller('AlertsCtrl', [ }); }; + function updateAlertVisibility() { + _.forEach($scope.alertSummaries, function(alertSummary) { + _.forEach(alertSummary.alerts, function(alert) { + // only show alert if it passes all filter criteria + alert.visible = + (alert.series_signature.framework_id === $scope.filterOptions.framework.id) && + (!$scope.filterOptions.hideImprovements || alert.is_regression) && + _.every($scope.filterOptions.filter.split(' '), + function(matchText) { + return !matchText || + alert.title.toLowerCase().indexOf( + matchText.toLowerCase()) > (-1); + }); + }); + alertSummary.anyVisible = _.any(alertSummary.alerts, + 'visible'); + }); + $scope.numFilteredAlertSummaries = _.filter($scope.alertSummaries, { anyVisible: false }).length; + + } + + $scope.filtersUpdated = function() { + $state.transitionTo('alerts', { + framework: $scope.filterOptions.framework.id, + filter: $scope.filterOptions.filter, + hideImprovements: Boolean($scope.filterOptions.hideImprovements) ? undefined : 0, + }, { + location: true, + inherit: true, + relative: $state.$current, + notify: false + }); + updateAlertVisibility(); + }; + // these methods handle the business logic of alert selection and // unselection $scope.anySelected = function(alerts) { @@ -274,6 +310,7 @@ perf.controller('AlertsCtrl', [ })).then(function() { $scope.alertSummaries = _.union($scope.alertSummaries, alertSummaries); + updateAlertVisibility(); }); } @@ -284,27 +321,39 @@ perf.controller('AlertsCtrl', [ }); }; - ThRepositoryModel.get_list().then(function(response) { - $scope.projects = response.data; - $scope.selectedProject = _.findWhere($scope.projects, { - name: thDefaultRepo ? thDefaultRepo : thDefaultRepo - }); - ThOptionCollectionModel.get_map().then( - function(optionCollectionMap) { - $scope.optionCollectionMap = optionCollectionMap; - if ($stateParams.id) { - PhAlerts.getAlertSummary($stateParams.id).then( - function(data) { - addAlertSummaries([data], null); - }); - } else { - PhAlerts.getAlertSummaries().then(function(data) { - addAlertSummaries(data.results, data.next); - }); - } - }); - - }); - + $q.all([PhFramework.getFrameworkList().then( + function(frameworks) { + $scope.frameworks = frameworks.data; + }), + ThRepositoryModel.get_list().then(function(response) { + $scope.projects = response.data; + $scope.selectedProject = _.findWhere($scope.projects, { + name: thDefaultRepo ? thDefaultRepo : thDefaultRepo + }); + }), + ThOptionCollectionModel.get_map().then( + function(optionCollectionMap) { + $scope.optionCollectionMap = optionCollectionMap; + })] + ).then(function() { + $scope.filterOptions = { + framework: _.find($scope.frameworks, { + id: parseInt($stateParams.framework) + }) || $scope.frameworks[0], + filter: $stateParams.filter || "", + hideImprovements: $stateParams.hideImprovements === undefined || + parseInt($stateParams.hideImprovements) + }; + if ($stateParams.id) { + PhAlerts.getAlertSummary($stateParams.id).then( + function(data) { + addAlertSummaries([data], null); + }); + } else { + PhAlerts.getAlertSummaries().then(function(data) { + addAlertSummaries(data.results, data.next); + }); + } + }); } ]); diff --git a/ui/js/perfapp.js b/ui/js/perfapp.js index c306424a8..8472bfd6d 100644 --- a/ui/js/perfapp.js +++ b/ui/js/perfapp.js @@ -12,7 +12,7 @@ perf.config(function($compileProvider, $httpProvider, $stateProvider, $urlRouter $stateProvider.state('alerts', { title: 'Perfherder Alerts', templateUrl: 'partials/perf/alertsctrl.html', - url: '/alerts?id', + url: '/alerts?id&framework&filter&hideImprovements', controller: 'AlertsCtrl' }).state('graphs', { title: 'Perfherder Graphs', diff --git a/ui/partials/perf/alertsctrl.html b/ui/partials/perf/alertsctrl.html index 34bdd3ebc..797e6e47f 100644 --- a/ui/partials/perf/alertsctrl.html +++ b/ui/partials/perf/alertsctrl.html @@ -1,144 +1,164 @@
- -
-
-
- - Alert #{{alertSummary.id}} - {{alertSummary.title}} - - +
+
+ - - - - Test - - - Previous - - New - Delta - - Confidence - - - - - - - - {{alert.title}} - - -  (invalid) - - -  (untriaged) - - -  (bug #{{alert.bug_number}}) - - -  (see - alert #{{alert.revised_summary_id}}) -    - - graph - - - {{alert.prev_value}} - - - - < - - - > - - - - {{alert.new_value}} - {{alert.amount_pct}}% - -
-
-
-
-
-
-
-
-
-
-
-
- +   +
+ +
+
+ +
+ +
+
+
+ + Alert #{{alertSummary.id}} - {{alertSummary.title}} + + +
+
+

Detected changes

- - - {{alert.t_value}} - - - - -
-
- - - - + + + +
+
- -
-
+

+ {{numFilteredAlertSummaries}} alerts not displayed because they had no changes matching filter criteria +

+
+
+
From 8e4cd5d851211837cdafbeb921ec6b91bac9ff87 Mon Sep 17 00:00:00 2001 From: William Lachance Date: Tue, 22 Dec 2015 15:43:00 -0500 Subject: [PATCH 2/2] Bug 1234621 - Misc. perfherder cleanup --- ui/js/controllers/perf/alerts.js | 16 ++++------------ ui/partials/perf/comparectrl.html | 2 +- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/ui/js/controllers/perf/alerts.js b/ui/js/controllers/perf/alerts.js index c06ef5f94..6cb9ad92b 100644 --- a/ui/js/controllers/perf/alerts.js +++ b/ui/js/controllers/perf/alerts.js @@ -83,14 +83,13 @@ perf.controller( perf.controller('AlertsCtrl', [ '$state', '$stateParams', '$scope', '$rootScope', '$http', '$q', '$modal', - 'thUrl', 'ThRepositoryModel', 'ThOptionCollectionModel', 'ThResultSetModel', - 'thDefaultRepo', 'PhFramework', 'PhSeries', 'PhAlerts', 'phTimeRanges', + 'thUrl', 'ThOptionCollectionModel', 'ThResultSetModel', + 'PhFramework', 'PhSeries', 'PhAlerts', 'phTimeRanges', 'phDefaultTimeRangeValue', 'phAlertResolutionMap', 'dateFilter', 'thDateFormat', function AlertsCtrl($state, $stateParams, $scope, $rootScope, $http, $q, - $modal, - thUrl, ThRepositoryModel, ThOptionCollectionModel, - ThResultSetModel, thDefaultRepo, PhFramework, + $modal, thUrl, ThOptionCollectionModel, + ThResultSetModel, PhFramework, PhSeries, PhAlerts, phTimeRanges, phDefaultTimeRangeValue, phAlertResolutionMap, dateFilter, thDateFormat) { @@ -103,7 +102,6 @@ perf.controller('AlertsCtrl', [ }; $scope.phAlertResolutionMap = phAlertResolutionMap; - // these methods $scope.changeAlertSummaryStatus = function(alertSummary, status) { PhAlerts.changeAlertSummaryStatus( alertSummary.id, status).then(function() { @@ -325,12 +323,6 @@ perf.controller('AlertsCtrl', [ function(frameworks) { $scope.frameworks = frameworks.data; }), - ThRepositoryModel.get_list().then(function(response) { - $scope.projects = response.data; - $scope.selectedProject = _.findWhere($scope.projects, { - name: thDefaultRepo ? thDefaultRepo : thDefaultRepo - }); - }), ThOptionCollectionModel.get_map().then( function(optionCollectionMap) { $scope.optionCollectionMap = optionCollectionMap; diff --git a/ui/partials/perf/comparectrl.html b/ui/partials/perf/comparectrl.html index c4365b51f..76076e7ca 100644 --- a/ui/partials/perf/comparectrl.html +++ b/ui/partials/perf/comparectrl.html @@ -22,7 +22,7 @@
 
- +