js/triage.js (295 lines of code) (raw):
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 BIG_SCREEN = "bigscreen";
var SMALL_SCREEN = "smallscreen";
var TriageData; // ICS Data
var BugQueries; // Queries database based on ICS data. Remove Me
var BugData; // Bug database covering all calendar dates
var UBData; // Bug database covering all calendar dates for ubdate bot
var TriageConfig;
var TotalQueries = 0;
// Not worth chasing toLocaleDateString etc. compatibility
var MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
/*
TODO:
* additional settings options
*/
$(document).ready(function () {
if (getTeam() == undefined) {
window.location.href = window.location.href + "?year=2023&team=media"
return;
}
$.getJSON('js/triage.json', function(data) {
load(data);
});
});
function load(jsonConfigData) {
// Load ICS file
loadSettingsInternal(jsonConfigData.triage);
run();
}
function run() {
let icsurl = '';
switch (getTeam()) {
case 'media':
document.getElementById('team-select').selectedIndex = 0;
icsurl = TriageConfig.jsonConfig.media_ics;
break;
case 'webrtc':
document.getElementById('team-select').selectedIndex = 1;
icsurl = TriageConfig.jsonConfig.webrtc_ics;
break;
case 'graphics':
document.getElementById('team-select').selectedIndex = 2;
icsurl = TriageConfig.jsonConfig.graphics_ics;
break;
}
let random = Math.floor(Math.random() * 9e9);
icsurl += '?rand=' + random;
console.log("Loading [" + icsurl + "]");
$.ajax({
url: icsurl,
crossDomain:true,
crossOrigin: true,
error: function (a, b, c) {
console.log("ics file load error: " + c);
},
success: function(data) {
// ICS DATA LOADED
// Store ics data in our global data object. See
// Notes.txt for format info.
TriageData = parseICSData(data);
//console.log(TriageData);
let now = new Date();
let currentYear = now.getFullYear();
let year = getYear(now);
// Global that points to the buckets data for the display year.
BugQueries = TriageData[year].data;
// Generates Bugzilla query strings from ics data
let count = setupQueryURLs(true);
// Updates the page title.
displayTitle(year, count);
// Add engineer names, bucket dates, and grayed out '?' buckets.
populateBuckets(year, count);
// Display links for other years and the shedule
displayYearFooter(currentYear, BugQueries);
// Make a single query for all bugs for both lists.
loadBugListDetail();
}
});
}
function refreshList(event) {
$("#errors").empty();
$("#buglists").empty();
$("#buglists").append("<div id='content'></div>");
run();
}
function loadBugListDetail() {
if (!BugQueries) {
return;
}
$("#errors").empty();
// Fire off a single bugzilla request per report
let url = TriageConfig.jsonConfig.BUGZILLA_REST_URL + TriageData['url'];
let key = getAPIKeyFromStorage();
if (key != null && key.length) {
url += "&api_key=" + key;
}
// Limit what data we retreive for better performance.
url += "&include_fields=" + TriageConfig.jsonConfig.include_fields;
$.ajax({
url: url,
crossDomain:true,
dataType: 'json',
ifModified: true,
success: function(data, status) {
if (status === 'success') {
// Global
BugData = data;
displayBugLists(updateBugList, 'data', BugData);
}
},
error: function(jqXHR, textStatus, errorThrown) {
console.log("url:", url);
console.log("status:", textStatus);
console.log("error thrown:", errorThrown);
console.log("response text:", jqXHR.responseText);
try {
let info = JSON.parse(jqXHR.responseText);
let text = info.message ? info.message : errorThrown;
console.log("detail:", text);
errorMsg(text);
return;
} catch(e) {
}
}
});
// Fire off a bugzilla request
url = TriageConfig.jsonConfig.BUGZILLA_REST_URL + TriageData['uburl'];
if (key != null && key.length) {
url += "&api_key=" + key;
}
// Limit what data we retreive for better bugzilla query performance.
url += "&" + TriageConfig.jsonConfig.include_fields;
console.log(TriageConfig.jsonConfig.BUGZILLA_URL + TriageData['uburl']);
$.ajax({
url: url,
crossDomain:true,
dataType: 'json',
ifModified: true,
success: function(data, status) {
if (status === 'success') {
UBData = data;
displayBugLists(updateBotList, 'ubdata', UBData);
}
},
error: function(jqXHR, textStatus, errorThrown) {
console.log("url:", url);
console.log("status:", textStatus);
console.log("error thrown:", errorThrown);
console.log("response text:", jqXHR.responseText);
try {
let info = JSON.parse(jqXHR.responseText);
let text = info.message ? info.message : errorThrown;
console.log("detail:", text);
errorMsg(text);
return;
} catch(e) {
}
}
});
}
var LastErrorText = "";
function errorMsg(text) {
if (LastErrorText == text)
return;
$("#errors").append(text);
LastErrorText = text;
}
function displayBugLists(displayCallback, div, data) {
for (let idx = 0; idx < BugQueries.length; idx++) {
let query = BugQueries[idx];
let qurl = '';
if (div == 'ubdata') {
if (!("uburl" in query)) {
continue;
}
qurl = query.uburl;
} else {
if (!("url" in query)) {
continue;
}
qurl = query.url;
}
// Same dates we display in html
let sfrom = query.from.split('-');
let sto = query.to.split('-');
// Calculate Bug Counts
let count = 0;
// Both to and from will be stored internally as UTC but will get converted to local
// time when in use, so be careful.
// Date.UTC(year, monthIndex, day, hour, minute, second, millisecond)
let from = new Date(Date.UTC(sfrom[0], parseInt(sfrom[1])-1, sfrom[2], 0, 0, 0, 0));
let to;
// If the ICS start and end dates span 8 days, use this for accurate bug counts.
to = new Date(Date.UTC(sto[0], parseInt(sto[1])-1, sto[2], 0, 0, 0, 0));
// If the ICS start and end dates span 7 days, use this for accurate bug counts.
//to = new Date(Date.UTC(sto[0], parseInt(sto[1])-1, sto[2], 23, 59, 59, 0));
//console.log(query.who);
//console.log('from='+from.toUTCString());
//console.log('to='+to.toUTCString());
for (let idy = 0; idy < data.bugs.length; idy++) {
let bug = data.bugs[idy];
// a lot of the time this isn't populated, resulting in an invalid date
//let changeTime = new Date(bug.last_change_time); // 2023-08-02T22:25:58Z
//console.log('change date:' + bug.last_change_time);
let creationTime = new Date(bug.creation_time);
//console.log('bug creation time:' + bug.creation_time);
if (creationTime.valueOf() >= from.valueOf() && creationTime.valueOf() <= to.valueOf()) { // UTC compare
//console.log('fits:', bug.id, bug.summary, creationTime);
count++;
} else {
//console.log('no fit:', bug.id, bug.summary);
//console.log(creationTime.valueOf(), from.valueOf(), to.valueOf())
}
}
query.bugcount = count;
let now = new Date();
let year = getYear(now);
let id = year + "-" + idx;
// This id was generated in insertEmptyBugLists
displayCallback(div, idx, query.bugcount,
TriageConfig.jsonConfig.BUGZILLA_URL + qurl);
}
}
// Add engineer names, bucket dates, and grayed out '?' buckets.
function populateBuckets(year, count) {
if (!BugQueries) {
return;
}
// Adds div placeholders for bucket entries.
insertEmptyBugLists(year, count);
for (let i = 0; i < BugQueries.length; i++) {
let query = BugQueries[i];
if (!("url" in query)) {
continue;
}
let dfrom = query.from.split('-');
let dto = query.to.split('-');
let id = year + "-" + i;
// Gray future buckets
let now = new Date();
let endDate = new Date(dfrom[0], parseInt(dfrom[1])-1, dfrom[2], 0, 0, 0, 0);
let cssTag = 'greyedout';
if (now > endDate) {
cssTag = '';
}
let markup = "<div class='bugcount'><div class='who " + cssTag + "'>"
+ query.who
+ "</div>"
+ "<div class='date " + cssTag + "'>("
+ MONTHS[dfrom[1]-1] + " " + dfrom[2] + " - "
+ MONTHS[dto[1]-1] + " " + dto[2] + ")</div>"
+ "<div id='data" + i + "'" + " class='data greyedout'>?</div>"
+ "<div id='ubdata" + i + "'" + " class='data greyedout'>?</div>"
+ "</div>";
// This id was generated in insertEmptyBugLists
$("#reportDiv" + id).replaceWith(markup);
}
}
function insertEmptyBugLists(year, count) {
let content = "";
if (BugQueries) {
for (let i = 0; i < count; i++) {
let sfrom = BugQueries[i].from.split('-');
let from = new Date(Date.UTC(sfrom[0], parseInt(sfrom[1])-1, sfrom[2], 0, 0, 0, 0));
content += "<div class='bugcount' id='reportDiv" + year + "-" + i + "'></div>";
}
$("#content").replaceWith(content);
}
}
/*
<div id="buglists" style="opacity: 1;">
<div class="bugcount">
<div class="who">Kelsey Gilbert</div>
<div class="date">(Dec 30 - Jan 6)</div>
<div class="data"><a target="_buglist" href="https://bug...">1</a><div class="data sub">B</div></div>
</div>
...
</div>
*/
// bug list
function updateBugList(divId, divIndex, totalBugs, searchUrl) {
let html = '';
if (totalBugs == 0) {
html = "<div class='data'><a target='_buglist' href='" + searchUrl + "'> </a></div>";
$("#data" + divIndex).replaceWith(html);
return;
}
html = "<div class='data'><a target='_buglist' href='" + searchUrl + "'>" + totalBugs + "</a><div class='data sub'><abbr title=\"Bug(s) in Bugzilla with no `Severity` set\">B</abbr></div></div>";
$("#" + divId + divIndex).replaceWith(html);
}
// updatebot list
function updateBotList(divId, divIndex, totalBugs, searchUrl) {
if (totalBugs == 0) {
$("#ubdata" + divIndex).replaceWith("");
return;
}
$("#ubdata" + divIndex).replaceWith("<div class='ubdata'><a target='_buglist' href=\"" + searchUrl
+ "\">" + totalBugs + "</a><div class='updata sub'><abbr title=\"UpdateBot bug(s) in Bugzilla with no `Severity` set\">UB</abbr></div></div>" );
}
function displayTitle(year, count) {
let team = getTeam();
let title = '';
switch (team) {
case 'graphics':
title = "Graphics Team " + year + " Triage";
break;
case 'media':
title = "Media Team " + year + " Triage";
break;
case 'webrtc':
title = "Web Conferencing Team " + year + " Triage";
break;
}
document.title = title;
$("#title").text(title);
}
function displayYearFooter(currentYear, icsBugQueries) {
var footer = "<div id=\'footer\' class=\'footer\'>";
var nextYear = currentYear + 1;
// The future schedule
footer += "<a href=\'?year=" + nextYear + "&team=" + getTeam() + "\'>" + nextYear + "</a> | ";
let endYear = 2023;
for (var year = currentYear; year >= endYear; year--) {
footer += "<a href=\"?year=" + year + "&team=" + getTeam() + "\">" + year + "</a>";
if (year != endYear) {
footer += ' | ';
}
}
footer += "</div>";
$("#body").append(footer);
}
// Progress for queries
function cb_initProgress() {
document.getElementById('progressmeter').max = TotalQueries;
document.getElementById('progressmeter').value = 0;
document.getElementById('progress').style.visibility = 'visible';
console.log('TotalQueries', TotalQueries);
}
function cb_stepdownProgress() {
document.getElementById('progressmeter').value += 1;
closeProgress();
}
function cb_closeProgress() {
if (document.getElementById('progressmeter').value >= TotalQueries)
document.getElementById('progress').style.visibility = 'hidden';
}
function onSettingsOpened() {
$("#buglists").css('opacity', '0.5');
$("#announcement").css('opacity', '0.5');
}
function onSettingsClosed() {
$("#buglists").css('opacity', '1.0');
$("#announcement").css('opacity', '1.0');
}