Initial commit that merge both the front end and the API in the same repository
This commit is contained in:
commit
76f98b74dd
285 changed files with 51318 additions and 0 deletions
0
api/funkwhale_api/favorites/__init__.py
Normal file
0
api/funkwhale_api/favorites/__init__.py
Normal file
33
api/funkwhale_api/favorites/migrations/0001_initial.py
Normal file
33
api/funkwhale_api/favorites/migrations/0001_initial.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('music', '0003_auto_20151222_2233'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='TrackFavorite',
|
||||
fields=[
|
||||
('id', models.AutoField(serialize=False, auto_created=True, verbose_name='ID', primary_key=True)),
|
||||
('creation_date', models.DateTimeField(default=django.utils.timezone.now)),
|
||||
('track', models.ForeignKey(related_name='track_favorites', to='music.Track')),
|
||||
('user', models.ForeignKey(related_name='track_favorites', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'ordering': ('-creation_date',),
|
||||
},
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='trackfavorite',
|
||||
unique_together=set([('track', 'user')]),
|
||||
),
|
||||
]
|
||||
0
api/funkwhale_api/favorites/migrations/__init__.py
Normal file
0
api/funkwhale_api/favorites/migrations/__init__.py
Normal file
18
api/funkwhale_api/favorites/models.py
Normal file
18
api/funkwhale_api/favorites/models.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
from django.db import models
|
||||
from django.utils import timezone
|
||||
|
||||
from funkwhale_api.music.models import Track
|
||||
|
||||
class TrackFavorite(models.Model):
|
||||
creation_date = models.DateTimeField(default=timezone.now)
|
||||
user = models.ForeignKey('users.User', related_name='track_favorites')
|
||||
track = models.ForeignKey(Track, related_name='track_favorites')
|
||||
|
||||
class Meta:
|
||||
unique_together = ('track', 'user')
|
||||
ordering = ('-creation_date',)
|
||||
|
||||
@classmethod
|
||||
def add(cls, track, user):
|
||||
favorite, created = cls.objects.get_or_create(user=user, track=track)
|
||||
return favorite
|
||||
12
api/funkwhale_api/favorites/serializers.py
Normal file
12
api/funkwhale_api/favorites/serializers.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
from rest_framework import serializers
|
||||
|
||||
from funkwhale_api.music.serializers import TrackSerializerNested
|
||||
|
||||
from . import models
|
||||
|
||||
|
||||
class UserTrackFavoriteSerializer(serializers.ModelSerializer):
|
||||
# track = TrackSerializerNested(read_only=True)
|
||||
class Meta:
|
||||
model = models.TrackFavorite
|
||||
fields = ('id', 'track', 'creation_date')
|
||||
0
api/funkwhale_api/favorites/tests/__init__.py
Normal file
0
api/funkwhale_api/favorites/tests/__init__.py
Normal file
113
api/funkwhale_api/favorites/tests/test_favorites.py
Normal file
113
api/funkwhale_api/favorites/tests/test_favorites.py
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
import json
|
||||
from test_plus.test import TestCase
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from funkwhale_api.music.models import Track, Artist
|
||||
from funkwhale_api.favorites.models import TrackFavorite
|
||||
from funkwhale_api.users.models import User
|
||||
|
||||
class TestFavorites(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.artist = Artist.objects.create(name='test')
|
||||
self.track = Track.objects.create(title='test', artist=self.artist)
|
||||
self.user = User.objects.create_user(username='test', email='test@test.com', password='test')
|
||||
|
||||
def test_user_can_add_favorite(self):
|
||||
TrackFavorite.add(self.track, self.user)
|
||||
|
||||
favorite = TrackFavorite.objects.latest('id')
|
||||
self.assertEqual(favorite.track, self.track)
|
||||
self.assertEqual(favorite.user, self.user)
|
||||
|
||||
def test_user_can_get_his_favorites(self):
|
||||
favorite = TrackFavorite.add(self.track, self.user)
|
||||
|
||||
url = reverse('api:favorites:tracks-list')
|
||||
self.client.login(username=self.user.username, password='test')
|
||||
|
||||
response = self.client.get(url)
|
||||
|
||||
expected = [
|
||||
{
|
||||
'track': self.track.pk,
|
||||
'id': favorite.id,
|
||||
'creation_date': favorite.creation_date.isoformat().replace('+00:00', 'Z'),
|
||||
}
|
||||
]
|
||||
parsed_json = json.loads(response.content.decode('utf-8'))
|
||||
|
||||
self.assertEqual(expected, parsed_json['results'])
|
||||
|
||||
def test_user_can_add_favorite_via_api(self):
|
||||
url = reverse('api:favorites:tracks-list')
|
||||
self.client.login(username=self.user.username, password='test')
|
||||
response = self.client.post(url, {'track': self.track.pk})
|
||||
|
||||
favorite = TrackFavorite.objects.latest('id')
|
||||
expected = {
|
||||
'track': self.track.pk,
|
||||
'id': favorite.id,
|
||||
'creation_date': favorite.creation_date.isoformat().replace('+00:00', 'Z'),
|
||||
}
|
||||
parsed_json = json.loads(response.content.decode('utf-8'))
|
||||
|
||||
self.assertEqual(expected, parsed_json)
|
||||
self.assertEqual(favorite.track, self.track)
|
||||
self.assertEqual(favorite.user, self.user)
|
||||
|
||||
def test_user_can_remove_favorite_via_api(self):
|
||||
favorite = TrackFavorite.add(self.track, self.user)
|
||||
|
||||
url = reverse('api:favorites:tracks-detail', kwargs={'pk': favorite.pk})
|
||||
self.client.login(username=self.user.username, password='test')
|
||||
response = self.client.delete(url, {'track': self.track.pk})
|
||||
self.assertEqual(response.status_code, 204)
|
||||
self.assertEqual(TrackFavorite.objects.count(), 0)
|
||||
|
||||
def test_user_can_remove_favorite_via_api_using_track_id(self):
|
||||
favorite = TrackFavorite.add(self.track, self.user)
|
||||
|
||||
url = reverse('api:favorites:tracks-remove')
|
||||
self.client.login(username=self.user.username, password='test')
|
||||
response = self.client.delete(
|
||||
url, json.dumps({'track': self.track.pk}),
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 204)
|
||||
self.assertEqual(TrackFavorite.objects.count(), 0)
|
||||
|
||||
from funkwhale_api.users.models import User
|
||||
|
||||
def test_can_restrict_api_views_to_authenticated_users(self):
|
||||
urls = [
|
||||
('api:favorites:tracks-list', 'get'),
|
||||
]
|
||||
|
||||
for route_name, method in urls:
|
||||
url = self.reverse(route_name)
|
||||
with self.settings(API_AUTHENTICATION_REQUIRED=True):
|
||||
response = getattr(self.client, method)(url)
|
||||
self.assertEqual(response.status_code, 401)
|
||||
|
||||
self.client.login(username=self.user.username, password='test')
|
||||
|
||||
for route_name, method in urls:
|
||||
url = self.reverse(route_name)
|
||||
with self.settings(API_AUTHENTICATION_REQUIRED=False):
|
||||
response = getattr(self.client, method)(url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_can_filter_tracks_by_favorites(self):
|
||||
favorite = TrackFavorite.add(self.track, self.user)
|
||||
|
||||
url = reverse('api:tracks-list')
|
||||
self.client.login(username=self.user.username, password='test')
|
||||
|
||||
response = self.client.get(url, data={'favorites': True})
|
||||
|
||||
parsed_json = json.loads(response.content.decode('utf-8'))
|
||||
self.assertEqual(parsed_json['count'], 1)
|
||||
self.assertEqual(parsed_json['results'][0]['id'], self.track.id)
|
||||
8
api/funkwhale_api/favorites/urls.py
Normal file
8
api/funkwhale_api/favorites/urls.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
from django.conf.urls import include, url
|
||||
from . import views
|
||||
|
||||
from rest_framework import routers
|
||||
router = routers.SimpleRouter()
|
||||
router.register(r'tracks', views.TrackFavoriteViewSet, 'tracks')
|
||||
|
||||
urlpatterns = router.urls
|
||||
54
api/funkwhale_api/favorites/views.py
Normal file
54
api/funkwhale_api/favorites/views.py
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
from rest_framework import generics, mixins, viewsets
|
||||
from rest_framework import status
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import pagination
|
||||
from rest_framework.decorators import list_route
|
||||
|
||||
from funkwhale_api.music.models import Track
|
||||
from funkwhale_api.common.permissions import ConditionalAuthentication
|
||||
|
||||
from . import models
|
||||
from . import serializers
|
||||
|
||||
|
||||
class CustomLimitPagination(pagination.PageNumberPagination):
|
||||
page_size = 100
|
||||
page_size_query_param = 'page_size'
|
||||
max_page_size = 100
|
||||
|
||||
|
||||
class TrackFavoriteViewSet(mixins.CreateModelMixin,
|
||||
mixins.DestroyModelMixin,
|
||||
mixins.ListModelMixin,
|
||||
viewsets.GenericViewSet):
|
||||
|
||||
serializer_class = serializers.UserTrackFavoriteSerializer
|
||||
queryset = (models.TrackFavorite.objects.all())
|
||||
permission_classes = [ConditionalAuthentication]
|
||||
pagination_class = CustomLimitPagination
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
instance = self.perform_create(serializer)
|
||||
serializer = self.get_serializer(instance=instance)
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
|
||||
|
||||
def get_queryset(self):
|
||||
return self.queryset.filter(user=self.request.user)
|
||||
|
||||
def perform_create(self, serializer):
|
||||
track = Track.objects.get(pk=serializer.data['track'])
|
||||
favorite = models.TrackFavorite.add(track=track, user=self.request.user)
|
||||
return favorite
|
||||
|
||||
@list_route(methods=['delete'])
|
||||
def remove(self, request, *args, **kwargs):
|
||||
try:
|
||||
pk = int(request.data['track'])
|
||||
favorite = request.user.track_favorites.get(track__pk=pk)
|
||||
except (AttributeError, ValueError, models.TrackFavorite.DoesNotExist):
|
||||
return Response({}, status=400)
|
||||
favorite.delete()
|
||||
return Response([], status=status.HTTP_204_NO_CONTENT)
|
||||
Loading…
Add table
Add a link
Reference in a new issue