Browse Source

Split spotifyvis code into different apps (#47)

Server is able to start, but none of the apps are linked together yet.
master
Kevin Mok 7 years ago
parent
commit
8b1344d453
  1. 0
      api/__init__.py
  2. 0
      api/admin.py
  3. 4
      api/apps.py
  4. 0
      api/models.py
  5. 0
      api/templates/api/logged_in.html
  6. 12
      api/urls.py
  7. 0
      api/utils.py
  8. 86
      api/views.py
  9. 0
      common-static/css/dark_bg.css
  10. 0
      graphs/__init__.py
  11. 4
      graphs/apps.py
  12. 104
      graphs/models.py
  13. 0
      graphs/static/graphs/scripts/artist_graph.js
  14. 0
      graphs/static/graphs/scripts/genre_graph.js
  15. 0
      graphs/templates/graphs/artist_graph.html
  16. 0
      graphs/templates/graphs/features_graph.html
  17. 0
      graphs/templates/graphs/genre_graph.html
  18. 12
      graphs/urls.py
  19. 51
      graphs/views.py
  20. 0
      login/__init__.py
  21. 4
      login/apps.py
  22. 0
      login/templates/login/index.html
  23. 11
      login/urls.py
  24. 115
      login/views.py
  25. 2
      manage.py
  26. 22
      musicvis/urls.py
  27. 250
      sample-track-obj.py
  28. 5
      spotifyvis/apps.py
  29. 8
      spotifyvis/settings.py
  30. 42
      spotifyvis/static/spotifyvis/scripts/index.js
  31. 21
      spotifyvis/templates/spotifyvis/user_data.html
  32. 67
      spotifyvis/tests.py
  33. 35
      spotifyvis/urls.py
  34. 2
      spotifyvis/wsgi.py

0
musicvis/__init__.py → api/__init__.py

0
spotifyvis/admin.py → api/admin.py

4
api/apps.py

@ -0,0 +1,4 @@
from django.apps import AppConfig
class ApiConfig(AppConfig):
name = 'api'

0
spotifyvis/models.py → api/models.py

0
spotifyvis/templates/spotifyvis/logged_in.html → api/templates/api/logged_in.html

12
api/urls.py

@ -0,0 +1,12 @@
from django.urls import path, include
from .views import *
urlpatterns = [
path('user_artists/<str:user_secret>', get_artist_data,
name='get_artist_data'),
path('user_genres/<str:user_secret>', get_genre_data,
name='get_genre_data'),
path('audio_features/<str:audio_feature>/<str:user_secret>',
get_audio_feature_data, name='get_audio_feature_data'),
]

0
spotifyvis/utils.py → api/utils.py

86
api/views.py

@ -0,0 +1,86 @@
# imports {{{ #
import math
import random
import requests
import os
import urllib
import secrets
import pprint
import string
from datetime import datetime
from django.http import JsonResponse
from django.db.models import Count, Q
from .utils import parse_library, get_artists_in_genre, update_track_genres
from .models import User, Track, AudioFeatures, Artist
# }}} imports #
TRACKS_TO_QUERY = 200
# get_artist_data {{{ #
def get_artist_data(request, user_secret):
"""Returns artist data as a JSON serialized list of dictionaries
The (key, value) pairs are (artist name, song count for said artist)
:param request: the HTTP request
:param user_secret: the user secret used for identification
:return: a JsonResponse
"""
user = User.objects.get(user_secret=user_secret)
artist_counts = Artist.objects.annotate(num_songs=Count('track',
filter=Q(track__users=user)))
processed_artist_counts = [{'name': artist.name,
'num_songs': artist.num_songs} for artist in artist_counts]
return JsonResponse(data=processed_artist_counts, safe=False)
# }}} get_artist_data #
# get_audio_feature_data {{{ #
def get_audio_feature_data(request, audio_feature, user_secret):
"""Returns all data points for a given audio feature
Args:
request: the HTTP request
audio_feature: The audio feature to be queried
user_secret: client secret, used to identify the user
"""
user = User.objects.get(user_secret=user_secret)
user_tracks = Track.objects.filter(users=user)
response_payload = {
'data_points': [],
}
for track in user_tracks:
try:
audio_feature_obj = AudioFeatures.objects.get(track=track)
response_payload['data_points'].append(getattr(audio_feature_obj, audio_feature))
except AudioFeatures.DoesNotExist:
continue
return JsonResponse(response_payload)
# }}} get_audio_feature_data #
# get_genre_data {{{ #
def get_genre_data(request, user_secret):
"""Return genre data needed to create the graph user.
TODO
"""
user = User.objects.get(user_secret=user_secret)
genre_counts = (Track.objects.filter(users__exact=user)
.values('genre')
.order_by('genre')
.annotate(num_songs=Count('genre'))
)
for genre_dict in genre_counts:
genre_dict['artists'] = get_artists_in_genre(user, genre_dict['genre'],
genre_dict['num_songs'])
print("*** Genre Breakdown ***")
pprint.pprint(list(genre_counts))
return JsonResponse(data=list(genre_counts), safe=False)
# }}} get_genre_data #

0
spotifyvis/static/spotifyvis/css/dark_bg.css → common-static/css/dark_bg.css

0
spotifyvis/migrations/__init__.py → graphs/__init__.py

4
graphs/apps.py

@ -0,0 +1,4 @@
from django.apps import AppConfig
class GraphsConfig(AppConfig):
name = 'graphs'

104
graphs/models.py

@ -0,0 +1,104 @@
from django.db import models
# id's are 22 in length in examples but set to 30 for buffer
MAX_ID = 30
# Genre {{{ #
class Genre(models.Model):
class Meta:
verbose_name = "Genre"
verbose_name_plural = "Genres"
name = models.CharField(primary_key=True, max_length=50)
num_songs = models.PositiveIntegerField()
def __str__(self):
return self.name
# }}} Genre #
# Artist {{{ #
class Artist(models.Model):
class Meta:
verbose_name = "Artist"
verbose_name_plural = "Artists"
artist_id = models.CharField(primary_key=True, max_length=MAX_ID)
# unique since only storing one genre per artist right now
name = models.CharField(unique=True, max_length=50)
genres = models.ManyToManyField(Genre, blank=True)
def __str__(self):
return self.name
# }}} Artist #
# User {{{ #
class User(models.Model):
class Meta:
verbose_name = "User"
verbose_name_plural = "Users"
user_id = models.CharField(primary_key=True, max_length=MAX_ID) # the user's Spotify ID
user_secret = models.CharField(max_length=50, default='')
def __str__(self):
return self.user_id
# }}} User #
# Track {{{ #
class Track(models.Model):
class Meta:
verbose_name = "Track"
verbose_name_plural = "Tracks"
track_id = models.CharField(primary_key=True, max_length=MAX_ID)
# artist = models.ForeignKey(Artist, on_delete=models.CASCADE)
artists = models.ManyToManyField(Artist, blank=True)
year = models.PositiveSmallIntegerField()
popularity = models.PositiveSmallIntegerField()
runtime = models.PositiveSmallIntegerField()
name = models.CharField(max_length=200)
users = models.ManyToManyField(User, blank=True)
genre = models.ForeignKey(Genre, on_delete=models.CASCADE, blank=True,
null=True)
def __str__(self):
track_str = "{}, genre: {}, artists: [".format(self.name, self.genre)
for artist in self.artists.all():
track_str += "{}, ".format(artist.name)
track_str += "]"
return track_str
# }}} Track #
# AudioFeatures {{{ #
class AudioFeatures(models.Model):
class Meta:
verbose_name = "AudioFeatures"
verbose_name_plural = "AudioFeatures"
track = models.OneToOneField(Track, on_delete=models.CASCADE, primary_key=True,)
acousticness = models.DecimalField(decimal_places=3, max_digits=3)
danceability = models.DecimalField(decimal_places=3, max_digits=3)
energy = models.DecimalField(decimal_places=3, max_digits=3)
instrumentalness = models.DecimalField(decimal_places=3, max_digits=3)
loudness = models.DecimalField(decimal_places=3, max_digits=6)
speechiness = models.DecimalField(decimal_places=3, max_digits=3)
tempo = models.DecimalField(decimal_places=3, max_digits=6)
valence = models.DecimalField(decimal_places=3, max_digits=3)
def __str__(self):
return super(AudioFeatures, self).__str__()
# }}} AudioFeatures #

0
spotifyvis/static/spotifyvis/scripts/artist_graph.js → graphs/static/graphs/scripts/artist_graph.js

0
spotifyvis/static/spotifyvis/scripts/genre_graph.js → graphs/static/graphs/scripts/genre_graph.js

0
spotifyvis/templates/spotifyvis/artist_graph.html → graphs/templates/graphs/artist_graph.html

0
spotifyvis/templates/spotifyvis/audio_features.html → graphs/templates/graphs/features_graph.html

0
spotifyvis/templates/spotifyvis/genre_graph.html → graphs/templates/graphs/genre_graph.html

12
graphs/urls.py

@ -0,0 +1,12 @@
from django.urls import path, include
from .views import *
urlpatterns = [
path('artists/<str:user_secret>', artist_data,
name='display_artist_graph'),
path('genre/<str:user_secret>', display_genre_graph,
name='display_genre_graph'),
path('audio_features/<str:user_secret>', audio_features,
name='display_audio_features'),
]

51
graphs/views.py

@ -0,0 +1,51 @@
# imports {{{ #
import math
import random
import requests
import os
import urllib
import secrets
import pprint
import string
from datetime import datetime
from django.shortcuts import render, redirect
# }}} imports #
def artist_data(request, user_secret):
"""Renders the artist data graph display page
:param request: the HTTP request
:param user_secret: the user secret used for identification
:return: render the artist data graph display page
"""
user = User.objects.get(user_secret=user_secret)
context = {
'user_id': user.user_id,
'user_secret': user_secret,
}
return render(request, "spotifyvis/artist_graph.html", context)
def display_genre_graph(request, user_secret):
user = User.objects.get(user_secret=user_secret)
context = {
'user_secret': user_secret,
}
return render(request, "spotifyvis/genre_graph.html", context)
def audio_features(request, user_secret):
"""Renders the audio features page
:param request: the HTTP request
:param user_secret: user secret used for identification
:return: renders the audio features page
"""
user = User.objects.get(user_secret=user_secret)
context = {
'user_id': user.user_id,
'user_secret': user_secret,
}
return render(request, "spotifyvis/audio_features.html", context)

0
login/__init__.py

4
login/apps.py

@ -0,0 +1,4 @@
from django.apps import AppConfig
class LoginConfig(AppConfig):
name = 'login'

0
spotifyvis/templates/spotifyvis/index.html → login/templates/login/index.html

11
login/urls.py

@ -0,0 +1,11 @@
from django.urls import path, include
from .views import *
urlpatterns = [
path('', index, name='index'),
path('spotify_login', spotify_login, name='spotify_login'),
path('callback', callback, name='callback'),
path('user_data', user_data, name='user_data'),
path('admin_graphs', admin_graphs, name='admin_graphs'),
]

115
spotifyvis/views.py → login/views.py

@ -11,10 +11,7 @@ import string
from datetime import datetime from datetime import datetime
from django.shortcuts import render, redirect from django.shortcuts import render, redirect
from django.http import HttpResponse, HttpResponseBadRequest, JsonResponse from django.http import HttpResponseBadRequest
from django.db.models import Count, Q
from .utils import parse_library, get_artists_in_genre, update_track_genres
from .models import User, Track, AudioFeatures, Artist
# }}} imports # # }}} imports #
@ -65,7 +62,7 @@ def index(request):
# login {{{ # # login {{{ #
# uses Authorization Code flow # uses Authorization Code flow
def login(request): def spotify_login(request):
# use a randomly generated state string to prevent cross-site request forgery attacks # use a randomly generated state string to prevent cross-site request forgery attacks
state_str = generate_random_string(16) state_str = generate_random_string(16)
request.session['state_string'] = state_str request.session['state_string'] = state_str
@ -117,7 +114,6 @@ def callback(request):
# user_data {{{ # # user_data {{{ #
def user_data(request): def user_data(request):
# get user token {{{ # # get user token {{{ #
@ -165,7 +161,8 @@ def user_data(request):
'user_secret': user.user_secret, 'user_secret': user.user_secret,
} }
parse_library(headers, TRACKS_TO_QUERY, user) # TODO: redirect to API app to parse library or loading page
# parse_library(headers, TRACKS_TO_QUERY, user)
return render(request, 'spotifyvis/logged_in.html', context) return render(request, 'spotifyvis/logged_in.html', context)
# }}} user_data # # }}} user_data #
@ -182,107 +179,3 @@ def admin_graphs(request):
} }
update_track_genres(user_obj) update_track_genres(user_obj)
return render(request, 'spotifyvis/logged_in.html', context) return render(request, 'spotifyvis/logged_in.html', context)
def artist_data(request, user_secret):
"""Renders the artist data graph display page
:param request: the HTTP request
:param user_secret: the user secret used for identification
:return: render the artist data graph display page
"""
user = User.objects.get(user_secret=user_secret)
context = {
'user_id': user.user_id,
'user_secret': user_secret,
}
return render(request, "spotifyvis/artist_graph.html", context)
# get_artist_data {{{ #
def get_artist_data(request, user_secret):
"""Returns artist data as a JSON serialized list of dictionaries
The (key, value) pairs are (artist name, song count for said artist)
:param request: the HTTP request
:param user_secret: the user secret used for identification
:return: a JsonResponse
"""
user = User.objects.get(user_secret=user_secret)
artist_counts = Artist.objects.annotate(num_songs=Count('track',
filter=Q(track__users=user)))
processed_artist_counts = [{'name': artist.name,
'num_songs': artist.num_songs} for artist in artist_counts]
return JsonResponse(data=processed_artist_counts, safe=False)
# }}} get_artist_data #
def display_genre_graph(request, user_secret):
user = User.objects.get(user_secret=user_secret)
context = {
'user_secret': user_secret,
}
return render(request, "spotifyvis/genre_graph.html", context)
def audio_features(request, user_secret):
"""Renders the audio features page
:param request: the HTTP request
:param user_secret: user secret used for identification
:return: renders the audio features page
"""
user = User.objects.get(user_secret=user_secret)
context = {
'user_id': user.user_id,
'user_secret': user_secret,
}
return render(request, "spotifyvis/audio_features.html", context)
# get_audio_feature_data {{{ #
def get_audio_feature_data(request, audio_feature, user_secret):
"""Returns all data points for a given audio feature
Args:
request: the HTTP request
audio_feature: The audio feature to be queried
user_secret: client secret, used to identify the user
"""
user = User.objects.get(user_secret=user_secret)
user_tracks = Track.objects.filter(users=user)
response_payload = {
'data_points': [],
}
for track in user_tracks:
try:
audio_feature_obj = AudioFeatures.objects.get(track=track)
response_payload['data_points'].append(getattr(audio_feature_obj, audio_feature))
except AudioFeatures.DoesNotExist:
continue
return JsonResponse(response_payload)
# }}} get_audio_feature_data #
# get_genre_data {{{ #
def get_genre_data(request, user_secret):
"""Return genre data needed to create the graph user.
TODO
"""
user = User.objects.get(user_secret=user_secret)
genre_counts = (Track.objects.filter(users__exact=user)
.values('genre')
.order_by('genre')
.annotate(num_songs=Count('genre'))
)
for genre_dict in genre_counts:
genre_dict['artists'] = get_artists_in_genre(user, genre_dict['genre'],
genre_dict['num_songs'])
print("*** Genre Breakdown ***")
pprint.pprint(list(genre_counts))
return JsonResponse(data=list(genre_counts), safe=False)
# }}} get_genre_data #

2
manage.py

@ -3,7 +3,7 @@ import os
import sys import sys
if __name__ == "__main__": if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "musicvis.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "spotifyvis.settings")
try: try:
from django.core.management import execute_from_command_line from django.core.management import execute_from_command_line
except ImportError as exc: except ImportError as exc:

22
musicvis/urls.py

@ -1,22 +0,0 @@
"""musicdata URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('', include('spotifyvis.urls')),
path('admin/', admin.site.urls),
]

250
sample-track-obj.py

@ -1,250 +0,0 @@
{
'added_at':'2018-05-18T19:16:36Z',
'track':{
'album':{
'album_type':'single',
'artists':[
{
'external_urls':{
'spotify':'https://open.spotify.com/artist/64KEffDW9EtZ1y2vBYgq8T'
},
'href':'https://api.spotify.com/v1/artists/64KEffDW9EtZ1y2vBYgq8T',
'id':'64KEffDW9EtZ1y2vBYgq8T',
'name':'Marshmello',
'type':'artist',
'uri':'spotify:artist:64KEffDW9EtZ1y2vBYgq8T'
},
{
'external_urls':{
'spotify':'https://open.spotify.com/artist/5gCRApTajqwbnHHPbr2Fpi'
},
'href':'https://api.spotify.com/v1/artists/5gCRApTajqwbnHHPbr2Fpi',
'id':'5gCRApTajqwbnHHPbr2Fpi',
'name':'Juicy J',
'type':'artist',
'uri':'spotify:artist:5gCRApTajqwbnHHPbr2Fpi'
},
{
'external_urls':{
'spotify':'https://open.spotify.com/artist/4IWBUUAFIplrNtaOHcJPRM'
},
'href':'https://api.spotify.com/v1/artists/4IWBUUAFIplrNtaOHcJPRM',
'id':'4IWBUUAFIplrNtaOHcJPRM',
'name':'James Arthur',
'type':'artist',
'uri':'spotify:artist:4IWBUUAFIplrNtaOHcJPRM'
}
],
'available_markets':[
'AD',
'AR',
'AT',
'AU',
'BE',
'BG',
'BO',
'BR',
'CA',
'CH',
'CL',
'CO',
'CR',
'CY',
'CZ',
'DE',
'DK',
'DO',
'EC',
'EE',
'ES',
'FI',
'FR',
'GB',
'GR',
'GT',
'HK',
'HN',
'HU',
'ID',
'IE',
'IL',
'IS',
'IT',
'JP',
'LI',
'LT',
'LU',
'LV',
'MC',
'MT',
'MX',
'MY',
'NI',
'NL',
'NO',
'NZ',
'PA',
'PE',
'PH',
'PL',
'PT',
'PY',
'RO',
'SE',
'SG',
'SK',
'SV',
'TH',
'TR',
'TW',
'US',
'UY',
'VN',
'ZA'
],
'external_urls':{
'spotify':'https://open.spotify.com/album/6TvqOieExu0IJb9Q1gOoCz'
},
'href':'https://api.spotify.com/v1/albums/6TvqOieExu0IJb9Q1gOoCz',
'id':'6TvqOieExu0IJb9Q1gOoCz',
'images':[
{
'height':640,
'url':'https://i.scdn.co/image/b3556956b8e4881c85228ada91aa953e5c0458ef',
'width':640
},
{
'height':300,
'url':'https://i.scdn.co/image/d76072f5ca739466bd27f42f3356fa1a38c6a92d',
'width':300
},
{
'height':64,
'url':'https://i.scdn.co/image/bfd092dfa503566d9c9a3042f213fe02bed8a5cc',
'width':64
}
],
'name':'You Can Cry',
'release_date':'2018-05-04',
'release_date_precision':'day',
'type':'album',
'uri':'spotify:album:6TvqOieExu0IJb9Q1gOoCz'
},
'artists':[
{
'external_urls':{
'spotify':'https://open.spotify.com/artist/64KEffDW9EtZ1y2vBYgq8T'
},
'href':'https://api.spotify.com/v1/artists/64KEffDW9EtZ1y2vBYgq8T',
'id':'64KEffDW9EtZ1y2vBYgq8T',
'name':'Marshmello',
'type':'artist',
'uri':'spotify:artist:64KEffDW9EtZ1y2vBYgq8T'
},
{
'external_urls':{
'spotify':'https://open.spotify.com/artist/5gCRApTajqwbnHHPbr2Fpi'
},
'href':'https://api.spotify.com/v1/artists/5gCRApTajqwbnHHPbr2Fpi',
'id':'5gCRApTajqwbnHHPbr2Fpi',
'name':'Juicy J',
'type':'artist',
'uri':'spotify:artist:5gCRApTajqwbnHHPbr2Fpi'
},
{
'external_urls':{
'spotify':'https://open.spotify.com/artist/4IWBUUAFIplrNtaOHcJPRM'
},
'href':'https://api.spotify.com/v1/artists/4IWBUUAFIplrNtaOHcJPRM',
'id':'4IWBUUAFIplrNtaOHcJPRM',
'name':'James Arthur',
'type':'artist',
'uri':'spotify:artist:4IWBUUAFIplrNtaOHcJPRM'
}
],
'available_markets':[
'AD',
'AR',
'AT',
'AU',
'BE',
'BG',
'BO',
'BR',
'CA',
'CH',
'CL',
'CO',
'CR',
'CY',
'CZ',
'DE',
'DK',
'DO',
'EC',
'EE',
'ES',
'FI',
'FR',
'GB',
'GR',
'GT',
'HK',
'HN',
'HU',
'ID',
'IE',
'IL',
'IS',
'IT',
'JP',
'LI',
'LT',
'LU',
'LV',
'MC',
'MT',
'MX',
'MY',
'NI',
'NL',
'NO',
'NZ',
'PA',
'PE',
'PH',
'PL',
'PT',
'PY',
'RO',
'SE',
'SG',
'SK',
'SV',
'TH',
'TR',
'TW',
'US',
'UY',
'VN',
'ZA'
],
'disc_number':1,
'duration_ms':194533,
'explicit':False,
'external_ids':{
'isrc':'USQX91800946'
},
'external_urls':{
'spotify':'https://open.spotify.com/track/3ZbJMlEL4Kcme0ONRO7Slx'
},
'href':'https://api.spotify.com/v1/tracks/3ZbJMlEL4Kcme0ONRO7Slx',
'id':'3ZbJMlEL4Kcme0ONRO7Slx',
'name':'You Can Cry',
'popularity':81,
'preview_url':'https://p.scdn.co/mp3-preview/6c31f3dee18a1e7c452ce9b6948a6e04aa7629d6?cid=aefd4e45060d4f9ba5bea0f6e6d36359',
'track_number':1,
'type':'track',
'uri':'spotify:track:3ZbJMlEL4Kcme0ONRO7Slx'
}
}

5
spotifyvis/apps.py

@ -1,5 +0,0 @@
from django.apps import AppConfig
class SpotifyvisConfig(AppConfig):
name = 'spotifyvis'

8
musicvis/settings.py → spotifyvis/settings.py

@ -37,7 +37,9 @@ INSTALLED_APPS = [
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'spotifyvis.apps.SpotifyvisConfig', 'login.apps.LoginConfig',
'api.apps.ApiConfig',
'graphs.apps.GraphsConfig',
] ]
MIDDLEWARE = [ MIDDLEWARE = [
@ -50,7 +52,7 @@ MIDDLEWARE = [
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
] ]
ROOT_URLCONF = 'musicvis.urls' ROOT_URLCONF = 'spotifyvis.urls'
TEMPLATES = [ TEMPLATES = [
{ {
@ -68,7 +70,7 @@ TEMPLATES = [
}, },
] ]
WSGI_APPLICATION = 'musicvis.wsgi.application' WSGI_APPLICATION = 'spotifyvis.wsgi.application'
# Database # Database

42
spotifyvis/static/spotifyvis/scripts/index.js

@ -1,42 +0,0 @@
document.getElementById("login-btn").addEventListener("click", function() {
let httpRequest = new XMLHttpRequest();
/*
* Handler for the response
*/
httpRequest.onreadystatechange = function() {
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status === 200) {
// hide the login button
document.getElementById('login').setAttribute("display", "none");
let responseData = JSON.parse(httpRequest.responseText);
let dataList = document.getElementById("data-list");
for (let key in responseData) {
let newLi = document.createElement("li");
let innerList = document.createElement("ul");
let dataLabel = document.createElement("li");
dataLabel.innerText = key;
let dataValue = document.createElement("li");
dataValue.innerText = responseData[key];
innerList.appendChild(dataLabel);
innerList.appendChild(dataValue);
newLi.appendChild(innerList);
dataList.appendChild(newLi);
}
} else {
alert("There was a problem with the login request, please try again!");
}
}
}
httpRequest.open('GET', '/login', true);
httpRequest.send();
});

21
spotifyvis/templates/spotifyvis/user_data.html

@ -1,21 +0,0 @@
{% load static %}
<!DOCTYPE html>
<!--[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 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>User Spotify Data</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="{% static 'spotifyvis/css/dark_bg.css' %}">
</head>
<body>
<!--[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>
<![endif]-->
<p>Logged in as {{ id }}</p>
</body>
</html>

67
spotifyvis/tests.py

@ -1,67 +0,0 @@
from django.test import TestCase
from .utils import update_std_dev
import math
# Create your tests here.
class UpdateStdDevTest(TestCase):
def test_two_data_points(self):
"""
tests if update_std_dev behaves correctly for two data points
"""
cur_mean = 5
cur_std_dev = 0
new_mean, new_std_dev = update_std_dev(cur_mean, cur_std_dev, 10, 2)
self.assertTrue(math.isclose(new_mean, 7.5, rel_tol=0.01))
self.assertTrue(math.isclose(new_std_dev, 3.5355, rel_tol=0.01))
def test_three_data_points(self):
"""
tests if update_std_dev behaves correctly for three data points
"""
cur_mean = 7.5
cur_std_dev = 3.5355
new_mean, new_std_dev = update_std_dev(cur_mean, cur_std_dev, 15, 3)
self.assertTrue(math.isclose(new_mean, 10, rel_tol=0.01))
self.assertTrue(math.isclose(new_std_dev, 5, rel_tol=0.01))
def test_four_data_points(self):
"""
tests if update_std_dev behaves correctly for four data points
"""
cur_mean = 10
cur_std_dev = 5
new_mean, new_std_dev = update_std_dev(cur_mean, cur_std_dev, 20, 4)
self.assertTrue(math.isclose(new_mean, 12.5, rel_tol=0.01))
self.assertTrue(math.isclose(new_std_dev, 6.455, rel_tol=0.01))
def test_five_data_points(self):
"""
tests if update_std_dev behaves correctly for five data points
"""
cur_mean = 12.5
cur_std_dev = 6.455
new_mean, new_std_dev = update_std_dev(cur_mean, cur_std_dev, 63, 5)
self.assertTrue(math.isclose(new_mean, 22.6, rel_tol=0.01))
self.assertTrue(math.isclose(new_std_dev, 23.2658, rel_tol=0.01))
def test_sixteen_data_points(self):
"""
tests if update_std_dev behaves correctly for sixteen data points
"""
cur_mean = 0.4441
cur_std_dev = 0.2855
new_mean, new_std_dev = update_std_dev(cur_mean, cur_std_dev, 0.7361, 16)
self.assertTrue(math.isclose(new_mean, 0.4624, rel_tol=0.01))
self.assertTrue(math.isclose(new_std_dev, 0.2853, rel_tol=0.01))

35
spotifyvis/urls.py

@ -1,19 +1,24 @@
from django.urls import path, include """musicdata URL Configuration
from .views import * The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
urlpatterns = [ urlpatterns = [
path('', index, name='index'), path('admin/', admin.site.urls),
path('login', login, name='login'), path('login/', include('login.urls')),
path('callback', callback, name='callback'), path('api/', include('api.urls')),
path('user_data', user_data, name='user_data'), path('graphs/', include('graphs.urls')),
path('admin_graphs', admin_graphs, name='admin_graphs'),
path('api/user_artists/<str:user_secret>', get_artist_data, name='get_artist_data'),
path('artists/<str:user_secret>', artist_data, name='display_artist_graph'),
path('api/user_genres/<str:user_secret>', get_genre_data, name='get_genre_data'),
path('graphs/genre/<str:user_secret>', display_genre_graph,
name='display_genre_graph'),
path('audio_features/<str:user_secret>', audio_features, name='display_audio_features'),
path('api/audio_features/<str:audio_feature>/<str:user_secret>',
get_audio_feature_data, name='get_audio_feature_data'),
] ]

2
musicvis/wsgi.py → spotifyvis/wsgi.py

@ -11,6 +11,6 @@ import os
from django.core.wsgi import get_wsgi_application from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "musicvis.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "spotifyvis.settings")
application = get_wsgi_application() application = get_wsgi_application()
|||||||
100:0
Loading…
Cancel
Save