<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[180000] trunk/Websites/perf.webkit.org</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/180000">180000</a></dd>
<dt>Author</dt> <dd>rniwa@webkit.org</dd>
<dt>Date</dt> <dd>2015-02-12 10:50:53 -0800 (Thu, 12 Feb 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>Perf dashboard should show the results of A/B testing
https://bugs.webkit.org/show_bug.cgi?id=141500

Reviewed by Chris Dumez.

Added the support for fetching test_runs for a specific test group in /api/runs/, and used it in the
analysis task page to fetch results for each test group.

Merged App.createChartData into App.Manifest.fetchRunsWithPlatformAndMetric so that App.BuildRequest
can use the formatter.

* public/api/runs.php:
(fetch_runs_for_config_and_test_group): Added.
(fetch_runs_for_config): Just return the fetched rows since main will format them with RunsGenerator.
(main): Use fetch_runs_for_config_and_test_group to fetch rows when a test group id is specified. Also
use RunsGenerator to format results.
(RunsGenerator): Added.
(RunsGenerator::__construct): Added.
(RunsGenerator::add_runs): Added.
(RunsGenerator::format_run): Moved.
(RunsGenerator::parse_revisions_array): Moved.

* public/v2/analysis.js:
(App.TestGroup): Fixed a typo. The property on a test group that refers to an analysis task is &quot;task&quot;.
(App.TestGroup._fetchChartData): Added. Fetches all A/B testing results for this group.
(App.BuildRequest.configLetter): Renamed from config since this returns a letter that identifies the
configuration associated with this build request such as &quot;A&quot; and &quot;B&quot;.
(App.BuildRequest.statusLabel): Added the missing label for failed build requests.
(App.BuildRequest.url): Added. Returns the URL associated with this build request.
(App.BuildRequest._meanFetched): Added. Retrieve the mean and the build number for this request via
_fetchChartData.

* public/v2/app.js:
(App.Pane._fetch): Set chartData directly here.
(App.Pane._updateMovingAverageAndEnvelope): Renamed from _computeChartData. No longer sets chartData
now that it's done in App.Pane._fetch.
(App.AnalysisTaskController._fetchedRuns): Updated per createChartData merge.

* public/v2/data.js:
(Measurement.prototype.buildId): Added.
(TimeSeries.prototype.findPointByBuild): Added.

* public/v2/index.html: Fixed a bug that build status URL was broken. We can't use link-to helper since
url is not an Ember routed path.

* public/v2/manifest.js:
(App.Manifest.fetchRunsWithPlatformAndMetric): Takes testGroupId as the third argument. Merged
App.createChartData here so that App.BuildRequest can use the formatter</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkWebsitesperfwebkitorgChangeLog">trunk/Websites/perf.webkit.org/ChangeLog</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicapirunsphp">trunk/Websites/perf.webkit.org/public/api/runs.php</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv2analysisjs">trunk/Websites/perf.webkit.org/public/v2/analysis.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv2appjs">trunk/Websites/perf.webkit.org/public/v2/app.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv2datajs">trunk/Websites/perf.webkit.org/public/v2/data.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv2indexhtml">trunk/Websites/perf.webkit.org/public/v2/index.html</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv2manifestjs">trunk/Websites/perf.webkit.org/public/v2/manifest.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkWebsitesperfwebkitorgChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/ChangeLog (179999 => 180000)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/ChangeLog        2015-02-12 18:42:21 UTC (rev 179999)
+++ trunk/Websites/perf.webkit.org/ChangeLog        2015-02-12 18:50:53 UTC (rev 180000)
</span><span class="lines">@@ -1,5 +1,56 @@
</span><span class="cx"> 2015-02-12  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
</span><span class="cx"> 
</span><ins>+        Perf dashboard should show the results of A/B testing
+        https://bugs.webkit.org/show_bug.cgi?id=141500
+
+        Reviewed by Chris Dumez.
+
+        Added the support for fetching test_runs for a specific test group in /api/runs/, and used it in the
+        analysis task page to fetch results for each test group.
+
+        Merged App.createChartData into App.Manifest.fetchRunsWithPlatformAndMetric so that App.BuildRequest
+        can use the formatter.
+
+        * public/api/runs.php:
+        (fetch_runs_for_config_and_test_group): Added.
+        (fetch_runs_for_config): Just return the fetched rows since main will format them with RunsGenerator.
+        (main): Use fetch_runs_for_config_and_test_group to fetch rows when a test group id is specified. Also
+        use RunsGenerator to format results.
+        (RunsGenerator): Added.
+        (RunsGenerator::__construct): Added.
+        (RunsGenerator::add_runs): Added.
+        (RunsGenerator::format_run): Moved.
+        (RunsGenerator::parse_revisions_array): Moved.
+
+        * public/v2/analysis.js:
+        (App.TestGroup): Fixed a typo. The property on a test group that refers to an analysis task is &quot;task&quot;.
+        (App.TestGroup._fetchChartData): Added. Fetches all A/B testing results for this group.
+        (App.BuildRequest.configLetter): Renamed from config since this returns a letter that identifies the
+        configuration associated with this build request such as &quot;A&quot; and &quot;B&quot;.
+        (App.BuildRequest.statusLabel): Added the missing label for failed build requests.
+        (App.BuildRequest.url): Added. Returns the URL associated with this build request.
+        (App.BuildRequest._meanFetched): Added. Retrieve the mean and the build number for this request via
+        _fetchChartData.
+
+        * public/v2/app.js:
+        (App.Pane._fetch): Set chartData directly here.
+        (App.Pane._updateMovingAverageAndEnvelope): Renamed from _computeChartData. No longer sets chartData
+        now that it's done in App.Pane._fetch.
+        (App.AnalysisTaskController._fetchedRuns): Updated per createChartData merge.
+
+        * public/v2/data.js:
+        (Measurement.prototype.buildId): Added.
+        (TimeSeries.prototype.findPointByBuild): Added.
+
+        * public/v2/index.html: Fixed a bug that build status URL was broken. We can't use link-to helper since
+        url is not an Ember routed path.
+
+        * public/v2/manifest.js:
+        (App.Manifest.fetchRunsWithPlatformAndMetric): Takes testGroupId as the third argument. Merged
+        App.createChartData here so that App.BuildRequest can use the formatter
+
+2015-02-12  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
+
</ins><span class="cx">         v2 UI should adjust the number of ticks on dashboards based on screen size
</span><span class="cx">         https://bugs.webkit.org/show_bug.cgi?id=141502
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicapirunsphp"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/api/runs.php (179999 => 180000)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/api/runs.php        2015-02-12 18:42:21 UTC (rev 179999)
+++ trunk/Websites/perf.webkit.org/public/api/runs.php        2015-02-12 18:50:53 UTC (rev 180000)
</span><span class="lines">@@ -2,52 +2,27 @@
</span><span class="cx"> 
</span><span class="cx"> require('../include/json-header.php');
</span><span class="cx"> 
</span><ins>+function fetch_runs_for_config_and_test_group($db, $config, $test_group_id) {
+    return $db-&gt;query_and_fetch_all('
+        SELECT test_runs.*, builds.*, array_agg((commit_repository, commit_revision, commit_time)) AS revisions
+            FROM builds
+                LEFT OUTER JOIN build_commits ON commit_build = build_id
+                LEFT OUTER JOIN commits ON build_commit = commit_id,
+                test_runs, build_requests, analysis_test_groups
+            WHERE run_build = build_id AND run_config = $1 AND request_build = build_id AND request_group = $2
+            GROUP BY build_id, run_id', array($config['config_id'], $test_group_id));
+}
+
</ins><span class="cx"> function fetch_runs_for_config($db, $config) {
</span><del>-    $raw_runs = $db-&gt;query_and_fetch_all('
</del><ins>+    return $db-&gt;query_and_fetch_all('
</ins><span class="cx">         SELECT test_runs.*, builds.*, array_agg((commit_repository, commit_revision, commit_time)) AS revisions
</span><span class="cx">             FROM builds
</span><span class="cx">                 LEFT OUTER JOIN build_commits ON commit_build = build_id
</span><span class="cx">                 LEFT OUTER JOIN commits ON build_commit = commit_id, test_runs
</span><span class="cx">             WHERE run_build = build_id AND run_config = $1 AND NOT EXISTS (SELECT * FROM build_requests WHERE request_build = build_id)
</span><span class="cx">             GROUP BY build_id, run_id', array($config['config_id']));
</span><del>-
-    $formatted_runs = array();
-    if (!$raw_runs)
-        return $formatted_runs;
-
-    foreach ($raw_runs as $run)
-        array_push($formatted_runs, format_run($run));
-
-    return $formatted_runs;
</del><span class="cx"> }
</span><span class="cx"> 
</span><del>-function parse_revisions_array($postgres_array) {
-    // e.g. {&quot;(WebKit,131456,\&quot;2012-10-16 14:53:00\&quot;)&quot;,&quot;(Chromium,162004,)&quot;}
-    $outer_array = json_decode('[' . trim($postgres_array, '{}') . ']');
-    $revisions = array();
-    foreach ($outer_array as $item) {
-        $name_and_revision = explode(',', trim($item, '()'));
-        if (!$name_and_revision[0])
-            continue;
-        $time = strtotime(trim($name_and_revision[2], '&quot;')) * 1000;
-        $revisions[trim($name_and_revision[0], '&quot;')] = array(trim($name_and_revision[1], '&quot;'), $time);
-    }
-    return $revisions;
-}
-
-function format_run($run) {
-    return array(
-        'id' =&gt; intval($run['run_id']),
-        'mean' =&gt; floatval($run['run_mean_cache']),
-        'iterationCount' =&gt; intval($run['run_iteration_count_cache']),
-        'sum' =&gt; floatval($run['run_sum_cache']),
-        'squareSum' =&gt; floatval($run['run_square_sum_cache']),
-        'revisions' =&gt; parse_revisions_array($run['revisions']),
-        'buildTime' =&gt; strtotime($run['build_time']) * 1000,
-        'buildNumber' =&gt; intval($run['build_number']),
-        'builder' =&gt; $run['build_builder']);
-}
-
</del><span class="cx"> function main($path) {
</span><span class="cx">     if (count($path) != 1)
</span><span class="cx">         exit_with_error('InvalidRequest');
</span><span class="lines">@@ -60,11 +35,6 @@
</span><span class="cx">     if (!$db-&gt;connect())
</span><span class="cx">         exit_with_error('DatabaseConnectionFailure');
</span><span class="cx"> 
</span><del>-    // FIXME: We should support revalication as well as caching results in the server side.
-    $maxage = config('jsonCacheMaxAge');
-    header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $maxage) . ' GMT');
-    header(&quot;Cache-Control: maxage=$maxage&quot;);
-
</del><span class="cx">     $platform_id = intval($parts[0]);
</span><span class="cx">     $metric_id = intval($parts[1]);
</span><span class="cx">     $config_rows = $db-&gt;query_and_fetch_all('SELECT config_id, config_type, config_platform, config_metric
</span><span class="lines">@@ -72,15 +42,75 @@
</span><span class="cx">     if (!$config_rows)
</span><span class="cx">         exit_with_error('ConfigurationNotFound');
</span><span class="cx"> 
</span><del>-    $results = array();
</del><ins>+    $test_group_id = array_get($_GET, 'testGroup');
+    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.
+        $maxage = config('jsonCacheMaxAge');
+        header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $maxage) . ' GMT');
+        header(&quot;Cache-Control: maxage=$maxage&quot;);
+    }
+
+    $generator = new RunsGenerator();
+
</ins><span class="cx">     foreach ($config_rows as $config) {
</span><del>-        if ($runs = fetch_runs_for_config($db, $config))
-            $results[$config['config_type']] = $runs;
</del><ins>+        if ($test_group_id)
+            $raw_runs = fetch_runs_for_config_and_test_group($db, $config, $test_group_id);
+        else
+            $raw_runs = fetch_runs_for_config($db, $config);
+        $generator-&gt;add_runs($config['config_type'], $raw_runs);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    exit_with_success($results);
</del><ins>+    exit_with_success($generator-&gt;results());
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+class RunsGenerator {
+    function __construct() {
+        $this-&gt;results = array();
+    }
+
+    function &amp;results() { return $this-&gt;results; }
+
+    function add_runs($name, $raw_runs) {
+        $formatted_runs = array();
+        if ($raw_runs) {
+            foreach ($raw_runs as $run)
+                array_push($formatted_runs, self::format_run($run));
+        }
+        $this-&gt;results[$name] = $formatted_runs;
+        return $formatted_runs;
+    }
+
+    private static function format_run($run) {
+        return array(
+            'id' =&gt; intval($run['run_id']),
+            'mean' =&gt; floatval($run['run_mean_cache']),
+            'iterationCount' =&gt; intval($run['run_iteration_count_cache']),
+            'sum' =&gt; floatval($run['run_sum_cache']),
+            'squareSum' =&gt; floatval($run['run_square_sum_cache']),
+            'revisions' =&gt; self::parse_revisions_array($run['revisions']),
+            'build' =&gt; $run['build_id'],
+            'buildTime' =&gt; strtotime($run['build_time']) * 1000,
+            'buildNumber' =&gt; intval($run['build_number']),
+            'builder' =&gt; $run['build_builder']);
+    }
+
+    private static function parse_revisions_array($postgres_array) {
+        // e.g. {&quot;(WebKit,131456,\&quot;2012-10-16 14:53:00\&quot;)&quot;,&quot;(Chromium,162004,)&quot;}
+        $outer_array = json_decode('[' . trim($postgres_array, '{}') . ']');
+        $revisions = array();
+        foreach ($outer_array as $item) {
+            $name_and_revision = explode(',', trim($item, '()'));
+            if (!$name_and_revision[0])
+                continue;
+            $time = strtotime(trim($name_and_revision[2], '&quot;')) * 1000;
+            $revisions[trim($name_and_revision[0], '&quot;')] = array(trim($name_and_revision[1], '&quot;'), $time);
+        }
+        return $revisions;
+    }
+}
+
</ins><span class="cx"> main(array_key_exists('PATH_INFO', $_SERVER) ? explode('/', trim($_SERVER['PATH_INFO'], '/')) : array());
</span><span class="cx"> 
</span><span class="cx"> ?&gt;
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv2analysisjs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v2/analysis.js (179999 => 180000)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v2/analysis.js        2015-02-12 18:42:21 UTC (rev 179999)
+++ trunk/Websites/perf.webkit.org/public/v2/analysis.js        2015-02-12 18:50:53 UTC (rev 180000)
</span><span class="lines">@@ -66,7 +66,7 @@
</span><span class="cx"> });
</span><span class="cx"> 
</span><span class="cx"> App.TestGroup = App.NameLabelModel.extend({
</span><del>-    analysisTask: DS.belongsTo('analysisTask'),
</del><ins>+    task: DS.belongsTo('analysisTask'),
</ins><span class="cx">     author: DS.attr('string'),
</span><span class="cx">     createdAt: DS.attr('date'),
</span><span class="cx">     buildRequests: DS.hasMany('buildRequests'),
</span><span class="lines">@@ -80,6 +80,21 @@
</span><span class="cx">         });
</span><span class="cx">         return rootSetIds;
</span><span class="cx">     }.property('buildRequests'),
</span><ins>+    _fetchChartData: function ()
+    {
+        var task = this.get('task');
+        if (!task)
+            return null;
+        var self = this;
+        return App.Manifest.fetchRunsWithPlatformAndMetric(this.store,
+            task.get('platform').get('id'), task.get('metric').get('id'), this.get('id')).then(
+            function (result) { self.set('chartData', result.data); },
+            function (error) {
+                // FIXME: Somehow this never gets called.
+                alert('Failed to fetch the results:' + error);
+                return null;
+            });
+    }.observes('task', 'task.platform', 'task.metric').on('init'),
</ins><span class="cx"> });
</span><span class="cx"> 
</span><span class="cx"> App.TestGroup.create = function (analysisTask, name, rootSets, repetitionCount)
</span><span class="lines">@@ -130,7 +145,7 @@
</span><span class="cx">         return this.get('order') + 1;
</span><span class="cx">     }.property('order'),
</span><span class="cx">     rootSet: DS.attr('number'),
</span><del>-    config: function ()
</del><ins>+    configLetter: function ()
</ins><span class="cx">     {
</span><span class="cx">         var rootSets = this.get('testGroup').get('rootSets');
</span><span class="cx">         var index = rootSets.indexOf(this.get('rootSet'));
</span><span class="lines">@@ -146,9 +161,27 @@
</span><span class="cx">             return 'Scheduled';
</span><span class="cx">         case 'running':
</span><span class="cx">             return 'Running';
</span><ins>+        case 'failed':
+            return 'Failed';
</ins><span class="cx">         case 'completed':
</span><span class="cx">             return 'Finished';
</span><span class="cx">         }
</span><span class="cx">     }.property('status'),
</span><ins>+    url: DS.attr('string'),
</ins><span class="cx">     build: DS.attr('number'),
</span><ins>+    _fetchMean: function ()
+    {
+        var testGroup = this.get('testGroup');
+        if (!testGroup)
+            return;
+        var chartData = testGroup.get('chartData');
+        if (!chartData)
+            return;
+
+        var point = chartData.current.findPointByBuild(this.get('build'));
+        if (!point)
+            return;
+        this.set('mean', chartData.formatter(point.value) + (chartData.unit ? ' ' + chartData.unit : ''));
+        this.set('buildNumber', point.measurement.buildNumber());
+    }.observes('build', 'testGroup', 'testGroup.chartData').on('init'),
</ins><span class="cx"> });
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv2appjs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v2/app.js (179999 => 180000)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v2/app.js        2015-02-12 18:42:21 UTC (rev 179999)
+++ trunk/Websites/perf.webkit.org/public/v2/app.js        2015-02-12 18:50:53 UTC (rev 180000)
</span><span class="lines">@@ -352,8 +352,8 @@
</span><span class="cx">             App.Manifest.fetchRunsWithPlatformAndMetric(this.get('store'), platformId, metricId).then(function (result) {
</span><span class="cx">                 self.set('platform', result.platform);
</span><span class="cx">                 self.set('metric', result.metric);
</span><del>-                self.set('fetchedData', result);
-                self._computeChartData();
</del><ins>+                self.set('chartData', result.data);
+                self._updateMovingAverageAndEnvelope();
</ins><span class="cx">             }, function (result) {
</span><span class="cx">                 if (!result || typeof(result) === &quot;string&quot;)
</span><span class="cx">                     self.set('failure', 'Failed to fetch the JSON with an error: ' + result);
</span><span class="lines">@@ -471,13 +471,12 @@
</span><span class="cx"> 
</span><span class="cx">         return chosenStrategy;
</span><span class="cx">     },
</span><del>-    _computeChartData: function ()
</del><ins>+    _updateMovingAverageAndEnvelope: function ()
</ins><span class="cx">     {
</span><del>-        if (!this.get('fetchedData'))
</del><ins>+        var chartData = this.get('chartData');
+        if (!chartData)
</ins><span class="cx">             return;
</span><span class="cx"> 
</span><del>-        var chartData = App.createChartData(this.get('fetchedData'));
-
</del><span class="cx">         var movingAverageStrategy = this.get('chosenMovingAverageStrategy');
</span><span class="cx">         this._updateStrategyConfigIfNeeded(movingAverageStrategy, 'movingAverageConfig');
</span><span class="cx"> 
</span><span class="lines">@@ -485,8 +484,6 @@
</span><span class="cx">         this._updateStrategyConfigIfNeeded(envelopingStrategy, 'envelopingConfig');
</span><span class="cx"> 
</span><span class="cx">         chartData.movingAverage = this._computeMovingAverageAndOutliers(chartData, movingAverageStrategy, envelopingStrategy);
</span><del>-
-        this.set('chartData', chartData);
</del><span class="cx">     }.observes('chosenMovingAverageStrategy', 'chosenMovingAverageStrategy.parameterList.@each.value',
</span><span class="cx">         'chosenEnvelopingStrategy', 'chosenEnvelopingStrategy.parameterList.@each.value'),
</span><span class="cx">     _computeMovingAverageAndOutliers: function (chartData, movingAverageStrategy, envelopingStrategy)
</span><span class="lines">@@ -543,20 +540,6 @@
</span><span class="cx">     },
</span><span class="cx"> });
</span><span class="cx"> 
</span><del>-App.createChartData = function (data)
-{
-    var runs = data.runs;
-    return {
-        current: runs.current.timeSeriesByCommitTime(),
-        baseline: runs.baseline ? runs.baseline.timeSeriesByCommitTime() : null,
-        target: runs.target ? runs.target.timeSeriesByCommitTime() : null,
-        unit: data.unit,
-        formatter: data.useSI ? d3.format('.4s') : d3.format('.4g'),
-        deltaFormatter: data.useSI ? d3.format('+.2s') : d3.format('+.2g'),
-        smallerIsBetter: data.smallerIsBetter,
-    };
-}
-
</del><span class="cx"> App.encodePrettifiedJSON = function (plain)
</span><span class="cx"> {
</span><span class="cx">     function numberIfPossible(string) {
</span><span class="lines">@@ -1027,11 +1010,10 @@
</span><span class="cx">             });
</span><span class="cx">         }));
</span><span class="cx">     },
</span><del>-    _fetchedRuns: function (data)
</del><ins>+    _fetchedRuns: function (result)
</ins><span class="cx">     {
</span><del>-        var runs = data.runs;
-
-        var currentTimeSeries = runs.current.timeSeriesByCommitTime();
</del><ins>+        var chartData = result.data;
+        var currentTimeSeries = chartData.current;
</ins><span class="cx">         if (!currentTimeSeries)
</span><span class="cx">             return; // FIXME: Report an error.
</span><span class="cx"> 
</span><span class="lines">@@ -1044,13 +1026,12 @@
</span><span class="cx">         highlightedItems[start.measurement.id()] = true;
</span><span class="cx">         highlightedItems[end.measurement.id()] = true;
</span><span class="cx"> 
</span><del>-        var chartData = App.createChartData(data);
</del><span class="cx">         var formatedPoints = currentTimeSeries.seriesBetweenPoints(start, end).map(function (point, index) {
</span><span class="cx">             return {
</span><span class="cx">                 id: point.measurement.id(),
</span><span class="cx">                 measurement: point.measurement,
</span><span class="cx">                 label: 'Point ' + (index + 1),
</span><del>-                value: chartData.formatter(point.value) + (data.unit ? ' ' + data.unit : ''),
</del><ins>+                value: chartData.formatter(point.value) + (chartData.unit ? ' ' + chartData.unit : ''),
</ins><span class="cx">             };
</span><span class="cx">         });
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv2datajs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v2/data.js (179999 => 180000)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v2/data.js        2015-02-12 18:42:21 UTC (rev 179999)
+++ trunk/Websites/perf.webkit.org/public/v2/data.js        2015-02-12 18:50:53 UTC (rev 180000)
</span><span class="lines">@@ -242,6 +242,11 @@
</span><span class="cx">     return this._latestCommitTime || this._buildTime;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+Measurement.prototype.buildId = function()
+{
+    return this._raw['build'];
+}
+
</ins><span class="cx"> Measurement.prototype.buildNumber = function ()
</span><span class="cx"> {
</span><span class="cx">     return this._raw['buildNumber'];
</span><span class="lines">@@ -315,10 +320,13 @@
</span><span class="cx"> 
</span><span class="cx"> // FIXME: We need to devise a way to fetch runs in multiple chunks so that
</span><span class="cx"> // we don't have to fetch the entire time series to just show the last 3 days.
</span><del>-RunsData.fetchRuns = function (platformId, metricId)
</del><ins>+RunsData.fetchRuns = function (platformId, metricId, testGroupId)
</ins><span class="cx"> {
</span><span class="cx">     var filename = platformId + '-' + metricId + '.json';
</span><span class="cx"> 
</span><ins>+    if (testGroupId)
+        filename += '?testGroup=' + testGroupId;
+
</ins><span class="cx">     return new Ember.RSVP.Promise(function (resolve, reject) {
</span><span class="cx">         $.getJSON('../api/runs/' + filename, function (data) {
</span><span class="cx">             if (data.status != 'OK') {
</span><span class="lines">@@ -355,6 +363,11 @@
</span><span class="cx">     this._max = max;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+TimeSeries.prototype.findPointByBuild = function (buildId)
+{
+    return this._series.find(function (point) { return point.measurement.buildId() == buildId; })
+}
+
</ins><span class="cx"> TimeSeries.prototype.findPointByMeasurementId = function (measurementId)
</span><span class="cx"> {
</span><span class="cx">     return this._series.find(function (point) { return point.measurement.id() == measurementId; });
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv2indexhtml"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v2/index.html (179999 => 180000)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v2/index.html        2015-02-12 18:42:21 UTC (rev 179999)
+++ trunk/Websites/perf.webkit.org/public/v2/index.html        2015-02-12 18:50:53 UTC (rev 180000)
</span><span class="lines">@@ -580,9 +580,9 @@
</span><span class="cx">                             {{#each buildRequests}}
</span><span class="cx">                                 &lt;tr&gt;
</span><span class="cx">                                     &lt;td&gt;{{orderLabel}}&lt;/td&gt;
</span><del>-                                    &lt;td&gt;{{config}}&lt;/td&gt;
-                                    &lt;td&gt;{{#if url}}{{#link-to url}}{{statusLabel}}{{/link-to}}{{else}}{{statusLabel}}{{/if}}&lt;/td&gt;
-                                    &lt;td&gt;{{build}}&lt;/td&gt;
</del><ins>+                                    &lt;td&gt;{{configLetter}}&lt;/td&gt;
+                                    &lt;td&gt;&lt;a {{bind-attr href=url}}&gt;{{statusLabel}}&lt;/a&gt;&lt;/td&gt;
+                                    &lt;td&gt;{{buildNumber}}&lt;/td&gt;
</ins><span class="cx">                                     &lt;td&gt;{{mean}}&lt;/td&gt;
</span><span class="cx">                                 &lt;/tr&gt;
</span><span class="cx">                             {{/each}}
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv2manifestjs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v2/manifest.js (179999 => 180000)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v2/manifest.js        2015-02-12 18:42:21 UTC (rev 179999)
+++ trunk/Websites/perf.webkit.org/public/v2/manifest.js        2015-02-12 18:50:53 UTC (rev 180000)
</span><span class="lines">@@ -279,10 +279,10 @@
</span><span class="cx">         dashboards.forEach(function (dashboard) { self._dashboardByName[dashboard.get('name')] = dashboard; });
</span><span class="cx">         this._defaultDashboardName = dashboards.length ? dashboards[0].get('name') : null;
</span><span class="cx">     },
</span><del>-    fetchRunsWithPlatformAndMetric: function (store, platformId, metricId)
</del><ins>+    fetchRunsWithPlatformAndMetric: function (store, platformId, metricId, testGroupId)
</ins><span class="cx">     {
</span><span class="cx">         return Ember.RSVP.all([
</span><del>-            RunsData.fetchRuns(platformId, metricId),
</del><ins>+            RunsData.fetchRuns(platformId, metricId, testGroupId),
</ins><span class="cx">             this.fetch(store),
</span><span class="cx">         ]).then(function (values) {
</span><span class="cx">             var runs = values[0];
</span><span class="lines">@@ -301,7 +301,20 @@
</span><span class="cx">             }[suffix];
</span><span class="cx">             var smallerIsBetter = unit != 'fps' &amp;&amp; unit != '/s'; // Assume smaller is better for unit-less metrics.
</span><span class="cx"> 
</span><del>-            return {platform: platform, metric: metric, runs: runs, unit: unit, useSI: unit == 'bytes', smallerIsBetter: smallerIsBetter};
</del><ins>+            var useSI = unit == 'bytes';
+            return {
+                platform: platform,
+                metric: metric,
+                data: {
+                    current: runs.current.timeSeriesByCommitTime(),
+                    baseline: runs.baseline ? runs.baseline.timeSeriesByCommitTime() : null,
+                    target: runs.target ? runs.target.timeSeriesByCommitTime() : null,
+                    unit: unit,
+                    formatter: useSI ? d3.format('.4s') : d3.format('.4g'),
+                    deltaFormatter: useSI ? d3.format('+.2s') : d3.format('+.2g'),
+                    smallerIsBetter: smallerIsBetter,
+                }
+            };
</ins><span class="cx">         });
</span><span class="cx">     },
</span><span class="cx"> }).create();
</span></span></pre>
</div>
</div>

</body>
</html>