Bug 1425955 - Fix a couple of minor bugs, make some of the wording clearer, and add watershed detail to the app update out of date dashboard
This commit is contained in:
Родитель
0a21a5621b
Коммит
eff8b91f10
|
@ -1,513 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Update orphaning dashboard</title>
|
||||
<meta charset="utf-8">
|
||||
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"> </script>
|
||||
<script type="text/javascript" src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
|
||||
<script type="text/javascript" src="https://telemetry.mozilla.org/new-pipeline/lib/d3pie.min.js"></script>
|
||||
<style>
|
||||
|
||||
body {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
position: relative;
|
||||
width: 960px;
|
||||
margin: 1em auto 4em;
|
||||
}
|
||||
.update-disabled-chart div {
|
||||
font: 10px sans-serif;
|
||||
background-color: steelblue;
|
||||
text-align: right;
|
||||
padding: 3px;
|
||||
margin: 1px;
|
||||
color: white;
|
||||
}
|
||||
.update-check-code-notify-chart div {
|
||||
font: 10px sans-serif;
|
||||
background-color: steelblue;
|
||||
text-align: right;
|
||||
padding: 3px;
|
||||
margin: 1px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#update-ping-code-description {
|
||||
height: 50px;
|
||||
border-radius: 5px;
|
||||
border: 2px solid grey;
|
||||
padding: 20px;
|
||||
}
|
||||
.axis text {
|
||||
font: 10px sans-serif;
|
||||
}
|
||||
|
||||
.axis path,
|
||||
.axis line {
|
||||
fill: none;
|
||||
stroke: #000;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.badbar {
|
||||
fill: #d10000;
|
||||
fill-opacity: .8;
|
||||
}
|
||||
|
||||
.badbar:hover {
|
||||
display: block;
|
||||
fill-opacity: 1;
|
||||
}
|
||||
|
||||
.okbar {
|
||||
fill: #0065d1;
|
||||
fill-opacity: .8;
|
||||
}
|
||||
|
||||
.okbar:hover {
|
||||
fill-opacity: 1;
|
||||
}
|
||||
|
||||
.x.axis path {
|
||||
display: none;
|
||||
}
|
||||
|
||||
label {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Update orphaning dashboard</h1>
|
||||
Select the desired weekly report:<br/>
|
||||
<select id="weekly-dropdown" onchange="populateDashboard()">
|
||||
</select>
|
||||
<br><br>
|
||||
<div id="out-of-date-of-concern-summary"></div>
|
||||
<div id="out-of-date-updates-disabled-summary"></div>
|
||||
<div id="up-to-date-summary"></div><br/>
|
||||
<br/>
|
||||
<div id="distribution-chart"></div><br/><br/>
|
||||
The users who are out of date with updates enabled encounter the following update ping codes:<br/>
|
||||
<br/>
|
||||
<div id="update-ping-code-description">Hover over a bar below to see a description of the value.</div><br/>
|
||||
<div id="update-ping-codes-chart-sorter"><input type="checkbox"> Sort values</div>
|
||||
<div id="update-ping-codes-chart"></div><br/><br/>
|
||||
<div id="version-distribution-chart-title">Orphaned users are distributed across the following Firefox versions:<br/><br/></div>
|
||||
<div id="version-distribution-chart"></div>
|
||||
|
||||
<br/><hr>
|
||||
This dashboard is maintained by <a href="mailto:mhowell@mozilla.com">:mhowell</a>.<br/>
|
||||
The Spark script for the weekly reports is scheduled to run every Wednesday and can be found <a href="https://github.com/mozilla-services/data-pipeline/tree/master/reports/update-orphaning/Update orphaning analysis using longitudinal dataset.ipynb">here</a>.
|
||||
|
||||
<script>
|
||||
var dataUrl = "https://s3-us-west-2.amazonaws.com/telemetry-public-analysis-2/data/spohl@mozilla.com/Update+Orphaning+View/";
|
||||
var branch = "";
|
||||
var today = new Date();
|
||||
var todaysWeekday = today.getDay();
|
||||
var daysToLastWed = 0;
|
||||
|
||||
if (todaysWeekday == 0) {
|
||||
daysToLastWed = 11;
|
||||
} else {
|
||||
daysToLastWed = 4 + todaysWeekday;
|
||||
}
|
||||
var lastWedDate = new Date();
|
||||
lastWedDate.setDate(today.getDate() - daysToLastWed);
|
||||
|
||||
var year = lastWedDate.getFullYear();
|
||||
var month = lastWedDate.getMonth() + 1;
|
||||
if (month < 10) {
|
||||
month = "0" + month;
|
||||
}
|
||||
var date = lastWedDate.getDate();
|
||||
if (date < 10) {
|
||||
date = "0" + date;
|
||||
}
|
||||
var friAfterLastWedDate = new Date();
|
||||
friAfterLastWedDate.setDate(lastWedDate.getDate() + 2);
|
||||
var lastWedDataFile = [year, month, date, ".json"].join("");
|
||||
var startDate = new Date();
|
||||
startDate.setDate(2);
|
||||
startDate.setMonth(2);
|
||||
startDate.setFullYear(2016);
|
||||
var tempWedDate = lastWedDate;
|
||||
var dateDropdown = document.getElementById("weekly-dropdown");
|
||||
|
||||
while (tempWedDate >= startDate) {
|
||||
var tempYear = tempWedDate.getFullYear();
|
||||
var tempMonth = tempWedDate.getMonth() + 1;
|
||||
if (tempMonth < 10) {
|
||||
tempMonth = "0" + tempMonth;
|
||||
}
|
||||
var tempDate = tempWedDate.getDate();
|
||||
if (tempDate < 10) {
|
||||
tempDate = "0" + tempDate;
|
||||
}
|
||||
dateDropdown.innerHTML += "<option value=\"" + tempYear + tempMonth + tempDate + ".json\">" + tempMonth + "/" + tempDate + "/" + tempYear + "</option>";
|
||||
tempWedDate.setDate(tempWedDate.getDate() - 7);
|
||||
}
|
||||
|
||||
var currentlySelectedDataFile = lastWedDataFile;
|
||||
|
||||
function populateDashboard(event) {
|
||||
var dataFile = null;
|
||||
if (event && event.type == "load") {
|
||||
dataFile = lastWedDataFile;
|
||||
} else {
|
||||
dataFile = document.getElementById("weekly-dropdown").value;
|
||||
}
|
||||
|
||||
// Reset divs in case we can't find the data file.
|
||||
{
|
||||
document.getElementById("out-of-date-of-concern-summary").innerHTML = "<strong>No data found!</strong><br>(Failing URL: " + dataUrl + branch + dataFile + ")";
|
||||
document.getElementById("out-of-date-updates-disabled-summary").innerHTML = "";
|
||||
document.getElementById("up-to-date-summary").innerHTML = "";
|
||||
document.getElementById("distribution-chart").innerHTML = "";
|
||||
document.getElementById("update-ping-codes-chart").innerHTML = "";
|
||||
document.getElementById("update-ping-code-description").innerHTML = "Hover over a bar below to see a description of the ping code.";
|
||||
document.getElementById("version-distribution-chart-title").style.visibility = "visible";
|
||||
document.getElementById("version-distribution-chart").innerHTML = "";
|
||||
document.getElementById("version-distribution-chart").style.visibility = "visible";
|
||||
}
|
||||
|
||||
$.getJSON(dataUrl + dataFile, {}, function(data) {
|
||||
var upToDate = data[0]["up-to-date"]["up-to-date"];
|
||||
var outOfDate = data[0]["up-to-date"]["out-of-date"];
|
||||
var outOfDateNotLongEnough = data[0]["up-to-date"]["out-of-date-not-run-long-enough"];
|
||||
outOfDateNotLongEnough = outOfDateNotLongEnough ? outOfDateNotLongEnough : 0;
|
||||
var totalUpdateData = upToDate + outOfDate + outOfDateNotLongEnough;
|
||||
var upToDateData = [upToDate, outOfDate];
|
||||
var updateEnabled = data[1]["update-enabled-disabled"]["update-enabled"];
|
||||
var updateDisabled = data[1]["update-enabled-disabled"]["update-disabled"];
|
||||
var first = true;
|
||||
|
||||
// Display a d3pie chart.
|
||||
// @param aDivId: a div id to populate with the d3pie
|
||||
// @param aTitle: a title for the d3pie
|
||||
// @param aDescription: a description for the d3pie
|
||||
// @param aSlices: an array of d3pie slices to populate the pie chart with
|
||||
function displayD3Pie(aDivId, aTitle, aDescription, aSlices) {
|
||||
new d3pie(aDivId, {
|
||||
"header": {
|
||||
"title": {
|
||||
"text": aTitle,
|
||||
"fontSize": 24,
|
||||
"font": "Arial"
|
||||
},
|
||||
"subtitle": {
|
||||
"text": aDescription,
|
||||
"color": "#999999",
|
||||
"fontSize": 16,
|
||||
"font": "Arial"
|
||||
},
|
||||
"titleSubtitlePadding": 9
|
||||
},
|
||||
"footer": {
|
||||
"color": "#999999",
|
||||
"fontSize": 10,
|
||||
"font": "open sans",
|
||||
"location": "bottom-left"
|
||||
},
|
||||
"size": {
|
||||
"canvasWidth": 960,
|
||||
"pieOuterRadius": "90%"
|
||||
},
|
||||
"data": {
|
||||
"sortOrder": "value-desc",
|
||||
"content": aSlices
|
||||
},
|
||||
"labels": {
|
||||
"outer": {
|
||||
"pieDistance": 32
|
||||
},
|
||||
"inner": {
|
||||
"hideWhenLessThanPercentage": 3
|
||||
},
|
||||
"mainLabel": {
|
||||
"fontSize": 11
|
||||
},
|
||||
"percentage": {
|
||||
"color": "#ffffff",
|
||||
"decimalPlaces": 0
|
||||
},
|
||||
"value": {
|
||||
"color": "#adadad",
|
||||
"fontSize": 11
|
||||
},
|
||||
"lines": {
|
||||
"enabled": true,
|
||||
"style": "straight"
|
||||
},
|
||||
"truncation": {
|
||||
"enabled": true
|
||||
}
|
||||
},
|
||||
"effects": {
|
||||
"pullOutSegmentOnClick": {
|
||||
"effect": "linear",
|
||||
"speed": 400,
|
||||
"size": 8
|
||||
}
|
||||
},
|
||||
"misc": {
|
||||
"gradient": {
|
||||
"enabled": true,
|
||||
"percentage": 100
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Create and display distribution pie chart
|
||||
{
|
||||
var distributionSlices =
|
||||
[{"label": "Up to date", "value": upToDate, "color": "#0065D1"},
|
||||
{"label": "Out of date, updates disabled",
|
||||
"value": updateDisabled, "color": "#F4F601"},
|
||||
{"label": "Out of date, of concern",
|
||||
"value": updateEnabled, "color": "#D10000"}];
|
||||
if (outOfDateNotLongEnough) {
|
||||
distributionSlices.push({"label": "Out of date, not run long enough",
|
||||
"value": outOfDateNotLongEnough,
|
||||
"color": "#004949"});
|
||||
}
|
||||
displayD3Pie("distribution-chart", "Update orphaning distribution",
|
||||
"Illustration of the distribution of the major " +
|
||||
"orphaning states in our user population",
|
||||
distributionSlices);
|
||||
}
|
||||
|
||||
// Populate update ping codes chart
|
||||
{
|
||||
var updateChecks = data[2]["update-check-code-notify"];
|
||||
var updateCodesData = [];
|
||||
var totalValues = 0;
|
||||
for (i = -1; i <= 34; i++) {
|
||||
var val = 0;
|
||||
if (updateChecks[i]) {
|
||||
val = updateChecks[i];
|
||||
}
|
||||
updateCodesData.push({index:i,value:val});
|
||||
totalValues += val;
|
||||
}
|
||||
|
||||
var margin = {top: 20, right: 20, bottom: 30, left: 40};
|
||||
var width = 960 - margin.left - margin.right;
|
||||
var height = 500 - margin.top - margin.bottom;
|
||||
|
||||
var formatPercent = d3.format(".0%");
|
||||
|
||||
var x = d3.scale.ordinal()
|
||||
.rangeRoundBands([0, width], .1, 1);
|
||||
|
||||
var y = d3.scale.linear()
|
||||
.range([height, 0]);
|
||||
|
||||
var xAxis = d3.svg.axis()
|
||||
.scale(x)
|
||||
.orient("bottom");
|
||||
|
||||
var yAxis = d3.svg.axis()
|
||||
.scale(y)
|
||||
.orient("left")
|
||||
.tickFormat(formatPercent);
|
||||
|
||||
var svg = d3.select("#update-ping-codes-chart").append("svg")
|
||||
.attr("width", width + margin.left + margin.right)
|
||||
.attr("height", height + margin.top + margin.bottom)
|
||||
.append("g")
|
||||
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
||||
|
||||
updateCodesData.forEach(function(d) {
|
||||
d.value = 1/totalValues*d.value;
|
||||
});
|
||||
|
||||
x.domain(updateCodesData.map(function(d) { return d.index; }));
|
||||
y.domain([0, d3.max(updateCodesData, function(d) { return d.value; })]);
|
||||
|
||||
svg.append("g")
|
||||
.attr("class", "x axis")
|
||||
.attr("transform", "translate(0," + height + ")")
|
||||
.call(xAxis);
|
||||
|
||||
svg.append("g")
|
||||
.attr("class", "y axis")
|
||||
.call(yAxis)
|
||||
.append("text")
|
||||
.attr("transform", "rotate(-90)")
|
||||
.attr("y", 6)
|
||||
.attr("dy", ".71em")
|
||||
.style("text-anchor", "end")
|
||||
.text("Percentage");
|
||||
|
||||
var concerningCodes = [-1, 0, 5, 14, 15, 18, 19, 20, 21, 22, 23, 24, 25, 27, 29, 30, 31, 32, 33, 34, 35, 36];
|
||||
// Note that the index for each code descriptions below is shifted by +1
|
||||
// for easier indexing, i.e. code -1 has index 0 in the array etc.
|
||||
var codeDescriptions = [
|
||||
"-1: The telemetry ping did not contain a value for the UPDATE_CHECK_CODE_NOTIFY histogram.",
|
||||
"0 - CHK_NO_UPDATE_FOUND: No update was found during the update check (no notification is shown).",
|
||||
"1 - CHK_ADDON_NO_INCOMPAT: No incompatible add-ons were found during the incompatible add-ons check, proceed with the download of the update (no notification is shown).",
|
||||
"2 - CHK_SHOWPROMPT_SNIPPET: The update.xml specified that a prompt should be shown (an update notification is shown).",
|
||||
"3 - CHK_SHOWPROMPT_PREF: Preferences specified that a prompt should be shown (an update notification is shown).",
|
||||
"4 - CHK_ADDON_PREF_DISABLED: Preferences disabled the incompatible add-on check (the update is downloaded in the background).",
|
||||
"5 - CHK_ADDON_SAME_APP_VER: The incompatible add-on check was not performed because the current app version was the same as the app version of the update (the update is downloaded in the background).",
|
||||
"6 - CHK_ADDON_UPDATES_FOR_INCOMPAT: Incompatible add-ons have been detected and all of them have updates available (the update is downloaded in the background).",
|
||||
"7 - CHK_ADDON_HAVE_INCOMPAT: Incompatible add-ons have been detected (an update notification is shown).",
|
||||
"8 - CHK_HAS_ACTIVEUPDATE: An active update is already in progress (no notification is shown).",
|
||||
"9 - CHK_IS_DOWNLOADING: A background download is already in progress (no notification is shown).",
|
||||
"10 - CHK_IS_STAGED: An update is already staged (no notification is shown).",
|
||||
"11 - CHK_IS_DOWNLOADED: An update is already downloaded (no notification is shown).",
|
||||
"12 - CHK_PREF_DISABLED: Preferences have disabled background update checks (no notification is shown).",
|
||||
"13 - CHK_ADMIN_DISABLED: Update checks are disabled by administrative locked preferences (no notification is shown).",
|
||||
"14 - CHK_NO_MUTEX: An update check couldn't be performed because of a lack of a mutex per hasUpdateMutex() (no notification is shown).",
|
||||
"15 - CHK_UNABLE_TO_CHECK: An update check couldn't be performed per gCanCheckForUpdates (no notification is shown). This should be covered by other codes and is recorded just in case.",
|
||||
"16 - CHK_DISABLED_FOR_SESSION: Background update checks were disabled for the current session (no notification is shown).",
|
||||
"17 - CHK_OFFLINE: An update check couldn't be performed while offline (no notification is shown).",
|
||||
"18 - CHK_CERT_ATTR_NO_UPDATE_PROMPT: A certificate check (with no update available) failed and the retry threshold was reached (an update notification is shown). This code may indicate an attempted MITM attack.",
|
||||
"19 - CHK_CERT_ATTR_NO_UPDATE_SILENT: A certificate check (with no update available) failed, but the retry threshold was not yet reached (no notification is shown). This code may indicate an attempted MITM attack.",
|
||||
"20 - CHK_CERT_ATTR_WITH_UPDATE_PROMPT: A certificate check (with an update available) failed and the retry threshold was reached (an update notification is shown). This code may indicate an attempted MITM attack.",
|
||||
"21 - CHK_CERT_ATTR_WITH_UPDATE_SILENT: A certificate check (with an update available) failed, but the retry threshold was not yet reached (no notification is shown). This code may indicate an attempted MITM attack.",
|
||||
"22 - CHK_GENERAL_ERROR_PROMPT: A general update failure occurred and the retry threshold was reached (an update notification is shown).",
|
||||
"23 - CHK_GENERAL_ERROR_SILENT: A general update failure occurred, but the retry threshold was not yet reached (no notification is shown).",
|
||||
"24 - CHK_NO_COMPAT_UPDATE_FOUND: Even though updates were available, no compatible update was found (no notification is shown).",
|
||||
"25 - CHK_UPDATE_PREVIOUS_VERSION: An update for a previous version was found (no notification is shown).",
|
||||
"26 - CHK_UPDATE_NEVER_PREF: An update was found for which the \"never\" preference was set to never install (no notification is shown).",
|
||||
"27 - CHK_UPDATE_INVALID_TYPE: An update was found without a type attribute (no notification is shown).",
|
||||
"28 - CHK_UNSUPPORTED: The system is no longer supported (a \"system is unsupported\" notification is shown).",
|
||||
"29 - CHK_UNABLE_TO_APPLY: An update could not be applied (a \"failure to apply\" notification is shown with manual installation instructions).",
|
||||
"30 - CHK_NO_OS_VERSION: An update check could not be performed due to a lack of OS information (no notification is shown).",
|
||||
"31 - CHK_NO_OS_ABI: An update check could not be performed due to a lack of OS ABI information (no notification is shown).",
|
||||
"32 - CHK_INVALID_DEFAULT_URL: An invalid URL was specified for the app.update.url default preference (no notification is shown).",
|
||||
"33 - CHK_INVALID_USER_OVERRIDE_URL: An invalid URL was specified for the app.update.url user preference (no notification is shown).",
|
||||
"34 - CHK_INVALID_DEFAULT_OVERRIDE_URL: An invalid URL was specified for the app.update.url.override user preference (no notification is shown).",
|
||||
"35 - CHK_ELEVATION_DISABLED_FOR_VERSION: OSX only. The threshold for update elevation failures and cancelations was reached for a particular version of an update (no notification is shown).",
|
||||
"36 - CHK_ELEVATION_OPTOUT_FOR_VERSION: OSX only. The user opted out of an elevated update for a particular version of an update (no notification is shown).",
|
||||
];
|
||||
function displayDescription(aIndex, aValue) {
|
||||
var percentage = aValue * 100;
|
||||
percentage = percentage.toFixed(1);
|
||||
document.getElementById("update-ping-code-description").innerHTML = codeDescriptions[aIndex + 1] + " (" + percentage + "%)";
|
||||
}
|
||||
function removeDescription() {
|
||||
document.getElementById("update-ping-code-description").innerHTML = "Hover over a bar below to see a description of the ping code.";
|
||||
}
|
||||
svg.selectAll(".okbar .badbar")
|
||||
.data(updateCodesData)
|
||||
.enter().append("rect")
|
||||
.attr("class", function(d) { if (concerningCodes.indexOf(d.index) > -1) {
|
||||
return "badbar";
|
||||
} else {
|
||||
return "okbar";
|
||||
}})
|
||||
.attr("x", function(d) { return x(d.index); })
|
||||
.attr("width", x.rangeBand())
|
||||
.attr("y", function(d) { return y(d.value); })
|
||||
.attr("height", function(d) { return height - y(d.value); })
|
||||
.on("mouseover", function(d) { return displayDescription(d.index, d.value); })
|
||||
.on("mouseout", removeDescription)
|
||||
|
||||
function change() {
|
||||
clearTimeout(sortTimeout);
|
||||
|
||||
// Copy-on-write since tweens are evaluated after a delay.
|
||||
var x0 = x.domain(updateCodesData.sort(this.checked
|
||||
? function(a, b) { return b.value - a.value; }
|
||||
: function(a, b) { return d3.ascending(a.index, b.index); })
|
||||
.map(function(d) { return d.index; }))
|
||||
.copy();
|
||||
|
||||
svg.selectAll(".okbar, .badbar")
|
||||
.sort(function(a, b) { return x0(a.index) - x0(b.index); });
|
||||
|
||||
var transition = svg.transition().duration(750),
|
||||
delay = function(d, i) { return i * 50; };
|
||||
|
||||
transition.selectAll(".okbar, .badbar")
|
||||
.delay(delay)
|
||||
.attr("x", function(d) { return x0(d.index); });
|
||||
|
||||
transition.select(".x.axis")
|
||||
.call(xAxis)
|
||||
.selectAll("g")
|
||||
.delay(delay);
|
||||
}
|
||||
d3.select("input").on("change", change);
|
||||
var sortTimeout = setTimeout(function() {
|
||||
d3.select("input").property("checked", true).each(change);
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
var latestVersion = data[3]["latest-version"];
|
||||
// Populate version distribution pie chart
|
||||
{
|
||||
var orphanedVersions = data[4];
|
||||
if (!orphanedVersions || !orphanedVersions["orphaned-by-versions"]) {
|
||||
document.getElementById("version-distribution-chart-title").style.visibility = "hidden";
|
||||
document.getElementById("version-distribution-chart").style.visibility = "hidden";
|
||||
} else {
|
||||
// Color-blind-friendly palette taken from:
|
||||
// http://mkweb.bcgsc.ca/biovis2012/
|
||||
var versionColors = ["#92B600", // green
|
||||
"#24FF24", // light green
|
||||
"#B6DBFF", // light blue
|
||||
"#004949", // dark green
|
||||
"#490092", // dark purple
|
||||
"#924900", // brown
|
||||
"#009292", // dark turquoise
|
||||
"#FFB677", // orange
|
||||
"#B66DFF", // purple
|
||||
"#006D6D", // darker turquoise
|
||||
"#6DB6FF", // blue
|
||||
"#FF6DB6", // pink
|
||||
"#DBD100", // dark yellow
|
||||
"#FFFF6D", // yellow
|
||||
"#000000"]; // black
|
||||
orphanedVersions = orphanedVersions["orphaned-by-versions"];
|
||||
var versionSlices = [];
|
||||
for (v in orphanedVersions) {
|
||||
var diff = latestVersion - v.split(".")[0];
|
||||
var color = versionColors[Math.min(diff, versionColors.length - 1)];
|
||||
versionSlices.push({"label": v, "value": orphanedVersions[v],
|
||||
"color": color});
|
||||
}
|
||||
displayD3Pie("version-distribution-chart",
|
||||
"Distribution of orphaned users across Firefox versions",
|
||||
"Illustration of the distribution of orphaned users " +
|
||||
"across Firefox versions",
|
||||
versionSlices);
|
||||
}
|
||||
}
|
||||
|
||||
// Populate summary
|
||||
{
|
||||
var ofConcernPercentage = 100 / totalUpdateData * updateEnabled;
|
||||
ofConcernPercentage = ofConcernPercentage.toFixed(1);
|
||||
var outOfDateNotLongEnoughPercentage = 0;
|
||||
if (outOfDateNotLongEnough) {
|
||||
outOfDateNotLongEnoughPercentage = 100 / totalUpdateData * outOfDateNotLongEnough;
|
||||
outOfDateNotLongEnoughPercentage = outOfDateNotLongEnoughPercentage.toFixed(1);
|
||||
}
|
||||
var updatesDisabledPercentage = 100 / totalUpdateData * updateDisabled;
|
||||
updatesDisabledPercentage = updatesDisabledPercentage.toFixed(1);
|
||||
var upToDatePercentage = 100 - ofConcernPercentage - updatesDisabledPercentage - outOfDateNotLongEnoughPercentage;
|
||||
upToDatePercentage = upToDatePercentage.toFixed(1);
|
||||
|
||||
var ofConcernSummary = document.getElementById("out-of-date-of-concern-summary");
|
||||
ofConcernSummary.innerHTML = "<strong>" + ofConcernPercentage + "%</strong> of users are currently out of date for reasons that are of concern (e.g. failure to download) and are considered orphaned.";
|
||||
if (outOfDateNotLongEnoughPercentage) {
|
||||
ofConcernSummary.innerHTML += "<br/><strong>" + outOfDateNotLongEnoughPercentage + "%</strong> of users are currently out of date, but haven't run Firefox for at least 2 hours since being orphaned.";
|
||||
}
|
||||
var updatesDisabledSummary = document.getElementById("out-of-date-updates-disabled-summary");
|
||||
updatesDisabledSummary.innerHTML = "<strong>" + updatesDisabledPercentage + "%</strong> of users are currently out of date with updates disabled.";
|
||||
var upToDateSummary = document.getElementById("up-to-date-summary");
|
||||
upToDateSummary.innerHTML = "<strong>" + upToDatePercentage + "%</strong> of users are up to date (version " + (latestVersion - 2) + "+).";
|
||||
}
|
||||
});
|
||||
}
|
||||
window.onload = populateDashboard;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -98,11 +98,16 @@ td:nth-child(3) {
|
|||
display: none;
|
||||
}
|
||||
.pie-chart {
|
||||
height: 500px;
|
||||
height: 490px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
#out-of-date-desc {
|
||||
margin-top: 20px;
|
||||
#version-dist-chart {
|
||||
height: 530px;
|
||||
}
|
||||
#out-of-date-desc,
|
||||
#not-min-reqs-desc {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
#not-min-reqs-desc {
|
||||
margin-bottom: 20px;
|
||||
|
|
|
@ -19,23 +19,24 @@ Report Week: <br/>
|
|||
<div id="main-div">
|
||||
<h1>Firefox Application Update Out Of Date Dashboard</h1>
|
||||
<div><p>The data below is from the Telemetry longitudinal data source which contains approximately
|
||||
1% of all Telemetry clients for each Firefox version. The period for the report is the 7 days prior
|
||||
to the Report Week date. The old dashboard can be found <a href="index-old.html">here</a>.</p>
|
||||
1% of all Telemetry clients for each Firefox version on the release channel. The period for the
|
||||
report is the 7 days prior to the selected Report Week date.</p>
|
||||
</div>
|
||||
<div id="error-message"></div>
|
||||
<div id="summary-dist-chart" class="pie-chart"></div>
|
||||
<table id="summary-dist-details"></table>
|
||||
<div id="out-of-date-desc"></div>
|
||||
<div id="not-min-reqs-chart" class="pie-chart"></div>
|
||||
<div id="not-min-reqs-desc">Clients are considered <b>out of date, potentially of concern</b> when
|
||||
<table id="not-min-reqs-details"></table>
|
||||
<div id="not-min-reqs-desc">Note: Clients are considered <b>out of date, potentially of concern</b> when
|
||||
they are out of date and haven't met the minimum requirements for there to be an expectation that
|
||||
the client should have updated. Since each client can match multuple criteria the order that they
|
||||
are applied affects the percentage of clients that match each criteria. The order that the
|
||||
criteria are applied is the same order as they are listed below.</div>
|
||||
<table id="not-min-reqs-details"></table>
|
||||
criteria are applied is the same order as they are listed above.</div>
|
||||
<div id="of-concern-chart" class="pie-chart"></div>
|
||||
<table id="of-concern-details"></table>
|
||||
<div id="version-dist-chart" class="pie-chart"></div>
|
||||
<table id="version-dist-details"></table>
|
||||
<div class="section-header">Out of date, of concern client last update check code (check phase)</div>
|
||||
<div class="section-hover">(hover over a bar for details below the chart)</div>
|
||||
<div><label><input id="check-code-sort-input" class="sort-checkbox" type="checkbox"> Sort values</label></div>
|
||||
|
@ -97,8 +98,7 @@ criteria are applied is the same order as they are listed below.</div>
|
|||
</div><br/>
|
||||
<hr>
|
||||
This dashboard is maintained by <a href="mailto:rstrong@mozilla.com">:rstrong</a>.<br/>
|
||||
The Spark Notebook for the weekly reports is scheduled to run every Sunday and can be found <a href="https://github.com/mozilla/mozilla-reports/tree/master/projects/app_update_out_of_date.kp/orig_src/app_update_out_of_date.ipynb">here</a>.
|
||||
|
||||
The Spark Notebook for this dashboard is scheduled to run on Monday and Tuesday every week since the longitudinal datasource isn't consistently available on Monday. The Spark Notebook can be found <a href="https://github.com/mozilla/mozilla-reports/tree/master/projects/app_update_out_of_date.kp/orig_src/app_update_out_of_date.ipynb">here</a>.
|
||||
<div><a href="https://www.mozilla.org/en-US/privacy/websites/">Mozilla's Website Privacy Notice</a></div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -128,10 +128,11 @@ const downloadCodeGeneralDetails = {
|
|||
"9": {code: "DWNLD_ERR_PATCH_SIZE_NOT_EQUAL", desc: "The size of the update file being downloaded does not equal the expected size."},
|
||||
"10": {code: "DWNLD_ERR_BINDING_ABORTED", desc: "The download request was aborted with NS_BINDING_ABORTED."},
|
||||
"11": {code: "DWNLD_ERR_ABORT", desc: "The download request was aborted with NS_ERROR_ABORT."},
|
||||
"12": {code: "DWNLD_ERR_DOCUMENT_NOT_CACHED", desc: "The download request was aborted with NS_ERROR_DOCUMENT_NOT_CACHED and the download will be retried as of Firefox 49 and greater (<a href='https://bugzilla.mozilla.org/show_bug.cgi?id=1272585'>Bug 1272585</a>)."},
|
||||
"12": {code: "DWNLD_ERR_DOCUMENT_NOT_CACHED", desc: "The download request was aborted. As of Firefox 49 the download will be continued (<a href='https://bugzilla.mozilla.org/show_bug.cgi?id=1272585'>Bug 1272585</a>) and as of Firefox 57 the error should rarely occur if at all."},
|
||||
"13": {code: "DWNLD_ERR_VERIFY_NO_REQUEST", desc: "The download network request object no longer exists."},
|
||||
"14": {code: "DWNLD_ERR_VERIFY_PATCH_SIZE_NOT_EQUAL", desc: "The size of the downloaded update file does not equal the expected size."},
|
||||
"15": {code: "DWNLD_ERR_VERIFY_NO_HASH_MATCH", desc: "The file hash does not equal the hash specified in the update xml file."}};
|
||||
"15": {code: "DWNLD_ERR_VERIFY_NO_HASH_MATCH", desc: "The file hash does not equal the hash specified in the update xml file."},
|
||||
"16": {code: "DWNLD_RESUME_FAILURE", desc: "The download in progress failed to resume."}};
|
||||
|
||||
// Download codes, names, and descriptions specific to UPDATE_DOWNLOAD_CODE_COMPLETE and UPDATE_DOWNLOAD_CODE_PARTIAL.
|
||||
const DOWNLOAD_CODE_DETAILS = {
|
||||
|
@ -220,9 +221,15 @@ const stateFailureCodeGeneralDetails = {
|
|||
"46": {code: "DELETE_ERROR_EXPECTED_DIR", desc: "When attempting to delete a directory a file was found."},
|
||||
"47": {code: "DELETE_ERROR_EXPECTED_FILE", desc: "When attempting to delete a file a directory was found."},
|
||||
"48": {code: "RENAME_ERROR_EXPECTED_FILE", desc: "When attempting to rename a file a directory was found."},
|
||||
"49": {code: "SERVICE_COULD_NOT_COPY_UPDATER", desc: "Unable to copy the updater to a secure location (Windows only)."},
|
||||
"50": {code: "SERVICE_STILL_APPLYING_TERMINATED", desc: "The update is still in the applying state and the updater process was terminated (Windows only)."},
|
||||
"51": {code: "SERVICE_STILL_APPLYING_NO_EXIT_CODE", desc: "The update is still in the applying state and the updater process did not return an exit code (Windows only)."},
|
||||
"49": {code: "SERVICE_COULD_NOT_COPY_UPDATER", desc: "The maintenance service was unable to copy the updater to a secure location (Windows only)."},
|
||||
"50": {code: "SERVICE_STILL_APPLYING_TERMINATED", desc: "The update is still in the applying state and the updater process was terminated by the maintenance service (Windows only)."},
|
||||
"51": {code: "SERVICE_STILL_APPLYING_NO_EXIT_CODE", desc: "The update is still in the applying state and the updater process did not return an exit code to the maintenance service (Windows only)."},
|
||||
"52": {code: "SERVICE_INVALID_APPLYTO_DIR_STAGED_ERROR", desc: "The directory to apply the update to is not the same as or a child of the installation directory when checked by the maintenance service (Windows only)."},
|
||||
"53": {code: "SERVICE_CALC_REG_PATH_ERROR", desc: "The maintenance service was unable to calculate the registry path for the installation (Windows only)."},
|
||||
"54": {code: "SERVICE_INVALID_APPLYTO_DIR_ERROR", desc: "Installation directory and working directory were not the same for a non-staged update performed by the maintenance service (Windows only)."},
|
||||
"55": {code: "SERVICE_INVALID_INSTALL_DIR_PATH_ERROR", desc: "The installation directory path passed to the maintenance service is invalid (Windows only)."},
|
||||
"56": {code: "SERVICE_INVALID_WORKING_DIR_PATH_ERROR", desc: "The working directory path passed to the maintenance service is invalid (Windows only)."},
|
||||
"57": {code: "SERVICE_INSTALL_DIR_REG_ERROR", desc: "The installation directory specified in the registry and checked by the maintenance service is invalid (Windows only)."},
|
||||
"61": {code: "WRITE_ERROR_FILE_COPY", desc: "Error copying a file."},
|
||||
"62": {code: "WRITE_ERROR_DELETE_FILE", desc: "Error deleting a file."},
|
||||
"63": {code: "WRITE_ERROR_OPEN_PATCH_FILE", desc: "Unable to create new file for patching."},
|
||||
|
@ -237,13 +244,21 @@ const stateFailureCodeGeneralDetails = {
|
|||
"72": {code: "INVALID_APPLYTO_DIR_STAGED_ERROR", desc: "The directory to apply the update to is not the same as or a child of the installation directory."},
|
||||
"73": {code: "LOCK_ERROR_PATCH_FILE", desc: "Error locking a patch file (Windows only)."},
|
||||
"74": {code: "INVALID_APPLYTO_DIR_ERROR", desc: "Installation directory and working directory were not the same for a non-staged update (Windows only)."},
|
||||
"75": {code: "INVALID_INSTALL_DIR_PATH_ERROR", desc: "The installation directory path is invalid."},
|
||||
"76": {code: "INVALID_WORKING_DIR_PATH_ERROR", desc: "The working directory path is invalid."},
|
||||
"77": {code: "INVALID_CALLBACK_PATH_ERROR", desc: "The application callback path is invalid."},
|
||||
"78": {code: "INVALID_CALLBACK_DIR_ERROR", desc: "The application callback directory is invalid."},
|
||||
"80": {code: "FOTA_GENERAL_ERROR", desc: "General error from the recovery service (B2G only)."},
|
||||
"81": {code: "FOTA_UNKNOWN_ERROR", desc: "Unexpected error from the recovery service (B2G only)."},
|
||||
"82": {code: "FOTA_FILE_OPERATION_ERROR", desc: "File operation error (B2G only)"},
|
||||
"83": {code: "FOTA_RECOVERY_ERROR", desc: "Error initializing the receovery service (B2G only)"},
|
||||
"84": {code: "INVALID ERROR CODE", desc: "Invalid update state code."},
|
||||
"99": {code: "INVALID ERROR CODE", desc: "Invalid update state failed code."},
|
||||
"100": {code: "INVALID ERROR CODE", desc: "Unknown"}};
|
||||
"90": {code: "ERR_OLDER_VERSION_OR_SAME_BUILD", desc: "The update was for an older version or the same version and build ID."},
|
||||
"91": {code: "ERR_UPDATE_STATE_NONE", desc: "The update status file existed but didn't have a valid state."},
|
||||
"92": {code: "ERR_CHANNEL_CHANGE", desc: "The update was for a different channel."},
|
||||
"98": {code: "INVALID_UPDATER_STATE_CODE", desc: "Invalid update state code."},
|
||||
"99": {code: "INVALID_UPDATER_STATUS_CODE", desc: "Invalid update status code."},
|
||||
"100": {code: "INVALID ERROR CODE", desc: "100 is a test code and should not be reported to telemetry."}};
|
||||
|
||||
const CHECK_CODE_GOOD = [1, 2, 3, 4, 6, 7, 8, 9, 10, 11];
|
||||
const CHECK_CODE_BAD = [0, 5, 14, 15, 18, 19, 20, 21, 22, 23, 24, 25, 27, 29, 30, 31, 32, 33, 34, 35, 36];
|
||||
|
@ -357,7 +372,7 @@ startDate.setMonth(9); // October
|
|||
startDate.setFullYear(2016);
|
||||
|
||||
function getDetailsText(aText) {
|
||||
var pos1 = aText.indexOf("<");
|
||||
var pos1 = aText.indexOf("<a");
|
||||
// If there are no links in the text just return the text.
|
||||
if (pos1 == -1) {
|
||||
return [aText, null, null, null];
|
||||
|
@ -437,7 +452,11 @@ function updateDetailsRow(aTableID, aRowNum, aSubset, aTotal, aTitle, aDesc) {
|
|||
if (linkURL && linkText && text2) {
|
||||
var a = document.createElement("a");
|
||||
a.setAttribute("href", linkURL);
|
||||
a.setAttribute("target", "_blank");
|
||||
if (linkURL.indexOf("#") == 0) {
|
||||
a.setAttribute("target", "_self");
|
||||
} else {
|
||||
a.setAttribute("target", "_blank");
|
||||
}
|
||||
a.textContent = linkText;
|
||||
row.cells[2].appendChild(a);
|
||||
text = document.createTextNode(text2);
|
||||
|
@ -496,7 +515,11 @@ function displayBarDetails(aChartPrefix, aIndex, aChartData, aJSONData, aDetails
|
|||
if (linkURL && linkText && textEnd) {
|
||||
a = document.createElement("a");
|
||||
a.setAttribute("href", linkURL);
|
||||
a.setAttribute("target", "_blank");
|
||||
if (linkURL.indexOf("#") == 0) {
|
||||
a.setAttribute("target", "_self");
|
||||
} else {
|
||||
a.setAttribute("target", "_blank");
|
||||
}
|
||||
a.textContent = linkText;
|
||||
detailsDiv.appendChild(a);
|
||||
textNode = document.createTextNode(textEnd);
|
||||
|
@ -634,6 +657,7 @@ function getJSONValue(aData, aKey, aSubKey, aDefault) {
|
|||
}
|
||||
|
||||
function populateDashboard(event) {
|
||||
document.getElementById("weekly-dropdown").disabled = true;
|
||||
var errDiv = document.getElementById("error-message");
|
||||
errDiv.style.display = "none";
|
||||
|
||||
|
@ -676,8 +700,9 @@ function populateDashboard(event) {
|
|||
"location": "bottom-left"
|
||||
},
|
||||
"size": {
|
||||
"canvasHeight": 520,
|
||||
"canvasWidth": 960,
|
||||
"pieOuterRadius": "90%"
|
||||
"pieOuterRadius": "80%"
|
||||
},
|
||||
"data": {
|
||||
"sortOrder": aSortOrder,
|
||||
|
@ -685,8 +710,8 @@ function populateDashboard(event) {
|
|||
},
|
||||
"labels": {
|
||||
"outer": {
|
||||
"hideWhenLessThanPercentage": 0,
|
||||
"pieDistance": 32
|
||||
"hideWhenLessThanPercentage": null,
|
||||
"pieDistance": 20
|
||||
},
|
||||
"inner": {
|
||||
"hideWhenLessThanPercentage": 3
|
||||
|
@ -788,8 +813,8 @@ function populateDashboard(event) {
|
|||
var distributionSlices = [];
|
||||
termLabel = "Up to date";
|
||||
updateDetailsRow("summary-dist-details", 0, versionUpToDate, totalUpdateData, termLabel,
|
||||
"the client's Firefox version is " + (latestVersion - 2) + " or higher " +
|
||||
"and less than " + (latestVersion + 1) + ".");
|
||||
"the client's Firefox version is " + (latestVersion - upToDateReleases) +
|
||||
" or higher on the release channel.");
|
||||
if (versionUpToDate) {
|
||||
distributionSlices.push({"label": termLabel, "value": versionUpToDate, "color": "#0065D1"});
|
||||
}
|
||||
|
@ -797,7 +822,9 @@ function populateDashboard(event) {
|
|||
termLabel = "Out of date, potentially of concern";
|
||||
updateDetailsRow("summary-dist-details", 1, outOfDateExcludedTotal, totalUpdateData, termLabel,
|
||||
"the client does not meet the minimum requirements for there to be an " +
|
||||
"expectation that the client should have updated (see chart below for details).");
|
||||
"expectation that the client should have updated (see the " +
|
||||
"<a href='#not-min-reqs-chart'>Out of date, potentially of concern reason " +
|
||||
"distribution chart</a> below for details).");
|
||||
if (outOfDateExcludedTotal) {
|
||||
distributionSlices.push({"label": termLabel, "value": outOfDateExcludedTotal, "color": "#DBD100"});
|
||||
}
|
||||
|
@ -805,7 +832,9 @@ function populateDashboard(event) {
|
|||
termLabel = "Out of date, of concern";
|
||||
updateDetailsRow("summary-dist-details", 2, ofConcernTrue, totalUpdateData, termLabel,
|
||||
"the client meets the minimum requirements for there to be an " +
|
||||
"expectation that the client should have updated.");
|
||||
"expectation that the client should have updated (see the " +
|
||||
"<a href='#of-concern-chart'>Out of date, of concern reason distribution " +
|
||||
"chart</a> below for details).");
|
||||
if (ofConcernTrue) {
|
||||
distributionSlices.push({"label": termLabel, "value": ofConcernTrue, "color": "#D10000"});
|
||||
}
|
||||
|
@ -823,11 +852,11 @@ function populateDashboard(event) {
|
|||
NotMinReqsSlices.push({"label": termLabel, "value": hasOutOfDateMaxVersionFalse, "color": "#0065D1"});
|
||||
}
|
||||
|
||||
termLabel = "No update pings";
|
||||
updateDetailsRow("not-min-reqs-details", 1, hasUpdatePingFalse, outOfDateExcludedTotal, termLabel,
|
||||
"the client has never sent an update telemetry ping for any Firefox " +
|
||||
"version. This can be caused by building without application update as " +
|
||||
"Firefox distributions typically do.");
|
||||
termLabel = "No update pings";
|
||||
updateDetailsRow("not-min-reqs-details", 1, hasUpdatePingFalse, outOfDateExcludedTotal, termLabel,
|
||||
"the client has never sent an update telemetry ping for any Firefox " +
|
||||
"version. This can be caused by building without application update as " +
|
||||
"Firefox distributions typically do.");
|
||||
if (hasUpdatePingFalse) {
|
||||
NotMinReqsSlices.push({"label": termLabel, "value": hasUpdatePingFalse, "color": "#004949"});
|
||||
}
|
||||
|
@ -849,8 +878,9 @@ function populateDashboard(event) {
|
|||
weeksOfSubsessionData + " weeks. Since there is an update telemetry ping " +
|
||||
"every 12 hours and it happens within minutes after startup when the 12 " +
|
||||
"hours have elapsed this is equivalent to these clients only running " +
|
||||
"Firefox on " + (minUpdatePingCount - 1) + " days or less during the "+
|
||||
"previous " + (weeksOfSubsessionData * 7) + " days.");
|
||||
"Firefox on " + (minUpdatePingCount - 1) + " days or less over the "+
|
||||
"previous " + weeksOfSubsessionData + " weeks (" + (weeksOfSubsessionData * 7) +
|
||||
" days)");
|
||||
if (hasMinUpdatePingCountFalse) {
|
||||
NotMinReqsSlices.push({"label": termLabel, "value": hasMinUpdatePingCountFalse, "color": "#924900"});
|
||||
}
|
||||
|
@ -925,10 +955,10 @@ function populateDashboard(event) {
|
|||
b.textContent = "out of date";
|
||||
outOfDateDesc.appendChild(b);
|
||||
textNode = document.createTextNode(" refers to Firefox 42 (first version with opt-out telemetry " +
|
||||
"for 100% of the release population) through versions less " +
|
||||
"than Firefox " + (latestVersion - upToDateReleases) + " (" +
|
||||
(upToDateReleases + 1) + " versions prior to the latest " +
|
||||
"Firefox version at the time this data was generated).");
|
||||
"for 100% of the release population) through versions less than Firefox " +
|
||||
(latestVersion - upToDateReleases) + " (" +
|
||||
(upToDateReleases + 1) + " versions prior to the latest Firefox " +
|
||||
"version at the time this data was generated) on the release channel.");
|
||||
outOfDateDesc.appendChild(textNode);
|
||||
|
||||
var ofConcernByVersion = data["ofConcernByVersion"];
|
||||
|
@ -950,13 +980,47 @@ function populateDashboard(event) {
|
|||
"#FFFF6D", // yellow
|
||||
"#24FF24", // light green
|
||||
"#000000"]; // black
|
||||
var detailRows = [];
|
||||
var versionSlices = [];
|
||||
for (var v in ofConcernByVersion) {
|
||||
var labelName = v;
|
||||
var diff = (latestVersion - labelName.split(".")[0]) - 3;
|
||||
var color = versionColors[Math.min(diff, versionColors.length - 1)];
|
||||
if (labelName == "43.0.1" || labelName == "47.0.2") {
|
||||
labelName = labelName + " (watershed)";
|
||||
switch (labelName) {
|
||||
case "43.0.1":
|
||||
labelName = labelName + " (SHA1 / SHA256 watershed)";
|
||||
detailRows[0] = {"labelName": labelName, "subset": ofConcernByVersion[v],
|
||||
"desc": "a Windows only update watershed was required to change the binary " +
|
||||
"signing certificate from SHA1 to SHA256 (see " +
|
||||
"<a href='https://bugzilla.mozilla.org/show_bug.cgi?id=1079858'>Bug 1079858</a>)."};
|
||||
break;
|
||||
case "47.0.2":
|
||||
labelName = labelName + " (Websense watershed)";
|
||||
detailRows[1] = {"labelName": labelName, "subset": ofConcernByVersion[v],
|
||||
"desc": "a Windows only update watershed was required to add detection for the " +
|
||||
"Websense application (see " +
|
||||
"<a href='https://bugzilla.mozilla.org/show_bug.cgi?id=1305847'>Bug 1305847</a>)."};
|
||||
break;
|
||||
case "52.0.2":
|
||||
labelName = labelName + " (XP / Vista EOL)";
|
||||
detailRows[2] = {"labelName": labelName, "subset": ofConcernByVersion[v],
|
||||
"desc": "Windows XP / Vista / 2003 are no longer supported as of Firefox 53. These " +
|
||||
"clients are being migrated to ESR52 by Release Engineering (see " +
|
||||
"<a href='https://bugzilla.mozilla.org/show_bug.cgi?id=1130266'>Bug 1130266</a>)."};
|
||||
break;
|
||||
case "56.0.2":
|
||||
labelName = labelName + " (JAWS watershed)";
|
||||
detailRows[3] = {"labelName": labelName, "subset": ofConcernByVersion[v],
|
||||
"desc": "a Windows only update watershed was required to add detection for the " +
|
||||
"JAWS application (see " +
|
||||
"<a href='https://bugzilla.mozilla.org/show_bug.cgi?id=617918'>Bug 617918</a>)."};
|
||||
break;
|
||||
case "57.0.2":
|
||||
labelName = labelName + " (LZMA watershed)";
|
||||
detailRows[4] = {"labelName": labelName, "subset": ofConcernByVersion[v],
|
||||
"desc": "an update watershed was added for LZMA update compression support (see " +
|
||||
"<a href='https://bugzilla.mozilla.org/show_bug.cgi?id=641212'>Bug 641212</a>)."};
|
||||
break;
|
||||
}
|
||||
versionSlices.push({"label": labelName, "value": ofConcernByVersion[v],
|
||||
"color": color});
|
||||
|
@ -964,6 +1028,19 @@ function populateDashboard(event) {
|
|||
displayD3Pie("version-dist-chart",
|
||||
"Out of date, of concern client distribution across Firefox versions",
|
||||
versionSlices, "label-asc");
|
||||
for (var i = 0; i < 5; i++) {
|
||||
if (detailRows[i]) {
|
||||
updateDetailsRow("version-dist-details", i, detailRows[i].subset, ofConcernTrue, detailRows[i].labelName,
|
||||
detailRows[i].desc);
|
||||
}
|
||||
}
|
||||
|
||||
var versionDetailsTable = document.getElementById("version-dist-details");
|
||||
for (var i = 4; i > -1; i--) {
|
||||
if (!detailRows[i] && versionDetailsTable.rows[i]) {
|
||||
versionDetailsTable.deleteRow(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var checkCodeNotifyOfConcern = data["checkCodeNotifyOfConcern"];
|
||||
|
@ -1021,7 +1098,9 @@ function populateDashboard(event) {
|
|||
});
|
||||
updateBarChart(stateFailureCodeStartupData, "state-failure-code-startup", STATE_FAILURE_CODE_STARTUP_BAR_CLASS_FN,
|
||||
stateFailureCodeStartupOfConcern, stateFailureCodeGeneralDetails, null, false);
|
||||
document.getElementById("weekly-dropdown").disabled = false;
|
||||
}).fail(function() {
|
||||
document.getElementById("weekly-dropdown").disabled = false;
|
||||
if (firstLoad) {
|
||||
// Try to load the previous week's data in case the current week's data
|
||||
// isn't available yet.
|
||||
|
|
Загрузка…
Ссылка в новой задаче