diff --git a/api/models.py b/api/models.py index 41daa01..4573f94 100644 --- a/api/models.py +++ b/api/models.py @@ -47,7 +47,7 @@ class Track(models.Model): 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() + year = models.PositiveSmallIntegerField(null=True) popularity = models.PositiveSmallIntegerField() runtime = models.PositiveSmallIntegerField() name = models.CharField(max_length=200) diff --git a/api/urls.py b/api/urls.py index 60126f6..2d9d78b 100644 --- a/api/urls.py +++ b/api/urls.py @@ -4,8 +4,10 @@ from .views import * app_name = 'api' urlpatterns = [ - path('scan/', parse_library, - name='scan'), + path('scan/library/', parse_library, + name='scan_library'), + path('scan/history/', parse_history, + name='scan_history'), 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 d9db42e..c6a384c 100644 --- a/api/utils.py +++ b/api/utils.py @@ -14,8 +14,8 @@ from login.models import User # }}} imports # -console_logging = True -# console_logging = False +# console_logging = True +console_logging = False artists_genre_processed = 0 features_processed = 0 @@ -74,19 +74,32 @@ def save_track_obj(track_dict, artists, user_obj): if len(track_query) != 0: return track_query[0], False else: - new_track = Track.objects.create( - 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'], - ) + # check if track is simple or full, simple Track object won't have year + # if 'album' in track_dict: + try: + new_track = Track.objects.create( + 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'], + ) + # else: + except KeyError: + new_track = Track.objects.create( + id=track_dict['id'], + popularity=int(track_dict['popularity']), + runtime=int(float(track_dict['duration_ms']) / 1000), + name=track_dict['name'], + ) # have to add artists and user_obj 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_obj) + # print(new_track.name, artist.name) + if user_obj != None: + new_track.users.add(user_obj) new_track.save() return new_track, True @@ -178,6 +191,7 @@ def add_artist_genres(headers, artist_objs): else: for genre in artists_response[i]['genres']: process_artist_genre(genre, artist_objs[i]) + # print(artist_objs[i].name, genre) if console_logging: global artists_genre_processed @@ -221,6 +235,15 @@ def get_artists_in_genre(user, genre, max_songs): # }}} get_artists_in_genre # +def create_artist_for_track(artist_dict): + """TODO: Docstring for create_artist_for_track. + + :artist_dict: TODO + :returns: None + + """ + pass + def get_user_header(user_obj): """Returns the authorization string needed to make an API call. diff --git a/api/views.py b/api/views.py index 5a482d2..bdcc248 100644 --- a/api/views.py +++ b/api/views.py @@ -19,13 +19,16 @@ from login.utils import get_user_context # }}} imports # USER_TRACKS_LIMIT = 50 +HISTORY_LIMIT = 50 ARTIST_LIMIT = 50 FEATURES_LIMIT = 100 # ARTIST_LIMIT = 25 # FEATURES_LIMIT = 25 TRACKS_TO_QUERY = 100 +HISTORY_ENDPOINT = 'https://api.spotify.com/v1/me/player/recently-played' console_logging = True +# console_logging = False # parse_library {{{ # @@ -119,6 +122,71 @@ def parse_library(request, user_secret): # }}} parse_library # +# parse_history {{{ # + +def parse_history(request, user_secret): + """Scans user's listening history and stores the information in a + database. + + :user_secret: secret for User object who's library is being scanned. + :returns: None + """ + + payload = {'limit': str(USER_TRACKS_LIMIT)} + artist_genre_queue = [] + user_obj = User.objects.get(secret=user_secret) + user_headers = get_user_header(user_obj) + + history_response = requests.get(HISTORY_ENDPOINT, + headers=user_headers, + params=payload).json()['items'] + + if console_logging: + tracks_processed = 0 + + for track_dict in history_response: + # 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( + 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(user_headers, artist_genre_queue) + artist_genre_queue = [] + track_artists.append(artist_obj) + + # }}} add artists # + + # don't associate history track with User, not necessarily in their + # library + track_obj, track_created = save_track_obj(track_dict['track'], + track_artists, None) + + if console_logging: + tracks_processed += 1 + print("Added track #{}: {} - {}".format( + tracks_processed, + track_obj.artists.first(), + track_obj.name, + )) + + if len(artist_genre_queue) > 0: + add_artist_genres(user_headers, artist_genre_queue) + + # TODO: update track genres from History relation + # update_track_genres(user_obj) + + return render(request, 'graphs/logged_in.html', get_user_context(user_obj)) + +# }}} get_history # + # get_artist_data {{{ # def get_artist_data(request, user_secret): diff --git a/graphs/templates/graphs/genre_graph.html b/graphs/templates/graphs/genre_graph.html index bf7324e..4eb5940 100644 --- a/graphs/templates/graphs/genre_graph.html +++ b/graphs/templates/graphs/genre_graph.html @@ -21,10 +21,18 @@ + {% load static %} - + + + + diff --git a/login/templates/login/scan.html b/login/templates/login/scan.html index 183742c..e0c6dce 100644 --- a/login/templates/login/scan.html +++ b/login/templates/login/scan.html @@ -18,8 +18,11 @@

You are using an outdated browser. Please upgrade your browser to improve your experience.

Logged in as {{ user_id }}

- + Scan Library + + Scan History + diff --git a/login/views.py b/login/views.py index 9406d19..1e0bcbe 100644 --- a/login/views.py +++ b/login/views.py @@ -19,6 +19,7 @@ from .utils import * TIME_FORMAT = '%Y-%m-%d-%H-%M-%S' TRACKS_TO_QUERY = 200 +AUTH_SCOPE = ['user-library-read', 'user-read-recently-played',] # generate_random_string {{{ # @@ -62,7 +63,7 @@ def spotify_login(request): 'response_type': 'code', 'redirect_uri': 'http://localhost:8000/login/callback', 'state': state_str, - 'scope': 'user-library-read', + 'scope': " ".join(AUTH_SCOPE), 'show_dialog': False }