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) { |
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 {{{ //
|
// wrap text {{{ //
|
||||
// wrapping long labels
|
// wrapping long labels
|
||||
// https://gist.github.com/guypursey/f47d8cd11a8ff24854305505dbbd8c07#file-index-html
|
// https://gist.github.com/guypursey/f47d8cd11a8ff24854305505dbbd8c07#file-index-html
|
||||
function wrap(text, width) { |
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 //
|
// }}} 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 |
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-compressor==2.2 |
||||
django-crispy-forms==1.7.2 |
django-crispy-forms==1.7.2 |
||||
django-filter==2.0.0 |
|
||||
|
django-filter==2.1.0 |
||||
django-sass-processor==0.7.2 |
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 |
et-xmlfile==1.0.1 |
||||
idna==2.7 |
|
||||
isort==4.3.4 |
|
||||
|
idna==2.8 |
||||
|
isort==4.3.12 |
||||
jdcal==1.4 |
jdcal==1.4 |
||||
lazy-object-proxy==1.3.1 |
lazy-object-proxy==1.3.1 |
||||
libsass==0.16.0 |
|
||||
|
libsass==0.17.0 |
||||
mccabe==0.6.1 |
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 |
PyYAML==3.13 |
||||
rcssmin==1.0.6 |
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 |
tablib==0.12.1 |
||||
typed-ast==1.1.0 |
|
||||
|
typed-ast==1.3.1 |
||||
unicodecsv==0.14.1 |
unicodecsv==0.14.1 |
||||
urllib3==1.24.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 |
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 |
/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