44 Commits

Author SHA1 Message Date
mark c7c808023a update quota 2025-08-03 12:01:48 +02:00
mark 898e275809 adds prod container and fixes build issues 2025-08-03 11:38:09 +02:00
mark 1aa28f978d fixes backend env 2025-08-03 11:08:38 +02:00
mark c21c98f6c4 nu met de juiste files toegevoegd 2025-08-03 10:50:49 +02:00
mark 54883d34c0 quota update. endpoint cleanup 2025-08-03 10:12:12 +02:00
mark 0cddc2dfb4 werkt het dan nu? 2025-08-02 19:04:35 +02:00
mark b1deaee4e6 fixes auth? 2025-08-02 15:35:18 +02:00
mark 78dc7be703 fixes?? nominations?? 2025-08-01 21:17:12 +02:00
mark 79adf3d3f5 remove auth status if not logged in 2025-08-01 18:32:48 +02:00
guus da288f20cd mv login page to root page 2025-07-31 23:30:14 +02:00
guus 0812a6b2cb styling fix nominaties 2025-07-30 22:50:30 +02:00
guus 507a65cfde changed styling nominaties 2025-07-30 22:48:02 +02:00
guus 90b523095a rm package.json in root 2025-07-28 20:24:10 +02:00
guus e1f50955f1 Merge pull request 'Data Access Layer' (#13) from add-data-layer into master
Reviewed-on: #13
Reviewed-by: guus <guus@noreply.%(DOMAIN)s>
2025-07-28 20:11:07 +02:00
mark c3a7a2d204 adds data access, login flow, navigation menu.
And some more stuff. :)
2025-07-27 10:26:40 +02:00
mark 31c6fd3265 adds track loading example 2025-07-19 19:53:24 +02:00
mark 7c92b33f91 adds type generation 2025-07-19 18:37:41 +02:00
mark ef0f82d516 first version of data fetcher 2025-07-19 10:50:41 +02:00
mark 041333d1da Merge pull request 'data link naar backend' (#11) from mark into master
Reviewed-on: #11
2025-07-15 17:36:35 +02:00
mark 6f867415f8 adds new config settings to example env 2025-07-15 17:36:19 +02:00
mark 43c6d7168b openapi defs for album and artist 2025-07-15 17:36:19 +02:00
mark 7a282e23f5 openapi definitions for track 2025-07-15 17:36:19 +02:00
mark 902243b969 adds openapi definitions to backend 2025-07-15 17:36:19 +02:00
guus 6f578eca6c voting page mock-up 2025-07-14 17:48:36 +02:00
guus 6b75e787c4 add routing links 2025-07-08 21:10:00 +02:00
guus 3c02ec930b update user grid cards 2025-07-08 17:26:01 +02:00
guus 499a35bf50 update lobby page design 2025-07-07 22:17:51 +02:00
guus a7ac9951a6 fix text outline for headers 2025-07-04 12:54:02 +02:00
guus 25d9f8ab7e add login page 2025-07-04 00:05:21 +02:00
mark 9263d88cee modules op de juiste plek pls make it stop aaaaaaaaaaaaaaaa 2025-07-02 20:49:12 +02:00
guus 389b6db432 add prettier tailwind 2025-07-02 19:14:47 +02:00
guus 142d2986b6 added fonts 2025-07-02 11:28:41 +02:00
guus 4134e3a234 eerste opzet nominatie pagina 2025-07-01 17:34:37 +02:00
guus 3185ca3c43 basic structure for pages 2025-07-01 14:48:56 +02:00
mark ca378790f1 first ugly version of authenticated backend requests.
needs some refactoring, but it works?
2025-06-30 13:22:27 +02:00
mark d9050b35ba api authentication yea 2025-06-29 01:38:21 +02:00
mark bfa1c7879b Authentication?
Voor een gegeven definitie van...
2025-06-25 16:38:22 +02:00
mark e7ca11d1bc playpause 2025-06-22 23:56:30 +02:00
mark dba9b58a6a rotating bg
with some custom classes.
2025-06-22 20:08:55 +02:00
mark 2a64d05b71 adds conical bg and tailwind.
not yet animated, but should be doable with @property.
2025-06-22 18:34:29 +02:00
mark ce5ec2a763 empty tag for wrapping component 2025-06-20 14:51:49 +02:00
mark 3576d85ac9 updated authentication classes 2025-06-20 14:37:34 +02:00
mark 970eb9e19e updated gitignore 2025-06-20 14:37:10 +02:00
mark dfc242084e first backend frontend split
with docker compose file
nextjs something something
also includes migrations
2025-06-20 14:06:00 +02:00
169 changed files with 3958 additions and 222 deletions
+3 -1
View File
@@ -3,6 +3,8 @@
db.sqlite3
*.pyc
*/__pycache__/
*/migrations/
albums/
wallpapers/
frontend/node_modules/
frontend/.next/
frontend/src/data/types.ts
View File
+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>
View File
+23
View File
@@ -0,0 +1,23 @@
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from mozilla_django_oidc.auth import OIDCAuthenticationBackend
from django.contrib.auth.models import User
class OIDCBearerTokenAuthentication(BaseAuthentication):
def authenticate(self, request):
auth = request.META.get('HTTP_AUTHORIZATION', '')
if not auth.startswith('Bearer '):
return None
token = auth.split(' ')[1]
backend = OIDCAuthenticationBackend()
try:
claims = backend.verify_token(token)
except Exception as e:
raise AuthenticationFailed(f'Invalid token: {e}')
user = backend.filter_users_by_claims(claims).first()
if not user:
raise AuthenticationFailed(f'Unknown user: {claims}')
return (user, None)
@@ -13,6 +13,7 @@ SECRET_KEY = 'oefjei1lwe918Alkfwuf3ionwu-@kt)6m1e)ah$&^_i9y!qffhm-a$#m6+++'
DEBUG = True
ALLOWED_HOSTS = ['*']
CORS_ALLOW_ALL_ORIGINS = True
CURRENT_HOST = 'http://127.0.0.1:8000' # Needs to be set for spotify callback to work.
@@ -26,12 +27,15 @@ INSTALLED_APPS = [
'django.contrib.messages',
'django.contrib.staticfiles',
'django_htmx',
'rest_framework'
'rest_framework',
'corsheaders',
'drf_spectacular',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
"corsheaders.middleware.CorsMiddleware",
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
@@ -88,12 +92,20 @@ AUTHENTICATION_BACKENDS = (
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'muzak.auth.OIDCBearerTokenAuthentication',
'mozilla_django_oidc.contrib.drf.OIDCAuthentication',
'rest_framework.authentication.SessionAuthentication',
],
'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')
@@ -101,6 +113,8 @@ OIDC_RP_CLIENT_SECRET = os.getenv('OIDC_RP_CLIENT_SECRET')
OIDC_OP_AUTHORIZATION_ENDPOINT = os.getenv('OIDC_OP_AUTHORIZATION_ENDPOINT')
OIDC_OP_TOKEN_ENDPOINT = os.getenv('OIDC_OP_TOKEN_ENDPOINT')
OIDC_OP_USER_ENDPOINT = os.getenv('OIDC_OP_USER_ENDPOINT')
OIDC_RP_SIGN_ALGO = os.getenv('OIDC_RP_SIGN_ALGO')
OIDC_OP_JWKS_ENDPOINT = os.getenv('OIDC_OP_JWKS_ENDPOINT')
LOGIN_REDIRECT_URL = "/"
LOGOUT_REDIRECT_URL = "/"
LOGIN_URL = "/"
@@ -0,0 +1,48 @@
# Generated by Django 5.0.1 on 2024-02-04 22:50
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Artist',
fields=[
('id', models.CharField(max_length=128, primary_key=True, serialize=False)),
('name', models.CharField(max_length=255)),
('popularity', models.SmallIntegerField(blank=True, null=True)),
('genres', models.JSONField()),
],
),
migrations.CreateModel(
name='Album',
fields=[
('id', models.CharField(max_length=128, primary_key=True, serialize=False)),
('name', models.CharField(max_length=255)),
('album_type', models.CharField(max_length=255)),
('total_tracks', models.PositiveSmallIntegerField(null=True)),
('image', models.ImageField(upload_to='albums/')),
('artists', models.ManyToManyField(to='playlist.artist')),
],
),
migrations.CreateModel(
name='Track',
fields=[
('id', models.CharField(max_length=128, primary_key=True, serialize=False)),
('title', models.CharField(max_length=255)),
('explicit', models.BooleanField(default=False)),
('duration_ms', models.PositiveIntegerField(null=True)),
('popularity', models.PositiveIntegerField(null=True)),
('banter', models.TextField()),
('album', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='playlist.album')),
('artists', models.ManyToManyField(to='playlist.artist')),
],
),
]
@@ -0,0 +1,18 @@
# Generated by Django 5.0.1 on 2024-02-05 21:06
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('playlist', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='track',
name='old',
field=models.BooleanField(default=False),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 5.0.1 on 2024-02-05 21:11
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('playlist', '0002_track_old'),
]
operations = [
migrations.RenameField(
model_name='track',
old_name='title',
new_name='name',
),
]
@@ -0,0 +1,23 @@
# Generated by Django 5.0.1 on 2024-02-05 22:11
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('playlist', '0003_rename_title_track_name'),
]
operations = [
migrations.AlterField(
model_name='artist',
name='genres',
field=models.JSONField(null=True),
),
migrations.AlterField(
model_name='track',
name='banter',
field=models.TextField(blank=True),
),
]
+28
View File
@@ -0,0 +1,28 @@
# Generated by Django 5.0.1 on 2024-02-07 12:26
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('playlist', '0004_alter_artist_genres_alter_track_banter'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Vote',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('points', models.IntegerField()),
('track', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='playlist.track')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'unique_together': {('user', 'track')},
},
),
]
@@ -0,0 +1,18 @@
# Generated by Django 5.0.1 on 2024-02-07 19:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('playlist', '0005_vote'),
]
operations = [
migrations.AddField(
model_name='track',
name='banter_done',
field=models.BooleanField(default=False),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 5.0.1 on 2024-02-12 13:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('playlist', '0006_track_banter_done'),
]
operations = [
migrations.AddField(
model_name='album',
name='image_url',
field=models.URLField(null=True),
),
]
@@ -0,0 +1,32 @@
# Generated by Django 5.0.1 on 2024-02-21 13:26
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('playlist', '0007_album_image_url'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Background',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('image', models.ImageField(upload_to='wallpapers/')),
('repeat', models.BooleanField(default=False)),
],
),
migrations.CreateModel(
name='Profile',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('background', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='playlist.background')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]
@@ -0,0 +1,19 @@
# Generated by Django 5.0.1 on 2024-02-21 13:31
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('playlist', '0008_background_profile'),
]
operations = [
migrations.AddField(
model_name='background',
name='name',
field=models.CharField(default='Wallpaper', max_length=128),
preserve_default=False,
),
]
@@ -0,0 +1,18 @@
# Generated by Django 5.0.1 on 2024-02-21 14:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('playlist', '0009_background_name'),
]
operations = [
migrations.AddField(
model_name='background',
name='color',
field=models.CharField(blank=True, max_length=32),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 5.0.1 on 2024-02-21 14:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('playlist', '0010_background_color'),
]
operations = [
migrations.AlterField(
model_name='background',
name='image',
field=models.ImageField(null=True, upload_to='wallpapers/'),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 5.0.1 on 2024-02-21 14:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('playlist', '0011_alter_background_image'),
]
operations = [
migrations.AlterField(
model_name='background',
name='image',
field=models.ImageField(blank=True, null=True, upload_to='wallpapers/'),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 5.0.1 on 2024-02-26 18:38
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('playlist', '0012_alter_background_image'),
]
operations = [
migrations.AddField(
model_name='profile',
name='last_voted',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='playlist.track'),
),
]
@@ -0,0 +1,23 @@
# Generated by Django 5.0.1 on 2024-02-26 20:46
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('playlist', '0013_profile_last_voted'),
]
operations = [
migrations.AddField(
model_name='profile',
name='quota',
field=models.FloatField(default=10.0),
),
migrations.AddField(
model_name='profile',
name='quota_last_updated',
field=models.DateTimeField(auto_now=True),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 5.0.1 on 2024-02-26 22:17
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('playlist', '0014_profile_quota_profile_quota_last_updated'),
]
operations = [
migrations.AlterField(
model_name='profile',
name='quota_last_updated',
field=models.DateTimeField(default=django.utils.timezone.now),
),
]
@@ -0,0 +1,31 @@
# Generated by Django 5.0.1 on 2024-03-08 23:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('playlist', '0015_alter_profile_quota_last_updated'),
]
operations = [
migrations.AddField(
model_name='profile',
name='spt_access_token',
field=models.CharField(default='', max_length=128),
),
migrations.AddField(
model_name='profile',
name='spt_refresh_token',
field=models.CharField(default='', max_length=128),
),
migrations.CreateModel(
name='Playlist',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255)),
('tracks', models.ManyToManyField(to='playlist.track')),
],
),
]
@@ -0,0 +1,18 @@
# Generated by Django 5.0.1 on 2024-03-08 23:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('playlist', '0016_profile_spt_access_token_profile_spt_refresh_token_and_more'),
]
operations = [
migrations.AddField(
model_name='profile',
name='spt_expires',
field=models.DateTimeField(default=None, null=True),
),
]
@@ -0,0 +1,23 @@
# Generated by Django 5.0.1 on 2024-03-13 08:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('playlist', '0017_profile_spt_expires'),
]
operations = [
migrations.AlterField(
model_name='profile',
name='spt_access_token',
field=models.CharField(default='', max_length=128, null=True),
),
migrations.AlterField(
model_name='profile',
name='spt_refresh_token',
field=models.CharField(default='', max_length=128, null=True),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 5.0.4 on 2024-07-20 06:58
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('playlist', '0018_alter_profile_spt_access_token_and_more'),
]
operations = [
migrations.AddField(
model_name='vote',
name='skipped',
field=models.BooleanField(default=False),
),
]
@@ -0,0 +1,31 @@
# Generated by Django 5.0.4 on 2024-07-21 07:59
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('playlist', '0019_vote_skipped'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='track',
name='nominated_by',
field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='profile',
name='spt_access_token',
field=models.CharField(blank=True, default='', max_length=256, null=True),
),
migrations.AlterField(
model_name='profile',
name='spt_refresh_token',
field=models.CharField(blank=True, default='', max_length=256, null=True),
),
]
@@ -0,0 +1,46 @@
# Generated by Django 5.0.4 on 2024-07-22 20:32
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('playlist', '0020_track_nominated_by_alter_profile_spt_access_token_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='track',
name='from_playlist',
field=models.CharField(blank=True, default=None, max_length=255, null=True),
),
migrations.AlterField(
model_name='track',
name='album',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='playlist.album'),
),
migrations.AlterField(
model_name='track',
name='banter',
field=models.TextField(blank=True, null=True),
),
migrations.AlterField(
model_name='track',
name='duration_ms',
field=models.PositiveIntegerField(blank=True, null=True),
),
migrations.AlterField(
model_name='track',
name='nominated_by',
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='track',
name='popularity',
field=models.PositiveIntegerField(blank=True, null=True),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 5.0.4 on 2024-07-23 11:29
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('playlist', '0021_track_from_playlist_alter_track_album_and_more'),
]
operations = [
migrations.AddField(
model_name='background',
name='cover',
field=models.BooleanField(default=False),
),
]
@@ -104,6 +104,11 @@ class Track(models.Model):
artists = list(map(lambda a:a.name, self.artists.all()))
return " & ".join(artists)
@property
def album_cover(self):
if self.album:
return self.album.image_url
@property
def name_sanitized(self):
return re.match("[^\-\(]*\w", self.name)[0]
+47
View File
@@ -0,0 +1,47 @@
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
fields = '__all__'
extra_kwargs = {
'url': {'view_name': 'api-album', 'lookup_field': 'id'},
'artists': {'view_name': 'api-artist', 'lookup_field': 'id'},
}
+31
View File
@@ -0,0 +1,31 @@
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
fields = ['id', 'name', 'url']
extra_kwargs = {
'url': {'view_name': 'api-artist', 'lookup_field': 'id'},
}
+61
View File
@@ -0,0 +1,61 @@
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):
artist = serializers.CharField()
album_cover = serializers.CharField()
class Meta:
model = Track
fields = '__all__'
extra_kwargs = {
'url': {'view_name': 'api-track', 'lookup_field': 'id'},
'artists': {'view_name': 'api-artist', 'lookup_field': 'id'},
'album': {'view_name': 'api-album', 'lookup_field': 'id'},
'nominated_by': {'view_name': 'api-user', 'lookup_field': 'id'},
}
+7
View File
@@ -0,0 +1,7 @@
from django.contrib.auth.models import User
from rest_framework import serializers
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ['id', 'username', 'email']

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Before

Width:  |  Height:  |  Size: 556 B

After

Width:  |  Height:  |  Size: 556 B

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before

Width:  |  Height:  |  Size: 462 B

After

Width:  |  Height:  |  Size: 462 B

Before

Width:  |  Height:  |  Size: 419 B

After

Width:  |  Height:  |  Size: 419 B

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Before

Width:  |  Height:  |  Size: 451 B

After

Width:  |  Height:  |  Size: 451 B

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before

Width:  |  Height:  |  Size: 498 B

After

Width:  |  Height:  |  Size: 498 B

Before

Width:  |  Height:  |  Size: 749 B

After

Width:  |  Height:  |  Size: 749 B

Before

Width:  |  Height:  |  Size: 411 B

After

Width:  |  Height:  |  Size: 411 B

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Before

Width:  |  Height:  |  Size: 394 B

After

Width:  |  Height:  |  Size: 394 B

Before

Width:  |  Height:  |  Size: 378 B

After

Width:  |  Height:  |  Size: 378 B

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before

Width:  |  Height:  |  Size: 524 B

After

Width:  |  Height:  |  Size: 524 B

Before

Width:  |  Height:  |  Size: 683 B

After

Width:  |  Height:  |  Size: 683 B

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Before

Width:  |  Height:  |  Size: 239 B

After

Width:  |  Height:  |  Size: 239 B

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before

Width:  |  Height:  |  Size: 393 B

After

Width:  |  Height:  |  Size: 393 B

Before

Width:  |  Height:  |  Size: 458 B

After

Width:  |  Height:  |  Size: 458 B

Some files were not shown because too many files have changed in this diff Show More