From af3c84cd8741334e723d09a895bf6576143deac1 Mon Sep 17 00:00:00 2001 From: Joel Bradshaw Date: Mon, 10 Jan 2022 06:43:43 +0000 Subject: [PATCH 01/15] Add basic logging config --- bookwyrm/settings.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index fe2f7467..d56b569d 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -106,6 +106,27 @@ TEMPLATES = [ }, ] +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'handlers': { + 'console': { + 'class': 'logging.StreamHandler', + }, + }, + 'root': { + 'handlers': ['console'], + 'level': 'WARNING', + }, + 'loggers': { + 'django': { + 'handlers': ['console'], + 'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'), + 'propagate': False, + }, + }, +} + WSGI_APPLICATION = "bookwyrm.wsgi.application" From 83851c29338ac269fefa7bc8971116e70b583e88 Mon Sep 17 00:00:00 2001 From: Joel Bradshaw Date: Mon, 10 Jan 2022 06:45:14 +0000 Subject: [PATCH 02/15] Add bookwyrm-specific logging --- bookwyrm/settings.py | 4 ++++ bookwyrm/views/inbox.py | 15 ++++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index d56b569d..31ad75d8 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -124,6 +124,10 @@ LOGGING = { 'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'), 'propagate': False, }, + 'bookwyrm': { + 'handlers': ['console'], + 'level': os.getenv('LOG_LEVEL', 'DEBUG' if DEBUG else 'INFO').upper(), + } }, } diff --git a/bookwyrm/views/inbox.py b/bookwyrm/views/inbox.py index 23982495..514cb685 100644 --- a/bookwyrm/views/inbox.py +++ b/bookwyrm/views/inbox.py @@ -10,12 +10,14 @@ from django.utils.decorators import method_decorator from django.views import View from django.views.decorators.csrf import csrf_exempt import requests +import logging from bookwyrm import activitypub, models from bookwyrm.tasks import app from bookwyrm.signatures import Signature from bookwyrm.utils import regex +logger = logging.getLogger(__name__) @method_decorator(csrf_exempt, name="dispatch") # pylint: disable=no-self-use @@ -71,6 +73,7 @@ def raise_is_blocked_user_agent(request): return url = url.group() if models.FederatedServer.is_blocked(url): + logger.debug(f"{url} is blocked, denying request based on user agent") raise PermissionDenied() @@ -78,16 +81,18 @@ def raise_is_blocked_activity(activity_json): """get the sender out of activity json and check if it's blocked""" actor = activity_json.get("actor") - # check if the user is banned/deleted - existing = models.User.find_existing_by_remote_id(actor) - if existing and existing.deleted: - raise PermissionDenied() - if not actor: # well I guess it's not even a valid activity so who knows return + # check if the user is banned/deleted + existing = models.User.find_existing_by_remote_id(actor) + if existing and existing.deleted: + logger.debug(f"{actor} is banned/deleted, denying request based on actor") + raise PermissionDenied() + if models.FederatedServer.is_blocked(actor): + logger.debug(f"{actor} is blocked, denying request based on actor") raise PermissionDenied() From 085dd24a62df7f08608fea096dcfe198079f4c72 Mon Sep 17 00:00:00 2001 From: Joel Bradshaw Date: Sun, 9 Jan 2022 23:26:27 -0800 Subject: [PATCH 03/15] Simplify and explain our overrides This should also fix the 500s-in-prod issue, yay --- bookwyrm/settings.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index 31ad75d8..a3b0d48d 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -106,27 +106,30 @@ TEMPLATES = [ }, ] +LOG_LEVEL = env('LOG_LEVEL', 'INFO').upper() +# Override aspects of the default handler to our taste +# See https://docs.djangoproject.com/en/3.2/topics/logging/#default-logging-configuration +# for a reference to the defaults we're overriding LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { + # Overrides the default handler, which does not log in prod 'console': { + 'level': LOG_LEVEL, 'class': 'logging.StreamHandler', }, }, - 'root': { - 'handlers': ['console'], - 'level': 'WARNING', - }, 'loggers': { + # Override the log level for the default logger 'django': { - 'handlers': ['console'], - 'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'), - 'propagate': False, + 'handlers': ['console', 'mail_admins'], + 'level': LOG_LEVEL, }, + # Add a bookwyrm-specific logger 'bookwyrm': { 'handlers': ['console'], - 'level': os.getenv('LOG_LEVEL', 'DEBUG' if DEBUG else 'INFO').upper(), + 'level': LOG_LEVEL, } }, } From 5cf1d8a30a50b67a15f1110538238b63eb114283 Mon Sep 17 00:00:00 2001 From: Joel Bradshaw Date: Sun, 9 Jan 2022 23:53:23 -0800 Subject: [PATCH 04/15] Make it black --- bookwyrm/settings.py | 30 +++++++++++++++--------------- bookwyrm/views/inbox.py | 1 + 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index a3b0d48d..57a49df0 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -106,31 +106,31 @@ TEMPLATES = [ }, ] -LOG_LEVEL = env('LOG_LEVEL', 'INFO').upper() +LOG_LEVEL = env("LOG_LEVEL", "INFO").upper() # Override aspects of the default handler to our taste # See https://docs.djangoproject.com/en/3.2/topics/logging/#default-logging-configuration # for a reference to the defaults we're overriding LOGGING = { - 'version': 1, - 'disable_existing_loggers': False, - 'handlers': { + "version": 1, + "disable_existing_loggers": False, + "handlers": { # Overrides the default handler, which does not log in prod - 'console': { - 'level': LOG_LEVEL, - 'class': 'logging.StreamHandler', + "console": { + "level": LOG_LEVEL, + "class": "logging.StreamHandler", }, }, - 'loggers': { + "loggers": { # Override the log level for the default logger - 'django': { - 'handlers': ['console', 'mail_admins'], - 'level': LOG_LEVEL, + "django": { + "handlers": ["console", "mail_admins"], + "level": LOG_LEVEL, }, # Add a bookwyrm-specific logger - 'bookwyrm': { - 'handlers': ['console'], - 'level': LOG_LEVEL, - } + "bookwyrm": { + "handlers": ["console"], + "level": LOG_LEVEL, + }, }, } diff --git a/bookwyrm/views/inbox.py b/bookwyrm/views/inbox.py index 514cb685..1d2c303b 100644 --- a/bookwyrm/views/inbox.py +++ b/bookwyrm/views/inbox.py @@ -19,6 +19,7 @@ from bookwyrm.utils import regex logger = logging.getLogger(__name__) + @method_decorator(csrf_exempt, name="dispatch") # pylint: disable=no-self-use class Inbox(View): From 4cc35ba25ee35f7d452e082ee3598c2ac9b65a75 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Mon, 17 Jan 2022 11:53:00 -0800 Subject: [PATCH 05/15] Cache query for author's books --- bookwyrm/models/book.py | 5 +++++ bookwyrm/views/author.py | 21 ++++++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/bookwyrm/models/book.py b/bookwyrm/models/book.py index 8d1b70ae..ffc03d3e 100644 --- a/bookwyrm/models/book.py +++ b/bookwyrm/models/book.py @@ -342,6 +342,11 @@ class Edition(Book): # set rank self.edition_rank = self.get_rank() + # clear author cache + if self.id: + for author_id in self.authors.values_list("id", flat=True): + cache.delete(f"author-books-{author_id}") + return super().save(*args, **kwargs) @classmethod diff --git a/bookwyrm/views/author.py b/bookwyrm/views/author.py index 2acb3b19..9bc95b79 100644 --- a/bookwyrm/views/author.py +++ b/bookwyrm/views/author.py @@ -12,6 +12,7 @@ from bookwyrm import forms, models from bookwyrm.activitypub import ActivitypubResponse from bookwyrm.connectors import connector_manager from bookwyrm.settings import PAGE_LENGTH +from bookwyrm.utils import cache from bookwyrm.views.helpers import is_api_request @@ -30,14 +31,24 @@ class Author(View): 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)) + book_ids = cache.get_or_set( + f"author-books-{author.id}", + lambda a: models.Edition.objects.filter( + Q(authors=a) | Q(parent_work__authors=a) + ) .annotate(default_id=Subquery(default_editions.values("id")[:1])) .filter(default_id=F("id")) - .order_by("-first_published_date", "-published_date", "-created_date") + .distinct() + .values_list("id", flat=True), + author, + timeout=15552000, + ) + + books = ( + models.Edition.objects.filter(id__in=book_ids) + .order_by("-published_date", "-first_published_date", "-created_date") .prefetch_related("authors") - ).distinct() + ) paginated = Paginator(books, PAGE_LENGTH) page = paginated.get_page(request.GET.get("page")) From 1e4aee8276f881aefe1b9f083d59a3fc2aee8882 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Mon, 17 Jan 2022 12:17:24 -0800 Subject: [PATCH 06/15] Cache user-specific ratings --- bookwyrm/models/status.py | 6 ++++++ bookwyrm/templatetags/bookwyrm_tags.py | 11 ++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/bookwyrm/models/status.py b/bookwyrm/models/status.py index ee138d97..1368e05e 100644 --- a/bookwyrm/models/status.py +++ b/bookwyrm/models/status.py @@ -3,6 +3,7 @@ from dataclasses import MISSING import re from django.apps import apps +from django.core.cache import cache from django.core.exceptions import PermissionDenied from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models @@ -373,6 +374,11 @@ class Review(BookStatus): activity_serializer = activitypub.Review pure_type = "Article" + def save(self, *args, **kwargs): + """clear rating caches""" + cache.delete(f"book-rating-{self.book.parent_work.id}-*") + super().save(*args, **kwargs) + class ReviewRating(Review): """a subtype of review that only contains a rating""" diff --git a/bookwyrm/templatetags/bookwyrm_tags.py b/bookwyrm/templatetags/bookwyrm_tags.py index a3e89d9e..6e4e8d84 100644 --- a/bookwyrm/templatetags/bookwyrm_tags.py +++ b/bookwyrm/templatetags/bookwyrm_tags.py @@ -13,10 +13,15 @@ register = template.Library() @register.filter(name="rating") def get_rating(book, user): """get the overall rating of a book""" - queryset = models.Review.privacy_filter(user).filter( - book__parent_work__editions=book + return cache.get_or_set( + f"book-rating-{book.parent_work.id}-{user.id}", + lambda u, b: models.Review.privacy_filter(u) + .filter(book__parent_work__editions=b) + .aggregate(Avg("rating"))["rating__avg"], + user, + book, + timeout=15552000, ) - return queryset.aggregate(Avg("rating"))["rating__avg"] @register.filter(name="user_rating") From 02dd1e9443944a2145ad386b6f4f60c4fbc42ca9 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Mon, 17 Jan 2022 12:45:32 -0800 Subject: [PATCH 07/15] Use False instead of None for test values --- bookwyrm/models/status.py | 3 ++- bookwyrm/templatetags/bookwyrm_tags.py | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/bookwyrm/models/status.py b/bookwyrm/models/status.py index 1368e05e..29b3ba9c 100644 --- a/bookwyrm/models/status.py +++ b/bookwyrm/models/status.py @@ -376,7 +376,8 @@ class Review(BookStatus): def save(self, *args, **kwargs): """clear rating caches""" - cache.delete(f"book-rating-{self.book.parent_work.id}-*") + if self.book.parent_work: + cache.delete(f"book-rating-{self.book.parent_work.id}-*") super().save(*args, **kwargs) diff --git a/bookwyrm/templatetags/bookwyrm_tags.py b/bookwyrm/templatetags/bookwyrm_tags.py index 6e4e8d84..9b4c507f 100644 --- a/bookwyrm/templatetags/bookwyrm_tags.py +++ b/bookwyrm/templatetags/bookwyrm_tags.py @@ -17,7 +17,7 @@ def get_rating(book, user): f"book-rating-{book.parent_work.id}-{user.id}", lambda u, b: models.Review.privacy_filter(u) .filter(book__parent_work__editions=b) - .aggregate(Avg("rating"))["rating__avg"], + .aggregate(Avg("rating"))["rating__avg"] or 0, user, book, timeout=15552000, @@ -144,7 +144,7 @@ def active_shelf(context, book): models.ShelfBook.objects.filter( shelf__user=u, book__parent_work__editions=b, - ).first() + ).first() or False ), user, book, @@ -162,7 +162,7 @@ def latest_read_through(book, user): lambda u, b: ( models.ReadThrough.objects.filter(user=u, book=b, is_active=True) .order_by("-start_date") - .first() + .first() or False ), user, book, From 13a2c58b98b738413c69590cf92dcb8bd0c7ad58 Mon Sep 17 00:00:00 2001 From: Joel Bradshaw Date: Mon, 17 Jan 2022 15:14:01 -0800 Subject: [PATCH 08/15] Use parameter-based formatting for debug --- bookwyrm/views/inbox.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bookwyrm/views/inbox.py b/bookwyrm/views/inbox.py index 1d2c303b..756a5829 100644 --- a/bookwyrm/views/inbox.py +++ b/bookwyrm/views/inbox.py @@ -74,7 +74,7 @@ def raise_is_blocked_user_agent(request): return url = url.group() if models.FederatedServer.is_blocked(url): - logger.debug(f"{url} is blocked, denying request based on user agent") + logger.debug("%s is blocked, denying request based on user agent", url) raise PermissionDenied() @@ -89,11 +89,11 @@ def raise_is_blocked_activity(activity_json): # check if the user is banned/deleted existing = models.User.find_existing_by_remote_id(actor) if existing and existing.deleted: - logger.debug(f"{actor} is banned/deleted, denying request based on actor") + logger.debug("%s is banned/deleted, denying request based on actor", actor) raise PermissionDenied() if models.FederatedServer.is_blocked(actor): - logger.debug(f"{actor} is blocked, denying request based on actor") + logger.debug("%s is blocked, denying request based on actor", actor) raise PermissionDenied() From 27acf668df60f464443c57a4b124fb209cedf8b3 Mon Sep 17 00:00:00 2001 From: Joel Bradshaw Date: Mon, 17 Jan 2022 15:56:24 -0800 Subject: [PATCH 09/15] Add the required bits for Django's logger So, you can't define handlers piecewise, and if you redefine a logger, you have to also include everything it uses, because your "new" logger doesn't have a reference to the original logging config to get things like mail_admins and require_debug_false. require_debug_true isn't strictly necessary here, but it seemed strange to just copy over one of them. --- bookwyrm/settings.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index 57a49df0..4ffded8c 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -110,18 +110,42 @@ LOG_LEVEL = env("LOG_LEVEL", "INFO").upper() # Override aspects of the default handler to our taste # See https://docs.djangoproject.com/en/3.2/topics/logging/#default-logging-configuration # for a reference to the defaults we're overriding +# +# It seems that in order to override anything you have to include its +# entire dependency tree (handlers and filters) which makes this a +# bit verbose LOGGING = { "version": 1, "disable_existing_loggers": False, + "filters": { + # These are copied from the default configuration, required for + # implementing mail_admins below + "require_debug_false": { + "()": "django.utils.log.RequireDebugFalse", + }, + "require_debug_true": { + "()": "django.utils.log.RequireDebugTrue", + }, + }, "handlers": { - # Overrides the default handler, which does not log in prod + # Overrides the default handler to make it log to console + # regardless of the DEBUG setting (default is to not log to + # console if DEBUG=False) "console": { "level": LOG_LEVEL, "class": "logging.StreamHandler", }, + # This is copied as-is from the default logger, and is + # required for the django section below + "mail_admins": { + "level": "ERROR", + "filters": ["require_debug_false"], + "class": "django.utils.log.AdminEmailHandler", + }, }, "loggers": { - # Override the log level for the default logger + # Install our new console handler for Django's logger, and + # override the log level while we're at it "django": { "handlers": ["console", "mail_admins"], "level": LOG_LEVEL, From 27a0b92de097f26fddf2a9c50dd3a7a73e762d0d Mon Sep 17 00:00:00 2001 From: Joel Bradshaw Date: Mon, 17 Jan 2022 16:06:25 -0800 Subject: [PATCH 10/15] Fix include order --- bookwyrm/views/inbox.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bookwyrm/views/inbox.py b/bookwyrm/views/inbox.py index 756a5829..6320b450 100644 --- a/bookwyrm/views/inbox.py +++ b/bookwyrm/views/inbox.py @@ -1,7 +1,10 @@ """ incoming activities """ import json import re +import logging + from urllib.parse import urldefrag +import requests from django.http import HttpResponse, Http404 from django.core.exceptions import BadRequest, PermissionDenied @@ -9,8 +12,6 @@ from django.shortcuts import get_object_or_404 from django.utils.decorators import method_decorator from django.views import View from django.views.decorators.csrf import csrf_exempt -import requests -import logging from bookwyrm import activitypub, models from bookwyrm.tasks import app From a09c813f734170541e6a8368b63c4ec244334fcf Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Mon, 17 Jan 2022 19:04:20 -0800 Subject: [PATCH 11/15] Python formatting --- bookwyrm/templatetags/bookwyrm_tags.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/bookwyrm/templatetags/bookwyrm_tags.py b/bookwyrm/templatetags/bookwyrm_tags.py index 9b4c507f..f9668197 100644 --- a/bookwyrm/templatetags/bookwyrm_tags.py +++ b/bookwyrm/templatetags/bookwyrm_tags.py @@ -17,7 +17,8 @@ def get_rating(book, user): f"book-rating-{book.parent_work.id}-{user.id}", lambda u, b: models.Review.privacy_filter(u) .filter(book__parent_work__editions=b) - .aggregate(Avg("rating"))["rating__avg"] or 0, + .aggregate(Avg("rating"))["rating__avg"] + or 0, user, book, timeout=15552000, @@ -144,7 +145,8 @@ def active_shelf(context, book): models.ShelfBook.objects.filter( shelf__user=u, book__parent_work__editions=b, - ).first() or False + ).first() + or False ), user, book, @@ -162,7 +164,8 @@ def latest_read_through(book, user): lambda u, b: ( models.ReadThrough.objects.filter(user=u, book=b, is_active=True) .order_by("-start_date") - .first() or False + .first() + or False ), user, book, From 8bbc4d3301caee8d87dbf1799eff5a1cb01635e3 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Tue, 18 Jan 2022 10:22:20 -0800 Subject: [PATCH 12/15] Cache checking if books are on shelve in shelve button snippet --- bookwyrm/models/shelf.py | 8 ++++++++ .../snippets/shelve_button/shelve_button.html | 12 +++++++----- .../shelve_button_dropdown_options.html | 2 +- .../shelve_button/shelve_button_options.html | 8 +++++++- bookwyrm/templatetags/bookwyrm_tags.py | 12 ++++++++++++ bookwyrm/views/reading.py | 4 +--- 6 files changed, 36 insertions(+), 10 deletions(-) diff --git a/bookwyrm/models/shelf.py b/bookwyrm/models/shelf.py index c578f082..320d495d 100644 --- a/bookwyrm/models/shelf.py +++ b/bookwyrm/models/shelf.py @@ -1,5 +1,6 @@ """ puttin' books on shelves """ import re +from django.core.cache import cache from django.core.exceptions import PermissionDenied from django.db import models from django.utils import timezone @@ -94,8 +95,15 @@ class ShelfBook(CollectionItemMixin, BookWyrmModel): def save(self, *args, **kwargs): if not self.user: self.user = self.shelf.user + if self.id and self.user.local: + cache.delete(f"book-on-shelf-{self.book.id}-{self.shelf.id}") super().save(*args, **kwargs) + def delete(self, *args, **kwargs): + if self.id and self.user.local: + cache.delete(f"book-on-shelf-{self.book.id}-{self.shelf.id}") + super().delete(*args, **kwargs) + class Meta: """an opinionated constraint! you can't put a book on shelf twice""" diff --git a/bookwyrm/templates/snippets/shelve_button/shelve_button.html b/bookwyrm/templates/snippets/shelve_button/shelve_button.html index 38f6be38..37c57fc2 100644 --- a/bookwyrm/templates/snippets/shelve_button/shelve_button.html +++ b/bookwyrm/templates/snippets/shelve_button/shelve_button.html @@ -6,8 +6,9 @@ {% with book.id|uuid as uuid %} {% active_shelf book as active_shelf %} {% latest_read_through book request.user as readthrough %} +{% with active_shelf_book=active_shelf.book %}
- {% if switch_mode and active_shelf.book != book %} + {% if switch_mode and active_shelf_book != book %}
{% include 'snippets/switch_edition_button.html' with edition=book size='is-small' %}
@@ -20,16 +21,17 @@
{% join "want_to_read" uuid as modal_id %} -{% include 'snippets/reading_modals/want_to_read_modal.html' with book=active_shelf.book id=modal_id class="" %} +{% include 'snippets/reading_modals/want_to_read_modal.html' with book=active_shelf_book id=modal_id class="" %} {% join "start_reading" uuid as modal_id %} -{% include 'snippets/reading_modals/start_reading_modal.html' with book=active_shelf.book id=modal_id class="" %} +{% include 'snippets/reading_modals/start_reading_modal.html' with book=active_shelf_book id=modal_id class="" %} {% join "finish_reading" uuid as modal_id %} -{% include 'snippets/reading_modals/finish_reading_modal.html' with book=active_shelf.book id=modal_id readthrough=readthrough class="" %} +{% include 'snippets/reading_modals/finish_reading_modal.html' with book=active_shelf_book id=modal_id readthrough=readthrough class="" %} {% join "progress_update" uuid as modal_id %} -{% include 'snippets/reading_modals/progress_update_modal.html' with book=active_shelf.book id=modal_id readthrough=readthrough class="" %} +{% include 'snippets/reading_modals/progress_update_modal.html' with book=active_shelf_book id=modal_id readthrough=readthrough class="" %} +{% endwith %} {% endwith %} {% endif %} diff --git a/bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown_options.html b/bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown_options.html index 0b36ddbb..15e2bb51 100644 --- a/bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown_options.html +++ b/bookwyrm/templates/snippets/shelve_button/shelve_button_dropdown_options.html @@ -38,7 +38,7 @@
{% csrf_token %} -
diff --git a/bookwyrm/templates/snippets/shelve_button/shelve_button_options.html b/bookwyrm/templates/snippets/shelve_button/shelve_button_options.html index add2a77c..d6ca9933 100644 --- a/bookwyrm/templates/snippets/shelve_button/shelve_button_options.html +++ b/bookwyrm/templates/snippets/shelve_button/shelve_button_options.html @@ -45,7 +45,13 @@
{% csrf_token %} -
diff --git a/bookwyrm/templatetags/bookwyrm_tags.py b/bookwyrm/templatetags/bookwyrm_tags.py index f9668197..4eff0d82 100644 --- a/bookwyrm/templatetags/bookwyrm_tags.py +++ b/bookwyrm/templatetags/bookwyrm_tags.py @@ -43,6 +43,18 @@ def get_user_rating(book, user): return 0 +@register.filter(name="is_book_on_shelf") +def get_is_book_on_shelf(book, shelf): + """is a book on a shelf""" + return cache.get_or_set( + f"book-on-shelf-{book.id}-{shelf.id}", + lambda b, s: s.books.filter(id=b.id).exists(), + book, + shelf, + timeout=15552000, + ) + + @register.filter(name="book_description") def get_book_description(book): """use the work's text if the book doesn't have it""" diff --git a/bookwyrm/views/reading.py b/bookwyrm/views/reading.py index d90c0e9d..2cd05202 100644 --- a/bookwyrm/views/reading.py +++ b/bookwyrm/views/reading.py @@ -46,9 +46,7 @@ class ReadingStatus(View): return HttpResponseBadRequest() # invalidate related caches - cache.delete( - f"active_shelf-{request.user.id}-{book_id}", - ) + cache.delete(f"active_shelf-{request.user.id}-{book_id}") desired_shelf = get_object_or_404( models.Shelf, identifier=identifier, user=request.user From 1c096ecfc2c0091d2e79760d345d93cce9daa452 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Tue, 18 Jan 2022 10:57:52 -0800 Subject: [PATCH 13/15] Fixes review markup for mastodon serialization --- .../snippets/generated_status/review_pure_name.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bookwyrm/templates/snippets/generated_status/review_pure_name.html b/bookwyrm/templates/snippets/generated_status/review_pure_name.html index d67fd018..fa5eb944 100644 --- a/bookwyrm/templates/snippets/generated_status/review_pure_name.html +++ b/bookwyrm/templates/snippets/generated_status/review_pure_name.html @@ -2,15 +2,15 @@ {% if rating %} {% blocktrans trimmed with book_title=book.title|safe book_path=book.local_path display_rating=rating|floatformat:"-1" review_title=name|safe count counter=rating %} -Review of "{{ book_title }}" ({{ display_rating }} star): {{ review_title }} +Review of "{{ book_title }}" ({{ display_rating }} star): {{ review_title }} {% plural %} -Review of "{{ book_title }}" ({{ display_rating }} stars): {{ review_title }} +Review of "{{ book_title }}" ({{ display_rating }} stars): {{ review_title }} {% endblocktrans %} {% else %} {% blocktrans trimmed with book_title=book.title|safe book_path=book.local_path review_title=name|safe %} -Review of "{{ book_title }}": {{ review_title }} +Review of "{{ book_title }}": {{ review_title }} {% endblocktrans %} {% endif %} From 41c3eceb689f4dbac726b7ca029789b1a8ba651a Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Tue, 18 Jan 2022 13:18:56 -0800 Subject: [PATCH 14/15] Updates tests --- bookwyrm/tests/models/test_status_model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bookwyrm/tests/models/test_status_model.py b/bookwyrm/tests/models/test_status_model.py index 69895d9b..efbc5f37 100644 --- a/bookwyrm/tests/models/test_status_model.py +++ b/bookwyrm/tests/models/test_status_model.py @@ -301,7 +301,7 @@ class Status(TestCase): self.assertEqual(activity["type"], "Article") self.assertEqual( activity["name"], - f"Review of \"{self.book.title}\" (3 stars): Review's name", + f"Review of \"{self.book.title}\" (3 stars): Review's name", ) self.assertEqual(activity["content"], "test content") self.assertEqual(activity["attachment"][0].type, "Document") @@ -326,7 +326,7 @@ class Status(TestCase): self.assertEqual(activity["type"], "Article") self.assertEqual( activity["name"], - f"Review of \"{self.book.title}\": Review name", + f"Review of \"{self.book.title}\": Review name", ) self.assertEqual(activity["content"], "test content") self.assertEqual(activity["attachment"][0].type, "Document") From e47b5e788ddab28e244c062511aad4e828302fd9 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Tue, 18 Jan 2022 13:31:50 -0800 Subject: [PATCH 15/15] Remove link form review title html isn't supported in the name field I guess --- .../snippets/generated_status/review_pure_name.html | 6 +++--- bookwyrm/tests/models/test_status_model.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bookwyrm/templates/snippets/generated_status/review_pure_name.html b/bookwyrm/templates/snippets/generated_status/review_pure_name.html index fa5eb944..27e1cf18 100644 --- a/bookwyrm/templates/snippets/generated_status/review_pure_name.html +++ b/bookwyrm/templates/snippets/generated_status/review_pure_name.html @@ -2,15 +2,15 @@ {% if rating %} {% blocktrans trimmed with book_title=book.title|safe book_path=book.local_path display_rating=rating|floatformat:"-1" review_title=name|safe count counter=rating %} -Review of "{{ book_title }}" ({{ display_rating }} star): {{ review_title }} +Review of "{{ book_title }}" ({{ display_rating }} star): {{ review_title }} {% plural %} -Review of "{{ book_title }}" ({{ display_rating }} stars): {{ review_title }} +Review of "{{ book_title }}" ({{ display_rating }} stars): {{ review_title }} {% endblocktrans %} {% else %} {% blocktrans trimmed with book_title=book.title|safe book_path=book.local_path review_title=name|safe %} -Review of "{{ book_title }}": {{ review_title }} +Review of "{{ book_title }}": {{ review_title }} {% endblocktrans %} {% endif %} diff --git a/bookwyrm/tests/models/test_status_model.py b/bookwyrm/tests/models/test_status_model.py index efbc5f37..837cd41d 100644 --- a/bookwyrm/tests/models/test_status_model.py +++ b/bookwyrm/tests/models/test_status_model.py @@ -301,7 +301,7 @@ class Status(TestCase): self.assertEqual(activity["type"], "Article") self.assertEqual( activity["name"], - f"Review of \"{self.book.title}\" (3 stars): Review's name", + f'Review of "{self.book.title}" (3 stars): Review\'s name', ) self.assertEqual(activity["content"], "test content") self.assertEqual(activity["attachment"][0].type, "Document") @@ -326,7 +326,7 @@ class Status(TestCase): self.assertEqual(activity["type"], "Article") self.assertEqual( activity["name"], - f"Review of \"{self.book.title}\": Review name", + f'Review of "{self.book.title}": Review name', ) self.assertEqual(activity["content"], "test content") self.assertEqual(activity["attachment"][0].type, "Document")