Skip to content

Instantly share code, notes, and snippets.

@kjgarza
Forked from peterneish/.block
Last active September 13, 2019 10:55
Show Gist options
  • Save kjgarza/19fbdef59283c440cdc3ee0bf1eb1a78 to your computer and use it in GitHub Desktop.
Save kjgarza/19fbdef59283c440cdc3ee0bf1eb1a78 to your computer and use it in GitHub Desktop.
Visualise issues for DataCite
licence: gpl-3.0
height: 800
scrolling: yes
border: yes

Visualisation of issues using labels for the DataCite User Stories. Issues were categorised using a kanban-style GitHub project

Hover over a category to see which labels are related - the wider the ribbon, the more tags are in common.

You can also click on a category to open the issue list for that label.

Kristian Garza

<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.group-tick line {
stroke: #000;
}
.ribbons {
fill-opacity: 0.67;
}
</style>
<svg width="1000" height="750"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var json_file = "https://api.github.com/search/issues?q=repo:datacite/datacite+-label:user%20story";
//var json_file = "issues.json"; // for local dev
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
outerRadius = Math.min(width, height) * 0.5 - 40,
innerRadius = outerRadius - 30;
var formatValue = d3.formatPrefix(",.0", 1e3);
var chord = d3.chord()
.padAngle(0.05)
.sortSubgroups(d3.descending);
var arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var ribbon = d3.ribbon()
.radius(innerRadius);
var ribbons;
var labels;
var color = d3.scaleOrdinal()
.domain(d3.range(4))
.range(["#000000", "#FFDD89", "#957244", "#F26223"]);
d3.json(json_file, function(data){
var transformed = transformData(data);
var matrix = transformed.matrix;
labels = transformed.labels;
var g = svg.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
.datum(chord(matrix));
var group = g.append("g")
.attr("class", "groups")
.selectAll("g")
.data(function(chords) { return chords.groups; })
.enter().append("g")
.append("a").attr("href", function(d) {
return 'https://github.com/datacite/datacite/issues?q=label%3A"' + labels[d.index].name + '"' ;
})
.attr("target", "_blank")
.on("mouseover", handleMouseOver())
.on("mouseout", handleMouseOut());
group.append("path")
.style("fill", function(d) { return labels[d.index].color; })
.style("stroke", function(d) { return d3.rgb(labels[d.index].color).darker(); })
.attr("d", arc);
ribbons = g.append("g")
.attr("class", "ribbons")
.selectAll("path")
.data(function(chords) { return chords; })
.enter().append("path")
.attr("d", ribbon)
.style("fill", function(d) { return labels[d.source.index].color; })
.style("stroke", function(d) { return d3.rgb(labels[d.source.index].color).darker(); });
var groupText = group.append("text")
.each(function(d) { d.angle = (d.startAngle + d.endAngle) / 2; })
.attr("x", 6)
.attr("dy", ".35em")
.attr("transform", function(d) {
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
+ "translate(" + (innerRadius + 26) + ")"
+ (d.angle > Math.PI ? " " : "");
})
.text(function(d,i) { return labels[i].name;});
}); // end d3.json
// creates matrix from the json data
function transformData(data){
var matrix = [];
var labels = [];
// extract labels and create the matrix and the list of values
data.items.forEach(function(item){
item.labels.forEach(function(label){
labels[label.name] = {'name': label.name, 'color': label.color, 'url': label.url};
item.labels.forEach(function(related){
if(!matrix[label.name]){
matrix[label.name] = [];
}
if(!matrix[label.name][related.name]){
matrix[label.name][related.name] = 0;
}
if(label.id != related.id){
matrix[label.name][related.name]++;
}
});
});
});
// now put the labels into an array for easier handling
var sorted_labels = [];
Object.keys(labels).sort().forEach(function(lcol){
sorted_labels.push(labels[lcol]);
});
labels = sorted_labels.sort(getSortMethod('+color','+name'));
var ret = [];
var ret_labels = [];
//Object.keys(labels).sort().forEach(function(lcol){
labels.forEach(function(lcol){
var r = [];
labels.forEach(function(lrow){
if(matrix[lcol.name][lrow.name]){
r.push(matrix[lcol.name][lrow.name]);
}
else{
r.push(0);
}
});
ret.push(r);
});
return {'matrix': ret, 'labels': labels};
}
function getSortMethod(){
var _args = Array.prototype.slice.call(arguments);
return function(a, b){
for(var x in _args){
var ax = a[_args[x].substring(1)];
var bx = b[_args[x].substring(1)];
var cx;
ax = typeof ax == "string" ? ax.toLowerCase() : ax / 1;
bx = typeof bx == "string" ? bx.toLowerCase() : bx / 1;
if(_args[x].substring(0,1) == "-"){cx = ax; ax = bx; bx = cx;}
if(ax != bx){return ax < bx ? -1 : 1;}
}
}
}
function fade(opacity) {
return function(d, i) {
ribbons
.filter(function(d) {
return d.source.index != i && d.target.index != i;
})
.style("opacity", opacity);
ribbons
.filter(function(d) {
return d.source.index == i || d.target.index == i;
})
.style("fill", function(d) { console.log(labels[i].color); return "#" + labels[i].color;});
};
}
function handleMouseOver(){
return function(d,i){
ribbons.filter(function(d) { return d.source.index != i && d.target.index != i; })
.style("opacity", 0.05);
ribbons.filter(function(d) { return d.source.index == i || d.target.index == i; })
.style("fill", function(d) { return "#" + labels[i].color;})
.style("opacity",1);
};
}
function handleMouseOut(){
return function(d,i){
// ribbons.filter(function(d) { return d.source.index != i && d.target.index != i; })
// .style("opacity", 1);
};
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment