diff --git a/api/models.py b/api/models.py index 7419443..5303876 100644 --- a/api/models.py +++ b/api/models.py @@ -1,4 +1,5 @@ from django.db import models +from login.models import User # id's are 22 in length in examples but set to 30 for buffer MAX_ID = 30 @@ -37,21 +38,6 @@ class Artist(models.Model): # }}} 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): diff --git a/api/urls.py b/api/urls.py index 204f9c8..1196097 100644 --- a/api/urls.py +++ b/api/urls.py @@ -2,7 +2,9 @@ from django.urls import path, include from .views import * +app_name = 'api' urlpatterns = [ + # path('scan/', get_artist_data), path('user_artists/', get_artist_data, name='get_artist_data'), path('user_genres/', get_genre_data, diff --git a/api/utils.py b/api/utils.py index 668c274..a3d1b22 100644 --- a/api/utils.py +++ b/api/utils.py @@ -17,93 +17,6 @@ FEATURES_LIMIT = 100 # ARTIST_LIMIT = 25 # FEATURES_LIMIT = 25 -# parse_library {{{ # - -def parse_library(headers, tracks, user): - """Scans user's library for certain number of tracks and store the information in a database - - :headers: For API call. - :tracks: Number of tracks to get from user's library. - :user: a User object representing the user whose library we are parsing - - :returns: None - - """ - # TODO: implement importing entire library with 0 as tracks param - # keeps track of point to get songs from - offset = 0 - payload = {'limit': str(USER_TRACKS_LIMIT)} - artist_genre_queue = [] - features_queue = [] - - # iterate until hit requested num of tracks - for i in range(0, tracks, USER_TRACKS_LIMIT): - payload['offset'] = str(offset) - saved_tracks_response = requests.get('https://api.spotify.com/v1/me/tracks', - headers=headers, - params=payload).json() - - for track_dict in saved_tracks_response['items']: - # add artists {{{ # - - # update artist info before track so that Track object can reference - # Artist object - track_artists = [] - for artist_dict in track_dict['track']['artists']: - artist_obj, artist_created = Artist.objects.get_or_create( - artist_id=artist_dict['id'], - name=artist_dict['name'],) - # only add/tally up artist genres if new - if artist_created: - artist_genre_queue.append(artist_obj) - if len(artist_genre_queue) == ARTIST_LIMIT: - add_artist_genres(headers, artist_genre_queue) - artist_genre_queue = [] - track_artists.append(artist_obj) - - # }}} add artists # - - # TODO: fix this, don't need any more - top_genre = "" - track_obj, track_created = save_track_obj(track_dict['track'], - track_artists, top_genre, user) - - # add audio features {{{ # - - # if a new track is not created, the associated audio feature does - # not need to be created again - if track_created: - features_queue.append(track_obj) - if len(features_queue) == FEATURES_LIMIT: - get_audio_features(headers, features_queue) - features_queue = [] - - # }}} add audio features # - - # temporary console logging - print("#{}-{}: {} - {}".format(offset + 1, - offset + USER_TRACKS_LIMIT, - track_obj.artists.first(), - track_obj.name)) - - # calculates num_songs with offset + songs retrieved - offset += USER_TRACKS_LIMIT - - # clean-up {{{ # - - # update remaining artists without genres and songs without features if - # there are any - if len(artist_genre_queue) > 0: - add_artist_genres(headers, artist_genre_queue) - if len(features_queue) > 0: - get_audio_features(headers, features_queue) - - # }}} clean-up # - - update_track_genres(user) - -# }}} parse_library # - # update_track_genres {{{ # def update_track_genres(user): diff --git a/api/views.py b/api/views.py index a3dfc27..92539d4 100644 --- a/api/views.py +++ b/api/views.py @@ -12,13 +12,100 @@ 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 .utils import get_artists_in_genre, update_track_genres from .models import User, Track, AudioFeatures, Artist # }}} imports # TRACKS_TO_QUERY = 200 +# parse_library {{{ # + +def parse_library(headers, tracks, user): + """Scans user's library for certain number of tracks and store the information in a database + + :headers: For API call. + :tracks: Number of tracks to get from user's library. + :user: a User object representing the user whose library we are parsing + + :returns: None + + """ + # TODO: implement importing entire library with 0 as tracks param + # keeps track of point to get songs from + offset = 0 + payload = {'limit': str(USER_TRACKS_LIMIT)} + artist_genre_queue = [] + features_queue = [] + + # iterate until hit requested num of tracks + for i in range(0, tracks, USER_TRACKS_LIMIT): + payload['offset'] = str(offset) + saved_tracks_response = requests.get('https://api.spotify.com/v1/me/tracks', + headers=headers, + params=payload).json() + + for track_dict in saved_tracks_response['items']: + # add artists {{{ # + + # update artist info before track so that Track object can reference + # Artist object + track_artists = [] + for artist_dict in track_dict['track']['artists']: + artist_obj, artist_created = Artist.objects.get_or_create( + artist_id=artist_dict['id'], + name=artist_dict['name'],) + # only add/tally up artist genres if new + if artist_created: + artist_genre_queue.append(artist_obj) + if len(artist_genre_queue) == ARTIST_LIMIT: + add_artist_genres(headers, artist_genre_queue) + artist_genre_queue = [] + track_artists.append(artist_obj) + + # }}} add artists # + + # TODO: fix this, don't need any more + top_genre = "" + track_obj, track_created = save_track_obj(track_dict['track'], + track_artists, top_genre, user) + + # add audio features {{{ # + + # if a new track is not created, the associated audio feature does + # not need to be created again + if track_created: + features_queue.append(track_obj) + if len(features_queue) == FEATURES_LIMIT: + get_audio_features(headers, features_queue) + features_queue = [] + + # }}} add audio features # + + # temporary console logging + print("#{}-{}: {} - {}".format(offset + 1, + offset + USER_TRACKS_LIMIT, + track_obj.artists.first(), + track_obj.name)) + + # calculates num_songs with offset + songs retrieved + offset += USER_TRACKS_LIMIT + + # clean-up {{{ # + + # update remaining artists without genres and songs without features if + # there are any + if len(artist_genre_queue) > 0: + add_artist_genres(headers, artist_genre_queue) + if len(features_queue) > 0: + get_audio_features(headers, features_queue) + + # }}} clean-up # + + update_track_genres(user) + +# }}} parse_library # + # get_artist_data {{{ # diff --git a/graphs/urls.py b/graphs/urls.py index 9bfcf99..0453083 100644 --- a/graphs/urls.py +++ b/graphs/urls.py @@ -2,6 +2,7 @@ from django.urls import path, include from .views import * +app_name = 'graphs' urlpatterns = [ path('artists/', artist_data, name='display_artist_graph'), diff --git a/login/models.py b/login/models.py new file mode 100644 index 0000000..e3fa787 --- /dev/null +++ b/login/models.py @@ -0,0 +1,22 @@ +from django.db import models + +# id's are 22 in length in examples but set to 30 for buffer +MAX_ID = 30 +# saw tokens being about ~150 chars in length +TOKEN_LENGTH = 200 + +class User(models.Model): + class Meta: + verbose_name = "User" + verbose_name_plural = "Users" + + # the user's Spotify ID + id = models.CharField(primary_key=True, max_length=MAX_ID) + secret = models.CharField(max_length=50, default='') + refresh_token = models.CharField(max_length=TOKEN_LENGTH) + access_token = models.CharField(max_length=TOKEN_LENGTH) + access_obtained_at = models.DateTimeField(auto_now_add=True) + access_expires_in = models.PositiveIntegerField() + + def __str__(self): + return self.user_id diff --git a/login/templates/login/index.html b/login/templates/login/index.html index 9f6e27a..3a41ec5 100644 --- a/login/templates/login/index.html +++ b/login/templates/login/index.html @@ -4,7 +4,7 @@ User Login - +