Ejemplo n.º 1
0
    def get(self, request):
        """info about a book"""
        query = request.GET.get("query")
        book_results = popular_books = []
        if query:
            book_results = connector_manager.local_search(query, raw=True)[:5]
        if len(book_results) < 5:
            popular_books = (
                models.Edition.objects.exclude(
                    # exclude already shelved
                    Q(parent_work__in=[
                        b.book.parent_work
                        for b in request.user.shelfbook_set.distinct().all()
                    ])
                    | Q(  # and exclude if it's already in search results
                        parent_work__in=[b.parent_work
                                         for b in book_results])).annotate(
                                             Count("shelfbook")).
                order_by("-shelfbook__count")[:5 - len(book_results)])

        data = {
            "book_results": book_results,
            "popular_books": popular_books,
            "next": self.next_view,
        }
        return TemplateResponse(request, "get_started/books.html", data)
Ejemplo n.º 2
0
def book_search(query, _, min_confidence, search_remote=False):
    """the real business is elsewhere"""
    if search_remote:
        return connector_manager.search(query, min_confidence=min_confidence)
    results = connector_manager.local_search(query, min_confidence=min_confidence)
    if not results:
        return None
    return [{"results": results}]
Ejemplo n.º 3
0
    def post(self, request, book_id=None):
        """ edit a book cool """
        # returns None if no match is found
        book = models.Edition.objects.filter(id=book_id).first()
        form = forms.EditionForm(request.POST, request.FILES, instance=book)

        data = {"book": book, "form": form}
        if not form.is_valid():
            return TemplateResponse(request, "edit_book.html", data)

        add_author = request.POST.get("add_author")
        # we're adding an author through a free text field
        if add_author:
            data["add_author"] = add_author
            data["author_matches"] = []
            for author in add_author.split(","):
                if not author:
                    continue
                # check for existing authors
                vector = SearchVector("name", weight="A") + SearchVector(
                    "aliases", weight="B")

                data["author_matches"].append({
                    "name":
                    author.strip(),
                    "matches":
                    (models.Author.objects.annotate(search=vector).annotate(
                        rank=SearchRank(vector, author)).filter(
                            rank__gt=0.4).order_by("-rank")[:5]),
                })
                print(data["author_matches"])

        # we're creating a new book
        if not book:
            # check if this is an edition of an existing work
            author_text = book.author_text if book else add_author
            data["book_matches"] = connector_manager.local_search(
                "%s %s" % (form.cleaned_data.get("title"), author_text),
                min_confidence=0.5,
                raw=True,
            )[:5]

        # either of the above cases requires additional confirmation
        if add_author or not book:
            # creting a book or adding an author to a book needs another step
            data["confirm_mode"] = True
            # this isn't preserved because it isn't part of the form obj
            data["remove_authors"] = request.POST.getlist("remove_authors")
            return TemplateResponse(request, "edit_book.html", data)

        remove_authors = request.POST.getlist("remove_authors")
        for author_id in remove_authors:
            book.authors.remove(author_id)

        book = form.save()
        return redirect("/book/%s" % book.id)
Ejemplo n.º 4
0
def book_search(query, _, min_confidence, search_remote=False):
    """the real business is elsewhere"""
    # try a local-only search
    if not search_remote:
        results = connector_manager.local_search(query,
                                                 min_confidence=min_confidence)
        if results:
            # gret, we found something
            return [{"results": results}], False
    # if there weere no local results, or the request was for remote, search all sources
    return connector_manager.search(query, min_confidence=min_confidence), True
Ejemplo n.º 5
0
    def get(self, request):
        ''' that search bar up top '''
        query = request.GET.get('q')
        min_confidence = request.GET.get('min_confidence', 0.1)

        if is_api_request(request):
            # only return local book results via json so we don't cascade
            book_results = connector_manager.local_search(
                query, min_confidence=min_confidence)
            return JsonResponse([r.json() for r in book_results], safe=False)

        # use webfinger for mastodon style [email protected] username
        if re.match(r'\B%s' % regex.full_username, query):
            handle_remote_webfinger(query)

        # do a  user search
        user_results = models.User.objects.annotate(
            similarity=Greatest(
                TrigramSimilarity('username', query),
                TrigramSimilarity('localname', query),
            )
        ).filter(
            similarity__gt=0.5,
        ).order_by('-similarity')[:10]

        # any relevent lists?
        list_results = privacy_filter(
            request.user, models.List.objects, ['public', 'followers']
        ).annotate(
            similarity=Greatest(
                TrigramSimilarity('name', query),
                TrigramSimilarity('description', query),
            )
        ).filter(
            similarity__gt=0.1,
        ).order_by('-similarity')[:10]

        book_results = connector_manager.search(
            query, min_confidence=min_confidence)
        data = {
            'title': 'Search Results',
            'book_results': book_results,
            'user_results': user_results,
            'list_results': list_results,
            'query': query,
        }
        return TemplateResponse(request, 'search_results.html', data)
Ejemplo n.º 6
0
    def get(self, request):
        """that search bar up top"""
        query = request.GET.get("q")
        min_confidence = request.GET.get("min_confidence", 0.1)
        search_type = request.GET.get("type")
        search_remote = (
            request.GET.get("remote", False) and request.user.is_authenticated
        )

        if is_api_request(request):
            # only return local book results via json so we don't cascade
            book_results = connector_manager.local_search(
                query, min_confidence=min_confidence
            )
            return JsonResponse([r.json() for r in book_results], safe=False)

        if query and not search_type:
            search_type = "user" if "@" in query else "book"

        endpoints = {
            "book": book_search,
            "user": user_search,
            "list": list_search,
        }
        if not search_type in endpoints:
            search_type = "book"

        data = {
            "query": query or "",
            "type": search_type,
            "remote": search_remote,
        }
        if query:
            results = endpoints[search_type](
                query, request.user, min_confidence, search_remote
            )
            if results:
                paginated = Paginator(results, PAGE_LENGTH).get_page(
                    request.GET.get("page")
                )
                data["results"] = paginated

        return TemplateResponse(request, "search/{:s}.html".format(search_type), data)
Ejemplo n.º 7
0
    def get(self, request):
        """ that search bar up top """
        query = request.GET.get("q")
        min_confidence = request.GET.get("min_confidence", 0.1)

        if is_api_request(request):
            # only return local book results via json so we don't cascade
            book_results = connector_manager.local_search(
                query, min_confidence=min_confidence)
            return JsonResponse([r.json() for r in book_results], safe=False)

        # use webfinger for mastodon style [email protected] username
        if re.match(r"\B%s" % regex.full_username, query):
            handle_remote_webfinger(query)

        # do a  user search
        user_results = (models.User.viewer_aware_objects(
            request.user).annotate(similarity=Greatest(
                TrigramSimilarity("username", query),
                TrigramSimilarity("localname", query),
            )).filter(similarity__gt=0.5, ).order_by("-similarity")[:10])

        # any relevent lists?
        list_results = (privacy_filter(
            request.user,
            models.List.objects,
            privacy_levels=["public", "followers"],
        ).annotate(similarity=Greatest(
            TrigramSimilarity("name", query),
            TrigramSimilarity("description", query),
        )).filter(similarity__gt=0.1, ).order_by("-similarity")[:10])

        book_results = connector_manager.search(query,
                                                min_confidence=min_confidence)
        data = {
            "book_results": book_results,
            "user_results": user_results,
            "list_results": list_results,
            "query": query,
        }
        return TemplateResponse(request, "search_results.html", data)
Ejemplo n.º 8
0
    def get(self, request, list_id):
        ''' display a book list '''
        book_list = get_object_or_404(models.List, id=list_id)
        if not object_visible_to_user(request.user, book_list):
            return HttpResponseNotFound()

        if is_api_request(request):
            return ActivitypubResponse(book_list.to_activity(**request.GET))

        query = request.GET.get('q')
        suggestions = None
        if query and request.user.is_authenticated:
            # search for books
            suggestions = connector_manager.local_search(query, raw=True)
        elif request.user.is_authenticated:
            # just suggest whatever books are nearby
            suggestions = request.user.shelfbook_set.filter(~Q(
                book__in=book_list.books.all()))
            suggestions = [s.book for s in suggestions[:5]]
            if len(suggestions) < 5:
                suggestions += [
                    s.default_edition for s in \
                        models.Work.objects.filter(
                            ~Q(editions__in=book_list.books.all()),
                        ).order_by('-updated_date')
                ][:5 - len(suggestions)]

        data = {
            'title': '%s | Lists' % book_list.name,
            'list': book_list,
            'items': book_list.listitem_set.filter(approved=True),
            'pending_count':
            book_list.listitem_set.filter(approved=False).count(),
            'suggested_books': suggestions,
            'list_form': forms.ListForm(instance=book_list),
            'query': query or ''
        }
        return TemplateResponse(request, 'lists/list.html', data)
Ejemplo n.º 9
0
    def get(self, request, list_id):
        """ display a book list """
        book_list = get_object_or_404(models.List, id=list_id)
        if not object_visible_to_user(request.user, book_list):
            return HttpResponseNotFound()

        if is_api_request(request):
            return ActivitypubResponse(book_list.to_activity(**request.GET))

        query = request.GET.get("q")
        suggestions = None
        if query and request.user.is_authenticated:
            # search for books
            suggestions = connector_manager.local_search(query, raw=True)
        elif request.user.is_authenticated:
            # just suggest whatever books are nearby
            suggestions = request.user.shelfbook_set.filter(~Q(
                book__in=book_list.books.all()))
            suggestions = [s.book for s in suggestions[:5]]
            if len(suggestions) < 5:
                suggestions += [
                    s.default_edition for s in models.Work.objects.filter(
                        ~Q(editions__in=book_list.books.all()), ).order_by(
                            "-updated_date")
                ][:5 - len(suggestions)]

        data = {
            "list": book_list,
            "items": book_list.listitem_set.filter(approved=True),
            "pending_count":
            book_list.listitem_set.filter(approved=False).count(),
            "suggested_books": suggestions,
            "list_form": forms.ListForm(instance=book_list),
            "query": query or "",
        }
        return TemplateResponse(request, "lists/list.html", data)
Ejemplo n.º 10
0
    def post(self, request, book_id=None):
        """ edit a book cool """
        # returns None if no match is found
        book = models.Edition.objects.filter(id=book_id).first()
        form = forms.EditionForm(request.POST, request.FILES, instance=book)

        data = {"book": book, "form": form}
        if not form.is_valid():
            return TemplateResponse(request, "book/edit_book.html", data)

        add_author = request.POST.get("add_author")
        # we're adding an author through a free text field
        if add_author:
            data["add_author"] = add_author
            data["author_matches"] = []
            for author in add_author.split(","):
                if not author:
                    continue
                # check for existing authors
                vector = SearchVector("name", weight="A") + SearchVector(
                    "aliases", weight="B"
                )

                data["author_matches"].append(
                    {
                        "name": author.strip(),
                        "matches": (
                            models.Author.objects.annotate(search=vector)
                            .annotate(rank=SearchRank(vector, author))
                            .filter(rank__gt=0.4)
                            .order_by("-rank")[:5]
                        ),
                    }
                )
                print(data["author_matches"])

        # we're creating a new book
        if not book:
            # check if this is an edition of an existing work
            author_text = book.author_text if book else add_author
            data["book_matches"] = connector_manager.local_search(
                "%s %s" % (form.cleaned_data.get("title"), author_text),
                min_confidence=0.5,
                raw=True,
            )[:5]

        # either of the above cases requires additional confirmation
        if add_author or not book:
            # creting a book or adding an author to a book needs another step
            data["confirm_mode"] = True
            # this isn't preserved because it isn't part of the form obj
            data["remove_authors"] = request.POST.getlist("remove_authors")
            # make sure the dates are passed in as datetime, they're currently a string
            # QueryDicts are immutable, we need to copy
            formcopy = data["form"].data.copy()
            try:
                formcopy["first_published_date"] = dateparse(
                    formcopy["first_published_date"]
                )
            except (MultiValueDictKeyError, ValueError):
                pass
            try:
                formcopy["published_date"] = dateparse(formcopy["published_date"])
            except (MultiValueDictKeyError, ValueError):
                pass
            data["form"].data = formcopy
            return TemplateResponse(request, "book/edit_book.html", data)

        remove_authors = request.POST.getlist("remove_authors")
        for author_id in remove_authors:
            book.authors.remove(author_id)

        book = form.save(commit=False)
        url = request.POST.get("cover-url")
        if url:
            image = set_cover_from_url(url)
            if image:
                book.cover.save(*image, save=False)
        book.save()
        return redirect("/book/%s" % book.id)
Ejemplo n.º 11
0
 def test_local_search(self):
     """ search only the local database """
     results = connector_manager.local_search("Example")
     self.assertEqual(len(results), 1)
     self.assertEqual(results[0].title, "Example Edition")
Ejemplo n.º 12
0
 def test_local_search(self):
     ''' search only the local database '''
     results = connector_manager.local_search('Example')
     self.assertEqual(len(results), 1)
     self.assertEqual(results[0].title, 'Example Edition')
Ejemplo n.º 13
0
    def get(self, request, list_id):
        """display a book list"""
        book_list = get_object_or_404(models.List, id=list_id)
        if not book_list.visible_to_user(request.user):
            return HttpResponseNotFound()

        if is_api_request(request):
            return ActivitypubResponse(book_list.to_activity(**request.GET))

        query = request.GET.get("q")
        suggestions = None

        # sort_by shall be "order" unless a valid alternative is given
        sort_by = request.GET.get("sort_by", "order")
        if sort_by not in ("order", "title", "rating"):
            sort_by = "order"

        # direction shall be "ascending" unless a valid alternative is given
        direction = request.GET.get("direction", "ascending")
        if direction not in ("ascending", "descending"):
            direction = "ascending"

        directional_sort_by = {
            "order": "order",
            "title": "book__title",
            "rating": "average_rating",
        }[sort_by]
        if direction == "descending":
            directional_sort_by = "-" + directional_sort_by

        items = book_list.listitem_set
        if sort_by == "rating":
            items = items.annotate(average_rating=Avg(
                Coalesce("book__review__rating", 0.0),
                output_field=DecimalField(),
            ))
        items = items.filter(approved=True).order_by(directional_sort_by)

        paginated = Paginator(items, PAGE_LENGTH)

        if query and request.user.is_authenticated:
            # search for books
            suggestions = connector_manager.local_search(
                query,
                raw=True,
                filters=[~Q(parent_work__editions__in=book_list.books.all())],
            )
        elif request.user.is_authenticated:
            # just suggest whatever books are nearby
            suggestions = request.user.shelfbook_set.filter(~Q(
                book__in=book_list.books.all()))
            suggestions = [s.book for s in suggestions[:5]]
            if len(suggestions) < 5:
                suggestions += [
                    s.default_edition for s in models.Work.objects.filter(
                        ~Q(editions__in=book_list.books.all()), ).order_by(
                            "-updated_date")
                ][:5 - len(suggestions)]

        page = paginated.get_page(request.GET.get("page"))
        data = {
            "list":
            book_list,
            "items":
            page,
            "page_range":
            paginated.get_elided_page_range(page.number,
                                            on_each_side=2,
                                            on_ends=1),
            "pending_count":
            book_list.listitem_set.filter(approved=False).count(),
            "suggested_books":
            suggestions,
            "list_form":
            forms.ListForm(instance=book_list),
            "query":
            query or "",
            "sort_form":
            forms.SortListForm({
                "direction": direction,
                "sort_by": sort_by
            }),
        }
        return TemplateResponse(request, "lists/list.html", data)