2016-08-28 21:17:04 +03:00
|
|
|
var correlations = (() => {
|
2016-08-28 20:31:52 +03:00
|
|
|
let correlationData;
|
2016-08-10 15:53:02 +03:00
|
|
|
|
2016-09-18 00:47:12 +03:00
|
|
|
function sha1(str) {
|
|
|
|
return crypto.subtle.digest('SHA-1', new TextEncoder('utf-8').encode(str))
|
|
|
|
.then(hash => hex(hash));
|
|
|
|
}
|
|
|
|
|
|
|
|
function hex(buffer) {
|
|
|
|
let hexCodes = [];
|
|
|
|
let view = new DataView(buffer);
|
|
|
|
|
|
|
|
for (let i = 0; i < view.byteLength; i += 4) {
|
|
|
|
// Using getUint32 reduces the number of iterations needed (we process 4 bytes each time).
|
|
|
|
let value = view.getUint32(i);
|
|
|
|
// toString(16) will give the hex representation of the number without padding.
|
|
|
|
let stringValue = value.toString(16);
|
|
|
|
// We use concatenation and slice for padding.
|
|
|
|
let padding = '00000000';
|
|
|
|
let paddedValue = (padding + stringValue).slice(-padding.length);
|
|
|
|
hexCodes.push(paddedValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Join all the hex strings into one
|
|
|
|
return hexCodes.join('');
|
|
|
|
}
|
|
|
|
|
2016-09-24 20:06:48 +03:00
|
|
|
function loadChannelsData() {
|
2016-08-28 20:31:52 +03:00
|
|
|
if (correlationData) {
|
2016-09-24 20:06:48 +03:00
|
|
|
return Promise.resolve();
|
2016-08-28 20:31:52 +03:00
|
|
|
}
|
2016-08-10 15:53:02 +03:00
|
|
|
|
2016-09-24 20:06:48 +03:00
|
|
|
return fetch('https://analysis-output.telemetry.mozilla.org/top-signatures-correlations/data/all.json.gz')
|
|
|
|
.then(response => response.json())
|
|
|
|
.then(totals => {
|
|
|
|
correlationData = {
|
|
|
|
'date': totals['date'],
|
|
|
|
};
|
|
|
|
|
|
|
|
for (let ch of ['release', 'beta', 'aurora', 'nightly']) {
|
|
|
|
correlationData[ch] = {
|
|
|
|
'total': totals[ch],
|
|
|
|
'signatures': {},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function loadCorrelationData(signature, channel) {
|
|
|
|
return loadChannelsData()
|
2016-09-18 00:47:12 +03:00
|
|
|
.then(() => {
|
|
|
|
if (signature in correlationData[channel]['signatures']) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sha1(signature)
|
|
|
|
.then(sha1signature => fetch('https://analysis-output.telemetry.mozilla.org/top-signatures-correlations/data/' + channel + '/' + sha1signature + '.json.gz'))
|
|
|
|
.then(response => response.json())
|
|
|
|
.then(data => {
|
|
|
|
correlationData[channel]['signatures'][signature] = data;
|
|
|
|
})
|
|
|
|
.catch(() => {});
|
|
|
|
})
|
|
|
|
.then(() => correlationData);
|
2016-08-28 20:31:52 +03:00
|
|
|
}
|
2016-08-10 15:53:02 +03:00
|
|
|
|
2016-09-24 20:06:48 +03:00
|
|
|
function getAnalysisDate() {
|
|
|
|
return loadChannelsData()
|
|
|
|
.then(() => correlationData['date']);
|
|
|
|
}
|
|
|
|
|
2016-08-28 20:31:52 +03:00
|
|
|
function itemToLabel(item) {
|
|
|
|
return Object.getOwnPropertyNames(item)
|
2016-08-28 21:17:04 +03:00
|
|
|
.map(key => key + ' = ' + item[key])
|
2016-08-28 20:31:52 +03:00
|
|
|
.join(' ∧ ');
|
|
|
|
}
|
2016-08-10 15:53:02 +03:00
|
|
|
|
2016-08-31 21:17:26 +03:00
|
|
|
function toPercentage(num) {
|
|
|
|
let result = (num * 100).toFixed(2);
|
|
|
|
|
|
|
|
if (result == '100.00') {
|
|
|
|
return '100.0'
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result.substring(0, result.indexOf('.')).length == 1) {
|
|
|
|
return '0' + result;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-09-12 15:50:22 +03:00
|
|
|
function sortCorrelationData(correlationData, total_a, total_b) {
|
2016-09-05 13:54:51 +03:00
|
|
|
return correlationData
|
2016-09-12 15:37:42 +03:00
|
|
|
.sort((a, b) => {
|
|
|
|
let rule_a_len = Object.keys(a.item).length;
|
|
|
|
let rule_b_len = Object.keys(b.item).length;
|
|
|
|
|
|
|
|
if (rule_a_len < rule_b_len) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rule_a_len > rule_b_len) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-09-12 15:50:22 +03:00
|
|
|
return Math.abs(b.count_b / total_b - b.count_a / total_a) - Math.abs(a.count_b / total_b - a.count_a / total_a);
|
2016-09-12 15:38:02 +03:00
|
|
|
});
|
2016-09-05 13:54:51 +03:00
|
|
|
}
|
|
|
|
|
2016-08-28 20:31:52 +03:00
|
|
|
function text(textElem, signature, channel) {
|
2016-09-18 00:47:12 +03:00
|
|
|
loadCorrelationData(signature, channel)
|
2016-08-28 21:17:04 +03:00
|
|
|
.then(data => {
|
2016-08-29 03:19:21 +03:00
|
|
|
textElem.textContent = '';
|
|
|
|
|
2016-09-18 00:47:12 +03:00
|
|
|
if (!(signature in data[channel]['signatures']) || !data[channel]['signatures'][signature]['results']) {
|
2016-09-22 23:17:07 +03:00
|
|
|
textElem.textContent = 'No correlation data was generated for the signature "' + signature + '" on the ' + channel + ' channel.'
|
2016-08-29 03:24:46 +03:00
|
|
|
return;
|
|
|
|
}
|
2016-08-10 15:53:02 +03:00
|
|
|
|
2016-09-18 00:47:12 +03:00
|
|
|
let correlationData = data[channel]['signatures'][signature]['results'];
|
|
|
|
|
2016-09-12 15:36:56 +03:00
|
|
|
let total_a = data[channel].total;
|
|
|
|
let total_b = data[channel]['signatures'][signature].total;
|
|
|
|
|
2016-09-12 15:55:46 +03:00
|
|
|
textElem.textContent = sortCorrelationData(correlationData, total_a, total_b)
|
2016-08-28 21:17:04 +03:00
|
|
|
.reduce((prev, cur) =>
|
2016-09-12 15:36:56 +03:00
|
|
|
prev + '(' + toPercentage(cur.count_b / total_b) + '% in signature vs ' + toPercentage(cur.count_a / total_a) + '% overall) ' + itemToLabel(cur.item) + '\n'
|
|
|
|
, '');
|
2016-08-28 20:31:52 +03:00
|
|
|
});
|
2016-08-10 17:53:24 +03:00
|
|
|
}
|
|
|
|
|
2016-09-12 15:55:46 +03:00
|
|
|
function graph(svgElem, signature, channel) {
|
2016-09-18 00:47:12 +03:00
|
|
|
loadCorrelationData(signature, channel)
|
2016-08-28 21:17:04 +03:00
|
|
|
.then(data => {
|
2016-08-28 20:31:52 +03:00
|
|
|
d3.select(svgElem).selectAll('*').remove();
|
|
|
|
|
2016-09-18 00:47:12 +03:00
|
|
|
if (!(signature in data[channel]['signatures']) || !data[channel]['signatures'][signature]['results']) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-09-12 15:50:22 +03:00
|
|
|
let total_a = data[channel].total;
|
|
|
|
let total_b = data[channel]['signatures'][signature].total;
|
|
|
|
|
2016-09-19 17:19:08 +03:00
|
|
|
let correlationData = data[channel]['signatures'][signature]['results']
|
|
|
|
.filter(elem => Object.keys(elem.item).length <= 1);
|
2016-09-12 15:50:22 +03:00
|
|
|
correlationData = sortCorrelationData(correlationData, total_a, total_b);
|
2016-09-05 13:54:51 +03:00
|
|
|
correlationData.reverse();
|
2016-08-28 20:31:52 +03:00
|
|
|
|
|
|
|
let margin = { top: 20, right: 300, bottom: 30, left: 300 };
|
2016-09-12 15:55:46 +03:00
|
|
|
let width = svgElem.getAttribute('width') - margin.left - margin.right;
|
|
|
|
let height = svgElem.getAttribute('height') - margin.top - margin.bottom;
|
2016-08-28 20:31:52 +03:00
|
|
|
|
|
|
|
let y0 = d3.scale.ordinal()
|
|
|
|
.rangeRoundBands([height, 0], .2, 0.5);
|
|
|
|
|
|
|
|
let y1 = d3.scale.ordinal();
|
|
|
|
|
|
|
|
let x = d3.scale.linear()
|
|
|
|
.range([0, width]);
|
|
|
|
|
|
|
|
let color = d3.scale.ordinal()
|
|
|
|
.range(['blue', 'red']);
|
|
|
|
|
|
|
|
let xAxis = d3.svg.axis()
|
|
|
|
.scale(x)
|
|
|
|
.tickSize(-height)
|
|
|
|
.orient('bottom');
|
|
|
|
|
|
|
|
let yAxis = d3.svg.axis()
|
|
|
|
.scale(y0)
|
|
|
|
.orient('left');
|
|
|
|
|
|
|
|
let svg = d3.select(svgElem)
|
|
|
|
.attr('width', width + margin.left + margin.right)
|
|
|
|
.attr('height', height + margin.top + margin.bottom)
|
|
|
|
.append('g')
|
|
|
|
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
|
|
|
|
|
2016-08-31 21:10:11 +03:00
|
|
|
let options = [signature, 'Overall'];
|
2016-08-28 20:31:52 +03:00
|
|
|
|
2016-08-28 21:17:04 +03:00
|
|
|
correlationData.forEach(d => {
|
2016-08-28 20:31:52 +03:00
|
|
|
d.values = [
|
2016-09-12 15:50:22 +03:00
|
|
|
{ name: 'Overall', value: d.count_a / total_a },
|
|
|
|
{ name: signature, value: d.count_b / total_b },
|
2016-08-28 20:31:52 +03:00
|
|
|
]
|
|
|
|
});
|
|
|
|
|
2016-08-28 21:17:04 +03:00
|
|
|
y0.domain(correlationData.map(d => itemToLabel(d.item)));
|
2016-08-28 20:31:52 +03:00
|
|
|
y1.domain(options).rangeRoundBands([0, y0.rangeBand()]);
|
|
|
|
x.domain([0, 100]);
|
|
|
|
|
|
|
|
svg.append('g')
|
|
|
|
.attr('class', 'x axis')
|
|
|
|
.attr('transform', 'translate(0,' + height + ')')
|
|
|
|
.call(xAxis);
|
|
|
|
|
|
|
|
svg.append('g')
|
|
|
|
.attr('class', 'y axis')
|
|
|
|
.call(yAxis);
|
|
|
|
|
|
|
|
let bar = svg.selectAll('.bar')
|
|
|
|
.data(correlationData)
|
|
|
|
.enter().append('g')
|
|
|
|
.attr('class', 'rect')
|
2016-08-28 21:17:04 +03:00
|
|
|
.attr('transform', d => 'translate( 0,'+ y0(itemToLabel(d.item)) +')');
|
2016-08-28 20:31:52 +03:00
|
|
|
|
|
|
|
let bar_enter = bar.selectAll('rect')
|
2016-08-28 21:17:04 +03:00
|
|
|
.data(d => d.values)
|
2016-08-28 20:31:52 +03:00
|
|
|
.enter()
|
|
|
|
|
|
|
|
bar_enter.append('rect')
|
|
|
|
.attr('height', y1.rangeBand())
|
2016-08-28 21:17:04 +03:00
|
|
|
.attr('y', d => y1(d.name))
|
|
|
|
.attr('x', d => 0)
|
|
|
|
.attr('value', d => d.name)
|
|
|
|
.attr('width', d => x((d.value * 100).toFixed(2)))
|
|
|
|
.style('fill', d => color(d.name));
|
2016-08-28 20:31:52 +03:00
|
|
|
|
|
|
|
bar_enter.append('text')
|
2016-08-28 21:17:04 +03:00
|
|
|
.attr('x', d => x((d.value * 100).toFixed(2)) + 5)
|
|
|
|
.attr('y', d => y1(d.name) + (y1.rangeBand()/2))
|
2016-08-28 20:31:52 +03:00
|
|
|
.attr('dy', '.35em')
|
2016-08-28 21:17:04 +03:00
|
|
|
.text(d => (d.value * 100).toFixed(2));
|
2016-08-28 20:31:52 +03:00
|
|
|
|
|
|
|
let legend = svg.selectAll('.legend')
|
|
|
|
.data(options.slice())
|
|
|
|
.enter().append('g')
|
|
|
|
.attr('class', 'legend')
|
2016-08-28 21:17:04 +03:00
|
|
|
.attr('transform', (d, i) => 'translate(' + margin.right + ',' + i * 20 + ')');
|
2016-08-28 20:31:52 +03:00
|
|
|
|
|
|
|
legend.append('rect')
|
|
|
|
.attr('x', width - 18)
|
|
|
|
.attr('width', 18)
|
|
|
|
.attr('height', 18)
|
|
|
|
.style('fill', color);
|
|
|
|
|
|
|
|
legend.append('text')
|
|
|
|
.attr('x', width - 24)
|
|
|
|
.attr('y', 9)
|
|
|
|
.attr('dy', '.35em')
|
|
|
|
.style('text-anchor', 'end')
|
2016-08-28 21:17:04 +03:00
|
|
|
.text(d => d);
|
2016-08-28 20:31:52 +03:00
|
|
|
});
|
|
|
|
}
|
2016-08-10 17:53:24 +03:00
|
|
|
|
2016-08-28 20:31:52 +03:00
|
|
|
return {
|
2016-09-24 20:06:48 +03:00
|
|
|
getAnalysisDate: getAnalysisDate,
|
2016-08-28 20:31:52 +03:00
|
|
|
text: text,
|
|
|
|
graph: graph,
|
|
|
|
};
|
|
|
|
})();
|