kopia lustrzana https://dev.funkwhale.audio/funkwhale/funkwhale
				
				
				
			See #542: Removed import requests code
							rodzic
							
								
									42933fa138
								
							
						
					
					
						commit
						62c98b2386
					
				|  | @ -65,10 +65,6 @@ v1_patterns += [ | |||
|         r"^users/", | ||||
|         include(("funkwhale_api.users.api_urls", "users"), namespace="users"), | ||||
|     ), | ||||
|     url( | ||||
|         r"^requests/", | ||||
|         include(("funkwhale_api.requests.api_urls", "requests"), namespace="requests"), | ||||
|     ), | ||||
|     url(r"^token/$", jwt_views.obtain_jwt_token, name="token"), | ||||
|     url(r"^token/refresh/$", jwt_views.refresh_jwt_token, name="token_refresh"), | ||||
| ] | ||||
|  |  | |||
|  | @ -2,7 +2,6 @@ from django_filters import rest_framework as filters | |||
| 
 | ||||
| from funkwhale_api.common import fields | ||||
| from funkwhale_api.music import models as music_models | ||||
| from funkwhale_api.requests import models as requests_models | ||||
| from funkwhale_api.users import models as users_models | ||||
| 
 | ||||
| 
 | ||||
|  | @ -51,13 +50,3 @@ class ManageInvitationFilterSet(filters.FilterSet): | |||
|         if value is None: | ||||
|             return queryset | ||||
|         return queryset.open(value) | ||||
| 
 | ||||
| 
 | ||||
| class ManageImportRequestFilterSet(filters.FilterSet): | ||||
|     q = fields.SearchFilter( | ||||
|         search_fields=["user__username", "albums", "artist_name", "comment"] | ||||
|     ) | ||||
| 
 | ||||
|     class Meta: | ||||
|         model = requests_models.ImportRequest | ||||
|         fields = ["q", "status"] | ||||
|  |  | |||
|  | @ -1,10 +1,9 @@ | |||
| from django.db import transaction | ||||
| from django.utils import timezone | ||||
| 
 | ||||
| from rest_framework import serializers | ||||
| 
 | ||||
| from funkwhale_api.common import serializers as common_serializers | ||||
| from funkwhale_api.music import models as music_models | ||||
| from funkwhale_api.requests import models as requests_models | ||||
| from funkwhale_api.users import models as users_models | ||||
| 
 | ||||
| from . import filters | ||||
|  | @ -169,69 +168,3 @@ class ManageInvitationActionSerializer(common_serializers.ActionSerializer): | |||
|     @transaction.atomic | ||||
|     def handle_delete(self, objects): | ||||
|         return objects.delete() | ||||
| 
 | ||||
| 
 | ||||
| class ManageImportRequestSerializer(serializers.ModelSerializer): | ||||
|     user = ManageUserSimpleSerializer(required=False) | ||||
| 
 | ||||
|     class Meta: | ||||
|         model = requests_models.ImportRequest | ||||
|         fields = [ | ||||
|             "id", | ||||
|             "status", | ||||
|             "creation_date", | ||||
|             "imported_date", | ||||
|             "user", | ||||
|             "albums", | ||||
|             "artist_name", | ||||
|             "comment", | ||||
|         ] | ||||
|         read_only_fields = [ | ||||
|             "id", | ||||
|             "status", | ||||
|             "creation_date", | ||||
|             "imported_date", | ||||
|             "user", | ||||
|             "albums", | ||||
|             "artist_name", | ||||
|             "comment", | ||||
|         ] | ||||
| 
 | ||||
|     def validate_code(self, value): | ||||
|         if not value: | ||||
|             return value | ||||
|         if users_models.Invitation.objects.filter(code__iexact=value).exists(): | ||||
|             raise serializers.ValidationError( | ||||
|                 "An invitation with this code already exists" | ||||
|             ) | ||||
|         return value | ||||
| 
 | ||||
| 
 | ||||
| class ManageImportRequestActionSerializer(common_serializers.ActionSerializer): | ||||
|     actions = [ | ||||
|         common_serializers.Action( | ||||
|             "mark_closed", | ||||
|             allow_all=True, | ||||
|             qs_filter=lambda qs: qs.filter(status__in=["pending", "accepted"]), | ||||
|         ), | ||||
|         common_serializers.Action( | ||||
|             "mark_imported", | ||||
|             allow_all=True, | ||||
|             qs_filter=lambda qs: qs.filter(status__in=["pending", "accepted"]), | ||||
|         ), | ||||
|         common_serializers.Action("delete", allow_all=False), | ||||
|     ] | ||||
|     filterset_class = filters.ManageImportRequestFilterSet | ||||
| 
 | ||||
|     @transaction.atomic | ||||
|     def handle_delete(self, objects): | ||||
|         return objects.delete() | ||||
| 
 | ||||
|     @transaction.atomic | ||||
|     def handle_mark_closed(self, objects): | ||||
|         return objects.update(status="closed") | ||||
| 
 | ||||
|     @transaction.atomic | ||||
|     def handle_mark_imported(self, objects): | ||||
|         now = timezone.now() | ||||
|         return objects.update(status="imported", imported_date=now) | ||||
|  |  | |||
|  | @ -5,10 +5,6 @@ from . import views | |||
| 
 | ||||
| library_router = routers.SimpleRouter() | ||||
| library_router.register(r"uploads", views.ManageUploadViewSet, "uploads") | ||||
| requests_router = routers.SimpleRouter() | ||||
| requests_router.register( | ||||
|     r"import-requests", views.ManageImportRequestViewSet, "import-requests" | ||||
| ) | ||||
| users_router = routers.SimpleRouter() | ||||
| users_router.register(r"users", views.ManageUserViewSet, "users") | ||||
| users_router.register(r"invitations", views.ManageInvitationViewSet, "invitations") | ||||
|  | @ -16,7 +12,4 @@ users_router.register(r"invitations", views.ManageInvitationViewSet, "invitation | |||
| urlpatterns = [ | ||||
|     url(r"^library/", include((library_router.urls, "instance"), namespace="library")), | ||||
|     url(r"^users/", include((users_router.urls, "instance"), namespace="users")), | ||||
|     url( | ||||
|         r"^requests/", include((requests_router.urls, "instance"), namespace="requests") | ||||
|     ), | ||||
| ] | ||||
|  |  | |||
|  | @ -3,7 +3,6 @@ from rest_framework.decorators import list_route | |||
| 
 | ||||
| from funkwhale_api.common import preferences | ||||
| from funkwhale_api.music import models as music_models | ||||
| from funkwhale_api.requests import models as requests_models | ||||
| from funkwhale_api.users import models as users_models | ||||
| from funkwhale_api.users.permissions import HasUserPermission | ||||
| 
 | ||||
|  | @ -93,31 +92,3 @@ class ManageInvitationViewSet( | |||
|         serializer.is_valid(raise_exception=True) | ||||
|         result = serializer.save() | ||||
|         return response.Response(result, status=200) | ||||
| 
 | ||||
| 
 | ||||
| class ManageImportRequestViewSet( | ||||
|     mixins.ListModelMixin, | ||||
|     mixins.RetrieveModelMixin, | ||||
|     mixins.UpdateModelMixin, | ||||
|     viewsets.GenericViewSet, | ||||
| ): | ||||
|     queryset = ( | ||||
|         requests_models.ImportRequest.objects.all() | ||||
|         .order_by("-id") | ||||
|         .select_related("user") | ||||
|     ) | ||||
|     serializer_class = serializers.ManageImportRequestSerializer | ||||
|     filter_class = filters.ManageImportRequestFilterSet | ||||
|     permission_classes = (HasUserPermission,) | ||||
|     required_permissions = ["library"] | ||||
|     ordering_fields = ["creation_date", "imported_date"] | ||||
| 
 | ||||
|     @list_route(methods=["post"]) | ||||
|     def action(self, request, *args, **kwargs): | ||||
|         queryset = self.get_queryset() | ||||
|         serializer = serializers.ManageImportRequestActionSerializer( | ||||
|             request.data, queryset=queryset | ||||
|         ) | ||||
|         serializer.is_valid(raise_exception=True) | ||||
|         result = serializer.save() | ||||
|         return response.Response(result, status=200) | ||||
|  |  | |||
|  | @ -1,11 +0,0 @@ | |||
| from django.contrib import admin | ||||
| 
 | ||||
| from . import models | ||||
| 
 | ||||
| 
 | ||||
| @admin.register(models.ImportRequest) | ||||
| class ImportRequestAdmin(admin.ModelAdmin): | ||||
|     list_display = ["artist_name", "user", "status", "creation_date"] | ||||
|     list_select_related = ["user"] | ||||
|     list_filter = ["status"] | ||||
|     search_fields = ["artist_name", "comment", "albums"] | ||||
|  | @ -1,8 +0,0 @@ | |||
| from rest_framework import routers | ||||
| 
 | ||||
| from . import views | ||||
| 
 | ||||
| router = routers.SimpleRouter() | ||||
| router.register(r"import-requests", views.ImportRequestViewSet, "import-requests") | ||||
| 
 | ||||
| urlpatterns = router.urls | ||||
|  | @ -1,15 +0,0 @@ | |||
| import factory | ||||
| 
 | ||||
| from funkwhale_api.factories import registry | ||||
| from funkwhale_api.users.factories import UserFactory | ||||
| 
 | ||||
| 
 | ||||
| @registry.register | ||||
| class ImportRequestFactory(factory.django.DjangoModelFactory): | ||||
|     artist_name = factory.Faker("name") | ||||
|     albums = factory.Faker("sentence") | ||||
|     user = factory.SubFactory(UserFactory) | ||||
|     comment = factory.Faker("paragraph") | ||||
| 
 | ||||
|     class Meta: | ||||
|         model = "requests.ImportRequest" | ||||
|  | @ -1,20 +0,0 @@ | |||
| import django_filters | ||||
| 
 | ||||
| from funkwhale_api.common import fields | ||||
| 
 | ||||
| from . import models | ||||
| 
 | ||||
| 
 | ||||
| class ImportRequestFilter(django_filters.FilterSet): | ||||
| 
 | ||||
|     q = fields.SearchFilter( | ||||
|         search_fields=["artist_name", "user__username", "albums", "comment"] | ||||
|     ) | ||||
| 
 | ||||
|     class Meta: | ||||
|         model = models.ImportRequest | ||||
|         fields = { | ||||
|             "artist_name": ["exact", "iexact", "startswith", "icontains"], | ||||
|             "status": ["exact"], | ||||
|             "user__username": ["exact"], | ||||
|         } | ||||
|  | @ -1,27 +0,0 @@ | |||
| from rest_framework import serializers | ||||
| 
 | ||||
| from funkwhale_api.users.serializers import UserBasicSerializer | ||||
| 
 | ||||
| from . import models | ||||
| 
 | ||||
| 
 | ||||
| class ImportRequestSerializer(serializers.ModelSerializer): | ||||
|     user = UserBasicSerializer(read_only=True) | ||||
| 
 | ||||
|     class Meta: | ||||
|         model = models.ImportRequest | ||||
|         fields = ( | ||||
|             "id", | ||||
|             "status", | ||||
|             "albums", | ||||
|             "artist_name", | ||||
|             "user", | ||||
|             "creation_date", | ||||
|             "imported_date", | ||||
|             "comment", | ||||
|         ) | ||||
|         read_only_fields = ("creation_date", "imported_date", "user", "status") | ||||
| 
 | ||||
|     def create(self, validated_data): | ||||
|         validated_data["user"] = self.context["user"] | ||||
|         return super().create(validated_data) | ||||
|  | @ -1,27 +0,0 @@ | |||
| from rest_framework import mixins, viewsets | ||||
| 
 | ||||
| from . import filters, models, serializers | ||||
| 
 | ||||
| 
 | ||||
| class ImportRequestViewSet( | ||||
|     mixins.CreateModelMixin, | ||||
|     mixins.RetrieveModelMixin, | ||||
|     mixins.ListModelMixin, | ||||
|     viewsets.GenericViewSet, | ||||
| ): | ||||
| 
 | ||||
|     serializer_class = serializers.ImportRequestSerializer | ||||
|     queryset = ( | ||||
|         models.ImportRequest.objects.all().select_related().order_by("-creation_date") | ||||
|     ) | ||||
|     filter_class = filters.ImportRequestFilter | ||||
|     ordering_fields = ("id", "artist_name", "creation_date", "status") | ||||
| 
 | ||||
|     def perform_create(self, serializer): | ||||
|         return serializer.save(user=self.request.user) | ||||
| 
 | ||||
|     def get_serializer_context(self): | ||||
|         context = super().get_serializer_context() | ||||
|         if self.request.user.is_authenticated: | ||||
|             context["user"] = self.request.user | ||||
|         return context | ||||
|  | @ -36,44 +36,3 @@ def test_user_update_permission(factories): | |||
|     assert user.permission_upload is True | ||||
|     assert user.permission_library is False | ||||
|     assert user.permission_settings is True | ||||
| 
 | ||||
| 
 | ||||
| def test_manage_import_request_mark_closed(factories): | ||||
|     affected = factories["requests.ImportRequest"].create_batch( | ||||
|         size=5, status="pending" | ||||
|     ) | ||||
|     # we do not update imported requests | ||||
|     factories["requests.ImportRequest"].create_batch(size=5, status="imported") | ||||
|     s = serializers.ManageImportRequestActionSerializer( | ||||
|         queryset=affected[0].__class__.objects.all(), | ||||
|         data={"objects": "all", "action": "mark_closed"}, | ||||
|     ) | ||||
| 
 | ||||
|     assert s.is_valid(raise_exception=True) is True | ||||
|     s.save() | ||||
| 
 | ||||
|     assert affected[0].__class__.objects.filter(status="imported").count() == 5 | ||||
|     for ir in affected: | ||||
|         ir.refresh_from_db() | ||||
|         assert ir.status == "closed" | ||||
| 
 | ||||
| 
 | ||||
| def test_manage_import_request_mark_imported(factories, now): | ||||
|     affected = factories["requests.ImportRequest"].create_batch( | ||||
|         size=5, status="pending" | ||||
|     ) | ||||
|     # we do not update closed requests | ||||
|     factories["requests.ImportRequest"].create_batch(size=5, status="closed") | ||||
|     s = serializers.ManageImportRequestActionSerializer( | ||||
|         queryset=affected[0].__class__.objects.all(), | ||||
|         data={"objects": "all", "action": "mark_imported"}, | ||||
|     ) | ||||
| 
 | ||||
|     assert s.is_valid(raise_exception=True) is True | ||||
|     s.save() | ||||
| 
 | ||||
|     assert affected[0].__class__.objects.filter(status="closed").count() == 5 | ||||
|     for ir in affected: | ||||
|         ir.refresh_from_db() | ||||
|         assert ir.status == "imported" | ||||
|         assert ir.imported_date == now | ||||
|  |  | |||
|  | @ -10,7 +10,6 @@ from funkwhale_api.manage import serializers, views | |||
|         (views.ManageUploadViewSet, ["library"], "and"), | ||||
|         (views.ManageUserViewSet, ["settings"], "and"), | ||||
|         (views.ManageInvitationViewSet, ["settings"], "and"), | ||||
|         (views.ManageImportRequestViewSet, ["library"], "and"), | ||||
|     ], | ||||
| ) | ||||
| def test_permissions(assert_user_permission, view, permissions, operator): | ||||
|  | @ -65,15 +64,3 @@ def test_invitation_view_create(factories, superuser_api_client, mocker): | |||
| 
 | ||||
|     assert response.status_code == 201 | ||||
|     assert superuser_api_client.user.invitations.latest("id") is not None | ||||
| 
 | ||||
| 
 | ||||
| def test_music_requests_view(factories, superuser_api_client, mocker): | ||||
|     invitations = factories["requests.ImportRequest"].create_batch(size=5) | ||||
|     qs = invitations[0].__class__.objects.order_by("-id") | ||||
|     url = reverse("api:v1:manage:requests:import-requests-list") | ||||
| 
 | ||||
|     response = superuser_api_client.get(url, {"sort": "-id"}) | ||||
|     expected = serializers.ManageImportRequestSerializer(qs, many=True).data | ||||
| 
 | ||||
|     assert response.data["count"] == len(invitations) | ||||
|     assert response.data["results"] == expected | ||||
|  |  | |||
|  | @ -1,26 +0,0 @@ | |||
| from django.urls import reverse | ||||
| 
 | ||||
| 
 | ||||
| def test_request_viewset_requires_auth(db, api_client): | ||||
|     url = reverse("api:v1:requests:import-requests-list") | ||||
|     response = api_client.get(url) | ||||
|     assert response.status_code == 401 | ||||
| 
 | ||||
| 
 | ||||
| def test_user_can_create_request(logged_in_api_client): | ||||
|     url = reverse("api:v1:requests:import-requests-list") | ||||
|     user = logged_in_api_client.user | ||||
|     data = { | ||||
|         "artist_name": "System of a Down", | ||||
|         "albums": "All please!", | ||||
|         "comment": "Please, they rock!", | ||||
|     } | ||||
|     response = logged_in_api_client.post(url, data) | ||||
| 
 | ||||
|     assert response.status_code == 201 | ||||
| 
 | ||||
|     ir = user.import_requests.latest("id") | ||||
|     assert ir.status == "pending" | ||||
|     assert ir.creation_date is not None | ||||
|     for field, value in data.items(): | ||||
|         assert getattr(ir, field) == value | ||||
|  | @ -34,9 +34,6 @@ | |||
|                 <router-link class="item" to="/about"> | ||||
|                   <translate>About this instance</translate> | ||||
|                 </router-link> | ||||
|                 <router-link class="item" :to="{name: 'library.request'}"> | ||||
|                   <translate>Request music</translate> | ||||
|                 </router-link> | ||||
|                 <a href="https://funkwhale.audio" class="item" target="_blank"><translate>Official website</translate></a> | ||||
|                 <a href="https://docs.funkwhale.audio" class="item" target="_blank"><translate>Documentation</translate></a> | ||||
|                 <a href="https://code.eliotberriot.com/funkwhale/funkwhale" class="item" target="_blank"> | ||||
|  |  | |||
|  | @ -27,12 +27,10 @@ import Favorites from '@/components/favorites/List' | |||
| import AdminSettings from '@/views/admin/Settings' | ||||
| import AdminLibraryBase from '@/views/admin/library/Base' | ||||
| import AdminLibraryFilesList from '@/views/admin/library/FilesList' | ||||
| import AdminLibraryRequestsList from '@/views/admin/library/RequestsList' | ||||
| import AdminUsersBase from '@/views/admin/users/Base' | ||||
| import AdminUsersDetail from '@/views/admin/users/UsersDetail' | ||||
| import AdminUsersList from '@/views/admin/users/UsersList' | ||||
| import AdminInvitationsList from '@/views/admin/users/InvitationsList' | ||||
| import MusicRequest from '@/views/library/MusicRequest' | ||||
| import FederationBase from '@/views/federation/Base' | ||||
| import FederationScan from '@/views/federation/Scan' | ||||
| import FederationLibraryDetail from '@/views/federation/LibraryDetail' | ||||
|  | @ -257,11 +255,6 @@ export default new Router({ | |||
|           path: 'files', | ||||
|           name: 'manage.library.files', | ||||
|           component: AdminLibraryFilesList | ||||
|         }, | ||||
|         { | ||||
|           path: 'requests', | ||||
|           name: 'manage.library.requests', | ||||
|           component: AdminLibraryRequestsList | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|  | @ -292,11 +285,6 @@ export default new Router({ | |||
|       component: Library, | ||||
|       children: [ | ||||
|         { path: '', component: LibraryHome, name: 'library.index' }, | ||||
|         { | ||||
|           path: 'requests/', | ||||
|           name: 'library.request', | ||||
|           component: MusicRequest | ||||
|         }, | ||||
|         { | ||||
|           path: 'artists/', | ||||
|           name: 'library.artists.browse', | ||||
|  |  | |||
|  | @ -4,15 +4,6 @@ | |||
|       <router-link | ||||
|         class="ui item" | ||||
|         :to="{name: 'manage.library.files'}"><translate>Files</translate></router-link> | ||||
|       <router-link | ||||
|         class="ui item" | ||||
|         :to="{name: 'manage.library.requests'}"> | ||||
|           <translate>Import requests</translate> | ||||
|           <div | ||||
|             :class="['ui', {'teal': $store.state.ui.notifications.importRequests > 0}, 'label']" | ||||
|             :title="labels.pendingRequests"> | ||||
|             {{ $store.state.ui.notifications.importRequests }}</div> | ||||
|           </router-link> | ||||
|     </div> | ||||
|     <router-view :key="$route.fullPath"></router-view> | ||||
|   </div> | ||||
|  | @ -23,10 +14,8 @@ export default { | |||
|   computed: { | ||||
|     labels () { | ||||
|       let title = this.$gettext('Manage library') | ||||
|       let pendingRequests = this.$gettext('Pending import requests') | ||||
|       return { | ||||
|         title, | ||||
|         pendingRequests | ||||
|         title | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  |  | |||
|  | @ -1,30 +0,0 @@ | |||
| <template> | ||||
|   <div v-title="labels.importRequests"> | ||||
|     <div class="ui vertical stripe segment"> | ||||
|       <h2 class="ui header"><translate>Import requests</translate></h2> | ||||
|       <div class="ui hidden divider"></div> | ||||
|       <library-requests-table></library-requests-table> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| import LibraryRequestsTable from '@/components/manage/library/RequestsTable' | ||||
| 
 | ||||
| export default { | ||||
|   components: { | ||||
|     LibraryRequestsTable | ||||
|   }, | ||||
|   computed: { | ||||
|     labels () { | ||||
|       return { | ||||
|         importRequests: this.$gettext('Import requests') | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <!-- Add "scoped" attribute to limit CSS to this component only --> | ||||
| <style scoped> | ||||
| </style> | ||||
|  | @ -1,32 +0,0 @@ | |||
| <template> | ||||
|   <div class="ui vertical stripe segment" v-title="labels.title"> | ||||
|     <div class="ui small text container"> | ||||
|       <h2 class="ui header"> | ||||
|         <translate>Request some music</translate> | ||||
|       </h2> | ||||
|       <request-form v-if="$store.state.auth.authenticated"></request-form> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| import RequestForm from '@/components/requests/Form' | ||||
| 
 | ||||
| export default { | ||||
|   components: { | ||||
|     RequestForm | ||||
|   }, | ||||
|   computed: { | ||||
|     labels () { | ||||
|       let title = this.$gettext('Request some music') | ||||
|       return { | ||||
|         title | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <!-- Add "scoped" attribute to limit CSS to this component only --> | ||||
| <style scoped> | ||||
| </style> | ||||
		Ładowanie…
	
		Reference in New Issue
	
	 Eliot Berriot
						Eliot Berriot