Merge branch 'production' into pretty
[wolnelektury.git] / apps / api / handlers.py
index 9bb8900..fddff74 100644 (file)
@@ -7,6 +7,7 @@ import json
 
 from django.conf import settings
 from django.contrib.sites.models import Site
 
 from django.conf import settings
 from django.contrib.sites.models import Site
+from django.core.cache import get_cache
 from django.core.urlresolvers import reverse
 from piston.handler import AnonymousBaseHandler, BaseHandler
 from piston.utils import rc
 from django.core.urlresolvers import reverse
 from piston.handler import AnonymousBaseHandler, BaseHandler
 from piston.utils import rc
@@ -15,7 +16,10 @@ from api.helpers import timestamp
 from api.models import Deleted
 from catalogue.forms import BookImportForm
 from catalogue.models import Book, Tag, BookMedia, Fragment
 from api.models import Deleted
 from catalogue.forms import BookImportForm
 from catalogue.models import Book, Tag, BookMedia, Fragment
+from picture.models import Picture
+from picture.forms import PictureImportForm
 
 
+from stats.utils import piwik_track
 
 API_BASE = WL_BASE = MEDIA_BASE = 'http://' + Site.objects.get_current().domain
 
 
 API_BASE = WL_BASE = MEDIA_BASE = 'http://' + Site.objects.get_current().domain
 
@@ -92,13 +96,12 @@ class BookDetailHandler(BaseHandler):
 
     """
     allowed_methods = ['GET']
 
     """
     allowed_methods = ['GET']
-    fields = ['title', 'parent',
-        'xml', 'html', 'pdf', 'epub', 'txt',
+    fields = ['title', 'parent'] + Book.formats + [
         'media', 'url'] + category_singular.keys()
 
         'media', 'url'] + category_singular.keys()
 
+    @piwik_track
     def read(self, request, slug):
     def read(self, request, slug):
-        """ Returns details of a book, identified by a slug. """
-
+        """ Returns details of a book, identified by a slug and lang. """
         try:
             return Book.objects.get(slug=slug)
         except Book.DoesNotExist:
         try:
             return Book.objects.get(slug=slug)
         except Book.DoesNotExist:
@@ -129,6 +132,7 @@ class AnonymousBooksHandler(AnonymousBaseHandler):
 
         return WL_BASE + book.get_absolute_url()
 
 
         return WL_BASE + book.get_absolute_url()
 
+    @piwik_track
     def read(self, request, tags, top_level=False):
         """ Lists all books with given tags.
 
     def read(self, request, tags, top_level=False):
         """ Lists all books with given tags.
 
@@ -200,7 +204,7 @@ def _file_getter(format):
         else:
             return ''
     return get_file
         else:
             return ''
     return get_file
-for format in ('xml', 'txt', 'html', 'epub', 'pdf'):
+for format in Book.formats:
     setattr(BooksHandler, format, _file_getter(format))
 
 
     setattr(BooksHandler, format, _file_getter(format))
 
 
@@ -209,6 +213,7 @@ class TagDetailHandler(BaseHandler):
 
     fields = ['name', 'sort_key', 'description']
 
 
     fields = ['name', 'sort_key', 'description']
 
+    @piwik_track
     def read(self, request, category, slug):
         """ Returns details of a tag, identified by category and slug. """
 
     def read(self, request, category, slug):
         """ Returns details of a tag, identified by category and slug. """
 
@@ -234,6 +239,7 @@ class TagsHandler(BaseHandler):
     model = Tag
     fields = ['name', 'href']
 
     model = Tag
     fields = ['name', 'href']
 
+    @piwik_track
     def read(self, request, category):
         """ Lists all tags in the category (eg. all themes). """
 
     def read(self, request, category):
         """ Lists all tags in the category (eg. all themes). """
 
@@ -242,9 +248,8 @@ class TagsHandler(BaseHandler):
         except KeyError, e:
             return rc.NOT_FOUND
 
         except KeyError, e:
             return rc.NOT_FOUND
 
-        tags = Tag.objects.filter(category=category_sng)
-        tags = [t for t in tags if t.get_count() > 0]
-        if tags:
+        tags = Tag.objects.filter(category=category_sng).exclude(book_count=0)
+        if tags.exists():
             return tags
         else:
             return rc.NOT_FOUND
             return tags
         else:
             return rc.NOT_FOUND
@@ -260,9 +265,9 @@ class TagsHandler(BaseHandler):
 class FragmentDetailHandler(BaseHandler):
     fields = ['book', 'anchor', 'text', 'url', 'themes']
 
 class FragmentDetailHandler(BaseHandler):
     fields = ['book', 'anchor', 'text', 'url', 'themes']
 
+    @piwik_track
     def read(self, request, slug, anchor):
         """ Returns details of a fragment, identified by book slug and anchor. """
     def read(self, request, slug, anchor):
         """ Returns details of a fragment, identified by book slug and anchor. """
-
         try:
             return Fragment.objects.get(book__slug=slug, anchor=anchor)
         except Fragment.DoesNotExist:
         try:
             return Fragment.objects.get(book__slug=slug, anchor=anchor)
         except Fragment.DoesNotExist:
@@ -282,6 +287,7 @@ class FragmentsHandler(BaseHandler):
 
     categories = set(['author', 'epoch', 'kind', 'genre', 'book', 'theme'])
 
 
     categories = set(['author', 'epoch', 'kind', 'genre', 'book', 'theme'])
 
+    @piwik_track
     def read(self, request, tags):
         """ Lists all fragments with given book, tags, themes.
 
     def read(self, request, tags):
         """ Lists all fragments with given book, tags, themes.
 
@@ -300,7 +306,8 @@ class FragmentsHandler(BaseHandler):
     def href(cls, fragment):
         """ Returns URI in the API for the fragment. """
 
     def href(cls, fragment):
         """ Returns URI in the API for the fragment. """
 
-        return API_BASE + reverse("api_fragment", args=[fragment.book.slug, fragment.anchor])
+        return API_BASE + reverse("api_fragment", 
+            args=[fragment.book.slug, fragment.anchor])
 
     @classmethod
     def url(cls, fragment):
 
     @classmethod
     def url(cls, fragment):
@@ -346,16 +353,15 @@ class CatalogueHandler(BaseHandler):
 
     @staticmethod
     def book_dict(book, fields=None):
 
     @staticmethod
     def book_dict(book, fields=None):
-        all_fields = ('url', 'title', 'description',
+        all_fields = ['url', 'title', 'description',
                       'gazeta_link', 'wiki_link',
                       'gazeta_link', 'wiki_link',
-                      'xml', 'epub', 'txt', 'pdf', 'html',
-                      'mp3', 'ogg', 'daisy',
+                      ] + Book.formats + BookMedia.formats + [
                       'parent', 'parent_number',
                       'tags',
                       'license', 'license_description', 'source_name',
                       'technical_editors', 'editors',
                       'author', 'sort_key',
                       'parent', 'parent_number',
                       'tags',
                       'license', 'license_description', 'source_name',
                       'technical_editors', 'editors',
                       'author', 'sort_key',
-                     )
+                     ]
         if fields:
             fields = (f for f in fields if f in all_fields)
         else:
         if fields:
             fields = (f for f in fields if f in all_fields)
         else:
@@ -366,7 +372,7 @@ class CatalogueHandler(BaseHandler):
         obj = {}
         for field in fields:
 
         obj = {}
         for field in fields:
 
-            if field in ('xml', 'epub', 'txt', 'pdf', 'html'):
+            if field in Book.formats:
                 f = getattr(book, field+'_file')
                 if f:
                     obj[field] = {
                 f = getattr(book, field+'_file')
                 if f:
                     obj[field] = {
@@ -374,7 +380,7 @@ class CatalogueHandler(BaseHandler):
                         'size': f.size,
                     }
 
                         'size': f.size,
                     }
 
-            elif field in ('mp3', 'ogg', 'daisy'):
+            elif field in BookMedia.formats:
                 media = []
                 for m in book.media.filter(type=field):
                     media.append({
                 media = []
                 for m in book.media.filter(type=field):
                     media.append({
@@ -503,7 +509,7 @@ class CatalogueHandler(BaseHandler):
                     changed_at__gte=since,
                     changed_at__lt=until):
             # only serve non-empty tags
                     changed_at__gte=since,
                     changed_at__lt=until):
             # only serve non-empty tags
-            if tag.get_count():
+            if tag.book_count:
                 tag_d = cls.tag_dict(tag, fields)
                 updated.append(tag_d)
             elif tag.created_at < since:
                 tag_d = cls.tag_dict(tag, fields)
                 updated.append(tag_d)
             elif tag.created_at < since:
@@ -526,6 +532,16 @@ class CatalogueHandler(BaseHandler):
     def changes(cls, request=None, since=0, until=None, book_fields=None,
                 tag_fields=None, tag_categories=None):
         until = cls.until(until)
     def changes(cls, request=None, since=0, until=None, book_fields=None,
                 tag_fields=None, tag_categories=None):
         until = cls.until(until)
+        since = int(since)
+
+        if not since:
+            cache = get_cache('api')
+            key = hash((book_fields, tag_fields, tag_categories,
+                    tuple(sorted(request.GET.items()))
+                  ))
+            value = cache.get(key)
+            if value is not None:
+                return value
 
         changes = {
             'time_checked': timestamp(until)
 
         changes = {
             'time_checked': timestamp(until)
@@ -541,12 +557,17 @@ class CatalogueHandler(BaseHandler):
                 if field == 'time_checked':
                     continue
                 changes.setdefault(field, {})[model] = changes_by_type[model][field]
                 if field == 'time_checked':
                     continue
                 changes.setdefault(field, {})[model] = changes_by_type[model][field]
+
+        if not since:
+            cache.set(key, changes)
+
         return changes
 
 
 class BookChangesHandler(CatalogueHandler):
     allowed_methods = ('GET',)
 
         return changes
 
 
 class BookChangesHandler(CatalogueHandler):
     allowed_methods = ('GET',)
 
+    @piwik_track
     def read(self, request, since):
         return self.book_changes(request, since)
 
     def read(self, request, since):
         return self.book_changes(request, since)
 
@@ -554,6 +575,7 @@ class BookChangesHandler(CatalogueHandler):
 class TagChangesHandler(CatalogueHandler):
     allowed_methods = ('GET',)
 
 class TagChangesHandler(CatalogueHandler):
     allowed_methods = ('GET',)
 
+    @piwik_track
     def read(self, request, since):
         return self.tag_changes(request, since)
 
     def read(self, request, since):
         return self.tag_changes(request, since)
 
@@ -561,5 +583,24 @@ class TagChangesHandler(CatalogueHandler):
 class ChangesHandler(CatalogueHandler):
     allowed_methods = ('GET',)
 
 class ChangesHandler(CatalogueHandler):
     allowed_methods = ('GET',)
 
+    @piwik_track
     def read(self, request, since):
         return self.changes(request, since)
     def read(self, request, since):
         return self.changes(request, since)
+
+
+class PictureHandler(BaseHandler):
+    model = Picture
+    fields = ('slug', 'title')
+    allowed_methods = ('POST',)
+
+    def create(self, request):
+        if not request.user.has_perm('picture.add_picture'):
+            return rc.FORBIDDEN
+
+        data = json.loads(request.POST.get('data'))
+        form = PictureImportForm(data)
+        if form.is_valid():
+            form.save()
+            return rc.CREATED
+        else:
+            return rc.NOT_FOUND