From c305a32717fd4b4b0d6657f0316c1d0f061a4e57 Mon Sep 17 00:00:00 2001 From: RecursiveGreen Date: Tue, 4 Jun 2019 12:03:12 -0400 Subject: [PATCH] Updated API for new Stores and general cleanup. --- savepointradio/api/serializers/profiles.py | 6 +- savepointradio/api/serializers/radio.py | 80 ++++++++++++++++++---- savepointradio/api/views/profiles.py | 6 +- savepointradio/api/views/radio.py | 26 ++++--- 4 files changed, 91 insertions(+), 27 deletions(-) diff --git a/savepointradio/api/serializers/profiles.py b/savepointradio/api/serializers/profiles.py index f61db15..5ab8cdb 100644 --- a/savepointradio/api/serializers/profiles.py +++ b/savepointradio/api/serializers/profiles.py @@ -4,7 +4,7 @@ from rest_framework.serializers import (IntegerField, ModelSerializer, Serializer) from profiles.models import RadioProfile, SongRequest, Rating -from .radio import BasicSongRetrieveSerializer +from .radio import SongMinimalSerializer User = get_user_model() @@ -44,7 +44,7 @@ class RateSongSerializer(Serializer): class HistorySerializer(ModelSerializer): profile = BasicProfileSerializer() - song = BasicSongRetrieveSerializer() + song = SongMinimalSerializer() class Meta: model = SongRequest @@ -52,7 +52,7 @@ class HistorySerializer(ModelSerializer): class BasicProfileRatingsSerializer(ModelSerializer): - song = BasicSongRetrieveSerializer() + song = SongMinimalSerializer() class Meta: model = Rating diff --git a/savepointradio/api/serializers/radio.py b/savepointradio/api/serializers/radio.py index 09f64a2..f6a7bab 100644 --- a/savepointradio/api/serializers/radio.py +++ b/savepointradio/api/serializers/radio.py @@ -1,71 +1,127 @@ -from rest_framework.serializers import (IntegerField, ListField, +from rest_framework.serializers import (DecimalField, IntegerField, ListField, ModelSerializer, Serializer, + SerializerMethodField, StringRelatedField) +from core.utils import iri_to_path from radio.models import Album, Artist, Game, Song class AlbumSerializer(ModelSerializer): + '''A base serializer for an album model.''' class Meta: model = Album fields = ('id', 'title') class ArtistSerializer(ModelSerializer): + '''A base serializer for an artist model.''' class Meta: model = Artist fields = ('id', 'alias', 'first_name', 'last_name') class ArtistFullnameSerializer(ModelSerializer): + ''' + A base serializer for an artist model, but combining all name + attributes into one field. + ''' class Meta: model = Artist fields = ('id', 'full_name') class GameSerializer(ModelSerializer): + '''A base serializer for a game model.''' class Meta: model = Game fields = ('id', 'title') -class BasicSongSerializer(ModelSerializer): - class Meta: - model = Song - fields = ('id', 'album', 'artists', 'game', 'title', 'average_rating', - 'is_requestable') +class SongSerializer(ModelSerializer): + '''A base serializer for a song model.''' + length = DecimalField( + max_digits=10, + decimal_places=2, + source='current_store.length' + ) - -class FullSongSerializer(ModelSerializer): class Meta: model = Song fields = ('id', 'album', 'artists', 'published_date', 'game', - 'num_played', 'last_played', 'length', 'song_type', 'title', - 'average_rating', 'is_requestable') + 'num_played', 'last_played', 'length', 'next_play', + 'song_type', 'title', 'average_rating', 'is_requestable') -class BasicSongRetrieveSerializer(BasicSongSerializer): +class SongMinimalSerializer(ModelSerializer): + '''Minimal song information, usually appended to favorites/ratings.''' album = AlbumSerializer() artists = ArtistFullnameSerializer(many=True) game = GameSerializer() + class Meta: + model = Song + fields = ('id', 'album', 'artists', 'game', 'title') -class FullSongRetrieveSerializer(FullSongSerializer): + +class SongListSerializer(ModelSerializer): + '''Song information used in large listings.''' + album = AlbumSerializer() + artists = ArtistFullnameSerializer(many=True) + game = GameSerializer() + length = DecimalField( + max_digits=10, + decimal_places=2, + source='current_store.length' + ) + + class Meta: + model = Song + fields = ('id', 'album', 'artists', 'game', 'title', 'average_rating', + 'length', 'is_requestable') + + +class SongRetrieveSerializer(SongSerializer): + ''' + An almost complete listing of a song's information, based on a single + object retrieval. + ''' album = AlbumSerializer() artists = ArtistSerializer(many=True) game = GameSerializer() class RadioSongSerializer(ModelSerializer): + ''' + A song serializer that is specific to the radio DJ and the underlying + audio manipulation application. + ''' album = StringRelatedField() artists = StringRelatedField(many=True) game = StringRelatedField() + length = DecimalField( + max_digits=10, + decimal_places=2, + source='current_store.length' + ) + path = SerializerMethodField() class Meta: model = Song fields = ('album', 'artists', 'game', 'song_type', 'title', 'length', 'path') + def get_path(self, obj): + '''Converts the IRI into a filesystem path.''' + iri = str(obj.current_store.iri) + if iri.startswith('file://'): + return iri_to_path(iri) + return iri + class SongArtistsListSerializer(Serializer): + ''' + A serializer for adding or removing artists from a song based on + the song's id number. + ''' artists = ListField(child=IntegerField(), min_length=1, max_length=10) diff --git a/savepointradio/api/views/profiles.py b/savepointradio/api/views/profiles.py index 2b4e568..52fe553 100644 --- a/savepointradio/api/views/profiles.py +++ b/savepointradio/api/views/profiles.py @@ -11,7 +11,7 @@ from ..serializers.profiles import (BasicProfileSerializer, FullProfileSerializer, HistorySerializer, BasicProfileRatingsSerializer) -from ..serializers.radio import BasicSongRetrieveSerializer +from ..serializers.radio import SongListSerializer class ProfileViewSet(viewsets.ModelViewSet): @@ -52,10 +52,10 @@ class ProfileViewSet(viewsets.ModelViewSet): page = self.paginate_queryset(favorites) if page is not None: - serializer = BasicSongRetrieveSerializer(page, many=True) + serializer = SongListSerializer(page, many=True) return self.get_paginated_response(serializer.data) - serializer = BasicSongRetrieveSerializer(favorites, many=True) + serializer = SongListSerializer(favorites, many=True) return Response(serializer.data) @action(detail=True, permission_classes=[AllowAny]) diff --git a/savepointradio/api/views/radio.py b/savepointradio/api/views/radio.py index 4287fb0..80ac724 100644 --- a/savepointradio/api/views/radio.py +++ b/savepointradio/api/views/radio.py @@ -10,9 +10,9 @@ from ..serializers.profiles import (BasicProfileSerializer, BasicSongRatingsSerializer, RateSongSerializer) from ..serializers.radio import (AlbumSerializer, ArtistSerializer, - GameSerializer, FullSongSerializer, - SongArtistsListSerializer, - FullSongRetrieveSerializer) + GameSerializer, SongSerializer, + SongListSerializer, SongRetrieveSerializer, + SongArtistsListSerializer) class AlbumViewSet(viewsets.ModelViewSet): @@ -83,9 +83,11 @@ class SongViewSet(viewsets.ModelViewSet): (Thanks to https://stackoverflow.com/questions/22616973/) ''' - if self.action in ['list', 'retrieve']: - return FullSongRetrieveSerializer - return FullSongSerializer + if self.action == 'list': + return SongListSerializer + if self.action == 'retrieve': + return SongRetrieveSerializer + return SongSerializer def _artists_change(self, request, remove=False): song = self.get_object() @@ -101,20 +103,21 @@ class SongViewSet(viewsets.ModelViewSet): message = 'Artists {} song.'.format(('added to', 'removed from')[remove]) return Response({'detail': message}) - else: - return Response(serializer.errors, - status=status.HTTP_400_BAD_REQUEST) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @action(methods=['post'], detail=True, permission_classes=[IsAdminUser]) def artists_add(self, request, pk=None): + '''Adds an artist to a song.''' return self._artists_change(request) @action(methods=['post'], detail=True, permission_classes=[IsAdminUser]) def artists_remove(self, request, pk=None): + '''Removes an artist from a song.''' return self._artists_change(request, remove=True) @action(detail=True, permission_classes=[AllowAny]) def favorites(self, request, pk=None): + '''Get a list of users who added this song to their favorites list.''' song = self.get_object() profiles = song.song_favorites.all().order_by('user__name') @@ -130,6 +133,7 @@ class SongViewSet(viewsets.ModelViewSet): detail=True, permission_classes=[IsAuthenticatedAndNotDJ]) def favorite(self, request, pk=None): + '''Add a song to the user's favorites list.''' song = self.get_object() profile = RadioProfile.objects.get(user=request.user) if song not in profile.favorites.all(): @@ -144,6 +148,7 @@ class SongViewSet(viewsets.ModelViewSet): detail=True, permission_classes=[IsAuthenticatedAndNotDJ]) def unfavorite(self, request, pk=None): + '''Remove a song from the user's favorites list.''' song = self.get_object() profile = RadioProfile.objects.get(user=request.user) if song in profile.favorites.all(): @@ -157,6 +162,7 @@ class SongViewSet(viewsets.ModelViewSet): @action(detail=True, permission_classes=[AllowAny]) def ratings(self, request, pk=None): + '''Get a list of a song's ratings.''' song = self.get_object() ratings = song.rating_set.all().order_by('-created_date') @@ -172,6 +178,7 @@ class SongViewSet(viewsets.ModelViewSet): detail=True, permission_classes=[IsAuthenticatedAndNotDJ]) def rate(self, request, pk=None): + '''Add a user's rating to a song.''' serializer = RateSongSerializer(data=request.data) if serializer.is_valid(): song = self.get_object() @@ -195,6 +202,7 @@ class SongViewSet(viewsets.ModelViewSet): detail=True, permission_classes=[IsAuthenticatedAndNotDJ]) def unrate(self, request, pk=None): + '''Remove a user's rating from a song.''' song = self.get_object() profile = RadioProfile.objects.get(user=request.user) rating = song.rating_set.filter(profile=profile)