Browse Source

Wrote up proposed model design for database

Have not actually created any models yet.
master
Kevin Mok 7 years ago
parent
commit
9fb37f0615
  1. 85
      spotifyvis/migrations/0001_initial.py
  2. 70
      spotifyvis/models.py
  3. 42
      spotifyvis/static/spotifyvis/scripts/index.js
  4. 14
      spotifyvis/templates/spotifyvis/index.html
  5. 2
      spotifyvis/tests.py
  6. 8
      spotifyvis/utils.py

85
spotifyvis/migrations/0001_initial.py

@ -0,0 +1,85 @@
# Generated by Django 2.0.5 on 2018-06-03 23:01
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Artist',
fields=[
('artist_id', models.CharField(max_length=30, primary_key=True, serialize=False)),
('name', models.CharField(max_length=50, unique=True)),
('genre', models.CharField(max_length=20)),
],
options={
'verbose_name': 'Artist',
'verbose_name_plural': 'Artists',
},
),
migrations.CreateModel(
name='Track',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('track_id', models.CharField(max_length=30)),
('year', models.PositiveSmallIntegerField()),
('popularity', models.DecimalField(decimal_places=2, max_digits=2)),
('runtime', models.PositiveSmallIntegerField()),
('name', models.CharField(max_length=75)),
],
options={
'verbose_name': 'Track',
'verbose_name_plural': 'Tracks',
},
),
migrations.CreateModel(
name='User',
fields=[
('user_id', models.CharField(max_length=30, primary_key=True, serialize=False)),
('username', models.CharField(max_length=30)),
],
options={
'verbose_name': 'User',
'verbose_name_plural': 'Users',
},
),
migrations.CreateModel(
name='AudioFeatures',
fields=[
('track', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='spotifyvis.Track')),
('danceability', models.DecimalField(decimal_places=2, max_digits=2)),
('energy', models.DecimalField(decimal_places=2, max_digits=2)),
('loudness', models.DecimalField(decimal_places=2, max_digits=2)),
('speechiness', models.DecimalField(decimal_places=2, max_digits=2)),
('acousticness', models.DecimalField(decimal_places=2, max_digits=2)),
('instrumentalness', models.DecimalField(decimal_places=2, max_digits=2)),
('valence', models.DecimalField(decimal_places=2, max_digits=2)),
('tempo', models.DecimalField(decimal_places=2, max_digits=2)),
],
options={
'verbose_name': 'AudioFeatures',
'verbose_name_plural': 'AudioFeatures',
},
),
migrations.AddField(
model_name='track',
name='artist',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='spotifyvis.Artist'),
),
migrations.AddField(
model_name='track',
name='users',
field=models.ManyToManyField(to='spotifyvis.User'),
),
migrations.AlterUniqueTogether(
name='track',
unique_together={('track_id', 'artist')},
),
]

70
spotifyvis/models.py

@ -1,17 +1,4 @@
from django.db import models from django.db import models
from django.contrib.postgres.fields import JSONField
class Genre(models.Model):
class Meta:
verbose_name = "Genre"
verbose_name_plural = "Genres"
name = models.CharField()
def __str__(self):
return self.name
class Artist(models.Model): class Artist(models.Model):
@ -19,47 +6,64 @@ class Artist(models.Model):
verbose_name = "Artist" verbose_name = "Artist"
verbose_name_plural = "Artists" verbose_name_plural = "Artists"
name = models.CharField()
genre = models.OneToOneField(Genre, on_delete=models.CASCADE)
artist_id = models.CharField(primary_key=True, max_length=30)
# unique since only storing one genre per artist right now
name = models.CharField(unique=True, max_length=50)
genre = models.CharField(max_length=20)
def __str__(self): def __str__(self):
return super(Artist, self).__str__()
return self.name
class AudioFeatures(models.Model):
class User(models.Model):
class Meta: class Meta:
verbose_name = "AudioFeatures"
verbose_name_plural = "AudioFeatures"
verbose_name = "User"
verbose_name_plural = "Users"
features = JSONField()
user_id = models.CharField(primary_key=True, max_length=30)
username = models.CharField(max_length=30)
def __str__(self): def __str__(self):
return self.features
return self.username
class Year(models.Model):
class Track(models.Model):
class Meta: class Meta:
verbose_name = "Year"
verbose_name_plural = "Years"
verbose_name = "Track"
verbose_name_plural = "Tracks"
unique_together = ('track_id', 'artist',)
track_id = models.CharField(max_length=30)
artist = models.ForeignKey(Artist, on_delete=models.CASCADE)
year = models.PositiveSmallIntegerField() year = models.PositiveSmallIntegerField()
popularity = models.DecimalField(decimal_places=2, max_digits=2)
runtime = models.PositiveSmallIntegerField()
name = models.CharField(max_length=75)
users = models.ManyToManyField(User)
def __str__(self): def __str__(self):
return self.year
return self.name
class Track(models.Model):
class AudioFeatures(models.Model):
class Meta: class Meta:
verbose_name = "Track"
verbose_name_plural = "Tracks"
verbose_name = "AudioFeatures"
verbose_name_plural = "AudioFeatures"
popularity = models.DecimalField(decimal_places=2)
runtime = models.PositiveSmallIntegerField()
name = models.CharField()
track = models.OneToOneField(Track, on_delete=models.CASCADE, primary_key=True,)
danceability = models.DecimalField(decimal_places=2, max_digits=2)
energy = models.DecimalField(decimal_places=2, max_digits=2)
loudness = models.DecimalField(decimal_places=2, max_digits=2)
speechiness = models.DecimalField(decimal_places=2, max_digits=2)
acousticness = models.DecimalField(decimal_places=2, max_digits=2)
instrumentalness = models.DecimalField(decimal_places=2, max_digits=2)
valence = models.DecimalField(decimal_places=2, max_digits=2)
tempo = models.DecimalField(decimal_places=2, max_digits=2)
def __str__(self): def __str__(self):
return self.name
return super(AudioFeatures, self).__str__()

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

@ -0,0 +1,42 @@
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();
});

14
spotifyvis/templates/spotifyvis/index.html

@ -1,4 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
{% load static %}
<html> <html>
<head> <head>
<title>User Login</title> <title>User Login</title>
@ -11,14 +12,25 @@
width: 500px; width: 500px;
} }
</style> </style>
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<div id="login"> <div id="login">
<h1>This is an example of the Authorization Code flow</h1> <h1>This is an example of the Authorization Code flow</h1>
<a href="/login" class="btn btn-primary">Log in with Spotify</a>
<a href="/login" class="btn btn-primary">Log In (Original)</a>
<button id="login-btn">Log In</button>
</div>
<div id="data-container">
<ul id="data-list">
</ul>
</div> </div>
</div> </div>
<script src="{% static 'spotifyvis/scripts/index.js' %}"></script>
</body> </body>
</html> </html>

2
spotifyvis/tests.py

@ -1,5 +1,5 @@
from django.test import TestCase from django.test import TestCase
from .views import update_std_dev
from .utils import update_std_dev
import math import math
# Create your tests here. # Create your tests here.

8
spotifyvis/utils.py

@ -27,10 +27,11 @@ def parse_library(headers, tracks, library_stats):
for track_dict in saved_tracks_response['items']: for track_dict in saved_tracks_response['items']:
# Track the number of samples for calculating # Track the number of samples for calculating
# audio feature averages and standard deviations on the fly # audio feature averages and standard deviations on the fly
num_samples += 1
get_track_info(track_dict['track'], library_stats, num_samples) get_track_info(track_dict['track'], library_stats, num_samples)
# get_genre(headers, track_dict['track']['album']['id']) # get_genre(headers, track_dict['track']['album']['id'])
audio_features_dict = get_audio_features(headers, track_dict['track']['id']) audio_features_dict = get_audio_features(headers, track_dict['track']['id'])
if len(audio_features_dict) != 0:
num_samples += 1
for feature, feature_data in audio_features_dict.items(): for feature, feature_data in audio_features_dict.items():
update_audio_feature_stats(feature, feature_data, num_samples, library_stats) update_audio_feature_stats(feature, feature_data, num_samples, library_stats)
for artist_dict in track_dict['track']['artists']: for artist_dict in track_dict['track']['artists']:
@ -51,10 +52,13 @@ def get_audio_features(headers, track_id):
track_id: the id of the soundtrack, needed to query the Spotify API track_id: the id of the soundtrack, needed to query the Spotify API
Returns: Returns:
A dictionary with the features as its keys
A dictionary with the features as its keys, if audio feature data is missing for the track,
an empty dictionary is returned.
""" """
response = requests.get("https://api.spotify.com/v1/audio-features/{}".format(track_id), headers = headers).json() response = requests.get("https://api.spotify.com/v1/audio-features/{}".format(track_id), headers = headers).json()
if 'error' in response:
return {}
features_dict = {} features_dict = {}
# Data that we don't need # Data that we don't need

Loading…
Cancel
Save