'liked' field in api book lists + fix for tests
authorJan Szejko <janek37@gmail.com>
Mon, 27 Aug 2018 10:47:51 +0000 (12:47 +0200)
committerJan Szejko <janek37@gmail.com>
Mon, 27 Aug 2018 10:47:51 +0000 (12:47 +0200)
src/api/handlers.py
src/api/urls.py

index dc84e88..9fb2886 100644 (file)
@@ -164,7 +164,7 @@ class BookDetails(object):
 
     @classmethod
     def cover_color(cls, book):
-        return WLCover.epoch_colors.get(book.extra_info['epoch'], '#000000')
+        return WLCover.epoch_colors.get(book.extra_info.get('epoch'), '#000000')
 
 
 class BookDetailHandler(BaseHandler, BookDetails):
@@ -276,9 +276,23 @@ class AnonymousBooksHandler(AnonymousBaseHandler, BookDetails):
 class BooksHandler(BookDetailHandler):
     allowed_methods = ('GET', 'POST')
     model = Book
-    fields = book_list_fields
+    fields = book_list_fields + ['liked']
     anonymous = AnonymousBooksHandler
 
+    # hack, because piston is stupid
+    @classmethod
+    def liked(cls, book):
+        return book.liked
+
+    def read(self, request, **kwargs):
+        books = AnonymousBooksHandler().read(request, **kwargs)
+        likes = set(Book.tagged.with_any(request.user.tag_set.all()).values_list('id', flat=True))
+
+        new_books = [
+            BookProxy(book).set('liked', book.id in likes)
+            for book in books]
+        return QuerySetProxy(new_books)
+
     def create(self, request, *args, **kwargs):
         if not request.user.has_perm('catalogue.add_book'):
             return rc.FORBIDDEN
@@ -312,15 +326,16 @@ class BookProxy(models.Model):
     class Meta:
         managed = False
 
-    def __init__(self, book, key):
+    def __init__(self, book, key=None):
         self.book = book
         self.key = key
 
+    def set(self, attr, value):
+        self.__setattr__(attr, value)
+        return self
+
     def __getattr__(self, item):
-        if item not in ('book', 'key'):
-            return self.book.__getattribute__(item)
-        else:
-            return self.__getattribute__(item)
+        return self.book.__getattribute__(item)
 
 
 class QuerySetProxy(models.QuerySet):
@@ -331,7 +346,7 @@ class QuerySetProxy(models.QuerySet):
         return iter(self.list)
 
 
-class FilterBooksHandler(AnonymousBooksHandler):
+class AnonFilterBooksHandler(AnonymousBooksHandler):
     fields = book_list_fields + ['key']
 
     def parse_bool(self, s):
@@ -402,6 +417,23 @@ class FilterBooksHandler(AnonymousBooksHandler):
         return QuerySetProxy(filtered_books)
 
 
+class FilterBooksHandler(BooksHandler):
+    anonymous = AnonFilterBooksHandler
+    fields = book_list_fields + ['key', 'liked']
+
+    # hack, because piston is stupid
+    @classmethod
+    def liked(cls, book):
+        return book.liked
+
+    def read(self, request):
+        qsp = AnonFilterBooksHandler().read(request)
+        likes = set(Book.tagged.with_any(request.user.tag_set.all()).values_list('id', flat=True))
+        for book in qsp.list:
+            book.set('liked', book.id in likes)
+        return qsp
+
+
 class BookPreviewHandler(BookDetailHandler):
     fields = BookDetailHandler.fields + ['slug']
 
@@ -706,7 +738,7 @@ class UserDataHandler(BaseHandler):
 
 
 class UserShelfHandler(BookDetailHandler):
-    fields = book_list_fields
+    fields = book_list_fields + ['liked']
 
     def parse_bool(self, s):
         if s in ('true', 'false'):
@@ -714,9 +746,15 @@ class UserShelfHandler(BookDetailHandler):
         else:
             return None
 
+    # hack, because piston is stupid
+    @classmethod
+    def liked(cls, book):
+        return book.liked
+
     def read(self, request, state):
         if not request.user.is_authenticated():
             return rc.FORBIDDEN
+        likes = set(Book.tagged.with_any(request.user.tag_set.all()).values_list('id', flat=True))
         if state not in ('reading', 'complete', 'likes'):
             return rc.NOT_FOUND
         after = request.GET.get('after')
@@ -731,7 +769,10 @@ class UserShelfHandler(BookDetailHandler):
             books = books.filter(slug__gt=after)
         if count:
             books = books[:count]
-        return books
+        new_books = []
+        for book in books:
+            new_books.append(BookProxy(book).set('liked', book.id in likes))
+        return QuerySetProxy(new_books)
 
 
 class UserLikeHandler(BaseHandler):
index 781766f..9cec9b8 100644 (file)
@@ -16,7 +16,27 @@ from api.piston_patch import oauth_user_auth
 auth = OAuthAuthentication(realm="Wolne Lektury")
 
 
+class DjangoAuthentication(object):
+    """
+    Authentication handler that always returns
+    True, so no authentication is needed, nor
+    initiated (`challenge` is missing.)
+    """
+    def is_authenticated(self, request):
+        return request.user.is_authenticated()
+
+    def challenge(self):
+        from django.http import HttpResponse
+        resp = HttpResponse("Authorization Required")
+        resp.status_code = 401
+        return resp
+
+
 def auth_resource(handler):
+    from django.conf import settings
+    if settings.DEBUG:
+        django_auth = DjangoAuthentication()
+        return CsrfExemptResource(handler=handler, authentication=django_auth)
     return CsrfExemptResource(handler=handler, authentication=auth)
 
 
@@ -24,7 +44,7 @@ book_list_resource = auth_resource(handler=handlers.BooksHandler)
 ebook_list_resource = Resource(handler=handlers.EBooksHandler)
 # book_list_resource = Resource(handler=handlers.BooksHandler)
 book_resource = Resource(handler=handlers.BookDetailHandler)
-filter_book_resource = Resource(handler=handlers.FilterBooksHandler)
+filter_book_resource = auth_resource(handler=handlers.FilterBooksHandler)
 epub_resource = auth_resource(handler=handlers.EpubHandler)
 
 preview_resource = Resource(handler=handlers.BookPreviewHandler)