ad02b379c74df68750474b72468569943353d64a
[WebKit-https.git] / Websites / perf.webkit.org / public / v2 / data.js
1 // We don't use DS.Model for these object types because we can't afford to process millions of them.
2
3 FetchCommitsForTimeRange = function (repository, from, to)
4 {
5     var url = '../api/commits/' + repository.get('id') + '/' + from + '-' + to;
6     console.log('Fetching ' + url)
7     return new Ember.RSVP.Promise(function (resolve, reject) {
8         $.getJSON(url, function (data) {
9             if (data.status != 'OK') {
10                 reject(data.status);
11                 return;
12             }
13             resolve(data.commits);
14         }).fail(function (xhr, status, error) {
15             reject(xhr.status + (error ? ', ' + error : ''));
16         })
17     });
18 }
19
20 function Measurement(rawData)
21 {
22     this._raw = rawData;
23
24     var latestTime = -1;
25     var revisions = this._raw['revisions'];
26     // FIXME: Fix this in the server side.
27     if (Array.isArray(revisions))
28         revisions = {};
29     this._raw['revisions'] = revisions;
30
31     for (var repositoryName in revisions) {
32         var commitTimeOrUndefined = revisions[repositoryName][1]; // e.g. ["162190", 1389945046000]
33         if (latestTime < commitTimeOrUndefined)
34             latestTime = commitTimeOrUndefined;
35     }
36     this._latestCommitTime = latestTime !== -1 ? new Date(latestTime) : null;
37     this._buildTime = new Date(this._raw['buildTime']);
38     this._confidenceInterval = undefined;
39     this._formattedRevisions = undefined;
40 }
41
42 Measurement.prototype.formattedRevisions = function (previousMeasurement)
43 {
44     var revisions = this._raw['revisions'];
45     var previousRevisions = previousMeasurement ? previousMeasurement._raw['revisions'] : null;
46     var formattedRevisions = {};
47     for (var repositoryName in revisions) {
48         var currentRevision = revisions[repositoryName][0];
49         var commitTimeInPOSIX = revisions[repositoryName][1];
50
51         var previousRevision = previousRevisions ? previousRevisions[repositoryName][0] : null;
52
53         var formatttedRevision = this._formatRevisionRange(previousRevision, currentRevision);
54         if (commitTimeInPOSIX)
55             formatttedRevision['commitTime'] = new Date(commitTimeInPOSIX);
56         formattedRevisions[repositoryName] = formatttedRevision;
57     }
58
59     return formattedRevisions;
60 }
61
62 Measurement.prototype._formatRevisionRange = function (previousRevision, currentRevision)
63 {
64     var revisionChanged = false;
65     if (previousRevision == currentRevision)
66         previousRevision = null;
67     else
68         revisionChanged = true;
69
70     var revisionPrefix = '';
71     var revisionDelimiter = '-';
72     var label = '';
73     if (parseInt(currentRevision) == currentRevision) { // e.g. r12345.
74         currentRevision = parseInt(currentRevision);
75         revisionPrefix = 'r';
76         if (previousRevision)
77             previousRevision = (parseInt(previousRevision) + 1);
78     } else if (currentRevision.indexOf(' ') >= 0) // e.g. 10.9 13C64.
79         revisionDelimiter = ' - ';
80     else if (currentRevision.length == 40) { // e.g. git hash
81         formattedCurrentHash = currentRevision.substring(0, 8);
82         if (previousRevision)
83             label = previousRevision.substring(0, 8) + '..' + formattedCurrentHash;
84         else
85             label = 'At ' + formattedCurrentHash;
86     }
87
88     if (!label) {
89         if (previousRevision)
90             label = revisionPrefix + previousRevision + revisionDelimiter + revisionPrefix + currentRevision;
91         else
92             label = 'At ' + revisionPrefix + currentRevision;
93     }
94
95     return {
96         label: label,
97         previousRevision: previousRevision,
98         currentRevision: currentRevision,
99         revisionChanged: revisionChanged
100     };
101 }
102
103 Measurement.prototype.id = function ()
104 {
105     return this._raw['id'];
106 }
107
108 Measurement.prototype.mean = function()
109 {
110     return this._raw['mean'];
111 }
112
113 Measurement.prototype.confidenceInterval = function()
114 {
115     if (this._confidenceInterval === undefined) {
116         var delta = Statistics.confidenceIntervalDelta(0.95, this._raw["iterationCount"], this._raw["sum"], this._raw["squareSum"]);
117         var mean = this.mean();
118         this._confidenceInterval = isNaN(delta) ? null : [mean - delta, mean + delta];
119     }
120     return this._confidenceInterval;
121 }
122
123 Measurement.prototype.latestCommitTime = function()
124 {
125     return this._latestCommitTime || this._buildTime;
126 }
127
128 Measurement.prototype.buildNumber = function ()
129 {
130     return this._raw['buildNumber'];
131 }
132
133 Measurement.prototype.builderId = function ()
134 {
135     return this._raw['builder'];
136 }
137
138 Measurement.prototype.buildTime = function()
139 {
140     return this._buildTime;
141 }
142
143 Measurement.prototype.formattedBuildTime = function ()
144 {
145     return Measurement._formatDate(this.buildTime());
146 }
147
148 Measurement._formatDate = function (date)
149 {
150     return date.toISOString().replace('T', ' ').replace(/\.\d+Z$/, '');
151 }
152
153 function RunsData(rawData)
154 {
155     this._measurements = rawData.map(function (run) { return new Measurement(run); });
156 }
157
158 RunsData.prototype.count = function ()
159 {
160     return this._measurements.length;
161 }
162
163 RunsData.prototype.timeSeriesByCommitTime = function ()
164 {
165     return new TimeSeries(this._measurements.map(function (measurement) {
166         var confidenceInterval = measurement.confidenceInterval();
167         return {
168             measurement: measurement,
169             time: measurement.latestCommitTime(),
170             value: measurement.mean(),
171             interval: measurement.confidenceInterval(),
172         };
173     }));
174 }
175
176 RunsData.prototype.timeSeriesByBuildTime = function ()
177 {
178     return new TimeSeries(this._measurements.map(function (measurement) {
179         return {
180             measurement: measurement,
181             time: measurement.buildTime(),
182             value: measurement.mean(),
183             interval: measurement.confidenceInterval(),
184         };
185     }));
186 }
187
188 // FIXME: We need to devise a way to fetch runs in multiple chunks so that
189 // we don't have to fetch the entire time series to just show the last 3 days.
190 RunsData.fetchRuns = function (platformId, metricId)
191 {
192     var filename = platformId + '-' + metricId + '.json';
193
194     return new Ember.RSVP.Promise(function (resolve, reject) {
195         $.getJSON('../api/runs/' + filename, function (data) {
196             if (data.status != 'OK') {
197                 reject(data.status);
198                 return;
199             }
200             delete data.status;
201
202             for (var config in data)
203                 data[config] = new RunsData(data[config]);
204
205             resolve(data);
206         }).fail(function (xhr, status, error) {
207             reject(xhr.status + (error ? ', ' + error : ''));
208         })
209     });
210 }
211
212 function TimeSeries(series)
213 {
214     this._series = series.sort(function (a, b) { return a.time - b.time; });
215     var self = this;
216     var min = undefined;
217     var max = undefined;
218     this._series.forEach(function (point, index) {
219         point.series = self;
220         point.seriesIndex = index;
221         if (min === undefined || min > point.value)
222             min = point.value;
223         if (max === undefined || max < point.value)
224             max = point.value;
225     });
226     this._min = min;
227     this._max = max;
228 }
229
230 TimeSeries.prototype.minMaxForTimeRange = function (startTime, endTime)
231 {
232     var data = this._series;
233     var i = 0;
234     if (startTime !== undefined) {
235         for (i = 0; i < data.length; i++) {
236             var point = data[i];
237             if (point.time >= startTime)
238                 break;
239         }
240         if (i)
241             i--;
242     }
243     var min = Number.MAX_VALUE;
244     var max = Number.MIN_VALUE;
245     for (; i < data.length; i++) {
246         var point = data[i];
247         var currentMin = point.interval ? point.interval[0] : point.value;
248         var currentMax = point.interval ? point.interval[1] : point.value;
249
250         if (currentMin < min)
251             min = currentMin;
252         if (currentMax > max)
253             max = currentMax;
254
255         if (point.time >= endTime)
256             break;
257     }
258     return [min, max];
259 }
260
261 TimeSeries.prototype.series = function () { return this._series; }
262
263 TimeSeries.prototype.previousPoint = function (point)
264 {
265     if (!point.seriesIndex)
266         return null;
267     return this._series[point.seriesIndex - 1];
268 }