diff --git a/bookwyrm/migrations/0053_auto_20201014_1700.py b/bookwyrm/migrations/0053_auto_20201014_1700.py new file mode 100644 index 00000000..d1306dfe --- /dev/null +++ b/bookwyrm/migrations/0053_auto_20201014_1700.py @@ -0,0 +1,15 @@ +# Generated by Django 3.0.7 on 2020-10-14 17:00 + +from django.contrib.postgres.operations import TrigramExtension +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('bookwyrm', '0052_auto_20201005_2145'), + ] + + operations = [ + TrigramExtension() + ] diff --git a/bookwyrm/outgoing.py b/bookwyrm/outgoing.py index 25a61c46..0efa8a4a 100644 --- a/bookwyrm/outgoing.py +++ b/bookwyrm/outgoing.py @@ -34,7 +34,7 @@ def outbox(request, username): ) -def handle_account_search(query): +def handle_remote_webfinger(query): ''' webfingerin' other servers ''' user = None domain = query.split('@')[1] @@ -61,14 +61,10 @@ def handle_account_search(query): def handle_follow(user, to_follow): ''' someone local wants to follow someone ''' - try: - relationship, _ = models.UserFollowRequest.objects.get_or_create( - user_subject=user, - user_object=to_follow, - ) - except IntegrityError as err: - if err.__cause__.diag.constraint_name != 'userfollowrequest_unique': - raise + relationship, _ = models.UserFollowRequest.objects.get_or_create( + user_subject=user, + user_object=to_follow, + ) activity = relationship.to_activity() broadcast(user, activity, direct_recipients=[to_follow]) diff --git a/bookwyrm/templates/search_results.html b/bookwyrm/templates/search_results.html new file mode 100644 index 00000000..bd5096fe --- /dev/null +++ b/bookwyrm/templates/search_results.html @@ -0,0 +1,45 @@ +{% extends 'layout.html' %} +{% block content %} +
+
+

Matching Books

+ {% for result_set in book_results %} + {% if result_set.results %} +
+ {% if not result_set.connector.local %} +

+ Results from {% if result_set.connector.name %}{{ result_set.connector.name }}{% else %}{{ result_set.connector.identifier }}{% endif %} +

+ {% endif %} + + {% for result in result_set.results %} +
+
+ {% csrf_token %} + + +
+
+ {% endfor %} +
+ {% endif %} + {% endfor %} + {% if not book_results %} +

No books found for "{{ query }}"

+ {% endif %} +
+
+

Matching Users

+ {% if not user_results %} +

No users found for "{{ query }}"

+ {% endif %} + {% for result in user_results %} +
+ {% include 'snippets/avatar.html' with user=result %} + {% include 'snippets/username.html' with user=result show_full=True %} + {% include 'snippets/follow_button.html' with user=result %} +
+ {% endfor %} +
+
+{% endblock %} diff --git a/bookwyrm/templates/user_results.html b/bookwyrm/templates/user_results.html index 9ea169e2..accfdc46 100644 --- a/bookwyrm/templates/user_results.html +++ b/bookwyrm/templates/user_results.html @@ -6,6 +6,7 @@

No results found for "{{ query }}"

{% endif %} {% for result in results %} + {{ result }}
{% include 'snippets/avatar.html' with user=result %} {% include 'snippets/username.html' with user=result show_full=True %} diff --git a/bookwyrm/views.py b/bookwyrm/views.py index 2bc840c0..9217c4b3 100644 --- a/bookwyrm/views.py +++ b/bookwyrm/views.py @@ -2,6 +2,7 @@ import re from django.contrib.auth.decorators import login_required, permission_required +from django.contrib.postgres.search import TrigramSimilarity from django.db.models import Avg, Count, Q from django.http import HttpResponseBadRequest, HttpResponseNotFound,\ JsonResponse @@ -147,22 +148,30 @@ def get_activity_feed(user, filter_level, model=models.Status): def search(request): ''' that search bar up top ''' query = request.GET.get('q') - if re.match(r'\w+@\w+.\w+', query): - # if something looks like a username, search with webfinger - results = outgoing.handle_account_search(query) - return TemplateResponse( - request, 'user_results.html', {'results': results, 'query': query} - ) - - # or just send the question over to book search if is_api_request(request): - # only return local results via json so we don't cause a cascade - results = books_manager.local_search(query) - return JsonResponse([r.__dict__ for r in results], safe=False) + # only return local book results via json so we don't cause a cascade + book_results = books_manager.local_search(query) + return JsonResponse([r.__dict__ for r in book_results], safe=False) - results = books_manager.search(query) - return TemplateResponse(request, 'book_results.html', {'results': results}) + # use webfinger looks like a mastodon style account@domain.com username + if re.match(r'\w+@\w+.\w+', query): + outgoing.handle_remote_webfinger(query) + + # do a local user search + user_results = models.User.objects.annotate( + similarity=TrigramSimilarity('username', query), + ).filter( + similarity__gt=0.1, + ).order_by('-similarity')[:10] + + book_results = books_manager.search(query) + data = { + 'book_results': book_results, + 'user_results': user_results, + 'query': query, + } + return TemplateResponse(request, 'search_results.html', data) @login_required