3 App.NameLabelModel = DS.Model.extend({
4 name: DS.attr('string'),
7 return this.get('name');
11 App.Test = App.NameLabelModel.extend({
12 url: DS.attr('string'),
13 parent: DS.belongsTo('test', {inverse: 'childTests'}),
14 childTests: DS.hasMany('test', {inverse: 'parent'}),
15 metrics: DS.hasMany('metrics'),
18 App.Metric = App.NameLabelModel.extend({
19 test: DS.belongsTo('test'),
20 aggregator: DS.attr('string'),
23 return this.get('name') + (this.get('aggregator') ? ' : ' + this.get('aggregator') : '');
24 }.property('name', 'aggregator'),
28 var parent = this.get('test');
30 path.push(parent.get('name'));
31 parent = parent.get('parent');
33 return path.reverse();
34 }.property('name', 'test'),
37 return this.get('path').join(' \u220b ') /* ∋ */
38 + ' : ' + this.get('label');
39 }.property('path', 'label'),
42 App.Builder = App.NameLabelModel.extend({
43 buildUrl: DS.attr('string'),
44 urlFromBuildNumber: function (buildNumber)
46 var template = this.get('buildUrl');
47 return template ? template.replace(/\$builderName/g, this.get('name')).replace(/\$buildNumber/g, buildNumber) : null;
51 App.BugTracker = App.NameLabelModel.extend({
52 bugUrl: DS.attr('string'),
53 newBugUrl: DS.attr('string'),
54 repositories: DS.hasMany('repository'),
55 urlFromBugNumber: function (bugNumber) {
56 var template = this.get('bugUrl');
57 return template && bugNumber ? template.replace(/\$number/g, bugNumber) : null;
61 App.DateArrayTransform = DS.Transform.extend({
62 deserialize: function (serialized)
64 return serialized.map(function (time) { return new Date(time); });
68 App.Platform = App.NameLabelModel.extend({
71 metrics: DS.hasMany('metric'),
72 lastModified: DS.attr('dateArray'),
73 containsMetric: function (metric)
76 this._metricSet = new Ember.Set(this.get('metrics'));
77 return this._metricSet.contains(metric);
79 lastModifiedTimeForMetric: function (metric)
81 var index = this.get('metrics').indexOf(metric);
84 return this.get('lastModified').objectAt(index);
86 containsTest: function (test)
89 var set = new Ember.Set();
90 this.get('metrics').forEach(function (metric) {
91 for (var test = metric.get('test'); test; test = test.get('parent'))
96 return this._testSet.contains(test);
100 App.Repository = App.NameLabelModel.extend({
101 url: DS.attr('string'),
102 blameUrl: DS.attr('string'),
103 hasReportedCommits: DS.attr('boolean'),
104 urlForRevision: function (currentRevision)
106 return (this.get('url') || '').replace(/\$1/g, currentRevision);
108 urlForRevisionRange: function (from, to)
110 return (this.get('blameUrl') || '').replace(/\$1/g, from).replace(/\$2/g, to);
114 App.Dashboard = App.NameLabelModel.extend({
115 serialized: DS.attr('string'),
118 var json = this.get('serialized');
120 var parsed = JSON.parse(json);
122 console.log("Failed to parse the grid:", error, json);
127 return this._normalizeTable(parsed);
128 }.property('serialized'),
132 return this.get('table').slice(1);
135 headerColumns: function ()
137 var table = this.get('table');
138 if (!table || !table.length)
140 return table[0].map(function (name, index) {
141 return {label:name, index: index};
145 _normalizeTable: function (table)
147 var maxColumnCount = Math.max(table.map(function (column) { return column.length; }));
148 for (var i = 1; i < table.length; i++) {
150 for (var j = 1; j < row.length; j++) {
151 if (row[j] && !(row[j] instanceof Array)) {
152 console.log('Unrecognized (platform, metric) pair at column ' + i + ' row ' + j + ':' + row[j]);
161 App.MetricSerializer = App.PlatformSerializer = DS.RESTSerializer.extend({
162 normalizePayload: function (payload)
165 platforms: this._normalizeIdMap(payload['all']),
166 builders: this._normalizeIdMap(payload['builders']),
167 tests: this._normalizeIdMap(payload['tests']).map(function (test) {
168 test['parent'] = test['parentId'];
171 metrics: this._normalizeIdMap(payload['metrics']),
172 repositories: this._normalizeIdMap(payload['repositories']),
173 bugTrackers: this._normalizeIdMap(payload['bugTrackers']),
177 for (var testId in payload['tests']) {
178 var test = payload['tests'][testId];
179 var parent = payload['tests'][test['parent']];
182 if (!parent['childTests'])
183 parent['childTests'] = [];
184 parent['childTests'].push(testId);
187 for (var metricId in payload['metrics']) {
188 var metric = payload['metrics'][metricId];
189 var test = payload['tests'][metric['test']];
190 if (!test['metrics'])
191 test['metrics'] = [];
192 test['metrics'].push(metricId);
196 var dashboardsInPayload = payload['dashboards'];
197 for (var dashboardName in dashboardsInPayload) {
198 results['dashboards'].push({
201 serialized: JSON.stringify(dashboardsInPayload[dashboardName])
208 _normalizeIdMap: function (idMap)
211 for (var id in idMap) {
212 var definition = idMap[id];
213 definition['id'] = id;
214 results.push(definition);
220 App.PlatformAdapter = DS.RESTAdapter.extend({
221 buildURL: function (type, id)
223 return '../data/manifest.json';
227 App.MetricAdapter = DS.RESTAdapter.extend({
228 buildURL: function (type, id)
230 return '../data/manifest.json';
234 App.Manifest = Ember.Controller.extend({
238 repositoriesWithReportedCommits: [],
244 _dashboardByName: {},
245 _defaultDashboardName: null,
247 fetch: function (store)
249 if (!this._fetchPromise)
250 this._fetchPromise = store.findAll('platform').then(this._fetchedManifest.bind(this, store));
251 return this._fetchPromise;
253 isFetched: function () { return !!this.get('platforms'); }.property('platforms'),
254 platform: function (id) { return this._platformById[id]; },
255 metric: function (id) { return this._metricById[id]; },
256 builder: function (id) { return this._builderById[id]; },
257 repository: function (id) { return this._repositoryById[id]; },
258 dashboardByName: function (name) { return this._dashboardByName[name]; },
259 defaultDashboardName: function () { return this._defaultDashboardName; },
260 _fetchedManifest: function (store)
262 var startTime = Date.now();
265 var topLevelTests = [];
266 store.all('test').forEach(function (test) {
267 var parent = test.get('parent');
269 topLevelTests.push(test);
271 this.set('topLevelTests', topLevelTests);
273 store.all('metric').forEach(function (metric) {
274 self._metricById[metric.get('id')] = metric;
276 var platforms = store.all('platform');
277 platforms.forEach(function (platform) {
278 self._platformById[platform.get('id')] = platform;
280 this.set('platforms', platforms);
282 store.all('builder').forEach(function (builder) {
283 self._builderById[builder.get('id')] = builder;
286 var repositories = store.all('repository');
287 repositories.forEach(function (repository) {
288 self._repositoryById[repository.get('id')] = repository;
290 this.set('repositories', repositories.sortBy('id'));
291 this.set('repositoriesWithReportedCommits',
292 repositories.filter(function (repository) { return repository.get('hasReportedCommits'); }));
294 this.set('bugTrackers', store.all('bugTracker').sortBy('name'));
296 var dashboards = store.all('dashboard').sortBy('name');
297 this.set('dashboards', dashboards);
298 dashboards.forEach(function (dashboard) { self._dashboardByName[dashboard.get('name')] = dashboard; });
299 this._defaultDashboardName = dashboards.length ? dashboards[0].get('name') : null;
301 fetchRunsWithPlatformAndMetric: function (store, platformId, metricId, testGroupId, useCache)
303 Ember.assert("Can't cache results for test groups", !(testGroupId && useCache));
305 return Ember.RSVP.all([
306 RunsData.fetchRuns(platformId, metricId, testGroupId, useCache),
308 ]).then(function (values) {
309 var response = values[0];
311 var platform = App.Manifest.platform(platformId);
312 var metric = App.Manifest.metric(metricId);
317 data: response ? self._formatFetchedData(metric.get('name'), response.configurations) : null,
318 shouldRefetch: !response || +response.lastModified < +platform.lastModifiedTimeForMetric(metric),
322 _makeFormatter: function (unit, sigFig, alwaysShowSign)
324 var isMiliseconds = false;
326 isMiliseconds = true;
329 var divisor = unit == 'B' ? 1024 : 1000;
331 var suffix = ['\u03BC', 'm', '', 'K', 'M', 'G', 'T', 'P', 'E'];
332 var threshold = sigFig >= 3 ? divisor : (divisor / 10);
333 return function (value) {
335 var sign = value >= 0 ? (alwaysShowSign ? '+' : '') : '-';
336 value = Math.abs(value);
337 for (i = isMiliseconds ? 1 : 2; value < 1 && i > 0; i--)
339 for (; value >= threshold; i++)
341 return sign + value.toPrecision(Math.max(2, sigFig)) + ' ' + suffix[i] + (unit || '');
344 _formatFetchedData: function (metricName, configurations)
346 var unit = RunsData.unitFromMetricName(metricName);
347 var smallerIsBetter = RunsData.isSmallerBetter(unit);
348 var deltaFormatterWithoutSign = this._makeFormatter(unit, 2, false);
350 var currentTimeSeries = configurations.current.timeSeriesByCommitTime(false);
351 var baselineTimeSeries = configurations.baseline ? configurations.baseline.timeSeriesByCommitTime(false) : null;
352 var targetTimeSeries = configurations.target ? configurations.target.timeSeriesByCommitTime(false) : null;
353 var unfilteredCurrentTimeSeries, unfilteredBaselineTimeSeries, unfilteredTargetTimeSeries;
356 current: currentTimeSeries,
357 baseline: baselineTimeSeries,
358 target: targetTimeSeries,
359 formatWithDeltaAndUnit: function (value, delta)
361 return this.formatter(value) + (delta && !isNaN(delta) ? ' \u00b1 ' + deltaFormatterWithoutSign(delta) : '');
363 formatter: this._makeFormatter(unit, 4, false),
364 deltaFormatter: this._makeFormatter(unit, 2, true),
365 smallerIsBetter: smallerIsBetter,
366 showOutlier: function (show)
368 if (!unfilteredCurrentTimeSeries) {
369 unfilteredCurrentTimeSeries = configurations.current.timeSeriesByCommitTime(true);
370 unfilteredBaselineTimeSeries = configurations.baseline ? configurations.baseline.timeSeriesByCommitTime(true) : null;
371 unfilteredTargetTimeSeries = configurations.target ? configurations.target.timeSeriesByCommitTime(true) : null;
373 this.current = show ? unfilteredCurrentTimeSeries : currentTimeSeries;
374 this.baseline = show ? unfilteredBaselineTimeSeries : baselineTimeSeries;
375 this.target = show ? unfilteredTargetTimeSeries : targetTimeSeries;