From 8b1344d45308c50369ddd06cf1403e85c64f6c26 Mon Sep 17 00:00:00 2001 From: Kevin Mok Date: Fri, 29 Jun 2018 04:15:08 -0400 Subject: [PATCH] Split spotifyvis code into different apps (#47) Server is able to start, but none of the apps are linked together yet. --- {musicvis => api}/__init__.py | 0 {spotifyvis => api}/admin.py | 0 api/apps.py | 4 + {spotifyvis => api}/models.py | 0 .../templates/api}/logged_in.html | 0 api/urls.py | 12 + {spotifyvis => api}/utils.py | 0 api/views.py | 86 ++++++ .../css/dark_bg.css | 0 {spotifyvis/migrations => graphs}/__init__.py | 0 graphs/apps.py | 4 + graphs/models.py | 104 ++++++++ .../static/graphs}/scripts/artist_graph.js | 0 .../static/graphs}/scripts/genre_graph.js | 0 .../templates/graphs}/artist_graph.html | 0 .../templates/graphs/features_graph.html | 0 .../templates/graphs}/genre_graph.html | 0 graphs/urls.py | 12 + graphs/views.py | 51 ++++ login/__init__.py | 0 login/apps.py | 4 + .../templates/login}/index.html | 0 login/urls.py | 11 + {spotifyvis => login}/views.py | 115 +------- manage.py | 2 +- musicvis/urls.py | 22 -- sample-track-obj.py | 250 ------------------ spotifyvis/apps.py | 5 - {musicvis => spotifyvis}/settings.py | 8 +- spotifyvis/static/spotifyvis/scripts/index.js | 42 --- .../templates/spotifyvis/user_data.html | 21 -- spotifyvis/tests.py | 67 ----- spotifyvis/urls.py | 35 +-- {musicvis => spotifyvis}/wsgi.py | 2 +- 34 files changed, 319 insertions(+), 538 deletions(-) rename {musicvis => api}/__init__.py (100%) rename {spotifyvis => api}/admin.py (100%) create mode 100644 api/apps.py rename {spotifyvis => api}/models.py (100%) rename {spotifyvis/templates/spotifyvis => api/templates/api}/logged_in.html (100%) create mode 100644 api/urls.py rename {spotifyvis => api}/utils.py (100%) create mode 100644 api/views.py rename {spotifyvis/static/spotifyvis => common-static}/css/dark_bg.css (100%) rename {spotifyvis/migrations => graphs}/__init__.py (100%) create mode 100644 graphs/apps.py create mode 100644 graphs/models.py rename {spotifyvis/static/spotifyvis => graphs/static/graphs}/scripts/artist_graph.js (100%) rename {spotifyvis/static/spotifyvis => graphs/static/graphs}/scripts/genre_graph.js (100%) rename {spotifyvis/templates/spotifyvis => graphs/templates/graphs}/artist_graph.html (100%) rename spotifyvis/templates/spotifyvis/audio_features.html => graphs/templates/graphs/features_graph.html (100%) rename {spotifyvis/templates/spotifyvis => graphs/templates/graphs}/genre_graph.html (100%) create mode 100644 graphs/urls.py create mode 100644 graphs/views.py create mode 100644 login/__init__.py create mode 100644 login/apps.py rename {spotifyvis/templates/spotifyvis => login/templates/login}/index.html (100%) create mode 100644 login/urls.py rename {spotifyvis => login}/views.py (58%) delete mode 100644 musicvis/urls.py delete mode 100644 sample-track-obj.py delete mode 100644 spotifyvis/apps.py rename {musicvis => spotifyvis}/settings.py (94%) delete mode 100644 spotifyvis/static/spotifyvis/scripts/index.js delete mode 100644 spotifyvis/templates/spotifyvis/user_data.html delete mode 100644 spotifyvis/tests.py rename {musicvis => spotifyvis}/wsgi.py (82%) diff --git a/musicvis/__init__.py b/api/__init__.py similarity index 100% rename from musicvis/__init__.py rename to api/__init__.py diff --git a/spotifyvis/admin.py b/api/admin.py similarity index 100% rename from spotifyvis/admin.py rename to api/admin.py diff --git a/api/apps.py b/api/apps.py new file mode 100644 index 0000000..c147de3 --- /dev/null +++ b/api/apps.py @@ -0,0 +1,4 @@ +from django.apps import AppConfig + +class ApiConfig(AppConfig): + name = 'api' diff --git a/spotifyvis/models.py b/api/models.py similarity index 100% rename from spotifyvis/models.py rename to api/models.py diff --git a/spotifyvis/templates/spotifyvis/logged_in.html b/api/templates/api/logged_in.html similarity index 100% rename from spotifyvis/templates/spotifyvis/logged_in.html rename to api/templates/api/logged_in.html diff --git a/api/urls.py b/api/urls.py new file mode 100644 index 0000000..204f9c8 --- /dev/null +++ b/api/urls.py @@ -0,0 +1,12 @@ +from django.urls import path, include + +from .views import * + +urlpatterns = [ + path('user_artists/', get_artist_data, + name='get_artist_data'), + path('user_genres/', get_genre_data, + name='get_genre_data'), + path('audio_features//', + get_audio_feature_data, name='get_audio_feature_data'), +] diff --git a/spotifyvis/utils.py b/api/utils.py similarity index 100% rename from spotifyvis/utils.py rename to api/utils.py diff --git a/api/views.py b/api/views.py new file mode 100644 index 0000000..a3dfc27 --- /dev/null +++ b/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 # diff --git a/spotifyvis/static/spotifyvis/css/dark_bg.css b/common-static/css/dark_bg.css similarity index 100% rename from spotifyvis/static/spotifyvis/css/dark_bg.css rename to common-static/css/dark_bg.css diff --git a/spotifyvis/migrations/__init__.py b/graphs/__init__.py similarity index 100% rename from spotifyvis/migrations/__init__.py rename to graphs/__init__.py diff --git a/graphs/apps.py b/graphs/apps.py new file mode 100644 index 0000000..6b4b056 --- /dev/null +++ b/graphs/apps.py @@ -0,0 +1,4 @@ +from django.apps import AppConfig + +class GraphsConfig(AppConfig): + name = 'graphs' diff --git a/graphs/models.py b/graphs/models.py new file mode 100644 index 0000000..7419443 --- /dev/null +++ b/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 # diff --git a/spotifyvis/static/spotifyvis/scripts/artist_graph.js b/graphs/static/graphs/scripts/artist_graph.js similarity index 100% rename from spotifyvis/static/spotifyvis/scripts/artist_graph.js rename to graphs/static/graphs/scripts/artist_graph.js diff --git a/spotifyvis/static/spotifyvis/scripts/genre_graph.js b/graphs/static/graphs/scripts/genre_graph.js similarity index 100% rename from spotifyvis/static/spotifyvis/scripts/genre_graph.js rename to graphs/static/graphs/scripts/genre_graph.js diff --git a/spotifyvis/templates/spotifyvis/artist_graph.html b/graphs/templates/graphs/artist_graph.html similarity index 100% rename from spotifyvis/templates/spotifyvis/artist_graph.html rename to graphs/templates/graphs/artist_graph.html diff --git a/spotifyvis/templates/spotifyvis/audio_features.html b/graphs/templates/graphs/features_graph.html similarity index 100% rename from spotifyvis/templates/spotifyvis/audio_features.html rename to graphs/templates/graphs/features_graph.html diff --git a/spotifyvis/templates/spotifyvis/genre_graph.html b/graphs/templates/graphs/genre_graph.html similarity index 100% rename from spotifyvis/templates/spotifyvis/genre_graph.html rename to graphs/templates/graphs/genre_graph.html diff --git a/graphs/urls.py b/graphs/urls.py new file mode 100644 index 0000000..9bfcf99 --- /dev/null +++ b/graphs/urls.py @@ -0,0 +1,12 @@ +from django.urls import path, include + +from .views import * + +urlpatterns = [ + path('artists/', artist_data, + name='display_artist_graph'), + path('genre/', display_genre_graph, + name='display_genre_graph'), + path('audio_features/', audio_features, + name='display_audio_features'), +] diff --git a/graphs/views.py b/graphs/views.py new file mode 100644 index 0000000..e65f73b --- /dev/null +++ b/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) diff --git a/login/__init__.py b/login/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/login/apps.py b/login/apps.py new file mode 100644 index 0000000..645de5f --- /dev/null +++ b/login/apps.py @@ -0,0 +1,4 @@ +from django.apps import AppConfig + +class LoginConfig(AppConfig): + name = 'login' diff --git a/spotifyvis/templates/spotifyvis/index.html b/login/templates/login/index.html similarity index 100% rename from spotifyvis/templates/spotifyvis/index.html rename to login/templates/login/index.html diff --git a/login/urls.py b/login/urls.py new file mode 100644 index 0000000..02ca78d --- /dev/null +++ b/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'), +] diff --git a/spotifyvis/views.py b/login/views.py similarity index 58% rename from spotifyvis/views.py rename to login/views.py index ef9b391..c479a19 100644 --- a/spotifyvis/views.py +++ b/login/views.py @@ -11,10 +11,7 @@ import string from datetime import datetime from django.shortcuts import render, redirect -from django.http import HttpResponse, HttpResponseBadRequest, 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 +from django.http import HttpResponseBadRequest # }}} imports # @@ -65,7 +62,7 @@ def index(request): # login {{{ # # uses Authorization Code flow -def login(request): +def spotify_login(request): # use a randomly generated state string to prevent cross-site request forgery attacks state_str = generate_random_string(16) request.session['state_string'] = state_str @@ -117,7 +114,6 @@ def callback(request): # user_data {{{ # - def user_data(request): # get user token {{{ # @@ -165,7 +161,8 @@ def user_data(request): '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) # }}} user_data # @@ -182,107 +179,3 @@ def admin_graphs(request): } update_track_genres(user_obj) 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 # diff --git a/manage.py b/manage.py index 7162f7a..f2b8f0f 100755 --- a/manage.py +++ b/manage.py @@ -3,7 +3,7 @@ import os import sys if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "musicvis.settings") + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "spotifyvis.settings") try: from django.core.management import execute_from_command_line except ImportError as exc: diff --git a/musicvis/urls.py b/musicvis/urls.py deleted file mode 100644 index c579fd9..0000000 --- a/musicvis/urls.py +++ /dev/null @@ -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), -] diff --git a/sample-track-obj.py b/sample-track-obj.py deleted file mode 100644 index c3a24ed..0000000 --- a/sample-track-obj.py +++ /dev/null @@ -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' - } -} diff --git a/spotifyvis/apps.py b/spotifyvis/apps.py deleted file mode 100644 index a232292..0000000 --- a/spotifyvis/apps.py +++ /dev/null @@ -1,5 +0,0 @@ -from django.apps import AppConfig - - -class SpotifyvisConfig(AppConfig): - name = 'spotifyvis' diff --git a/musicvis/settings.py b/spotifyvis/settings.py similarity index 94% rename from musicvis/settings.py rename to spotifyvis/settings.py index 0cedb25..7d951f5 100644 --- a/musicvis/settings.py +++ b/spotifyvis/settings.py @@ -37,7 +37,9 @@ INSTALLED_APPS = [ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', - 'spotifyvis.apps.SpotifyvisConfig', + 'login.apps.LoginConfig', + 'api.apps.ApiConfig', + 'graphs.apps.GraphsConfig', ] MIDDLEWARE = [ @@ -50,7 +52,7 @@ MIDDLEWARE = [ 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] -ROOT_URLCONF = 'musicvis.urls' +ROOT_URLCONF = 'spotifyvis.urls' TEMPLATES = [ { @@ -68,7 +70,7 @@ TEMPLATES = [ }, ] -WSGI_APPLICATION = 'musicvis.wsgi.application' +WSGI_APPLICATION = 'spotifyvis.wsgi.application' # Database diff --git a/spotifyvis/static/spotifyvis/scripts/index.js b/spotifyvis/static/spotifyvis/scripts/index.js deleted file mode 100644 index cfac1d1..0000000 --- a/spotifyvis/static/spotifyvis/scripts/index.js +++ /dev/null @@ -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(); -}); - diff --git a/spotifyvis/templates/spotifyvis/user_data.html b/spotifyvis/templates/spotifyvis/user_data.html deleted file mode 100644 index 67c99db..0000000 --- a/spotifyvis/templates/spotifyvis/user_data.html +++ /dev/null @@ -1,21 +0,0 @@ -{% load static %} - - - - - - - - - User Spotify Data - - - - - - -

Logged in as {{ id }}

- - diff --git a/spotifyvis/tests.py b/spotifyvis/tests.py deleted file mode 100644 index ed439b4..0000000 --- a/spotifyvis/tests.py +++ /dev/null @@ -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)) \ No newline at end of file diff --git a/spotifyvis/urls.py b/spotifyvis/urls.py index c28cfcd..62baf8a 100644 --- a/spotifyvis/urls.py +++ b/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 = [ - path('', index, name='index'), - path('login', login, name='login'), - path('callback', callback, name='callback'), - path('user_data', user_data, name='user_data'), - path('admin_graphs', admin_graphs, name='admin_graphs'), - path('api/user_artists/', get_artist_data, name='get_artist_data'), - path('artists/', artist_data, name='display_artist_graph'), - path('api/user_genres/', get_genre_data, name='get_genre_data'), - path('graphs/genre/', display_genre_graph, - name='display_genre_graph'), - path('audio_features/', audio_features, name='display_audio_features'), - path('api/audio_features//', - get_audio_feature_data, name='get_audio_feature_data'), + path('admin/', admin.site.urls), + path('login/', include('login.urls')), + path('api/', include('api.urls')), + path('graphs/', include('graphs.urls')), ] diff --git a/musicvis/wsgi.py b/spotifyvis/wsgi.py similarity index 82% rename from musicvis/wsgi.py rename to spotifyvis/wsgi.py index 089d0ea..5fdd0f1 100644 --- a/musicvis/wsgi.py +++ b/spotifyvis/wsgi.py @@ -11,6 +11,6 @@ import os 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()