Merge pull request #1033 from bookwyrm-social/search-changes
Search changes
This commit is contained in:
@ -1,7 +1,5 @@
|
||||
{% extends 'layout.html' %}
|
||||
{% load i18n %}
|
||||
{% load bookwyrm_tags %}
|
||||
{% load humanize %}
|
||||
|
||||
{% block title %}{% trans "Directory" %}{% endblock %}
|
||||
|
||||
@ -41,59 +39,7 @@
|
||||
<div class="columns is-multiline">
|
||||
{% for user in users %}
|
||||
<div class="column is-one-third">
|
||||
<div class="card is-stretchable">
|
||||
<div class="card-content">
|
||||
<div class="media">
|
||||
<a href="{{ user.local_path }}" class="media-left">
|
||||
{% include 'snippets/avatar.html' with user=user large=True %}
|
||||
</a>
|
||||
<div class="media-content">
|
||||
<a href="{{ user.local_path }}" class="is-block mb-2">
|
||||
<span class="title is-4 is-block">{{ user.display_name }}</span>
|
||||
<span class="subtitle is-7 is-block">@{{ user|username }}</span>
|
||||
</a>
|
||||
{% include 'snippets/follow_button.html' with user=user %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{% if user.summary %}
|
||||
{{ user.summary | to_markdown | safe | truncatechars_html:40 }}
|
||||
{% else %} {% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<footer class="card-footer">
|
||||
{% if user != request.user %}
|
||||
{% if user.mutuals %}
|
||||
<div class="card-footer-item">
|
||||
<div class="has-text-centered">
|
||||
<p class="title is-6 mb-0">{{ user.mutuals }}</p>
|
||||
<p class="help">{% blocktrans count counter=user.mutuals %}follower you follow{% plural %}followers you follow{% endblocktrans %}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% elif user.shared_books %}
|
||||
<div class="card-footer-item">
|
||||
<div class="has-text-centered">
|
||||
<p class="title is-6 mb-0">{{ user.shared_books }}</p>
|
||||
<p class="help">{% blocktrans count counter=user.shared_books %}book on your shelves{% plural %}books on your shelves{% endblocktrans %}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<div class="card-footer-item">
|
||||
<div class="has-text-centered">
|
||||
<p class="title is-6 mb-0">{{ user.status_set.count|intword }}</p>
|
||||
<p class="help">{% trans "posts" %}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer-item">
|
||||
<div class="has-text-centered">
|
||||
<p class="title is-6 mb-0">{{ user.last_active_date|naturalday }}</p>
|
||||
<p class="help">{% trans "last active" %}</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
{% include 'directory/user_card.html' %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
57
bookwyrm/templates/directory/user_card.html
Normal file
57
bookwyrm/templates/directory/user_card.html
Normal file
@ -0,0 +1,57 @@
|
||||
{% load i18n %}
|
||||
{% load bookwyrm_tags %}
|
||||
{% load humanize %}
|
||||
|
||||
<div class="card is-stretchable">
|
||||
<div class="card-content">
|
||||
<div class="media">
|
||||
<a href="{{ user.local_path }}" class="media-left">
|
||||
{% include 'snippets/avatar.html' with user=user large=True %}
|
||||
</a>
|
||||
<div class="media-content">
|
||||
<a href="{{ user.local_path }}" class="is-block mb-2">
|
||||
<span class="title is-4 is-block">{{ user.display_name }}</span>
|
||||
<span class="subtitle is-7 is-block">@{{ user|username }}</span>
|
||||
</a>
|
||||
{% include 'snippets/follow_button.html' with user=user %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{% if user.summary %}
|
||||
{{ user.summary | to_markdown | safe | truncatechars_html:40 }}
|
||||
{% else %} {% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<footer class="card-footer">
|
||||
{% if user != request.user %}
|
||||
{% if user.mutuals %}
|
||||
<div class="card-footer-item">
|
||||
<div class="has-text-centered">
|
||||
<p class="title is-6 mb-0">{{ user.mutuals }}</p>
|
||||
<p class="help">{% blocktrans count counter=user.mutuals %}follower you follow{% plural %}followers you follow{% endblocktrans %}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% elif user.shared_books %}
|
||||
<div class="card-footer-item">
|
||||
<div class="has-text-centered">
|
||||
<p class="title is-6 mb-0">{{ user.shared_books }}</p>
|
||||
<p class="help">{% blocktrans count counter=user.shared_books %}book on your shelves{% plural %}books on your shelves{% endblocktrans %}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<div class="card-footer-item">
|
||||
<div class="has-text-centered">
|
||||
<p class="title is-6 mb-0">{{ user.status_set.count|intword }}</p>
|
||||
<p class="help">{% trans "posts" %}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer-item">
|
||||
<div class="has-text-centered">
|
||||
<p class="title is-6 mb-0">{{ user.last_active_date|naturalday }}</p>
|
||||
<p class="help">{% trans "last active" %}</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
74
bookwyrm/templates/search/book.html
Normal file
74
bookwyrm/templates/search/book.html
Normal file
@ -0,0 +1,74 @@
|
||||
{% extends 'search/layout.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block panel %}
|
||||
|
||||
{% if results %}
|
||||
{% with results|first as local_results %}
|
||||
<ul class="block">
|
||||
{% for result in local_results.results %}
|
||||
<li class="pd-4">
|
||||
{% include 'snippets/search_result_text.html' with result=result %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endwith %}
|
||||
|
||||
<div class="block">
|
||||
{% for result_set in results|slice:"1:" %}
|
||||
{% if result_set.results %}
|
||||
<section class="box has-background-white-bis">
|
||||
{% if not result_set.connector.local %}
|
||||
<header class="columns is-mobile">
|
||||
<div class="column">
|
||||
<h3 class="title is-5">
|
||||
Results from
|
||||
<a href="{{ result_set.connector.base_url }}" target="_blank">{{ result_set.connector.name|default:result_set.connector.identifier }}</a>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="column is-narrow">
|
||||
{% trans "Show" as button_text %}
|
||||
{% include 'snippets/toggle/open_button.html' with text=button_text small=True controls_text="more-results-panel" controls_uid=result_set.connector.identifier class="is-small" icon="arrow-down" pressed=forloop.first %}
|
||||
</div>
|
||||
</header>
|
||||
{% endif %}
|
||||
|
||||
<div class="box has-background-white is-shadowless{% if not forloop.first %} is-hidden{% endif %}" id="more-results-panel-{{ result_set.connector.identifier }}">
|
||||
<div class="is-flex is-flex-direction-row-reverse">
|
||||
<div>
|
||||
{% trans "Close" as button_text %}
|
||||
{% include 'snippets/toggle/toggle_button.html' with label=button_text class="delete" nonbutton=True controls_text="more-results-panel" controls_uid=result_set.connector.identifier pressed=forloop.first %}
|
||||
</div>
|
||||
|
||||
<ul class="is-flex-grow-1">
|
||||
{% for result in result_set.results %}
|
||||
<li class="mb-5">
|
||||
{% include 'snippets/search_result_text.html' with result=result remote_result=True %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% if not remote %}
|
||||
<p class="block">
|
||||
{% if request.user.is_authenticated %}
|
||||
<a href="{{ request.path }}?q={{ query }}&type=book&remote=true">
|
||||
{% trans "Load results from other catalogues" %}
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{% url 'login' %}">
|
||||
{% trans "Log in to import or add books." %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
70
bookwyrm/templates/search/layout.html
Normal file
70
bookwyrm/templates/search/layout.html
Normal file
@ -0,0 +1,70 @@
|
||||
{% extends 'layout.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "Search" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="block">
|
||||
<h1 class="title">
|
||||
{% blocktrans %}Search{% endblocktrans %}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<form class="block" action="{% url 'search' %}" method="GET">
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
<input type="input" class="input" name="q" value="{{ query }}" aria-label="{% trans 'Search query' %}">
|
||||
</div>
|
||||
<div class="control">
|
||||
<div class="select" aria-label="{% trans 'Search type' %}">
|
||||
<select name="type">
|
||||
<option value="book" {% if type == "book" %}selected{% endif %}>{% trans "Books" %}</option>
|
||||
{% if request.user.is_authenticated %}
|
||||
<option value="user" {% if type == "user" %}selected{% endif %}>{% trans "Users" %}</option>
|
||||
{% endif %}
|
||||
<option value="list" {% if type == "list" %}selected{% endif %}>{% trans "Lists" %}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button type="submit" class="button is-primary">
|
||||
<span>Search</span>
|
||||
<span class="icon icon-search" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% if query %}
|
||||
<nav class="tabs">
|
||||
<ul>
|
||||
<li{% if type == "book" %} class="is-active"{% endif %}>
|
||||
<a href="{% url 'search' %}?q={{ query }}&type=book">{% trans "Books" %}</a>
|
||||
</li>
|
||||
{% if request.user.is_authenticated %}
|
||||
<li{% if type == "user" %} class="is-active"{% endif %}>
|
||||
<a href="{% url 'search' %}?q={{ query }}&type=user">{% trans "Users" %}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li{% if type == "list" %} class="is-active"{% endif %}>
|
||||
<a href="{% url 'search' %}?q={{ query }}&type=list">{% trans "Lists" %}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<section class="block">
|
||||
{% if not results %}
|
||||
<p>
|
||||
<em>{% blocktrans %}No results found for "{{ query }}"{% endblocktrans %}</em>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% block panel %}
|
||||
{% endblock %}
|
||||
|
||||
<div>
|
||||
{% include 'snippets/pagination.html' with page=results path=request.path %}
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
7
bookwyrm/templates/search/list.html
Normal file
7
bookwyrm/templates/search/list.html
Normal file
@ -0,0 +1,7 @@
|
||||
{% extends 'search/layout.html' %}
|
||||
|
||||
{% block panel %}
|
||||
|
||||
{% include 'lists/list_items.html' with lists=results %}
|
||||
|
||||
{% endblock %}
|
14
bookwyrm/templates/search/user.html
Normal file
14
bookwyrm/templates/search/user.html
Normal file
@ -0,0 +1,14 @@
|
||||
{% extends 'search/layout.html' %}
|
||||
{% load bookwyrm_tags %}
|
||||
|
||||
{% block panel %}
|
||||
|
||||
<div class="columns is-multiline">
|
||||
{% for user in results %}
|
||||
<div class="column is-one-third">
|
||||
{% include 'directory/user_card.html' %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
@ -1,133 +0,0 @@
|
||||
{% extends 'layout.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "Search Results" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% with book_results|first as local_results %}
|
||||
<div class="block">
|
||||
<h1 class="title">{% blocktrans %}Search Results for "{{ query }}"{% endblocktrans %}</h1>
|
||||
</div>
|
||||
|
||||
<div class="block columns">
|
||||
<div class="column">
|
||||
<h2 class="title is-4">{% trans "Matching Books" %}</h2>
|
||||
<section class="block">
|
||||
{% if not local_results.results %}
|
||||
<p><em>{% blocktrans %}No books found for "{{ query }}"{% endblocktrans %}</em></p>
|
||||
{% if not user.is_authenticated %}
|
||||
<p>
|
||||
<a href="{% url 'login' %}">{% trans "Log in to import or add books." %}</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<ul>
|
||||
{% for result in local_results.results %}
|
||||
<li class="pd-4">
|
||||
{% include 'snippets/search_result_text.html' with result=result %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
{% if request.user.is_authenticated %}
|
||||
{% if book_results|slice:":1" and local_results.results %}
|
||||
<div class="block">
|
||||
<h3 class="title is-6">
|
||||
{% trans "Didn't find what you were looking for?" %}
|
||||
</h3>
|
||||
{% trans "Show results from other catalogues" as button_text %}
|
||||
{% include 'snippets/toggle/open_button.html' with text=button_text small=True controls_text="more-results" %}
|
||||
|
||||
{% if local_results.results %}
|
||||
{% trans "Hide results from other catalogues" as button_text %}
|
||||
{% include 'snippets/toggle/close_button.html' with text=button_text small=True controls_text="more-results" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="{% if local_results.results %}is-hidden{% endif %}" id="more-results">
|
||||
{% for result_set in book_results|slice:"1:" %}
|
||||
{% if result_set.results %}
|
||||
<section class="box has-background-white-bis">
|
||||
{% if not result_set.connector.local %}
|
||||
<header class="columns is-mobile">
|
||||
<div class="column">
|
||||
<h3 class="title is-5">
|
||||
Results from
|
||||
<a href="{{ result_set.connector.base_url }}" target="_blank">{{ result_set.connector.name|default:result_set.connector.identifier }}</a>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="column is-narrow">
|
||||
{% trans "Show" as button_text %}
|
||||
{% include 'snippets/toggle/open_button.html' with text=button_text small=True controls_text="more-results-panel" controls_uid=result_set.connector.identifier class="is-small" icon="arrow-down" pressed=forloop.first %}
|
||||
</div>
|
||||
</header>
|
||||
{% endif %}
|
||||
|
||||
<div class="box has-background-white is-shadowless{% if not forloop.first %} is-hidden{% endif %}" id="more-results-panel-{{ result_set.connector.identifier }}">
|
||||
<div class="is-flex is-flex-direction-row-reverse">
|
||||
<div>
|
||||
{% trans "Close" as button_text %}
|
||||
{% include 'snippets/toggle/toggle_button.html' with label=button_text class="delete" nonbutton=True controls_text="more-results-panel" controls_uid=result_set.connector.identifier pressed=forloop.first %}
|
||||
</div>
|
||||
|
||||
<ul class="is-flex-grow-1">
|
||||
{% for result in result_set.results %}
|
||||
<li class="mb-5">
|
||||
{% include 'snippets/search_result_text.html' with result=result remote_result=True %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="block">
|
||||
<a href="/create-book">Manually add book</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="column">
|
||||
{% if request.user.is_authenticated %}
|
||||
<section class="box">
|
||||
<h2 class="title is-4">{% trans "Matching Users" %}</h2>
|
||||
{% if not user_results %}
|
||||
<p><em>{% blocktrans %}No users found for "{{ query }}"{% endblocktrans %}</em></p>
|
||||
{% endif %}
|
||||
<ul>
|
||||
{% for result in user_results %}
|
||||
<li class="block">
|
||||
<a href="{{ result.local_path }}">
|
||||
{% include 'snippets/avatar.html' with user=result %}
|
||||
{{ result.display_name }}
|
||||
</a> ({{ result.username }})
|
||||
{% include 'snippets/follow_button.html' with user=result %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</section>
|
||||
{% endif %}
|
||||
<section class="box">
|
||||
<h2 class="title is-4">{% trans "Lists" %}</h2>
|
||||
{% if not list_results %}
|
||||
<p><em>{% blocktrans %}No lists found for "{{ query }}"{% endblocktrans %}</em></p>
|
||||
{% endif %}
|
||||
{% for result in list_results %}
|
||||
<div class="block">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="{% url 'list' result.id %}">{{ result.name }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
{% endwith %}
|
||||
{% endblock %}
|
@ -2,6 +2,7 @@
|
||||
import json
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.http import JsonResponse
|
||||
from django.template.response import TemplateResponse
|
||||
from django.test import TestCase
|
||||
@ -12,7 +13,7 @@ from bookwyrm.connectors import abstract_connector
|
||||
from bookwyrm.settings import DOMAIN
|
||||
|
||||
|
||||
class ShelfViews(TestCase):
|
||||
class Views(TestCase):
|
||||
"""tag views"""
|
||||
|
||||
def setUp(self):
|
||||
@ -52,7 +53,7 @@ class ShelfViews(TestCase):
|
||||
self.assertEqual(data[0]["title"], "Test Book")
|
||||
self.assertEqual(data[0]["key"], "https://%s/book/%d" % (DOMAIN, self.book.id))
|
||||
|
||||
def test_search_html_response(self):
|
||||
def test_search_books(self):
|
||||
"""searches remote connectors"""
|
||||
view = views.Search.as_view()
|
||||
|
||||
@ -92,7 +93,7 @@ class ShelfViews(TestCase):
|
||||
connector=connector,
|
||||
)
|
||||
|
||||
request = self.factory.get("", {"q": "Test Book"})
|
||||
request = self.factory.get("", {"q": "Test Book", "remote": True})
|
||||
request.user = self.local_user
|
||||
with patch("bookwyrm.views.search.is_api_request") as is_api:
|
||||
is_api.return_value = False
|
||||
@ -101,19 +102,44 @@ class ShelfViews(TestCase):
|
||||
response = view(request)
|
||||
self.assertIsInstance(response, TemplateResponse)
|
||||
response.render()
|
||||
self.assertEqual(
|
||||
response.context_data["book_results"][0].title, "Gideon the Ninth"
|
||||
)
|
||||
self.assertEqual(response.context_data["results"][0].title, "Gideon the Ninth")
|
||||
|
||||
def test_search_html_response_users(self):
|
||||
def test_search_users(self):
|
||||
"""searches remote connectors"""
|
||||
view = views.Search.as_view()
|
||||
request = self.factory.get("", {"q": "mouse"})
|
||||
request = self.factory.get("", {"q": "mouse", "type": "user"})
|
||||
request.user = self.local_user
|
||||
with patch("bookwyrm.views.search.is_api_request") as is_api:
|
||||
is_api.return_value = False
|
||||
with patch("bookwyrm.connectors.connector_manager.search"):
|
||||
response = view(request)
|
||||
response = view(request)
|
||||
|
||||
self.assertIsInstance(response, TemplateResponse)
|
||||
response.render()
|
||||
self.assertEqual(response.context_data["user_results"][0], self.local_user)
|
||||
self.assertEqual(response.context_data["results"][0], self.local_user)
|
||||
|
||||
def test_search_users_logged_out(self):
|
||||
"""searches remote connectors"""
|
||||
view = views.Search.as_view()
|
||||
request = self.factory.get("", {"q": "mouse", "type": "user"})
|
||||
|
||||
anonymous_user = AnonymousUser
|
||||
anonymous_user.is_authenticated = False
|
||||
request.user = anonymous_user
|
||||
|
||||
response = view(request)
|
||||
|
||||
response.render()
|
||||
self.assertEqual(response.context_data["results"].object_list.count(), 0)
|
||||
|
||||
def test_search_lists(self):
|
||||
"""searches remote connectors"""
|
||||
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
|
||||
booklist = models.List.objects.create(
|
||||
user=self.local_user, name="test list"
|
||||
)
|
||||
view = views.Search.as_view()
|
||||
request = self.factory.get("", {"q": "test", "type": "list"})
|
||||
request.user = self.local_user
|
||||
response = view(request)
|
||||
|
||||
self.assertIsInstance(response, TemplateResponse)
|
||||
response.render()
|
||||
self.assertEqual(response.context_data["results"][0], booklist)
|
||||
|
@ -163,7 +163,7 @@ urlpatterns = [
|
||||
name="direct-messages-user",
|
||||
),
|
||||
# search
|
||||
re_path(r"^search/?$", views.Search.as_view()),
|
||||
re_path(r"^search/?$", views.Search.as_view(), name="search"),
|
||||
# imports
|
||||
re_path(r"^import/?$", views.Import.as_view()),
|
||||
re_path(r"^import/(\d+)/?$", views.ImportStatus.as_view()),
|
||||
|
@ -2,6 +2,7 @@
|
||||
import re
|
||||
|
||||
from django.contrib.postgres.search import TrigramSimilarity
|
||||
from django.core.paginator import Paginator
|
||||
from django.db.models.functions import Greatest
|
||||
from django.http import JsonResponse
|
||||
from django.template.response import TemplateResponse
|
||||
@ -9,6 +10,7 @@ from django.views import View
|
||||
|
||||
from bookwyrm import models
|
||||
from bookwyrm.connectors import connector_manager
|
||||
from bookwyrm.settings import PAGE_LENGTH
|
||||
from bookwyrm.utils import regex
|
||||
from .helpers import is_api_request, privacy_filter
|
||||
from .helpers import handle_remote_webfinger
|
||||
@ -22,6 +24,10 @@ class Search(View):
|
||||
"""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
|
||||
@ -30,48 +36,86 @@ class Search(View):
|
||||
)
|
||||
return JsonResponse([r.json() for r in book_results], safe=False)
|
||||
|
||||
data = {"query": query or ""}
|
||||
if not search_type:
|
||||
search_type = "user" if "@" in query else "book"
|
||||
|
||||
# use webfinger for mastodon style account@domain.com username
|
||||
if query and re.match(regex.full_username, query):
|
||||
handle_remote_webfinger(query)
|
||||
endpoints = {
|
||||
"book": book_search,
|
||||
"user": user_search,
|
||||
"list": list_search,
|
||||
}
|
||||
if not search_type in endpoints:
|
||||
search_type = "book"
|
||||
|
||||
# do a user search
|
||||
if request.user.is_authenticated:
|
||||
data["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]
|
||||
data = {
|
||||
"query": query or "",
|
||||
"type": search_type,
|
||||
"remote": search_remote,
|
||||
}
|
||||
if query:
|
||||
results = endpoints[search_type](
|
||||
query, request.user, min_confidence, search_remote
|
||||
)
|
||||
paginated = Paginator(results, PAGE_LENGTH).get_page(
|
||||
request.GET.get("page")
|
||||
)
|
||||
data["results"] = paginated
|
||||
|
||||
# any relevent lists?
|
||||
data["list_results"] = (
|
||||
privacy_filter(
|
||||
request.user,
|
||||
models.List.objects,
|
||||
privacy_levels=["public", "followers"],
|
||||
return TemplateResponse(request, "search/{:s}.html".format(search_type), data)
|
||||
|
||||
|
||||
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}]
|
||||
|
||||
|
||||
def user_search(query, viewer, *_):
|
||||
"""cool kids members only user search"""
|
||||
# logged out viewers can't search users
|
||||
if not viewer.is_authenticated:
|
||||
return models.User.objects.none()
|
||||
|
||||
# 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)
|
||||
if re.match(regex.full_username, query):
|
||||
handle_remote_webfinger(query)
|
||||
|
||||
return (
|
||||
models.User.viewer_aware_objects(viewer)
|
||||
.annotate(
|
||||
similarity=Greatest(
|
||||
TrigramSimilarity("username", query),
|
||||
TrigramSimilarity("localname", query),
|
||||
)
|
||||
.annotate(
|
||||
similarity=Greatest(
|
||||
TrigramSimilarity("name", query),
|
||||
TrigramSimilarity("description", query),
|
||||
)
|
||||
)
|
||||
.filter(
|
||||
similarity__gt=0.1,
|
||||
)
|
||||
.order_by("-similarity")[:10]
|
||||
)
|
||||
|
||||
data["book_results"] = connector_manager.search(
|
||||
query, min_confidence=min_confidence
|
||||
.filter(
|
||||
similarity__gt=0.5,
|
||||
)
|
||||
return TemplateResponse(request, "search_results.html", data)
|
||||
.order_by("-similarity")[:10]
|
||||
)
|
||||
|
||||
|
||||
def list_search(query, viewer, *_):
|
||||
"""any relevent lists?"""
|
||||
return (
|
||||
privacy_filter(
|
||||
viewer,
|
||||
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]
|
||||
)
|
||||
|
Reference in New Issue
Block a user