From 207abed654dff732586f1a5e113119a71e8eb008 Mon Sep 17 00:00:00 2001 From: Robert George Date: Sat, 14 Jan 2023 14:52:54 -0800 Subject: [PATCH 01/10] Add support for specifying redis url to support unix sockets --- .env.example | 4 ++++ bookwyrm/management/commands/erase_streams.py | 7 +------ bookwyrm/redis_store.py | 7 +------ bookwyrm/settings.py | 4 ++-- bookwyrm/views/admin/celery_status.py | 7 +------ celerywyrm/settings.py | 5 +++-- 6 files changed, 12 insertions(+), 22 deletions(-) diff --git a/.env.example b/.env.example index dd7bec4f2..4c1c2eefe 100644 --- a/.env.example +++ b/.env.example @@ -32,6 +32,8 @@ REDIS_ACTIVITY_PORT=6379 REDIS_ACTIVITY_PASSWORD=redispassword345 # Optional, use a different redis database (defaults to 0) # REDIS_ACTIVITY_DB_INDEX=0 +# Alternatively specify the full redis url, i.e. if you need to use a unix:// socket +# REDIS_ACTIVITY_URL= # Redis as celery broker REDIS_BROKER_HOST=redis_broker @@ -39,6 +41,8 @@ REDIS_BROKER_PORT=6379 REDIS_BROKER_PASSWORD=redispassword123 # Optional, use a different redis database (defaults to 0) # REDIS_BROKER_DB_INDEX=0 +# Alternatively specify the full redis url, i.e. if you need to use a unix:// socket +# REDIS_BROKER_URL= # Monitoring for celery FLOWER_PORT=8888 diff --git a/bookwyrm/management/commands/erase_streams.py b/bookwyrm/management/commands/erase_streams.py index 9d971d699..ecd36006c 100644 --- a/bookwyrm/management/commands/erase_streams.py +++ b/bookwyrm/management/commands/erase_streams.py @@ -4,12 +4,7 @@ import redis from bookwyrm import settings -r = redis.Redis( - host=settings.REDIS_ACTIVITY_HOST, - port=settings.REDIS_ACTIVITY_PORT, - password=settings.REDIS_ACTIVITY_PASSWORD, - db=settings.REDIS_ACTIVITY_DB_INDEX, -) +r = redis.from_url(settings.REDIS_ACTIVITY_URL) def erase_streams(): diff --git a/bookwyrm/redis_store.py b/bookwyrm/redis_store.py index ae50db2ee..f25829f5c 100644 --- a/bookwyrm/redis_store.py +++ b/bookwyrm/redis_store.py @@ -4,12 +4,7 @@ import redis from bookwyrm import settings -r = redis.Redis( - host=settings.REDIS_ACTIVITY_HOST, - port=settings.REDIS_ACTIVITY_PORT, - password=settings.REDIS_ACTIVITY_PASSWORD, - db=settings.REDIS_ACTIVITY_DB_INDEX, -) +r = redis.from_url(settings.REDIS_ACTIVITY_URL) class RedisStore(ABC): diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index 74ef7d313..3090d27f5 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -207,7 +207,7 @@ REDIS_ACTIVITY_HOST = env("REDIS_ACTIVITY_HOST", "localhost") REDIS_ACTIVITY_PORT = env("REDIS_ACTIVITY_PORT", 6379) REDIS_ACTIVITY_PASSWORD = env("REDIS_ACTIVITY_PASSWORD", None) REDIS_ACTIVITY_DB_INDEX = env("REDIS_ACTIVITY_DB_INDEX", 0) - +REDIS_ACTIVITY_URL = env("REDIS_ACTIVITY_URL", f"redis://:{REDIS_ACTIVITY_PASSWORD}@{REDIS_ACTIVITY_HOST}:{REDIS_ACTIVITY_PORT}/{REDIS_ACTIVITY_DB_INDEX}") MAX_STREAM_LENGTH = int(env("MAX_STREAM_LENGTH", 200)) STREAMS = [ @@ -232,7 +232,7 @@ else: CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", - "LOCATION": f"redis://:{REDIS_ACTIVITY_PASSWORD}@{REDIS_ACTIVITY_HOST}:{REDIS_ACTIVITY_PORT}/{REDIS_ACTIVITY_DB_INDEX}", + "LOCATION": REDIS_ACTIVITY_URL, "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", }, diff --git a/bookwyrm/views/admin/celery_status.py b/bookwyrm/views/admin/celery_status.py index 7da148a06..5221fd619 100644 --- a/bookwyrm/views/admin/celery_status.py +++ b/bookwyrm/views/admin/celery_status.py @@ -8,12 +8,7 @@ import redis from celerywyrm import settings from bookwyrm.tasks import app as celery -r = redis.Redis( - host=settings.REDIS_BROKER_HOST, - port=settings.REDIS_BROKER_PORT, - password=settings.REDIS_BROKER_PASSWORD, - db=settings.REDIS_BROKER_DB_INDEX, -) +r = redis.from_url(settings.REDIS_BROKER_URL) # pylint: disable= no-self-use @method_decorator(login_required, name="dispatch") diff --git a/celerywyrm/settings.py b/celerywyrm/settings.py index 94e530221..3b6dc998a 100644 --- a/celerywyrm/settings.py +++ b/celerywyrm/settings.py @@ -8,9 +8,10 @@ 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) REDIS_BROKER_DB_INDEX = env("REDIS_BROKER_DB_INDEX", 0) +REDIS_BROKER_URL = env("REDIS_BROKER_URL",f"redis://:{REDIS_BROKER_PASSWORD}@{REDIS_BROKER_HOST}:{REDIS_BROKER_PORT}/{REDIS_BROKER_DB_INDEX}") -CELERY_BROKER_URL = f"redis://:{REDIS_BROKER_PASSWORD}@{REDIS_BROKER_HOST}:{REDIS_BROKER_PORT}/{REDIS_BROKER_DB_INDEX}" -CELERY_RESULT_BACKEND = f"redis://:{REDIS_BROKER_PASSWORD}@{REDIS_BROKER_HOST}:{REDIS_BROKER_PORT}/{REDIS_BROKER_DB_INDEX}" +CELERY_BROKER_URL = REDIS_BROKER_URL.replace("unix:","redis+socket:") +CELERY_RESULT_BACKEND = REDIS_BROKER_URL.replace("unix:","redis+socket:") CELERY_DEFAULT_QUEUE = "low_priority" CELERY_CREATE_MISSING_QUEUES = True From f362343dfaa404d9fab08f13da6bccaeb05f8ea0 Mon Sep 17 00:00:00 2001 From: Robert George Date: Sat, 14 Jan 2023 15:44:10 -0800 Subject: [PATCH 02/10] style fixes --- bookwyrm/settings.py | 5 ++++- celerywyrm/settings.py | 9 ++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index 3090d27f5..5967b010b 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -207,7 +207,10 @@ REDIS_ACTIVITY_HOST = env("REDIS_ACTIVITY_HOST", "localhost") REDIS_ACTIVITY_PORT = env("REDIS_ACTIVITY_PORT", 6379) REDIS_ACTIVITY_PASSWORD = env("REDIS_ACTIVITY_PASSWORD", None) REDIS_ACTIVITY_DB_INDEX = env("REDIS_ACTIVITY_DB_INDEX", 0) -REDIS_ACTIVITY_URL = env("REDIS_ACTIVITY_URL", f"redis://:{REDIS_ACTIVITY_PASSWORD}@{REDIS_ACTIVITY_HOST}:{REDIS_ACTIVITY_PORT}/{REDIS_ACTIVITY_DB_INDEX}") +REDIS_ACTIVITY_URL = env( + "REDIS_ACTIVITY_URL", + f"redis://:{REDIS_ACTIVITY_PASSWORD}@{REDIS_ACTIVITY_HOST}:{REDIS_ACTIVITY_PORT}/{REDIS_ACTIVITY_DB_INDEX}" +) MAX_STREAM_LENGTH = int(env("MAX_STREAM_LENGTH", 200)) STREAMS = [ diff --git a/celerywyrm/settings.py b/celerywyrm/settings.py index 3b6dc998a..146699f53 100644 --- a/celerywyrm/settings.py +++ b/celerywyrm/settings.py @@ -8,10 +8,13 @@ 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) REDIS_BROKER_DB_INDEX = env("REDIS_BROKER_DB_INDEX", 0) -REDIS_BROKER_URL = env("REDIS_BROKER_URL",f"redis://:{REDIS_BROKER_PASSWORD}@{REDIS_BROKER_HOST}:{REDIS_BROKER_PORT}/{REDIS_BROKER_DB_INDEX}") +REDIS_BROKER_URL = env( + "REDIS_BROKER_URL", + f"redis://:{REDIS_BROKER_PASSWORD}@{REDIS_BROKER_HOST}:{REDIS_BROKER_PORT}/{REDIS_BROKER_DB_INDEX}" +) -CELERY_BROKER_URL = REDIS_BROKER_URL.replace("unix:","redis+socket:") -CELERY_RESULT_BACKEND = REDIS_BROKER_URL.replace("unix:","redis+socket:") +CELERY_BROKER_URL = REDIS_BROKER_URL.replace("unix:", "redis+socket:") +CELERY_RESULT_BACKEND = REDIS_BROKER_URL.replace("unix:", "redis+socket:") CELERY_DEFAULT_QUEUE = "low_priority" CELERY_CREATE_MISSING_QUEUES = True From 95b66480c9d087881d279e87b0a6a900f96726fd Mon Sep 17 00:00:00 2001 From: Robert George Date: Sat, 14 Jan 2023 18:20:37 -0800 Subject: [PATCH 03/10] lint fixes --- bookwyrm/settings.py | 2 +- celerywyrm/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index 5967b010b..1a45c4c6d 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -209,7 +209,7 @@ REDIS_ACTIVITY_PASSWORD = env("REDIS_ACTIVITY_PASSWORD", None) REDIS_ACTIVITY_DB_INDEX = env("REDIS_ACTIVITY_DB_INDEX", 0) REDIS_ACTIVITY_URL = env( "REDIS_ACTIVITY_URL", - f"redis://:{REDIS_ACTIVITY_PASSWORD}@{REDIS_ACTIVITY_HOST}:{REDIS_ACTIVITY_PORT}/{REDIS_ACTIVITY_DB_INDEX}" + f"redis://:{REDIS_ACTIVITY_PASSWORD}@{REDIS_ACTIVITY_HOST}:{REDIS_ACTIVITY_PORT}/{REDIS_ACTIVITY_DB_INDEX}", ) MAX_STREAM_LENGTH = int(env("MAX_STREAM_LENGTH", 200)) diff --git a/celerywyrm/settings.py b/celerywyrm/settings.py index 146699f53..7519ea6e3 100644 --- a/celerywyrm/settings.py +++ b/celerywyrm/settings.py @@ -10,7 +10,7 @@ REDIS_BROKER_PORT = env("REDIS_BROKER_PORT", 6379) REDIS_BROKER_DB_INDEX = env("REDIS_BROKER_DB_INDEX", 0) REDIS_BROKER_URL = env( "REDIS_BROKER_URL", - f"redis://:{REDIS_BROKER_PASSWORD}@{REDIS_BROKER_HOST}:{REDIS_BROKER_PORT}/{REDIS_BROKER_DB_INDEX}" + f"redis://:{REDIS_BROKER_PASSWORD}@{REDIS_BROKER_HOST}:{REDIS_BROKER_PORT}/{REDIS_BROKER_DB_INDEX}", ) CELERY_BROKER_URL = REDIS_BROKER_URL.replace("unix:", "redis+socket:") From 858bf70d6241771e5225009d2238e8ab9822b04d Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Tue, 24 Jan 2023 08:46:29 -0800 Subject: [PATCH 04/10] Make follow activities a high priority This should go a long way towards fixing the problems with follows not going through to remote servers. All it does is move relationship related activities from the medium priority queue, which gets backlogged easily, to the high priority queue, which is less backlogged. The risk here is that the high priority queue could end up getting backlogged, so this isn't the last word on fixing this, but I think the volume of activities that this will add to it will be manageable. --- bookwyrm/views/inbox.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bookwyrm/views/inbox.py b/bookwyrm/views/inbox.py index eec35f9d1..e5372a3e4 100644 --- a/bookwyrm/views/inbox.py +++ b/bookwyrm/views/inbox.py @@ -14,7 +14,7 @@ from django.views import View from django.views.decorators.csrf import csrf_exempt from bookwyrm import activitypub, models -from bookwyrm.tasks import app, MEDIUM +from bookwyrm.tasks import app, MEDIUM, HIGH from bookwyrm.signatures import Signature from bookwyrm.utils import regex @@ -60,7 +60,11 @@ class Inbox(View): return HttpResponse() return HttpResponse(status=401) - activity_task.delay(activity_json) + # Make activities relating to follow/unfollow a high priority + high = ["Follow", "Accept", "Reject", "Block", "Unblock", "Undo"] + + priority = HIGH if activity_json["type"] in high else MEDIUM + activity_task.apply_async(args=(activity_json), queue=priority) return HttpResponse() From b89cab1ee589b2fc603d39accfe1485e77b132df Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 25 Jan 2023 07:39:43 -0800 Subject: [PATCH 05/10] Fixes args passed to inbox activity task --- bookwyrm/views/inbox.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/views/inbox.py b/bookwyrm/views/inbox.py index e5372a3e4..747b5eccd 100644 --- a/bookwyrm/views/inbox.py +++ b/bookwyrm/views/inbox.py @@ -64,7 +64,7 @@ class Inbox(View): high = ["Follow", "Accept", "Reject", "Block", "Unblock", "Undo"] priority = HIGH if activity_json["type"] in high else MEDIUM - activity_task.apply_async(args=(activity_json), queue=priority) + activity_task.apply_async(args=(activity_json,), queue=priority) return HttpResponse() From 9fdcc7debddccf36c6681e52cf150f12afe1d26c Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 25 Jan 2023 09:32:45 -0800 Subject: [PATCH 06/10] Fixes mocks in tests --- bookwyrm/tests/test_signing.py | 3 ++- bookwyrm/tests/views/inbox/test_inbox.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/bookwyrm/tests/test_signing.py b/bookwyrm/tests/test_signing.py index 26d5e99a9..961e45435 100644 --- a/bookwyrm/tests/test_signing.py +++ b/bookwyrm/tests/test_signing.py @@ -35,6 +35,7 @@ Sender = namedtuple("Sender", ("remote_id", "key_pair")) class Signature(TestCase): """signature test""" + # pylint: disable=invalid-name def setUp(self): """create users and test data""" with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( @@ -86,7 +87,7 @@ class Signature(TestCase): data = json.dumps(get_follow_activity(sender, self.rat)) digest = digest or make_digest(data) signature = make_signature(signer or sender, self.rat.inbox, now, digest) - with patch("bookwyrm.views.inbox.activity_task.delay"): + with patch("bookwyrm.views.inbox.activity_task.apply_async"): with patch("bookwyrm.models.user.set_remote_server.delay"): return self.send(signature, now, send_data or data, digest) diff --git a/bookwyrm/tests/views/inbox/test_inbox.py b/bookwyrm/tests/views/inbox/test_inbox.py index e328b1ba4..61acde5d3 100644 --- a/bookwyrm/tests/views/inbox/test_inbox.py +++ b/bookwyrm/tests/views/inbox/test_inbox.py @@ -15,6 +15,7 @@ from bookwyrm import models, views class Inbox(TestCase): """readthrough tests""" + # pylint: disable=invalid-name def setUp(self): """basic user and book data""" self.client = Client() @@ -119,7 +120,7 @@ class Inbox(TestCase): with patch("bookwyrm.views.inbox.has_valid_signature") as mock_valid: mock_valid.return_value = True - with patch("bookwyrm.views.inbox.activity_task.delay"): + with patch("bookwyrm.views.inbox.activity_task.apply_async"): result = self.client.post( "/inbox", json.dumps(activity), content_type="application/json" ) From aa6eaccfbbd8641379b5c5c2d3b3aacaa2841c53 Mon Sep 17 00:00:00 2001 From: Hugh Rundle Date: Thu, 26 Jan 2023 16:22:50 +1100 Subject: [PATCH 07/10] use mocks for isni API calls - fixes a couple of tests making author ISNI calls - notes a future TODO to remove some possibly useless code --- bookwyrm/tests/views/books/test_edit_book.py | 40 ++++++++++++++++++++ bookwyrm/utils/isni.py | 2 + 2 files changed, 42 insertions(+) diff --git a/bookwyrm/tests/views/books/test_edit_book.py b/bookwyrm/tests/views/books/test_edit_book.py index 9e3f84a1b..2c3254df0 100644 --- a/bookwyrm/tests/views/books/test_edit_book.py +++ b/bookwyrm/tests/views/books/test_edit_book.py @@ -1,6 +1,7 @@ """ test for app action functionality """ from unittest.mock import patch import responses +from responses import matchers from django.contrib.auth.models import Group, Permission from django.contrib.contenttypes.models import ContentType @@ -46,6 +47,43 @@ class EditBookViews(TestCase): parent_work=self.work, ) + self.authors_body = "1.10000000084510024" + + self.author_body = "0000000084510024https://isni.org/isni/000000008451002460Catherine Amy Dawson Scottpoet and novelistpublicVIAFWKPQ544961C. A.Dawson Scott1865-1934publicVIAFNLPa28927850VIAF45886165ALLCREhttp://viaf.org/viaf/45886165Wikipediahttps://en.wikipedia.org/wiki/Catherine_Amy_Dawson_Scott" + + responses.get( + "http://isni.oclc.org/sru/", + content_type="text/xml", + match=[ + matchers.query_param_matcher( + {"query": 'pica.na="Sappho"'}, strict_match=False + ) + ], + body=self.authors_body, + ) + + responses.get( + "http://isni.oclc.org/sru/", + content_type="text/xml", + match=[ + matchers.query_param_matcher( + {"query": 'pica.na="Some Guy"'}, strict_match=False + ) + ], + body=self.authors_body, + ) + + responses.get( + "http://isni.oclc.org/sru/", + content_type="text/xml", + match=[ + matchers.query_param_matcher( + {"query": 'pica.isn="0000000084510024"'}, strict_match=False + ) + ], + body=self.author_body, + ) + models.SiteSettings.objects.create() def test_edit_book_get(self): @@ -97,6 +135,7 @@ class EditBookViews(TestCase): result.context_data["cover_url"], "http://local.host/cover.jpg" ) + @responses.activate def test_edit_book_add_author(self): """lets a user edit a book with new authors""" view = views.EditBook.as_view() @@ -227,6 +266,7 @@ class EditBookViews(TestCase): self.book.refresh_from_db() self.assertTrue(self.book.cover) + @responses.activate def test_add_authors_helper(self): """converts form input into author matches""" form = forms.EditionForm(instance=self.book) diff --git a/bookwyrm/utils/isni.py b/bookwyrm/utils/isni.py index ea0364e55..a6b0060fc 100644 --- a/bookwyrm/utils/isni.py +++ b/bookwyrm/utils/isni.py @@ -85,6 +85,8 @@ def find_authors_by_name(name_string, description=False): # build list of possible authors possible_authors = [] for element in root.iter("responseRecord"): + + # TODO: we don't seem to do anything with the personal_name variable - is this code block needed? personal_name = element.find(".//forename/..") if not personal_name: continue From 1fe6892d0a56c869e0a2ca5811647e0e387ff25c Mon Sep 17 00:00:00 2001 From: Hugh Rundle Date: Thu, 26 Jan 2023 16:54:14 +1100 Subject: [PATCH 08/10] pylint --- bookwyrm/tests/views/books/test_edit_book.py | 3 ++- bookwyrm/utils/isni.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/bookwyrm/tests/views/books/test_edit_book.py b/bookwyrm/tests/views/books/test_edit_book.py index 2c3254df0..2dc25095f 100644 --- a/bookwyrm/tests/views/books/test_edit_book.py +++ b/bookwyrm/tests/views/books/test_edit_book.py @@ -46,9 +46,10 @@ class EditBookViews(TestCase): remote_id="https://example.com/book/1", parent_work=self.work, ) - + # pylint: disable=line-too-long self.authors_body = "1.10000000084510024" + # pylint: disable=line-too-long self.author_body = "0000000084510024https://isni.org/isni/000000008451002460Catherine Amy Dawson Scottpoet and novelistpublicVIAFWKPQ544961C. A.Dawson Scott1865-1934publicVIAFNLPa28927850VIAF45886165ALLCREhttp://viaf.org/viaf/45886165Wikipediahttps://en.wikipedia.org/wiki/Catherine_Amy_Dawson_Scott" responses.get( diff --git a/bookwyrm/utils/isni.py b/bookwyrm/utils/isni.py index a6b0060fc..bcff3810f 100644 --- a/bookwyrm/utils/isni.py +++ b/bookwyrm/utils/isni.py @@ -86,7 +86,8 @@ def find_authors_by_name(name_string, description=False): possible_authors = [] for element in root.iter("responseRecord"): - # TODO: we don't seem to do anything with the personal_name variable - is this code block needed? + # TODO: we don't seem to do anything with the + # personal_name variable - is this code block needed? personal_name = element.find(".//forename/..") if not personal_name: continue From c86fdfbd40f6bc2b3913abb4e6ce3377ba3d027d Mon Sep 17 00:00:00 2001 From: Hugh Rundle Date: Thu, 26 Jan 2023 16:56:11 +1100 Subject: [PATCH 09/10] black --- bookwyrm/utils/isni.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/utils/isni.py b/bookwyrm/utils/isni.py index bcff3810f..318de30ef 100644 --- a/bookwyrm/utils/isni.py +++ b/bookwyrm/utils/isni.py @@ -86,7 +86,7 @@ def find_authors_by_name(name_string, description=False): possible_authors = [] for element in root.iter("responseRecord"): - # TODO: we don't seem to do anything with the + # TODO: we don't seem to do anything with the # personal_name variable - is this code block needed? personal_name = element.find(".//forename/..") if not personal_name: From 9c3c34834f723bf9cc9d1a193056cc76d10af595 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 26 Jan 2023 07:39:25 -0800 Subject: [PATCH 10/10] Update version number --- bookwyrm/settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index 1a45c4c6d..99aae9694 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -11,7 +11,7 @@ from django.utils.translation import gettext_lazy as _ env = Env() env.read_env() DOMAIN = env("DOMAIN") -VERSION = "0.5.3" +VERSION = "0.5.4" RELEASE_API = env( "RELEASE_API", @@ -21,7 +21,7 @@ RELEASE_API = env( PAGE_LENGTH = env("PAGE_LENGTH", 15) DEFAULT_LANGUAGE = env("DEFAULT_LANGUAGE", "English") -JS_CACHE = "ad848b97" +JS_CACHE = "cd848b9a" # email EMAIL_BACKEND = env("EMAIL_BACKEND", "django.core.mail.backends.smtp.EmailBackend")