From 5981575f0e9503d40bd35a6299bc8cc361661e35 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Tue, 4 Jan 2022 17:59:35 -0800 Subject: [PATCH 01/37] Cache template snipped for shelve buttons --- .../templates/snippets/shelve_button/shelve_button.html | 3 +++ bookwyrm/views/reading.py | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/bookwyrm/templates/snippets/shelve_button/shelve_button.html b/bookwyrm/templates/snippets/shelve_button/shelve_button.html index 38f6be38c..98edac6a1 100644 --- a/bookwyrm/templates/snippets/shelve_button/shelve_button.html +++ b/bookwyrm/templates/snippets/shelve_button/shelve_button.html @@ -1,7 +1,9 @@ {% load bookwyrm_tags %} {% load utilities %} +{% load cache %} {% if request.user.is_authenticated %} +{% cache 900 shelve_button request.user.id book.id %} {% with book.id|uuid as uuid %} {% active_shelf book as active_shelf %} @@ -32,4 +34,5 @@ {% include 'snippets/reading_modals/progress_update_modal.html' with book=active_shelf.book id=modal_id readthrough=readthrough class="" %} {% endwith %} +{% endcache %} {% endif %} diff --git a/bookwyrm/views/reading.py b/bookwyrm/views/reading.py index 35847558e..6531efc23 100644 --- a/bookwyrm/views/reading.py +++ b/bookwyrm/views/reading.py @@ -1,5 +1,7 @@ """ the good stuff! the books! """ from django.contrib.auth.decorators import login_required +from django.core.cache import cache +from django.core.cache.utils import make_template_fragment_key from django.db import transaction from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseNotFound from django.shortcuts import get_object_or_404, redirect @@ -44,6 +46,10 @@ class ReadingStatus(View): if not identifier: return HttpResponseBadRequest() + # invalidate the template cache + cache_key = make_template_fragment_key("shelve_button", [request.user.id, book_id]) + cache.delete(cache_key) + desired_shelf = get_object_or_404( models.Shelf, identifier=identifier, user=request.user ) From cc4469e7e10b674f31569831a7fce0787c3f15b3 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Tue, 4 Jan 2022 18:05:26 -0800 Subject: [PATCH 02/37] Python formatting --- bookwyrm/templates/snippets/shelve_button/shelve_button.html | 2 +- bookwyrm/views/reading.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/bookwyrm/templates/snippets/shelve_button/shelve_button.html b/bookwyrm/templates/snippets/shelve_button/shelve_button.html index 98edac6a1..825eabb18 100644 --- a/bookwyrm/templates/snippets/shelve_button/shelve_button.html +++ b/bookwyrm/templates/snippets/shelve_button/shelve_button.html @@ -3,7 +3,7 @@ {% load cache %} {% if request.user.is_authenticated %} -{% cache 900 shelve_button request.user.id book.id %} +{% cache shelve_button request.user.id book.id %} {% with book.id|uuid as uuid %} {% active_shelf book as active_shelf %} diff --git a/bookwyrm/views/reading.py b/bookwyrm/views/reading.py index 6531efc23..73bc0b834 100644 --- a/bookwyrm/views/reading.py +++ b/bookwyrm/views/reading.py @@ -47,7 +47,9 @@ class ReadingStatus(View): return HttpResponseBadRequest() # invalidate the template cache - cache_key = make_template_fragment_key("shelve_button", [request.user.id, book_id]) + cache_key = make_template_fragment_key( + "shelve_button", [request.user.id, book_id] + ) cache.delete(cache_key) desired_shelf = get_object_or_404( From b91649478b624b182df0af65ee502146e05981b6 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Tue, 4 Jan 2022 18:07:30 -0800 Subject: [PATCH 03/37] Fixes cache value --- bookwyrm/templates/snippets/shelve_button/shelve_button.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/templates/snippets/shelve_button/shelve_button.html b/bookwyrm/templates/snippets/shelve_button/shelve_button.html index 825eabb18..4d4d07877 100644 --- a/bookwyrm/templates/snippets/shelve_button/shelve_button.html +++ b/bookwyrm/templates/snippets/shelve_button/shelve_button.html @@ -3,7 +3,7 @@ {% load cache %} {% if request.user.is_authenticated %} -{% cache shelve_button request.user.id book.id %} +{% cache None shelve_button request.user.id book.id %} {% with book.id|uuid as uuid %} {% active_shelf book as active_shelf %} From 629140cad4b38685235772f46b0c9a688a7c5531 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 5 Jan 2022 08:27:31 -0800 Subject: [PATCH 04/37] Adds redis cache backend --- bookwyrm/settings.py | 14 ++++++++++++++ requirements.txt | 1 + 2 files changed, 15 insertions(+) diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index f2068a16b..0242ba477 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -119,6 +119,20 @@ STREAMS = [ {"key": "books", "name": _("Books Timeline"), "shortname": _("Books")}, ] +# Redis cache backend +# pylint: disable=line-too-long +CACHES = { + "default": { + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": f"redis://:{REDIS_ACTIVITY_PASSWORD}@{REDIS_ACTIVITY_HOST}:{REDIS_ACTIVITY_PORT}/0", + "OPTIONS": { + "CLIENT_CLASS": "django_redis.client.DefaultClient", + } + } +} +SESSION_ENGINE = "django.contrib.sessions.backends.cache" +SESSION_CACHE_ALIAS = "default" + # Database # https://docs.djangoproject.com/en/3.2/ref/settings/#databases diff --git a/requirements.txt b/requirements.txt index 9c22a6eda..ec97e3897 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,6 +17,7 @@ django-rename-app==0.1.2 pytz>=2021.1 boto3==1.17.88 django-storages==1.11.1 +django-redis==5.2.0 # Dev black==21.4b0 From bebb2c167ee00720bdf554a26f04f957edc50c16 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 5 Jan 2022 08:27:39 -0800 Subject: [PATCH 05/37] Use redis with password in dev --- .env.dev.example | 8 ++++---- bookwyrm/redis_store.py | 6 ++++-- celerywyrm/settings.py | 13 +++++++------ docker-compose.yml | 6 ++++-- redis.conf | 9 +++++++++ 5 files changed, 28 insertions(+), 14 deletions(-) create mode 100644 redis.conf diff --git a/.env.dev.example b/.env.dev.example index 22e12de12..b65c1b023 100644 --- a/.env.dev.example +++ b/.env.dev.example @@ -26,15 +26,15 @@ POSTGRES_HOST=db MAX_STREAM_LENGTH=200 REDIS_ACTIVITY_HOST=redis_activity REDIS_ACTIVITY_PORT=6379 -#REDIS_ACTIVITY_PASSWORD=redispassword345 +REDIS_ACTIVITY_PASSWORD=redispassword345 # Redis as celery broker REDIS_BROKER_PORT=6379 -#REDIS_BROKER_PASSWORD=redispassword123 +REDIS_BROKER_PASSWORD=redispassword123 FLOWER_PORT=8888 -#FLOWER_USER=mouse -#FLOWER_PASSWORD=changeme +FLOWER_USER=mouse +FLOWER_PASSWORD=changeme EMAIL_HOST=smtp.mailgun.org EMAIL_PORT=587 diff --git a/bookwyrm/redis_store.py b/bookwyrm/redis_store.py index 78f373a2e..3aec8bd68 100644 --- a/bookwyrm/redis_store.py +++ b/bookwyrm/redis_store.py @@ -5,10 +5,12 @@ import redis from bookwyrm import settings r = redis.Redis( - host=settings.REDIS_ACTIVITY_HOST, port=settings.REDIS_ACTIVITY_PORT, db=0 + host=settings.REDIS_ACTIVITY_HOST, + port=settings.REDIS_ACTIVITY_PORT, + password=settings.REDIS_ACTIVITY_PASSWORD, + db=0, ) - class RedisStore(ABC): """sets of ranked, related objects, like statuses for a user's feed""" diff --git a/celerywyrm/settings.py b/celerywyrm/settings.py index 05ffdcabf..082bb5db1 100644 --- a/celerywyrm/settings.py +++ b/celerywyrm/settings.py @@ -3,12 +3,13 @@ # pylint: disable=unused-wildcard-import from bookwyrm.settings import * -CELERY_BROKER_URL = "redis://:{}@redis_broker:{}/0".format( - requests.utils.quote(env("REDIS_BROKER_PASSWORD", "")), env("REDIS_BROKER_PORT") -) -CELERY_RESULT_BACKEND = "redis://:{}@redis_broker:{}/0".format( - requests.utils.quote(env("REDIS_BROKER_PASSWORD", "")), env("REDIS_BROKER_PORT") -) +REDIS_BROKER_PASSWORD = requests.utils.quote(env("REDIS_BROKER_PASSWORD", None)) +REDIS_BROKER_HOST = env("REDIS_BROKER_HOST", "redis_broker") +REDIS_BROKER_PORT = env("REDIS_BROKER_PORT", 6379) + +# pylint: disable=line-too-long +CELERY_BROKER_URL = f"redis://:{REDIS_BROKER_PASSWORD}@{REDIS_BROKER_HOST}:{REDIS_BROKER_PORT}/0" +CELERY_RESULT_BACKEND = f"redis://:{REDIS_BROKER_PASSWORD}@{REDIS_BROKER_HOST}:{REDIS_BROKER_PORT}/0" CELERY_DEFAULT_QUEUE = "low_priority" diff --git a/docker-compose.yml b/docker-compose.yml index afa40b05e..25a397eae 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -38,16 +38,17 @@ services: - 8000:8000 redis_activity: image: redis - command: ["redis-server", "--appendonly", "yes"] + command: redis-server --requirepass ${REDIS_ACTIVITY_PASSWORD} --appendonly yes --port ${REDIS_ACTIVITY_PORT} env_file: .env networks: - main restart: on-failure volumes: + - ./redis.conf:/etc/redis/redis.conf - redis_activity_data:/data redis_broker: image: redis - command: ["redis-server", "--appendonly", "yes"] + command: redis-server --requirepass ${REDIS_BROKER_PASSWORD} --appendonly yes --port ${REDIS_BROKER_PORT} env_file: .env ports: - 6379:6379 @@ -55,6 +56,7 @@ services: - main restart: on-failure volumes: + - ./redis.conf:/etc/redis/redis.conf - redis_broker_data:/data celery_worker: env_file: .env diff --git a/redis.conf b/redis.conf new file mode 100644 index 000000000..2a417579f --- /dev/null +++ b/redis.conf @@ -0,0 +1,9 @@ +bind 127.0.0.1 ::1 +protected-mode yes +port 6379 + +rename-command FLUSHDB "" +rename-command FLUSHALL "" +rename-command DEBUG "" +rename-command CONFIG "" +rename-command SHUTDOWN "" From e6f7828361ad25accb2a26cbb3947a136aecce2f Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 5 Jan 2022 09:30:21 -0800 Subject: [PATCH 06/37] Python formatting --- bookwyrm/redis_store.py | 1 + bookwyrm/settings.py | 2 +- celerywyrm/settings.py | 9 ++++++--- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/bookwyrm/redis_store.py b/bookwyrm/redis_store.py index 3aec8bd68..595868ff5 100644 --- a/bookwyrm/redis_store.py +++ b/bookwyrm/redis_store.py @@ -11,6 +11,7 @@ r = redis.Redis( db=0, ) + class RedisStore(ABC): """sets of ranked, related objects, like statuses for a user's feed""" diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index 0242ba477..fcc9b51f7 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -127,7 +127,7 @@ CACHES = { "LOCATION": f"redis://:{REDIS_ACTIVITY_PASSWORD}@{REDIS_ACTIVITY_HOST}:{REDIS_ACTIVITY_PORT}/0", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", - } + }, } } SESSION_ENGINE = "django.contrib.sessions.backends.cache" diff --git a/celerywyrm/settings.py b/celerywyrm/settings.py index 082bb5db1..24729345b 100644 --- a/celerywyrm/settings.py +++ b/celerywyrm/settings.py @@ -7,9 +7,12 @@ REDIS_BROKER_PASSWORD = requests.utils.quote(env("REDIS_BROKER_PASSWORD", None)) REDIS_BROKER_HOST = env("REDIS_BROKER_HOST", "redis_broker") REDIS_BROKER_PORT = env("REDIS_BROKER_PORT", 6379) -# pylint: disable=line-too-long -CELERY_BROKER_URL = f"redis://:{REDIS_BROKER_PASSWORD}@{REDIS_BROKER_HOST}:{REDIS_BROKER_PORT}/0" -CELERY_RESULT_BACKEND = f"redis://:{REDIS_BROKER_PASSWORD}@{REDIS_BROKER_HOST}:{REDIS_BROKER_PORT}/0" +CELERY_BROKER_URL = ( + f"redis://:{REDIS_BROKER_PASSWORD}@{REDIS_BROKER_HOST}:{REDIS_BROKER_PORT}/0" +) +CELERY_RESULT_BACKEND = ( + f"redis://:{REDIS_BROKER_PASSWORD}@{REDIS_BROKER_HOST}:{REDIS_BROKER_PORT}/0" +) CELERY_DEFAULT_QUEUE = "low_priority" From 66456fc508f4d07589f2fce37ccf2be81971a8b6 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 5 Jan 2022 09:32:10 -0800 Subject: [PATCH 07/37] Adds test env var --- .github/workflows/django-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/django-tests.yml b/.github/workflows/django-tests.yml index 038751935..bfe5bb2f1 100644 --- a/.github/workflows/django-tests.yml +++ b/.github/workflows/django-tests.yml @@ -46,6 +46,7 @@ jobs: POSTGRES_HOST: 127.0.0.1 CELERY_BROKER: "" REDIS_BROKER_PORT: 6379 + REDIS_BROKER_PASSWORD: beep FLOWER_PORT: 8888 EMAIL_HOST: "smtp.mailgun.org" EMAIL_PORT: 587 From 2cad762646381ae571cbca1918b4d754e3123e95 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 5 Jan 2022 09:51:00 -0800 Subject: [PATCH 08/37] Use in-memory cache for CI tests --- .github/workflows/django-tests.yml | 1 + bookwyrm/settings.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/.github/workflows/django-tests.yml b/.github/workflows/django-tests.yml index bfe5bb2f1..4968b40cf 100644 --- a/.github/workflows/django-tests.yml +++ b/.github/workflows/django-tests.yml @@ -47,6 +47,7 @@ jobs: CELERY_BROKER: "" REDIS_BROKER_PORT: 6379 REDIS_BROKER_PASSWORD: beep + USE_LOCAL_CACHE: true FLOWER_PORT: 8888 EMAIL_HOST: "smtp.mailgun.org" EMAIL_PORT: 587 diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index fcc9b51f7..59931dab9 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -130,6 +130,10 @@ CACHES = { }, } } +if env("USE_LOCAL_CACHE", False): + # use the default local memory cache for testing + CACHE = None + SESSION_ENGINE = "django.contrib.sessions.backends.cache" SESSION_CACHE_ALIAS = "default" From d8dbf94c290a470aec007cfa00a99c0203327441 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 5 Jan 2022 10:16:49 -0800 Subject: [PATCH 09/37] Fixes cache syntax --- bookwyrm/settings.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index 59931dab9..57717fb99 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -120,22 +120,20 @@ STREAMS = [ ] # Redis cache backend -# pylint: disable=line-too-long -CACHES = { - "default": { - "BACKEND": "django_redis.cache.RedisCache", - "LOCATION": f"redis://:{REDIS_ACTIVITY_PASSWORD}@{REDIS_ACTIVITY_HOST}:{REDIS_ACTIVITY_PORT}/0", - "OPTIONS": { - "CLIENT_CLASS": "django_redis.client.DefaultClient", - }, +if not env("USE_LOCAL_CACHE", False): + # pylint: disable=line-too-long + CACHES = { + "default": { + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": f"redis://:{REDIS_ACTIVITY_PASSWORD}@{REDIS_ACTIVITY_HOST}:{REDIS_ACTIVITY_PORT}/0", + "OPTIONS": { + "CLIENT_CLASS": "django_redis.client.DefaultClient", + }, + } } -} -if env("USE_LOCAL_CACHE", False): - # use the default local memory cache for testing - CACHE = None -SESSION_ENGINE = "django.contrib.sessions.backends.cache" -SESSION_CACHE_ALIAS = "default" + SESSION_ENGINE = "django.contrib.sessions.backends.cache" + SESSION_CACHE_ALIAS = "default" # Database # https://docs.djangoproject.com/en/3.2/ref/settings/#databases From a5309e99731cbd650f437b3ebe17a7119822cea4 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 5 Jan 2022 10:22:25 -0800 Subject: [PATCH 10/37] Cache status contents --- bookwyrm/templates/snippets/status/layout.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bookwyrm/templates/snippets/status/layout.html b/bookwyrm/templates/snippets/status/layout.html index 93620a083..139aeb6ce 100644 --- a/bookwyrm/templates/snippets/status/layout.html +++ b/bookwyrm/templates/snippets/status/layout.html @@ -1,6 +1,7 @@ {% extends 'components/card.html' %} {% load i18n %} {% load utilities %} +{% load cache %} {% block card-header %}
{% endblock %} +{# three day cache #} +{% cache 259200 status_content status.id %} {% block card-content %}{% endblock %} +{% endcache %} {% block card-footer %} {% if moderation_mode and perms.bookwyrm.moderate_post %} From 6823d5f1b7d4493056583c9b1c5f333cd6c2b1e6 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 5 Jan 2022 12:55:19 -0800 Subject: [PATCH 11/37] Cache follow button template snippet --- bookwyrm/models/relationship.py | 16 ++++++++++++++++ bookwyrm/templates/snippets/follow_button.html | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/bookwyrm/models/relationship.py b/bookwyrm/models/relationship.py index fc7a9df83..034174546 100644 --- a/bookwyrm/models/relationship.py +++ b/bookwyrm/models/relationship.py @@ -1,5 +1,7 @@ """ defines relationships between users """ from django.apps import apps +from django.core.cache import cache +from django.core.cache.utils import make_template_fragment_key from django.db import models, transaction, IntegrityError from django.db.models import Q @@ -36,6 +38,20 @@ class UserRelationship(BookWyrmModel): """the remote user needs to recieve direct broadcasts""" return [u for u in [self.user_subject, self.user_object] if not u.local] + def save(self, *args, **kwargs): + """clear the template cache""" + # invalidate the template cache + cache_keys = [ + make_template_fragment_key( + "follow_button", [self.user_subject.id, self.user_object.id] + ), + make_template_fragment_key( + "follow_button", [self.user_object.id, self.user_subject.id] + ), + ] + cache.delete_many(cache_keys) + super().save(*args, **kwargs) + class Meta: """relationships should be unique""" diff --git a/bookwyrm/templates/snippets/follow_button.html b/bookwyrm/templates/snippets/follow_button.html index 530322b94..4e4793bee 100644 --- a/bookwyrm/templates/snippets/follow_button.html +++ b/bookwyrm/templates/snippets/follow_button.html @@ -1,4 +1,7 @@ {% load i18n %} +{% load cache %} + +{% cache None follow_button request.user.id user.id %} {% if request.user == user or not request.user.is_authenticated %} {% elif user in request.user.blocks.all %} {% include 'snippets/block_button.html' with blocks=True %} @@ -42,3 +45,4 @@ {% endif %}
{% endif %} +{% endcache %} From 3cda27577f14ae6242d43de800493f80279d1a80 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 5 Jan 2022 13:01:53 -0800 Subject: [PATCH 12/37] Removes status content cache --- .../templates/snippets/status/headers/generatednote.html | 5 +++++ bookwyrm/templates/snippets/status/layout.html | 4 ---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/bookwyrm/templates/snippets/status/headers/generatednote.html b/bookwyrm/templates/snippets/status/headers/generatednote.html index cc684a5f2..7fc635ab2 100644 --- a/bookwyrm/templates/snippets/status/headers/generatednote.html +++ b/bookwyrm/templates/snippets/status/headers/generatednote.html @@ -1,3 +1,7 @@ +{% load cache %} + +{# Three day cache #} +{% cache 259200 generated_note_header status.id %} {% if status.content == 'wants to read' %} {% include 'snippets/status/headers/to_read.html' with book=status.mention_books.first %} {% elif status.content == 'finished reading' %} @@ -7,3 +11,4 @@ {% else %} {{ status.content }} {% endif %} +{% endcache %} diff --git a/bookwyrm/templates/snippets/status/layout.html b/bookwyrm/templates/snippets/status/layout.html index 139aeb6ce..93620a083 100644 --- a/bookwyrm/templates/snippets/status/layout.html +++ b/bookwyrm/templates/snippets/status/layout.html @@ -1,7 +1,6 @@ {% extends 'components/card.html' %} {% load i18n %} {% load utilities %} -{% load cache %} {% block card-header %}
{% endblock %} -{# three day cache #} -{% cache 259200 status_content status.id %} {% block card-content %}{% endblock %} -{% endcache %} {% block card-footer %} {% if moderation_mode and perms.bookwyrm.moderate_post %} From ad6d7d5ecadbb28f8b073ba22ac6589f536b3bce Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 5 Jan 2022 13:12:49 -0800 Subject: [PATCH 13/37] Caches suggested books --- bookwyrm/templates/feed/layout.html | 77 +----------------- bookwyrm/templates/feed/suggested_books.html | 82 ++++++++++++++++++++ bookwyrm/templatetags/bookwyrm_tags.py | 9 +++ bookwyrm/views/feed.py | 1 - bookwyrm/views/reading.py | 9 ++- 5 files changed, 97 insertions(+), 81 deletions(-) create mode 100644 bookwyrm/templates/feed/suggested_books.html diff --git a/bookwyrm/templates/feed/layout.html b/bookwyrm/templates/feed/layout.html index 6e7ec849f..5697f2669 100644 --- a/bookwyrm/templates/feed/layout.html +++ b/bookwyrm/templates/feed/layout.html @@ -8,82 +8,7 @@
{% if user.is_authenticated %}
-
-

{% trans "Your Books" %}

- {% if not suggested_books %} -

{% trans "There are no books here right now! Try searching for a book to get started" %}

- {% else %} - {% with active_book=request.GET.book %} -
-
-
    - {% for shelf in suggested_books %} - {% if shelf.books %} - {% with shelf_counter=forloop.counter %} -
  • -

    - {% if shelf.identifier == 'to-read' %}{% trans "To Read" %} - {% elif shelf.identifier == 'reading' %}{% trans "Currently Reading" %} - {% elif shelf.identifier == 'read' %}{% trans "Read" %} - {% else %}{{ shelf.name }}{% endif %} -

    -
    - -
    -
  • - {% endwith %} - {% endif %} - {% endfor %} -
-
- {% for shelf in suggested_books %} - {% with shelf_counter=forloop.counter %} - {% for book in shelf.books %} -
- -
-
-
-

{% include 'snippets/book_titleby.html' with book=book %}

- {% include 'snippets/shelve_button/shelve_button.html' with book=book %} -
-
-
- {% trans "Close" as button_text %} - {% include 'snippets/toggle/toggle_button.html' with label=button_text controls_text="book" controls_uid=book.id class="delete" nonbutton=True pressed=True %} -
-
-
- {% include 'snippets/create_status.html' with book=book %} -
-
- {% endfor %} - {% endwith %} - {% endfor %} -
- {% endwith %} - {% endif %} -
- + {% include "feed/suggested_books.html" %} {% if goal %}
diff --git a/bookwyrm/templates/feed/suggested_books.html b/bookwyrm/templates/feed/suggested_books.html new file mode 100644 index 000000000..d559c6caa --- /dev/null +++ b/bookwyrm/templates/feed/suggested_books.html @@ -0,0 +1,82 @@ +{% load i18n %} +{% load cache %} +{% load bookwyrm_tags %} + +{% cache None suggested_books request.user.id %} +{% suggested_books as suggested_books %} +
+

{% trans "Your Books" %}

+ {% if not suggested_books %} +

{% trans "There are no books here right now! Try searching for a book to get started" %}

+ {% else %} + {% with active_book=request.GET.book %} +
+
+
    + {% for shelf in suggested_books %} + {% if shelf.books %} + {% with shelf_counter=forloop.counter %} +
  • +

    + {% if shelf.identifier == 'to-read' %}{% trans "To Read" %} + {% elif shelf.identifier == 'reading' %}{% trans "Currently Reading" %} + {% elif shelf.identifier == 'read' %}{% trans "Read" %} + {% else %}{{ shelf.name }}{% endif %} +

    +
    + +
    +
  • + {% endwith %} + {% endif %} + {% endfor %} +
+
+ {% for shelf in suggested_books %} + {% with shelf_counter=forloop.counter %} + {% for book in shelf.books %} +
+ +
+
+
+

{% include 'snippets/book_titleby.html' with book=book %}

+ {% include 'snippets/shelve_button/shelve_button.html' with book=book %} +
+
+
+ {% trans "Close" as button_text %} + {% include 'snippets/toggle/toggle_button.html' with label=button_text controls_text="book" controls_uid=book.id class="delete" nonbutton=True pressed=True %} +
+
+
+ {% include 'snippets/create_status.html' with book=book %} +
+
+ {% endfor %} + {% endwith %} + {% endfor %} +
+ {% endwith %} + {% endif %} +
+{% endcache %} diff --git a/bookwyrm/templatetags/bookwyrm_tags.py b/bookwyrm/templatetags/bookwyrm_tags.py index 9d84d1ff8..c15df0906 100644 --- a/bookwyrm/templatetags/bookwyrm_tags.py +++ b/bookwyrm/templatetags/bookwyrm_tags.py @@ -3,6 +3,7 @@ from django import template from django.db.models import Avg from bookwyrm import models +from bookwyrm.views.feed import get_suggested_books register = template.Library() @@ -115,3 +116,11 @@ def mutuals_count(context, user): if not viewer.is_authenticated: return None return user.followers.filter(followers=viewer).count() + + +@register.simple_tag(takes_context=True) +def suggested_books(context): + """get books for suggested books panel""" + # this happens here instead of in the view so that the template snippet can + # be cached in the template + return get_suggested_books(context["request"].user) diff --git a/bookwyrm/views/feed.py b/bookwyrm/views/feed.py index f6f9fb37a..e060ae616 100644 --- a/bookwyrm/views/feed.py +++ b/bookwyrm/views/feed.py @@ -223,7 +223,6 @@ def feed_page_data(user): goal = models.AnnualGoal.objects.filter(user=user, year=timezone.now().year).first() return { - "suggested_books": get_suggested_books(user), "goal": goal, "goal_form": forms.GoalForm(), } diff --git a/bookwyrm/views/reading.py b/bookwyrm/views/reading.py index 73bc0b834..c7eda10e1 100644 --- a/bookwyrm/views/reading.py +++ b/bookwyrm/views/reading.py @@ -47,10 +47,11 @@ class ReadingStatus(View): return HttpResponseBadRequest() # invalidate the template cache - cache_key = make_template_fragment_key( - "shelve_button", [request.user.id, book_id] - ) - cache.delete(cache_key) + cache_keys = [ + make_template_fragment_key("shelve_button", [request.user.id, book_id]), + make_template_fragment_key("suggested_books", [request.user.id]), + ] + cache.delete_many(cache_keys) desired_shelf = get_object_or_404( models.Shelf, identifier=identifier, user=request.user From 4a43ad95efff0d5f36a890f0945672e48f59b63a Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 5 Jan 2022 13:51:34 -0800 Subject: [PATCH 14/37] Linting on suggested_users --- bookwyrm/suggested_users.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/bookwyrm/suggested_users.py b/bookwyrm/suggested_users.py index 86c181a28..d313c7028 100644 --- a/bookwyrm/suggested_users.py +++ b/bookwyrm/suggested_users.py @@ -29,6 +29,7 @@ class SuggestedUsers(RedisStore): def get_counts_from_rank(self, rank): # pylint: disable=no-self-use """calculate mutuals count and shared books count from rank""" + # pylint: disable=c-extension-no-member return { "mutuals": math.floor(rank), # "shared_books": int(1 / (-1 * (rank % 1 - 1))) - 1, @@ -119,16 +120,17 @@ def get_annotated_users(viewer, *args, **kwargs): ), distinct=True, ), - # shared_books=Count( - # "shelfbook", - # filter=Q( - # ~Q(id=viewer.id), - # shelfbook__book__parent_work__in=[ - # s.book.parent_work for s in viewer.shelfbook_set.all() - # ], - # ), - # distinct=True, - # ), + # pylint: disable=line-too-long + # shared_books=Count( + # "shelfbook", + # filter=Q( + # ~Q(id=viewer.id), + # shelfbook__book__parent_work__in=[ + # s.book.parent_work for s in viewer.shelfbook_set.all() + # ], + # ), + # distinct=True, + # ), ) ) From 0da0091237377cd04004a7e25ba8eedb10a46df5 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 5 Jan 2022 14:33:10 -0800 Subject: [PATCH 15/37] Cache title author snippet --- bookwyrm/models/author.py | 13 +++++++++++++ bookwyrm/models/book.py | 7 +++++++ bookwyrm/templates/snippets/book_titleby.html | 4 ++++ 3 files changed, 24 insertions(+) diff --git a/bookwyrm/models/author.py b/bookwyrm/models/author.py index 5cc11afd6..a4d8f9f16 100644 --- a/bookwyrm/models/author.py +++ b/bookwyrm/models/author.py @@ -1,6 +1,8 @@ """ database schema for info about authors """ import re from django.contrib.postgres.indexes import GinIndex +from django.core.cache import cache +from django.core.cache.utils import make_template_fragment_key from django.db import models from bookwyrm import activitypub @@ -34,6 +36,17 @@ class Author(BookDataModel): ) bio = fields.HtmlField(null=True, blank=True) + def save(self, *args, **kwargs): + """clear related template caches""" + # clear template caches + cache_keys = [ + make_template_fragment_key("titleby", [book]) + for book in self.book_set.values_list("id", flat=True) + ] + cache.delete_many(cache_keys) + + return super().save(*args, **kwargs) + @property def isni_link(self): """generate the url from the isni id""" diff --git a/bookwyrm/models/book.py b/bookwyrm/models/book.py index 0a551bf28..a9dd9508d 100644 --- a/bookwyrm/models/book.py +++ b/bookwyrm/models/book.py @@ -3,6 +3,8 @@ import re from django.contrib.postgres.search import SearchVectorField from django.contrib.postgres.indexes import GinIndex +from django.core.cache import cache +from django.core.cache.utils import make_template_fragment_key from django.db import models, transaction from django.db.models import Prefetch from django.dispatch import receiver @@ -185,6 +187,11 @@ class Book(BookDataModel): """can't be abstract for query reasons, but you shouldn't USE it""" if not isinstance(self, Edition) and not isinstance(self, Work): raise ValueError("Books should be added as Editions or Works") + + # clear template caches + cache_key = make_template_fragment_key("titleby", [self.id]) + cache.delete(cache_key) + return super().save(*args, **kwargs) def get_remote_id(self): diff --git a/bookwyrm/templates/snippets/book_titleby.html b/bookwyrm/templates/snippets/book_titleby.html index 6dbaeb265..d8e3af994 100644 --- a/bookwyrm/templates/snippets/book_titleby.html +++ b/bookwyrm/templates/snippets/book_titleby.html @@ -1,6 +1,8 @@ {% load i18n %} {% load utilities %} +{% load cache %} {% spaceless %} +{% cache None titleby book.id %} {% if book.authors.exists %} {% blocktrans trimmed with path=book.local_path title=book|book_title %} @@ -10,4 +12,6 @@ {% else %} {{ book|book_title }} {% endif %} + +{% endcache %} {% endspaceless %} From 7df99afdc7eb4f82dba68e97b8fd27b8ff58eafc Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 5 Jan 2022 14:54:51 -0800 Subject: [PATCH 16/37] Cache status interact buttons --- bookwyrm/models/status.py | 1 + .../templates/snippets/status/layout.html | 60 ++++++++++--------- bookwyrm/views/interaction.py | 14 ++++- 3 files changed, 46 insertions(+), 29 deletions(-) diff --git a/bookwyrm/models/status.py b/bookwyrm/models/status.py index c7c0a4253..ee138d979 100644 --- a/bookwyrm/models/status.py +++ b/bookwyrm/models/status.py @@ -82,6 +82,7 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel): if not self.reply_parent: self.thread_id = self.id + super().save(broadcast=False, update_fields=["thread_id"]) def delete(self, *args, **kwargs): # pylint: disable=unused-argument diff --git a/bookwyrm/templates/snippets/status/layout.html b/bookwyrm/templates/snippets/status/layout.html index 93620a083..174c379f1 100644 --- a/bookwyrm/templates/snippets/status/layout.html +++ b/bookwyrm/templates/snippets/status/layout.html @@ -1,6 +1,7 @@ {% extends 'components/card.html' %} {% load i18n %} {% load utilities %} +{% load cache %} {% block card-header %} - - -{% if not moderation_mode %} - -{% endif %} + {% cache 259200 interact request.user.id status.id %} + + + + {% if not moderation_mode %} + + {% endif %} + {% endcache %} {% else %} - {% endif %} {% endblock %} diff --git a/bookwyrm/views/interaction.py b/bookwyrm/views/interaction.py index 910360d7a..9e897beb8 100644 --- a/bookwyrm/views/interaction.py +++ b/bookwyrm/views/interaction.py @@ -1,6 +1,8 @@ """ boosts and favs """ -from django.db import IntegrityError from django.contrib.auth.decorators import login_required +from django.core.cache import cache +from django.core.cache.utils import make_template_fragment_key +from django.db import IntegrityError from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseNotFound from django.shortcuts import redirect from django.utils.decorators import method_decorator @@ -17,6 +19,7 @@ class Favorite(View): def post(self, request, status_id): """create a like""" + clear_cache(request.user.id, status_id) status = models.Status.objects.get(id=status_id) try: models.Favorite.objects.create(status=status, user=request.user) @@ -43,6 +46,7 @@ class Unfavorite(View): return HttpResponseNotFound() favorite.delete() + clear_cache(request.user.id, status_id) if is_api_request(request): return HttpResponse() return redirect(request.headers.get("Referer", "/")) @@ -70,6 +74,7 @@ class Boost(View): privacy=status.privacy, user=request.user, ) + clear_cache(request.user.id, status_id) if is_api_request(request): return HttpResponse() return redirect(request.headers.get("Referer", "/")) @@ -87,6 +92,13 @@ class Unboost(View): ).first() boost.delete() + clear_cache(request.user.id, status_id) if is_api_request(request): return HttpResponse() return redirect(request.headers.get("Referer", "/")) + + +def clear_cache(user_id, status_id): + """clear template cache""" + cache_key = make_template_fragment_key("interact", [user_id, status_id]) + cache.delete(cache_key) From b0fef8f0e306f826f24c269b3b249624a319cc37 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 5 Jan 2022 15:53:01 -0800 Subject: [PATCH 17/37] Cache landing page --- bookwyrm/views/landing/landing.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bookwyrm/views/landing/landing.py b/bookwyrm/views/landing/landing.py index c8bba0664..74b5ee513 100644 --- a/bookwyrm/views/landing/landing.py +++ b/bookwyrm/views/landing/landing.py @@ -1,6 +1,8 @@ """ non-interactive pages """ from django.template.response import TemplateResponse from django.views import View +from django.utils.decorators import method_decorator +from django.views.decorators.cache import cache_page from bookwyrm import forms from bookwyrm.views import helpers @@ -31,6 +33,7 @@ class Home(View): class Landing(View): """preview of recently reviewed books""" + @method_decorator(cache_page(60 * 60), name="dispatch") def get(self, request): """tiled book activity page""" data = { From 0f37e0ad42262966a01271d5adb9a5198f357ce7 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 5 Jan 2022 15:55:12 -0800 Subject: [PATCH 18/37] Don't try to clear caches for nonexistant authors --- bookwyrm/models/author.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bookwyrm/models/author.py b/bookwyrm/models/author.py index a4d8f9f16..5edac57d5 100644 --- a/bookwyrm/models/author.py +++ b/bookwyrm/models/author.py @@ -39,12 +39,12 @@ class Author(BookDataModel): def save(self, *args, **kwargs): """clear related template caches""" # clear template caches - cache_keys = [ - make_template_fragment_key("titleby", [book]) - for book in self.book_set.values_list("id", flat=True) - ] - cache.delete_many(cache_keys) - + if self.id: + cache_keys = [ + make_template_fragment_key("titleby", [book]) + for book in self.book_set.values_list("id", flat=True) + ] + cache.delete_many(cache_keys) return super().save(*args, **kwargs) @property From 2fed1888626a4cbaaa19f4df2497e51cd0fbea01 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 5 Jan 2022 17:35:42 -0800 Subject: [PATCH 19/37] Configure email sender from .env file --- .env.dev.example | 3 +++ .env.prod.example | 3 +++ bookwyrm/emailing.py | 2 +- bookwyrm/settings.py | 4 +++- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.env.dev.example b/.env.dev.example index 22e12de12..4a2cac261 100644 --- a/.env.dev.example +++ b/.env.dev.example @@ -42,6 +42,9 @@ EMAIL_HOST_USER=mail@your.domain.here EMAIL_HOST_PASSWORD=emailpassword123 EMAIL_USE_TLS=true EMAIL_USE_SSL=false +EMAIL_SENDER_NAME=admin +# defaults to DOMAIN +EMAIL_SENDER_DOMAIN= # Thumbnails Generation ENABLE_THUMBNAIL_GENERATION=false diff --git a/.env.prod.example b/.env.prod.example index 2e0ced5e8..e016839fd 100644 --- a/.env.prod.example +++ b/.env.prod.example @@ -42,6 +42,9 @@ EMAIL_HOST_USER=mail@your.domain.here EMAIL_HOST_PASSWORD=emailpassword123 EMAIL_USE_TLS=true EMAIL_USE_SSL=false +EMAIL_SENDER_NAME=admin +# defaults to DOMAIN +EMAIL_SENDER_DOMAIN= # Thumbnails Generation ENABLE_THUMBNAIL_GENERATION=false diff --git a/bookwyrm/emailing.py b/bookwyrm/emailing.py index 08fd9ef85..efef12638 100644 --- a/bookwyrm/emailing.py +++ b/bookwyrm/emailing.py @@ -69,7 +69,7 @@ def format_email(email_name, data): def send_email(recipient, subject, html_content, text_content): """use a task to send the email""" email = EmailMultiAlternatives( - subject, text_content, settings.DEFAULT_FROM_EMAIL, [recipient] + subject, text_content, settings.EMAIL_SENDER, [recipient] ) email.attach_alternative(html_content, "text/html") email.send() diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index f2068a16b..d7f4a5050 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -24,7 +24,9 @@ EMAIL_HOST_USER = env("EMAIL_HOST_USER") EMAIL_HOST_PASSWORD = env("EMAIL_HOST_PASSWORD") EMAIL_USE_TLS = env.bool("EMAIL_USE_TLS", True) EMAIL_USE_SSL = env.bool("EMAIL_USE_SSL", False) -DEFAULT_FROM_EMAIL = f"admin@{DOMAIN}" +EMAIL_SENDER_NAME = env.bool("EMAIL_SENDER_NAME", "admin") +EMAIL_SENDER_DOMAIN = env.bool("EMAIL_SENDER_NAME", DOMAIN) +EMAIL_SENDER = f"{EMAIL_SENDER_NAME}@{EMAIL_SENDER_DOMAIN}" # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) From 99a5924ea8150ec04a50448416e5da5850c39a33 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 5 Jan 2022 17:36:52 -0800 Subject: [PATCH 20/37] Not bool --- bookwyrm/settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index d7f4a5050..7bf710ed4 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -24,8 +24,8 @@ EMAIL_HOST_USER = env("EMAIL_HOST_USER") EMAIL_HOST_PASSWORD = env("EMAIL_HOST_PASSWORD") EMAIL_USE_TLS = env.bool("EMAIL_USE_TLS", True) EMAIL_USE_SSL = env.bool("EMAIL_USE_SSL", False) -EMAIL_SENDER_NAME = env.bool("EMAIL_SENDER_NAME", "admin") -EMAIL_SENDER_DOMAIN = env.bool("EMAIL_SENDER_NAME", DOMAIN) +EMAIL_SENDER_NAME = env("EMAIL_SENDER_NAME", "admin") +EMAIL_SENDER_DOMAIN = env("EMAIL_SENDER_NAME", DOMAIN) EMAIL_SENDER = f"{EMAIL_SENDER_NAME}@{EMAIL_SENDER_DOMAIN}" # Build paths inside the project like this: os.path.join(BASE_DIR, ...) From b3bfaf0586cbf436bbbe6ba4b9d118c2c3c8836d Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 6 Jan 2022 10:27:12 -0800 Subject: [PATCH 21/37] Use 6 month cache for items that don't really need to expire --- bookwyrm/templates/feed/suggested_books.html | 3 ++- bookwyrm/templates/snippets/book_titleby.html | 4 +++- bookwyrm/templates/snippets/follow_button.html | 3 ++- bookwyrm/templates/snippets/shelve_button/shelve_button.html | 3 ++- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/bookwyrm/templates/feed/suggested_books.html b/bookwyrm/templates/feed/suggested_books.html index d559c6caa..b2f1b5d28 100644 --- a/bookwyrm/templates/feed/suggested_books.html +++ b/bookwyrm/templates/feed/suggested_books.html @@ -2,7 +2,8 @@ {% load cache %} {% load bookwyrm_tags %} -{% cache None suggested_books request.user.id %} +{# 6 month cache #} +{% cache 15552000 suggested_books request.user.id %} {% suggested_books as suggested_books %}

{% trans "Your Books" %}

diff --git a/bookwyrm/templates/snippets/book_titleby.html b/bookwyrm/templates/snippets/book_titleby.html index d8e3af994..5e35e36a7 100644 --- a/bookwyrm/templates/snippets/book_titleby.html +++ b/bookwyrm/templates/snippets/book_titleby.html @@ -2,7 +2,9 @@ {% load utilities %} {% load cache %} {% spaceless %} -{% cache None titleby book.id %} + +{# 6 month cache #} +{% cache 15552000 titleby book.id %} {% if book.authors.exists %} {% blocktrans trimmed with path=book.local_path title=book|book_title %} diff --git a/bookwyrm/templates/snippets/follow_button.html b/bookwyrm/templates/snippets/follow_button.html index 4e4793bee..a5c40b24b 100644 --- a/bookwyrm/templates/snippets/follow_button.html +++ b/bookwyrm/templates/snippets/follow_button.html @@ -1,7 +1,8 @@ {% load i18n %} {% load cache %} -{% cache None follow_button request.user.id user.id %} +{# 6 month cache #} +{% cache 15552000 follow_button request.user.id user.id %} {% if request.user == user or not request.user.is_authenticated %} {% elif user in request.user.blocks.all %} {% include 'snippets/block_button.html' with blocks=True %} diff --git a/bookwyrm/templates/snippets/shelve_button/shelve_button.html b/bookwyrm/templates/snippets/shelve_button/shelve_button.html index 4d4d07877..0ffc708d3 100644 --- a/bookwyrm/templates/snippets/shelve_button/shelve_button.html +++ b/bookwyrm/templates/snippets/shelve_button/shelve_button.html @@ -3,7 +3,8 @@ {% load cache %} {% if request.user.is_authenticated %} -{% cache None shelve_button request.user.id book.id %} +{# 6 month cache #} +{% cache 15552000 shelve_button request.user.id book.id %} {% with book.id|uuid as uuid %} {% active_shelf book as active_shelf %} From e416ef05e8a0fd2c3c6cf803c75134bd24b97ee0 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 6 Jan 2022 11:07:22 -0800 Subject: [PATCH 22/37] Fixes cache of status interact The CSRF token was being cached which caused submits to fail --- bookwyrm/templates/snippets/status/layout.html | 3 --- .../snippets/status/status_options.html | 16 ++++++++++------ bookwyrm/templatetags/interaction.py | 14 ++++++++++++-- bookwyrm/views/interaction.py | 15 ++++----------- 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/bookwyrm/templates/snippets/status/layout.html b/bookwyrm/templates/snippets/status/layout.html index 174c379f1..5cbcef208 100644 --- a/bookwyrm/templates/snippets/status/layout.html +++ b/bookwyrm/templates/snippets/status/layout.html @@ -1,7 +1,6 @@ {% extends 'components/card.html' %} {% load i18n %} {% load utilities %} -{% load cache %} {% block card-header %} {% endif %} - {% endcache %} {% else %} diff --git a/bookwyrm/templates/snippets/status/status_options.html b/bookwyrm/templates/snippets/status/status_options.html index 854d4779e..fdf8ac148 100644 --- a/bookwyrm/templates/snippets/status/status_options.html +++ b/bookwyrm/templates/snippets/status/status_options.html @@ -20,17 +20,21 @@ {% if status.status_type != 'GeneratedNote' and status.status_type != 'Rating' %} {% endif %} {% else %} {# things you can do to other people's statuses #}