Fix genre graph not displaying
- take top genres/artists for graphs - improve graphs' appearance - update pip packages
This commit is contained in:
10
api/utils.py
10
api/utils.py
@@ -222,9 +222,13 @@ def get_artists_in_genre(user, genre):
|
|||||||
|
|
||||||
processed_artist_counts = {}
|
processed_artist_counts = {}
|
||||||
for artist in user_artists:
|
for artist in user_artists:
|
||||||
processed_artist_counts[artist.name] = round(artist.track_set
|
# TODO: figure out collab problem #
|
||||||
.filter(genre=genre_obj, users=user)
|
# artist_count = math.floor(artist.track_set.filter(genre=genre_obj,
|
||||||
.count() * track_count / total_artist_counts, 2)
|
# users=user).count() * track_count / total_artist_counts)
|
||||||
|
artist_count = artist.track_set.filter(genre=genre_obj,
|
||||||
|
users=user).count()
|
||||||
|
if artist_count > 0:
|
||||||
|
processed_artist_counts[artist.name] = artist_count
|
||||||
return processed_artist_counts
|
return processed_artist_counts
|
||||||
|
|
||||||
# }}} get_artists_in_genre #
|
# }}} get_artists_in_genre #
|
||||||
|
|||||||
29
api/views.py
29
api/views.py
@@ -141,7 +141,7 @@ def get_artist_data(request, user_secret):
|
|||||||
artist_counts = Artist.objects.annotate(num_songs=Count('track',
|
artist_counts = Artist.objects.annotate(num_songs=Count('track',
|
||||||
filter=Q(track__users=user)))
|
filter=Q(track__users=user)))
|
||||||
processed_artist_counts = [{'name': artist.name, 'num_songs': artist.num_songs}
|
processed_artist_counts = [{'name': artist.name, 'num_songs': artist.num_songs}
|
||||||
for artist in artist_counts]
|
for artist in artist_counts if artist.num_songs > 1]
|
||||||
if CONSOLE_LOGGING:
|
if CONSOLE_LOGGING:
|
||||||
pprint(processed_artist_counts)
|
pprint(processed_artist_counts)
|
||||||
return JsonResponse(data=processed_artist_counts, safe=False)
|
return JsonResponse(data=processed_artist_counts, safe=False)
|
||||||
@@ -175,7 +175,6 @@ def get_audio_feature_data(request, audio_feature, user_secret):
|
|||||||
|
|
||||||
# get_genre_data {{{ #
|
# get_genre_data {{{ #
|
||||||
|
|
||||||
|
|
||||||
def get_genre_data(request, user_secret):
|
def get_genre_data(request, user_secret):
|
||||||
"""Return genre data needed to create the graph
|
"""Return genre data needed to create the graph
|
||||||
TODO
|
TODO
|
||||||
@@ -187,27 +186,17 @@ def get_genre_data(request, user_secret):
|
|||||||
# annotates each genre and not each Track, due to the earlier values() call
|
# annotates each genre and not each Track, due to the earlier values() call
|
||||||
.annotate(num_songs=Count('genre'))
|
.annotate(num_songs=Count('genre'))
|
||||||
)
|
)
|
||||||
|
genre_counts = [genre_dict for genre_dict in genre_counts if
|
||||||
|
genre_dict['num_songs'] > 3]
|
||||||
|
|
||||||
# genre_counts is a QuerySet with the format
|
# genre_counts is a QuerySet with the format
|
||||||
# [{'genre': 'classical', 'num_songs': 100}, {'genre': 'pop', 'num_songs': 50}...]
|
'''
|
||||||
|
Now genre_counts has the format [ {'genre': 'classical', 'num_songs': 100,
|
||||||
|
'artists': { 'Helene Grimaud': 40.5, 'Beethoven': 31.2, ... }},... ]
|
||||||
|
'''
|
||||||
for genre_dict in genre_counts:
|
for genre_dict in genre_counts:
|
||||||
genre_dict['artists'] = get_artists_in_genre(user, genre_dict['genre'])
|
genre_dict['artists'] = get_artists_in_genre(user, genre_dict['genre'])
|
||||||
'''
|
|
||||||
Now genre_counts has the format
|
|
||||||
[
|
|
||||||
{'genre': 'classical',
|
|
||||||
'num_songs': 100,
|
|
||||||
'artists': {
|
|
||||||
'Helene Grimaud': 40.5,
|
|
||||||
'Beethoven': 31.2,
|
|
||||||
'Mozart': 22...
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{'genre': 'pop',
|
|
||||||
'num_songs': 150,
|
|
||||||
'artists': {...}
|
|
||||||
},...
|
|
||||||
]
|
|
||||||
'''
|
|
||||||
if CONSOLE_LOGGING:
|
if CONSOLE_LOGGING:
|
||||||
print("*** Genre Breakdown ***")
|
print("*** Genre Breakdown ***")
|
||||||
pprint(list(genre_counts))
|
pprint(list(genre_counts))
|
||||||
|
|||||||
@@ -1,137 +1,154 @@
|
|||||||
function create_genre_graph(data) {
|
function create_genre_graph(data) {
|
||||||
// convert strings to nums {{{ //
|
// convert strings to nums {{{ //
|
||||||
|
|
||||||
data.forEach(function(d) {
|
data.forEach(function(d) {
|
||||||
d.num_songs = +d.num_songs;
|
d.num_songs = +d.num_songs;
|
||||||
console.log(d.genre, d.num_songs);
|
console.log(d.genre, d.num_songs);
|
||||||
let artist_names = Object.keys(d.artists);
|
let artist_names = Object.keys(d.artists);
|
||||||
artist_names.forEach(function(e) {
|
artist_names.forEach(function(e) {
|
||||||
d.artists[e] = +d.artists[e];
|
d.artists[e] = +d.artists[e];
|
||||||
console.log(e, d.artists[e]);
|
console.log(e, d.artists[e]);
|
||||||
//console.log(e, d.artists[e], d.artists[e] + 1);
|
//console.log(e, d.artists[e], d.artists[e] + 1);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// }}} 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
|
||||||
|
}) * 1.6]).nice();
|
||||||
|
|
||||||
|
// }}} domains //
|
||||||
|
|
||||||
|
// 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 {{{ //
|
||||||
|
|
||||||
// }}} convert strings to nums //
|
let keys = Object.keys(genre_dict.artists);
|
||||||
|
let stack = d3.stack()
|
||||||
// domains {{{ //
|
.order(d3.stackOrderDescending)
|
||||||
|
.keys(keys)([genre_dict.artists])
|
||||||
data.sort(function(a, b) {
|
// unpack the column
|
||||||
return b.num_songs - a.num_songs;
|
.map((d, i) => {
|
||||||
});
|
return {
|
||||||
x.domain(data.map(function(d) {
|
key: keys[i],
|
||||||
return d.genre;
|
data: d[0]
|
||||||
}));
|
}
|
||||||
// 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 //
|
|
||||||
|
|
||||||
// setup bar colors {{{ //
|
|
||||||
|
|
||||||
let max_artists = d3.max(data, function(d) {
|
// }}} process artist breakdown //
|
||||||
return Object.keys(d.artists).length;
|
|
||||||
});
|
// add bars {{{ //
|
||||||
let colorScale = d3.scaleOrdinal().range(randomColor({
|
|
||||||
count: max_artists,
|
|
||||||
luminosity: 'light',
|
|
||||||
}));
|
|
||||||
|
|
||||||
// }}} setup bar colors //
|
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 //
|
||||||
|
|
||||||
for (let genre_dict of data) {
|
// x-axis {{{ //
|
||||||
|
|
||||||
// process artist breakdown {{{ //
|
g.append("g")
|
||||||
|
.attr("class", "axis")
|
||||||
let keys = Object.keys(genre_dict.artists);
|
.attr("transform", "translate(0," + height + ")")
|
||||||
let stack = d3.stack()
|
.call(d3.axisBottom(x))
|
||||||
.order(d3.stackOrderDescending)
|
.selectAll(".tick text")
|
||||||
.keys(keys)([genre_dict.artists])
|
.style('font-size', '1.5em')
|
||||||
// unpack the column
|
.call(wrap, x.bandwidth());
|
||||||
.map((d, i) => {
|
|
||||||
return {
|
// }}} x-axis //
|
||||||
key: keys[i],
|
|
||||||
data: d[0]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// }}} process artist breakdown //
|
|
||||||
|
|
||||||
// add bars {{{ //
|
// y-axis {{{ //
|
||||||
|
|
||||||
g.append("g")
|
g.append("g")
|
||||||
.selectAll("rect")
|
.attr("class", "axis")
|
||||||
.data(stack)
|
.call(d3.axisLeft(y).ticks(null, "s"))
|
||||||
.enter().append("rect")
|
.append("text")
|
||||||
.attr("x", x(genre_dict.genre))
|
.attr("x", 2)
|
||||||
.attr("y", function(d) {
|
.attr("y", y(y.ticks().pop()) + 0.5)
|
||||||
return y(d.data[1]);
|
.attr("dy", "0.32em")
|
||||||
})
|
.attr("fill", "white")
|
||||||
.attr("height", d => y(d.data[0]) - y(d.data[1]))
|
.style('font-size', '2em')
|
||||||
.attr("width", x.bandwidth())
|
.attr("text-anchor", "start")
|
||||||
.attr('fill', (d, i) => colorScale(i))
|
.text("Songs");
|
||||||
// keep 3 significant figures in the song count label
|
|
||||||
.append('title').text(d => d.key + ': ' + (d.data[1] - d.data[0]).toPrecision(3));
|
// }}} y-axis //
|
||||||
|
|
||||||
// }}} add bars //
|
|
||||||
|
|
||||||
// x-axis {{{ //
|
// title {{{ //
|
||||||
|
|
||||||
g.append("g")
|
g.append("text")
|
||||||
.attr("class", "axis")
|
.attr('x', (width / 2))
|
||||||
.attr("transform", "translate(0," + height + ")")
|
.attr('y', (margin.top / 2))
|
||||||
.call(d3.axisBottom(x))
|
.attr('fill', "white")
|
||||||
.selectAll(".tick text")
|
.attr('text-anchor', 'middle')
|
||||||
.call(wrap, x.bandwidth());
|
.attr("font-weight", "bold")
|
||||||
|
.style('font-size', '2em')
|
||||||
// }}} x-axis //
|
.text('Genre Graph (With Artists)');
|
||||||
|
|
||||||
|
// }}} title //
|
||||||
|
|
||||||
// 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 //
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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() {
|
text.each(function() {
|
||||||
let text = d3.select(this),
|
let text = d3.select(this),
|
||||||
words = text.text().split(/\s+/).reverse(),
|
words = text.text().split(/\s+/).reverse(),
|
||||||
word,
|
word,
|
||||||
line = [],
|
line = [],
|
||||||
lineNumber = 0,
|
lineNumber = 0,
|
||||||
lineHeight = 1.1, // ems
|
lineHeight = 1.1, // ems
|
||||||
y = text.attr("y"),
|
y = text.attr("y"),
|
||||||
dy = parseFloat(text.attr("dy")),
|
dy = parseFloat(text.attr("dy")),
|
||||||
tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em")
|
tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em")
|
||||||
while (word = words.pop()) {
|
while (word = words.pop()) {
|
||||||
line.push(word);
|
line.push(word);
|
||||||
tspan.text(line.join(" "));
|
tspan.text(line.join(" "));
|
||||||
if (tspan.node().getComputedTextLength() > width) {
|
if (tspan.node().getComputedTextLength() > width) {
|
||||||
line.pop();
|
line.pop();
|
||||||
tspan.text(line.join(" "));
|
tspan.text(line.join(" "));
|
||||||
line = [word];
|
line = [word];
|
||||||
tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", `${++lineNumber * lineHeight + dy}em`).text(word);
|
tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", `${++lineNumber * lineHeight + dy}em`).text(word);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// }}} wrap text //
|
// }}} wrap text //
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
{% load static %}
|
{% load static %}
|
||||||
{% load sass_tags %}
|
{% load sass_tags %}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
|
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
|
||||||
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
|
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
|
||||||
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
|
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
|
||||||
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
|
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<title>Features Graphs</title>
|
<title>Features Graphs</title>
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="stylesheet" href="{% sass_src 'scss/custom.scss' %}">
|
<link rel="stylesheet" href="{% sass_src 'scss/custom.scss' %}">
|
||||||
<style>
|
<style>
|
||||||
.tick {
|
.tick {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<!--[if lt IE 7]>
|
<!--[if lt IE 7]>
|
||||||
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="#">upgrade your browser</a> to improve your experience.</p>
|
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="#">upgrade your browser</a> to improve your experience.</p>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -38,10 +38,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="https://d3js.org/d3.v5.js"></script>
|
<script src="https://d3js.org/d3.v5.js"></script>
|
||||||
<script src="{% static "graphs/scripts/audio_feat_graph.js" %}"></script>
|
<script src="{% static "graphs/scripts/audio_feat_graph.js" %}"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
let userSecret = "{{ user_secret }}";
|
let userSecret = "{{ user_secret }}";
|
||||||
let graphParams = {
|
let graphParams = {
|
||||||
"acousticness": {
|
"acousticness": {
|
||||||
intervalEndPoints: {begin: 0, end: 1.0, step: 0.20},
|
intervalEndPoints: {begin: 0, end: 1.0, step: 0.20},
|
||||||
@@ -74,6 +74,6 @@
|
|||||||
drawAudioFeatGraph(featureKey, params.intervalEndPoints,
|
drawAudioFeatGraph(featureKey, params.intervalEndPoints,
|
||||||
params.colId, userSecret);
|
params.colId, userSecret);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<!-- header {{{ -->
|
<!-- header {{{ -->
|
||||||
|
|
||||||
<!DOC
|
<!DOC
|
||||||
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
|
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
|
||||||
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
|
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
|
||||||
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
|
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
|
||||||
<!--[if gt IE 8]><!-->
|
<!--[if gt IE 8]><!-->
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load sass_tags %}
|
{% load sass_tags %}
|
||||||
@@ -14,44 +14,46 @@
|
|||||||
<title>Genre Graph</title>
|
<title>Genre Graph</title>
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<!-- <link rel="stylesheet" href="{% sass_src 'scss/custom.scss' %}"> -->
|
<link rel="stylesheet" href="{% sass_src 'scss/custom.scss' %}">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<!-- }}} header -->
|
<!-- }}} header -->
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<script src="https://d3js.org/d3.v5.min.js"></script>
|
<script src="https://d3js.org/d3.v5.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/randomcolor/0.5.2/randomColor.min.js"></script>
|
||||||
|
|
||||||
{% load static %}
|
{% load static %}
|
||||||
<script src="{% static "graphs/scripts/genre_graph.js" %}"></script>
|
<script src="{% static "graphs/scripts/genre_graph.js" %}"></script>
|
||||||
|
|
||||||
<!-- <div class="row">
|
<!-- <div class="row">
|
||||||
<div class="col-" id="genre-graph"></div>
|
<div class="col-" id="genre-graph"></div>
|
||||||
</div> -->
|
</div> -->
|
||||||
|
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12" id="genre-column">
|
<div class="col-md-12" id="genre-column">
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<svg id="genre-graph" width="600" height="400"
|
</div>
|
||||||
viewBox="0 0 600 400"
|
{% comment %} <svg id="genre-graph" width="600" height="400"
|
||||||
perserveAspectRatio="xMinYMid">
|
viewBox="0 0 600 400"
|
||||||
</svg>
|
perserveAspectRatio="xMinYMid"> {% endcomment %}
|
||||||
|
<svg id="genre-graph" width="1500" height="900">
|
||||||
|
</svg>
|
||||||
<script>
|
<script>
|
||||||
let svg = d3.select("svg"),
|
let svg = d3.select("svg"),
|
||||||
margin = {top: 20, right: 20, bottom: 30, left: 40},
|
margin = {top: 20, right: 20, bottom: 30, left: 40},
|
||||||
width = +svg.attr("width") - margin.left - margin.right,
|
width = +svg.attr("width") - margin.left - margin.right,
|
||||||
height = +svg.attr("height") - margin.top - margin.bottom,
|
height = +svg.attr("height") - margin.top - margin.bottom,
|
||||||
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
||||||
let x = d3.scaleBand()
|
let x = d3.scaleBand()
|
||||||
.rangeRound([0, width])
|
.rangeRound([0, width])
|
||||||
.paddingInner(0.1)
|
.paddingInner(0.1)
|
||||||
.paddingOuter(0.7)
|
.paddingOuter(0.7)
|
||||||
.align(0.1);
|
.align(0.1);
|
||||||
let y = d3.scaleLinear()
|
let y = d3.scaleLinear()
|
||||||
.rangeRound([height, 0]);
|
.rangeRound([height, 0]);
|
||||||
|
|
||||||
d3.json("{% url "api:get_genre_data" user_secret %}").then(create_genre_graph);
|
d3.json("{% url "api:get_genre_data" user_secret %}").then(create_genre_graph);
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,40 +1,37 @@
|
|||||||
astroid==2.0.4
|
astroid==2.2.4
|
||||||
certifi==2018.10.15
|
certifi==2018.11.29
|
||||||
chardet==3.0.4
|
chardet==3.0.4
|
||||||
Django==2.1.3
|
defusedxml==0.5.0
|
||||||
django-appconf==1.0.2
|
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
|
django-tables2==2.0.5
|
||||||
djangorestframework==3.9.0
|
djangorestframework==3.9.2
|
||||||
docopt==0.6.2
|
|
||||||
et-xmlfile==1.0.1
|
et-xmlfile==1.0.1
|
||||||
idna==2.7
|
idna==2.8
|
||||||
isort==4.3.4
|
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
|
odfpy==1.4.0
|
||||||
openpyxl==2.5.10
|
openpyxl==2.6.1
|
||||||
pkg-resources==0.0.0
|
psycopg2-binary==2.7.7
|
||||||
psycopg2==2.7.6.1
|
pylint==2.3.1
|
||||||
psycopg2-binary==2.7.6.1
|
python-dateutil==2.8.0
|
||||||
pylint==2.1.1
|
pytz==2018.9
|
||||||
python-dateutil==2.7.5
|
|
||||||
pytz==2018.7
|
|
||||||
PyYAML==3.13
|
PyYAML==3.13
|
||||||
rcssmin==1.0.6
|
rcssmin==1.0.6
|
||||||
requests==2.20.1
|
requests==2.21.0
|
||||||
rjsmin==1.0.12
|
rjsmin==1.1.0
|
||||||
six==1.11.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
|
wrapt==1.11.1
|
||||||
xlrd==1.1.0
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user