Perf dashboard should show the results of A/B testing
[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     platform: DS.belongsTo('platform'),
5     metric: DS.belongsTo('metric'),
6     startRun: DS.attr('number'),
7     endRun: DS.attr('number'),
8     bugs: DS.hasMany('bugs'),
9     testGroups: function (key, value, oldValue) {
10         return this.store.find('testGroup', {task: this.get('id')});
11     }.property(),
12     triggerable: function () {
13         return this.store.find('triggerable', {task: this.get('id')}).then(function (triggerables) {
14             return triggerables.objectAt(0);
15         }, function () {
16             return null;
17         });
18     }.property(),
19     label: function () {
20         var label = this.get('name');
21         var bugs = this.get('bugs').map(function (bug) { return bug.get('label'); }).join(' / ');
22         return bugs ? label + ' (' + bugs + ')' : label;
23     }.property('name', 'bugs'),
24 });
25
26 App.Bug = App.Model.extend({
27     task: DS.belongsTo('AnalysisTask'),
28     bugTracker: DS.belongsTo('BugTracker'),
29     createdAt: DS.attr('date'),
30     number: DS.attr('number'),
31     label: function () {
32         return this.get('bugTracker').get('label') + ': ' + this.get('number');
33     }.property('name', 'bugTracker'),
34 });
35
36 // FIXME: Use DS.RESTAdapter instead.
37 App.AnalysisTask.create = function (name, startMeasurement, endMeasurement)
38 {
39     return PrivilegedAPI.sendRequest('create-analysis-task', {
40         name: name,
41         startRun: startMeasurement.id(),
42         endRun: endMeasurement.id(),
43     });
44 }
45
46 App.AnalysisTaskAdapter = DS.RESTAdapter.extend({
47     buildURL: function (type, id)
48     {
49         return '../api/analysis-tasks/' + (id ? id : '');
50     },
51 });
52
53 App.BugAdapter = DS.RESTAdapter.extend({
54     createRecord: function (store, type, record)
55     {
56         var param = {
57             task: record.get('task').get('id'),
58             bugTracker: record.get('bugTracker').get('id'),
59             number: record.get('number'),
60         };
61         return PrivilegedAPI.sendRequest('associate-bug', param).then(function (data) {
62             param['id'] = data['bugId'];
63             return {'bug': param};
64         });
65     }
66 });
67
68 App.TestGroup = App.NameLabelModel.extend({
69     task: DS.belongsTo('analysisTask'),
70     author: DS.attr('string'),
71     createdAt: DS.attr('date'),
72     buildRequests: DS.hasMany('buildRequests'),
73     rootSets: function ()
74     {
75         var rootSetIds = [];
76         this.get('buildRequests').forEach(function (request) {
77             var rootSet = request.get('rootSet');
78             if (!rootSetIds.contains(rootSet))
79                 rootSetIds.push(rootSet);
80         });
81         return rootSetIds;
82     }.property('buildRequests'),
83     _fetchChartData: function ()
84     {
85         var task = this.get('task');
86         if (!task)
87             return null;
88         var self = this;
89         return App.Manifest.fetchRunsWithPlatformAndMetric(this.store,
90             task.get('platform').get('id'), task.get('metric').get('id'), this.get('id')).then(
91             function (result) { self.set('chartData', result.data); },
92             function (error) {
93                 // FIXME: Somehow this never gets called.
94                 alert('Failed to fetch the results:' + error);
95                 return null;
96             });
97     }.observes('task', 'task.platform', 'task.metric').on('init'),
98 });
99
100 App.TestGroup.create = function (analysisTask, name, rootSets, repetitionCount)
101 {
102     var param = {
103         task: analysisTask.get('id'),
104         name: name,
105         rootSets: rootSets,
106         repetitionCount: repetitionCount,
107     };
108     return PrivilegedAPI.sendRequest('create-test-group', param).then(function (data) {
109         analysisTask.set('testGroups'); // Refetch test groups.
110         return analysisTask.store.find('testGroup', data['testGroupId']);
111     });
112 }
113
114 App.TestGroupAdapter = DS.RESTAdapter.extend({
115     buildURL: function (type, id)
116     {
117         return '../api/test-groups/' + (id ? id : '');
118     },
119 });
120
121 App.Triggerable = App.NameLabelModel.extend({
122     acceptedRepositories: DS.hasMany('repositories'),
123 });
124
125 App.TriggerableAdapter = DS.RESTAdapter.extend({
126     buildURL: function (type, id)
127     {
128         return '../api/triggerables/' + (id ? id : '');
129     },
130 });
131
132 App.AnalysisTaskSerializer = App.TestGroupSerializer = App.TriggerableSerializer = DS.RESTSerializer.extend({
133     normalizePayload: function (payload)
134     {
135         delete payload['status'];
136         return payload;
137     }
138 });
139
140 App.BuildRequest = App.Model.extend({
141     testGroup: DS.belongsTo('testGroup'),
142     order: DS.attr('number'),
143     orderLabel: function ()
144     {
145         return this.get('order') + 1;
146     }.property('order'),
147     rootSet: DS.attr('number'),
148     configLetter: function ()
149     {
150         var rootSets = this.get('testGroup').get('rootSets');
151         var index = rootSets.indexOf(this.get('rootSet'));
152         return String.fromCharCode('A'.charCodeAt(0) + index);
153     }.property('testGroup', 'testGroup.rootSets'),
154     status: DS.attr('string'),
155     statusLabel: function ()
156     {
157         switch (this.get('status')) {
158         case 'pending':
159             return 'Waiting to be scheduled';
160         case 'scheduled':
161             return 'Scheduled';
162         case 'running':
163             return 'Running';
164         case 'failed':
165             return 'Failed';
166         case 'completed':
167             return 'Finished';
168         }
169     }.property('status'),
170     url: DS.attr('string'),
171     build: DS.attr('number'),
172     _fetchMean: function ()
173     {
174         var testGroup = this.get('testGroup');
175         if (!testGroup)
176             return;
177         var chartData = testGroup.get('chartData');
178         if (!chartData)
179             return;
180
181         var point = chartData.current.findPointByBuild(this.get('build'));
182         if (!point)
183             return;
184         this.set('mean', chartData.formatter(point.value) + (chartData.unit ? ' ' + chartData.unit : ''));
185         this.set('buildNumber', point.measurement.buildNumber());
186     }.observes('build', 'testGroup', 'testGroup.chartData').on('init'),
187 });