UI to associate bugs with an analysis task is crappy
[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     _fetchTestResults: function ()
133     {
134         var task = this.get('task');
135         if (!task)
136             return null;
137         var self = this;
138         return App.Manifest.fetchRunsWithPlatformAndMetric(this.store,
139             task.get('platform').get('id'), task.get('metric').get('id'), this.get('id')).then(
140             function (result) { self.set('testResults', result.data); },
141             function (error) {
142                 // FIXME: Somehow this never gets called.
143                 alert('Failed to fetch the results:' + error);
144                 return null;
145             });
146     }.observes('task', 'task.platform', 'task.metric').on('init'),
147 });
148
149 App.TestGroup.create = function (analysisTask, name, rootSets, repetitionCount)
150 {
151     var param = {
152         task: analysisTask.get('id'),
153         name: name,
154         rootSets: rootSets,
155         repetitionCount: repetitionCount,
156     };
157     return PrivilegedAPI.sendRequest('create-test-group', param).then(function (data) {
158         analysisTask.set('testGroups'); // Refetch test groups.
159         return analysisTask.store.find('testGroup', data['testGroupId']);
160     });
161 }
162
163 App.TestGroupAdapter = DS.RESTAdapter.extend({
164     buildURL: function (type, id)
165     {
166         return '../api/test-groups/' + (id ? id : '');
167     },
168 });
169
170 App.Triggerable = App.NameLabelModel.extend({
171     acceptedRepositories: DS.hasMany('repositories'),
172 });
173
174 App.TriggerableAdapter = DS.RESTAdapter.extend({
175     buildURL: function (type, id)
176     {
177         return '../api/triggerables/' + (id ? id : '');
178     },
179 });
180
181 App.AnalysisTaskSerializer = App.TestGroupSerializer = App.TriggerableSerializer = DS.RESTSerializer.extend({
182     normalizePayload: function (payload)
183     {
184         delete payload['status'];
185         return payload;
186     }
187 });
188
189 App.BuildRequest = App.Model.extend({
190     testGroup: DS.belongsTo('testGroup'),
191     order: DS.attr('number'),
192     orderLabel: function ()
193     {
194         return this.get('order') + 1;
195     }.property('order'),
196     rootSet: DS.belongsTo('rootSet'),
197     status: DS.attr('string'),
198     statusLabel: function ()
199     {
200         switch (this.get('status')) {
201         case 'pending':
202             return 'Waiting to be scheduled';
203         case 'scheduled':
204             return 'Scheduled';
205         case 'running':
206             return 'Running';
207         case 'failed':
208             return 'Failed';
209         case 'completed':
210             return 'Completed';
211         }
212     }.property('status'),
213     url: DS.attr('string'),
214     build: DS.attr('number'),
215 });
216
217 App.BuildRequest.aggregateStatuses = function (requests)
218 {
219     var completeCount = 0;
220     var failureCount = 0;
221     requests.forEach(function (request) {
222         switch (request.get('status')) {
223         case 'failed':
224             failureCount++;
225             break;
226         case 'completed':
227             completeCount++;
228             break;
229         }
230     });
231     if (completeCount == requests.length)
232         return 'Done';
233     if (failureCount == requests.length)
234         return 'All failed';
235     var status = completeCount + ' out of ' + requests.length + ' completed';
236     if (failureCount)
237         status += ', ' + failureCount + ' failed';
238     return status;
239 }