Add basic login.
[wolnelektury.git] / src / opds / views.py
index 8e929c6..38f34ef 100644 (file)
@@ -1,5 +1,5 @@
-# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+# This file is part of Wolne Lektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Wolne Lektury. See NOTICE for more information.
 #
 from functools import reduce
 import os.path
@@ -8,7 +8,7 @@ from urllib.parse import urljoin
 from django.contrib.syndication.views import Feed
 from django.shortcuts import get_object_or_404
 from django.urls import reverse
-from django.utils.feedgenerator import Atom1Feed
+from django.utils.feedgenerator import Atom1Feed, Enclosure
 from django.conf import settings
 from django.http import Http404
 from django.contrib.sites.models import Site
@@ -16,8 +16,8 @@ from django.utils.functional import lazy
 
 from basicauth import logged_in_or_basicauth, factory_decorator
 from catalogue.models import Book, Tag
+from search.utils import UnaccentSearchQuery, UnaccentSearchVector
 
-from search.views import Search
 import operator
 import logging
 import re
@@ -145,23 +145,32 @@ class OPDSFeed(Atom1Feed):
 
         # Enclosure as OPDS Acquisition Link
         for enc in item.get('enclosures', []):
-            handler.addQuickElement(
-                "link", '',
+            handler.startElement(
+                "link",
                 {
                     "rel": "http://opds-spec.org/acquisition",
                     "href": enc.url,
-                    "length": enc.length,
+                    "length": str(enc.length),
                     "type": enc.mime_type,
                 })
-            # add a "red book" icon
-            handler.addQuickElement(
-                "link", '',
-                {
-                    "rel": "http://opds-spec.org/thumbnail",
-                    "href": self._book_img,
-                    "length": self._book_img_size,
-                    "type": "image/png",
+            if hasattr(enc, 'indirect'):
+                NS = 'http://opds-spec.org/2010/catalog'
+                handler.startPrefixMapping('opds', NS)
+                handler.startElementNS((NS, 'indirectAcquisition'), 'opds:indirectAcquisition', {
+                    (None, 'type'): enc.indirect,
                 })
+                handler.endElementNS((NS, 'indirectAcquisition'), 'opds:indirectAcquisition')
+                handler.endPrefixMapping('opds')
+            handler.endElement('link')
+        # add a "red book" icon
+        handler.addQuickElement(
+            "link", '',
+            {
+                "rel": "http://opds-spec.org/thumbnail",
+                "href": self._book_img,
+                "length": self._book_img_size,
+                "type": "image/png",
+            })
 
         # Categories.
         for cat in item['categories']:
@@ -175,7 +184,6 @@ class OPDSFeed(Atom1Feed):
 class AcquisitionFeed(Feed):
     feed_type = OPDSFeed
     link = 'http://www.wolnelektury.pl/'
-    item_enclosure_mime_type = "application/epub+zip"
     author_name = "Wolne Lektury"
     author_link = "http://www.wolnelektury.pl/"
 
@@ -200,11 +208,23 @@ class AcquisitionFeed(Feed):
         except AttributeError:
             return ''
 
-    def item_enclosure_url(self, book):
-        return full_url(book.epub_url()) if book.epub_file else None
-
-    def item_enclosure_length(self, book):
-        return book.epub_file.size if book.epub_file else None
+    def item_enclosures(self, book):
+        enc = []
+        if book.epub_file:
+            enc.append(Enclosure(
+                url=full_url(book.epub_url()),
+                length=book.epub_file.size,
+                mime_type="application/epub+zip"
+            ))
+        if book.has_mp3_file():
+            e = Enclosure(
+                url=full_url(reverse('download_zip_mp3', args=[book.slug])),
+                length=sum(bm.file.size for bm in book.get_media('mp3')),
+                mime_type="application/zip"
+            )
+            e.indirect = 'audio/mpeg'
+            enc.append(e)
+        return enc
 
 
 @piwik_track
@@ -277,7 +297,9 @@ class ByTagFeed(AcquisitionFeed):
         return get_object_or_404(Tag, category=category, slug=slug)
 
     def items(self, tag):
-        return Book.tagged_top_level([tag])
+        qs = Book.tagged_top_level([tag])
+        qs = qs.filter(preview=False, findable=True)
+        return qs
 
 
 @factory_decorator(logged_in_or_basicauth())
@@ -350,15 +372,6 @@ class SearchFeed(AcquisitionFeed):
         'text': (10, 11),
         }
 
-    PARAMS_TO_FIELDS = {
-        'author': 'authors',
-        'translator': 'translators',
-        #        'title': 'title',
-        'categories': 'tag_name_pl',
-        'description': 'text',
-        #        'text': 'text',
-        }
-
     ATOM_PLACEHOLDER = re.compile(r"^{(atom|opds):\w+}$")
 
     def get_object(self, request):
@@ -413,30 +426,33 @@ class SearchFeed(AcquisitionFeed):
             # query is set above.
             log.debug("Inline query = [%s], criteria: %s" % (query, criteria))
 
-        srch = Search()
-
-        book_hit_filter = srch.index.Q(book_id__any=True)
-        filters = [book_hit_filter] + [srch.index.Q(
-            **{self.PARAMS_TO_FIELDS.get(cn, cn): criteria[cn]}
-            ) for cn in self.MATCHES.keys() if cn in criteria
-            if criteria[cn]]
-
+        books = Book.objects.filter(findable=True, preview=False).annotate(
+            search_vector=UnaccentSearchVector('title')
+        )
         if query:
-            q = srch.index.query(
-                reduce(
-                    operator.or_,
-                    [srch.index.Q(**{self.PARAMS_TO_FIELDS.get(cn, cn): query}) for cn in self.MATCHES.keys()],
-                    srch.index.Q()))
-        else:
-            q = srch.index.query(srch.index.Q())
-
-        q = srch.apply_filters(q, filters).field_limit(score=True, fields=['book_id'])
-        results = q.execute()
-
-        book_scores = dict([(r['book_id'], r['score']) for r in results])
-        books = Book.objects.filter(findable=True, id__in=set([r['book_id'] for r in results]))
-        books = list(books)
-        books.sort(reverse=True, key=lambda book: book_scores[book.id])
+            squery = UnaccentSearchQuery(query, config=settings.SEARCH_CONFIG)
+            books = books.filter(search_vector=squery)
+        if criteria['author']:
+            authors = Tag.objects.filter(category='author').annotate(
+                search_vector=UnaccentSearchVector('name_pl')
+            ).filter(search_vector=UnaccentSearchQuery(criteria['author'], config=settings.SEARCH_CONFIG))
+            books = books.filter(tag_relations__tag__in=authors)
+        if criteria['categories']:
+            tags = Tag.objects.filter(category__in=('genre', 'kind', 'epoch')).annotate(
+                search_vector=UnaccentSearchVector('name_pl')
+            ).filter(search_vector=UnaccentSearchQuery(criteria['categories'], config=settings.SEARCH_CONFIG))
+            books = books.filter(tag_relations__tag__in=tags)
+        if criteria['translator']:
+            # TODO
+            pass
+        if criteria['title']:
+            books = books.filter(
+                search_vector=UnaccentSearchQuery(criteria['title'], config=settings.SEARCH_CONFIG)
+            )
+
+        books = books.exclude(ancestor__in=books)
+
+        books = books.order_by('popularity__count')
         return books
 
     def get_link(self, query):