Changeset 179763 in webkit
- Timestamp:
- Feb 6, 2015 2:43:08 PM (9 years ago)
- Location:
- trunk/Websites/perf.webkit.org
- Files:
-
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Websites/perf.webkit.org/ChangeLog
r179731 r179763 1 2015-02-06 Ryosuke Niwa <rniwa@webkit.org> 2 3 New perf dashboard should have multiple dashboard pages 4 https://bugs.webkit.org/show_bug.cgi?id=141339 5 6 Reviewed by Chris Dumez. 7 8 Added the support for multiple dashboard pages. Also added the status of the latest data point. 9 e.g. "5% better than target" 10 11 * public/v2/app.css: Tweaked the styles to work around the fact Ember.js creates empty script elements. 12 Also hid the border lines around charts on the dashboard page for a cleaner look. 13 14 * public/v2/app.js: 15 (App.IndexRoute): Added. Navigate to /dashboard/<defaultDashboardName> once the manifest.json is loaded. 16 (App.IndexRoute.beforeModel): Added. 17 (App.DashboardRoute): Added. 18 (App.DashboardRoute.model): Added. Return the dashboard specified by the name. 19 (App.CustomDashboardRoute): Added. This route is used for a customized dashboard specified by "grid". 20 (App.CustomDashboardRoute.model): Create a dashboard model from "grid" query parameter. 21 (App.CustomDashboardRoute.renderTemplate): Use the dashboard template. 22 (App.DashboardController): Renamed from App.IndexController. 23 (App.DashboardController.modelChanged): Renamed from gridChanged. Removed the code to deal with "grid" 24 and "defaultDashboard" as these are taken care of by newly added routers. 25 (App.DashboardController.computeGrid): Renamed from updateGrid. No longer updates "grid" since this is 26 now done in actions.toggleEditMode. 27 (App.DashboardController.actions.toggleEditMode): Navigate to CustomDashboardRoute when start editing 28 an existing dashboard. 29 30 (App.Pane.computeStatus): Moved from App.PaneController so that to be called in App.Pane.latestStatus. 31 Also moved the code to compute the delta with respect to the previous data point from _updateDetails. 32 (App.Pane._relativeDifferentToLaterPointInTimeSeries): Ditto. 33 (App.Pane.latestStatus): Added. Used by the dashboard template to show the status of the latest result. 34 35 (App.createChartData): Added deltaFormatter to show less significant digits for differences. 36 37 (App.PaneController._updateDetails): Updated per changes to computeStatus. 38 39 * public/v2/chart-pane.css: Added style rules for the status labels on the dashboard. 40 41 * public/v2/data.js: 42 (TimeSeries.prototype.lastPoint): Added. 43 44 * public/v2/index.html: Prefetch manifest.json as soon as possible, show the latest data points' status 45 on the dashboard, and enumerate all predefined dashboards. 46 47 * public/v2/interactive-chart.js: 48 (App.InteractiveChartComponent._relayoutDataAndAxes): Slightly adjust the offset at which we show unit 49 for the dashboard page. 50 51 * public/v2/manifest.js: 52 (App.Dashboard): Inherit from App.NameLabelModel now that each predefined dashboard has a name. 53 (App.MetricSerializer.normalizePayload): Parse all predefined dashboards instead of a single dashboard. 54 IDs are generated for each dashboard for forward compatibility. 55 (App.Manifest): 56 (App.Manifest.dashboardByName): Added. 57 (App.Manifest.defaultDashboardName): Added. 58 (App.Manifest._fetchedManifest): Create dashboard model objects for all predefined ones. 59 1 60 2015-02-05 Ryosuke Niwa <rniwa@webkit.org> 2 61 -
trunk/Websites/perf.webkit.org/public/v2/app.css
r179661 r179763 202 202 line-height: 1rem; 203 203 } 204 #navigation li:not(:last- child) a {204 #navigation li:not(:last-of-type) a { 205 205 border-top-right-radius: 0; 206 206 border-bottom-right-radius: 0; 207 207 border-right: 0; 208 208 } 209 #navigation li:not(:first- child) a {209 #navigation li:not(:first-of-type) a { 210 210 border-top-left-radius: 0; 211 211 border-bottom-left-radius: 0; … … 398 398 399 399 table.dashboard tbody td .chart { 400 border: solid 1px #ddd;400 border: solid 0px #ddd; 401 401 border-radius: 0.5rem; 402 402 margin: 0.5rem 0.5rem; -
trunk/Websites/perf.webkit.org/public/v2/app.js
r179731 r179763 2 2 3 3 App.Router.map(function () { 4 this.resource('customDashboard', {path: 'dashboard/custom'}); 5 this.resource('dashboard', {path: 'dashboard/:name'}); 4 6 this.resource('charts', {path: 'charts'}); 5 7 this.resource('analysis', {path: 'analysis'}); … … 55 57 }); 56 58 57 App.IndexController = Ember.Controller.extend({ 59 App.IndexRoute = Ember.Route.extend({ 60 beforeModel: function () 61 { 62 var self = this; 63 App.Manifest.fetch(this.store).then(function () { 64 self.transitionTo('dashboard', App.Manifest.defaultDashboardName()); 65 }); 66 }, 67 }); 68 69 App.DashboardRoute = Ember.Route.extend({ 70 model: function (param) 71 { 72 return App.Manifest.fetch(this.store).then(function () { 73 return App.Manifest.dashboardByName(param.name); 74 }); 75 }, 76 }); 77 78 App.CustomDashboardRoute = Ember.Route.extend({ 79 controllerName: 'dashboard', 80 model: function (param) 81 { 82 return this.store.createRecord('dashboard', {serialized: param.grid}); 83 }, 84 renderTemplate: function() 85 { 86 this.render('dashboard'); 87 } 88 }); 89 90 App.DashboardController = Ember.Controller.extend({ 58 91 queryParams: ['grid', 'numberOfDays'], 59 _previousGrid: {},60 92 headerColumns: [], 61 93 rows: [], … … 63 95 editMode: false, 64 96 65 gridChanged: function () 66 { 67 var grid = this.get('grid'); 68 if (grid === this._previousGrid) 69 return; 70 71 var dashboard = null; 72 if (grid) { 73 dashboard = this.store.createRecord('dashboard', {serialized: grid}); 74 if (!dashboard.get('headerColumns').length) 75 dashboard = null; 76 } 77 if (!dashboard) 78 dashboard = App.Manifest.get('defaultDashboard'); 97 modelChanged: function () 98 { 99 var dashboard = this.get('model'); 79 100 if (!dashboard) 80 101 return; … … 96 117 97 118 this.set('emptyRow', new Array(columnCount)); 98 }.observes(' grid', 'App.Manifest.defaultDashboard').on('init'),99 100 updateGrid: function()119 }.observes('model').on('init'), 120 121 computeGrid: function() 101 122 { 102 123 var headers = this.get('headerColumns').map(function (header) { return header.label; }); … … 107 128 })); 108 129 })); 109 this._previousGrid = JSON.stringify(table); 110 this.set('grid', this._previousGrid); 130 return JSON.stringify(table); 111 131 }, 112 132 … … 174 194 { 175 195 this.toggleProperty('editMode'); 176 if (!this.get('editMode')) 177 this.updateGrid(); 196 if (this.get('editMode')) 197 this.transitionToRoute('dashboard', 'custom', {name: null, queryParams: {grid: this.computeGrid()}}); 198 else 199 this.set('grid', this.computeGrid()); 178 200 }, 179 201 }, … … 363 385 return !!id.match(/^[A-Za-z0-9_]+$/); 364 386 return false; 365 } 387 }, 388 computeStatus: function (currentPoint, previousPoint) 389 { 390 var chartData = this.get('chartData'); 391 var diffFromBaseline = this._relativeDifferentToLaterPointInTimeSeries(currentPoint, chartData.baseline); 392 var diffFromTarget = this._relativeDifferentToLaterPointInTimeSeries(currentPoint, chartData.target); 393 394 var label = ''; 395 var className = ''; 396 var formatter = d3.format('.3p'); 397 398 var smallerIsBetter = chartData.smallerIsBetter; 399 if (diffFromBaseline !== undefined && diffFromBaseline > 0 == smallerIsBetter) { 400 label = formatter(Math.abs(diffFromBaseline)) + ' ' + (smallerIsBetter ? 'above' : 'below') + ' baseline'; 401 className = 'worse'; 402 } else if (diffFromTarget !== undefined && diffFromTarget < 0 == smallerIsBetter) { 403 label = formatter(Math.abs(diffFromTarget)) + ' ' + (smallerIsBetter ? 'below' : 'above') + ' target'; 404 className = 'better'; 405 } else if (diffFromTarget !== undefined) 406 label = formatter(Math.abs(diffFromTarget)) + ' until target'; 407 408 var valueDelta = previousPoint ? chartData.deltaFormatter(currentPoint.value - previousPoint.value) : null; 409 if (valueDelta && valueDelta > 0) 410 valueDelta = '+' + valueDelta; 411 412 return {className: className, label: label, currentValue: chartData.formatter(currentPoint.value), valueDelta: valueDelta}; 413 }, 414 _relativeDifferentToLaterPointInTimeSeries: function (currentPoint, timeSeries) 415 { 416 if (!currentPoint || !timeSeries) 417 return undefined; 418 419 var referencePoint = timeSeries.findPointAfterTime(currentPoint.time); 420 if (!referencePoint) 421 return undefined; 422 423 return (currentPoint.value - referencePoint.value) / referencePoint.value; 424 }, 425 latestStatus: function () 426 { 427 var chartData = this.get('chartData'); 428 if (!chartData || !chartData.current) 429 return null; 430 431 var lastPoint = chartData.current.lastPoint(); 432 if (!lastPoint) 433 return null; 434 435 return this.computeStatus(lastPoint, chartData.current.previousPoint(lastPoint)); 436 }.property('chartData'), 366 437 }); 367 438 … … 375 446 unit: data.unit, 376 447 formatter: data.useSI ? d3.format('.4s') : d3.format('.4g'), 448 deltaFormatter: data.useSI ? d3.format('.2s') : d3.format('.2g'), 377 449 smallerIsBetter: data.smallerIsBetter, 378 450 }; … … 731 803 732 804 var currentMeasurement; 733 var oldMeasurement;805 var previousPoint; 734 806 if (currentPoint) { 735 807 currentMeasurement = currentPoint.measurement; 736 var previousPoint = currentPoint.series.previousPoint(currentPoint); 737 oldMeasurement = previousPoint ? previousPoint.measurement : null; 808 previousPoint = currentPoint.series.previousPoint(currentPoint); 738 809 } else { 739 810 currentMeasurement = selectedPoints[selectedPoints.length - 1].measurement; 740 oldMeasurement = selectedPoints[0].measurement;811 previousPoint = selectedPoints[0]; 741 812 } 813 var oldMeasurement = previousPoint ? previousPoint.measurement : null; 742 814 743 815 var formattedRevisions = currentMeasurement.formattedRevisions(oldMeasurement); … … 763 835 } 764 836 765 var chartData = this.get('chartData');766 var valueDiff = oldMeasurement ? chartData.formatter(currentMeasurement.mean() - oldMeasurement.mean()) : null;767 if (valueDiff && valueDiff > 0)768 valueDiff = '+' + valueDiff;769 770 837 this.set('details', Ember.Object.create({ 771 status: this._computeStatus(currentPoint), 772 currentValue: chartData.formatter(currentMeasurement.mean()), 773 valueDiff: valueDiff, 838 status: this.get('model').computeStatus(currentPoint, previousPoint), 774 839 buildNumber: buildNumber, 775 840 buildURL: buildURL, … … 784 849 this.set('cannotAnalyze', !this.get('newAnalysisTaskName') || !points || points.length < 2); 785 850 }.observes('newAnalysisTaskName'), 786 _computeStatus: function (currentPoint)787 {788 var chartData = this.get('chartData');789 790 var diffFromBaseline = this._relativeDifferentToLaterPointInTimeSeries(currentPoint, chartData.baseline);791 var diffFromTarget = this._relativeDifferentToLaterPointInTimeSeries(currentPoint, chartData.target);792 793 var label = '';794 var className = '';795 var formatter = d3.format('.3p');796 797 var smallerIsBetter = chartData.smallerIsBetter;798 if (diffFromBaseline !== undefined && diffFromBaseline > 0 == smallerIsBetter) {799 label = formatter(Math.abs(diffFromBaseline)) + ' ' + (smallerIsBetter ? 'above' : 'below') + ' baseline';800 className = 'worse';801 } else if (diffFromTarget !== undefined && diffFromTarget < 0 == smallerIsBetter) {802 label = formatter(Math.abs(diffFromTarget)) + ' ' + (smallerIsBetter ? 'below' : 'above') + ' target';803 className = 'better';804 } else if (diffFromTarget !== undefined)805 label = formatter(Math.abs(diffFromTarget)) + ' until target';806 807 return {className: className, label: label};808 },809 _relativeDifferentToLaterPointInTimeSeries: function (currentPoint, timeSeries)810 {811 if (!currentPoint || !timeSeries)812 return undefined;813 814 var referencePoint = timeSeries.findPointAfterTime(currentPoint.time);815 if (!referencePoint)816 return undefined;817 818 return (currentPoint.value - referencePoint.value) / referencePoint.value;819 }820 851 }); 821 852 -
trunk/Websites/perf.webkit.org/public/v2/chart-pane.css
r179729 r179763 321 321 stroke: #f66; 322 322 } 323 .chart-pane .status .worse { 323 .chart-pane .status .worse, 324 .dashboard-status .worse { 324 325 color: #c33; 325 326 } … … 328 329 stroke: #66f; 329 330 } 330 .chart-pane .status .better { 331 .chart-pane .status .better, 332 .dashboard-status .better { 331 333 color: #33c; 334 } 335 336 .dashboard-status .status-label { 337 margin-left: 1rem; 332 338 } 333 339 -
trunk/Websites/perf.webkit.org/public/v2/data.js
r179710 r179763 406 406 TimeSeries.prototype.series = function () { return this._series; } 407 407 408 TimeSeries.prototype.lastPoint = function () 409 { 410 if (!this._series || !this._series.length) 411 return null; 412 return this._series[this._series.length - 1]; 413 } 414 408 415 TimeSeries.prototype.previousPoint = function (point) 409 416 { -
trunk/Websites/perf.webkit.org/public/v2/index.html
r179731 r179763 4 4 <meta charset="utf-8"> 5 5 <title>WebKit Performance Monitor (Beta)</title> 6 7 <link rel="prefetch" href="../data/manifest.json"> 8 <script type="application/json" src="../data/manifest.json"></script> 9 10 <link rel="stylesheet" href="app.css"> 11 <link rel="stylesheet" href="chart-pane.css"> 12 13 <script src="js/jquery.min.js" defer></script> 6 14 <script src="js/jquery.min.js" defer></script> 7 15 <script src="js/handlebars.js" defer></script> … … 17 25 <script src="interactive-chart.js" defer></script> 18 26 <script src="commits-viewer.js" defer></script> 19 <link rel="stylesheet" href="app.css"> 20 <link rel="stylesheet" href="chart-pane.css"> 21 22 <script type="text/x-handlebars" data-template-name="index"> 27 28 <script type="text/x-handlebars" data-template-name="dashboard"> 23 29 <header id="header"> 24 30 {{partial "navbar"}} … … 79 85 {{else}} 80 86 {{#if chartData}} 87 <div class="dashboard-status"> 88 {{#if latestStatus}} 89 {{latestStatus.currentValue}} {{chartData.unit}} 90 {{#if latestStatus.label}} 91 <span {{bind-attr class=":status-label latestStatus.className"}}>{{latestStatus.label}}</span> 92 {{/if}} 93 {{/if}} 94 </div> 81 95 {{#link-to 'charts' (query-params paneList=paneList since=controller.since)}} 82 {{interactive-chart83 chartData=chartData84 domain=controller.sharedDomain85 enableSelection=false}}96 {{interactive-chart 97 chartData=chartData 98 domain=controller.sharedDomain 99 enableSelection=false}} 86 100 {{/link-to}} 87 101 {{else}} … … 259 273 <th>Current</th> 260 274 <td> 261 {{details. currentValue}} {{chartData.unit}}262 {{#if details. valueDiff}}263 ({{details. valueDiff}} {{chartData.unit}})275 {{details.status.currentValue}} {{chartData.unit}} 276 {{#if details.status.valueDelta}} 277 ({{details.status.valueDelta}} {{chartData.unit}}) 264 278 {{/if}} 265 279 {{#if details.status.label}} … … 379 393 <h1><a href="#">WebKit Perf Monitor</a></h1> 380 394 <ul> 381 {{#link-to 'index' tagName='li'}} 382 {{#link-to 'index'}}Dashboard{{/link-to}} 383 {{/link-to}} 395 {{#each App.Manifest.dashboards}} 396 {{#if name}} 397 {{#link-to 'dashboard' name tagName='li'}} 398 {{#link-to 'dashboard' name}}{{label}}{{/link-to}} 399 {{/link-to}} 400 {{/if}} 401 {{/each}} 384 402 {{#link-to 'charts' tagName='li'}} 385 403 {{#link-to 'charts'}}Charts{{/link-to}} -
trunk/Websites/perf.webkit.org/public/v2/interactive-chart.js
r179710 r179763 289 289 if (this._yAxisUnitContainer) 290 290 this._yAxisUnitContainer.remove(); 291 var x = - 3 * this._rem;291 var x = - 3.2 * this._rem; 292 292 var y = this._contentHeight / 2; 293 293 this._yAxisUnitContainer = this._yAxisLabels.append("text") -
trunk/Websites/perf.webkit.org/public/v2/manifest.js
r179710 r179763 93 93 }); 94 94 95 App.Dashboard = App. Model.extend({95 App.Dashboard = App.NameLabelModel.extend({ 96 96 serialized: DS.attr('string'), 97 97 table: function () … … 153 153 repositories: this._normalizeIdMap(payload['repositories']), 154 154 bugTrackers: this._normalizeIdMap(payload['bugTrackers']), 155 dashboards: [ {id: 1, serialized: JSON.stringify(payload['defaultDashboard'])}],155 dashboards: [], 156 156 }; 157 157 … … 172 172 test['metrics'] = []; 173 173 test['metrics'].push(metricId); 174 } 175 176 var id = 1; 177 var dashboardsInPayload = payload['dashboards']; 178 for (var dashboardName in dashboardsInPayload) { 179 results['dashboards'].push({ 180 id: id, 181 name: dashboardName, 182 serialized: JSON.stringify(dashboardsInPayload[dashboardName]) 183 }); 184 id++; 174 185 } 175 186 … … 212 223 _builderById: {}, 213 224 _repositoryById: {}, 225 _dashboardByName: {}, 226 _defaultDashboardName: null, 214 227 _fetchPromise: null, 215 228 fetch: function (store) … … 224 237 builder: function (id) { return this._builderById[id]; }, 225 238 repository: function (id) { return this._repositoryById[id]; }, 239 dashboardByName: function (name) { return this._dashboardByName[name]; }, 240 defaultDashboardName: function () { return this._defaultDashboardName; }, 226 241 _fetchedManifest: function (store) 227 242 { … … 260 275 this.set('bugTrackers', store.all('bugTracker').sortBy('name')); 261 276 262 this.set('defaultDashboard', store.all('dashboard').objectAt(0)); 277 var dashboards = store.all('dashboard').sortBy('name'); 278 this.set('dashboards', dashboards); 279 dashboards.forEach(function (dashboard) { self._dashboardByName[dashboard.get('name')] = dashboard; }); 280 this._defaultDashboardName = dashboards.length ? dashboards[0].get('name') : null; 263 281 }, 264 282 fetchRunsWithPlatformAndMetric: function (store, platformId, metricId)
Note: See TracChangeset
for help on using the changeset viewer.