Merge pull request 'data link naar backend' (#11) from mark into master

Reviewed-on: #11
This commit was merged in pull request #11.
This commit is contained in:
2025-07-15 17:36:35 +02:00
10 changed files with 155 additions and 2 deletions
+3 -1
View File
@@ -1,11 +1,13 @@
SPOTIFY_ENCODED_ID=<Spotify-API-Token>
OIDC_RP_CLIENT_ID=<OIDC-Client-ID>
OIDC_RP_CLIENT_SECRET=<OIDC-Client-Secret>
OIDC_RP_SIGN_ALGO=RS256
OIDC_OP_AUTHORIZATION_ENDPOINT=https://auth.yourwebsite.com/application/o/authorize/
OIDC_OP_TOKEN_ENDPOINT=https://auth.yourwebsite.com/application/o/token/
OIDC_OP_USER_ENDPOINT=https://auth.yourwebsite.com/application/o/userinfo/
OIDC_OP_JWKS_ENDPOINT=https://auth.yourwebsite.com/application/o/name/jwks/
AI_ENDPOINT=http://localhost:11434/api/generate
AI_MODEL=myModel
DATE_OPEN=<ISO-formatted date>
DATE_NOM_END=<ISO-formatted date>
DATE_VOTE_END=<ISO-formatted date>
DATE_VOTE_END=<ISO-formatted date>
+9 -1
View File
@@ -29,6 +29,7 @@ INSTALLED_APPS = [
'django_htmx',
'rest_framework',
'corsheaders',
'drf_spectacular',
]
MIDDLEWARE = [
@@ -97,7 +98,14 @@ REST_FRAMEWORK = {
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
]
],
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
}
SPECTACULAR_SETTINGS = {
'TITLE': 'HDcon Muzak',
'DESCRIPTION': 'Gewoon een leuk tooltje om samen met je vrienden een muzieklijst samen te stellen.',
'VERSION': '1.0.0',
}
OIDC_RP_CLIENT_ID = os.getenv('OIDC_RP_CLIENT_ID')
+35
View File
@@ -1,7 +1,42 @@
from os import extsep
from django.db.models.functions import Extract
from playlist.models import Album
from rest_framework import serializers
from drf_spectacular.utils import extend_schema_serializer, OpenApiExample
@extend_schema_serializer(
examples = [
OpenApiExample(
name='Album Example',
value = {
"url": "http://muzak.hoekveen.net/api/album/7MejfRSNnrpcLZIxkeZDqR",
"name": "Leftoverture (Expanded Edition)",
"album_type": "album",
"total_tracks": 10,
"image": "http://127.0.0.1:8001/albums/Leftoverture_Expanded_Edition.png",
"image_url": "https://i.scdn.co/image/ab67616d0000b2731be40e44db112e123e5e8b51",
"artists": [
"http://127.0.0.1:8001/api/artist/2hl0xAkS2AIRAu23TVMBG1"
]
}
),
OpenApiExample(
name = 'Multiple artist single',
value = {
"url": "http://127.0.0.1:8001/api/album/1pHD8AFu4z1CvuTPjZFOFi",
"name": "ラビリンス",
"album_type": "single",
"total_tracks": 4,
"image": None,
"image_url": "https://i.scdn.co/image/ab67616d0000b2731607d3aa3a69ca0e1ffbe26b",
"artists": [
"http://127.0.0.1:8001/api/artist/4ZX8Wr8KHHrW7radu6IwYG",
"http://127.0.0.1:8001/api/artist/4d2zOuYJHBPJTpVblHEKJb"
]
}
)
]
)
class AlbumSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Album
+21
View File
@@ -1,6 +1,27 @@
from playlist.models import Artist
from rest_framework import serializers
from drf_spectacular.utils import extend_schema_serializer, OpenApiExample
@extend_schema_serializer(
examples=[
OpenApiExample(
'Daft Punk',
value = {
"id": "4tZwfgrHOc3mvqYlEYSvVi",
"name": "Daft Punk",
"url": "http://127.0.0.1:8001/api/artist/4tZwfgrHOc3mvqYlEYSvVi"
}
),
OpenApiExample(
'コリッキー',
value = {
"id": "307y5sbPNvRpXjBcZgS25q",
"name": "コリッキー",
"url": "http://127.0.0.1:8001/api/artist/307y5sbPNvRpXjBcZgS25q"
}
)
]
)
class ArtistSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Artist
+45
View File
@@ -1,6 +1,51 @@
from playlist.models import Track
from rest_framework import serializers
from drf_spectacular.utils import extend_schema_serializer, OpenApiExample
@extend_schema_serializer(
examples = [
OpenApiExample(
'Song with banter',
value={
"url": "http://127.0.0.1:8001/api/track/0AzD1FEuvkXP1verWfaZdv",
"name": "Cbat",
"explicit": False,
"duration_ms": 171720,
"popularity": 0,
"banter": "Als je denkt dat \"Cbat\" van Hudson Mohawke de soundtrack is voor het onhandig manoeuvreren van een stelletje klunzen die elkaar constant in de weg zitten, dan heb je waarschijnlijk gelijk. Dit nummer, waarin de beats net zo chaotisch zijn als de liefdesperikelen van een tiener op een eerste date, is het perfecte liedje voor iedereen die zich ooit heeft afgevraagd hoe je een kat kunt imiteren zonder dat je daadwerkelijk een kat bent.",
"banter_done": True,
"old": False,
"from_playlist": None,
"nominated_by": "http://127.0.0.1:8001/api/user/1",
"album": "http://127.0.0.1:8001/api/album/0d99LxnQpiPLgSGDRuU9HT",
"artists": [
"http://127.0.0.1:8001/api/artist/6olWbKW2VLhFCHfOi0iEDb"
]
}
),
OpenApiExample(
'Song from old playlist',
description='While this track is from an old playlist, old is still set to False as it has been nominated again this year.',
value={
"url": "http://127.0.0.1:8001/api/track/4QGUlo1swUpXduW23JM57S",
"name": "You Bring On The Sun",
"explicit": False,
"duration_ms": 215400,
"popularity": 43,
"banter": None,
"banter_done": False,
"old": False,
"from_playlist": "HD Con 2020 A",
"nominated_by": None,
"album": "http://127.0.0.1:8001/api/album/01QglfxMtbN2EVEyGWpPyQ",
"artists": [
"http://127.0.0.1:8001/api/artist/0gcMPgunYh4rX1UOdvZKBn"
]
}
),
]
)
class TrackSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Track
+3
View File
@@ -2,10 +2,13 @@ from django.urls import path
from . import views
from django.conf import settings
from django.conf.urls.static import static
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
urlpatterns = [
### API
path('api/', views.api_root, name="api-root"),
path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
path('api/schema/swagger-ui/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
path('api/track/', views.TrackListView.as_view(), name="api-tracks"),
path('api/track/<id>', views.TrackDetailView.as_view(), name="api-track"),
path('api/artist/<id>', views.ArtistDetailView.as_view(), name="api-artist"),
+3
View File
@@ -2,14 +2,17 @@ from rest_framework.views import APIView
from rest_framework.response import Response
from playlist.models import Album
from playlist.serializers import AlbumSerializer
from drf_spectacular.utils import extend_schema
class AlbumListView(APIView):
@extend_schema(responses={200: AlbumSerializer(many=True)})
def get(self, request):
albums = Album.objects.all()
serializer = AlbumSerializer(albums, many=True, context={'request': request})
return Response(serializer.data)
class AlbumDetailView(APIView):
@extend_schema(responses={200: AlbumSerializer})
def get(self, request, id):
album = Album.objects.get(pk=id)
serializer = AlbumSerializer(album, context={'request': request})
+3
View File
@@ -3,13 +3,16 @@ from rest_framework.views import APIView
from rest_framework.response import Response
from playlist.models import Artist
from playlist.serializers import ArtistSerializer
from drf_spectacular.utils import extend_schema
@extend_schema(responses={200: ArtistSerializer(many=True)})
class ArtistListView(APIView):
def get(self, request):
artists = Artist.objects.all()
serializer = ArtistSerializer(artists, many=True, context={'request': request})
return Response(serializer.data)
@extend_schema(responses={200: ArtistSerializer})
class ArtistDetailView(APIView):
def get(self, request, id):
artist = Artist.objects.get(pk=id)
+32
View File
@@ -4,13 +4,23 @@ from playlist.models import Track, Profile
from playlist.serializers import TrackSerializer
from playlist.spotify import spt
from playlist.tasks import get_banter, get_and_dither_image
from drf_spectacular.utils import extend_schema
class TrackListView(APIView):
@extend_schema(
responses={200: TrackSerializer(many=True)},
description="Retrieve a list of all tracks."
)
def get(self, request):
tracks = Track.objects.all()
serializer = TrackSerializer(tracks, many=True, context={'request': request})
return Response(serializer.data)
@extend_schema(
request=None,
responses={201: TrackSerializer, 403: {'description': 'Forbidden'}},
description="Create a new track from a Spotify link."
)
def post(self, request):
(profile, _) = Profile.objects.get_or_create(user=request.user)
(can_nom, reason) = profile.can_nominate
@@ -20,6 +30,11 @@ class TrackListView(APIView):
serializer = TrackSerializer(track, context={'request': request})
return Response(serializer.data)
@extend_schema(
request=None,
responses={204: None, 403: {'description': 'Forbidden'}},
description="Delete all tracks. Only superusers are allowed."
)
def delete(self, request):
if request.user.is_superuser:
Track.objects.all().delete()
@@ -28,7 +43,24 @@ class TrackListView(APIView):
return Response({'error': 'Only superusers can delete all tracks'}, status=403)
class TrackDetailView(APIView):
@extend_schema(
responses={200: TrackSerializer},
description="Retrieve a track by its ID."
)
def get(self, request, id):
track = Track.objects.get(pk=id)
serializer = TrackSerializer(track, context={'request': request})
return Response(serializer.data)
@extend_schema(
request=None,
responses={204: None, 403: {'description': 'Forbidden'}},
description="Delete a single track by its ID. Only superusers are allowed."
)
def delete(self, request, id):
if request.user.is_superuser:
track = Track.objects.get(pk=id)
track.delete()
return Response(status=204)
else:
return Response({'error': 'Only superusers can delete tracks'}, status=403)
+1
View File
@@ -10,3 +10,4 @@ djangorestframework
markdown
PyYAML
Pygments
drf-spectacular