stab-crashes/scomp.js

396 строки
13 KiB
JavaScript
Исходник Обычный вид История

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
var gDebug, gLog;
var gAnalysisPath = "../../";
var gBzBasePath = "https://bugzilla.mozilla.org/";
var gSocorroPath = "https://crash-stats.mozilla.org/";
2021-01-26 14:24:21 +03:00
var gSearchBase,
gSearch1,
gSearch2,
gLimit = 20,
gFetchLimit = 300;
var gSigData = {},
gSocorroAPIToken,
gBugInfo = {};
2021-01-26 14:24:21 +03:00
window.onload = function () {
// Socorro API token is not required, so the implementation in this code was never finished.
gSocorroAPIToken = getParameterByName("token");
// Fetch Super Search common parameters and specific parameters for query 1 and 2.
searchCommon = getParameterByName("common");
searchParam1 = getParameterByName("p1");
searchParam2 = getParameterByName("p2");
// Fetch limits from parameters.
var limit = getParameterByName("limit");
2021-01-26 14:24:21 +03:00
if (limit.match(/^(\d+)+$/) && limit >= 3 && limit <= 1000) {
gLimit = limit;
}
var flimit = getParameterByName("fetchlimit");
2021-01-26 14:24:21 +03:00
if (flimit.match(/^(\d+)+$/) && flimit >= 3 && flimit <= 1000) {
gFetchLimit = flimit;
}
gSearch1 = searchCommon + (searchParam1 ? "&" + searchParam1 : "");
gSearch2 = searchCommon + (searchParam2 ? "&" + searchParam2 : "");
gSearchBase = searchCommon;
document.getElementById("paramCommon").value = searchCommon;
document.getElementById("paramS1").value = searchParam1;
document.getElementById("paramS2").value = searchParam2;
document.getElementById("search1link").href =
2021-01-26 14:24:21 +03:00
gSocorroPath + "search/?" + gSearch1;
document.getElementById("search2link").href =
2021-01-26 14:24:21 +03:00
gSocorroPath + "search/?" + gSearch2;
2021-01-26 14:24:21 +03:00
if (gSearch1 && gSearch2 && gSearch1 != gSearch2) {
processData();
2021-01-26 14:24:21 +03:00
} else {
document.getElementById("scompParams").classList.remove("hidden");
document.getElementById("scompResult").classList.add("hidden");
}
2021-01-26 14:24:21 +03:00
document.getElementById("searchParamToggle").onclick = function () {
var paramArea = document.getElementById("scompParams");
if (paramArea.classList.contains("hidden")) {
paramArea.classList.remove("hidden");
2021-01-26 14:24:21 +03:00
} else {
paramArea.classList.add("hidden");
}
return false;
2021-01-26 14:24:21 +03:00
};
};
function processData() {
var tblBody = document.getElementById("scompTBody");
displayMessage("Requesting data for first search…");
// Only return the signature facet, no "normal" results (crash IDs).
2021-01-26 14:24:21 +03:00
fetchFile(
gSocorroPath +
"api/SuperSearch/?_facets=signature&_results_number=0" +
"&" +
gSearch1 +
"&_facets_size=" +
gFetchLimit,
"json",
function (aData1) {
if (aData1) {
var result1 = aData1.facets.signature;
var total1 = aData1.total;
displayMessage("Requesting data for second search…");
2021-01-26 14:24:21 +03:00
fetchFile(
gSocorroPath +
"api/SuperSearch/?_facets=signature&_results_number=0" +
"&" +
gSearch2 +
"&_facets_size=" +
gFetchLimit,
"json",
function (aData2) {
if (aData2) {
var result2 = aData2.facets.signature;
var total2 = aData2.total;
2021-01-26 14:24:21 +03:00
displayMessage(
"Processing " +
result1.length +
" and " +
result2.length +
" results for searches…"
);
// Assemble data into a form we can use.
for (var i = 0; i <= result1.length - 1; i++) {
gSigData[result1[i].term] = {
2021-01-26 14:24:21 +03:00
count1: result1[i].count,
pct1: (100 * result1[i].count) / total1,
signature: result1[i].term,
count2: 0,
pct2: 0,
};
}
// Assemble data into a form we can use.
for (var i = 0; i <= result2.length - 1; i++) {
if (!gSigData[result2[i].term]) {
gSigData[result2[i].term] = {
2021-01-26 14:24:21 +03:00
count1: 0,
pct1: 0,
signature: result2[i].term,
};
}
gSigData[result2[i].term].count2 = result2[i].count;
2021-01-26 14:24:21 +03:00
gSigData[result2[i].term].pct2 =
(100 * result2[i].count) / total2;
}
// Calculate the differences.
for (var signature in gSigData) {
2021-01-26 14:24:21 +03:00
gSigData[signature].pctcmp =
gSigData[signature].pct2 - gSigData[signature].pct1;
}
2021-01-26 14:24:21 +03:00
setTimeout(function () {
buildDataTable();
fetchBugs();
}, 0);
2021-01-26 14:24:21 +03:00
} else {
displayMessage("ERROR - couldn't fetch search #2!");
}
}
);
2021-01-26 14:24:21 +03:00
} else {
displayMessage("ERROR - couldn't fetch search #1!");
}
}
);
}
function fetchBugs() {
for (var signature in gSigData) {
gSigData[signature].bugs = [];
if (document.getElementById("sdata_" + encodeURIComponent(signature))) {
// Only actually fetch if this is actually shown.
fetchBugsForSignature(signature);
}
}
}
function fetchBugsForSignature(aSignature) {
gSigData[aSignature].bugs = [];
2021-01-26 14:24:21 +03:00
fetchFile(
gSocorroPath + "api/Bugs/?signatures=" + encodeURIComponent(aSignature),
"json",
function (aSignature, aData) {
if (aData) {
for (var i = 0; i <= aData.hits.length - 1; i++) {
if (aData.hits[i].signature == aSignature) {
gSigData[aSignature].bugs.push(aData.hits[i].id);
}
}
buildBugsField(aSignature);
2021-01-26 14:24:21 +03:00
} else {
console.log("ERROR - couldn't find bug data for " + aSignature + "!");
}
}.bind(undefined, aSignature) // Prepend signature to the argument list.
);
}
function buildDataTable() {
var msgRow = document.getElementById("message_row");
if (msgRow) {
msgRow.parentNode.removeChild(msgRow);
}
// Header
2021-01-26 14:24:21 +03:00
var trow = document
.getElementById("scompTHeader")
.appendChild(document.createElement("tr"));
var cell = trow.appendChild(document.createElement("th"));
cell.textContent = "#";
cell.setAttribute("title", "Rank");
var cell = trow.appendChild(document.createElement("th"));
cell.textContent = "Signature";
var cell = trow.appendChild(document.createElement("th"));
cell.textContent = "Bugs";
var cell = trow.appendChild(document.createElement("th"));
cell.textContent = "#1";
var cell = trow.appendChild(document.createElement("th"));
cell.textContent = "#2";
var cell = trow.appendChild(document.createElement("th"));
cell.textContent = "+/-";
// Body
var tblBody = document.getElementById("scompTBody");
2021-01-26 14:24:21 +03:00
var sigSorted = Object.keys(gSigData).sort(function (a, b) {
return Math.abs(gSigData[b].pctcmp) - Math.abs(gSigData[a].pctcmp);
});
var listNum = Math.min(gLimit, sigSorted.length);
for (var i = 0; i <= listNum - 1; i++) {
signature = sigSorted[i];
var trow = tblBody.appendChild(document.createElement("tr"));
trow.setAttribute("id", "sdata_" + encodeURIComponent(signature));
var cell = trow.appendChild(document.createElement("td"));
cell.textContent = i + 1;
cell.classList.add("rank");
var cell = trow.appendChild(document.createElement("td"));
cell.classList.add("sig");
var link = cell.appendChild(document.createElement("a"));
2021-01-26 14:24:21 +03:00
link.setAttribute(
"href",
gSocorroPath +
"signature?" +
gSearchBase +
"&signature=" +
encodeURIComponent(signature)
);
link.textContent = signature;
var cell = trow.appendChild(document.createElement("td"));
cell.classList.add("bugs");
var cell = trow.appendChild(document.createElement("td"));
cell.classList.add("pct");
cell.textContent = gSigData[signature].pct1.toFixed(1) + "%";
cell.setAttribute("title", gSigData[signature].count1);
if (gSigData[signature].count1 == 0) {
cell.classList.add("zero");
}
var cell = trow.appendChild(document.createElement("td"));
cell.classList.add("pct");
cell.textContent = gSigData[signature].pct2.toFixed(1) + "%";
cell.setAttribute("title", gSigData[signature].count2);
if (gSigData[signature].count2 == 0) {
cell.classList.add("zero");
}
var cell = trow.appendChild(document.createElement("td"));
cell.classList.add("pctcmp");
if (gSigData[signature].pctcmp > 0) {
cell.classList.add("plus");
2021-01-26 14:24:21 +03:00
} else if (gSigData[signature].pctcmp < 0) {
cell.classList.add("minus");
}
2021-01-26 14:24:21 +03:00
cell.textContent =
(gSigData[signature].pctcmp > 0 ? "+" : "") +
gSigData[signature].pctcmp.toFixed(1) +
"%";
}
}
function buildBugsField(aSignature) {
2021-01-26 14:24:21 +03:00
var sigRow = document.getElementById(
"sdata_" + encodeURIComponent(aSignature)
);
var bugsField = sigRow.querySelector(".bugs");
for (var i = 0; i <= gSigData[aSignature].bugs.length - 1; i++) {
2021-01-26 14:24:21 +03:00
if (i > 0) {
// Add spaces when we have multiple bugs.
bugsField.appendChild(document.createTextNode(" "));
}
var link = bugsField.appendChild(document.createElement("a"));
link.dataset["bugid"] = gSigData[aSignature].bugs[i];
2021-01-26 14:24:21 +03:00
link.setAttribute(
"href",
gBzBasePath + "show_bug.cgi?id=" + gSigData[aSignature].bugs[i]
);
link.textContent = gSigData[aSignature].bugs[i];
// Add Bugzilla data.
if (gBugInfo[gSigData[aSignature].bugs[i]]) {
beautifyBugzillaLink(link);
2021-01-26 14:24:21 +03:00
} else {
fetchFile(
gBzBasePath +
"rest/bug/" +
gSigData[aSignature].bugs[i] +
"?include_fields=id,summary,status,resolution",
"json",
function (aLink, aData) {
if (aData && aData.bugs && aData.bugs.length) {
gBugInfo[aData.bugs[0].id] = aData.bugs[0];
beautifyBugzillaLink(aLink);
2021-01-26 14:24:21 +03:00
} else if (aData && aData.error) {
// On error, create fake bug info.
2021-01-26 14:24:21 +03:00
gBugInfo[aLink.dataset["bugid"]] = {
status: "ERROR",
resolution: "",
summary: aData.message,
};
beautifyBugzillaLink(aLink);
2021-01-26 14:24:21 +03:00
} else {
console.log(
"ERROR - couldn't find info for bug " +
aLink.dataset["bugid"] +
"!"
);
}
}.bind(undefined, link), // Prepend link to the argument list.
true // Accept 401 responses and still return them as JSON.
);
}
}
}
function beautifyBugzillaLink(aLink) {
if (gBugInfo[aLink.dataset["bugid"]]) {
aLink.dataset["status"] = gBugInfo[aLink.dataset["bugid"]].status;
aLink.dataset["resolution"] = gBugInfo[aLink.dataset["bugid"]].resolution;
2021-01-26 14:24:21 +03:00
aLink.title =
gBugInfo[aLink.dataset["bugid"]].status +
" " +
gBugInfo[aLink.dataset["bugid"]].resolution +
" - " +
gBugInfo[aLink.dataset["bugid"]].summary;
} else {
console.log(
"ERROR - info for bug " +
aLink.dataset["bugid"] +
" should exist but doesn't!"
);
}
}
function displayMessage(aErrorMessage) {
var msgRow = document.getElementById("message_row");
if (msgRow) {
msgRow.parentNode.removeChild(msgRow);
}
2021-01-26 14:24:21 +03:00
var trow = document
.getElementById("scompTBody")
.appendChild(document.createElement("tr"));
trow.setAttribute("id", "message_row");
2021-01-26 14:24:21 +03:00
var cell = trow.appendChild(document.createElement("td"));
cell.textContent = aErrorMessage;
return cell;
}
function fetchFile(aURL, aFormat, aCallback, aAccept401) {
var XHR = new XMLHttpRequest();
2021-01-26 14:24:21 +03:00
XHR.onreadystatechange = function () {
if (XHR.readyState == 4) {
/*
gLog.appendChild(document.createElement("li"))
.appendChild(document.createTextNode(aURL + " - " + XHR.status +
" " + XHR.statusText));*/
}
2021-01-26 14:24:21 +03:00
if (
XHR.readyState == 4 &&
(XHR.status == 200 || (XHR.status == 401 && aAccept401))
) {
// so far so good
2021-01-26 14:24:21 +03:00
if (
XHR.responseXML != null &&
aFormat == "xml" &&
XHR.responseXML.getElementById("test").firstChild.data
)
aCallback(aXHR.responseXML.getElementById("test").firstChild.data);
else if (XHR.responseText != null && aFormat == "json")
aCallback(JSON.parse(XHR.responseText));
2021-01-26 14:24:21 +03:00
else aCallback(XHR.responseText);
} else if (XHR.readyState == 4 && XHR.status != 200) {
// fetched the wrong page or network error...
console.log("ERROR: XHR status " + XHR.status + " - " + aURL);
aCallback(null);
}
};
XHR.open("GET", aURL);
if (gSocorroAPIToken) {
// XXX: Should work but doesn't yet! We'll need to figure this out.
// Use this path when we have a token so bug 1143424 can be tested.
XHR.setRequestHeader("Auth-Token", gSocorroAPIToken);
}
2021-01-26 14:24:21 +03:00
if (aFormat == "json") {
XHR.setRequestHeader("Accept", "application/json");
} else if (aFormat == "xml") {
XHR.setRequestHeader("Accept", "application/xml");
}
try {
XHR.send();
2021-01-26 14:24:21 +03:00
} catch (e) {
console.log("ERROR: XHR send - " + e + " - " + aURL);
aCallback(null);
}
}
function getParameterByName(aName) {
// from http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
name = aName.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
2021-01-26 14:24:21 +03:00
results = regex.exec(location.search);
return results === null
? ""
: decodeURIComponent(results[1].replace(/\+/g, " "));
}