def get(self, request, author_id): """landing page for an author""" author = get_object_or_404(models.Author, id=author_id) if is_api_request(request): return ActivitypubResponse(author.to_activity()) default_editions = models.Edition.objects.filter( parent_work=OuterRef("parent_work") ).order_by("-edition_rank") books = ( models.Edition.viewer_aware_objects(request.user) .filter(Q(authors=author) | Q(parent_work__authors=author)) .annotate(default_id=Subquery(default_editions.values("id")[:1])) .filter(default_id=F("id")) .order_by("-first_published_date", "-published_date", "-created_date") .prefetch_related("authors") ).distinct() paginated = Paginator(books, PAGE_LENGTH) page = paginated.get_page(request.GET.get("page")) data = { "author": author, "books": page, "page_range": paginated.get_elided_page_range( page.number, on_each_side=2, on_ends=1 ), } return TemplateResponse(request, "author/author.html", data)
def get(self, request, username, status_id): ''' display a particular status (and replies, etc) ''' try: user = get_user_from_username(request.user, username) status = models.Status.objects.select_subclasses().get( id=status_id, deleted=False) except ValueError: return HttpResponseNotFound() # the url should have the poster's username in it if user != status.user: return HttpResponseNotFound() # make sure the user is authorized to see the status if not object_visible_to_user(request.user, status): return HttpResponseNotFound() if is_api_request(request): return ActivitypubResponse( status.to_activity(pure=not is_bookwyrm_request(request))) data = { **feed_page_data(request.user), **{ 'title': 'Status by %s' % user.username, 'status': status, } } return TemplateResponse(request, 'feed/status.html', data)
def get(self, request, book_id): """ list of editions of a book """ work = get_object_or_404(models.Work, id=book_id) try: page = int(request.GET.get("page", 1)) except ValueError: page = 1 if is_api_request(request): return ActivitypubResponse(work.to_edition_list(**request.GET)) filters = {} if request.GET.get("language"): filters["languages__contains"] = [request.GET.get("language")] if request.GET.get("format"): filters["physical_format__iexact"] = request.GET.get("format") editions = work.editions.order_by("-edition_rank").all() languages = set(sum([e.languages for e in editions], [])) paginated = Paginator(editions.filter(**filters).all(), PAGE_LENGTH) data = { "editions": paginated.get_page(page), "work": work, "languages": languages, "formats": set( e.physical_format.lower() for e in editions if e.physical_format ), } return TemplateResponse(request, "book/editions.html", data)
def get(self, request, username, status_id): """ display a particular status (and replies, etc) """ try: user = get_user_from_username(request.user, username) status = models.Status.objects.select_subclasses().get( id=status_id, deleted=False) except (ValueError, models.Status.DoesNotExist): return HttpResponseNotFound() # the url should have the poster's username in it if user != status.user: return HttpResponseNotFound() # make sure the user is authorized to see the status if not status.visible_to_user(request.user): return HttpResponseNotFound() if is_api_request(request): return ActivitypubResponse( status.to_activity(pure=not is_bookwyrm_request(request))) data = { **feed_page_data(request.user), **{ "status": status, }, } return TemplateResponse(request, "feed/status.html", data)
def get(self, request, author_id, slug=None): """landing page for an author""" author = get_object_or_404(models.Author, id=author_id) if is_api_request(request): return ActivitypubResponse(author.to_activity()) if redirect_local_path := maybe_redirect_local_path(request, author): return redirect_local_path
def get(self, request, book_id): """list of editions of a book""" work = get_object_or_404(models.Work, id=book_id) if is_api_request(request): return ActivitypubResponse(work.to_edition_list(**request.GET)) filters = {} if request.GET.get("language"): filters["languages__contains"] = [request.GET.get("language")] if request.GET.get("format"): filters["physical_format__iexact"] = request.GET.get("format") editions = work.editions.order_by("-edition_rank") languages = set(sum(editions.values_list("languages", flat=True), [])) editions = editions.filter(**filters) query = request.GET.get("q") if query: searchable_array_fields = ["languages", "publishers"] searchable_fields = [ "title", "physical_format", "isbn_10", "isbn_13", "oclc_number", "asin", ] search_filter_entries = [{ f"{f}__icontains": query } for f in searchable_fields] + [{ f"{f}__iexact": query } for f in searchable_array_fields] editions = editions.filter( reduce(operator.or_, (Q(**f) for f in search_filter_entries))) paginated = Paginator(editions, PAGE_LENGTH) page = paginated.get_page(request.GET.get("page")) data = { "editions": page, "page_range": paginated.get_elided_page_range(page.number, on_each_side=2, on_ends=1), "work": work, "work_form": forms.EditionFromWorkForm(instance=work), "languages": languages, "formats": set(e.physical_format.lower() for e in editions if e.physical_format), } return TemplateResponse(request, "book/editions/editions.html", data)
def get(self, request, username): """profile page for a user""" user = get_user_from_username(request.user, username) if is_api_request(request): # we have a json request return ActivitypubResponse(user.to_activity()) # otherwise we're at a UI view shelf_preview = [] # only show other shelves that should be visible shelves = user.shelf_set is_self = request.user.id == user.id if not is_self: follower = user.followers.filter(id=request.user.id).exists() if follower: shelves = shelves.filter(privacy__in=["public", "followers"]) else: shelves = shelves.filter(privacy="public") for user_shelf in shelves.all(): if not user_shelf.books.count(): continue shelf_preview.append({ "name": user_shelf.name, "local_path": user_shelf.local_path, "books": user_shelf.books.all()[:3], "size": user_shelf.books.count(), }) if len(shelf_preview) > 2: break # user's posts activities = (privacy_filter( request.user, user.status_set.select_subclasses(), ).select_related("reply_parent").prefetch_related( "mention_books", "mention_users")) paginated = Paginator(activities, PAGE_LENGTH) goal = models.AnnualGoal.objects.filter( user=user, year=timezone.now().year).first() if goal and not goal.visible_to_user(request.user): goal = None data = { "user": user, "is_self": is_self, "shelves": shelf_preview, "shelf_count": shelves.count(), "activities": paginated.get_page(request.GET.get("page", 1)), "goal": goal, } return TemplateResponse(request, "user/user.html", data)
def get(self, request, book_id): """ list of editions of a book """ work = get_object_or_404(models.Work, id=book_id) if is_api_request(request): return ActivitypubResponse(work.to_edition_list(**request.GET)) data = { "editions": work.editions.order_by("-edition_rank").all(), "work": work, } return TemplateResponse(request, "editions.html", data)
def get(self, request, book_id): ''' list of editions of a book ''' work = get_object_or_404(models.Work, id=book_id) if is_api_request(request): return ActivitypubResponse(work.to_edition_list(**request.GET)) data = { 'title': 'Editions of %s' % work.title, 'editions': work.editions.order_by('-edition_rank').all(), 'work': work, } return TemplateResponse(request, 'editions.html', data)
def get(self, request, list_id, **kwargs): """display a book list""" add_failed = kwargs.get("add_failed", False) add_succeeded = kwargs.get("add_succeeded", False) book_list = get_object_or_404(models.List, id=list_id) book_list.raise_visible_to_user(request.user) if is_api_request(request): return ActivitypubResponse(book_list.to_activity(**request.GET)) if r := maybe_redirect_local_path(request, book_list): return r
def get(self, request, username, status_id): """ ordered collection of replies to a status """ # the html view is the same as Status if not is_api_request(request): status_view = Status.as_view() return status_view(request, username, status_id) # the json view is different than Status status = models.Status.objects.get(id=status_id) if status.user.localname != username: return HttpResponseNotFound() return ActivitypubResponse(status.to_replies(**request.GET))
def get(self, request, username, shelf_identifier): """ display a shelf """ try: user = get_user_from_username(request.user, username) except models.User.DoesNotExist: return HttpResponseNotFound() try: page = int(request.GET.get("page", 1)) except ValueError: page = 1 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)) paginated = Paginator( models.ShelfBook.objects.filter( user=user, shelf=shelf).order_by("-updated_date").all(), PAGE_LENGTH, ) data = { "user": user, "is_self": is_self, "shelves": shelves.all(), "shelf": shelf, "books": paginated.page(page), } return TemplateResponse(request, "user/shelf.html", data)
def get(self, request, tag_id): """ see books related to a tag """ tag_obj = get_object_or_404(models.Tag, identifier=tag_id) if is_api_request(request): return ActivitypubResponse(tag_obj.to_activity(**request.GET)) books = models.Edition.objects.filter( usertag__tag__identifier=tag_id).distinct() data = { "books": books, "tag": tag_obj, } return TemplateResponse(request, "tag.html", data)
def get(self, request, username): """list of followers""" user = get_user_from_username(request.user, username) if is_api_request(request): return ActivitypubResponse(user.to_followers_activity(**request.GET)) paginated = Paginator(user.followers.all(), PAGE_LENGTH) data = { "user": user, "is_self": request.user.id == user.id, "follow_list": paginated.get_page(request.GET.get("page")), } return TemplateResponse(request, "user/relationships/followers.html", data)
def get(self, request, author_id): """ landing page for an author """ author = get_object_or_404(models.Author, id=author_id) if is_api_request(request): return ActivitypubResponse(author.to_activity()) books = models.Work.objects.filter( Q(authors=author) | Q(editions__authors=author)).distinct() data = { "author": author, "books": [b.get_default_edition() for b in books], } return TemplateResponse(request, "author.html", data)
def get(self, request, username, shelf_identifier=None): """ display a shelf """ try: user = get_user_from_username(request.user, username) except models.User.DoesNotExist: return HttpResponseNotFound() try: page = int(request.GET.get("page", 1)) except ValueError: page = 1 shelves = privacy_filter(request.user, user.shelf_set) # get the shelf and make sure the logged in user should be able to see it if shelf_identifier: try: shelf = user.shelf_set.get(identifier=shelf_identifier) except models.Shelf.DoesNotExist: return HttpResponseNotFound() if not shelf.visible_to_user(request.user): return HttpResponseNotFound() # this is a constructed "all books" view, with a fake "shelf" obj else: FakeShelf = namedtuple( "Shelf", ("identifier", "name", "user", "books", "privacy")) books = models.Edition.objects.filter( shelfbook__shelf__in=shelves.all()).distinct() shelf = FakeShelf("all", _("All books"), user, books, "public") is_self = request.user == user if is_api_request(request): return ActivitypubResponse(shelf.to_activity(**request.GET)) paginated = Paginator( shelf.books.order_by("-updated_date").all(), PAGE_LENGTH, ) data = { "user": user, "is_self": is_self, "shelves": shelves.all(), "shelf": shelf, "books": paginated.get_page(page), } return TemplateResponse(request, "user/shelf.html", data)
def get(self, 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( user=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, 'user/shelf.html', data)
def get(self, request, tag_id): ''' see books related to a tag ''' tag_obj = models.Tag.objects.filter(identifier=tag_id).first() if not tag_obj: return HttpResponseNotFound() if is_api_request(request): return ActivitypubResponse(tag_obj.to_activity(**request.GET)) books = models.Edition.objects.filter( usertag__tag__identifier=tag_id).distinct() data = { 'title': tag_obj.name, 'books': books, 'tag': tag_obj, } return TemplateResponse(request, 'tag.html', data)
def get(self, request, username): """list of followers""" user = get_user_from_username(request.user, username) if is_api_request(request): return ActivitypubResponse(user.to_following_activity(**request.GET)) if user.hide_follows: raise PermissionDenied() following = annotate_if_follows(request.user, user.following) paginated = Paginator(following.all(), PAGE_LENGTH) data = { "user": user, "is_self": request.user.id == user.id, "follow_list": paginated.get_page(request.GET.get("page")), } return TemplateResponse(request, "user/relationships/following.html", data)
def get(self, request, username, status_id, slug=None): """display a particular status (and replies, etc)""" user = get_user_from_username(request.user, username) status = get_object_or_404( models.Status.objects.select_subclasses(), user=user, id=status_id, deleted=False, ) # make sure the user is authorized to see the status status.raise_visible_to_user(request.user) if is_api_request(request): return ActivitypubResponse( status.to_activity(pure=not is_bookwyrm_request(request))) if redirect_local_path := maybe_redirect_local_path(request, status): return redirect_local_path
def get(self, request, username): """ list of followers """ try: user = get_user_from_username(request.user, username) except models.User.DoesNotExist: return HttpResponseNotFound() # make sure we're not blocked if is_blocked(request.user, user): return HttpResponseNotFound() if is_api_request(request): return ActivitypubResponse( user.to_followers_activity(**request.GET)) data = { "user": user, "is_self": request.user.id == user.id, "followers": user.followers.all(), } return TemplateResponse(request, "user/followers.html", data)
def get(self, request, username): ''' list of followers ''' try: user = get_user_from_username(request.user, username) except models.User.DoesNotExist: return HttpResponseNotFound() # make sure we're not blocked if is_blocked(request.user, user): return HttpResponseNotFound() if is_api_request(request): return ActivitypubResponse( user.to_followers_activity(**request.GET)) data = { 'title': '%s: followers' % user.name, 'user': user, 'is_self': request.user.id == user.id, 'followers': user.followers.all(), } return TemplateResponse(request, 'user/followers.html', data)
def get(self, request, list_id): ''' display a book list ''' book_list = get_object_or_404(models.List, id=list_id) if not object_visible_to_user(request.user, book_list): return HttpResponseNotFound() if is_api_request(request): return ActivitypubResponse(book_list.to_activity(**request.GET)) query = request.GET.get('q') suggestions = None if query and request.user.is_authenticated: # search for books suggestions = connector_manager.local_search(query, raw=True) elif request.user.is_authenticated: # just suggest whatever books are nearby suggestions = request.user.shelfbook_set.filter(~Q( book__in=book_list.books.all())) suggestions = [s.book for s in suggestions[:5]] if len(suggestions) < 5: suggestions += [ s.default_edition for s in \ models.Work.objects.filter( ~Q(editions__in=book_list.books.all()), ).order_by('-updated_date') ][:5 - len(suggestions)] data = { 'title': '%s | Lists' % book_list.name, 'list': book_list, 'items': book_list.listitem_set.filter(approved=True), 'pending_count': book_list.listitem_set.filter(approved=False).count(), 'suggested_books': suggestions, 'list_form': forms.ListForm(instance=book_list), 'query': query or '' } return TemplateResponse(request, 'lists/list.html', data)
def get(self, request, author_id): """landing page for an author""" author = get_object_or_404(models.Author, id=author_id) if is_api_request(request): return ActivitypubResponse(author.to_activity()) books = models.Work.objects.filter( authors=author, editions__authors=author).distinct() paginated = Paginator(books, PAGE_LENGTH) page = paginated.get_page(request.GET.get("page")) data = { "author": author, "books": page, "page_range": paginated.get_elided_page_range(page.number, on_each_side=2, on_ends=1), } return TemplateResponse(request, "author/author.html", data)
def get(self, request, book_id, **kwargs): """info about a book""" if is_api_request(request): book = get_object_or_404(models.Book.objects.select_subclasses(), id=book_id) return ActivitypubResponse(book.to_activity()) user_statuses = (kwargs.get("user_statuses", False) if request.user.is_authenticated else False) # it's safe to use this OR because edition and work and subclasses of the same # table, so they never have clashing IDs book = (models.Edition.viewer_aware_objects( request.user).filter(Q(id=book_id) | Q( parent_work__id=book_id)).order_by("-edition_rank"). select_related("parent_work").prefetch_related( "authors", "file_links").first()) if not book or not book.parent_work: raise Http404() if redirect_local_path := not user_statuses and maybe_redirect_local_path( request, book): return redirect_local_path
def get(self, request, list_id): """ display a book list """ book_list = get_object_or_404(models.List, id=list_id) if not object_visible_to_user(request.user, book_list): return HttpResponseNotFound() if is_api_request(request): return ActivitypubResponse(book_list.to_activity(**request.GET)) query = request.GET.get("q") suggestions = None if query and request.user.is_authenticated: # search for books suggestions = connector_manager.local_search(query, raw=True) elif request.user.is_authenticated: # just suggest whatever books are nearby suggestions = request.user.shelfbook_set.filter(~Q( book__in=book_list.books.all())) suggestions = [s.book for s in suggestions[:5]] if len(suggestions) < 5: suggestions += [ s.default_edition for s in models.Work.objects.filter( ~Q(editions__in=book_list.books.all()), ).order_by( "-updated_date") ][:5 - len(suggestions)] data = { "list": book_list, "items": book_list.listitem_set.filter(approved=True), "pending_count": book_list.listitem_set.filter(approved=False).count(), "suggested_books": suggestions, "list_form": forms.ListForm(instance=book_list), "query": query or "", } return TemplateResponse(request, "lists/list.html", data)
def get(self, request, username): ''' profile page for a user ''' try: user = get_user_from_username(request.user, username) except models.User.DoesNotExist: return HttpResponseNotFound() # make sure we're not blocked if is_blocked(request.user, user): return HttpResponseNotFound() if is_api_request(request): # we have a json request return ActivitypubResponse(user.to_activity()) # otherwise we're at a UI view try: page = int(request.GET.get('page', 1)) except ValueError: page = 1 shelf_preview = [] # only show other shelves that should be visible shelves = user.shelf_set is_self = request.user.id == user.id if not is_self: follower = user.followers.filter(id=request.user.id).exists() if follower: shelves = shelves.filter(privacy__in=['public', 'followers']) else: shelves = shelves.filter(privacy='public') for user_shelf in shelves.all(): if not user_shelf.books.count(): continue shelf_preview.append({ 'name': user_shelf.name, 'local_path': user_shelf.local_path, 'books': user_shelf.books.all()[:3], 'size': user_shelf.books.count(), }) if len(shelf_preview) > 2: break # user's posts activities = get_activity_feed(request.user, ['public', 'unlisted', 'followers'], queryset=user.status_set) paginated = Paginator(activities, PAGE_LENGTH) goal = models.AnnualGoal.objects.filter( user=user, year=timezone.now().year).first() if not object_visible_to_user(request.user, goal): goal = None data = { 'title': user.name, 'user': user, 'is_self': is_self, 'shelves': shelf_preview, 'shelf_count': shelves.count(), 'activities': paginated.page(page), 'goal': goal, } return TemplateResponse(request, 'user/user.html', data)
def get(self, request, username, shelf_identifier=None): """display a shelf""" user = get_user_from_username(request.user, username) is_self = user == request.user if is_self: shelves = user.shelf_set.all() else: shelves = models.Shelf.privacy_filter( request.user).filter(user=user).all() # 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) shelf.raise_visible_to_user(request.user) books = shelf.books else: # this is a constructed "all books" view, with a fake "shelf" obj FakeShelf = namedtuple( "Shelf", ("identifier", "name", "user", "books", "privacy")) books = ( models.Edition.viewer_aware_objects(request.user).filter( # privacy is ensured because the shelves are already filtered above shelfbook__shelf__in=shelves).distinct()) shelf = FakeShelf("all", _("All books"), user, books, "public") if is_api_request(request) and shelf_identifier: return ActivitypubResponse(shelf.to_activity(**request.GET)) reviews = models.Review.objects if not is_self: reviews = models.Review.privacy_filter(request.user) reviews = reviews.filter( user=user, rating__isnull=False, book__id=OuterRef("id"), deleted=False, ).order_by("-published_date") reading = models.ReadThrough.objects reading = reading.filter( user=user, book__id=OuterRef("id")).order_by("start_date") books = books.annotate(shelved_date=Max("shelfbook__shelved_date")) books = books.annotate( rating=Subquery(reviews.values("rating")[:1]), start_date=Subquery(reading.values("start_date")[:1]), finish_date=Subquery(reading.values("finish_date")[:1]), author=Subquery( models.Book.objects.filter( id=OuterRef("id")).values("authors__name")[:1]), ).prefetch_related("authors") books = sort_books(books, request.GET.get("sort")) paginated = Paginator( books, PAGE_LENGTH, ) page = paginated.get_page(request.GET.get("page")) data = { "user": user, "is_self": is_self, "shelves": shelves, "shelf": shelf, "books": page, "edit_form": forms.ShelfForm(instance=shelf if shelf_identifier else None), "create_form": forms.ShelfForm(), "sort": request.GET.get("sort"), "page_range": paginated.get_elided_page_range(page.number, on_each_side=2, on_ends=1), } return TemplateResponse(request, "shelf/shelf.html", data)
def get(self, request, username): """ profile page for a user """ try: user = get_user_from_username(request.user, username) except models.User.DoesNotExist: return HttpResponseNotFound() # make sure we're not blocked if is_blocked(request.user, user): return HttpResponseNotFound() if is_api_request(request): # we have a json request return ActivitypubResponse(user.to_activity()) # otherwise we're at a UI view try: page = int(request.GET.get("page", 1)) except ValueError: page = 1 shelf_preview = [] # only show other shelves that should be visible shelves = user.shelf_set is_self = request.user.id == user.id if not is_self: follower = user.followers.filter(id=request.user.id).exists() if follower: shelves = shelves.filter(privacy__in=["public", "followers"]) else: shelves = shelves.filter(privacy="public") for user_shelf in shelves.all(): if not user_shelf.books.count(): continue shelf_preview.append({ "name": user_shelf.name, "local_path": user_shelf.local_path, "books": user_shelf.books.all()[:3], "size": user_shelf.books.count(), }) if len(shelf_preview) > 2: break # user's posts activities = get_activity_feed( request.user, queryset=user.status_set.select_subclasses(), ) paginated = Paginator(activities, PAGE_LENGTH) goal = models.AnnualGoal.objects.filter( user=user, year=timezone.now().year).first() if not object_visible_to_user(request.user, goal): goal = None data = { "user": user, "is_self": is_self, "shelves": shelf_preview, "shelf_count": shelves.count(), "activities": paginated.page(page), "goal": goal, } return TemplateResponse(request, "user/user.html", data)
def get(self, request, username, status_id): """display a particular status (and replies, etc)""" user = get_user_from_username(request.user, username) status = get_object_or_404( models.Status.objects.select_subclasses(), user=user, id=status_id, deleted=False, ) # make sure the user is authorized to see the status status.raise_visible_to_user(request.user) if is_api_request(request): return ActivitypubResponse( status.to_activity(pure=not is_bookwyrm_request(request)) ) visible_thread = ( models.Status.privacy_filter(request.user) .filter(thread_id=status.thread_id) .values_list("id", flat=True) ) visible_thread = list(visible_thread) ancestors = models.Status.objects.select_subclasses().raw( """ WITH RECURSIVE get_thread(depth, id, path) AS ( SELECT 1, st.id, ARRAY[st.id] FROM bookwyrm_status st WHERE id = '%s' AND id = ANY(%s) UNION SELECT (gt.depth + 1), st.reply_parent_id, path || st.id FROM get_thread gt, bookwyrm_status st WHERE st.id = gt.id AND depth < 5 AND st.id = ANY(%s) ) SELECT * FROM get_thread ORDER BY path DESC; """, params=[status.reply_parent_id or 0, visible_thread, visible_thread], ) children = models.Status.objects.select_subclasses().raw( """ WITH RECURSIVE get_thread(depth, id, path) AS ( SELECT 1, st.id, ARRAY[st.id] FROM bookwyrm_status st WHERE reply_parent_id = '%s' AND id = ANY(%s) UNION SELECT (gt.depth + 1), st.id, path || st.id FROM get_thread gt, bookwyrm_status st WHERE st.reply_parent_id = gt.id AND depth < 5 AND st.id = ANY(%s) ) SELECT * FROM get_thread ORDER BY path; """, params=[status.id, visible_thread, visible_thread], ) preview = None if hasattr(status, "book"): preview = status.book.preview_image elif status.mention_books.exists(): preview = status.mention_books.first().preview_image data = { **feed_page_data(request.user), **{ "status": status, "children": children, "ancestors": ancestors, "preview": preview, }, } return TemplateResponse(request, "feed/status.html", data)