moves on api master
authorRadek Czajka <rczajka@rczajka.pl>
Wed, 18 Dec 2024 14:17:41 +0000 (15:17 +0100)
committerRadek Czajka <rczajka@rczajka.pl>
Wed, 18 Dec 2024 14:17:41 +0000 (15:17 +0100)
src/api/fields.py
src/api/pagination.py [new file with mode: 0644]
src/api/urls.py
src/catalogue/api/serializers.py
src/catalogue/api/urls2.py [new file with mode: 0644]
src/catalogue/api/views.py
src/social/api/views.py
src/wolnelektury/settings/contrib.py

index cdfcc47..7283234 100644 (file)
@@ -3,7 +3,7 @@
 #
 from rest_framework import serializers
 from sorl.thumbnail import default
-from django.urls import reverse
+from rest_framework.reverse import reverse
 from club.models import Membership
 
 
@@ -20,13 +20,14 @@ class AbsoluteURLField(serializers.ReadOnlyField):
                 self.view_args[fields[0]] = fields[1] if len(fields) > 1 else fields[0]
 
     def to_representation(self, value):
+        request = self.context['request']
         if self.view_name is not None:
             kwargs = {
                 arg: getattr(value, field)
                 for (arg, field) in self.view_args.items()
             }
-            value = reverse(self.view_name, kwargs=kwargs)
-        return self.context['request'].build_absolute_uri(value)
+            return reverse(self.view_name, kwargs=kwargs, request=request)
+        return request.build_absolute_uri(value)
 
 
 class LegacyMixin:
diff --git a/src/api/pagination.py b/src/api/pagination.py
new file mode 100644 (file)
index 0000000..0c4ae09
--- /dev/null
@@ -0,0 +1,14 @@
+from rest_framework.pagination import LimitOffsetPagination, PageLink
+from rest_framework.response import Response
+
+
+class WLLimitOffsetPagination(LimitOffsetPagination):
+    def get_paginated_response(self, data):
+        return Response({
+            "member": data,
+            "totalItems": self.count,
+            "view": {
+                "previous": self.get_previous_link(),
+                "next": self.get_next_link(),
+            }
+        })
index 703283e..b0ee94d 100644 (file)
@@ -9,7 +9,15 @@ from stats.utils import piwik_track_view
 from . import views
 
 
+urlpatterns1 = [
+    path('login/', csrf_exempt(views.LoginView.as_view())),
+    path('', include('catalogue.api.urls2')),
+]
+
+
 urlpatterns = [
+    path('2/', include((urlpatterns1, 'api'), namespace="v2")),
+
     path('oauth/request_token/', csrf_exempt(views.OAuth1RequestTokenView.as_view())),
     path('oauth/authorize/', views.oauth_user_auth, name='oauth_user_auth'),
     path('oauth/access_token/', csrf_exempt(views.OAuth1AccessTokenView.as_view())),
@@ -37,4 +45,5 @@ urlpatterns = [
 
     path('', include('social.api.urls')),
     path('', include('catalogue.api.urls')),
+
 ]
index daa86fe..ee3c506 100644 (file)
@@ -44,6 +44,30 @@ class TranslatorSerializer(serializers.Serializer):
     name = serializers.CharField(source='*')
 
 
+class BookSerializer2(serializers.ModelSerializer):
+    url = AbsoluteURLField()
+    href = AbsoluteURLField(view_name='catalogue_api_book', view_args=['slug'])
+    xml = EmbargoURLField(source='xml_url')
+    html = EmbargoURLField(source='html_url')
+    txt = EmbargoURLField(source='txt_url')
+    fb2 = EmbargoURLField(source='fb2_url')
+    epub = EmbargoURLField(source='epub_url')
+    mobi = EmbargoURLField(source='mobi_url')
+    pdf = EmbargoURLField(source='pdf_url')
+
+    class Meta:
+        model = Book
+        fields = [
+            'full_sort_key', 'title',
+            'href', 'url', 'language',
+            #'epochs', 'genres', 'kinds', 'authors', 'translators',
+            #'children',
+            'parent', 'preview',
+            'epub', 'mobi', 'pdf', 'html', 'txt', 'fb2', 'xml',
+            'cover_thumb', 'cover',
+            'isbn_pdf', 'isbn_epub', 'isbn_mobi',
+        ]
+
 class BookSerializer(LegacyMixin, serializers.ModelSerializer):
     author = serializers.CharField(source='author_unicode')
     kind = serializers.CharField(source='kind_unicode')
diff --git a/src/catalogue/api/urls2.py b/src/catalogue/api/urls2.py
new file mode 100644 (file)
index 0000000..3fd0023
--- /dev/null
@@ -0,0 +1,18 @@
+# This file is part of Wolne Lektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Wolne Lektury. See NOTICE for more information.
+#
+from django.urls import path, re_path
+from stats.utils import piwik_track_view
+from . import views
+
+
+urlpatterns = [
+    path('books/',
+         piwik_track_view(views.BookList2.as_view()),
+         name='catalogue_api_book_list'
+         ),
+    path('books/<slug:slug>/',
+         piwik_track_view(views.BookDetail2.as_view()),
+         name='catalogue_api_book'
+         ),
+]
index c0dc57f..abe8064 100644 (file)
@@ -30,6 +30,10 @@ from . import serializers
 book_tag_categories = ['author', 'epoch', 'kind', 'genre']
 
 
+class LegacyListAPIView(ListAPIView):
+    pagination_class = None
+
+
 class CreateOnPutMixin:
     '''
     Creates a new model instance when PUTting a nonexistent resource.
@@ -47,7 +51,7 @@ class CreateOnPutMixin:
                 raise
 
 
-class CollectionList(ListAPIView):
+class CollectionList(LegacyListAPIView):
     queryset = Collection.objects.filter(listed=True)
     serializer_class = serializers.CollectionListSerializer
 
@@ -61,7 +65,7 @@ class CollectionDetail(CreateOnPutMixin, RetrieveUpdateAPIView):
 
 
 @vary_on_auth  # Because of 'liked'.
-class BookList(ListAPIView):
+class BookList(LegacyListAPIView):
     permission_classes = [DjangoModelPermissionsOrAnonReadOnly]
     queryset = Book.objects.none()  # Required for DjangoModelPermissions
     serializer_class = serializers.BookListSerializer
@@ -179,6 +183,19 @@ class BookList(ListAPIView):
         return Response({}, status=status.HTTP_201_CREATED)
 
 
+class BookList2(ListAPIView):
+    permission_classes = [DjangoModelPermissionsOrAnonReadOnly]
+    queryset = Book.objects.none()  # Required for DjangoModelPermissions
+    serializer_class = serializers.BookSerializer2
+
+    def get_queryset(self):
+        books = Book.objects.all()
+        books = books.filter(findable=True)
+        books = order_books(books, True)
+
+        return books
+
+
 @vary_on_auth  # Because of 'liked'.
 class BookDetail(RetrieveAPIView):
     queryset = Book.objects.all()
@@ -186,13 +203,19 @@ class BookDetail(RetrieveAPIView):
     serializer_class = serializers.BookDetailSerializer
 
 
+class BookDetail2(RetrieveAPIView):
+    queryset = Book.objects.all()
+    lookup_field = 'slug'
+    serializer_class = serializers.BookSerializer2
+
+
 @vary_on_auth  # Because of embargo links.
 class EbookList(BookList):
     serializer_class = serializers.EbookSerializer
 
 
 @method_decorator(never_cache, name='dispatch')
-class Preview(ListAPIView):
+class Preview(LegacyListAPIView):
     #queryset = Book.objects.filter(preview=True)
     serializer_class = serializers.BookPreviewSerializer
 
@@ -205,7 +228,7 @@ class Preview(ListAPIView):
 
 
 @vary_on_auth  # Because of 'liked'.
-class FilterBookList(ListAPIView):
+class FilterBookList(LegacyListAPIView):
     serializer_class = serializers.FilterBookListSerializer
 
     def parse_bool(self, s):
@@ -289,7 +312,7 @@ class EpubView(RetrieveAPIView):
         return HttpResponse(self.get_object().get_media('epub'))
 
 
-class TagCategoryView(ListAPIView):
+class TagCategoryView(LegacyListAPIView):
     serializer_class = serializers.TagSerializer
 
     def get_queryset(self):
@@ -373,7 +396,7 @@ class TagView(RetrieveAPIView):
 
 
 @vary_on_auth  # Because of 'liked'.
-class FragmentList(ListAPIView):
+class FragmentList(LegacyListAPIView):
     serializer_class = serializers.FragmentSerializer
 
     def get_queryset(self):
index c5506ce..a299304 100644 (file)
@@ -36,6 +36,7 @@ class LikeView(APIView):
 class ShelfView(ListAPIView):
     permission_classes = [IsAuthenticated]
     serializer_class = BookSerializer
+    pagination_class = None
 
     def get_queryset(self):
         state = self.kwargs['state']
index aaef8b2..e5b2f60 100644 (file)
@@ -31,7 +31,10 @@ REST_FRAMEWORK = {
         'api.drf_auth.WLTokenAuthentication',
         'api.drf_auth.PistonOAuthAuthentication',
         'rest_framework.authentication.SessionAuthentication',
-    )
+    ),
+    'DEFAULT_PAGINATION_CLASS': 'api.pagination.WLLimitOffsetPagination',
+    'PAGE_SIZE': 10,
+    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning',
 }