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();