Fix the link noted 'contribute to features' in status.html
[WebKit-https.git] / Websites / perf.webkit.org / public / v2 / analysis.js
1 App.AnalysisTask = App.NameLabelModel.extend({
2     author: DS.attr('string'),
3     createdAt: DS.attr('date'),
4     formattedCreatedAt: function () {
5         var format = d3.time.format("%Y-%m-%d");
6         return format(this.get('createdAt'));
7     }.property('createdAt'),
8     platform: DS.belongsTo('platform'),
9     metric: DS.belongsTo('metric'),
10     startRun: DS.attr('number'),
11     endRun: DS.attr('number'),
12     bugs: DS.hasMany('bugs'),
13     buildRequestCount: DS.attr('number'),
14     finishedBuildRequestCount: DS.attr('number'),
15     result: DS.attr('string'),
16     needed: DS.attr('number'), // DS.attr('boolean') treats null as false.
17     saveStatus: function ()
18     {
19         return PrivilegedAPI.sendRequest('update-analysis-task', {
20             task: this.get('id'),
21             result: this.get('result'),
22             needed: this.get('needed'),
23         });
24     },
25     statusLabel: function ()
26     {
27         var total = this.get('buildRequestCount');
28         var finished = this.get('finishedBuildRequestCount');
29         var result = this.get('result');
30         if (result && total == finished)
31             return result.capitalize();
32         if (!total)
33             return 'Empty';
34         if (total != finished)
35             return finished + ' out of ' + total;
36         return 'Done';
37     }.property('buildRequestCount', 'finishedBuildRequestCount'),
38     testGroups: function (key, value, oldValue)
39     {
40         return this.store.find('testGroup', {task: this.get('id')});
41     }.property(),
42     triggerable: function ()
43     {
44         return this.store.find('triggerable', {task: this.get('id')}).then(function (triggerables) {
45             return triggerables.objectAt(0);
46         }, function (error) {
47             console.log('Failed to fetch triggerables', error);
48             return null;
49         });
50     }.property(),
51     label: function ()
52     {
53         var label = this.get('name');
54         var bugs = this.get('bugs').map(function (bug) { return bug.get('label'); }).join(' / ');
55         return bugs ? label + ' (' + bugs + ')' : label;
56     }.property('name', 'bugs'),
57 });
58
59 App.Bug = App.Model.extend({
60     task: DS.belongsTo('AnalysisTask'),
61     bugTracker: DS.belongsTo('BugTracker'),
62     createdAt: DS.attr('date'),
63     number: DS.attr('number'),
64     label: function () {
65         return this.get('bugTracker').get('label') + ': ' + this.get('number');
66     }.property('name', 'bugTracker'),
67 });
68
69 // FIXME: Use DS.RESTAdapter instead.
70 App.AnalysisTask.create = function (name, startMeasurement, endMeasurement)
71 {
72     return PrivilegedAPI.sendRequest('create-analysis-task', {
73         name: name,
74         startRun: startMeasurement.id(),
75         endRun: endMeasurement.id(),
76     });
77 }
78
79 App.AnalysisTaskAdapter = DS.RESTAdapter.extend({
80     buildURL: function (type, id)
81     {
82         return '../api/analysis-tasks/' + (id ? id : '');
83     },
84 });
85
86 App.BugAdapter = DS.RESTAdapter.extend({
87     createRecord: function (store, type, record)
88     {
89         var param = {
90             task: record.get('task').get('id'),
91             bugTracker: record.get('bugTracker').get('id'),
92             number: record.get('number'),
93         };
94         return PrivilegedAPI.sendRequest('associate-bug', param).then(function (data) {
95             param['id'] = data['bugId'];
96             return {'bug': param};
97         });
98     }
99 });
100
101 App.Root = App.Model.extend({
102     repository: DS.belongsTo('repository'),
103     revision: DS.attr('string'),
104 });
105
106 App.RootSet = App.Model.extend({
107     roots: DS.hasMany('roots'),
108     revisionForRepository: function (repository)
109     {
110         var root = this.get('roots').findBy('repository', repository);
111         if (!root)
112             return null;
113         return root.get('revision');
114     }
115 });
116
117 App.TestGroup = App.NameLabelModel.extend({
118     task: DS.belongsTo('analysisTask'),
119     author: DS.attr('string'),
120     createdAt: DS.attr('date'),
121     buildRequests: DS.hasMany('buildRequests'),
122     rootSets: DS.hasMany('rootSets'),
123     _fetchTestResults: function ()
124     {
125         var task = this.get('task');
126         if (!task)
127             return null;
128         var self = this;
129         return App.Manifest.fetchRunsWithPlatformAndMetric(this.store,
130             task.get('platform').get('id'), task.get('metric').get('id'), this.get('id')).then(
131             function (result) { self.set('testResults', result.data); },
132             function (error) {
133                 // FIXME: Somehow this never gets called.
134                 alert('Failed to fetch the results:' + error);
135                 return null;
136             });
137     }.observes('task', 'task.platform', 'task.metric').on('init'),
138 });
139
140 App.TestGroup.create = function (analysisTask, name, rootSets, repetitionCount)
141 {
142     var param = {
143         task: analysisTask.get('id'),
144         name: name,
145         rootSets: rootSets,
146         repetitionCount: repetitionCount,
147     };
148     return PrivilegedAPI.sendRequest('create-test-group', param).then(function (data) {
149         analysisTask.set('testGroups'); // Refetch test groups.
150         return analysisTask.store.find('testGroup', data['testGroupId']);
151     });
152 }
153
154 App.TestGroupAdapter = DS.RESTAdapter.extend({
155     buildURL: function (type, id)
156     {
157         return '../api/test-groups/' + (id ? id : '');
158     },
159 });
160
161 App.Triggerable = App.NameLabelModel.extend({
162     acceptedRepositories: DS.hasMany('repositories'),
163 });
164
165 App.TriggerableAdapter = DS.RESTAdapter.extend({
166     buildURL: function (type, id)
167     {
168         return '../api/triggerables/' + (id ? id : '');
169     },
170 });
171
172 App.AnalysisTaskSerializer = App.TestGroupSerializer = App.TriggerableSerializer = DS.RESTSerializer.extend({
173     normalizePayload: function (payload)
174     {
175         delete payload['status'];
176         return payload;
177     }
178 });
179
180 App.BuildRequest = App.Model.extend({
181     testGroup: DS.belongsTo('testGroup'),
182     order: DS.attr('number'),
183     orderLabel: function ()
184     {
185         return this.get('order') + 1;
186     }.property('order'),
187     rootSet: DS.belongsTo('rootSet'),
188     status: DS.attr('string'),
189     statusLabel: function ()
190     {
191         switch (this.get('status')) {
192         case 'pending':
193             return 'Waiting to be scheduled';
194         case 'scheduled':
195             return 'Scheduled';
196         case 'running':
197             return 'Running';
198         case 'failed':
199             return 'Failed';
200         case 'completed':
201             return 'Completed';
202         }
203     }.property('status'),
204     url: DS.attr('string'),
205     build: DS.attr('number'),
206 });
207
208 App.BuildRequest.aggregateStatuses = function (requests)
209 {
210     var completeCount = 0;
211     var failureCount = 0;
212     requests.forEach(function (request) {
213         switch (request.get('status')) {
214         case 'failed':
215             failureCount++;
216             break;
217         case 'completed':
218             completeCount++;
219             break;
220         }
221     });
222     if (completeCount == requests.length)
223         return 'Done';
224     if (failureCount == requests.length)
225         return 'All failed';
226     var status = completeCount + ' out of ' + requests.length + ' completed';
227     if (failureCount)
228         status += ', ' + failureCount + ' failed';
229     return status;
230 }