diff --git a/graphs/static/graphs/scripts/artist_graph.js b/graphs/static/graphs/scripts/artist_graph.js index c1f48cb..023dfd9 100644 --- a/graphs/static/graphs/scripts/artist_graph.js +++ b/graphs/static/graphs/scripts/artist_graph.js @@ -5,13 +5,25 @@ */ function drawArtistGraph(artistData, parentElem) { let margin = {top: 20, right: 30, bottom: 30, left: 40}; - let width = 960 - margin.right - margin.left; - let height = 540 - margin.top - margin.bottom; + let width = 1000 - margin.right - margin.left; + let height = 1000 - margin.top - margin.bottom; let color = d3.scaleOrdinal(d3.schemeCategory10); + /* + ** Next four variables were part of an attempt to make bubbles larger, + ** didn't work + */ + let songCounts = artistData.children.map(function(artist) { return artist.num_songs; }); // array of counts + let songCountExtent = d3.extent(songCounts); // [min song count, max song count] + let circleSize = { + min: 45, + max: 75 + }; + let circleRadiusScale = d3.scaleSqrt().domain(songCountExtent).range([circleSize.min, circleSize.max]); + let bubble = d3.pack(artistData) - .size([width, height]) - .padding(1.5); + .size([width + 100, height + 100]) + .padding(0.2); let svg = d3.select(parentElem) .append("svg") @@ -23,7 +35,7 @@ function drawArtistGraph(artistData, parentElem) { .sum(function(d) { return d.num_songs; }); let node = svg.selectAll(".node") - .data(bubble(nodes).descendants()) + .data(bubble(nodes).leaves()) .enter() .filter(function(d) { return !d.children; @@ -36,7 +48,7 @@ function drawArtistGraph(artistData, parentElem) { node.append("title") .text(function(d) { - return `${d.name}: ${d.num_songs}`; + return d.data.name + ": " + d.data.num_songs; }); node.append("circle") diff --git a/graphs/static/graphs/scripts/audio_feat_graph.js b/graphs/static/graphs/scripts/audio_feat_graph.js new file mode 100644 index 0000000..1fab293 --- /dev/null +++ b/graphs/static/graphs/scripts/audio_feat_graph.js @@ -0,0 +1,105 @@ +/** Queries the backend for audio feature data, draws the bar chart + * illustrating the frequencies of values, and appends the chart to + * a designated parent element + * + * @param audioFeature: the name of the audio feature (string) + * @param intervalEndPoints: a sorted array of 5 real numbers defining the intervals (categories) of values, + * for example: + * [0, 0.25, 0.5, 0.75, 1.0] for instrumentalness would define ranges + * (0-0.25), (0.25-0.5), (0.5-0.75), (0.75-1.0) + * @param parentElem: the DOM element to append the graph to (a selector string) + * @param userSecret: the user secret string for identification + * @return None + */ +function drawAudioFeatGraph(audioFeature, intervalEndPoints, parentElem, userSecret) { + // TODO: Not hard code the dimensions? + let margin = {top: 20, right: 30, bottom: 30, left: 40}; + let width = 480 - margin.left - margin.right, + height = 270 - margin.top - margin.bottom; + + let featureData = {}; + // Create the keys first in order + for (let index = 0; index < intervalEndPoints.length - 1; index++) { + let key = `${intervalEndPoints[index]} ~ ${intervalEndPoints[index + 1]}`; + featureData[key] = 0; + } + // define the vertical scaling function + let vScale = d3.scaleLinear().range([height, 0]); + + d3.json(`/api/audio_features/${audioFeature}/${userSecret}`) + .then(function(response) { + // categorize the data points + for (let dataPoint of response.data_points) { + dataPoint = parseFloat(dataPoint); + let index = intervalEndPoints.length - 2; + // find the index of the first element greater than dataPoint + while (dataPoint < intervalEndPoints[index]) { + index -= 1; + } + let key = `${intervalEndPoints[index]} ~ ${intervalEndPoints[index + 1]}`; + featureData[key] += 1; + } + + let dataSet = Object.values(featureData); + let dataRanges = Object.keys(featureData); // Ranges of audio features, e.g. 0-0.25, 0.25-0.5, etc + let dataArr = []; + // turn the counts into an array of objects, e.g. {range: "0-0.25", counts: 5} + for (let i = 0; i < dataRanges.length; i++) { + dataArr.push({ + range: dataRanges[i], + counts: featureData[dataRanges[i]] + }); + } + vScale.domain([0, d3.max(dataSet)]).nice(); + + let hScale = d3.scaleBand().domain(dataRanges).rangeRound([0, width]).padding(0.5); + + let xAxis = d3.axisBottom().scale(hScale); + let yAxis = d3.axisLeft().scale(vScale); + + let featureSVG = d3.select(parentElem) + .append('svg').attr('width', width + margin.left + margin.right) + .attr('height', height + margin.top + margin.bottom); + + let featureGraph = featureSVG.append("g") + .attr("transform", `translate(${margin.left}, ${margin.top})`) + .attr("fill", "teal"); + + featureGraph.selectAll(".bar") + .data(dataArr) + .enter().append('rect') + .attr('class', 'bar') + .attr('x', function(d) { return hScale(d.range); }) + .attr('y', function(d) { return vScale(d.counts); }) + .attr("height", function(d) { return height - vScale(d.counts); }) + .attr("width", hScale.bandwidth()); + + // function(d) { return hScale(d.range); } + + featureGraph.append('g') + .attr('class', 'axis') + .attr('transform', `translate(0, ${height})`) + .call(xAxis); + + featureGraph.append('g') + .attr('class', 'axis') + .call(yAxis); + + featureSVG.append("text") + .attr('x', (width / 2)) + .attr('y', (margin.top / 2)) + .attr('text-anchor', 'middle') + .style('font-size', '14px') + .text(`${capFeatureStr(audioFeature)}`); + + }); +} + +/** + * Returns the audio feature name string with the first letter capitalized + * @param audioFeature: the name of the audio feature + * @returns the audio feature name string with the first letter capitalized + */ +function capFeatureStr(audioFeature) { + return audioFeature.charAt(0).toUpperCase() + audioFeature.slice(1); +} \ No newline at end of file diff --git a/graphs/templates/graphs/artist_graph.html b/graphs/templates/graphs/artist_graph.html index 4d0f183..78a682c 100644 --- a/graphs/templates/graphs/artist_graph.html +++ b/graphs/templates/graphs/artist_graph.html @@ -9,14 +9,10 @@ + diff --git a/reset_db.sh b/reset_db.sh index 51037df..b833b8a 100755 --- a/reset_db.sh +++ b/reset_db.sh @@ -7,7 +7,7 @@ # if $INVENV is 1, then in virtualenv # echo $INVENV # if [ $INVENV -eq 1 ]; then -rm login/migrations/0* api/migrations/0* graphs/migrations/0* +rm login/migrations/0* api/migrations/0* sudo -u postgres psql -f reset_db.sql python manage.py makemigrations python manage.py migrate