diff --git a/bookwyrm/book_search.py b/bookwyrm/book_search.py index 3012482fd..cf48f4832 100644 --- a/bookwyrm/book_search.py +++ b/bookwyrm/book_search.py @@ -43,6 +43,7 @@ def search( min_confidence: float = 0, filters: Optional[list[Any]] = None, return_first: bool = False, + books: Optional[QuerySet[models.Edition]] = None, ) -> Union[Optional[models.Edition], QuerySet[models.Edition]]: """search your local database""" filters = filters or [] @@ -54,13 +55,15 @@ def search( # first, try searching unique identifiers # unique identifiers never have spaces, title/author usually do if not " " in query: - results = search_identifiers(query, *filters, return_first=return_first) + results = search_identifiers( + query, *filters, return_first=return_first, books=books + ) # if there were no identifier results... if not results: # then try searching title/author results = search_title_author( - query, min_confidence, *filters, return_first=return_first + query, min_confidence, *filters, return_first=return_first, books=books ) return results @@ -98,9 +101,17 @@ def format_search_result(search_result): def search_identifiers( - query, *filters, return_first=False + query, + *filters, + return_first=False, + books=None, ) -> Union[Optional[models.Edition], QuerySet[models.Edition]]: - """tries remote_id, isbn; defined as dedupe fields on the model""" + """search Editions by deduplication fields + + Best for cases when we can assume someone is searching for an exact match on + commonly unique data identifiers like isbn or specific library ids. + """ + books = books or models.Edition.objects if connectors.maybe_isbn(query): # Oh did you think the 'S' in ISBN stood for 'standard'? normalized_isbn = query.strip().upper().rjust(10, "0") @@ -111,7 +122,7 @@ def search_identifiers( for f in models.Edition._meta.get_fields() if hasattr(f, "deduplication_field") and f.deduplication_field ] - results = models.Edition.objects.filter( + results = books.filter( *filters, reduce(operator.or_, (Q(**f) for f in or_filters)) ).distinct() @@ -121,12 +132,17 @@ def search_identifiers( def search_title_author( - query, min_confidence, *filters, return_first=False + query, + min_confidence, + *filters, + return_first=False, + books=None, ) -> QuerySet[models.Edition]: """searches for title and author""" + books = books or models.Edition.objects query = SearchQuery(query, config="simple") | SearchQuery(query, config="english") results = ( - models.Edition.objects.filter(*filters, search_vector=query) + books.filter(*filters, search_vector=query) .annotate(rank=SearchRank(F("search_vector"), query)) .filter(rank__gt=min_confidence) .order_by("-rank") diff --git a/bookwyrm/templates/shelf/shelf.html b/bookwyrm/templates/shelf/shelf.html index 45a94fed9..71f4bc088 100644 --- a/bookwyrm/templates/shelf/shelf.html +++ b/bookwyrm/templates/shelf/shelf.html @@ -101,7 +101,6 @@ {% plural %} {{ formatted_count }} books {% endblocktrans %} - {% if books.has_other_pages %} {% blocktrans trimmed with start=books.start_index end=books.end_index %} (showing {{ start }}-{{ end }}) @@ -111,6 +110,8 @@ {% endif %} {% endwith %} + {% include 'shelf/shelves_filters.html' with user=user query=query %} + {% if is_self and shelf.id %}
@@ -209,7 +210,17 @@ {% else %} -

{% trans "This shelf is empty." %}

+

+ + {% if shelves_filter_query %} + {% blocktrans trimmed %} + We couldn't find any books that matched {{ shelves_filter_query }} + {% endblocktrans %} + {% else %} + {% trans "This shelf is empty." %} + {% endif %} + +

{% endif %}
diff --git a/bookwyrm/templates/shelf/shelves_filter_field.html b/bookwyrm/templates/shelf/shelves_filter_field.html new file mode 100644 index 000000000..707f033ea --- /dev/null +++ b/bookwyrm/templates/shelf/shelves_filter_field.html @@ -0,0 +1,9 @@ +{% extends 'snippets/filters_panel/filter_field.html' %} +{% load i18n %} + +{% block filter %} +
+ + +
+{% endblock %} diff --git a/bookwyrm/templates/shelf/shelves_filters.html b/bookwyrm/templates/shelf/shelves_filters.html new file mode 100644 index 000000000..ad7fc3dbc --- /dev/null +++ b/bookwyrm/templates/shelf/shelves_filters.html @@ -0,0 +1,5 @@ +{% extends 'snippets/filters_panel/filters_panel.html' %} + +{% block filter_fields %} + {% include 'shelf/shelves_filter_field.html' %} +{% endblock %} diff --git a/bookwyrm/views/search.py b/bookwyrm/views/search.py index 743f33f59..f8eddd1a6 100644 --- a/bookwyrm/views/search.py +++ b/bookwyrm/views/search.py @@ -51,7 +51,7 @@ class Search(View): def api_book_search(request): """Return books via API response""" query = request.GET.get("q") - query = isbn_check(query) + query = isbn_check_and_format(query) min_confidence = request.GET.get("min_confidence", 0) # only return local book results via json so we don't cascade book_results = search(query, min_confidence=min_confidence) @@ -64,7 +64,7 @@ def book_search(request): """the real business is elsewhere""" query = request.GET.get("q") # check if query is isbn - query = isbn_check(query) + query = isbn_check_and_format(query) min_confidence = request.GET.get("min_confidence", 0) search_remote = request.GET.get("remote", False) and request.user.is_authenticated @@ -159,7 +159,7 @@ def list_search(request): return TemplateResponse(request, "search/list.html", data) -def isbn_check(query): +def isbn_check_and_format(query): """isbn10 or isbn13 check, if so remove separators""" if query: su_num = re.sub(r"(?<=\d)\D(?=\d|[xX])", "", query) diff --git a/bookwyrm/views/shelf/shelf.py b/bookwyrm/views/shelf/shelf.py index dbbcc2d3a..a39512fe6 100644 --- a/bookwyrm/views/shelf/shelf.py +++ b/bookwyrm/views/shelf/shelf.py @@ -15,12 +15,14 @@ from bookwyrm import forms, models from bookwyrm.activitypub import ActivitypubResponse from bookwyrm.settings import PAGE_LENGTH from bookwyrm.views.helpers import is_api_request, get_user_from_username +from bookwyrm.book_search import search # pylint: disable=no-self-use class Shelf(View): """shelf page""" + # pylint: disable=R0914 def get(self, request, username, shelf_identifier=None): """display a shelf""" user = get_user_from_username(request.user, username) @@ -32,6 +34,8 @@ class Shelf(View): else: shelves = models.Shelf.privacy_filter(request.user).filter(user=user).all() + shelves_filter_query = request.GET.get("filter") + # get the shelf and make sure the logged in user should be able to see it if shelf_identifier: shelf = get_object_or_404(user.shelf_set, identifier=shelf_identifier) @@ -42,6 +46,7 @@ class Shelf(View): FakeShelf = namedtuple( "Shelf", ("identifier", "name", "user", "books", "privacy") ) + books = ( models.Edition.viewer_aware_objects(request.user) .filter( @@ -50,6 +55,7 @@ class Shelf(View): ) .distinct() ) + shelf = FakeShelf("all", _("All books"), user, books, "public") if is_api_request(request) and shelf_identifier: @@ -86,6 +92,9 @@ class Shelf(View): books = sort_books(books, request.GET.get("sort")) + if shelves_filter_query: + books = search(shelves_filter_query, books=books) + paginated = Paginator( books, PAGE_LENGTH, @@ -103,6 +112,8 @@ class Shelf(View): "page_range": paginated.get_elided_page_range( page.number, on_each_side=2, on_ends=1 ), + "shelves_filter_query": shelves_filter_query, + "size": "small", } return TemplateResponse(request, "shelf/shelf.html", data)