From 13f36beec3f6b210a9bdc0f09042a13d081160d9 Mon Sep 17 00:00:00 2001 From: Eliot Berriot Date: Wed, 24 Jul 2019 10:24:30 +0200 Subject: [PATCH] See #432: added admin API endpoints to retrieve and delete tags --- api/funkwhale_api/manage/filters.py | 9 ++++++ api/funkwhale_api/manage/serializers.py | 28 ++++++++++++++++ api/funkwhale_api/manage/urls.py | 1 + api/funkwhale_api/manage/views.py | 43 ++++++++++++++++++++++++- api/tests/manage/test_serializers.py | 19 +++++++++++ api/tests/manage/test_views.py | 28 ++++++++++++++++ 6 files changed, 127 insertions(+), 1 deletion(-) diff --git a/api/funkwhale_api/manage/filters.py b/api/funkwhale_api/manage/filters.py index de12ab1ab..c8b8e60a5 100644 --- a/api/funkwhale_api/manage/filters.py +++ b/api/funkwhale_api/manage/filters.py @@ -13,6 +13,7 @@ from funkwhale_api.federation import utils as federation_utils from funkwhale_api.moderation import models as moderation_models from funkwhale_api.music import models as music_models from funkwhale_api.users import models as users_models +from funkwhale_api.tags import models as tags_models class ActorField(forms.CharField): @@ -340,3 +341,11 @@ class ManageInstancePolicyFilterSet(filters.FilterSet): "silence_notifications", "reject_media", ] + + +class ManageTagFilterSet(filters.FilterSet): + q = fields.SearchFilter(search_fields=["name"]) + + class Meta: + model = tags_models.Tag + fields = ["q"] diff --git a/api/funkwhale_api/manage/serializers.py b/api/funkwhale_api/manage/serializers.py index 67e0178e0..eab3b87f0 100644 --- a/api/funkwhale_api/manage/serializers.py +++ b/api/funkwhale_api/manage/serializers.py @@ -10,6 +10,7 @@ from funkwhale_api.federation import tasks as federation_tasks from funkwhale_api.moderation import models as moderation_models from funkwhale_api.music import models as music_models from funkwhale_api.music import serializers as music_serializers +from funkwhale_api.tags import models as tags_models from funkwhale_api.users import models as users_models from . import filters @@ -564,3 +565,30 @@ class ManageUploadSerializer(serializers.ModelSerializer): "track", "library", ) + + +class ManageTagSerializer(ManageBaseAlbumSerializer): + + tracks_count = serializers.SerializerMethodField() + albums_count = serializers.SerializerMethodField() + artists_count = serializers.SerializerMethodField() + + class Meta: + model = tags_models.Tag + fields = [ + "id", + "name", + "creation_date", + "tracks_count", + "albums_count", + "artists_count", + ] + + def get_tracks_count(self, obj): + return getattr(obj, "_tracks_count", None) + + def get_albums_count(self, obj): + return getattr(obj, "_albums_count", None) + + def get_artists_count(self, obj): + return getattr(obj, "_artists_count", None) diff --git a/api/funkwhale_api/manage/urls.py b/api/funkwhale_api/manage/urls.py index 2af18f5b7..b830f0023 100644 --- a/api/funkwhale_api/manage/urls.py +++ b/api/funkwhale_api/manage/urls.py @@ -24,6 +24,7 @@ users_router.register(r"invitations", views.ManageInvitationViewSet, "invitation other_router = routers.OptionalSlashRouter() other_router.register(r"accounts", views.ManageActorViewSet, "accounts") +other_router.register(r"tags", views.ManageTagViewSet, "tags") urlpatterns = [ url( diff --git a/api/funkwhale_api/manage/views.py b/api/funkwhale_api/manage/views.py index c788dd96b..09737a7a8 100644 --- a/api/funkwhale_api/manage/views.py +++ b/api/funkwhale_api/manage/views.py @@ -2,7 +2,7 @@ from rest_framework import mixins, response, viewsets from rest_framework import decorators as rest_decorators from django.db.models import Count, Prefetch, Q, Sum, OuterRef, Subquery -from django.db.models.functions import Coalesce +from django.db.models.functions import Coalesce, Length from django.shortcuts import get_object_or_404 from funkwhale_api.common import models as common_models @@ -14,6 +14,7 @@ from funkwhale_api.history import models as history_models from funkwhale_api.music import models as music_models from funkwhale_api.moderation import models as moderation_models from funkwhale_api.playlists import models as playlists_models +from funkwhale_api.tags import models as tags_models from funkwhale_api.users import models as users_models @@ -452,3 +453,43 @@ class ManageInstancePolicyViewSet( def perform_create(self, serializer): serializer.save(actor=self.request.user.actor) + + +class ManageTagViewSet( + mixins.ListModelMixin, + mixins.RetrieveModelMixin, + mixins.DestroyModelMixin, + mixins.CreateModelMixin, + viewsets.GenericViewSet, +): + lookup_field = "name" + queryset = ( + tags_models.Tag.objects.all() + .order_by("-creation_date") + .annotate(items_count=Count("tagged_items")) + .annotate(length=Length("name")) + ) + serializer_class = serializers.ManageTagSerializer + filterset_class = filters.ManageTagFilterSet + required_scope = "instance:libraries" + ordering_fields = ["id", "creation_date", "name", "items_count", "length"] + + def get_queryset(self): + queryset = super().get_queryset() + from django.contrib.contenttypes.models import ContentType + + album_ct = ContentType.objects.get_for_model(music_models.Album) + track_ct = ContentType.objects.get_for_model(music_models.Track) + artist_ct = ContentType.objects.get_for_model(music_models.Artist) + queryset = queryset.annotate( + _albums_count=Count( + "tagged_items", filter=Q(tagged_items__content_type=album_ct) + ), + _tracks_count=Count( + "tagged_items", filter=Q(tagged_items__content_type=track_ct) + ), + _artists_count=Count( + "tagged_items", filter=Q(tagged_items__content_type=artist_ct) + ), + ) + return queryset diff --git a/api/tests/manage/test_serializers.py b/api/tests/manage/test_serializers.py index e621bf14c..c1c532fac 100644 --- a/api/tests/manage/test_serializers.py +++ b/api/tests/manage/test_serializers.py @@ -496,3 +496,22 @@ def test_action_serializer_delete(factory, serializer_class, factories): s.handle_delete(objects[0].__class__.objects.all()) assert objects[0].__class__.objects.count() == 0 + + +def test_manage_tag_serializer(factories): + tag = factories["tags.Tag"]() + + setattr(tag, "_tracks_count", 42) + setattr(tag, "_albums_count", 54) + setattr(tag, "_artists_count", 66) + expected = { + "id": tag.id, + "name": tag.name, + "creation_date": tag.creation_date.isoformat().split("+")[0] + "Z", + "tracks_count": 42, + "albums_count": 54, + "artists_count": 66, + } + s = serializers.ManageTagSerializer(tag) + + assert s.data == expected diff --git a/api/tests/manage/test_views.py b/api/tests/manage/test_views.py index 72394052c..710722b90 100644 --- a/api/tests/manage/test_views.py +++ b/api/tests/manage/test_views.py @@ -377,3 +377,31 @@ def test_upload_delete(factories, superuser_api_client): response = superuser_api_client.delete(url) assert response.status_code == 204 + + +def test_tag_detail(factories, superuser_api_client): + tag = factories["tags.Tag"]() + url = reverse("api:v1:manage:tags-detail", kwargs={"name": tag.name}) + response = superuser_api_client.get(url) + + assert response.status_code == 200 + assert response.data["name"] == tag.name + + +def test_tag_list(factories, superuser_api_client, settings): + tag = factories["tags.Tag"]() + url = reverse("api:v1:manage:tags-list") + response = superuser_api_client.get(url) + + assert response.status_code == 200 + + assert response.data["count"] == 1 + assert response.data["results"][0]["name"] == tag.name + + +def test_tag_delete(factories, superuser_api_client): + tag = factories["tags.Tag"]() + url = reverse("api:v1:manage:tags-detail", kwargs={"name": tag.name}) + response = superuser_api_client.delete(url) + + assert response.status_code == 204