bookwyrm-mastodon/bookwyrm/vviews.py

143 lines
4.4 KiB
Python

''' views for pages you can go to in the application '''
import re
from django.contrib.postgres.search import TrigramSimilarity
from django.db.models.functions import Greatest
from django.http import HttpResponseNotFound, JsonResponse
from django.template.response import TemplateResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_GET
from bookwyrm import outgoing
from bookwyrm import models
from bookwyrm.activitypub import ActivitypubResponse
from bookwyrm.connectors import connector_manager
from bookwyrm.utils import regex
def get_edition(book_id):
''' look up a book in the db and return an edition '''
book = models.Book.objects.select_subclasses().get(id=book_id)
if isinstance(book, models.Work):
book = book.get_default_edition()
return book
def get_user_from_username(username):
''' helper function to resolve a localname or a username to a user '''
# raises DoesNotExist if user is now found
try:
return models.User.objects.get(localname=username)
except models.User.DoesNotExist:
return models.User.objects.get(username=username)
def is_api_request(request):
''' check whether a request is asking for html or data '''
return 'json' in request.headers.get('Accept') or \
request.path[-5:] == '.json'
def server_error_page(request):
''' 500 errors '''
return TemplateResponse(
request, 'error.html', {'title': 'Oops!'}, status=500)
def not_found_page(request, _):
''' 404s '''
return TemplateResponse(
request, 'notfound.html', {'title': 'Not found'}, status=404)
@require_GET
def search(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 cause a 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 account@domain.com username
if re.match(r'\B%s' % regex.full_username, query):
outgoing.handle_remote_webfinger(query)
# do a local 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]
book_results = connector_manager.search(
query, min_confidence=min_confidence)
data = {
'title': 'Search Results',
'book_results': book_results,
'user_results': user_results,
'query': query,
}
return TemplateResponse(request, 'search_results.html', data)
@csrf_exempt
@require_GET
def user_shelves_page(request, username):
''' list of followers '''
return shelf_page(request, username, None)
@require_GET
def shelf_page(request, username, shelf_identifier):
''' display a shelf '''
try:
user = get_user_from_username(username)
except models.User.DoesNotExist:
return HttpResponseNotFound()
if shelf_identifier:
shelf = user.shelf_set.get(identifier=shelf_identifier)
else:
shelf = user.shelf_set.first()
is_self = request.user == user
shelves = user.shelf_set
if not is_self:
follower = user.followers.filter(id=request.user.id).exists()
# make sure the user has permission to view the shelf
if shelf.privacy == 'direct' or \
(shelf.privacy == 'followers' and not follower):
return HttpResponseNotFound()
# only show other shelves that should be visible
if follower:
shelves = shelves.filter(privacy__in=['public', 'followers'])
else:
shelves = shelves.filter(privacy='public')
if is_api_request(request):
return ActivitypubResponse(shelf.to_activity(**request.GET))
books = models.ShelfBook.objects.filter(
added_by=user, shelf=shelf
).order_by('-updated_date').all()
data = {
'title': '%s\'s %s shelf' % (user.display_name, shelf.name),
'user': user,
'is_self': is_self,
'shelves': shelves.all(),
'shelf': shelf,
'books': [b.book for b in books],
}
return TemplateResponse(request, 'shelf.html', data)