Title: [182496] trunk/Websites/perf.webkit.org
Revision
182496
Author
[email protected]
Date
2015-04-07 14:42:37 -0700 (Tue, 07 Apr 2015)

Log Message

Perf dashboard should have a way of marking outliers
https://bugs.webkit.org/show_bug.cgi?id=143466

Reviewed by Chris Dumez.

Added UI to mark a data point as an outlier as well as a button to toggle the visibility of outliers.
Added a new privileged API /privileged-api/update-run-status to store this boolean flag.

* init-database.sql: Added run_marked_outlier column to test_runs table.

* public/admin/tests.php:

* public/api/runs.php:
(main): Only emit Cache-Control and Expires headers in v1 UI.
(RunsGenerator::format_run): Emit markedOutlier.

* public/include/admin-header.php:

* public/include/db.php:
(Database::is_true): Made it static.

* public/include/manifest.php:
(Manifest::platforms):

* public/index.html: Call into /api/runs/ with ?cache=true.

* public/privileged-api/update-run-status.php: Added.
(main): Updates the newly added column in test_runs table.

* public/v2/app.js:
(App.Pane._fetch):
(App.Pane.refetchRuns): Extracted from App.Pane._fetch.
(App.Pane._didFetchRuns): Renamed from _updateChartData.
(App.Pane._setNewChartData): Added. Pick the right time series based based on the value of showOutlier.
Cloning chartData is necessary when toggling the outlier visibility or using statistics tools because
the interactive chart component only observes changes to chartData and not individual properties of it.
(App.Pane._highlightPointsMarkedAsOutlier): Added. Highlight points marked as outliers.
(App.Pane._movingAverageOrEnvelopeStrategyDidChange): Call to _setNewChartData replaced the code to
clone chartData here.

(App.PaneController.actions.toggleShowOutlier): Toggle the visibility of points marked as outliers by
invoking App.Pane._setNewChartData.
(App.PaneController._detailsChanged): Don't hide the analysis pane when details changed since keep
opening the pane for marking points as outliers would be annoying.
(App.PaneController._updateCanAnalyze): Update 'cannotMarkOutlier' as well as 'cannotAnalyze'.
(App.PaneController.selectedMeasurement): Added.
(App.PaneController.showOutlierTitle): Added.
(App.PaneController._selectedItemIsMarkedOutlierDidChange): Added. Call out to setMarkedOutlier to
mark the selected point as an outlier via the newly added privileged API.

* public/v2/chart-pane.css: Updated styles.

* public/v2/data.js:
(PrivilegedAPI._post): Report the semantic errors.
(Measurement.prototype.markedOutlier): Added.
(Measurement.prototype.setMarkedOutlier): Added. Uses PrivilegedAPI to update the database.
(RunsData.prototype.timeSeriesByCommitTime): Added a new argument, includeOutliers, to indicate
whether the time series should include measurements marked as outliers or not.
(RunsData.prototype.timeSeriesByBuildTime): Ditto.
(RunsData.prototype._timeSeriesByTimeInternal): Extracted from timeSeriesByCommitTime and
timeSeriesByBuildTime to share code. Now ignores measurements marked as outliers if needed.

* public/v2/index.html: Added an icon for showing and hiding outliers. Also added a checkbox to
mark individual points as outliers.

* public/v2/interactive-chart.js:
(App.InteractiveChartComponent._selectClosestPointToMouseAsCurrentItem): Re-enable the distance
heuristics that takes vertical closeness into account. This heuristics is more useful when marking
some points as outliers. This heuristics was disabled because the behavior was unpredictable but
with the arrow key navigation support, this is no longer an issue.

* public/v2/manifest.js:
(App.Manifest._formatFetchedData): Added showOutlier to the chart data. This function dynamically
updates the time series in this chart data in order to include or exclude outliers.

Modified Paths

Added Paths

Diff

Modified: trunk/Websites/perf.webkit.org/ChangeLog (182495 => 182496)


--- trunk/Websites/perf.webkit.org/ChangeLog	2015-04-07 21:34:05 UTC (rev 182495)
+++ trunk/Websites/perf.webkit.org/ChangeLog	2015-04-07 21:42:37 UTC (rev 182496)
@@ -1,3 +1,80 @@
+2015-04-07  Ryosuke Niwa  <[email protected]>
+
+        Perf dashboard should have a way of marking outliers
+        https://bugs.webkit.org/show_bug.cgi?id=143466
+
+        Reviewed by Chris Dumez.
+
+        Added UI to mark a data point as an outlier as well as a button to toggle the visibility of outliers.
+        Added a new privileged API /privileged-api/update-run-status to store this boolean flag.
+
+        * init-database.sql: Added run_marked_outlier column to test_runs table.
+
+        * public/admin/tests.php:
+
+        * public/api/runs.php:
+        (main): Only emit Cache-Control and Expires headers in v1 UI.
+        (RunsGenerator::format_run): Emit markedOutlier.
+
+        * public/include/admin-header.php:
+
+        * public/include/db.php:
+        (Database::is_true): Made it static.
+
+        * public/include/manifest.php:
+        (Manifest::platforms):
+
+        * public/index.html: Call into /api/runs/ with ?cache=true.
+
+        * public/privileged-api/update-run-status.php: Added.
+        (main): Updates the newly added column in test_runs table.
+
+        * public/v2/app.js:
+        (App.Pane._fetch):
+        (App.Pane.refetchRuns): Extracted from App.Pane._fetch.
+        (App.Pane._didFetchRuns): Renamed from _updateChartData.
+        (App.Pane._setNewChartData): Added. Pick the right time series based based on the value of showOutlier.
+        Cloning chartData is necessary when toggling the outlier visibility or using statistics tools because
+        the interactive chart component only observes changes to chartData and not individual properties of it.
+        (App.Pane._highlightPointsMarkedAsOutlier): Added. Highlight points marked as outliers.
+        (App.Pane._movingAverageOrEnvelopeStrategyDidChange): Call to _setNewChartData replaced the code to
+        clone chartData here.
+
+        (App.PaneController.actions.toggleShowOutlier): Toggle the visibility of points marked as outliers by
+        invoking App.Pane._setNewChartData.
+        (App.PaneController._detailsChanged): Don't hide the analysis pane when details changed since keep
+        opening the pane for marking points as outliers would be annoying.
+        (App.PaneController._updateCanAnalyze): Update 'cannotMarkOutlier' as well as 'cannotAnalyze'.
+        (App.PaneController.selectedMeasurement): Added.
+        (App.PaneController.showOutlierTitle): Added.
+        (App.PaneController._selectedItemIsMarkedOutlierDidChange): Added. Call out to setMarkedOutlier to
+        mark the selected point as an outlier via the newly added privileged API.
+
+        * public/v2/chart-pane.css: Updated styles.
+
+        * public/v2/data.js:
+        (PrivilegedAPI._post): Report the semantic errors.
+        (Measurement.prototype.markedOutlier): Added.
+        (Measurement.prototype.setMarkedOutlier): Added. Uses PrivilegedAPI to update the database.
+        (RunsData.prototype.timeSeriesByCommitTime): Added a new argument, includeOutliers, to indicate
+        whether the time series should include measurements marked as outliers or not.
+        (RunsData.prototype.timeSeriesByBuildTime): Ditto.
+        (RunsData.prototype._timeSeriesByTimeInternal): Extracted from timeSeriesByCommitTime and
+        timeSeriesByBuildTime to share code. Now ignores measurements marked as outliers if needed.
+
+        * public/v2/index.html: Added an icon for showing and hiding outliers. Also added a checkbox to
+        mark individual points as outliers.
+
+        * public/v2/interactive-chart.js:
+        (App.InteractiveChartComponent._selectClosestPointToMouseAsCurrentItem): Re-enable the distance
+        heuristics that takes vertical closeness into account. This heuristics is more useful when marking
+        some points as outliers. This heuristics was disabled because the behavior was unpredictable but
+        with the arrow key navigation support, this is no longer an issue.
+
+        * public/v2/manifest.js:
+        (App.Manifest._formatFetchedData): Added showOutlier to the chart data. This function dynamically
+        updates the time series in this chart data in order to include or exclude outliers.
+
 2015-04-03  Ryosuke Niwa  <[email protected]>
 
         Perf dashboard should be able to trigger A/B testing jobs for iOS

Modified: trunk/Websites/perf.webkit.org/init-database.sql (182495 => 182496)


--- trunk/Websites/perf.webkit.org/init-database.sql	2015-04-07 21:34:05 UTC (rev 182495)
+++ trunk/Websites/perf.webkit.org/init-database.sql	2015-04-07 21:42:37 UTC (rev 182496)
@@ -135,6 +135,7 @@
     run_mean_cache double precision,
     run_sum_cache double precision,
     run_square_sum_cache double precision,
+    run_marked_outlier boolean,
     CONSTRAINT test_config_build_must_be_unique UNIQUE(run_config, run_build));
 CREATE INDEX run_config_index ON test_runs(run_config);
 CREATE INDEX run_build_index ON test_runs(run_build);

Modified: trunk/Websites/perf.webkit.org/public/admin/tests.php (182495 => 182496)


--- trunk/Websites/perf.webkit.org/public/admin/tests.php	2015-04-07 21:34:05 UTC (rev 182495)
+++ trunk/Websites/perf.webkit.org/public/admin/tests.php	2015-04-07 21:42:37 UTC (rev 182496)
@@ -196,9 +196,9 @@
                         if (!$configurations)
                             continue;
                         echo "<label><input type=\"checkbox\" name=\"metric_platforms[]\" value=\"{$platform['platform_id']}\"";
-                        if ($db->is_true($configurations[0]['config_is_in_dashboard']))
+                        if (Database::is_true($configurations[0]['config_is_in_dashboard']))
                             echo ' checked';
-                        else if ($db->is_true($platform['platform_hidden']))
+                        else if (Database::is_true($platform['platform_hidden']))
                             echo 'disabled';
                         echo ">$platform_name</label>";
                     }

Modified: trunk/Websites/perf.webkit.org/public/api/runs.php (182495 => 182496)


--- trunk/Websites/perf.webkit.org/public/api/runs.php	2015-04-07 21:34:05 UTC (rev 182495)
+++ trunk/Websites/perf.webkit.org/public/api/runs.php	2015-04-07 21:42:37 UTC (rev 182496)
@@ -43,10 +43,10 @@
         exit_with_error('ConfigurationNotFound');
 
     $test_group_id = array_get($_GET, 'testGroup');
+    $should_cache = array_get($_GET, 'cache');
     if ($test_group_id)
         $test_group_id = intval($test_group_id);
-    else {
-        // FIXME: We should support revalication as well as caching results in the server side.
+    else if ($should_cache) { // Only v1 UI needs caching.
         $maxage = config('jsonCacheMaxAge');
         header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $maxage) . ' GMT');
         header("Cache-Control: maxage=$maxage");
@@ -102,6 +102,7 @@
             'iterationCount' => intval($run['run_iteration_count_cache']),
             'sum' => floatval($run['run_sum_cache']),
             'squareSum' => floatval($run['run_square_sum_cache']),
+            'markedOutlier' => Database::is_true($run['run_marked_outlier']),
             'revisions' => self::parse_revisions_array($run['revisions']),
             'build' => $run['build_id'],
             'buildTime' => Database::to_js_time($run['build_time']),

Modified: trunk/Websites/perf.webkit.org/public/include/admin-header.php (182495 => 182496)


--- trunk/Websites/perf.webkit.org/public/include/admin-header.php	2015-04-07 21:34:05 UTC (rev 182495)
+++ trunk/Websites/perf.webkit.org/public/include/admin-header.php	2015-04-07 21:42:37 UTC (rev 182496)
@@ -130,7 +130,7 @@
             $show_update_button = $show_update_button_if_needed;
             break;
         case 'boolean':
-            $checkedness = $this->db->is_true($value) ? ' checked' : '';
+            $checkedness = Database::is_true($value) ? ' checked' : '';
             echo <<< END
 <input type="checkbox" name="$name"$checkedness>
 END;

Modified: trunk/Websites/perf.webkit.org/public/include/db.php (182495 => 182496)


--- trunk/Websites/perf.webkit.org/public/include/db.php	2015-04-07 21:34:05 UTC (rev 182495)
+++ trunk/Websites/perf.webkit.org/public/include/db.php	2015-04-07 21:42:37 UTC (rev 182496)
@@ -60,7 +60,7 @@
         $this->connection = false;
     }
 
-    function is_true($value) {
+    static function is_true($value) {
         return $value == 't';
     }
 

Modified: trunk/Websites/perf.webkit.org/public/include/manifest.php (182495 => 182496)


--- trunk/Websites/perf.webkit.org/public/include/manifest.php	2015-04-07 21:34:05 UTC (rev 182495)
+++ trunk/Websites/perf.webkit.org/public/include/manifest.php	2015-04-07 21:42:37 UTC (rev 182496)
@@ -79,7 +79,7 @@
         $platform_metrics = array();
         if ($config_table) {
             foreach ($config_table as $config_row) {
-                if ($is_dashboard && !$this->db->is_true($config_row['config_is_in_dashboard']))
+                if ($is_dashboard && !Database::is_true($config_row['config_is_in_dashboard']))
                     continue;
 
                 $new_last_modified = array_get($config_row, 'config_runs_last_modified', 0);
@@ -104,7 +104,7 @@
         $platforms = array();
         if ($platform_table) {
             foreach ($platform_table as $platform_row) {
-                if ($this->db->is_true($platform_row['platform_hidden']))
+                if (Database::is_true($platform_row['platform_hidden']))
                     continue;
                 $id = $platform_row['platform_id'];
                 if (array_key_exists($id, $platform_metrics)) {

Modified: trunk/Websites/perf.webkit.org/public/index.html (182495 => 182496)


--- trunk/Websites/perf.webkit.org/public/index.html	2015-04-07 21:34:05 UTC (rev 182495)
+++ trunk/Websites/perf.webkit.org/public/index.html	2015-04-07 21:42:37 UTC (rev 182496)
@@ -840,7 +840,7 @@
         return runs;
     }
 
-    $.getJSON('api/runs/' + filename, function (response) {
+    $.getJSON('api/runs/' + filename + '?cache=true', function (response) {
         var data = ""
         callback(createRunAndResults(data.current), createRunAndResults(data.baseline), createRunAndResults(data.target));
     });

Added: trunk/Websites/perf.webkit.org/public/privileged-api/update-run-status.php (0 => 182496)


--- trunk/Websites/perf.webkit.org/public/privileged-api/update-run-status.php	                        (rev 0)
+++ trunk/Websites/perf.webkit.org/public/privileged-api/update-run-status.php	2015-04-07 21:42:37 UTC (rev 182496)
@@ -0,0 +1,30 @@
+<?php
+
+require_once('../include/json-header.php');
+
+function main() {
+    $data = ""
+
+    $run_id = array_get($data, 'run');
+    if (!$run_id)
+        exit_with_error('MissingRunId');
+
+    $db = connect();
+    $run = $db->select_first_row('test_runs', 'run', array('id' => $run_id));
+    if (!$run)
+        exit_with_error('InvalidRun', array('run' => $run_id));
+
+    $marked_outlier = array_get($data, 'markedOutlier');
+
+    $db->begin_transaction();
+    $db->update_row('test_runs', 'run', array('id' => $run_id), array(
+        'id' => $run_id,
+        'marked_outlier' => $marked_outlier ? 't' : 'f'));
+    $db->commit_transaction();
+
+    exit_with_success();
+}
+
+main();
+
+?>

Modified: trunk/Websites/perf.webkit.org/public/v2/app.js (182495 => 182496)


--- trunk/Websites/perf.webkit.org/public/v2/app.js	2015-04-07 21:34:05 UTC (rev 182495)
+++ trunk/Websites/perf.webkit.org/public/v2/app.js	2015-04-07 21:42:37 UTC (rev 182496)
@@ -349,29 +349,57 @@
         else if (!this._isValidId(metricId))
             this.set('failure', metricId ? 'Invalid metric id:' + metricId : 'Metric id was not specified');
         else {
-            var store = this.get('store');
-            var updateChartData = this._updateChartData.bind(this);
-            var handleErrors = this._handleFetchErrors.bind(this, platformId, metricId);
+            var self = this;
             var useCache = true;
-            App.Manifest.fetchRunsWithPlatformAndMetric(store, platformId, metricId, null, useCache).then(function (result) {
-                    updateChartData(result);
-                    if (!result.shouldRefetch)
-                        return;
-
-                    useCache = false;
-                    App.Manifest.fetchRunsWithPlatformAndMetric(store, platformId, metricId, null, useCache)
-                        .then(updateChartData, handleErrors);
-                }, handleErrors);
+            App.Manifest.fetchRunsWithPlatformAndMetric(this.get('store'), platformId, metricId, null, useCache)
+                .then(function (result) {
+                    self._didFetchRuns(result);
+                    if (result.shouldRefetch)
+                        self.refetchRuns();
+                }, this._handleFetchErrors.bind(this, platformId, metricId));
             this.fetchAnalyticRanges();
         }
     }.observes('platformId', 'metricId').on('init'),
-    _updateChartData: function (result)
+    refetchRuns: function () {
+        var platform = this.get('platform');
+        var metric = this.get('metric');
+        Ember.assert('refetchRuns should be called only after platform and metric are resolved', platform && metric);
+
+        var useCache = false;
+        App.Manifest.fetchRunsWithPlatformAndMetric(this.get('store'), platform.get('id'), metric.get('id'), null, useCache)
+            .then(this._didFetchRuns.bind(this), this._handleFetchErrors.bind(this, platform.get('id'), metric.get('id')));
+    },
+    _didFetchRuns: function (result)
     {
         this.set('platform', result.platform);
         this.set('metric', result.metric);
-        this.set('chartData', result.data);
+        this._setNewChartData(result.data);
+    },
+    _setNewChartData: function (chartData)
+    {
+        var newChartData = {};
+        for (var property in chartData)
+            newChartData[property] = chartData[property];
+
+        var showOutlier = this.get('showOutlier');
+        newChartData.showOutlier(showOutlier);
+        this.set('chartData', newChartData);
         this._updateMovingAverageAndEnvelope();
+
+        if (!this.get('anomalyDetectionStrategies').filterBy('enabled').length)
+            this._highlightPointsMarkedAsOutlier(newChartData);
     },
+    _highlightPointsMarkedAsOutlier: function (newChartData)
+    {
+        var data = ""
+        var items = {};
+        for (var i = 0; i < data.length; i++) {
+            if (data[i].measurement.markedOutlier())
+                items[data[i].measurement.id()] = true;
+        }
+
+        this.set('highlightedItems', items);
+    },
     _handleFetchErrors: function (platformId, metricId, result)
     {
         if (!result || typeof(result) === "string")
@@ -518,16 +546,10 @@
         this.set('highlightedItems', anomalies);
     },
     _movingAverageOrEnvelopeStrategyDidChange: function () {
-        this._updateMovingAverageAndEnvelope();
-
-        var newChartData = {};
         var chartData = this.get('chartData');
         if (!chartData)
             return;
-        for (var property in chartData)
-            newChartData[property] = chartData[property];
-        this.set('chartData', newChartData);
-
+        this._setNewChartData(chartData);
     }.observes('chosenMovingAverageStrategy', '[email protected]',
         'chosenEnvelopingStrategy', '[email protected]',
         '[email protected]'),
@@ -925,6 +947,15 @@
                 this.set('showingStatPane', false);
             }
         },
+        toggleShowOutlier: function ()
+        {
+            var pane = this.get('model');
+            pane.toggleProperty('showOutlier');
+            var chartData = pane.get('chartData');
+            if (!chartData)
+                return;
+            pane._setNewChartData(chartData);
+        },
         createAnalysisTask: function ()
         {
             var name = this.get('newAnalysisTaskName');
@@ -978,10 +1009,6 @@
             Ember.run.debounce(this, 'propagateZoom', 100);
         },
     },
-    _detailsChanged: function ()
-    {
-        this.set('showingAnalysisPane', false);
-    }.observes('details'),
     _overviewSelectionChanged: function ()
     {
         var overviewSelection = this.get('overviewSelection');
@@ -1013,9 +1040,43 @@
     }.observes('parentController.sharedZoom').on('init'),
     _updateCanAnalyze: function ()
     {
-        var points = this.get('model').get('selectedPoints');
+        var pane = this.get('model');
+        var points = pane.get('selectedPoints');
         this.set('cannotAnalyze', !this.get('newAnalysisTaskName') || !points || points.length < 2);
-    }.observes('newAnalysisTaskName', 'model.selectedPoints'),
+        this.set('cannotMarkOutlier', !!points || !this.get('selectedItem'));
+
+        var selectedMeasurement = this.selectedMeasurement();
+        this.set('selectedItemIsMarkedOutlier', selectedMeasurement && selectedMeasurement.markedOutlier());
+
+    }.observes('newAnalysisTaskName', 'model.selectedPoints', 'model.selectedItem').on('init'),
+    selectedMeasurement: function () {
+        var chartData = this.get('model').get('chartData');
+        var selectedItem = this.get('selectedItem');
+        if (!chartData || !selectedItem)
+            return null;
+        var point = chartData.current.findPointByMeasurementId(selectedItem);
+        Ember.assert('selectedItem should always be in the current chart data', point);
+        return point.measurement;
+    },
+    showOutlierTitle: function ()
+    {
+        return this.get('showOutlier') ? 'Hide outliers' : 'Show outliers';
+    }.property('showOutlier'),
+    _selectedItemIsMarkedOutlierDidChange: function ()
+    {
+        var selectedMeasurement = this.selectedMeasurement();
+        if (!selectedMeasurement)
+            return;
+        var selectedItemIsMarkedOutlier = this.get('selectedItemIsMarkedOutlier');
+        if (selectedMeasurement.markedOutlier() == selectedItemIsMarkedOutlier)
+            return;
+        var pane = this.get('model');
+        selectedMeasurement.setMarkedOutlier(!!selectedItemIsMarkedOutlier).then(function () {
+            pane.refetchRuns();
+        }, function (error) {
+            alert(error);
+        });
+    }.observes('selectedItemIsMarkedOutlier'),
 });
 
 App.AnalysisRoute = Ember.Route.extend({

Modified: trunk/Websites/perf.webkit.org/public/v2/chart-pane.css (182495 => 182496)


--- trunk/Websites/perf.webkit.org/public/v2/chart-pane.css	2015-04-07 21:34:05 UTC (rev 182495)
+++ trunk/Websites/perf.webkit.org/public/v2/chart-pane.css	2015-04-07 21:42:37 UTC (rev 182496)
@@ -44,13 +44,30 @@
 .chart-pane a.stat-button {
     display: inline-block;
     position: absolute;
-    right: 3.15rem;
+    right: 4.45rem;
     top: 0.55rem;
 }
 
-.chart-pane a.bugs-button {
+.chart-pane a.outlier-button {
     display: inline-block;
     position: absolute;
+    right: 3.25rem; /* Shifted to left by 0.1rem for better aesthetics */
+    top: 0.55rem;
+}
+
+a.outlier-button.hide g.show-outlier-icon {
+    fill: transparent;
+    stroke: transparent;
+}
+
+a.outlier-button.show g.hide-outlier-icon {
+    fill: transparent;
+    stroke: transparent;
+}
+
+.chart-pane a.analysis-button {
+    display: inline-block;
+    position: absolute;
     right: 1.85rem;
     top: 0.55rem;
 }
@@ -78,26 +95,25 @@
     display: none;
 }
 
-.stat-pane {
+.stat-pane,
+.annotation-pane {
     right: 2.6rem;
     padding: 0;
 }
 
-.stat-pane fieldset {
-    border: solid 1px #ccc;
-    border-radius: 0.5rem;
-    margin: 0.2rem;
-    padding: 0;
+.popup-pane > .caution {
+    margin: 0;
+    padding: 0.3rem 0.5rem;
 }
 
-.stat-option {
+.popup-pane > section {
     margin: 0;
     padding: 0;
     font-size: 0.8rem;
     max-width: 17rem;
 }
 
-.stat-option h1 {
+.popup-pane > section > h1 {
     font-size: inherit;
     line-height: 0.8rem;
     padding: 0.3rem 0.5rem;
@@ -106,11 +122,11 @@
     border-bottom: solid 1px #ccc;
 }
 
-.stat-option:first-child h1 {
+.popup-pane > section:first-child h1 {
     border-top: none;
 }
 
-.stat-option > * {
+.popup-pane > section > * {
     display: block;
     margin: 0.1rem 0.5rem 0.1rem 1rem;
 }
@@ -119,14 +135,10 @@
     width: 4rem;
 }
 
-.analysis-pane {
+.annotation-pane {
     right: 1.3rem;
 }
 
-.analysis-pane > * {
-    margin: 0.2rem;
-}
-
 .search-pane {
     right: 0rem;
 }

Modified: trunk/Websites/perf.webkit.org/public/v2/data.js (182495 => 182496)


--- trunk/Websites/perf.webkit.org/public/v2/data.js	2015-04-07 21:34:05 UTC (rev 182495)
+++ trunk/Websites/perf.webkit.org/public/v2/data.js	2015-04-07 21:42:37 UTC (rev 182496)
@@ -39,9 +39,10 @@
             data: parameters ? JSON.stringify(parameters) : '{}',
             dataType: 'json',
         }).done(function (data) {
-            if (data.status != 'OK')
+            if (data.status != 'OK') {
+                console.log('PrivilegedAPI failed', data);
                 reject(data.status);
-            else
+            } else
                 resolve(data);
         }).fail(function (xhr, status, error) {
             reject(xhr.status + (error ? ', ' + error : '') + '\n\nWith response:\n' + xhr.responseText);
@@ -288,6 +289,20 @@
     return bugs && Object.keys(bugs).length;
 }
 
+Measurement.prototype.markedOutlier = function ()
+{
+    return this._raw['markedOutlier'];
+}
+
+Measurement.prototype.setMarkedOutlier = function (markedOutlier)
+{
+    var params = {'run': this.id(), 'markedOutlier': markedOutlier};
+    return PrivilegedAPI.sendRequest('update-run-status', params).then(function (data) {
+    }, function (error) {
+        alert('Failed to update the outlier status: ' + error);
+    });
+}
+
 function RunsData(rawData)
 {
     this._measurements = rawData.map(function (run) { return new Measurement(run); });
@@ -298,29 +313,32 @@
     return this._measurements.length;
 }
 
-RunsData.prototype.timeSeriesByCommitTime = function ()
+RunsData.prototype.timeSeriesByCommitTime = function (includeOutliers)
 {
-    return new TimeSeries(this._measurements.map(function (measurement) {
-        var confidenceInterval = measurement.confidenceInterval();
-        return {
-            measurement: measurement,
-            time: measurement.latestCommitTime(),
-            value: measurement.mean(),
-            interval: measurement.confidenceInterval(),
-        };
-    }));
+    return this._timeSeriesByTimeInternal(true, includeOutliers);
 }
 
-RunsData.prototype.timeSeriesByBuildTime = function ()
+RunsData.prototype.timeSeriesByBuildTime = function (includeOutliers)
 {
-    return new TimeSeries(this._measurements.map(function (measurement) {
-        return {
+    return this._timeSeriesByTimeInternal(false, includeOutliers);
+}
+
+RunsData.prototype._timeSeriesByTimeInternal = function (useCommitType, includeOutliers)
+{
+    var series = new Array();
+    var seriesIndex = 0;
+    for (var measurement of this._measurements) {
+        if (measurement.markedOutlier() && !includeOutliers)
+            continue;
+        series.push({
             measurement: measurement,
-            time: measurement.buildTime(),
+            time: useCommitType ? measurement.latestCommitTime() : measurement.buildTime(),
             value: measurement.mean(),
             interval: measurement.confidenceInterval(),
-        };
-    }));
+            markedOutlier: measurement.markedOutlier(),
+        });
+    }
+    return new TimeSeries(series);
 }
 
 // FIXME: We need to devise a way to fetch runs in multiple chunks so that

Modified: trunk/Websites/perf.webkit.org/public/v2/index.html (182495 => 182496)


--- trunk/Websites/perf.webkit.org/public/v2/index.html	2015-04-07 21:34:05 UTC (rev 182495)
+++ trunk/Websites/perf.webkit.org/public/v2/index.html	2015-04-07 21:42:37 UTC (rev 182496)
@@ -150,13 +150,15 @@
                     {{#if movingAverageStrategies}}
                         <a href="" title="Statistical Tools" class="stat-button" {{action "toggleStatPane"}}>{{partial "stat-button"}}</a>
                     {{/if}}
-                    {{#if App.Manifest.bugTrackers}}
-                        <a href="" title="Analysis" class="bugs-button" {{action "toggleBugsPane"}}>
-                            {{partial "analysis-button"}}
-                        </a>
-                    {{/if}}
+                    <a href="" {{bind-attr title=showOutlierTitle class=":outlier-button showOutlier:show:hide"}}
+                        {{action "toggleShowOutlier"}}>
+                        {{partial "outlier-button"}}
+                    </a>
+                    <a href="" title="Analyze the selected range" class="analysis-button" {{action "toggleBugsPane"}}>
+                        {{partial "analysis-button"}}
+                    </a>
                     {{#if App.Manifest.repositoriesWithReportedCommits}}
-                        <a href="" title="Search" class="search-button" {{action "toggleSearchPane"}}>{{partial "search-button"}}</a>
+                        <a href="" title="Search commits by a keyword" class="search-button" {{action "toggleSearchPane"}}>{{partial "search-button"}}</a>
                     {{/if}}
                 </header>
 
@@ -204,8 +206,14 @@
                 </div>
 
                 <div {{bind-attr class=":popup-pane :analysis-pane showingAnalysisPane::hidden"}}>
-                    <label>Name: {{input type=text value=newAnalysisTaskName}}</label>
-                    <button {{action "createAnalysisTask"}} {{bind-attr disabled=cannotAnalyze}}>Analyze</button>
+                    <section class="analysis-option-option">
+                        <h1>Start A/B testing or associate bugs</h1>
+                        <label>Name: {{input type=text value=newAnalysisTaskName}} <button {{action "createAnalysisTask"}} {{bind-attr disabled=cannotAnalyze}}>Analyze</button></label>
+                    </section>
+                    <section class="analysis-option-option">
+                        <h1>Marking outliers</h1>
+                        <label>{{input type=checkbox checked=selectedItemIsMarkedOutlier disabled=cannotMarkOutlier}} Mark as an outlier and hide it.</label>
+                    </section>
                 </div>
 
                 <form {{bind-attr class=":popup-pane :search-pane showingSearchPane::hidden"}}>
@@ -404,6 +412,26 @@
         </section>
     </script>
 
+    <script type="text/x-handlebars" data-template-name="outlier-button">
+        <svg class="outlier-button icon-button" viewBox="0 0 100 100">
+            <g stroke="black" fill="black" stroke-width="15">
+                <line x1="0" y1="70" x2="40" y2="70"/>
+                <circle cx="15" cy="70" r="8"/>
+                <circle cx="45" cy="70" r="8"/>
+                <circle cx="85" cy="70" r="8"/>
+                <line x1="85" y1="70" x2="100" y2="70"/>
+                <g class="show-outlier-icon">
+                    <line x1="45" y1="70" x2="65" y2="20"/>
+                    <line x1="65" y1="20" x2="85" y2="70"/>
+                    <circle cx="65" cy="20" r="8"/>
+                </g>
+                <g class="hide-outlier-icon">
+                    <line x1="45" y1="70" x2="85" y2="70"/>
+                </g>
+            </g>
+        </svg>
+    </script>
+
     <script type="text/x-handlebars" data-template-name="analysis-button">
         <svg class="analysis-button icon-button" viewBox="0 0 100 100">
             <g stroke="black" fill="black" stroke-width="15">

Modified: trunk/Websites/perf.webkit.org/public/v2/interactive-chart.js (182495 => 182496)


--- trunk/Websites/perf.webkit.org/public/v2/interactive-chart.js	2015-04-07 21:34:05 UTC (rev 182495)
+++ trunk/Websites/perf.webkit.org/public/v2/interactive-chart.js	2015-04-07 21:42:37 UTC (rev 182496)
@@ -524,9 +524,6 @@
             var yDiff = mY - point.y;
             return xDiff * xDiff + yDiff * yDiff / 16; // Favor horizontal affinity.
         };
-        distanceHeuristics = function (m) {
-            return Math.abs(xScale(m.time) - point.x);
-        }
 
         var newItemIndex;
         if (point && !this._currentSelection()) {

Modified: trunk/Websites/perf.webkit.org/public/v2/manifest.js (182495 => 182496)


--- trunk/Websites/perf.webkit.org/public/v2/manifest.js	2015-04-07 21:34:05 UTC (rev 182495)
+++ trunk/Websites/perf.webkit.org/public/v2/manifest.js	2015-04-07 21:42:37 UTC (rev 182496)
@@ -333,10 +333,15 @@
         var unitSuffix = unit ? ' ' + unit : '';
         var deltaFormatterWithoutSign = useSI ? d3.format('.2s') : d3.format('.2g');
 
+        var currentTimeSeries = configurations.current.timeSeriesByCommitTime(false);
+        var baselineTimeSeries = configurations.baseline ? configurations.baseline.timeSeriesByCommitTime(false) : null;
+        var targetTimeSeries = configurations.target ? configurations.target.timeSeriesByCommitTime(false) : null;
+        var unfilteredCurrentTimeSeries, unfilteredBaselineTimeSeries, unfilteredTargetTimeSeries;
+
         return {
-            current: configurations.current.timeSeriesByCommitTime(),
-            baseline: configurations.baseline ? configurations.baseline.timeSeriesByCommitTime() : null,
-            target: configurations.target ? configurations.target.timeSeriesByCommitTime() : null,
+            current: currentTimeSeries,
+            baseline: baselineTimeSeries,
+            target: targetTimeSeries,
             unit: unit,
             formatWithUnit: function (value) { return this.formatter(value) + unitSuffix; },
             formatWithDeltaAndUnit: function (value, delta)
@@ -346,6 +351,17 @@
             formatter: useSI ? d3.format('.4s') : d3.format('.4g'),
             deltaFormatter: useSI ? d3.format('+.2s') : d3.format('+.2g'),
             smallerIsBetter: smallerIsBetter,
+            showOutlier: function (show)
+            {
+                if (!unfilteredCurrentTimeSeries) {
+                    unfilteredCurrentTimeSeries = configurations.current.timeSeriesByCommitTime(true);
+                    unfilteredBaselineTimeSeries = configurations.baseline ? configurations.baseline.timeSeriesByCommitTime(true) : null;
+                    unfilteredTargetTimeSeries = configurations.target ? configurations.target.timeSeriesByCommitTime(true) : null;
+                }
+                this.current = show ? unfilteredCurrentTimeSeries : currentTimeSeries;
+                this.baseline = show ? unfilteredBaselineTimeSeries : baselineTimeSeries;
+                this.target = show ? unfilteredTargetTimeSeries : targetTimeSeries;
+            },
         };
     }
 }).create();
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to