Browse Source
Split spotifyvis code into different apps (#47)
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
34 changed files with 319 additions and 538 deletions
-
0api/__init__.py
-
0api/admin.py
-
4api/apps.py
-
0api/models.py
-
0api/templates/api/logged_in.html
-
12api/urls.py
-
0api/utils.py
-
86api/views.py
-
0common-static/css/dark_bg.css
-
0graphs/__init__.py
-
4graphs/apps.py
-
104graphs/models.py
-
0graphs/static/graphs/scripts/artist_graph.js
-
0graphs/static/graphs/scripts/genre_graph.js
-
0graphs/templates/graphs/artist_graph.html
-
0graphs/templates/graphs/features_graph.html
-
0graphs/templates/graphs/genre_graph.html
-
12graphs/urls.py
-
51graphs/views.py
-
0login/__init__.py
-
4login/apps.py
-
0login/templates/login/index.html
-
11login/urls.py
-
115login/views.py
-
2manage.py
-
22musicvis/urls.py
-
250sample-track-obj.py
-
5spotifyvis/apps.py
-
8spotifyvis/settings.py
-
42spotifyvis/static/spotifyvis/scripts/index.js
-
21spotifyvis/templates/spotifyvis/user_data.html
-
67spotifyvis/tests.py
-
35spotifyvis/urls.py
-
2spotifyvis/wsgi.py
@ -0,0 +1,4 @@ |
|||
from django.apps import AppConfig |
|||
|
|||
class ApiConfig(AppConfig): |
|||
name = 'api' |
@ -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,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,0 +1,4 @@ |
|||
from django.apps import AppConfig |
|||
|
|||
class GraphsConfig(AppConfig): |
|||
name = 'graphs' |
@ -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,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'), |
|||
] |
@ -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,0 +1,4 @@ |
|||
from django.apps import AppConfig |
|||
|
|||
class LoginConfig(AppConfig): |
|||
name = 'login' |
@ -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'), |
|||
] |
@ -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), |
|||
] |
@ -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' |
|||
} |
|||
} |
@ -1,5 +0,0 @@ |
|||
from django.apps import AppConfig |
|||
|
|||
|
|||
class SpotifyvisConfig(AppConfig): |
|||
name = 'spotifyvis' |
@ -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(); |
|||
}); |
|||
|
@ -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> |
@ -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)) |
@ -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/<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'), |
|||
path('admin/', admin.site.urls), |
|||
path('login/', include('login.urls')), |
|||
path('api/', include('api.urls')), |
|||
path('graphs/', include('graphs.urls')), |
|||
] |
Write
Preview
Loading…
Cancel
Save
Reference in new issue