Skip to content

Instantly share code, notes, and snippets.

@rayburgemeestre
Last active February 17, 2021 12:54
Show Gist options
  • Save rayburgemeestre/e2a4ed179d0820c0e8d4 to your computer and use it in GitHub Desktop.
Save rayburgemeestre/e2a4ed179d0820c0e8d4 to your computer and use it in GitHub Desktop.
Tampermonkey jira priority
// ==UserScript==
// @name Visualize issue ranks in the Jira Agile board
// @namespace http://cppse.nl
// @version 2.3.4.3
// @description It will search for the ranks using an Ajax Jira API call with two seconds in between
// and update the board with colored rank indicators.
// @author Ray Burgemeetre
// @match https://*/secure/RapidBoard.jspa*
// @updateURL http://cppse.nl/public/tampermonkey_agile_board_prios-meta.js
// @downloadURL http://cppse.nl/public/tampermonkey_agile_board_prios.js
// @grant none
// ==/UserScript==
(function () {
// Configuration
var // The JQL for fetching the ranks
jql = "sprint in openSprints() and status != 'Done' AND type not in (Sub-task) order by Rank",
// First four colors are shades of green.. (after that, stuff fades from grey to red)
colors = [green(0), green(1), green(2), green(3)];
// Just for my convenience
if (window.location.href.match(/brightcomputing/)) {
jql = "sprint in openSprints() and status not in ('Done', 'Resolved', 'Closed') AND type not in (Sub-task) AND project in ('Cluster Manager', 'Master Installer', 'Miscellaneous') AND labels in (bigdata,hadoop, spark) order by Rank"
}
function updateRanksInBoard()
{
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState != XMLHttpRequest.DONE)
return;
if (xmlhttp.status == 200) {
var ranks = {}, issues = document.querySelectorAll('.ghx-issue'), isSubTask,
key, color, rankBoxEl, rankEl, tmp;
// console.log(xmlhttp.responseText);
var response = JSON.parse(xmlhttp.responseText);
// Create lookup for ranks per issue key
for (var i in response['issues']) {
ranks[response['issues'][i]['key']] = parseInt(i) + 1;
}
// Visualize them in a small box for each issue
for (var i=0; i<issues.length; i++) {
key = issues[i].getAttribute('data-issue-key');
isSubTask = issues[i].className.indexOf('ghx-issue-subtask') !== -1;
color = false;
// Visualize subtasks with the ranking of their parent
if (isSubTask) {
key = issues[i].parentNode.parentNode.getAttribute('data-issue-key');
color = '#E8F5FF';
}
if (!ranks[key])
continue;
if (!color) {
tmp = Object.keys(response['issues']).length;
color = colors[ranks[key] - 1] || red(ranks[key] - colors.length - 1, tmp);
}
try {
// Ensure the "ghx-end" div exists, simply create it if it doesn't.
// (if issues have no estimates etc., this div isn't rendered)
if (!(tmp = issues[i].querySelector('.ghx-end'))) {
tmp = document.createElement('div');
tmp.className = 'ghx-end';
issues[i].appendChild(tmp);
}
// Create rank cornered indicator if it doesn't exist yet
rankEl = issues[i].querySelector('.ghx-end .my-rank');
if (!rankEl) {
rankBoxEl = document.createElement('div');
rankBoxEl.className = 'ghx-corner';
rankEl = document.createElement('span');
rankEl.className = 'aui-badge my-rank';
rankBoxEl.appendChild(rankEl);
issues[i].querySelector('.ghx-end').appendChild(rankBoxEl);
}
// Populate indicator
rankEl.style.backgroundColor = color;
rankEl.innerHTML = ranks[key];
// Squeeze them to fit
tmp = issues[i].querySelector('.ghx-end').querySelectorAll('.ghx-corner');
for (var j=0; j<tmp.length; j++) {
tmp[j].style.float = 'left';
}
}
catch (e) {
console.log('DOM manipulation failed, ignoring exception (probably variant of Jira Board): ', e);
}
}
// Periodically update ranks
setTimeout(updateRanksInBoard, 2000);
}
else {
console.log(textStatus + " : " + errorThrown);
// Periodically update ranks
setTimeout(updateRanksInBoard, 2000);
}
}
xmlhttp.open("GET", "//" + window.location.host + "/rest/api/latest/search?jql=" + escape(jql), true);
xmlhttp.setRequestHeader('Content-type', 'application/json');
xmlhttp.send();
}
//window.addEventListener('DOMContentLoaded', updateRanksInBoard, false);
setTimeout(updateRanksInBoard, 3000); // somehow Jira messes up window.onload, and some other alternatives I tried...
// Utility functions
function green(level)
{
// Starts at rgb(0, 255, 0) and fades to rgb(255, 255, 255)
function divideTimes(num, divide, times)
{
if (!times)
return num;
--times;
if (!times)
return num / divide;
else
return divideTimes(num / divide, divide, times);
}
var c = Math.floor(255.0 - divideTimes(255, 2, level));
return 'rgb(' + c + ', 255, ' + c + ')';
}
function red(level, maxlevel)
{
// Starts at rgb(237, 237, 237) and fades to rgb(255, 0, 0)
try {
var r = (((255 - 237) / maxlevel) * level) + 237,
g = 237 - (( 237 / maxlevel) * level),
b = 237 - (( 237 / maxlevel) * level);
return 'rgb(' + Math.floor(r) + ', ' + Math.floor(g) + ', ' + Math.floor(b) + ')';
}
catch (e) { return 'rgb(255, 0, 0)'; }
}
})();
@rayburgemeestre
Copy link
Author

Hi Kat, Thanks! I've refactored out the $.ajax() call, hopefully that will work better. Thanks :)

@rayburgemeestre
Copy link
Author

I also made it a little more generic/robust hopefully (and project independant, hope it still works for AutoTrack?) updating should be possible via Tampermonkey itself. I needed a few changes to get it to work on another Jira 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment