bookwyrm/bookwyrm/views/search.py

126 lines
4 KiB
Python
Raw Normal View History

2021-03-08 16:49:10 +00:00
""" search views"""
2021-01-13 20:03:27 +00:00
import re
from django.contrib.postgres.search import TrigramSimilarity
2021-05-01 17:47:01 +00:00
from django.core.paginator import Paginator
2021-01-13 20:03:27 +00:00
from django.db.models.functions import Greatest
from django.http import JsonResponse
from django.template.response import TemplateResponse
from django.views import View
from bookwyrm import models
from bookwyrm.connectors import connector_manager
2021-05-01 17:47:01 +00:00
from bookwyrm.settings import PAGE_LENGTH
2021-01-13 20:03:27 +00:00
from bookwyrm.utils import regex
2021-02-01 19:50:47 +00:00
from .helpers import is_api_request, privacy_filter
2021-01-13 20:03:27 +00:00
from .helpers import handle_remote_webfinger
# pylint: disable= no-self-use
class Search(View):
2021-04-26 16:15:42 +00:00
"""search users or books"""
2021-03-08 16:49:10 +00:00
2021-05-01 01:59:02 +00:00
def get(self, request):
2021-04-26 16:15:42 +00:00
"""that search bar up top"""
2021-03-08 16:49:10 +00:00
query = request.GET.get("q")
min_confidence = request.GET.get("min_confidence", 0)
2021-05-01 01:59:02 +00:00
search_type = request.GET.get("type")
search_remote = (
request.GET.get("remote", False) and request.user.is_authenticated
)
2021-01-13 20:03:27 +00:00
if is_api_request(request):
# only return local book results via json so we don't cascade
book_results = connector_manager.local_search(
2021-03-08 16:49:10 +00:00
query, min_confidence=min_confidence
)
2021-01-13 20:03:27 +00:00
return JsonResponse([r.json() for r in book_results], safe=False)
2021-05-20 23:34:32 +00:00
if query and not search_type:
2021-05-01 01:59:02 +00:00
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"
2021-05-01 02:19:10 +00:00
data = {
"query": query or "",
"type": search_type,
2021-05-01 02:56:29 +00:00
"remote": search_remote,
2021-05-01 02:19:10 +00:00
}
2021-05-01 02:56:29 +00:00
if query:
2021-07-28 20:52:16 +00:00
results, search_remote = endpoints[search_type](
2021-05-01 02:56:29 +00:00
query, request.user, min_confidence, search_remote
)
2021-05-02 05:20:23 +00:00
if results:
paginated = Paginator(results, PAGE_LENGTH).get_page(
request.GET.get("page")
)
data["results"] = paginated
2021-07-28 20:52:16 +00:00
data["remote"] = search_remote
2021-05-01 01:35:09 +00:00
2021-05-01 02:19:10 +00:00
return TemplateResponse(request, "search/{:s}.html".format(search_type), data)
2021-05-01 01:06:30 +00:00
2021-05-01 02:56:29 +00:00
def book_search(query, _, min_confidence, search_remote=False):
2021-05-01 02:19:10 +00:00
"""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
2021-07-28 20:52:16 +00:00
return [{"results": results}], False
# if there weere no local results, or the request was for remote, search all sources
2021-07-28 20:52:16 +00:00
return connector_manager.search(query, min_confidence=min_confidence), True
2021-05-01 01:06:30 +00:00
2021-05-01 02:56:29 +00:00
def user_search(query, viewer, *_):
2021-05-01 02:19:10 +00:00
"""cool kids members only user search"""
2021-05-01 01:06:30 +00:00
# logged out viewers can't search users
if not viewer.is_authenticated:
2021-07-28 23:43:49 +00:00
return models.User.objects.none(), None
2021-05-01 01:06:30 +00:00
# use webfinger for mastodon style account@domain.com username to load the user if
# they don't exist locally (handle_remote_webfinger will check the db)
2021-06-18 21:12:56 +00:00
if re.match(regex.FULL_USERNAME, query):
2021-05-01 01:06:30 +00:00
handle_remote_webfinger(query)
2021-05-01 02:19:10 +00:00
return (
models.User.viewer_aware_objects(viewer)
.annotate(
similarity=Greatest(
TrigramSimilarity("username", query),
TrigramSimilarity("localname", query),
2021-03-08 16:49:10 +00:00
)
2021-05-01 02:19:10 +00:00
)
.filter(
similarity__gt=0.5,
)
.order_by("-similarity")[:10]
2021-07-28 20:52:16 +00:00
), None
2021-05-01 01:06:30 +00:00
2021-01-13 20:03:27 +00:00
2021-05-01 02:56:29 +00:00
def list_search(query, viewer, *_):
2021-05-01 01:06:30 +00:00
"""any relevent lists?"""
2021-05-01 02:19:10 +00:00
return (
privacy_filter(
viewer,
models.List.objects,
privacy_levels=["public", "followers"],
)
.annotate(
similarity=Greatest(
TrigramSimilarity("name", query),
TrigramSimilarity("description", query),
2021-02-01 19:50:47 +00:00
)
2021-05-01 02:19:10 +00:00
)
.filter(
similarity__gt=0.1,
)
.order_by("-similarity")[:10]
2021-07-28 20:52:16 +00:00
), None