Browse Source
Fix genre graph not displaying
Fix genre graph not displaying
- take top genres/artists for graphs - improve graphs' appearance - update pip packagesmaster
Kevin Mok
6 years ago
7 changed files with 226 additions and 215 deletions
-
10api/utils.py
-
29api/views.py
-
251graphs/static/graphs/scripts/genre_graph.js
-
50graphs/templates/graphs/features_graphs.html
-
50graphs/templates/graphs/genre_graph.html
-
49requirements.txt
-
2update-history.sh
@ -1,137 +1,154 @@ |
|||
function create_genre_graph(data) { |
|||
// convert strings to nums {{{ //
|
|||
|
|||
data.forEach(function(d) { |
|||
d.num_songs = +d.num_songs; |
|||
console.log(d.genre, d.num_songs); |
|||
let artist_names = Object.keys(d.artists); |
|||
artist_names.forEach(function(e) { |
|||
d.artists[e] = +d.artists[e]; |
|||
console.log(e, d.artists[e]); |
|||
//console.log(e, d.artists[e], d.artists[e] + 1);
|
|||
}); |
|||
// convert strings to nums {{{ //
|
|||
|
|||
data.forEach(function(d) { |
|||
d.num_songs = +d.num_songs; |
|||
console.log(d.genre, d.num_songs); |
|||
let artist_names = Object.keys(d.artists); |
|||
artist_names.forEach(function(e) { |
|||
d.artists[e] = +d.artists[e]; |
|||
console.log(e, d.artists[e]); |
|||
//console.log(e, d.artists[e], d.artists[e] + 1);
|
|||
}); |
|||
|
|||
// }}} convert strings to nums //
|
|||
}); |
|||
|
|||
// }}} convert strings to nums //
|
|||
|
|||
// domains {{{ //
|
|||
|
|||
data.sort(function(a, b) { |
|||
return b.num_songs - a.num_songs; |
|||
}); |
|||
x.domain(data.map(function(d) { |
|||
return d.genre; |
|||
})); |
|||
// y.domain([0, d3.max(data, function(d) { return d.num_songs; }) * 1.25]).nice();
|
|||
y.domain([0, d3.max(data, function(d) { |
|||
return d.num_songs; // returns the maximum number of songs in the genre
|
|||
})]).nice(); |
|||
|
|||
// }}} domains //
|
|||
// domains {{{ //
|
|||
|
|||
data.sort(function(a, b) { |
|||
return b.num_songs - a.num_songs; |
|||
}); |
|||
x.domain(data.map(function(d) { |
|||
return d.genre; |
|||
})); |
|||
// y.domain([0, d3.max(data, function(d) { return d.num_songs; }) * 1.25]).nice();
|
|||
y.domain([0, d3.max(data, function(d) { |
|||
return d.num_songs; // returns the maximum number of songs in the genre
|
|||
}) * 1.6]).nice(); |
|||
|
|||
// }}} domains //
|
|||
|
|||
// setup bar colors {{{ //
|
|||
// setup bar colors {{{ //
|
|||
|
|||
let max_artists = d3.max(data, function(d) { |
|||
return Object.keys(d.artists).length; |
|||
}); |
|||
let colorScale = d3.scaleOrdinal().range(randomColor({ |
|||
count: max_artists, |
|||
luminosity: 'light', |
|||
// hue: '#3399FF',
|
|||
// hue: '#00ced1',
|
|||
hue: '#0099CC', |
|||
})); |
|||
|
|||
// }}} setup bar colors //
|
|||
|
|||
for (let genre_dict of data) { |
|||
|
|||
// process artist breakdown {{{ //
|
|||
|
|||
let max_artists = d3.max(data, function(d) { |
|||
return Object.keys(d.artists).length; |
|||
}); |
|||
let colorScale = d3.scaleOrdinal().range(randomColor({ |
|||
count: max_artists, |
|||
luminosity: 'light', |
|||
})); |
|||
let keys = Object.keys(genre_dict.artists); |
|||
let stack = d3.stack() |
|||
.order(d3.stackOrderDescending) |
|||
.keys(keys)([genre_dict.artists]) |
|||
// unpack the column
|
|||
.map((d, i) => { |
|||
return { |
|||
key: keys[i], |
|||
data: d[0] |
|||
} |
|||
}); |
|||
|
|||
// }}} setup bar colors //
|
|||
// }}} process artist breakdown //
|
|||
|
|||
for (let genre_dict of data) { |
|||
|
|||
// process artist breakdown {{{ //
|
|||
|
|||
let keys = Object.keys(genre_dict.artists); |
|||
let stack = d3.stack() |
|||
.order(d3.stackOrderDescending) |
|||
.keys(keys)([genre_dict.artists]) |
|||
// unpack the column
|
|||
.map((d, i) => { |
|||
return { |
|||
key: keys[i], |
|||
data: d[0] |
|||
} |
|||
}); |
|||
|
|||
// }}} process artist breakdown //
|
|||
// add bars {{{ //
|
|||
|
|||
g.append("g") |
|||
.selectAll("rect") |
|||
.data(stack) |
|||
.enter().append("rect") |
|||
.attr("x", x(genre_dict.genre)) |
|||
.attr("y", function(d) { |
|||
return y(d.data[1]); |
|||
}) |
|||
.attr("height", d => y(d.data[0]) - y(d.data[1])) |
|||
.attr("width", x.bandwidth()) |
|||
.attr('fill', (d, i) => colorScale(i)) |
|||
.style('font-size', '1.5em') |
|||
.append('title').text(d => d.key + ': ' + (d.data[1] - d.data[0]).toPrecision(1)); |
|||
|
|||
// }}} add bars //
|
|||
|
|||
// add bars {{{ //
|
|||
|
|||
g.append("g") |
|||
.selectAll("rect") |
|||
.data(stack) |
|||
.enter().append("rect") |
|||
.attr("x", x(genre_dict.genre)) |
|||
.attr("y", function(d) { |
|||
return y(d.data[1]); |
|||
}) |
|||
.attr("height", d => y(d.data[0]) - y(d.data[1])) |
|||
.attr("width", x.bandwidth()) |
|||
.attr('fill', (d, i) => colorScale(i)) |
|||
// keep 3 significant figures in the song count label
|
|||
.append('title').text(d => d.key + ': ' + (d.data[1] - d.data[0]).toPrecision(3)); |
|||
|
|||
// }}} add bars //
|
|||
// x-axis {{{ //
|
|||
|
|||
g.append("g") |
|||
.attr("class", "axis") |
|||
.attr("transform", "translate(0," + height + ")") |
|||
.call(d3.axisBottom(x)) |
|||
.selectAll(".tick text") |
|||
.style('font-size', '1.5em') |
|||
.call(wrap, x.bandwidth()); |
|||
|
|||
// }}} x-axis //
|
|||
|
|||
// x-axis {{{ //
|
|||
|
|||
g.append("g") |
|||
.attr("class", "axis") |
|||
.attr("transform", "translate(0," + height + ")") |
|||
.call(d3.axisBottom(x)) |
|||
.selectAll(".tick text") |
|||
.call(wrap, x.bandwidth()); |
|||
|
|||
// }}} x-axis //
|
|||
// y-axis {{{ //
|
|||
|
|||
g.append("g") |
|||
.attr("class", "axis") |
|||
.call(d3.axisLeft(y).ticks(null, "s")) |
|||
.append("text") |
|||
.attr("x", 2) |
|||
.attr("y", y(y.ticks().pop()) + 0.5) |
|||
.attr("dy", "0.32em") |
|||
.attr("fill", "white") |
|||
.style('font-size', '2em') |
|||
.attr("text-anchor", "start") |
|||
.text("Songs"); |
|||
|
|||
// }}} y-axis //
|
|||
|
|||
// y-axis {{{ //
|
|||
|
|||
g.append("g") |
|||
.attr("class", "axis") |
|||
.call(d3.axisLeft(y).ticks(null, "s")) |
|||
.append("text") |
|||
.attr("x", 2) |
|||
.attr("y", y(y.ticks().pop()) + 0.5) |
|||
.attr("dy", "0.32em") |
|||
.attr("fill", "#000") |
|||
.attr("font-weight", "bold") |
|||
.attr("text-anchor", "start") |
|||
.text("Songs"); |
|||
|
|||
// }}} y-axis //
|
|||
// title {{{ //
|
|||
|
|||
g.append("text") |
|||
.attr('x', (width / 2)) |
|||
.attr('y', (margin.top / 2)) |
|||
.attr('fill', "white") |
|||
.attr('text-anchor', 'middle') |
|||
.attr("font-weight", "bold") |
|||
.style('font-size', '2em') |
|||
.text('Genre Graph (With Artists)'); |
|||
|
|||
// }}} title //
|
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
// wrap text {{{ //
|
|||
// wrapping long labels
|
|||
// https://gist.github.com/guypursey/f47d8cd11a8ff24854305505dbbd8c07#file-index-html
|
|||
function wrap(text, width) { |
|||
text.each(function() { |
|||
let text = d3.select(this), |
|||
words = text.text().split(/\s+/).reverse(), |
|||
word, |
|||
line = [], |
|||
lineNumber = 0, |
|||
lineHeight = 1.1, // ems
|
|||
y = text.attr("y"), |
|||
dy = parseFloat(text.attr("dy")), |
|||
tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em") |
|||
while (word = words.pop()) { |
|||
line.push(word); |
|||
tspan.text(line.join(" ")); |
|||
if (tspan.node().getComputedTextLength() > width) { |
|||
line.pop(); |
|||
tspan.text(line.join(" ")); |
|||
line = [word]; |
|||
tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", `${++lineNumber * lineHeight + dy}em`).text(word); |
|||
} |
|||
} |
|||
}) |
|||
text.each(function() { |
|||
let text = d3.select(this), |
|||
words = text.text().split(/\s+/).reverse(), |
|||
word, |
|||
line = [], |
|||
lineNumber = 0, |
|||
lineHeight = 1.1, // ems
|
|||
y = text.attr("y"), |
|||
dy = parseFloat(text.attr("dy")), |
|||
tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em") |
|||
while (word = words.pop()) { |
|||
line.push(word); |
|||
tspan.text(line.join(" ")); |
|||
if (tspan.node().getComputedTextLength() > width) { |
|||
line.pop(); |
|||
tspan.text(line.join(" ")); |
|||
line = [word]; |
|||
tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", `${++lineNumber * lineHeight + dy}em`).text(word); |
|||
} |
|||
} |
|||
}) |
|||
} |
|||
|
|||
// }}} wrap text //
|
@ -1,40 +1,37 @@ |
|||
astroid==2.0.4 |
|||
certifi==2018.10.15 |
|||
astroid==2.2.4 |
|||
certifi==2018.11.29 |
|||
chardet==3.0.4 |
|||
Django==2.1.3 |
|||
django-appconf==1.0.2 |
|||
defusedxml==0.5.0 |
|||
Django==2.1.7 |
|||
django-appconf==1.0.3 |
|||
django-compressor==2.2 |
|||
django-crispy-forms==1.7.2 |
|||
django-filter==2.0.0 |
|||
django-filter==2.1.0 |
|||
django-sass-processor==0.7.2 |
|||
django-tables2==2.0.3 |
|||
djangorestframework==3.9.0 |
|||
docopt==0.6.2 |
|||
django-tables2==2.0.5 |
|||
djangorestframework==3.9.2 |
|||
et-xmlfile==1.0.1 |
|||
idna==2.7 |
|||
isort==4.3.4 |
|||
idna==2.8 |
|||
isort==4.3.12 |
|||
jdcal==1.4 |
|||
lazy-object-proxy==1.3.1 |
|||
libsass==0.16.0 |
|||
libsass==0.17.0 |
|||
mccabe==0.6.1 |
|||
odfpy==1.3.6 |
|||
openpyxl==2.5.10 |
|||
pkg-resources==0.0.0 |
|||
psycopg2==2.7.6.1 |
|||
psycopg2-binary==2.7.6.1 |
|||
pylint==2.1.1 |
|||
python-dateutil==2.7.5 |
|||
pytz==2018.7 |
|||
odfpy==1.4.0 |
|||
openpyxl==2.6.1 |
|||
psycopg2-binary==2.7.7 |
|||
pylint==2.3.1 |
|||
python-dateutil==2.8.0 |
|||
pytz==2018.9 |
|||
PyYAML==3.13 |
|||
rcssmin==1.0.6 |
|||
requests==2.20.1 |
|||
rjsmin==1.0.12 |
|||
six==1.11.0 |
|||
requests==2.21.0 |
|||
rjsmin==1.1.0 |
|||
six==1.12.0 |
|||
tablib==0.12.1 |
|||
typed-ast==1.1.0 |
|||
typed-ast==1.3.1 |
|||
unicodecsv==0.14.1 |
|||
urllib3==1.24.1 |
|||
wrapt==1.10.11 |
|||
xlrd==1.1.0 |
|||
wrapt==1.11.1 |
|||
xlrd==1.2.0 |
|||
xlwt==1.3.0 |
|||
yarg==0.1.9 |
@ -1 +1,3 @@ |
|||
#!/bin/bash |
|||
|
|||
/home/kevin/coding/spotify-lib-vis/bin/python /home/kevin/coding/spotify-lib-vis/src/manage.py update-history >> /home/kevin/coding/spotify-lib-vis/src/api/management/commands/update-history.log |
Write
Preview
Loading…
Cancel
Save
Reference in new issue