user lists in sync
[wolnelektury.git] / src / social / api / views.py
index c58efd1..36c40e1 100644 (file)
@@ -4,7 +4,8 @@
 from datetime import datetime
 from pytz import utc
 from django.http import Http404
 from datetime import datetime
 from pytz import utc
 from django.http import Http404
-from rest_framework.generics import ListAPIView, ListCreateAPIView, RetrieveAPIView, RetrieveUpdateAPIView, RetrieveUpdateDestroyAPIView, DestroyAPIView, get_object_or_404
+from django.utils.timezone import now
+from rest_framework.generics import ListAPIView, ListCreateAPIView, RetrieveAPIView, RetrieveUpdateAPIView, RetrieveUpdateDestroyAPIView, get_object_or_404
 from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly
 from rest_framework.response import Response
 from rest_framework import serializers
 from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly
 from rest_framework.response import Response
 from rest_framework import serializers
@@ -15,7 +16,6 @@ from catalogue.api.helpers import order_books, books_after
 from catalogue.api.serializers import BookSerializer
 from catalogue.models import Book
 import catalogue.models
 from catalogue.api.serializers import BookSerializer
 from catalogue.models import Book
 import catalogue.models
-from social.utils import likes, get_set
 from social.views import get_sets_for_book_ids
 from social import models
 
 from social.views import get_sets_for_book_ids
 from social import models
 
@@ -32,9 +32,9 @@ class LikeView(APIView):
         book = get_object_or_404(Book, slug=slug)
         action = request.query_params.get('action', 'like')
         if action == 'like':
         book = get_object_or_404(Book, slug=slug)
         action = request.query_params.get('action', 'like')
         if action == 'like':
-            book.like(request.user)
+            models.UserList.like(request.user, book)
         elif action == 'unlike':
         elif action == 'unlike':
-            book.unlike(request.user)
+            models.UserList.unlike(request.user, book)
         return Response({})
 
 
         return Response({})
 
 
@@ -48,12 +48,12 @@ class LikeView2(APIView):
 
     def put(self, request, slug):
         book = get_object_or_404(Book, slug=slug)
 
     def put(self, request, slug):
         book = get_object_or_404(Book, slug=slug)
-        book.like(request.user)
+        models.UserList.like(request.user, book)
         return Response({"likes": likes(request.user, book)})
 
     def delete(self, request, slug):
         book = get_object_or_404(Book, slug=slug)
         return Response({"likes": likes(request.user, book)})
 
     def delete(self, request, slug):
         book = get_object_or_404(Book, slug=slug)
-        book.unlike(request.user)
+        models.UserList.unlike(request.user, book)
         return Response({"likes": likes(request.user, book)})
 
 
         return Response({"likes": likes(request.user, book)})
 
 
@@ -77,49 +77,50 @@ class MyLikesView(APIView):
     permission_classes = [IsAuthenticated]
 
     def get(self, request):
     permission_classes = [IsAuthenticated]
 
     def get(self, request):
-        ids = catalogue.models.tag.TagRelation.objects.filter(tag__user=request.user).values_list('object_id', flat=True).distinct()
-        books = Book.objects.filter(id__in=ids)
-        books = {b.id: b.slug for b in books}
-        res = get_sets_for_book_ids(ids, request.user)
-        res = {books[bid]: v for bid, v in res.items()}
-
-        res = list(books.values())
-        res.sort()
-        return Response(res)
+        ul = models.UserList.get_favorites_list(request.user)
+        if ul is None:
+            return Response([])
+        return Response(
+            ul.userlistitem_set.exclude(deleted=True).exclude(book=None).values_list('book__slug', flat=True)
+        )
 
 
 
 
-class TaggedBooksField(serializers.Field):
+class UserListItemsField(serializers.Field):
     def to_representation(self, value):
     def to_representation(self, value):
-        return catalogue.models.Book.tagged.with_all([value]).values_list('slug', flat=True)
+        return value.userlistitem_set.exclude(deleted=True).exclude(book=None).values_list('book__slug', flat=True)
 
     def to_internal_value(self, value):
         return {'books': catalogue.models.Book.objects.filter(slug__in=value)}
 
 
 class UserListSerializer(serializers.ModelSerializer):
 
     def to_internal_value(self, value):
         return {'books': catalogue.models.Book.objects.filter(slug__in=value)}
 
 
 class UserListSerializer(serializers.ModelSerializer):
-    books = TaggedBooksField(source='*')
+    books = UserListItemsField(source='*')
 
     class Meta:
 
     class Meta:
-        model = catalogue.models.Tag
+        model = models.UserList
         fields = ['name', 'slug', 'books']
         read_only_fields = ['slug']
 
     def create(self, validated_data):
         fields = ['name', 'slug', 'books']
         read_only_fields = ['slug']
 
     def create(self, validated_data):
-        instance = get_set(validated_data['user'], validated_data['name'])
-        catalogue.models.tag.TagRelation.objects.filter(tag=instance).delete()
+        instance = models.UserList.get_by_name(
+            validated_data['user'],
+            validated_data['name'],
+            create=True
+        )
+        instance.userlistitem_set.all().delete()
         for book in validated_data['books']:
         for book in validated_data['books']:
-            catalogue.models.Tag.objects.add_tag(book, instance)
+            instance.append(book)
         return instance
 
     def update(self, instance, validated_data):
         return instance
 
     def update(self, instance, validated_data):
-        catalogue.models.tag.TagRelation.objects.filter(tag=instance).delete()
+        instance.userlistitem_set.all().delete()
         for book in validated_data['books']:
         for book in validated_data['books']:
-            catalogue.models.Tag.objects.add_tag(book, instance)
+            instance.append(instance)
         return instance
 
 class UserListBooksSerializer(UserListSerializer):
     class Meta:
         return instance
 
 class UserListBooksSerializer(UserListSerializer):
     class Meta:
-        model = catalogue.models.Tag
+        model = models.UserList
         fields = ['books']
 
 
         fields = ['books']
 
 
@@ -130,7 +131,11 @@ class ListsView(ListCreateAPIView):
     serializer_class = UserListSerializer
 
     def get_queryset(self):
     serializer_class = UserListSerializer
 
     def get_queryset(self):
-        return catalogue.models.Tag.objects.filter(user=self.request.user).exclude(name='')
+        return models.UserList.objects.filter(
+            user=self.request.user,
+            favorites=False,
+            deleted=False
+        )
 
     def perform_create(self, serializer):
         serializer.save(user=self.request.user)
 
     def perform_create(self, serializer):
         serializer.save(user=self.request.user)
@@ -143,7 +148,10 @@ class ListView(RetrieveUpdateDestroyAPIView):
     serializer_class = UserListSerializer
 
     def get_object(self):
     serializer_class = UserListSerializer
 
     def get_object(self):
-        return get_object_or_404(catalogue.models.Tag, slug=self.kwargs['slug'], user=self.request.user)
+        return get_object_or_404(
+            models.UserList,
+            slug=self.kwargs['slug'],
+            user=self.request.user)
 
     def perform_update(self, serializer):
         serializer.save(user=self.request.user)
 
     def perform_update(self, serializer):
         serializer.save(user=self.request.user)
@@ -153,18 +161,25 @@ class ListView(RetrieveUpdateDestroyAPIView):
         serializer.is_valid(raise_exception=True)
         instance = self.get_object()
         for book in serializer.validated_data['books']:
         serializer.is_valid(raise_exception=True)
         instance = self.get_object()
         for book in serializer.validated_data['books']:
-            catalogue.models.Tag.objects.add_tag(book, instance)
+            instance.append(book)
         return Response(self.get_serializer(instance).data)
 
         return Response(self.get_serializer(instance).data)
 
+    def perform_destroy(self, instance):
+        instance.update(
+            deleted=True,
+            updated_at=now()
+        )
+
 
 @never_cache
 class ListItemView(APIView):
     permission_classes = [IsAuthenticated]
 
     def delete(self, request, slug, book):
 
 @never_cache
 class ListItemView(APIView):
     permission_classes = [IsAuthenticated]
 
     def delete(self, request, slug, book):
-        instance = get_object_or_404(catalogue.models.Tag, slug=slug, user=self.request.user)
+        instance = get_object_or_404(
+            models.UserList, slug=slug, user=self.request.user)
         book = get_object_or_404(catalogue.models.Book, slug=book)
         book = get_object_or_404(catalogue.models.Book, slug=book)
-        catalogue.models.Tag.objects.remove_tag(book, instance)
+        instance.remove(book=book)
         return Response(UserListSerializer(instance).data)
 
 
         return Response(UserListSerializer(instance).data)
 
 
@@ -182,7 +197,7 @@ class ShelfView(ListAPIView):
         after = self.request.query_params.get('after')
         count = int(self.request.query_params.get('count', 50))
         if state == 'likes':
         after = self.request.query_params.get('after')
         count = int(self.request.query_params.get('count', 50))
         if state == 'likes':
-            books = Book.tagged.with_any(self.request.user.tag_set.all())
+            books = Book.objects.filter(userlistitem__list__user=self.request.user)
         else:
             ids = BookUserData.objects.filter(user=self.request.user, complete=state == 'complete')\
                 .values_list('book_id', flat=True)
         else:
             ids = BookUserData.objects.filter(user=self.request.user, complete=state == 'complete')\
                 .values_list('book_id', flat=True)
@@ -296,6 +311,7 @@ class SyncSerializer(serializers.Serializer):
         return ret
 
 
         return ret
 
 
+@never_cache
 class SyncView(ListAPIView):
     permission_classes = [IsAuthenticated]
     serializer_class = SyncSerializer
 class SyncView(ListAPIView):
     permission_classes = [IsAuthenticated]
     serializer_class = SyncSerializer
@@ -320,6 +336,20 @@ class SyncView(ListAPIView):
                     p, context={'request': self.request}
                 ) if not p.deleted else None
             })
                     p, context={'request': self.request}
                 ) if not p.deleted else None
             })
+
+        for p in models.UserList.objects.filter(
+                user=self.request.user,
+                updated_at__gt=timestamp).order_by('updated_at'):
+            data.append({
+                'timestamp': p.updated_at.timestamp(),
+                'type': 'user-list',
+                'id': p.slug,
+                'object': UserListSerializer(
+                    p, context={'request': self.request}
+                ) if not p.deleted else None
+            })
+
+        data.sort(key=lambda x: x['timestamp'])
         return data
 
     def post(self, request):
         return data
 
     def post(self, request):
@@ -329,6 +359,9 @@ class SyncView(ListAPIView):
             ser.is_valid(raise_exception=True)
             d = ser.validated_data
             if d['type'] == 'progress':
             ser.is_valid(raise_exception=True)
             d = ser.validated_data
             if d['type'] == 'progress':
+                if d['object'] is not None:
+                    objser = ProgressSerializer(data=d['object'])
+                    objser.is_valid(raise_exception=True)
                 models.Progress.sync(
                     user=request.user,
                     slug=d['id'],
                 models.Progress.sync(
                     user=request.user,
                     slug=d['id'],