More API views migrated.
authorRadek Czajka <rczajka@rczajka.pl>
Mon, 4 Feb 2019 08:59:28 +0000 (09:59 +0100)
committerRadek Czajka <rczajka@rczajka.pl>
Mon, 4 Feb 2019 08:59:28 +0000 (09:59 +0100)
src/api/handlers.py
src/api/templates/api/main.html
src/api/tests/res/responses/fragment.json
src/api/tests/res/responses/fragments.json
src/api/tests/res/responses/tags.json
src/api/urls.py
src/catalogue/api/serializers.py
src/catalogue/api/urls.py
src/catalogue/api/views.py
src/catalogue/models/fragment.py

index 7872a10..4296817 100644 (file)
@@ -17,7 +17,7 @@ from sorl.thumbnail import default
 
 from api.models import BookUserData
 from catalogue.forms import BookImportForm
-from catalogue.models import Book, Tag, BookMedia, Fragment, Collection
+from catalogue.models import Book, Tag, BookMedia
 from catalogue.models.tag import prefetch_relations
 from paypal.rest import user_is_subscribed
 from picture.models import Picture
@@ -100,25 +100,7 @@ def read_tags(tags, request, allowed):
 # RESTful handlers
 
 
-class BookMediaHandler(BaseHandler):
-    """ Responsible for representing media in Books. """
 
-    model = BookMedia
-    fields = ['name', 'type', 'url', 'artist', 'director']
-
-    @classmethod
-    def url(cls, media):
-        """ Link to media on site. """
-
-        return MEDIA_BASE + media.file.url
-
-    @classmethod
-    def artist(cls, media):
-        return media.extra_info.get('artist_name', '')
-
-    @classmethod
-    def director(cls, media):
-        return media.extra_info.get('director_name', '')
 
 
 class BookDetails(object):
@@ -490,155 +472,6 @@ def add_file_getters():
 add_file_getters()
 
 
-class TagDetails(object):
-    """Custom Tag fields."""
-
-    @classmethod
-    def href(cls, tag):
-        """ Returns URI in the API for the tag. """
-
-        return API_BASE + reverse("api_tag", args=[category_plural[tag.category], tag.slug])
-
-    @classmethod
-    def url(cls, tag):
-        """ Returns URL on the site. """
-
-        return WL_BASE + tag.get_absolute_url()
-
-
-class TagDetailHandler(BaseHandler, TagDetails):
-    """ Responsible for details of a single Tag object. """
-
-    fields = ['name', 'url', 'sort_key', 'description']
-
-    @piwik_track
-    def read(self, request, category, slug):
-        """ Returns details of a tag, identified by category and slug. """
-
-        try:
-            category_sng = category_singular[category]
-        except KeyError:
-            return rc.NOT_FOUND
-
-        try:
-            return Tag.objects.get(category=category_sng, slug=slug)
-        except Tag.DoesNotExist:
-            return rc.NOT_FOUND
-
-
-class TagsHandler(BaseHandler, TagDetails):
-    """ Main handler for Tag objects.
-
-    Responsible for lists of Tag objects
-    and fields used for representing Tags.
-
-    """
-    allowed_methods = ('GET',)
-    model = Tag
-    fields = ['name', 'href', 'url', 'slug']
-
-    @piwik_track
-    def read(self, request, category=None, pk=None):
-        """ Lists all tags in the category (eg. all themes). """
-        if pk is not None:
-            # FIXME: Unused?
-            try:
-                return Tag.objects.exclude(category='set').get(pk=pk)
-            except Book.DoesNotExist:
-                return rc.NOT_FOUND
-
-        try:
-            category_sng = category_singular[category]
-        except KeyError:
-            return rc.NOT_FOUND
-
-        after = request.GET.get('after')
-        count = request.GET.get('count')
-
-        tags = Tag.objects.filter(category=category_sng).exclude(items=None).order_by('slug')
-
-        book_only = request.GET.get('book_only') == 'true'
-        picture_only = request.GET.get('picture_only') == 'true'
-        if book_only:
-            tags = tags.filter(for_books=True)
-        if picture_only:
-            tags = tags.filter(for_pictures=True)
-
-        if after:
-            tags = tags.filter(slug__gt=after)
-
-        if count:
-            tags = tags[:count]
-
-        return tags
-
-
-class FragmentDetails(object):
-    """Custom Fragment fields."""
-
-    @classmethod
-    def href(cls, fragment):
-        """ Returns URI in the API for the fragment. """
-
-        return API_BASE + reverse("api_fragment", args=[fragment.book.slug, fragment.anchor])
-
-    @classmethod
-    def url(cls, fragment):
-        """ Returns URL on the site for the fragment. """
-
-        return WL_BASE + fragment.get_absolute_url()
-
-    @classmethod
-    def themes(cls, fragment):
-        """ Returns a list of theme tags for the fragment. """
-
-        return fragment.tags.filter(category='theme')
-
-
-class FragmentDetailHandler(BaseHandler, FragmentDetails):
-    fields = ['book', 'anchor', 'text', 'url', 'themes']
-
-    @piwik_track
-    def read(self, request, book, anchor):
-        """ Returns details of a fragment, identified by book slug and anchor. """
-        try:
-            return Fragment.objects.get(book__slug=book, anchor=anchor)
-        except Fragment.DoesNotExist:
-            return rc.NOT_FOUND
-
-
-class FragmentsHandler(BaseHandler, FragmentDetails):
-    """ Main handler for Fragments.
-
-    Responsible for lists of Fragment objects
-    and fields used for representing Fragments.
-
-    """
-    model = Fragment
-    fields = ['book', 'url', 'anchor', 'href']
-    allowed_methods = ('GET',)
-
-    categories = {'author', 'epoch', 'kind', 'genre', 'book', 'theme'}
-
-    @piwik_track
-    def read(self, request, tags):
-        """ Lists all fragments with given book, tags, themes.
-
-        :param tags: should be a path of categories and slugs, i.e.:
-             books/book-slug/authors/an-author/themes/a-theme/
-
-        """
-        try:
-            tags, ancestors = read_tags(tags, request, allowed=self.categories)
-        except ValueError:
-            return rc.NOT_FOUND
-        fragments = Fragment.tagged.with_all(tags).select_related('book')
-        if fragments.exists():
-            return fragments
-        else:
-            return rc.NOT_FOUND
-
-
 class PictureHandler(BaseHandler):
     model = Picture
     fields = ('slug', 'title')
index a718f21..14dd045 100755 (executable)
         <li><a href='{% url "api_daisy_list" "" %}'>
           {% url "api_daisy_list" "" %}</a> – {% trans "DAISY" %}</li>
 
-        <li><a href='{% url "api_tag_list" "authors" %}'>
-          {% url "api_tag_list" "authors" %}</a> – {% trans "List of all authors" %}</li>
-        <li><a href='{% url "api_tag_list" "epochs" %}'>
-          {% url "api_tag_list" "epochs" %}</a> – {% trans "List of all epochs" %}</li>
-        <li><a href='{% url "api_tag_list" "genres" %}'>
-          {% url "api_tag_list" "genres" %}</a> – {% trans "List of all genres" %}</li>
-        <li><a href='{% url "api_tag_list" "kinds" %}'>
-          {% url "api_tag_list" "kinds" %}</a> – {% trans "List of all kinds" %}</li>
+        <li><a href='{% url "catalogue_api_tag_list" "author" %}'>
+          {% url "catalogue_api_tag_list" "author" %}</a> – {% trans "List of all authors" %}</li>
+        <li><a href='{% url "catalogue_api_tag_list" "epoch" %}'>
+          {% url "catalogue_api_tag_list" "epoch" %}</a> – {% trans "List of all epochs" %}</li>
+        <li><a href='{% url "catalogue_api_tag_list" "genre" %}'>
+          {% url "catalogue_api_tag_list" "genre" %}</a> – {% trans "List of all genres" %}</li>
+        <li><a href='{% url "catalogue_api_tag_list" "kind" %}'>
+          {% url "catalogue_api_tag_list" "kind" %}</a> – {% trans "List of all kinds" %}</li>
 
-        <li><a href='{% url "api_tag_list" "themes" %}'>
-          {% url "api_tag_list" "themes" %}</a> – {% trans "List of all themes" %}</li>
+        <li><a href='{% url "catalogue_api_tag_list" "theme" %}'>
+          {% url "catalogue_api_tag_list" "theme" %}</a> – {% trans "List of all themes" %}</li>
         <li><a href='{% url "catalogue_api_collections" %}'>
           {% url "catalogue_api_collections" %}</a> – {% trans "Collections" %}</li>
     </ul>
 
     <p>
       {% url "catalogue_api_book" "studnia-i-wahadlo" as e1 %}
-      {% url "api_tag" "authors" "edgar-allan-poe" as e2 %}
+      {% url "catalogue_api_tag" "author" "edgar-allan-poe" as e2 %}
       {% blocktrans %}
         Each element of those lists contains a link (in a "href") attibute
         which points to individual resource's details, i.e.:
index 40db3f4..e579e50 100644 (file)
@@ -1,18 +1,18 @@
 {
-    "url": "https://example.com/katalog/lektura/child.html#man-anchor", 
+    "url": "http://testserver/katalog/lektura/child.html#man-anchor",
     "text": "A fragment", 
     "book": {
         "kind": "", 
         "full_sort_key": "$child$2", 
         "author": "", 
-        "url": "https://example.com/katalog/lektura/child/", 
+        "url": "http://testserver/katalog/lektura/child/",
         "cover_color": "#000000", 
         "title": "Child", 
         "cover": "", 
         "liked": null, 
         "slug": "child", 
         "epoch": "", 
-        "href": "https://example.com/api/books/child/", 
+        "href": "http://testserver/api/books/child/",
         "genre": "Wiersz", 
         "simple_thumb": "", 
         "has_audio": false, 
@@ -21,8 +21,8 @@
     "anchor": "an-anchor", 
     "themes": [
         {
-            "url": "https://example.com/katalog/motyw/koniec/", 
-            "href": "https://example.com/api/themes/koniec/", 
+            "url": "http://testserver/katalog/motyw/koniec/",
+            "href": "http://testserver/api/themes/koniec/",
             "name": "Koniec", 
             "slug": "koniec"
         }
index 1733ba1..e464557 100644 (file)
@@ -1,24 +1,24 @@
 [
     {
-        "url": "https://example.com/katalog/lektura/child.html#man-anchor", 
+        "url": "http://testserver/katalog/lektura/child.html#man-anchor",
         "book": {
             "kind": "", 
             "full_sort_key": "$child$2", 
             "author": "", 
-            "url": "https://example.com/katalog/lektura/child/", 
+            "url": "http://testserver/katalog/lektura/child/",
             "cover_color": "#000000", 
             "title": "Child", 
             "cover": "", 
             "liked": null, 
             "slug": "child", 
             "epoch": "", 
-            "href": "https://example.com/api/books/child/", 
+            "href": "http://testserver/api/books/child/",
             "genre": "Wiersz", 
             "simple_thumb": "", 
             "has_audio": false, 
             "cover_thumb": ""
         }, 
         "anchor": "an-anchor", 
-        "href": "https://example.com/api/books/child/fragments/an-anchor/"
+        "href": "http://testserver/api/books/child/fragments/an-anchor/"
     }
 ]
index 4729beb..0232c90 100644 (file)
@@ -1,13 +1,13 @@
 [
     {
-        "url": "https://example.com/katalog/gatunek/sonet/", 
-        "href": "https://example.com/api/genres/sonet/", 
+        "url": "http://testserver/katalog/gatunek/sonet/",
+        "href": "http://testserver/api/genres/sonet/",
         "name": "Sonet", 
         "slug": "sonet"
     }, 
     {
-        "url": "https://example.com/katalog/gatunek/wiersz/", 
-        "href": "https://example.com/api/genres/wiersz/", 
+        "url": "http://testserver/katalog/gatunek/wiersz/",
+        "href": "http://testserver/api/genres/wiersz/",
         "name": "Wiersz", 
         "slug": "wiersz"
     }
index 7e393a2..19e63dd 100644 (file)
@@ -51,12 +51,6 @@ shelf_resource = auth_resource(handler=handlers.UserShelfHandler)
 
 like_resource = auth_resource(handler=handlers.UserLikeHandler)
 
-tag_list_resource = Resource(handler=handlers.TagsHandler)
-tag_resource = Resource(handler=handlers.TagDetailHandler)
-
-fragment_resource = Resource(handler=handlers.FragmentDetailHandler)
-fragment_list_resource = Resource(handler=handlers.FragmentsHandler)
-
 picture_resource = auth_resource(handler=handlers.PictureHandler)
 
 blog_resource = Resource(handler=handlers.BlogEntryHandler)
@@ -73,9 +67,6 @@ urlpatterns = [
 
     url(r'^$', TemplateView.as_view(template_name='api/main.html'), name='api'),
 
-    # These are the new ones.
-    url(r'^', include('catalogue.api.urls')),
-
     # info boxes (used by mobile app)
     url(r'book/(?P<book_id>\d*?)/info\.html$', catalogue.views.book_info),
     url(r'tag/(?P<tag_id>\d*?)/info\.html$', catalogue.views.tag_info),
@@ -88,12 +79,6 @@ urlpatterns = [
 
     url(r'^like/(?P<slug>[a-z0-9-]+)/$', like_resource, name='api_like'),
 
-    # objects details
-    url(r'^(?P<category>[a-z0-9-]+)/(?P<slug>[a-z0-9-]+)/$',
-        tag_resource, name="api_tag"),
-    url(r'^books/(?P<book>[a-z0-9-]+)/fragments/(?P<anchor>[a-z0-9-]+)/$',
-        fragment_resource, name="api_fragment"),
-
     # books by tags
     url(tags_re + r'books/' + paginate_re,
         book_list_resource, name='api_book_list'),
@@ -118,10 +103,5 @@ urlpatterns = [
 
     url(r'^blog/$', blog_resource),
 
-    # fragments by book, tags, themes
-    # this should be paged
-    url(r'^(?P<tags>(?:(?:[a-z0-9-]+/){2}){1,6})fragments/$', fragment_list_resource),
-
-    # tags by category
-    url(r'^(?P<category>[a-z0-9-]+)/$', tag_list_resource, name='api_tag_list'),
+    url(r'^', include('catalogue.api.urls')),
 ]
index 1705a51..7c792d5 100644 (file)
@@ -1,14 +1,14 @@
 from rest_framework import serializers
 from api.fields import AbsoluteURLField, LegacyMixin
-from catalogue.models import Book, Collection, Tag, BookMedia
+from catalogue.models import Book, Collection, Tag, BookMedia, Fragment
 from .fields import BookLiked, ThumbnailField
 
 
 class TagSerializer(serializers.ModelSerializer):
     url = AbsoluteURLField()
     href = AbsoluteURLField(
-        view_name='api_tag',
-        view_args=('category:category_plural', 'slug')
+        view_name='catalogue_api_tag',
+        view_args=('category', 'slug')
     )
 
     class Meta:
@@ -16,6 +16,14 @@ class TagSerializer(serializers.ModelSerializer):
         fields = ['url', 'href', 'name', 'slug']
 
 
+class TagDetailSerializer(serializers.ModelSerializer):
+    url = AbsoluteURLField()
+
+    class Meta:
+        model = Tag
+        fields = ['name', 'url', 'sort_key', 'description']
+
+
 class BookSerializer(LegacyMixin, serializers.ModelSerializer):
     author = serializers.CharField(source='author_unicode')
     kind = serializers.CharField(source='kind_unicode')
@@ -100,3 +108,23 @@ class CollectionSerializer(serializers.ModelSerializer):
     class Meta:
         model = Collection
         fields = ['url', 'books', 'description', 'title']
+
+
+class FragmentSerializer(serializers.ModelSerializer):
+    book = BookSerializer()
+    url = AbsoluteURLField()
+    href = AbsoluteURLField(source='get_api_url')
+
+    class Meta:
+        model = Fragment
+        fields = ['book', 'url', 'anchor', 'href']
+
+
+class FragmentDetailSerializer(serializers.ModelSerializer):
+    book = BookSerializer()
+    url = AbsoluteURLField()
+    themes = TagSerializer(many=True)
+
+    class Meta:
+        model = Fragment
+        fields = ['book', 'anchor', 'text', 'url', 'themes']
index 30f329a..7235267 100644 (file)
@@ -15,4 +15,11 @@ urlpatterns = [
     url(r'^books/(?P<slug>[^/]+)/$', views.BookDetail.as_view(), name='catalogue_api_book'),
 
     url(r'^epub/(?P<slug>[a-z0-9-]+)/$', views.EpubView.as_view(), name='catalogue_api_epub'),
+
+    url(r'^(?P<tags>(?:(?:[a-z0-9-]+/){2}){1,6})fragments/$', views.FragmentList.as_view()),
+    url(r'^books/(?P<book>[a-z0-9-]+)/fragments/(?P<anchor>[a-z0-9-]+)/$',
+        views.FragmentView.as_view(), name="catalogue_api_fragment"),
+
+    url(r'^(?P<category>[a-z]+)s/$', views.TagCategoryView.as_view(), name='catalogue_api_tag_list'),
+    url(r'^(?P<category>[a-z]+)s/(?P<slug>[a-z0-9-]+)/$', views.TagView.as_view(), name="catalogue_api_tag"),
 ]
index c20cccb..aba794d 100644 (file)
@@ -1,8 +1,9 @@
 from django.http import HttpResponse
-from rest_framework.generics import ListAPIView, RetrieveAPIView
+from rest_framework.generics import ListAPIView, RetrieveAPIView, get_object_or_404
 from paypal.permissions import IsSubscribed
+from api.handlers import read_tags
 from . import serializers
-from catalogue.models import Book, Collection
+from catalogue.models import Book, Collection, Tag, Fragment
 
 
 class CollectionList(ListAPIView):
@@ -29,3 +30,61 @@ class EpubView(RetrieveAPIView):
 
     def get(self, *args, **kwargs):
         return HttpResponse(self.get_object().get_media('epub'))
+
+
+class TagCategoryView(ListAPIView):
+    serializer_class = serializers.TagSerializer
+
+    def get_queryset(self):
+        category = self.kwargs['category']
+        tags = Tag.objects.filter(category=category).exclude(items=None).order_by('slug')
+        if self.request.query_params.get('book_only') == 'true':
+            tags = tags.filter(for_books=True)
+        if self.request.GET.get('picture_only') == 'true':
+            tags = filter(for_pictures=True)
+
+        after = self.request.query_params.get('after')
+        count = self.request.query_params.get('count')
+        if after:
+            tags = tags.filter(slug__gt=after)
+        if count:
+            tags = tags[:count]
+
+        return tags
+
+
+class TagView(RetrieveAPIView):
+    serializer_class = serializers.TagDetailSerializer
+
+    def get_object(self):
+        return get_object_or_404(
+            Tag,
+            category=self.kwargs['category'],
+            slug=self.kwargs['slug']
+        )
+
+
+class FragmentList(ListAPIView):
+    serializer_class = serializers.FragmentSerializer
+
+    def get_queryset(self):
+        try:
+            tags, ancestors = read_tags(
+                self.kwargs['tags'],
+                self.request,
+                allowed={'author', 'epoch', 'kind', 'genre', 'book', 'theme'}
+            )
+        except ValueError:
+            raise Http404
+        return Fragment.tagged.with_all(tags).select_related('book')
+
+
+class FragmentView(RetrieveAPIView):
+    serializer_class = serializers.FragmentDetailSerializer
+
+    def get_object(self):
+        return get_object_or_404(
+            Fragment,
+            book__slug=self.kwargs['book'],
+            anchor=self.kwargs['anchor']
+        )
index a3dbdea..dc159a5 100644 (file)
@@ -35,10 +35,17 @@ class Fragment(models.Model):
     def get_absolute_url(self):
         return '%s#m%s' % (reverse('book_text', args=[self.book.slug]), self.anchor)
 
+    def get_api_url(self):
+        return reverse('catalogue_api_fragment', args=[self.book.slug, self.anchor])
+
     def get_short_text(self):
         """Returns short version of the fragment."""
         return self.short_text if self.short_text else self.text
 
+    @property
+    def themes(self):
+        return self.tags.filter(category='theme')
+
     def flush_includes(self, languages=True):
         if not languages:
             return