Graphs and tables for your Spotify account.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

142 lines
6.8 KiB

  1. {% load static %}
  2. <!DOCTYPE html>
  3. <!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
  4. <!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
  5. <!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
  6. <!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
  7. <head>
  8. <meta charset="utf-8">
  9. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  10. <title>User Spotify Data</title>
  11. <meta name="description" content="">
  12. <meta name="viewport" content="width=device-width, initial-scale=1">
  13. <style>
  14. .tick {
  15. font-size: 15px;
  16. }
  17. </style>
  18. </head>
  19. <body>
  20. <!--[if lt IE 7]>
  21. <p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="#">upgrade your browser</a> to improve your experience.</p>
  22. <![endif]-->
  23. <p>Logged in as {{ user_id }}</p>
  24. <script src="https://d3js.org/d3.v5.js"></script>
  25. <script type="text/javascript">
  26. /** Queries the backend for audio feature data, draws the bar chart
  27. * illustrating the frequencies of values, and appends the chart to
  28. * a designated parent element
  29. *
  30. * @param audioFeature: the name of the audio feature (string)
  31. * @param intervalEndPoints: a sorted array of 5 real numbers defining the intervals (categories) of values,
  32. * for example:
  33. * [0, 0.25, 0.5, 0.75, 1.0] for instrumentalness would define ranges
  34. * (0-0.25), (0.25-0.5), (0.5-0.75), (0.75-1.0)
  35. * @param parentElem: the DOM element to append the graph to (a selector string)
  36. * @return None
  37. */
  38. function drawAudioFeatGraph(audioFeature, intervalEndPoints, parentElem) {
  39. // TODO: Not hard code the dimensions?
  40. let margin = {top: 20, right: 30, bottom: 30, left: 40};
  41. let width = 480 - margin.left - margin.right,
  42. height = 270 - margin.top - margin.bottom;
  43. let featureData = {};
  44. // Create the keys first in order
  45. for (let index = 0; index < intervalEndPoints.length - 1; index++) {
  46. let key = `${intervalEndPoints[index]} ~ ${intervalEndPoints[index + 1]}`;
  47. featureData[key] = 0;
  48. }
  49. // define the vertical scaling function
  50. let vScale = d3.scaleLinear().range([height, 0]);
  51. d3.json(`/api/audio_features/${audioFeature}/{{ user_secret }}`)
  52. .then(function(response) {
  53. // categorize the data points
  54. for (let dataPoint of response.data_points) {
  55. dataPoint = parseFloat(dataPoint);
  56. let index = intervalEndPoints.length - 2;
  57. // find the index of the first element greater than dataPoint
  58. while (dataPoint < intervalEndPoints[index]) {
  59. index -= 1;
  60. }
  61. let key = `${intervalEndPoints[index]} ~ ${intervalEndPoints[index + 1]}`;
  62. featureData[key] += 1;
  63. }
  64. let dataSet = Object.values(featureData);
  65. let dataRanges = Object.keys(featureData); // Ranges of audio features, e.g. 0-0.25, 0.25-0.5, etc
  66. let dataArr = [];
  67. // turn the counts into an array of objects, e.g. {range: "0-0.25", counts: 5}
  68. for (let i = 0; i < dataRanges.length; i++) {
  69. dataArr.push({
  70. range: dataRanges[i],
  71. counts: featureData[dataRanges[i]]
  72. });
  73. }
  74. vScale.domain([0, d3.max(dataSet)]).nice();
  75. let hScale = d3.scaleBand().domain(dataRanges).rangeRound([0, width]).padding(0.5);
  76. let xAxis = d3.axisBottom().scale(hScale);
  77. let yAxis = d3.axisLeft().scale(vScale);
  78. let featureSVG = d3.select(parentElem)
  79. .append('svg').attr('width', width + margin.left + margin.right)
  80. .attr('height', height + margin.top + margin.bottom);
  81. let featureGraph = featureSVG.append("g")
  82. .attr("transform", `translate(${margin.left}, ${margin.top})`)
  83. .attr("fill", "teal");
  84. featureGraph.selectAll(".bar")
  85. .data(dataArr)
  86. .enter().append('rect')
  87. .attr('class', 'bar')
  88. .attr('x', function(d) { return hScale(d.range); })
  89. .attr('y', function(d) { return vScale(d.counts); })
  90. .attr("height", function(d) { return height - vScale(d.counts); })
  91. .attr("width", hScale.bandwidth());
  92. // function(d) { return hScale(d.range); }
  93. featureGraph.append('g')
  94. .attr('class', 'axis')
  95. .attr('transform', `translate(0, ${height})`)
  96. .call(xAxis);
  97. featureGraph.append('g')
  98. .attr('class', 'axis')
  99. .call(yAxis);
  100. featureSVG.append("text")
  101. .attr('x', (width / 2))
  102. .attr('y', (margin.top / 2))
  103. .attr('text-anchor', 'middle')
  104. .style('font-size', '14px')
  105. .text(`${capFeatureStr(audioFeature)}`);
  106. });
  107. }
  108. /**
  109. * Returns the audio feature name string with the first letter capitalized
  110. * @param audioFeature: the name of the audio feature
  111. * @returns the audio feature name string with the first letter capitalized
  112. */
  113. function capFeatureStr(audioFeature) {
  114. return audioFeature.charAt(0).toUpperCase() + audioFeature.slice(1);
  115. }
  116. drawAudioFeatGraph("instrumentalness", [0, 0.25, 0.5, 0.75, 1.0], 'body');
  117. drawAudioFeatGraph("valence", [0, 0.25, 0.5, 0.75, 1.0], 'body');
  118. drawAudioFeatGraph("energy", [0, 0.25, 0.5, 0.75, 1.0], 'body');
  119. drawAudioFeatGraph("tempo", [40, 80, 120, 160, 200], 'body');
  120. drawAudioFeatGraph("danceability", [0, 0.25, 0.5, 0.75, 1.0], 'body');
  121. drawAudioFeatGraph("acousticness", [0, 0.25, 0.5, 0.75, 1.0], 'body');
  122. drawAudioFeatGraph("loudness", [-60, -45, -30, -15, 0], 'body');
  123. drawAudioFeatGraph("speechiness", [0, 0.25, 0.5, 0.75, 1.0], 'body');
  124. </script>
  125. </body>
  126. </html>