From 8b2bbfd4d3bac7bedf4e67b53fffcca51931d3ab Mon Sep 17 00:00:00 2001 From: Mark Hoekveen Date: Tue, 20 May 2025 19:08:07 +0200 Subject: [PATCH] initial API views --- playlist/serializers/__init__.py | 2 ++ playlist/serializers/artist.py | 10 ++++++++ playlist/serializers/track.py | 11 +++++++++ playlist/urls.py | 5 ++++ playlist/views/__init__.py | 4 +++ playlist/views/api/artist.py | 22 +++++++++++++++++ playlist/views/api/root.py | 11 +++++++++ playlist/views/api/track.py | 34 ++++++++++++++++++++++++++ playlist/{views.py => views/static.py} | 18 +++++++------- 9 files changed, 108 insertions(+), 9 deletions(-) create mode 100644 playlist/serializers/__init__.py create mode 100644 playlist/serializers/artist.py create mode 100644 playlist/serializers/track.py create mode 100644 playlist/views/__init__.py create mode 100644 playlist/views/api/artist.py create mode 100644 playlist/views/api/root.py create mode 100644 playlist/views/api/track.py rename playlist/{views.py => views/static.py} (97%) diff --git a/playlist/serializers/__init__.py b/playlist/serializers/__init__.py new file mode 100644 index 0000000..74134e8 --- /dev/null +++ b/playlist/serializers/__init__.py @@ -0,0 +1,2 @@ +from .track import TrackSerializer +from .artist import ArtistSerializer diff --git a/playlist/serializers/artist.py b/playlist/serializers/artist.py new file mode 100644 index 0000000..6742b58 --- /dev/null +++ b/playlist/serializers/artist.py @@ -0,0 +1,10 @@ +from playlist.models import Artist +from rest_framework import serializers + +class ArtistSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = Artist + fields = ['id', 'name', 'url'] + extra_kwargs = { + 'url': {'view_name': 'api-artist', 'lookup_field': 'id'}, + } diff --git a/playlist/serializers/track.py b/playlist/serializers/track.py new file mode 100644 index 0000000..a659f62 --- /dev/null +++ b/playlist/serializers/track.py @@ -0,0 +1,11 @@ +from playlist.models import Track +from rest_framework import serializers + +class TrackSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = Track + fields = ['url', 'id', 'name', 'artists', 'nominated_by', 'explicit', 'duration_ms', 'popularity', 'banter', 'banter_done', 'old'] + extra_kwargs = { + 'url': {'view_name': 'api-track', 'lookup_field': 'id'}, + 'artists': {'view_name': 'api-artist', 'lookup_field': 'id'} + } diff --git a/playlist/urls.py b/playlist/urls.py index 395f796..a6f6405 100644 --- a/playlist/urls.py +++ b/playlist/urls.py @@ -4,6 +4,11 @@ from django.conf import settings from django.conf.urls.static import static urlpatterns = [ + path('api/', views.api_root, name="api-root"), + 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('track/', views.NominateView.as_view(), name="nominate"), path('vote/', views.VoteView.as_view(), name="vote"), path('vote/undo', views.UndoView.as_view(), name="undo"), diff --git a/playlist/views/__init__.py b/playlist/views/__init__.py new file mode 100644 index 0000000..7a37020 --- /dev/null +++ b/playlist/views/__init__.py @@ -0,0 +1,4 @@ +from .static import * +from .api.track import * +from .api.artist import * +from .api.root import * diff --git a/playlist/views/api/artist.py b/playlist/views/api/artist.py new file mode 100644 index 0000000..75d084d --- /dev/null +++ b/playlist/views/api/artist.py @@ -0,0 +1,22 @@ +from itertools import permutations +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 Artist +from playlist.serializers import ArtistSerializer + +class ArtistListView(APIView): + authentication_classes = [SessionAuthentication] + permission_classes = [IsAuthenticated] + + def get(self, request): + artists = Artist.objects.all() + serializer = ArtistSerializer(artists, many=True, context={'request': request}) + return Response(serializer.data) + +class ArtistDetailView(APIView): + def get(self, request, id): + artist = Artist.objects.get(pk=id) + serializer = ArtistSerializer(artist, context={'request': request}) + return Response(serializer.data) diff --git a/playlist/views/api/root.py b/playlist/views/api/root.py new file mode 100644 index 0000000..b247a6f --- /dev/null +++ b/playlist/views/api/root.py @@ -0,0 +1,11 @@ +from rest_framework.decorators import api_view +from rest_framework.response import Response +from rest_framework.reverse import reverse + + +@api_view(['GET']) +def api_root(request, format=None): + return Response({ + 'tracks': reverse('api-tracks', request=request, format=format), + 'artists': reverse('api-artists', request=request, format=format) + }) diff --git a/playlist/views/api/track.py b/playlist/views/api/track.py new file mode 100644 index 0000000..497eebd --- /dev/null +++ b/playlist/views/api/track.py @@ -0,0 +1,34 @@ +from rest_framework import viewsets +from rest_framework.views import APIView +from rest_framework.response import Response +from playlist.models import Track +from playlist.serializers import TrackSerializer + +class TrackViewSet(viewsets.ModelViewSet): + queryset = Track.objects.all() + serializer_class = TrackSerializer + +class TrackListView(APIView): + def get(self, request): + tracks = Track.objects.all() + serializer = TrackSerializer(tracks, many=True, context={'request': request}) + return Response(serializer.data) + +class TrackDetailView(APIView): + def get(self, request, id): + track = Track.objects.get(pk=id) + serializer = TrackSerializer(track, context={'request': request}) + return Response(serializer.data) + def put(self, request, id): + track = Track.objects.get(pk=id) + serializer = TrackSerializer(track, data=request.data, context={'request': request}) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data) + return Response(serializer.errors, status=400) + def post(self, request): + serializer = TrackSerializer(data=request.data, context={'request': request}) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data) + return Response(serializer.errors, status=400) diff --git a/playlist/views.py b/playlist/views/static.py similarity index 97% rename from playlist/views.py rename to playlist/views/static.py index 4d48705..1afdcdc 100644 --- a/playlist/views.py +++ b/playlist/views/static.py @@ -10,10 +10,10 @@ import os from datetime import UTC, datetime from collections import OrderedDict from dotenv import load_dotenv -from .models import Track, Artist, Album, Vote, Background, Profile, Playlist -from .utils import track_from_json, get_unvoted, get_unvoted_count -from .spotify import spt -from .tasks import get_banter, get_and_dither_image +from ..models import Track, Artist, Album, Vote, Background, Profile, Playlist +from ..utils import track_from_json, get_unvoted, get_unvoted_count +from ..spotify import spt +from ..tasks import get_banter, get_and_dither_image load_dotenv() @@ -90,7 +90,7 @@ class NominateView(LoginRequiredMixin, View): get_banter.delay(track.id) return TemplateResponse(request, "Nominate.html", { "message_type": "warning", - "message": f"Hey een klassieker, die stond al in {track.from_playlist}. Geen probleem, we proberen hem nog een keer dit jaar!", + "message": f"Hey een klassieker, die stond al in {track.from_playlist}. Geen probleem, we proberen hem nog een keer dit jaar!", "message_title": str(track) }) return TemplateResponse(request, "Nominate.html", { @@ -119,7 +119,7 @@ class NominateView(LoginRequiredMixin, View): get_banter.delay(track.id) profile.quota -= 1 profile.save() - + return TemplateResponse(request, "Nominate.html", {"message_type": "nominated", "message_title": str(track), "message": "Genomineerd!"}) class SettingsView(LoginRequiredMixin, View): @@ -153,7 +153,7 @@ class SettingsView(LoginRequiredMixin, View): pass finally: return self.get(request) - + class UndoView(LoginRequiredMixin, View): def get(self, request): return HttpResponseRedirect('/vote/') @@ -208,7 +208,7 @@ class OverView(LoginRequiredMixin, View): artists[artist.name][vote.track.album.name] = [] artists[artist.name][vote.track.album.name] += [vote.track] artists_ordered = OrderedDict(sorted(artists.items())) - + context = { "artists": artists_ordered } @@ -293,7 +293,7 @@ class PlaylistImportView(LoginRequiredMixin, View): print(e) return self.get(request) - + class StroopwafelView(View): def get(self, request): nominated_count = Track.objects.all().count()