diff --git a/muzak/settings.py b/muzak/settings.py index a074ac5..b5b1e97 100644 --- a/muzak/settings.py +++ b/muzak/settings.py @@ -111,4 +111,4 @@ USE_TZ = True STATIC_URL = 'static/' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' -SESSION_COOKIE_AGE = 60*60*24*4 \ No newline at end of file +SESSION_COOKIE_AGE = 60*60*24*4 diff --git a/playlist/models/vote.py b/playlist/models/vote.py index e927613..92af7b4 100644 --- a/playlist/models/vote.py +++ b/playlist/models/vote.py @@ -1,10 +1,11 @@ from django.db import models from django.contrib.auth.models import User +from django.core.validators import MinValueValidator, MaxValueValidator class Vote(models.Model): class Meta: unique_together = ["user", "track"] track = models.ForeignKey('Track', on_delete=models.CASCADE) user = models.ForeignKey(User, on_delete=models.CASCADE) - points = models.IntegerField() + points = models.IntegerField(validators=[MinValueValidator(1), MaxValueValidator(5)]) skipped = models.BooleanField(default=False) diff --git a/playlist/serializers/__init__.py b/playlist/serializers/__init__.py index 21a6e86..0edf028 100644 --- a/playlist/serializers/__init__.py +++ b/playlist/serializers/__init__.py @@ -3,4 +3,4 @@ from .artist import ArtistSerializer from .user import UserSerializer from .album import AlbumSerializer from .profile import ProfileSerializer, BackgroundSerializer -from .vote import VoteSerializer +from .vote import * diff --git a/playlist/serializers/vote.py b/playlist/serializers/vote.py index 713ad01..8f44005 100644 --- a/playlist/serializers/vote.py +++ b/playlist/serializers/vote.py @@ -1,5 +1,5 @@ from rest_framework import serializers -from playlist.models import Vote +from playlist.models import Vote, Track class VoteSerializer(serializers.HyperlinkedModelSerializer): class Meta: @@ -10,3 +10,19 @@ class VoteSerializer(serializers.HyperlinkedModelSerializer): 'track': {'view_name': 'api-track', 'lookup_field': 'id'}, 'user': {'view_name': 'api-user', 'lookup_field': 'id'} } + +class VoteSaveSerializer(serializers.ModelSerializer): + class Meta: + model = Vote + fields = ['track', 'points', 'skipped'] + + def create(self, validated_data): + """ + Create a new vote. Deletes a previous skipped vote if it exists. + """ + user = self.context['request'].user + track = validated_data['track'] + previous_vote = Vote.objects.filter(user=user, track=track).first() + if previous_vote and previous_vote.skipped: + previous_vote.delete() + return Vote.objects.create(**validated_data) diff --git a/playlist/urls.py b/playlist/urls.py index 82145df..5f670b8 100644 --- a/playlist/urls.py +++ b/playlist/urls.py @@ -6,15 +6,15 @@ from django.conf.urls.static import static urlpatterns = [ ### API path('api/', views.api_root, name="api-root"), - path('api/track', views.TrackListView.as_view(), name="api-tracks"), + path('api/track/', views.TrackListView.as_view(), name="api-tracks"), path('api/track/', views.TrackDetailView.as_view(), name="api-track"), path('api/artist/', views.ArtistDetailView.as_view(), name="api-artist"), path('api/artist', views.ArtistListView.as_view(), name="api-artists"), path('api/album/', views.AlbumDetailView.as_view(), name="api-album"), - path('api/album', views.AlbumListView.as_view(), name="api-albums"), - path('api/vote', views.VoteListView.as_view(), name="api-votes"), + path('api/album/', views.AlbumListView.as_view(), name="api-albums"), + path('api/vote/', views.VoteListView.as_view(), name="api-votes"), path('api/vote/', views.VoteDetailView.as_view(), name="api-vote"), - path('api/user', views.UserListView.as_view(), name="api-users"), + path('api/user/', views.UserListView.as_view(), name="api-users"), path('api/user/', views.UserDetailView.as_view(), name="api-user"), path('api/user//votes', views.VoteListView.as_view(), name="api-user-votes"), path('api/profile/', views.ProfileDetailView.as_view(), name="api-profile"), diff --git a/playlist/views/api/root.py b/playlist/views/api/root.py index 8da8d24..60c43fb 100644 --- a/playlist/views/api/root.py +++ b/playlist/views/api/root.py @@ -10,4 +10,5 @@ def api_root(request, format=None): 'artists': reverse('api-artists', request=request, format=format), 'users': reverse('api-users', request=request, format=format), 'albums': reverse('api-albums', request=request, format=format), + 'votes': reverse('api-votes', request=request, format=format), }) diff --git a/playlist/views/api/vote.py b/playlist/views/api/vote.py index 15fc955..34d94d1 100644 --- a/playlist/views/api/vote.py +++ b/playlist/views/api/vote.py @@ -1,9 +1,10 @@ +from django.db import IntegrityError from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.authentication import SessionAuthentication from rest_framework.permissions import IsAuthenticated -from playlist.models import Vote -from playlist.serializers import VoteSerializer +from playlist.models import Vote, Profile +from playlist.serializers import VoteSerializer, VoteSaveSerializer class VoteListView(APIView): authentication_classes = [SessionAuthentication] @@ -17,6 +18,26 @@ class VoteListView(APIView): serializer = VoteSerializer(votes, many=True, context={'request': request}) return Response(serializer.data) + def post(self, request): + (profile,_) = Profile.objects.get_or_create(user=request.user) + if not profile.can_vote: + return Response({'error': 'User cannot vote'}, status=403) + serializer = VoteSaveSerializer(data=request.data, context={'request': request}) + if serializer.is_valid(): + try: + serializer.save(user=request.user) + return Response(serializer.data, status=201) + except IntegrityError: + return Response({'error': 'Duplicate vote'}, status=400) + return Response(serializer.errors, status=400) + + def delete(self, request): + if request.user.is_superuser: + Vote.objects.all().delete() + return Response(status=204) + else: + return Response(status=403) + class VoteDetailView(APIView): authentication_classes = [SessionAuthentication] permission_classes = [IsAuthenticated]