build-requests should use conform to JSON API format
[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     url: function () {
65         return this.get('bugTracker').urlFromBugNumber(this.get('number'));
66     }.property('bugTracker.bugUrl', 'number'),
67     label: function () {
68         return this.get('bugTracker').get('label') + ': ' + this.get('number');
69     }.property('name', 'bugTracker'),
70 });
71
72 // FIXME: Use DS.RESTAdapter instead.
73 App.AnalysisTask.create = function (name, startMeasurement, endMeasurement)
74 {
75     return PrivilegedAPI.sendRequest('create-analysis-task', {
76         name: name,
77         startRun: startMeasurement.id(),
78         endRun: endMeasurement.id(),
79     });
80 }
81
82 App.AnalysisTaskAdapter = DS.RESTAdapter.extend({
83     buildURL: function (type, id)
84     {
85         return '../api/analysis-tasks/' + (id ? id : '');
86     },
87 });
88
89 App.BugAdapter = DS.RESTAdapter.extend({
90     createRecord: function (store, type, record)
91     {
92         var param = {
93             task: record.get('task').get('id'),
94             bugTracker: record.get('bugTracker').get('id'),
95             number: record.get('number'),
96         };
97         return PrivilegedAPI.sendRequest('associate-bug', param).then(function (data) {
98             param['id'] = data['bugId'];
99             return {'bug': param};
100         });
101     },
102     deleteRecord: function (store, type, record)
103     {
104         return PrivilegedAPI.sendRequest('associate-bug', {bugToDelete: record.get('id')}).then(function () {
105             return {};
106         });
107     }
108 });
109
110 App.Root = App.Model.extend({
111     repository: DS.belongsTo('repository'),
112     revision: DS.attr('string'),
113 });
114
115 App.RootSet = App.Model.extend({
116     roots: DS.hasMany('roots'),
117     revisionForRepository: function (repository)
118     {
119         var root = this.get('roots').findBy('repository', repository);
120         if (!root)
121             return null;
122         return root.get('revision');
123     }
124 });
125
126 App.TestGroup = App.NameLabelModel.extend({
127     task: DS.belongsTo('analysisTask'),
128     author: DS.attr('string'),
129     createdAt: DS.attr('date'),
130     buildRequests: DS.hasMany('buildRequests'),
131     rootSets: DS.hasMany('rootSets'),
132     platform: DS.belongsTo('platform'),
133     _fetchTestResults: function ()
134     {
135         var task = this.get('task');
136         var platform = this.get('platform') || (task ? task.get('platform') : null)
137         if (!task || !platform)
138             return null;
139         var self = this;
140         return App.Manifest.fetchRunsWithPlatformAndMetric(this.store,
141             platform.get('id'), task.get('metric').get('id'), this.get('id')).then(
142             function (result) { self.set('testResults', result.data); },
143             function (error) {
144                 // FIXME: Somehow this never gets called.
145                 alert('Failed to fetch the results:' + error);
146                 return null;
147             });
148     }.observes('task', 'task.platform', 'task.metric', 'platform').on('init'),
149 });
150
151 App.TestGroup.create = function (analysisTask, name, rootSets, repetitionCount)
152 {
153     var param = {
154         task: analysisTask.get('id'),
155         name: name,
156         rootSets: rootSets,
157         repetitionCount: repetitionCount,
158     };
159     return PrivilegedAPI.sendRequest('create-test-group', param).then(function (data) {
160         analysisTask.set('testGroups'); // Refetch test groups.
161         return analysisTask.store.find('testGroup', data['testGroupId']);
162     });
163 }
164
165 App.TestGroupAdapter = DS.RESTAdapter.extend({
166     buildURL: function (type, id)
167     {
168         return '../api/test-groups/' + (id ? id : '');
169     },
170 });
171
172 App.Triggerable = App.NameLabelModel.extend({
173     acceptedRepositories: DS.hasMany('repositories'),
174 });
175
176 App.TriggerableAdapter = DS.RESTAdapter.extend({
177     buildURL: function (type, id)
178     {
179         return '../api/triggerables/' + (id ? id : '');
180     },
181 });
182
183 App.AnalysisTaskSerializer = App.TestGroupSerializer = App.TriggerableSerializer = DS.RESTSerializer.extend({
184     normalizePayload: function (payload)
185     {
186         delete payload['status'];
187         return payload;
188     }
189 });
190
191 App.BuildRequest = App.Model.extend({
192     testGroup: DS.belongsTo('testGroup'),
193     order: DS.attr('number'),
194     orderLabel: function ()
195     {
196         return this.get('order') + 1;
197     }.property('order'),
198     rootSet: DS.belongsTo('rootSet'),
199     status: DS.attr('string'),
200     statusLabel: function ()
201     {
202         switch (this.get('status')) {
203         case 'pending':
204             return 'Waiting to be scheduled';
205         case 'scheduled':
206             return 'Scheduled';
207         case 'running':
208             return 'Running';
209         case 'failed':
210             return 'Failed';
211         case 'completed':
212             return 'Completed';
213         }
214     }.property('status'),
215     url: DS.attr('string'),
216     build: DS.attr('number'),
217 });
218
219 App.BuildRequest.aggregateStatuses = function (requests)
220 {
221     var completeCount = 0;
222     var failureCount = 0;
223     requests.forEach(function (request) {
224         switch (request.get('status')) {
225         case 'failed':
226             failureCount++;
227             break;
228         case 'completed':
229             completeCount++;
230             break;
231         }
232     });
233     if (completeCount == requests.length)
234         return 'Done';
235     if (failureCount == requests.length)
236         return 'All failed';
237     var status = completeCount + ' out of ' + requests.length + ' completed';
238     if (failureCount)
239         status += ', ' + failureCount + ' failed';
240     return status;
241 }