The dashboard on new perf monitor should be configurable
[WebKit-https.git] / Websites / perf.webkit.org / public / v2 / manifest.js
1 App.Model = DS.Model;
2
3 App.NameLabelModel = DS.Model.extend({
4     name: DS.attr('string'),
5     label: function ()
6     {
7         return this.get('name');
8     }.property('name'),
9 });
10
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'),
16 });
17
18 App.Metric = App.NameLabelModel.extend({
19     test: DS.belongsTo('test'),
20     aggregator: DS.attr('string'),
21     label: function ()
22     {
23         return this.get('name') + ' : ' + this.get('aggregator');
24     }.property('name', 'aggregator'),
25     path: function ()
26     {
27         var path = [];
28         var parent = this.get('test');
29         while (parent) {
30             path.push(parent.get('name'));
31             parent = parent.get('parent');
32         }
33         return path.reverse();
34     }.property('name', 'test'),
35     fullName: function ()
36     {
37         return this.get('path').join(' \u2208 ') /* ∈ */
38             + ' : ' + this.get('label');
39     }.property('path', 'label'),
40 });
41
42 App.Builder = App.NameLabelModel.extend({
43     buildUrl: DS.attr('string'),
44     urlFromBuildNumber: function (buildNumber)
45     {
46         var template = this.get('buildUrl');
47         return template ? template.replace(/\$buildNumber/g, buildNumber) : null;
48     }
49 });
50
51 App.BugTracker = App.NameLabelModel.extend({
52     bugUrl: DS.attr('string'),
53     newBugUrl: DS.attr('string'),
54     repositories: DS.hasMany('repository'),
55 });
56
57 App.Platform = App.NameLabelModel.extend({
58     _metricSet: null,
59     _testSet: null,
60     metrics: DS.hasMany('metric'),
61     containsMetric: function (metric)
62     {
63         if (!this._metricSet)
64             this._metricSet = new Ember.Set(this.get('metrics'));
65         return this._metricSet.contains(metric);
66     },
67     containsTest: function (test)
68     {
69         if (!this._testSet) {
70             var set = new Ember.Set();
71             this.get('metrics').forEach(function (metric) {
72                 for (var test = metric.get('test'); test; test = test.get('parent'))
73                     set.push(test);
74             });
75             this._testSet = set;
76         }
77         return this._testSet.contains(test);
78     },
79 });
80
81 App.Repository = App.NameLabelModel.extend({
82     url: DS.attr('string'),
83     blameUrl: DS.attr('string'),
84     hasReportedCommits: DS.attr('boolean'),
85     urlForRevision: function (currentRevision)
86     {
87         return (this.get('url') || '').replace(/\$1/g, currentRevision);
88     },
89     urlForRevisionRange: function (from, to)
90     {
91         return (this.get('blameUrl') || '').replace(/\$1/g, from).replace(/\$2/g, to);
92     },
93 });
94
95 App.Dashboard = App.Model.extend({
96     serialized: DS.attr('string'),
97     table: function ()
98     {
99         var json = this.get('serialized');
100         try {
101             var parsed = JSON.parse(json);
102         } catch (error) {
103             console.log("Failed to parse the grid:", error, json);
104             return [];
105         }
106         if (!parsed)
107             return [];
108         return this._normalizeTable(parsed);
109     }.property('serialized'),
110
111     rows: function ()
112     {
113         return this.get('table').slice(1);
114     }.property('table'),
115
116     headerColumns: function ()
117     {
118         var table = this.get('table');
119         if (!table || !table.length)
120             return [];
121         return table[0].map(function (name, index) {
122             return {label:name, index: index};
123         });
124     }.property('table'),
125
126     _normalizeTable: function (table)
127     {
128         var maxColumnCount = Math.max(table.map(function (column) { return column.length; }));
129         for (var i = 1; i < table.length; i++) {
130             var row = table[i];
131             for (var j = 1; j < row.length; j++) {
132                 if (row[j] && !(row[j] instanceof Array)) {
133                     console.log('Unrecognized (platform, metric) pair at column ' + i + ' row ' + j + ':' + row[j]);
134                     row[j] = [];
135                 }
136             }
137         }
138         return table;
139     },
140 });
141
142 App.MetricSerializer = App.PlatformSerializer = DS.RESTSerializer.extend({
143     normalizePayload: function (payload)
144     {
145         var results = {
146             platforms: this._normalizeIdMap(payload['all']),
147             builders: this._normalizeIdMap(payload['builders']),
148             tests: this._normalizeIdMap(payload['tests']).map(function (test) {
149                 test['parent'] = test['parentId'];
150                 return test;
151             }),
152             metrics: this._normalizeIdMap(payload['metrics']),
153             repositories: this._normalizeIdMap(payload['repositories']),
154             bugTrackers: this._normalizeIdMap(payload['bugTrackers']),
155             dashboards: [{id: 1, serialized: JSON.stringify(payload['defaultDashboard'])}],
156         };
157
158         for (var testId in payload['tests']) {
159             var test = payload['tests'][testId];
160             var parent = payload['tests'][test['parent']];
161             if (!parent)
162                 continue;
163             if (!parent['childTests'])
164                 parent['childTests'] = [];
165             parent['childTests'].push(testId);
166         }
167
168         for (var metricId in payload['metrics']) {
169             var metric = payload['metrics'][metricId];
170             var test = payload['tests'][metric['test']];
171             if (!test['metrics'])
172                 test['metrics'] = [];
173             test['metrics'].push(metricId);
174         }
175
176         return results;
177     },
178     _normalizeIdMap: function (idMap)
179     {
180         var results = [];
181         for (var id in idMap) {
182             var definition = idMap[id];
183             definition['id'] = id;
184             results.push(definition);
185         }
186         return results;
187     }
188 });
189
190 App.PlatformAdapter = DS.RESTAdapter.extend({
191     buildURL: function (type, id)
192     {
193         return '../data/manifest.json';
194     },
195 });
196
197 App.MetricAdapter = DS.RESTAdapter.extend({
198     buildURL: function (type, id)
199     {
200         return '../data/manifest.json';
201     },
202 });
203
204 App.Manifest = Ember.Controller.extend({
205     platforms: null,
206     topLevelTests: null,
207     repositories: [],
208     repositoriesWithReportedCommits: [],
209     bugTrackers: [],
210     _platformById: {},
211     _metricById: {},
212     _builderById: {},
213     _repositoryById: {},
214     _fetchPromise: null,
215     fetch: function (store)
216     {
217         if (!this._fetchPromise)
218             this._fetchPromise = store.findAll('platform').then(this._fetchedManifest.bind(this, store));
219         return this._fetchPromise;
220     },
221     isFetched: function () { return !!this.get('platforms'); }.property('platforms'),
222     platform: function (id) { return this._platformById[id]; },
223     metric: function (id) { return this._metricById[id]; },
224     builder: function (id) { return this._builderById[id]; },
225     repository: function (id) { return this._repositoryById[id]; },
226     _fetchedManifest: function (store)
227     {
228         var startTime = Date.now();
229         var self = this;
230
231         var topLevelTests = [];
232         store.all('test').forEach(function (test) {
233             var parent = test.get('parent');
234             if (!parent)
235                 topLevelTests.push(test);
236         });
237         this.set('topLevelTests', topLevelTests);
238
239         store.all('metric').forEach(function (metric) {
240             self._metricById[metric.get('id')] = metric;
241         });
242         var platforms = store.all('platform');
243         platforms.forEach(function (platform) {
244             self._platformById[platform.get('id')] = platform;
245         });
246         this.set('platforms', platforms);
247
248         store.all('builder').forEach(function (builder) {
249             self._builderById[builder.get('id')] = builder;
250         });
251
252         var repositories = store.all('repository');
253         repositories.forEach(function (repository) {
254             self._repositoryById[repository.get('id')] = repository;
255         });
256         this.set('repositories', repositories.sortBy('id'));
257         this.set('repositoriesWithReportedCommits',
258             repositories.filter(function (repository) { return repository.get('hasReportedCommits'); }));
259
260         this.set('bugTrackers', store.all('bugTracker').sortBy('name'));
261
262         this.set('defaultDashboard', store.all('dashboard').objectAt(0));
263     },
264     fetchRunsWithPlatformAndMetric: function (store, platformId, metricId)
265     {
266         return Ember.RSVP.all([
267             RunsData.fetchRuns(platformId, metricId),
268             this.fetch(store),
269         ]).then(function (values) {
270             var runs = values[0];
271
272             var platform = App.Manifest.platform(platformId);
273             var metric = App.Manifest.metric(metricId);
274
275             // FIXME: Include this information in JSON and process it in RunsData.fetchRuns
276             var unit = {'Combined': '', // Assume smaller is better for now.
277                 'FrameRate': 'fps',
278                 'Runs': 'runs/s',
279                 'Time': 'ms',
280                 'Malloc': 'bytes',
281                 'JSHeap': 'bytes',
282                 'Allocations': 'bytes',
283                 'EndAllocations': 'bytes',
284                 'MaxAllocations': 'bytes',
285                 'MeanAllocations': 'bytes'}[metric.get('name')];
286             runs.unit = unit;
287
288             return {platform: platform, metric: metric, runs: runs};
289         });
290     },
291 }).create();