We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
"Your Books" doesn't display my most recent title
**Describe the bug**
For the last couple of months, your books is not showing anything I've read recently 

**To Reproduce**
![image](https://user-images.githubusercontent.com/1735650/138808998-67a648b2-c114-45cc-8de4-5d024ab28451.png)

**Expected behavior**
Expect to see the mostly recently read book on your books

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Instance**
Bookwrym.social

**Additional context**
Add any other context about the problem here.

---

**Desktop (please complete the following information):**
    - OS: [e.g. iOS]
    - Browser [e.g. chrome, safari]
    - Version [e.g. 22]

**Smartphone (please complete the following information):**
    - Device: [e.g. iPhone6]
    - OS: [e.g. iOS8.1]
    - Browser [e.g. stock browser, safari]
    - Version [e.g. 22]

"Your Books" doesn't display my most recent title
**Describe the bug**
For the last couple of months, your books is not showing anything I've read recently 

**To Reproduce**
![image](https://user-images.githubusercontent.com/1735650/138808998-67a648b2-c114-45cc-8de4-5d024ab28451.png)

**Expected behavior**
Expect to see the mostly recently read book on your books

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Instance**
Bookwrym.social

**Additional context**
Add any other context about the problem here.

---

**Desktop (please complete the following information):**
    - OS: [e.g. iOS]
    - Browser [e.g. chrome, safari]
    - Version [e.g. 22]

**Smartphone (please complete the following information):**
    - Device: [e.g. iPhone6]
    - OS: [e.g. iOS8.1]
    - Browser [e.g. stock browser, safari]
    - Version [e.g. 22]


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `bookwyrm/views/feed.py`
Content:
```
1 """ non-interactive pages """
2 from django.contrib.auth.decorators import login_required
3 from django.core.paginator import Paginator
4 from django.db.models import Q
5 from django.http import HttpResponseNotFound, Http404
6 from django.shortcuts import get_object_or_404
7 from django.template.response import TemplateResponse
8 from django.utils import timezone
9 from django.utils.decorators import method_decorator
10 from django.views import View
11 
12 from bookwyrm import activitystreams, forms, models
13 from bookwyrm.activitypub import ActivitypubResponse
14 from bookwyrm.settings import PAGE_LENGTH, STREAMS
15 from bookwyrm.suggested_users import suggested_users
16 from .helpers import get_user_from_username
17 from .helpers import is_api_request, is_bookwyrm_request
18 
19 
20 # pylint: disable= no-self-use
21 @method_decorator(login_required, name="dispatch")
22 class Feed(View):
23     """activity stream"""
24 
25     def get(self, request, tab):
26         """user's homepage with activity feed"""
27         tab = [s for s in STREAMS if s["key"] == tab]
28         tab = tab[0] if tab else STREAMS[0]
29 
30         activities = activitystreams.streams[tab["key"]].get_activity_stream(
31             request.user
32         )
33         paginated = Paginator(activities, PAGE_LENGTH)
34 
35         suggestions = suggested_users.get_suggestions(request.user)
36 
37         data = {
38             **feed_page_data(request.user),
39             **{
40                 "user": request.user,
41                 "activities": paginated.get_page(request.GET.get("page")),
42                 "suggested_users": suggestions,
43                 "tab": tab,
44                 "streams": STREAMS,
45                 "goal_form": forms.GoalForm(),
46                 "path": f"/{tab['key']}",
47             },
48         }
49         return TemplateResponse(request, "feed/feed.html", data)
50 
51 
52 @method_decorator(login_required, name="dispatch")
53 class DirectMessage(View):
54     """dm view"""
55 
56     def get(self, request, username=None):
57         """like a feed but for dms only"""
58         # remove fancy subclasses of status, keep just good ol' notes
59         activities = (
60             models.Status.privacy_filter(request.user, privacy_levels=["direct"])
61             .filter(
62                 review__isnull=True,
63                 comment__isnull=True,
64                 quotation__isnull=True,
65                 generatednote__isnull=True,
66             )
67             .order_by("-published_date")
68         )
69 
70         user = None
71         if username:
72             try:
73                 user = get_user_from_username(request.user, username)
74             except Http404:
75                 pass
76         if user:
77             activities = activities.filter(Q(user=user) | Q(mention_users=user))
78 
79         paginated = Paginator(activities, PAGE_LENGTH)
80         data = {
81             **feed_page_data(request.user),
82             **{
83                 "user": request.user,
84                 "partner": user,
85                 "activities": paginated.get_page(request.GET.get("page")),
86                 "path": "/direct-messages",
87             },
88         }
89         return TemplateResponse(request, "feed/direct_messages.html", data)
90 
91 
92 class Status(View):
93     """get posting"""
94 
95     def get(self, request, username, status_id):
96         """display a particular status (and replies, etc)"""
97         user = get_user_from_username(request.user, username)
98         status = get_object_or_404(
99             models.Status.objects.select_subclasses(),
100             user=user,
101             id=status_id,
102             deleted=False,
103         )
104         # make sure the user is authorized to see the status
105         status.raise_visible_to_user(request.user)
106 
107         if is_api_request(request):
108             return ActivitypubResponse(
109                 status.to_activity(pure=not is_bookwyrm_request(request))
110             )
111 
112         visible_thread = (
113             models.Status.privacy_filter(request.user)
114             .filter(thread_id=status.thread_id)
115             .values_list("id", flat=True)
116         )
117         visible_thread = list(visible_thread)
118 
119         ancestors = models.Status.objects.select_subclasses().raw(
120             """
121             WITH RECURSIVE get_thread(depth, id, path) AS (
122 
123                 SELECT 1, st.id, ARRAY[st.id]
124                 FROM bookwyrm_status st
125                 WHERE id = '%s' AND id = ANY(%s)
126 
127                 UNION
128 
129                 SELECT (gt.depth + 1), st.reply_parent_id, path || st.id
130                 FROM get_thread gt, bookwyrm_status st
131 
132                 WHERE st.id = gt.id AND depth < 5 AND st.id = ANY(%s)
133 
134             )
135 
136             SELECT * FROM get_thread ORDER BY path DESC;
137         """,
138             params=[status.reply_parent_id or 0, visible_thread, visible_thread],
139         )
140         children = models.Status.objects.select_subclasses().raw(
141             """
142             WITH RECURSIVE get_thread(depth, id, path) AS (
143 
144                 SELECT 1, st.id, ARRAY[st.id]
145                 FROM bookwyrm_status st
146                 WHERE reply_parent_id = '%s' AND id = ANY(%s)
147 
148                 UNION
149 
150                 SELECT (gt.depth + 1), st.id, path || st.id
151                 FROM get_thread gt, bookwyrm_status st
152 
153                 WHERE st.reply_parent_id = gt.id AND depth < 5 AND st.id = ANY(%s)
154 
155             )
156 
157             SELECT * FROM get_thread ORDER BY path;
158         """,
159             params=[status.id, visible_thread, visible_thread],
160         )
161 
162         data = {
163             **feed_page_data(request.user),
164             **{
165                 "status": status,
166                 "children": children,
167                 "ancestors": ancestors,
168             },
169         }
170         return TemplateResponse(request, "feed/status.html", data)
171 
172 
173 class Replies(View):
174     """replies page (a json view of status)"""
175 
176     def get(self, request, username, status_id):
177         """ordered collection of replies to a status"""
178         # the html view is the same as Status
179         if not is_api_request(request):
180             status_view = Status.as_view()
181             return status_view(request, username, status_id)
182 
183         # the json view is different than Status
184         status = models.Status.objects.get(id=status_id)
185         if status.user.localname != username:
186             return HttpResponseNotFound()
187         status.raise_visible_to_user(request.user)
188 
189         return ActivitypubResponse(status.to_replies(**request.GET))
190 
191 
192 def feed_page_data(user):
193     """info we need for every feed page"""
194     if not user.is_authenticated:
195         return {}
196 
197     goal = models.AnnualGoal.objects.filter(user=user, year=timezone.now().year).first()
198     return {
199         "suggested_books": get_suggested_books(user),
200         "goal": goal,
201         "goal_form": forms.GoalForm(),
202     }
203 
204 
205 def get_suggested_books(user, max_books=5):
206     """helper to get a user's recent books"""
207     book_count = 0
208     preset_shelves = [("reading", max_books), ("read", 2), ("to-read", max_books)]
209     suggested_books = []
210     for (preset, shelf_max) in preset_shelves:
211         limit = (
212             shelf_max
213             if shelf_max < (max_books - book_count)
214             else max_books - book_count
215         )
216         shelf = user.shelf_set.get(identifier=preset)
217         if not shelf.books.exists():
218             continue
219 
220         shelf_preview = {
221             "name": shelf.name,
222             "identifier": shelf.identifier,
223             "books": models.Edition.viewer_aware_objects(user)
224             .filter(
225                 shelfbook__shelf=shelf,
226             )
227             .prefetch_related("authors")[:limit],
228         }
229         suggested_books.append(shelf_preview)
230         book_count += len(shelf_preview["books"])
231     return suggested_books
232
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/bookwyrm/views/feed.py b/bookwyrm/views/feed.py
--- a/bookwyrm/views/feed.py
+++ b/bookwyrm/views/feed.py
@@ -224,6 +224,7 @@
             .filter(
                 shelfbook__shelf=shelf,
             )
+            .order_by("-shelfbook__shelved_date")
             .prefetch_related("authors")[:limit],
         }
         suggested_books.append(shelf_preview)

{"golden_diff": "diff --git a/bookwyrm/views/feed.py b/bookwyrm/views/feed.py\n--- a/bookwyrm/views/feed.py\n+++ b/bookwyrm/views/feed.py\n@@ -224,6 +224,7 @@\n             .filter(\n                 shelfbook__shelf=shelf,\n             )\n+            .order_by(\"-shelfbook__shelved_date\")\n             .prefetch_related(\"authors\")[:limit],\n         }\n         suggested_books.append(shelf_preview)\n", "issue": "\"Your Books\" doesn't display my most recent title\n**Describe the bug**\r\nFor the last couple of months, your books is not showing anything I've read recently \r\n\r\n**To Reproduce**\r\n![image](https://user-images.githubusercontent.com/1735650/138808998-67a648b2-c114-45cc-8de4-5d024ab28451.png)\r\n\r\n**Expected behavior**\r\nExpect to see the mostly recently read book on your books\r\n\r\n**Screenshots**\r\nIf applicable, add screenshots to help explain your problem.\r\n\r\n**Instance**\r\nBookwrym.social\r\n\r\n**Additional context**\r\nAdd any other context about the problem here.\r\n\r\n---\r\n\r\n**Desktop (please complete the following information):**\r\n    - OS: [e.g. iOS]\r\n    - Browser [e.g. chrome, safari]\r\n    - Version [e.g. 22]\r\n\r\n**Smartphone (please complete the following information):**\r\n    - Device: [e.g. iPhone6]\r\n    - OS: [e.g. iOS8.1]\r\n    - Browser [e.g. stock browser, safari]\r\n    - Version [e.g. 22]\r\n\n\"Your Books\" doesn't display my most recent title\n**Describe the bug**\r\nFor the last couple of months, your books is not showing anything I've read recently \r\n\r\n**To Reproduce**\r\n![image](https://user-images.githubusercontent.com/1735650/138808998-67a648b2-c114-45cc-8de4-5d024ab28451.png)\r\n\r\n**Expected behavior**\r\nExpect to see the mostly recently read book on your books\r\n\r\n**Screenshots**\r\nIf applicable, add screenshots to help explain your problem.\r\n\r\n**Instance**\r\nBookwrym.social\r\n\r\n**Additional context**\r\nAdd any other context about the problem here.\r\n\r\n---\r\n\r\n**Desktop (please complete the following information):**\r\n    - OS: [e.g. iOS]\r\n    - Browser [e.g. chrome, safari]\r\n    - Version [e.g. 22]\r\n\r\n**Smartphone (please complete the following information):**\r\n    - Device: [e.g. iPhone6]\r\n    - OS: [e.g. iOS8.1]\r\n    - Browser [e.g. stock browser, safari]\r\n    - Version [e.g. 22]\r\n\n", "before_files": [{"content": "\"\"\" non-interactive pages \"\"\"\nfrom django.contrib.auth.decorators import login_required\nfrom django.core.paginator import Paginator\nfrom django.db.models import Q\nfrom django.http import HttpResponseNotFound, Http404\nfrom django.shortcuts import get_object_or_404\nfrom django.template.response import TemplateResponse\nfrom django.utils import timezone\nfrom django.utils.decorators import method_decorator\nfrom django.views import View\n\nfrom bookwyrm import activitystreams, forms, models\nfrom bookwyrm.activitypub import ActivitypubResponse\nfrom bookwyrm.settings import PAGE_LENGTH, STREAMS\nfrom bookwyrm.suggested_users import suggested_users\nfrom .helpers import get_user_from_username\nfrom .helpers import is_api_request, is_bookwyrm_request\n\n\n# pylint: disable= no-self-use\n@method_decorator(login_required, name=\"dispatch\")\nclass Feed(View):\n    \"\"\"activity stream\"\"\"\n\n    def get(self, request, tab):\n        \"\"\"user's homepage with activity feed\"\"\"\n        tab = [s for s in STREAMS if s[\"key\"] == tab]\n        tab = tab[0] if tab else STREAMS[0]\n\n        activities = activitystreams.streams[tab[\"key\"]].get_activity_stream(\n            request.user\n        )\n        paginated = Paginator(activities, PAGE_LENGTH)\n\n        suggestions = suggested_users.get_suggestions(request.user)\n\n        data = {\n            **feed_page_data(request.user),\n            **{\n                \"user\": request.user,\n                \"activities\": paginated.get_page(request.GET.get(\"page\")),\n                \"suggested_users\": suggestions,\n                \"tab\": tab,\n                \"streams\": STREAMS,\n                \"goal_form\": forms.GoalForm(),\n                \"path\": f\"/{tab['key']}\",\n            },\n        }\n        return TemplateResponse(request, \"feed/feed.html\", data)\n\n\n@method_decorator(login_required, name=\"dispatch\")\nclass DirectMessage(View):\n    \"\"\"dm view\"\"\"\n\n    def get(self, request, username=None):\n        \"\"\"like a feed but for dms only\"\"\"\n        # remove fancy subclasses of status, keep just good ol' notes\n        activities = (\n            models.Status.privacy_filter(request.user, privacy_levels=[\"direct\"])\n            .filter(\n                review__isnull=True,\n                comment__isnull=True,\n                quotation__isnull=True,\n                generatednote__isnull=True,\n            )\n            .order_by(\"-published_date\")\n        )\n\n        user = None\n        if username:\n            try:\n                user = get_user_from_username(request.user, username)\n            except Http404:\n                pass\n        if user:\n            activities = activities.filter(Q(user=user) | Q(mention_users=user))\n\n        paginated = Paginator(activities, PAGE_LENGTH)\n        data = {\n            **feed_page_data(request.user),\n            **{\n                \"user\": request.user,\n                \"partner\": user,\n                \"activities\": paginated.get_page(request.GET.get(\"page\")),\n                \"path\": \"/direct-messages\",\n            },\n        }\n        return TemplateResponse(request, \"feed/direct_messages.html\", data)\n\n\nclass Status(View):\n    \"\"\"get posting\"\"\"\n\n    def get(self, request, username, status_id):\n        \"\"\"display a particular status (and replies, etc)\"\"\"\n        user = get_user_from_username(request.user, username)\n        status = get_object_or_404(\n            models.Status.objects.select_subclasses(),\n            user=user,\n            id=status_id,\n            deleted=False,\n        )\n        # make sure the user is authorized to see the status\n        status.raise_visible_to_user(request.user)\n\n        if is_api_request(request):\n            return ActivitypubResponse(\n                status.to_activity(pure=not is_bookwyrm_request(request))\n            )\n\n        visible_thread = (\n            models.Status.privacy_filter(request.user)\n            .filter(thread_id=status.thread_id)\n            .values_list(\"id\", flat=True)\n        )\n        visible_thread = list(visible_thread)\n\n        ancestors = models.Status.objects.select_subclasses().raw(\n            \"\"\"\n            WITH RECURSIVE get_thread(depth, id, path) AS (\n\n                SELECT 1, st.id, ARRAY[st.id]\n                FROM bookwyrm_status st\n                WHERE id = '%s' AND id = ANY(%s)\n\n                UNION\n\n                SELECT (gt.depth + 1), st.reply_parent_id, path || st.id\n                FROM get_thread gt, bookwyrm_status st\n\n                WHERE st.id = gt.id AND depth < 5 AND st.id = ANY(%s)\n\n            )\n\n            SELECT * FROM get_thread ORDER BY path DESC;\n        \"\"\",\n            params=[status.reply_parent_id or 0, visible_thread, visible_thread],\n        )\n        children = models.Status.objects.select_subclasses().raw(\n            \"\"\"\n            WITH RECURSIVE get_thread(depth, id, path) AS (\n\n                SELECT 1, st.id, ARRAY[st.id]\n                FROM bookwyrm_status st\n                WHERE reply_parent_id = '%s' AND id = ANY(%s)\n\n                UNION\n\n                SELECT (gt.depth + 1), st.id, path || st.id\n                FROM get_thread gt, bookwyrm_status st\n\n                WHERE st.reply_parent_id = gt.id AND depth < 5 AND st.id = ANY(%s)\n\n            )\n\n            SELECT * FROM get_thread ORDER BY path;\n        \"\"\",\n            params=[status.id, visible_thread, visible_thread],\n        )\n\n        data = {\n            **feed_page_data(request.user),\n            **{\n                \"status\": status,\n                \"children\": children,\n                \"ancestors\": ancestors,\n            },\n        }\n        return TemplateResponse(request, \"feed/status.html\", data)\n\n\nclass Replies(View):\n    \"\"\"replies page (a json view of status)\"\"\"\n\n    def get(self, request, username, status_id):\n        \"\"\"ordered collection of replies to a status\"\"\"\n        # the html view is the same as Status\n        if not is_api_request(request):\n            status_view = Status.as_view()\n            return status_view(request, username, status_id)\n\n        # the json view is different than Status\n        status = models.Status.objects.get(id=status_id)\n        if status.user.localname != username:\n            return HttpResponseNotFound()\n        status.raise_visible_to_user(request.user)\n\n        return ActivitypubResponse(status.to_replies(**request.GET))\n\n\ndef feed_page_data(user):\n    \"\"\"info we need for every feed page\"\"\"\n    if not user.is_authenticated:\n        return {}\n\n    goal = models.AnnualGoal.objects.filter(user=user, year=timezone.now().year).first()\n    return {\n        \"suggested_books\": get_suggested_books(user),\n        \"goal\": goal,\n        \"goal_form\": forms.GoalForm(),\n    }\n\n\ndef get_suggested_books(user, max_books=5):\n    \"\"\"helper to get a user's recent books\"\"\"\n    book_count = 0\n    preset_shelves = [(\"reading\", max_books), (\"read\", 2), (\"to-read\", max_books)]\n    suggested_books = []\n    for (preset, shelf_max) in preset_shelves:\n        limit = (\n            shelf_max\n            if shelf_max < (max_books - book_count)\n            else max_books - book_count\n        )\n        shelf = user.shelf_set.get(identifier=preset)\n        if not shelf.books.exists():\n            continue\n\n        shelf_preview = {\n            \"name\": shelf.name,\n            \"identifier\": shelf.identifier,\n            \"books\": models.Edition.viewer_aware_objects(user)\n            .filter(\n                shelfbook__shelf=shelf,\n            )\n            .prefetch_related(\"authors\")[:limit],\n        }\n        suggested_books.append(shelf_preview)\n        book_count += len(shelf_preview[\"books\"])\n    return suggested_books\n", "path": "bookwyrm/views/feed.py"}], "after_files": [{"content": "\"\"\" non-interactive pages \"\"\"\nfrom django.contrib.auth.decorators import login_required\nfrom django.core.paginator import Paginator\nfrom django.db.models import Q\nfrom django.http import HttpResponseNotFound, Http404\nfrom django.shortcuts import get_object_or_404\nfrom django.template.response import TemplateResponse\nfrom django.utils import timezone\nfrom django.utils.decorators import method_decorator\nfrom django.views import View\n\nfrom bookwyrm import activitystreams, forms, models\nfrom bookwyrm.activitypub import ActivitypubResponse\nfrom bookwyrm.settings import PAGE_LENGTH, STREAMS\nfrom bookwyrm.suggested_users import suggested_users\nfrom .helpers import get_user_from_username\nfrom .helpers import is_api_request, is_bookwyrm_request\n\n\n# pylint: disable= no-self-use\n@method_decorator(login_required, name=\"dispatch\")\nclass Feed(View):\n    \"\"\"activity stream\"\"\"\n\n    def get(self, request, tab):\n        \"\"\"user's homepage with activity feed\"\"\"\n        tab = [s for s in STREAMS if s[\"key\"] == tab]\n        tab = tab[0] if tab else STREAMS[0]\n\n        activities = activitystreams.streams[tab[\"key\"]].get_activity_stream(\n            request.user\n        )\n        paginated = Paginator(activities, PAGE_LENGTH)\n\n        suggestions = suggested_users.get_suggestions(request.user)\n\n        data = {\n            **feed_page_data(request.user),\n            **{\n                \"user\": request.user,\n                \"activities\": paginated.get_page(request.GET.get(\"page\")),\n                \"suggested_users\": suggestions,\n                \"tab\": tab,\n                \"streams\": STREAMS,\n                \"goal_form\": forms.GoalForm(),\n                \"path\": f\"/{tab['key']}\",\n            },\n        }\n        return TemplateResponse(request, \"feed/feed.html\", data)\n\n\n@method_decorator(login_required, name=\"dispatch\")\nclass DirectMessage(View):\n    \"\"\"dm view\"\"\"\n\n    def get(self, request, username=None):\n        \"\"\"like a feed but for dms only\"\"\"\n        # remove fancy subclasses of status, keep just good ol' notes\n        activities = (\n            models.Status.privacy_filter(request.user, privacy_levels=[\"direct\"])\n            .filter(\n                review__isnull=True,\n                comment__isnull=True,\n                quotation__isnull=True,\n                generatednote__isnull=True,\n            )\n            .order_by(\"-published_date\")\n        )\n\n        user = None\n        if username:\n            try:\n                user = get_user_from_username(request.user, username)\n            except Http404:\n                pass\n        if user:\n            activities = activities.filter(Q(user=user) | Q(mention_users=user))\n\n        paginated = Paginator(activities, PAGE_LENGTH)\n        data = {\n            **feed_page_data(request.user),\n            **{\n                \"user\": request.user,\n                \"partner\": user,\n                \"activities\": paginated.get_page(request.GET.get(\"page\")),\n                \"path\": \"/direct-messages\",\n            },\n        }\n        return TemplateResponse(request, \"feed/direct_messages.html\", data)\n\n\nclass Status(View):\n    \"\"\"get posting\"\"\"\n\n    def get(self, request, username, status_id):\n        \"\"\"display a particular status (and replies, etc)\"\"\"\n        user = get_user_from_username(request.user, username)\n        status = get_object_or_404(\n            models.Status.objects.select_subclasses(),\n            user=user,\n            id=status_id,\n            deleted=False,\n        )\n        # make sure the user is authorized to see the status\n        status.raise_visible_to_user(request.user)\n\n        if is_api_request(request):\n            return ActivitypubResponse(\n                status.to_activity(pure=not is_bookwyrm_request(request))\n            )\n\n        visible_thread = (\n            models.Status.privacy_filter(request.user)\n            .filter(thread_id=status.thread_id)\n            .values_list(\"id\", flat=True)\n        )\n        visible_thread = list(visible_thread)\n\n        ancestors = models.Status.objects.select_subclasses().raw(\n            \"\"\"\n            WITH RECURSIVE get_thread(depth, id, path) AS (\n\n                SELECT 1, st.id, ARRAY[st.id]\n                FROM bookwyrm_status st\n                WHERE id = '%s' AND id = ANY(%s)\n\n                UNION\n\n                SELECT (gt.depth + 1), st.reply_parent_id, path || st.id\n                FROM get_thread gt, bookwyrm_status st\n\n                WHERE st.id = gt.id AND depth < 5 AND st.id = ANY(%s)\n\n            )\n\n            SELECT * FROM get_thread ORDER BY path DESC;\n        \"\"\",\n            params=[status.reply_parent_id or 0, visible_thread, visible_thread],\n        )\n        children = models.Status.objects.select_subclasses().raw(\n            \"\"\"\n            WITH RECURSIVE get_thread(depth, id, path) AS (\n\n                SELECT 1, st.id, ARRAY[st.id]\n                FROM bookwyrm_status st\n                WHERE reply_parent_id = '%s' AND id = ANY(%s)\n\n                UNION\n\n                SELECT (gt.depth + 1), st.id, path || st.id\n                FROM get_thread gt, bookwyrm_status st\n\n                WHERE st.reply_parent_id = gt.id AND depth < 5 AND st.id = ANY(%s)\n\n            )\n\n            SELECT * FROM get_thread ORDER BY path;\n        \"\"\",\n            params=[status.id, visible_thread, visible_thread],\n        )\n\n        data = {\n            **feed_page_data(request.user),\n            **{\n                \"status\": status,\n                \"children\": children,\n                \"ancestors\": ancestors,\n            },\n        }\n        return TemplateResponse(request, \"feed/status.html\", data)\n\n\nclass Replies(View):\n    \"\"\"replies page (a json view of status)\"\"\"\n\n    def get(self, request, username, status_id):\n        \"\"\"ordered collection of replies to a status\"\"\"\n        # the html view is the same as Status\n        if not is_api_request(request):\n            status_view = Status.as_view()\n            return status_view(request, username, status_id)\n\n        # the json view is different than Status\n        status = models.Status.objects.get(id=status_id)\n        if status.user.localname != username:\n            return HttpResponseNotFound()\n        status.raise_visible_to_user(request.user)\n\n        return ActivitypubResponse(status.to_replies(**request.GET))\n\n\ndef feed_page_data(user):\n    \"\"\"info we need for every feed page\"\"\"\n    if not user.is_authenticated:\n        return {}\n\n    goal = models.AnnualGoal.objects.filter(user=user, year=timezone.now().year).first()\n    return {\n        \"suggested_books\": get_suggested_books(user),\n        \"goal\": goal,\n        \"goal_form\": forms.GoalForm(),\n    }\n\n\ndef get_suggested_books(user, max_books=5):\n    \"\"\"helper to get a user's recent books\"\"\"\n    book_count = 0\n    preset_shelves = [(\"reading\", max_books), (\"read\", 2), (\"to-read\", max_books)]\n    suggested_books = []\n    for (preset, shelf_max) in preset_shelves:\n        limit = (\n            shelf_max\n            if shelf_max < (max_books - book_count)\n            else max_books - book_count\n        )\n        shelf = user.shelf_set.get(identifier=preset)\n        if not shelf.books.exists():\n            continue\n\n        shelf_preview = {\n            \"name\": shelf.name,\n            \"identifier\": shelf.identifier,\n            \"books\": models.Edition.viewer_aware_objects(user)\n            .filter(\n                shelfbook__shelf=shelf,\n            )\n            .order_by(\"-shelfbook__shelved_date\")\n            .prefetch_related(\"authors\")[:limit],\n        }\n        suggested_books.append(shelf_preview)\n        book_count += len(shelf_preview[\"books\"])\n    return suggested_books\n", "path": "bookwyrm/views/feed.py"}]}