Browse Source

Setup Track table, modified models to data

Recreated migrations from scratch to fix bug with initializing Track
objects.
master
Kevin Mok 6 years ago
parent
commit
cdcc33c014
  1. 2
      musicvis/settings.py
  2. 26
      spotifyvis/migrations/0001_initial.py
  3. 22
      spotifyvis/models.py
  4. 59
      spotifyvis/utils.py
  5. 9
      spotifyvis/views.py

2
musicvis/settings.py

@ -110,7 +110,7 @@ AUTH_PASSWORD_VALIDATORS = [
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
TIME_ZONE = 'America/Toronto'
USE_I18N = True

26
spotifyvis/migrations/0001_initial.py

@ -1,4 +1,4 @@
# Generated by Django 2.0.5 on 2018-06-03 23:01
# Generated by Django 2.0.5 on 2018-06-06 07:26
from django.db import migrations, models
import django.db.models.deletion
@ -20,34 +20,32 @@ class Migration(migrations.Migration):
('genre', models.CharField(max_length=20)),
],
options={
'verbose_name': 'Artist',
'verbose_name_plural': 'Artists',
'verbose_name': 'Artist',
},
),
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)),
('track_id', models.CharField(max_length=30, primary_key=True, serialize=False)),
('year', models.PositiveSmallIntegerField()),
('popularity', models.DecimalField(decimal_places=2, max_digits=2)),
('popularity', models.PositiveSmallIntegerField()),
('runtime', models.PositiveSmallIntegerField()),
('name', models.CharField(max_length=75)),
],
options={
'verbose_name': 'Track',
'verbose_name_plural': 'Tracks',
'verbose_name': 'Track',
},
),
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',
'verbose_name': 'User',
},
),
migrations.CreateModel(
@ -64,22 +62,18 @@ class Migration(migrations.Migration):
('tempo', models.DecimalField(decimal_places=2, max_digits=2)),
],
options={
'verbose_name': 'AudioFeatures',
'verbose_name_plural': 'AudioFeatures',
'verbose_name': 'AudioFeatures',
},
),
migrations.AddField(
model_name='track',
name='artist',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='spotifyvis.Artist'),
name='artists',
field=models.ManyToManyField(blank=True, 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')},
field=models.ManyToManyField(blank=True, to='spotifyvis.User'),
),
]

22
spotifyvis/models.py

@ -1,5 +1,8 @@
from django.db import models
# id's are 22 in length in examples but set to 30 for buffer
id_length=30
# Artist {{{ #
class Artist(models.Model):
@ -7,7 +10,7 @@ class Artist(models.Model):
verbose_name = "Artist"
verbose_name_plural = "Artists"
artist_id = models.CharField(primary_key=True, max_length=30)
artist_id = models.CharField(primary_key=True, max_length=id_length)
# unique since only storing one genre per artist right now
name = models.CharField(unique=True, max_length=50)
genre = models.CharField(max_length=20)
@ -24,11 +27,11 @@ class User(models.Model):
verbose_name = "User"
verbose_name_plural = "Users"
user_id = models.CharField(primary_key=True, max_length=30) # the user's Spotify ID
username = models.CharField(max_length=30) # User's Spotify user name, if set
user_id = models.CharField(primary_key=True, max_length=id_length) # the user's Spotify ID
# username = models.CharField(max_length=30) # User's Spotify user name, if set
def __str__(self):
return self.username
return self.user_id
# }}} User #
@ -39,15 +42,16 @@ class Track(models.Model):
class Meta:
verbose_name = "Track"
verbose_name_plural = "Tracks"
unique_together = ('track_id', 'artist',)
# unique_together = ('track_id', 'artist',)
track_id = models.CharField(max_length=30)
artist = models.ForeignKey(Artist, on_delete=models.CASCADE)
track_id = models.CharField(primary_key=True, max_length=id_length)
# artist = models.ForeignKey(Artist, on_delete=models.CASCADE)
artists = models.ManyToManyField(Artist, blank=True)
year = models.PositiveSmallIntegerField()
popularity = models.DecimalField(decimal_places=2, max_digits=2)
popularity = models.PositiveSmallIntegerField()
runtime = models.PositiveSmallIntegerField()
name = models.CharField(max_length=75)
users = models.ManyToManyField(User)
users = models.ManyToManyField(User, blank=True)
def __str__(self):
return self.name

59
spotifyvis/utils.py

@ -30,23 +30,38 @@ def parse_library(headers, tracks, library_stats, user):
num_samples = 0 # number of actual track samples
feature_data_points = 0 # number of feature data analyses (some tracks do not have analyses available)
# iterate until hit requested num of tracks
for _ in range(0, tracks, limit):
payload['offset'] = str(offset)
# get current set of tracks
saved_tracks_response = requests.get('https://api.spotify.com/v1/me/tracks', headers=headers, params=payload).json()
# TODO: refactor the for loop body into helper function
# iterate through each track
for track_dict in saved_tracks_response['items']:
num_samples += 1
# update artist info before track so that Track object can reference
# Artist object
track_artists = []
for artist_dict in track_dict['track']['artists']:
increase_artist_count(headers, artist_dict['name'],
artist_dict['id'], library_stats)
track_artists.append(Artist.objects.get_or_create(
artist_id=artist_dict['id'],
name=artist_dict['name'],
)[0])
save_track_obj(track_dict['track'], track_artists, user)
get_track_info(track_dict['track'], library_stats, num_samples)
# get_genre(headers, track_dict['track']['album']['id'])
audio_features_dict = get_audio_features(headers, track_dict['track']['id'])
if len(audio_features_dict) != 0:
# Track the number of audio analyses for calculating
# audio feature averages and standard deviations on the fly
feature_data_points += 1
for feature, feature_data in audio_features_dict.items():
update_audio_feature_stats(feature, feature_data, feature_data_points, library_stats)
for artist_dict in track_dict['track']['artists']:
increase_artist_count(headers, artist_dict['name'], artist_dict['id'], library_stats)
update_audio_feature_stats(feature, feature_data,
feature_data_points, library_stats)
# calculates num_songs with offset + songs retrieved
library_stats['num_songs'] = offset + len(saved_tracks_response['items'])
offset += limit
@ -55,6 +70,32 @@ def parse_library(headers, tracks, library_stats, user):
# }}} parse_library #
def save_track_obj(track_dict, artists, user):
"""Make an entry in the database for this track if it doesn't exist already.
:track_dict: TODO
:artists: artists of the song, passed in as a list of Artist objects.
:user: TODO
:returns: None
"""
if len(Track.objects.filter(track_id__exact=track_dict['id'])) == 0:
new_track = Track.objects.create(
track_id=track_dict['id'],
year=track_dict['album']['release_date'].split('-')[0],
popularity=int(track_dict['popularity']),
runtime=int(float(track_dict['duration_ms']) / 1000),
name=track_dict['name'],
)
# print("pop/run: ", new_track.popularity, new_track.runtime)
# have to add artists and user after saving object since track needs to
# have ID before filling in m2m field
for artist in artists:
new_track.artists.add(artist)
new_track.users.add(user)
new_track.save()
# get_audio_features {{{ #
def get_audio_features(headers, track_id):
@ -185,14 +226,6 @@ def increase_artist_count(headers, artist_name, artist_id, library_stats):
else:
library_stats['artists'][artist_name]['count'] += 1
# add artist to database if new
if len(Artist.objects.filter(artist_id__contains=artist_id)) == 0:
new_artist = Artist(
artist_id=artist_id,
name=artist_name,
)
new_artist.save()
# }}} increase_artist_count #
# update_popularity_stats {{{ #

9
spotifyvis/views.py

@ -139,12 +139,9 @@ def user_data(request):
user_data_response = requests.get('https://api.spotify.com/v1/me', headers = headers).json()
request.session['user_id'] = user_data_response['id'] # store the user_id so it may be used to create model
request.session['user_name'] = user_data_response['display_name']
user = None # will be set to the current user object later
# try:
# user = User.objects.get(user_id=request.session['user_id'])
# except User.DoesNotExist:
# user = User.objects.create(user_id=request.session['user_id'], user_name=request.session['user_name'])
# request.session['user_name'] = user_data_response['display_name']
user = User.objects.get_or_create(user_id=user_data_response['id'])[0]
context = {
'user_name': user_data_response['display_name'],
'id': user_data_response['id'],

Loading…
Cancel
Save