BOOKS_PER_PAGE = 20 # Number of books shown per page in the OPDS AUTHORS_PER_PAGE = 40 # Number of books shown per page in the OPDS def simple_search(queryset, searchterms,
                  search_title=False, search_author=False):
    q_objects = []
    results = queryset

    subterms = searchterms.split(' ')
    for subterm in subterms:
        word = subterm
        if search_title:
            q_objects.append(Q(a_title__icontains = word))
        if search_author:
            q_objects.append(Q(a_author__icontains = word))

    for q_object in q_objects:
        results = results.filter(q_object)
    return results def advanced_search(queryset, searchterms):
    """
    Does an advanced search in several fields of the books.
    """
    q_objects = []
    results = queryset

    subterms = searchterms.split('AND')
    for subterm in subterms:
        if ':' in subterm:
            key, word = subterm.split(':')
            key = key.strip()
            if key == 'title':
                q_objects.append(Q(a_title__icontains = word))
            if key == 'author':
                q_objects.append(Q(a_author__icontains = word))
            if key == 'publisher':
                q_objects.append(Q(dc_publisher__icontains = word))
            if key == 'identifier':
                q_objects.append(Q(dc_identifier__icontains = word))
            if key == 'summary':
                q_objects.append(Q(a_summary__icontains = word))
        else:
            word = subterm
            try:
                results = results.filter(Q(a_title__icontains = word) | \
                    Q(a_author__a_author__icontains = word) | \
                    Q(dc_publisher__icontains = word) | \
                    Q(dc_identifier__icontains = word) | \
                    Q(a_summary__icontains = word))
            except Book.DoesNotExist:
                results = Book.objects.none()

    for q_object in q_objects:
        results = results.filter(q_object)
    return results class BookForm(ModelForm):
    # dc_language = ModelChoiceField(Language.objects, widget=SelectWithPop)

    class Meta:
        model = Book
        exclude = ('mimetype', 'file_sha256sum', )

    def save(self, commit=True):
        """
        Store the MIME type of the uploaded book in the database.

        This is given by the browser in the POST request.

        """
        instance = super(BookForm, self).save(commit=False)
        book_file = self.cleaned_data['book_file']
        if instance.mimetype is None:
            instance.mimetype = book_file.content_type
        if commit:
            instance.save()
        return instance class AddLanguageForm(ModelForm):
    class Meta:
        model = Language
        exclude = ('code',) class TagGroup(models.Model):
    name = models.CharField(max_length=200, blank=False)
    slug = models.SlugField(max_length=200, blank=False)
    #tags = TagableManager()

    class Meta:
        verbose_name = "Tag group"
        verbose_name_plural = "Tag groups"

    def __unicode__(self):
        return self.name

    def __str__(self):
        return self.name class Book(models.Model):
    """
    This model stores the book file, and all the metadata that is
    needed to publish it in a OPDS atom feed.

    It also stores other information, like tags and downloads, so the
    book can be listed in OPDS catalogs.

    """
    book_file = models.FileField(upload_to='books')
    file_sha256sum = models.CharField(max_length=64, unique=True)
    mimetype = models.CharField(max_length=200, null=True)
    time_added = models.DateTimeField(auto_now_add=True)
    tags = TaggableManager(blank=True)
    downloads = models.IntegerField(default=0)
    a_id = UUIDField('atom:id')
    a_status = models.ForeignKey(Status, blank=False, null=False, default=settings.DEFAULT_BOOK_STATUS)
    a_title = models.CharField('atom:title', max_length=200)
    a_author = models.ForeignKey(Author, blank=False, null=False)
    a_updated = models.DateTimeField('atom:updated', auto_now=True)
    a_summary = models.TextField('atom:summary', blank=True)
    a_category = models.CharField('atom:category', max_length=200, blank=True)
    a_rights = models.CharField('atom:rights', max_length=200, blank=True)
    dc_language = models.ForeignKey(Language, blank=True, null=True)
    dc_publisher = models.CharField('dc:publisher', max_length=200, blank=True)
    dc_issued = models.CharField('dc:issued', max_length=100, blank=True)
    dc_identifier = models.CharField('dc:identifier', max_length=50, \
    help_text='Use ISBN for this', blank=True)
    cover_img = models.FileField(blank=True, upload_to='covers')

    def validate_unique(self, *args, **kwargs):
        if not self.file_sha256sum:
            self.file_sha256sum = sha256_sum(self.book_file)
        unicity = self.__class__.objects.filter(file_sha256sum=self.file_sha256sum)
        if self.pk is not None:
            unicity = unicity.exclude(pk=self.pk)
        if unicity.exists():
            raise ValidationError({
                NON_FIELD_ERRORS:['The book already exists in the server.',]})

    def save(self, *args, **kwargs):
        assert self.file_sha256sum
        if not self.cover_img:
            # FIXME: we should use mimetype
            if self.book_file.name.endswith('.epub'):
                # get the cover path from the epub file
                epub_file = Epub(self.book_file)
                cover_path = epub_file.get_cover_image_path()
                if cover_path is not None and os.path.exists(cover_path):
                    cover_file = File(open(cover_path, "rb"))
                    self.cover_img.save(os.path.basename(cover_path), # pylint: disable=no-member
                                        cover_file)
                epub_file.close()

        super(Book, self).save(*args, **kwargs)

    class Meta:
        ordering = ('-time_added',)
        get_latest_by = "time_added"

    def __unicode__(self):
        return self.a_title

    def __str__(self):
        return self.a_title

    @models.permalink
    def get_absolute_url(self):
        return ('book_detail', [self.pk]) class Author(models.Model):
    a_author = models.CharField('atom:author', max_length=200, unique=True)

    def __unicode__(self):
        return self.a_author

    def __str__(self):
        return self.a_author def page_qstring(request, page_number=None):
    """
    Return the query string for the URL.

    If page_number is given, modify the query for that page.
    """
    qdict = dict(request.GET.items())
    if page_number is not None:
        qdict['page'] = str(page_number)

    if len(qdict) > 0:
        qstring = '?'+'&'.join(('%s=%s' % (k, v) for k, v in qdict.items()))
    else:
        qstring = ''

    return qstring def generate_catalog(request, page_obj):
    links = []
    links.append({'title': 'Home', 'type': 'application/atom+xml',
                  'rel': 'start',
                  'href': reverse('root_feed')})

    if page_obj.has_previous():
        previous_page = page_obj.previous_page_number()
        links.append({'title': 'Previous results', 'type': 'application/atom+xml',
                      'rel': 'previous',
                      'href': page_qstring(request, previous_page)})

    if page_obj.has_next():
        next_page = page_obj.next_page_number()
        links.append({'title': 'Next results', 'type': 'application/atom+xml',
                      'rel': 'next',
                      'href': page_qstring(request, next_page)})

    feed = AtomFeed(title = 'Pathagar Bookserver OPDS feed', \
        atom_id = 'pathagar:full-catalog', subtitle = \
        'OPDS catalog for the Pathagar book server', \
        extra_attrs = ATTRS, hide_generator=True, links=links)

    for book in page_obj.object_list:
        if book.cover_img:
            linklist = [{'rel': \
                    'http://opds-spec.org/acquisition', 'href': \
                    reverse('book_download',
                            kwargs=dict(book_id=book.pk)),
                    'type': __get_mimetype(book)}, {'rel': \
                    'http://opds-spec.org/cover', 'href': \
                    book.cover_img.url }]
        else:
            linklist = [{'rel': \
                    'http://opds-spec.org/acquisition', 'href': \
                    reverse('book_download',
                            kwargs=dict(book_id=book.pk)),
                    'type': __get_mimetype(book)}]
        add_kwargs = {
            'content': book.a_summary,
            'links': linklist,
            'authors': [{'name' : str(book.a_author)}],
            'dc_publisher': book.dc_publisher,
            'dc_issued': book.dc_issued,
            'dc_identifier': book.dc_identifier,
        }

        if book.dc_language is not None:
            add_kwargs['dc_language'] = book.dc_language.code

        feed.add_item(book.a_id, book.a_title, book.a_updated, **add_kwargs)

    s = StringIO()
    feed.write(s, 'UTF-8')
    return s.getvalue() def generate_author_catalog(request, page_obj):
    nav = 'application/atom+xml' #;profile=opds-catalog;kind=navigation'
    links = []
    links.append({'title': 'Home', 'type': nav, #'application/atom+xml',
                  'rel': 'start',
                  'href': reverse('root_feed')})

    if page_obj.has_previous():
        previous_page = page_obj.previous_page_number()
        links.append({'title': 'Previous results', 'type': nav, #'application/atom+xml',
                      'rel': 'previous',
                      'href': page_qstring(request, previous_page)})

    if page_obj.has_next():
        next_page = page_obj.next_page_number()
        links.append({'title': 'Next results', 'type': nav, #'application/atom+xml',
                      'rel': 'next',
                      'href': page_qstring(request, next_page)})

    feed = AtomFeed(title = 'Pathagar Bookserver OPDS feed', \
        atom_id = 'pathagar:full-catalog', subtitle = \
        'OPDS catalog for the Pathagar book server', \
        extra_attrs = ATTRS, hide_generator=True, links=links)

    print("send ", len(page_obj.object_list))
    for author in page_obj.object_list:
        linklist = [{'rel': 'subsection',
                     'href': reverse('by_title',
                                     kwargs=dict(author_id=author.pk)),
                     'type': nav}]
        add_kwargs = {
            'content': '', #book.a_summary,
            'links': linklist,
        }

        feed.add_item(str(author.pk), author.a_author, datetime.datetime.now(), **add_kwargs)

    s = StringIO()
    feed.write(s, 'UTF-8')
    return s.getvalue() def generate_root_catalog():
    subsections = [
        {'id': 'latest', 'title': 'Latest', 'updated': datetime.datetime.now(),
         'links': [{'rel': 'subsection', 'type': 'application/atom+xml', \
                    'href': reverse('latest_feed')}]},
        {'id': 'by-title', 'title': 'By Title', 'updated': datetime.datetime.now(),
         'links': [{'rel': 'subsection', 'type': 'application/atom+xml', \
                    'href': reverse('by_title_feed')}]},
        {'id': 'by-author', 'title': 'By Author', 'updated': datetime.datetime.now(),
         'links': [{'rel': 'subsection', 'type': 'application/atom+xml', \
                    'href': reverse('by_author_feed')}]},
        {'id': 'by-popularity', 'title': 'Most downloaded', 'updated': datetime.datetime.now(),
         'links': [{'rel': 'subsection', 'type': 'application/atom+xml', \
                    'href': reverse('most_downloaded_feed')}]},
        {'id': 'tags', 'title': 'Tags', 'updated': datetime.datetime.now(),
         'links': [{'rel': 'subsection', 'type': 'application/atom+xml', \
                    'href': reverse('tags_feed')}]},
        {'id': 'tag-groups', 'title': 'Tag groups', 'updated': datetime.datetime.now(),
         'links': [{'rel': 'subsection', 'type': 'application/atom+xml', \
                    'href': reverse('tags_listgroups')}]},
    ]
    return generate_nav_catalog(subsections) def generate_tags_catalog(tags):
    def convert_tag(tag):
        return {'id': tag.name, 'title': tag.name,  'updated': datetime.datetime.now(),
                'links': [{'rel': 'subsection', 'type': 'application/atom+xml', \
                           'href': reverse('by_tag_feed', kwargs=dict(tag=tag.name))}]}

    tags_subsections = map(convert_tag, tags)
    return generate_nav_catalog(tags_subsections) def generate_taggroups_catalog(tag_groups):
    def convert_group(group):
        return {'id': group.slug, 'title': group.name,  'updated': datetime.datetime.now(),
                'links': [{'rel': 'subsection', 'type': 'application/atom+xml', \
                           'href': reverse('tag_groups_feed', kwargs=dict(group_slug=group.slug))}]}

    tags_subsections = map(convert_group, tag_groups)
    return generate_nav_catalog(tags_subsections) BOOK_PUBLISHED = getattr(settings, 'BOOK_PUBLISHED', 1)
import os
from django.conf import settings
from django.core.urlresolvers import reverse
from django.http import HttpResponse
from django.http import Http404
from django.shortcuts import render
from django.shortcuts import get_object_or_404
from django.shortcuts import redirect
from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator, InvalidPage, EmptyPage
from django.views.generic.detail import DetailView
from django.views.generic.edit import UpdateView
from django.views.generic.edit import CreateView
from django.views.generic.edit import FormView
from django.views.generic.edit import DeleteView
from pathagar.settings import BOOKS_PER_PAGE, AUTHORS_PER_PAGE
from django.conf import settings
from tagging.models import Tag
from taggit.models import Tag as tTag
from sendfile import sendfile
from books.search import simple_search, advanced_search
from books.forms import BookForm, AddLanguageForm
from books.models import TagGroup, Book, Author
from books.popuphandler import handlePopAdd
from books.opds import page_qstring
from books.opds import generate_catalog
from books.opds import generate_author_catalog
from books.opds import generate_root_catalog
from books.opds import generate_tags_catalog
from books.opds import generate_taggroups_catalog
from books.app_settings import BOOK_PUBLISHED
    return render(request, 'books/tag_list.html',
                  context)

def tags_listgroups(request):
    tag_groups = TagGroup.objects.all()
    catalog = generate_taggroups_catalog(tag_groups)
    return HttpResponse(catalog, content_type='application/atom+xml')

def _book_list(request, queryset, qtype=None, list_by='latest', **kwargs):
    """
    Filter the books, paginate the result, and return either a HTML
    book list, or a atom+xml OPDS catalog.

    """
    q = request.GET.get('q')
    search_all = request.GET.get('search-all') == 'on'
    search_title = request.GET.get('search-title') == 'on'
    search_author = request.GET.get('search-author') == 'on'

    user = request.user
    if not user.is_authenticated():
        queryset = queryset.filter(a_status = BOOK_PUBLISHED)

    published_books_count = Book.objects.filter(a_status = BOOK_PUBLISHED).count()
    unpublished_books_count = Book.objects.exclude(a_status = BOOK_PUBLISHED).count()

    # If no search options are specified, assumes search all, the
    # advanced search will be used:
    if not search_all and not search_title and not search_author:
        search_all = True

    # If search queried, modify the queryset with the result of the
    # search:
    if q is not None:
        if search_all:
            queryset = advanced_search(queryset, q)
        else:
            queryset = simple_search(queryset, q,
                                     search_title, search_author)

    paginator = Paginator(queryset, BOOKS_PER_PAGE)
    page = int(request.GET.get('page', '1'))

    try:
        page_obj = paginator.page(page)
    except (EmptyPage, InvalidPage):
        page_obj = paginator.page(paginator.num_pages)

    # Build the query string:
    qstring = page_qstring(request)

    # Return OPDS Atom Feed:
    if qtype == 'feed':
        catalog = generate_catalog(request, page_obj)
        return HttpResponse(catalog, content_type='application/atom+xml')

    # Return HTML page:
    extra_context = dict(kwargs)
    extra_context.update({
        'book_list': page_obj.object_list,
        'published_books': published_books_count,
        'unpublished_books': unpublished_books_count,
        'q': q,
        'paginator': paginator,
        'page_obj': page_obj,
        'search_title': search_title,
        'search_author': search_author, 'list_by': list_by,
        'qstring': qstring,
        'allow_public_add_book': settings.ALLOW_PUBLIC_ADD_BOOKS
    })
    return render(request, 'books/book_list.html',
                  context=extra_context)

def _author_list(request, queryset, qtype=None, list_by='latest', **kwargs):
    """
    Filter the books, paginate the result, and return either a HTML
    book list, or a atom+xml OPDS catalog.

    """
    q = request.GET.get('q')
    # search_all = request.GET.get('search-all') == 'on'
    # search_title = request.GET.get('search-title') == 'on'
    search_author = request.GET.get('search-author') == 'on'

    # context_instance = RequestContext(request)
    # user = resolve_variable('user', context_instance)
    # if not user.is_authenticated():
    #     queryset = queryset.filter(a_status = BOOK_PUBLISHED)

    # published_books_count = Book.objects.filter(a_status = BOOK_PUBLISHED).count()
    # unpublished_books_count = Book.objects.exclude(a_status = BOOK_PUBLISHED).count()

    # If no search options are specified, assumes search all, the
    # advanced search will be used:
    # if not search_all and not search_title and not search_author:
    #     search_all = True

    # FIXME
    # If search queried, modify the queryset with the result of the
    # search:
    # if q is not None:
    #     if search_all:
    #         queryset = advanced_search(queryset, q)
    #     else:
    #         queryset = simple_search(queryset, q,
    #                                  search_title, search_author)

    paginator = Paginator(queryset, BOOKS_PER_PAGE)
    page = int(request.GET.get('page', '1'))

    try:
        page_obj = paginator.page(page)
    except (EmptyPage, InvalidPage):
        page_obj = paginator.page(paginator.num_pages)

    # Build the query string:
    qstring = page_qstring(request)

    # Return OPDS Atom Feed:
    if qtype == 'feed':

        catalog = generate_author_catalog(request, page_obj)