Merge branch '432-tags-api' into 'develop'

See #432: API endpoints to query tags

See merge request funkwhale/funkwhale!824
This commit is contained in:
Eliot Berriot 2019-07-12 14:53:38 +02:00
commit 8ee5578693
10 changed files with 141 additions and 18 deletions

View File

@ -10,11 +10,12 @@ from funkwhale_api.common import routers as common_routers
from funkwhale_api.music import views
from funkwhale_api.playlists import views as playlists_views
from funkwhale_api.subsonic.views import SubsonicViewSet
from funkwhale_api.tags import views as tags_views
router = common_routers.OptionalSlashRouter()
router.register(r"settings", GlobalPreferencesViewSet, basename="settings")
router.register(r"activity", activity_views.ActivityViewSet, "activity")
router.register(r"tags", views.TagViewSet, "tags")
router.register(r"tags", tags_views.TagViewSet, "tags")
router.register(r"tracks", views.TrackViewSet, "tracks")
router.register(r"uploads", views.UploadViewSet, "uploads")
router.register(r"libraries", views.LibraryViewSet, "libraries")

View File

@ -51,7 +51,7 @@ def create_tagged_tracks(factories, count, dependencies):
objs = []
for track in dependencies["tracks"]:
tag = random.choice(dependencies["tags"])
objs.append(factories["tags.TaggedItem"](content_object=track, tag=tag))
objs.append(factories["tags.TaggedItem"].build(content_object=track, tag=tag))
return tags_models.TaggedItem.objects.bulk_create(
objs, batch_size=BATCH_SIZE, ignore_conflicts=True
@ -217,27 +217,24 @@ class Command(BaseCommand):
if not count:
return []
dependencies = row.get("depends_on", [])
dependencies_results = {}
create_dependencies = options.get("create_dependencies")
for dependency in dependencies:
if not create_dependencies:
continue
dep_count = options.get(dependency["id"])
if not create_dependencies and dep_count is None:
continue
if dep_count is None:
factor = options[
"{}_{}_factor".format(row["id"], dependency["field"])
] or dependency.get("default_factor")
dep_count = math.ceil(factor * count)
dependencies_results[dependency["id"]] = self.create_batch(
results[dependency["id"]] = self.create_batch(
CONFIG_BY_ID[dependency["id"]], results, options, count=dep_count
)
self.stdout.write("Creating {} {}".format(count, row["id"]))
handler = row.get("handler")
if handler:
objects = handler(
factories.registry, count, dependencies=dependencies_results
)
objects = handler(factories.registry, count, dependencies=results)
else:
objects = create_objects(
row, factories.registry, count, **row.get("factory_kwargs", {})
@ -246,7 +243,7 @@ class Command(BaseCommand):
if not dependency.get("set", True):
continue
if create_dependencies:
candidates = dependencies_results[dependency["id"]]
candidates = results[dependency["id"]]
else:
# we use existing objects in the database
queryset = dependency.get(

View File

@ -447,14 +447,6 @@ class UploadViewSet(
instance.delete()
class TagViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Tag.objects.all().order_by("name")
serializer_class = serializers.TagSerializer
permission_classes = [oauth_permissions.ScopePermission]
required_scope = "libraries"
anonymous_policy = "setting"
class Search(views.APIView):
max_results = 3
permission_classes = [oauth_permissions.ScopePermission]

View File

@ -0,0 +1,21 @@
import django_filters
from django_filters import rest_framework as filters
from funkwhale_api.common import fields
from . import models
class TagFilter(filters.FilterSet):
q = fields.SearchFilter(search_fields=["name"])
ordering = django_filters.OrderingFilter(
fields=(
("name", "name"),
("creation_date", "creation_date"),
("__size", "length"),
)
)
class Meta:
model = models.Tag
fields = ["q"]

View File

@ -0,0 +1,9 @@
from rest_framework import serializers
from . import models
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = models.Tag
fields = ["name", "creation_date"]

View File

@ -0,0 +1,22 @@
from django.db.models import functions
from rest_framework import viewsets
from funkwhale_api.users.oauth import permissions as oauth_permissions
from . import filters
from . import models
from . import serializers
class TagViewSet(viewsets.ReadOnlyModelViewSet):
lookup_field = "name"
queryset = (
models.Tag.objects.all()
.annotate(__size=functions.Length("name"))
.order_by("name")
)
serializer_class = serializers.TagSerializer
permission_classes = [oauth_permissions.ScopePermission]
required_scope = "libraries"
anonymous_policy = "setting"
filterset_class = filters.TagFilter

View File

@ -0,0 +1,16 @@
from funkwhale_api.tags import filters
from funkwhale_api.tags import models
def test_filter_search_tag(factories, queryset_equal_list):
matches = [
factories["tags.Tag"](name="Tag1"),
factories["tags.Tag"](name="TestTag1"),
factories["tags.Tag"](name="TestTag12"),
]
factories["tags.Tag"](name="TestTag")
factories["tags.Tag"](name="TestTag2")
qs = models.Tag.objects.all().order_by("name")
filterset = filters.TagFilter({"q": "tag1"}, queryset=qs)
assert filterset.qs == matches

View File

@ -0,0 +1,14 @@
from funkwhale_api.tags import serializers
def test_tag_serializer(factories):
tag = factories["tags.Tag"]()
serializer = serializers.TagSerializer(tag)
expected = {
"name": tag.name,
"creation_date": tag.creation_date.isoformat().split("+")[0] + "Z",
}
assert serializer.data == expected

View File

@ -0,0 +1,50 @@
from django.urls import reverse
from funkwhale_api.tags import serializers
def test_tags_list(factories, logged_in_api_client):
url = reverse("api:v1:tags-list")
tag = factories["tags.Tag"]()
expected = {
"count": 1,
"next": None,
"previous": None,
"results": [serializers.TagSerializer(tag).data],
}
response = logged_in_api_client.get(url)
assert response.data == expected
def test_tags_list_ordering_length(factories, logged_in_api_client):
url = reverse("api:v1:tags-list")
tags = [
factories["tags.Tag"](name="iamareallylongtag"),
factories["tags.Tag"](name="reallylongtag"),
factories["tags.Tag"](name="short"),
factories["tags.Tag"](name="bar"),
]
expected = {
"count": 4,
"next": None,
"previous": None,
"results": [serializers.TagSerializer(tag).data for tag in tags],
}
response = logged_in_api_client.get(url, {"ordering": "-length"})
assert response.data == expected
def test_tags_detail(factories, logged_in_api_client):
tag = factories["tags.Tag"]()
url = reverse("api:v1:tags-detail", kwargs={"name": tag.name})
expected = serializers.TagSerializer(tag).data
response = logged_in_api_client.get(url)
assert response.data == expected

View File

@ -35,6 +35,7 @@ from funkwhale_api.users.oauth import scopes
"get",
),
("api:v1:federation:library-follows-list", {}, "read:follows", "get"),
("api:v1:tags-list", {}, "read:libraries", "get"),
# admin / privileged stuff
("api:v1:instance:admin-settings-list", {}, "read:instance:settings", "get"),
(