Commit a3649c2f authored by Dmitry Shelepnev's avatar Dmitry Shelepnev
Browse files

New pagination in Catalog Feed. Fix search_type=i for web_backend

parent dc355141
Loading
Loading
Loading
Loading
+82 −48
Original line number Diff line number Diff line
from itertools import chain
from opds_catalog.sviews import Feed

#from itertools import chain

from django.utils import timezone
from django.utils.translation import ugettext as _
from django.utils.feedgenerator import Atom1Feed, Enclosure, rfc3339_date
from django.contrib.syndication.views import Feed
#from django.contrib.syndication.views import Feed
from django.core.urlresolvers import reverse
from django.core.paginator import Paginator, EmptyPage
from django.shortcuts import render
from django.db.models import Count, Min, Sum

from django.db.models import Count, Min

from opds_catalog.models import Book, Catalog, Author, Genre, Series, bookshelf, Counter, lang_menu
from opds_catalog import models
from opds_catalog import settings
from opds_catalog.opds_middleware import BasicAuthMiddleware
from opds_catalog.opds_paginator import Paginator as OPDS_Paginator

import time

class AuthFeed(Feed):
    request = None
@@ -197,38 +201,66 @@ class MainFeed(AuthFeed):
class CatalogsFeed(AuthFeed):
    feed_type = opdsFeed
    subtitle = settings.SUBTITLE
    description_template = "book_description_cat.html"
    ##description_template = "book_description.html"

    def get_object(self, request, cat_id=None, page=1):
        print('get_object', time.time())
        if not isinstance(page, int):
            page = int(page)

        try:
            if cat_id is not None:
            return (Catalog.objects.get(id=cat_id), page)
                cat = Catalog.objects.get(id=cat_id)
            else:
            return (Catalog.objects.get(parent__id=cat_id), page)
                cat = Catalog.objects.get(parent__id=cat_id)
        except Catalog.DoesNotExist:
            cat = None
        
        catalogs_list = Catalog.objects.filter(parent=cat).order_by("cat_name")
        catalogs_count = catalogs_list.count()
        # prefetch_related on sqlite on items >999 therow error "too many SQL variables"
        #books_list = Book.objects.filter(catalog=cat).prefetch_related('authors','genres','series').order_by("title")
        books_list = Book.objects.filter(catalog=cat).order_by("search_title")
        books_count = books_list.count()
        
        # Получаем результирующий список
        op = OPDS_Paginator(catalogs_count, books_count, page, settings.MAXITEMS)
        items = []
        
        for row in catalogs_list[op.d1_first_pos:op.d1_last_pos]:
            p = {'is_catalog':1, 'title': row.cat_name,'id': row.id, 'cat_type':row.cat_type, 'parent_id':row.parent_id}       
            items.append(p)
              
        for row in books_list[op.d2_first_pos:op.d2_last_pos]:
            p = {'is_catalog':0, 'lang_code': row.lang_code, 'filename': row.filename, 'path': row.path, \
                  'registerdate': row.registerdate, 'id': row.id, 'annotation': row.annotation, \
                  'docdate': row.docdate, 'format': row.format, 'title': row.title, 'filesize': row.filesize//1000,
                  'authors':row.authors.values(), 'genres':row.genres.values(), 'series':row.series.values()}         
            items.append(p)
            
        return items, cat, op.get_data_dict()            

    def title(self, obj):
        cat, current_page = obj
        items, cat, paginator = obj
        if cat.parent:
            return "%s | %s | %s"%(settings.TITLE,_("By catalogs"), cat.path)
        else:
            return "%s | %s"%(settings.TITLE,_("By catalogs"))

    def link(self, obj):
        cat, current_page = obj
        return reverse("opds_catalog:cat_page", kwargs={"cat_id":cat.id, "page":current_page})
        items, cat, paginator = obj
        return reverse("opds_catalog:cat_page", kwargs={"cat_id":cat.id, "page":paginator['number']})

    def feed_extra_kwargs(self, obj):
        cat, current_page = obj
        items, cat, paginator = obj
        start_url = reverse("opds_catalog:main")
        if current_page != 1:
            prev_url = reverse("opds_catalog:cat_page", kwargs={"cat_id":cat.id,"page":(current_page-1)})
        if paginator['has_previos']:
            prev_url = reverse("opds_catalog:cat_page", kwargs={"cat_id":cat.id,"page":paginator['previous_page_number']})
        else:
            prev_url  = None

        if current_page*settings.MAXITEMS<Catalog.objects.filter(parent=cat).count() + Book.objects.filter(catalog=cat).count():
            next_url = reverse("opds_catalog:cat_page", kwargs={"cat_id":cat.id,"page":(current_page+1)})
        if paginator['has_next']:
            next_url = reverse("opds_catalog:cat_page", kwargs={"cat_id":cat.id,"page":paginator['next_page_number']})
        else:
            next_url  = None

@@ -240,49 +272,51 @@ class CatalogsFeed(AuthFeed):
        }

    def items(self, obj):
        cat, current_page = obj
        catalogs_list = Catalog.objects.filter(parent=cat).order_by("cat_name")
        # prefetch_related on sqlite on items >999 therow error "too many SQL variables"
        #books_list = Book.objects.filter(catalog=cat).prefetch_related('authors','genres','series').order_by("title")
        books_list = Book.objects.filter(catalog=cat).order_by("search_title")
        union_list = list(chain(catalogs_list,books_list))
        paginator = Paginator(union_list,settings.MAXITEMS)
        try:
            page = paginator.page(current_page)
        except EmptyPage:
            page = paginator.page(paginator.num_pages)

        return page
        items, cat, paginator = obj
        print('items', time.time())
        return items

    def item_title(self, item):
        if isinstance(item, Catalog):
            return item.cat_name
        else:
            return item.title
        return item['title']

    def item_guid(self, item):
        if isinstance(item, Catalog):
            gp = 'c:'
        else:
            gp = 'b:'
        return "%s%s"%(gp,item.id)
        gp = 'c:' if item['is_catalog'] else 'b:'
        return "%s%s"%(gp,item['id'])

    def item_link(self, item):
        if isinstance(item, Catalog):
            return reverse("opds_catalog:cat_tree", kwargs={"cat_id":item.id})
        if item['is_catalog']:
            return reverse("opds_catalog:cat_tree", kwargs={"cat_id":item['id']})
        else:
            return reverse("opds_catalog:download", kwargs={"book_id":item.id,"zip_flag":0})
            return reverse("opds_catalog:download", kwargs={"book_id":item['id'],"zip_flag":0})

    def item_enclosures(self, item):
        if isinstance(item, Catalog):
            return (opdsEnclosure(reverse("opds_catalog:cat_tree", kwargs={"cat_id":item.id}),"application/atom+xml;profile=opds-catalog;kind=navigation", "subsection"),)
        if item['is_catalog']:
            return (opdsEnclosure(reverse("opds_catalog:cat_tree", kwargs={"cat_id":item['id']}),"application/atom+xml;profile=opds-catalog;kind=navigation", "subsection"),)
        else:
            return (
                opdsEnclosure(reverse("opds_catalog:download", kwargs={"book_id":item.id,"zip_flag":0}),"application/fb2" ,"http://opds-spec.org/acquisition/open-access"),
                opdsEnclosure(reverse("opds_catalog:download", kwargs={"book_id":item.id,"zip_flag":1}),"application/fb2+zip", "http://opds-spec.org/acquisition/open-access"),
                opdsEnclosure(reverse("opds_catalog:cover", kwargs={"book_id":item.id}),"image/jpeg", "http://opds-spec.org/image"),
                opdsEnclosure(reverse("opds_catalog:download", kwargs={"book_id":item['id'],"zip_flag":0}),"application/fb2" ,"http://opds-spec.org/acquisition/open-access"),
                opdsEnclosure(reverse("opds_catalog:download", kwargs={"book_id":item['id'],"zip_flag":1}),"application/fb2+zip", "http://opds-spec.org/acquisition/open-access"),
                opdsEnclosure(reverse("opds_catalog:cover", kwargs={"book_id":item['id']}),"image/jpeg", "http://opds-spec.org/image"),
            )
    
    def item_description(self, item):
        if item['is_catalog']:
            return item['title']
        else: 
            s="<b> Book name: </b>%(title)s<br/>"
            if item['authors']: s += "<b>Authors: </b>%(authors)s<br/>"
            if item['genres']: s += "<b>Genres: </b>%(genres)s<br/>"
            if item['series']: s += "<b>Series: </b>%(series)s<br/>"
            s += "<b>File: </b>%(filename)s<br/><b>File size: </b>%(filesize)s<br/><b>Changes date: </b>%(docdate)s<br/>"
            #if item['doubles']: s += "<b>Doubles count: </b>%(doubles)s<br/>"
            s +="<p class='book'>%(annotation)s</p>"
            print(item['authors'])
            return s%{'title':item['title'],'filename':item['filename'], 'filesize':item['filesize'],'docdate':item['docdate'],'annotation':item['annotation'],
                      'authors':", ".join(a['full_name'] for a in item['authors']),
                      'genres':", ".join(g['subsection'] for g in item['genres']),
                      'series':", ".join(s['ser'] for s in item['series']),                     
                      }
                            
def OpenSearch(request):
    """
    Выводим шаблон поиска

opds_catalog/sviews.py

0 → 100644
+262 −0
Original line number Diff line number Diff line
from __future__ import unicode_literals

from calendar import timegm

from django.conf import settings
from django.contrib.sites.shortcuts import get_current_site
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
from django.http import Http404, HttpResponse
from django.template import TemplateDoesNotExist, loader
from django.utils import feedgenerator, six
from django.utils.encoding import force_text, iri_to_uri, smart_text
from django.utils.html import escape
from django.utils.http import http_date
from django.utils.timezone import get_default_timezone, is_naive, make_aware

import time


def add_domain(domain, url, secure=False):
    protocol = 'https' if secure else 'http'
    if url.startswith('//'):
        # Support network-path reference (see #16753) - RSS requires a protocol
        url = '%s:%s' % (protocol, url)
    elif not url.startswith(('http://', 'https://', 'mailto:')):
        url = iri_to_uri('%s://%s%s' % (protocol, domain, url))
    return url


class FeedDoesNotExist(ObjectDoesNotExist):
    pass


class Feed(object):
    feed_type = feedgenerator.DefaultFeed
    title_template = None
    description_template = None

    def __call__(self, request, *args, **kwargs):
        print('__call__ 1', time.time())
        try:
            obj = self.get_object(request, *args, **kwargs)
        except ObjectDoesNotExist:
            raise Http404('Feed object does not exist.')
        print('__call__ 2', time.time())
        feedgen = self.get_feed(obj, request)
        print('__call__ 3', time.time())
        response = HttpResponse(content_type=feedgen.content_type)
        print('__call__ 4', time.time())
        if hasattr(self, 'item_pubdate') or hasattr(self, 'item_updateddate'):
            # if item_pubdate or item_updateddate is defined for the feed, set
            # header so as ConditionalGetMiddleware is able to send 304 NOT MODIFIED
            response['Last-Modified'] = http_date(
                timegm(feedgen.latest_post_date().utctimetuple()))
        print('__call__ 6', time.time())    
        feedgen.write(response, 'utf-8')
        print('__call__ 7', time.time())
        return response

    def item_title(self, item):
        # Titles should be double escaped by default (see #6533)
        return escape(force_text(item))

    def item_description(self, item):
        return force_text(item)

    def item_link(self, item):
        try:
            return item.get_absolute_url()
        except AttributeError:
            raise ImproperlyConfigured(
                'Give your %s class a get_absolute_url() method, or define an '
                'item_link() method in your Feed class.' % item.__class__.__name__
            )

    def item_enclosures(self, item):
        enc_url = self.__get_dynamic_attr('item_enclosure_url', item)
        if enc_url:
            enc = feedgenerator.Enclosure(
                url=smart_text(enc_url),
                length=smart_text(self.__get_dynamic_attr('item_enclosure_length', item)),
                mime_type=smart_text(self.__get_dynamic_attr('item_enclosure_mime_type', item)),
            )
            return [enc]
        return []

    def __get_dynamic_attr(self, attname, obj, default=None):
        try:
            attr = getattr(self, attname)
        except AttributeError:
            return default
        if callable(attr):
            # Check co_argcount rather than try/excepting the function and
            # catching the TypeError, because something inside the function
            # may raise the TypeError. This technique is more accurate.
            try:
                code = six.get_function_code(attr)
            except AttributeError:
                code = six.get_function_code(attr.__call__)
            if code.co_argcount == 2:       # one argument is 'self'
                return attr(obj)
            else:
                return attr()
        return attr

    def feed_extra_kwargs(self, obj):
        """
        Returns an extra keyword arguments dictionary that is used when
        initializing the feed generator.
        """
        return {}

    def item_extra_kwargs(self, item):
        """
        Returns an extra keyword arguments dictionary that is used with
        the `add_item` call of the feed generator.
        """
        return {}

    def get_object(self, request, *args, **kwargs):
        return None

    def get_context_data(self, **kwargs):
        """
        Returns a dictionary to use as extra context if either
        ``self.description_template`` or ``self.item_template`` are used.

        Default implementation preserves the old behavior
        of using {'obj': item, 'site': current_site} as the context.
        """
        return {'obj': kwargs.get('item'), 'site': kwargs.get('site')}

    def get_feed(self, obj, request):
        """
        Returns a feedgenerator.DefaultFeed object, fully populated, for
        this feed. Raises FeedDoesNotExist for invalid parameters.
        """
        print('get_feed 1', time.time())
        current_site = get_current_site(request)
        print('get_feed 2', time.time())
        
        link = self.__get_dynamic_attr('link', obj)
        print('get_feed 3', time.time())
        link = add_domain(current_site.domain, link, request.is_secure())
        print('get_feed 4', time.time())

        feed = self.feed_type(
            title=self.__get_dynamic_attr('title', obj),
            subtitle=self.__get_dynamic_attr('subtitle', obj),
            link=link,
            description=self.__get_dynamic_attr('description', obj),
            language=settings.LANGUAGE_CODE,
            feed_url=add_domain(
                current_site.domain,
                self.__get_dynamic_attr('feed_url', obj) or request.path,
                request.is_secure(),
            ),
            author_name=self.__get_dynamic_attr('author_name', obj),
            author_link=self.__get_dynamic_attr('author_link', obj),
            author_email=self.__get_dynamic_attr('author_email', obj),
            categories=self.__get_dynamic_attr('categories', obj),
            feed_copyright=self.__get_dynamic_attr('feed_copyright', obj),
            feed_guid=self.__get_dynamic_attr('feed_guid', obj),
            ttl=self.__get_dynamic_attr('ttl', obj),
            **self.feed_extra_kwargs(obj)
        )
        print('get_feed 5', time.time())
        
        title_tmp = None
        if self.title_template is not None:
            try:
                title_tmp = loader.get_template(self.title_template)
            except TemplateDoesNotExist:
                pass
        print('get_feed 6', time.time())
        
        description_tmp = None
        if self.description_template is not None:
            try:
                description_tmp = loader.get_template(self.description_template)
            except TemplateDoesNotExist:
                pass

        print('get_feed 7', time.time())
        
        i=0
        for item in self.__get_dynamic_attr('items', obj):
            if i==0: print('for item 1', time.time())
            context = self.get_context_data(item=item, site=current_site,
                                            obj=obj, request=request)
            if i==0: print('for item 2', time.time())
            if title_tmp is not None:
                title = title_tmp.render(context, request)
            else:
                title = self.__get_dynamic_attr('item_title', item)

            if i==0: print('for item 3', time.time())
            if description_tmp is not None:
                description = description_tmp.render(context, request)
            else:
                description = self.__get_dynamic_attr('item_description', item)

            if i==0: print('for item 4', time.time())
            link = add_domain(
                current_site.domain,
                self.__get_dynamic_attr('item_link', item),
                request.is_secure(),
            )

            if i==0: print('for item 5', time.time())
            enclosures = self.__get_dynamic_attr('item_enclosures', item)

            if i==0: print('for item 6', time.time())
            author_name = self.__get_dynamic_attr('item_author_name', item)

            if i==0: print('for item 7', time.time())
            if author_name is not None:
                author_email = self.__get_dynamic_attr('item_author_email', item)
                author_link = self.__get_dynamic_attr('item_author_link', item)
            else:
                author_email = author_link = None

            if i==0: print('for item 8', time.time())
            tz = get_default_timezone()

            if i==0: print('for item 9', time.time())
            pubdate = self.__get_dynamic_attr('item_pubdate', item)

            if i==0: print('for item 10', time.time())
            if pubdate and is_naive(pubdate):
                pubdate = make_aware(pubdate, tz)

            if i==0: print('for item 11', time.time())
            updateddate = self.__get_dynamic_attr('item_updateddate', item)

            if i==0: print('for item 12', time.time())
            if updateddate and is_naive(updateddate):
                updateddate = make_aware(updateddate, tz)

            if i==0: print('for item 13', time.time())
            feed.add_item(
                title=title,
                link=link,
                description=description,
                unique_id=self.__get_dynamic_attr('item_guid', item, link),
                unique_id_is_permalink=self.__get_dynamic_attr(
                    'item_guid_is_permalink', item),
                enclosures=enclosures,
                pubdate=pubdate,
                updateddate=updateddate,
                author_name=author_name,
                author_email=author_email,
                author_link=author_link,
                categories=self.__get_dynamic_attr('item_categories', item),
                item_copyright=self.__get_dynamic_attr('item_copyright', item),
                **self.item_extra_kwargs(item)
            )
            if i==0: print('for item 14', time.time())
            
            i += 1
            
        print('get_feed 8', time.time(), i)
        return feed
+2 −2
Original line number Diff line number Diff line
{% load i18n %}

{% autoescape off %}
{% if obj.cat_name %}
    {{ obj.cat_name }}
{% if obj.is_catalog %}
    {{ obj.title }}
{% else %}
    <b>{% trans "Book name:" %} </b>{{ obj.title }}<br/>
    {% if obj.authors %}<b>{% trans "Authors:" %} </b>{% for a in obj.authors %}{{ a.full_name }}; {% endfor %}<br/>{% endif %}
+4 −4
Original line number Diff line number Diff line
@@ -5,9 +5,9 @@
    {{ obj.cat_name }}
{% else %}
    <b>{% trans "Book name:" %} </b>{{ obj.title }}<br/>
    <b>{% trans "Authors:" %} </b>{% for a in obj.authors.all %}{{ a.full_name }}; {% endfor %}<br/>
    <b>{% trans "Genres:" %} </b>{% for g in obj.genres.all %}{{ g.subsection }}; {% endfor %}<br/>
    <b>{% trans "Series:" %} </b>{% for s in obj.series.all %}{{s.ser}}; {% endfor %}<br/>
    {% if obj.authors %}<b>{% trans "Authors:" %} </b>{% for a in obj.authors.all %}{{ a.full_name }}; {% endfor %}<br/>{% endif %}
    {% if obj.genres %}<b>{% trans "Genres:" %} </b>{% for g in obj.genres.all %}{{ g.subsection }}; {% endfor %}<br/>{% endif %}
    {% if obj.series %}<b>{% trans "Series:" %} </b>{% for s in obj.series.all %}{{s.ser}}; {% endfor %}<br/>{% endif %}
    <b>{% trans "File:" %} </b>{{ obj.filename }}<br/>
    <b>{% trans "File size:" %} </b>{{ obj.filesize }}<br/>
    <b>{% trans "Changes date:" %} </b>{{ obj.docdate }}<br/>   
+13 −13
Original line number Diff line number Diff line
from random import randint
from itertools import chain

from django.shortcuts import render, redirect
from django.template.context_processors import csrf
from django.core.paginator import Paginator, InvalidPage
from django.db.models import Count, Min
from django.utils.translation import ugettext as _
from django.contrib.auth import authenticate, login, logout, REDIRECT_FIELD_NAME
from django.contrib.auth import authenticate, login, REDIRECT_FIELD_NAME
from django.contrib.auth.decorators import user_passes_test
from django.core.urlresolvers import reverse, reverse_lazy

@@ -158,12 +156,13 @@ def SearchBooksView(request):
        elif searchtype == 'i':
            try:
                book_id = int(searchterms)
                btitle = Book.objects.get(id=book_id).title
                mbook = Book.objects.get(id=book_id)
            except:
                book_id = 0
                btitle = ""
            books = Book.objects.filter(id=book_id)                 
            args['breadcrumbs'] = [_('Books'),btitle]
                mbook = None
            #books = Book.objects.filter(id=book_id) 
            books = Book.objects.filter(title__iexact=mbook.title, authors__in=mbook.authors.all()).distinct().order_by('-docdate')                
            args['breadcrumbs'] = [_('Books'),mbook.title]
            args['searchobject'] = 'title'
        
        # prefetch_related on sqlite on items >999 therow error "too many SQL variables"    
@@ -212,6 +211,7 @@ def SearchBooksView(request):
                else:
                    double_flag = False   
            
            if op.d1_first_pos!=0:     
                items.pop(0)                                   
              
        args['paginator'] = op.get_data_dict()
@@ -520,13 +520,13 @@ def LoginView(request):
    except KeyError:
        return render(request, 'sopds_login.html', args) 
    
    next = request.GET.get('next',reverse("web:main"))
    next_url = request.GET.get('next',reverse("web:main"))

    user = authenticate(username=username, password=password)
    if user is not None:
        if user.is_active:
            login(request, user)
            return redirect(next)
            return redirect(next_url)
        else:
            args['system_message']={'text':_('This account is not active!'),'type':'alert'}
            return render(request, 'sopds_login.html', args)