Merge branch 'main' into move

This commit is contained in:
Hugh Rundle 2023-09-26 08:10:18 +10:00 committed by GitHub
commit 088b9ab555
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 186 additions and 67 deletions

View file

@ -1,11 +1,20 @@
""" Use the range message from isbn-international to hyphenate ISBNs """ """ Use the range message from isbn-international to hyphenate ISBNs """
import os import os
from typing import Optional
from xml.etree import ElementTree from xml.etree import ElementTree
from xml.etree.ElementTree import Element
import requests import requests
from bookwyrm import settings from bookwyrm import settings
def _get_rules(element: Element) -> list[Element]:
if (rules_el := element.find("Rules")) is not None:
return rules_el.findall("Rule")
return []
class IsbnHyphenator: class IsbnHyphenator:
"""Class to manage the range message xml file and use it to hyphenate ISBNs""" """Class to manage the range message xml file and use it to hyphenate ISBNs"""
@ -15,58 +24,94 @@ class IsbnHyphenator:
) )
__element_tree = None __element_tree = None
def update_range_message(self): def update_range_message(self) -> None:
"""Download the range message xml file and save it locally""" """Download the range message xml file and save it locally"""
response = requests.get(self.__range_message_url) response = requests.get(self.__range_message_url)
with open(self.__range_file_path, "w", encoding="utf-8") as file: with open(self.__range_file_path, "w", encoding="utf-8") as file:
file.write(response.text) file.write(response.text)
self.__element_tree = None self.__element_tree = None
def hyphenate(self, isbn_13): def hyphenate(self, isbn_13: Optional[str]) -> Optional[str]:
"""hyphenate the given ISBN-13 number using the range message""" """hyphenate the given ISBN-13 number using the range message"""
if isbn_13 is None: if isbn_13 is None:
return None return None
if self.__element_tree is None: if self.__element_tree is None:
self.__element_tree = ElementTree.parse(self.__range_file_path) self.__element_tree = ElementTree.parse(self.__range_file_path)
gs1_prefix = isbn_13[:3] gs1_prefix = isbn_13[:3]
reg_group = self.__find_reg_group(isbn_13, gs1_prefix) reg_group = self.__find_reg_group(isbn_13, gs1_prefix)
if reg_group is None: if reg_group is None:
return isbn_13 # failed to hyphenate return isbn_13 # failed to hyphenate
registrant = self.__find_registrant(isbn_13, gs1_prefix, reg_group) registrant = self.__find_registrant(isbn_13, gs1_prefix, reg_group)
if registrant is None: if registrant is None:
return isbn_13 # failed to hyphenate return isbn_13 # failed to hyphenate
publication = isbn_13[len(gs1_prefix) + len(reg_group) + len(registrant) : -1] publication = isbn_13[len(gs1_prefix) + len(reg_group) + len(registrant) : -1]
check_digit = isbn_13[-1:] check_digit = isbn_13[-1:]
return "-".join((gs1_prefix, reg_group, registrant, publication, check_digit)) return "-".join((gs1_prefix, reg_group, registrant, publication, check_digit))
def __find_reg_group(self, isbn_13, gs1_prefix): def __find_reg_group(self, isbn_13: str, gs1_prefix: str) -> Optional[str]:
for ean_ucc_el in self.__element_tree.find("EAN.UCCPrefixes").findall( if self.__element_tree is None:
"EAN.UCC" self.__element_tree = ElementTree.parse(self.__range_file_path)
):
if ean_ucc_el.find("Prefix").text == gs1_prefix: ucc_prefixes_el = self.__element_tree.find("EAN.UCCPrefixes")
for rule_el in ean_ucc_el.find("Rules").findall("Rule"): if ucc_prefixes_el is None:
length = int(rule_el.find("Length").text) return None
for ean_ucc_el in ucc_prefixes_el.findall("EAN.UCC"):
if (
prefix_el := ean_ucc_el.find("Prefix")
) is not None and prefix_el.text == gs1_prefix:
for rule_el in _get_rules(ean_ucc_el):
length_el = rule_el.find("Length")
if length_el is None:
continue
length = int(text) if (text := length_el.text) else 0
if length == 0: if length == 0:
continue continue
reg_grp_range = [
int(x[:length]) for x in rule_el.find("Range").text.split("-") range_el = rule_el.find("Range")
] if range_el is None or range_el.text is None:
continue
reg_grp_range = [int(x[:length]) for x in range_el.text.split("-")]
reg_group = isbn_13[len(gs1_prefix) : len(gs1_prefix) + length] reg_group = isbn_13[len(gs1_prefix) : len(gs1_prefix) + length]
if reg_grp_range[0] <= int(reg_group) <= reg_grp_range[1]: if reg_grp_range[0] <= int(reg_group) <= reg_grp_range[1]:
return reg_group return reg_group
return None return None
return None return None
def __find_registrant(self, isbn_13, gs1_prefix, reg_group): def __find_registrant(
self, isbn_13: str, gs1_prefix: str, reg_group: str
) -> Optional[str]:
from_ind = len(gs1_prefix) + len(reg_group) from_ind = len(gs1_prefix) + len(reg_group)
for group_el in self.__element_tree.find("RegistrationGroups").findall("Group"):
if group_el.find("Prefix").text == "-".join((gs1_prefix, reg_group)): if self.__element_tree is None:
for rule_el in group_el.find("Rules").findall("Rule"): self.__element_tree = ElementTree.parse(self.__range_file_path)
length = int(rule_el.find("Length").text)
reg_groups_el = self.__element_tree.find("RegistrationGroups")
if reg_groups_el is None:
return None
for group_el in reg_groups_el.findall("Group"):
if (
prefix_el := group_el.find("Prefix")
) is not None and prefix_el.text == "-".join((gs1_prefix, reg_group)):
for rule_el in _get_rules(group_el):
length_el = rule_el.find("Length")
if length_el is None:
continue
length = int(text) if (text := length_el.text) else 0
if length == 0: if length == 0:
continue continue
range_el = rule_el.find("Range")
if range_el is None or range_el.text is None:
continue
registrant_range = [ registrant_range = [
int(x[:length]) for x in rule_el.find("Range").text.split("-") int(x[:length]) for x in range_el.text.split("-")
] ]
registrant = isbn_13[from_ind : from_ind + length] registrant = isbn_13[from_ind : from_ind + length]
if registrant_range[0] <= int(registrant) <= registrant_range[1]: if registrant_range[0] <= int(registrant) <= registrant_range[1]:

View file

@ -217,6 +217,13 @@ class Book(BookDataModel):
"""editions and works both use "book" instead of model_name""" """editions and works both use "book" instead of model_name"""
return f"https://{DOMAIN}/book/{self.id}" return f"https://{DOMAIN}/book/{self.id}"
def guess_sort_title(self):
"""Get a best-guess sort title for the current book"""
articles = chain(
*(LANGUAGE_ARTICLES.get(language, ()) for language in tuple(self.languages))
)
return re.sub(f'^{" |^".join(articles)} ', "", str(self.title).lower())
def __repr__(self): def __repr__(self):
# pylint: disable=consider-using-f-string # pylint: disable=consider-using-f-string
return "<{} key={!r} title={!r}>".format( return "<{} key={!r} title={!r}>".format(
@ -374,16 +381,7 @@ class Edition(Book):
# Create sort title by removing articles from title # Create sort title by removing articles from title
if self.sort_title in [None, ""]: if self.sort_title in [None, ""]:
if self.sort_title in [None, ""]: self.sort_title = self.guess_sort_title()
articles = chain(
*(
LANGUAGE_ARTICLES.get(language, ())
for language in tuple(self.languages)
)
)
self.sort_title = re.sub(
f'^{" |^".join(articles)} ', "", str(self.title).lower()
)
return super().save(*args, **kwargs) return super().save(*args, **kwargs)

View file

@ -1,5 +1,6 @@
""" models for storing different kinds of Activities """ """ models for storing different kinds of Activities """
from dataclasses import MISSING from dataclasses import MISSING
from typing import Optional
import re import re
from django.apps import apps from django.apps import apps
@ -269,7 +270,7 @@ class GeneratedNote(Status):
"""indicate the book in question for mastodon (or w/e) users""" """indicate the book in question for mastodon (or w/e) users"""
message = self.content message = self.content
books = ", ".join( books = ", ".join(
f'<a href="{book.remote_id}">"{book.title}"</a>' f'<a href="{book.remote_id}"><i>{book.title}</i></a>'
for book in self.mention_books.all() for book in self.mention_books.all()
) )
return f"{self.user.display_name} {message} {books}" return f"{self.user.display_name} {message} {books}"
@ -320,17 +321,14 @@ class Comment(BookStatus):
@property @property
def pure_content(self): def pure_content(self):
"""indicate the book in question for mastodon (or w/e) users""" """indicate the book in question for mastodon (or w/e) users"""
if self.progress_mode == "PG" and self.progress and (self.progress > 0): progress = self.progress or 0
return_value = ( citation = (
f'{self.content}<p>(comment on <a href="{self.book.remote_id}">' f'comment on <a href="{self.book.remote_id}">'
f'"{self.book.title}"</a>, page {self.progress})</p>' f"<i>{self.book.title}</i></a>"
) )
else: if self.progress_mode == "PG" and progress > 0:
return_value = ( citation += f", p. {progress}"
f'{self.content}<p>(comment on <a href="{self.book.remote_id}">' return f"{self.content}<p>({citation})</p>"
f'"{self.book.title}"</a>)</p>'
)
return return_value
activity_serializer = activitypub.Comment activity_serializer = activitypub.Comment
@ -354,22 +352,24 @@ class Quotation(BookStatus):
blank=True, blank=True,
) )
def _format_position(self) -> Optional[str]:
"""serialize page position"""
beg = self.position
end = self.endposition or 0
if self.position_mode != "PG" or not beg:
return None
return f"pp. {beg}-{end}" if end > beg else f"p. {beg}"
@property @property
def pure_content(self): def pure_content(self):
"""indicate the book in question for mastodon (or w/e) users""" """indicate the book in question for mastodon (or w/e) users"""
quote = re.sub(r"^<p>", '<p>"', self.quote) quote = re.sub(r"^<p>", '<p>"', self.quote)
quote = re.sub(r"</p>$", '"</p>', quote) quote = re.sub(r"</p>$", '"</p>', quote)
if self.position_mode == "PG" and self.position and (self.position > 0): title, href = self.book.title, self.book.remote_id
return_value = ( citation = f'— <a href="{href}"><i>{title}</i></a>'
f'{quote} <p>-- <a href="{self.book.remote_id}">' if position := self._format_position():
f'"{self.book.title}"</a>, page {self.position}</p>{self.content}' citation += f", {position}"
) return f"{quote} <p>{citation}</p>{self.content}"
else:
return_value = (
f'{quote} <p>-- <a href="{self.book.remote_id}">'
f'"{self.book.title}"</a></p>{self.content}'
)
return return_value
activity_serializer = activitypub.Quotation activity_serializer = activitypub.Quotation

View file

@ -1,5 +1,7 @@
""" bookwyrm settings and configuration """ """ bookwyrm settings and configuration """
import os import os
from typing import AnyStr
from environs import Env from environs import Env
import requests import requests
@ -12,7 +14,7 @@ from django.core.exceptions import ImproperlyConfigured
env = Env() env = Env()
env.read_env() env.read_env()
DOMAIN = env("DOMAIN") DOMAIN = env("DOMAIN")
VERSION = "0.6.4" VERSION = "0.6.5"
RELEASE_API = env( RELEASE_API = env(
"RELEASE_API", "RELEASE_API",
@ -37,7 +39,7 @@ EMAIL_SENDER_DOMAIN = env("EMAIL_SENDER_DOMAIN", DOMAIN)
EMAIL_SENDER = f"{EMAIL_SENDER_NAME}@{EMAIL_SENDER_DOMAIN}" EMAIL_SENDER = f"{EMAIL_SENDER_NAME}@{EMAIL_SENDER_DOMAIN}"
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) BASE_DIR: AnyStr = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
LOCALE_PATHS = [ LOCALE_PATHS = [
os.path.join(BASE_DIR, "locale"), os.path.join(BASE_DIR, "locale"),
] ]

View file

@ -106,7 +106,7 @@ const tries = {
e: { e: {
p: { p: {
u: { u: {
b: "ePub", b: "EPUB",
}, },
}, },
}, },

View file

@ -10,7 +10,7 @@
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="last_edited_by" value="{{ request.user.id }}"> <input type="hidden" name="last_edited_by" value="{{ request.user.id }}">
{% if form.parent_work %} {% if book.parent_work.id or form.parent_work %}
<input type="hidden" name="parent_work" value="{% firstof book.parent_work.id form.parent_work %}"> <input type="hidden" name="parent_work" value="{% firstof book.parent_work.id form.parent_work %}">
{% endif %} {% endif %}

View file

@ -5,7 +5,7 @@
{% block content %} {% block content %}
<div class="block"> <div class="block">
<h1 class="title">{% blocktrans with work_path=work.local_path work_title=work|book_title %}Editions of <a href="{{ work_path }}">"{{ work_title }}"</a>{% endblocktrans %}</h1> <h1 class="title">{% blocktrans with work_path=work.local_path work_title=work|book_title %}Editions of <a href="{{ work_path }}"><i>{{ work_title }}</i></a>{% endblocktrans %}</h1>
</div> </div>
{% include 'book/editions/edition_filters.html' %} {% include 'book/editions/edition_filters.html' %}

View file

@ -35,7 +35,7 @@
required="" required=""
id="id_filetype" id="id_filetype"
value="{% firstof file_link_form.filetype.value '' %}" value="{% firstof file_link_form.filetype.value '' %}"
placeholder="ePub" placeholder="EPUB"
list="mimetypes-list" list="mimetypes-list"
data-autocomplete="mimetype" data-autocomplete="mimetype"
> >

View file

@ -3,14 +3,13 @@
xmlns="http://a9.com/-/spec/opensearch/1.1/" xmlns="http://a9.com/-/spec/opensearch/1.1/"
xmlns:moz="http://www.mozilla.org/2006/browser/search/" xmlns:moz="http://www.mozilla.org/2006/browser/search/"
> >
<ShortName>{{ site_name }}</ShortName> <ShortName>{{ site.name }}</ShortName>
<Description>{% blocktrans trimmed with site_name=site.name %} <Description>{% blocktrans trimmed with site_name=site.name %}
{{ site_name }} search {{ site_name }} search
{% endblocktrans %}</Description> {% endblocktrans %}</Description>
<Image width="16" height="16" type="image/x-icon">{{ image }}</Image> <Image width="16" height="16" type="image/x-icon">{{ image }}</Image>
<Url <Url
type="text/html" type="text/html"
method="get"
template="https://{{ DOMAIN }}{% url 'search' %}?q={searchTerms}" template="https://{{ DOMAIN }}{% url 'search' %}?q={searchTerms}"
/> />
</OpenSearchDescription> </OpenSearchDescription>

View file

@ -212,7 +212,7 @@ class Status(TestCase):
def test_generated_note_to_pure_activity(self, *_): def test_generated_note_to_pure_activity(self, *_):
"""subclass of the base model version with a "pure" serializer""" """subclass of the base model version with a "pure" serializer"""
status = models.GeneratedNote.objects.create( status = models.GeneratedNote.objects.create(
content="test content", user=self.local_user content="reads", user=self.local_user
) )
status.mention_books.set([self.book]) status.mention_books.set([self.book])
status.mention_users.set([self.local_user]) status.mention_users.set([self.local_user])
@ -220,7 +220,7 @@ class Status(TestCase):
self.assertEqual(activity["id"], status.remote_id) self.assertEqual(activity["id"], status.remote_id)
self.assertEqual( self.assertEqual(
activity["content"], activity["content"],
f'mouse test content <a href="{self.book.remote_id}">"Test Edition"</a>', f'mouse reads <a href="{self.book.remote_id}"><i>Test Edition</i></a>',
) )
self.assertEqual(len(activity["tag"]), 2) self.assertEqual(len(activity["tag"]), 2)
self.assertEqual(activity["type"], "Note") self.assertEqual(activity["type"], "Note")
@ -249,14 +249,18 @@ class Status(TestCase):
def test_comment_to_pure_activity(self, *_): def test_comment_to_pure_activity(self, *_):
"""subclass of the base model version with a "pure" serializer""" """subclass of the base model version with a "pure" serializer"""
status = models.Comment.objects.create( status = models.Comment.objects.create(
content="test content", user=self.local_user, book=self.book content="test content", user=self.local_user, book=self.book, progress=27
) )
activity = status.to_activity(pure=True) activity = status.to_activity(pure=True)
self.assertEqual(activity["id"], status.remote_id) self.assertEqual(activity["id"], status.remote_id)
self.assertEqual(activity["type"], "Note") self.assertEqual(activity["type"], "Note")
self.assertEqual( self.assertEqual(
activity["content"], activity["content"],
f'test content<p>(comment on <a href="{self.book.remote_id}">"Test Edition"</a>)</p>', (
"test content"
f'<p>(comment on <a href="{self.book.remote_id}">'
"<i>Test Edition</i></a>, p. 27)</p>"
),
) )
self.assertEqual(activity["attachment"][0]["type"], "Document") self.assertEqual(activity["attachment"][0]["type"], "Document")
# self.assertTrue( # self.assertTrue(
@ -295,7 +299,11 @@ class Status(TestCase):
self.assertEqual(activity["type"], "Note") self.assertEqual(activity["type"], "Note")
self.assertEqual( self.assertEqual(
activity["content"], activity["content"],
f'a sickening sense <p>-- <a href="{self.book.remote_id}">"Test Edition"</a></p>test content', (
"a sickening sense "
f'<p>— <a href="{self.book.remote_id}">'
"<i>Test Edition</i></a></p>test content"
),
) )
self.assertEqual(activity["attachment"][0]["type"], "Document") self.assertEqual(activity["attachment"][0]["type"], "Document")
self.assertTrue( self.assertTrue(
@ -306,6 +314,29 @@ class Status(TestCase):
) )
self.assertEqual(activity["attachment"][0]["name"], "Test Edition") self.assertEqual(activity["attachment"][0]["name"], "Test Edition")
def test_quotation_page_serialization(self, *_):
"""serialization of quotation page position"""
tests = [
("single pos", 7, None, "p. 7"),
("page range", 7, 10, "pp. 7-10"),
]
for desc, beg, end, pages in tests:
with self.subTest(desc):
status = models.Quotation.objects.create(
quote="<p>my quote</p>",
content="",
user=self.local_user,
book=self.book,
position=beg,
endposition=end,
position_mode="PG",
)
activity = status.to_activity(pure=True)
self.assertRegex(
activity["content"],
f'^<p>"my quote"</p> <p>— <a .+</a>, {pages}</p>$',
)
def test_review_to_activity(self, *_): def test_review_to_activity(self, *_):
"""subclass of the base model version with a "pure" serializer""" """subclass of the base model version with a "pure" serializer"""
status = models.Review.objects.create( status = models.Review.objects.create(

View file

@ -0,0 +1,31 @@
""" test ISBN hyphenator for books """
from django.test import TestCase
from bookwyrm.isbn.isbn import hyphenator_singleton as hyphenator
class TestISBN(TestCase):
"""isbn hyphenator"""
def test_isbn_hyphenation(self):
"""different isbn hyphenations"""
# nothing
self.assertEqual(hyphenator.hyphenate(None), None)
# 978-0 (English language) 3700000-6389999
self.assertEqual(hyphenator.hyphenate("9780439554930"), "978-0-439-55493-0")
# 978-2 (French language) 0000000-1999999
self.assertEqual(hyphenator.hyphenate("9782070100927"), "978-2-07-010092-7")
# 978-3 (German language) 2000000-6999999
self.assertEqual(hyphenator.hyphenate("9783518188125"), "978-3-518-18812-5")
# 978-4 (Japan) 0000000-1999999
self.assertEqual(hyphenator.hyphenate("9784101050454"), "978-4-10-105045-4")
# 978-626 (Taiwan) 9500000-9999999
self.assertEqual(hyphenator.hyphenate("9786269533251"), "978-626-95332-5-1")
# 979-8 (United States) 4000000-8499999
self.assertEqual(hyphenator.hyphenate("9798627974040"), "979-8-6279-7404-0")
# 978-626 (Taiwan) 8000000-9499999 (unassigned)
self.assertEqual(hyphenator.hyphenate("9786268533251"), "9786268533251")
# 978 range 6600000-6999999 (unassigned)
self.assertEqual(hyphenator.hyphenate("9786769533251"), "9786769533251")
# 979-8 (United States) 2300000-3499999 (unassigned)
self.assertEqual(hyphenator.hyphenate("9798311111111"), "9798311111111")

View file

@ -1,6 +1,7 @@
""" url routing for the app and api """ """ url routing for the app and api """
from django.conf.urls.static import static from django.conf.urls.static import static
from django.contrib import admin from django.contrib import admin
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.urls import path, re_path from django.urls import path, re_path
from django.views.generic.base import TemplateView from django.views.generic.base import TemplateView
@ -780,5 +781,8 @@ urlpatterns = [
path("guided-tour/<tour>", views.toggle_guided_tour), path("guided-tour/<tour>", views.toggle_guided_tour),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
# Serves /static when DEBUG is true.
urlpatterns.extend(staticfiles_urlpatterns())
# pylint: disable=invalid-name # pylint: disable=invalid-name
handler500 = "bookwyrm.views.server_error" handler500 = "bookwyrm.views.server_error"

View file

@ -5,6 +5,7 @@ from django.shortcuts import get_object_or_404, redirect
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.views import View from django.views import View
from django.views.decorators.http import require_POST
from bookwyrm import forms, models from bookwyrm import forms, models
from bookwyrm.settings import PAGE_LENGTH from bookwyrm.settings import PAGE_LENGTH
@ -108,6 +109,7 @@ class EditAnnouncement(View):
@login_required @login_required
@permission_required("bookwyrm.edit_instance_settings", raise_exception=True) @permission_required("bookwyrm.edit_instance_settings", raise_exception=True)
@require_POST
def delete_announcement(_, announcement_id): def delete_announcement(_, announcement_id):
"""delete announcement""" """delete announcement"""
announcement = get_object_or_404(models.Announcement, id=announcement_id) announcement = get_object_or_404(models.Announcement, id=announcement_id)

View file

@ -32,6 +32,9 @@ class EditBook(View):
def get(self, request, book_id): def get(self, request, book_id):
"""info about a book""" """info about a book"""
book = get_edition(book_id) book = get_edition(book_id)
# This doesn't update the sort title, just pre-populates it in the form
if book.sort_title in ["", None]:
book.sort_title = book.guess_sort_title()
if not book.description: if not book.description:
book.description = book.parent_work.description book.description = book.parent_work.description
data = {"book": book, "form": forms.EditionForm(instance=book)} data = {"book": book, "form": forms.EditionForm(instance=book)}
@ -40,6 +43,7 @@ class EditBook(View):
def post(self, request, book_id): def post(self, request, book_id):
"""edit a book cool""" """edit a book cool"""
book = get_object_or_404(models.Edition, id=book_id) book = get_object_or_404(models.Edition, id=book_id)
form = forms.EditionForm(request.POST, request.FILES, instance=book) form = forms.EditionForm(request.POST, request.FILES, instance=book)
data = {"book": book, "form": form} data = {"book": book, "form": form}

View file

@ -2,7 +2,7 @@ version: '3'
services: services:
nginx: nginx:
image: nginx:latest image: nginx:1.25.2
restart: unless-stopped restart: unless-stopped
ports: ports:
- "1333:80" - "1333:80"
@ -38,7 +38,7 @@ services:
ports: ports:
- "8000" - "8000"
redis_activity: redis_activity:
image: redis image: redis:7.2.1
command: redis-server --requirepass ${REDIS_ACTIVITY_PASSWORD} --appendonly yes --port ${REDIS_ACTIVITY_PORT} command: redis-server --requirepass ${REDIS_ACTIVITY_PASSWORD} --appendonly yes --port ${REDIS_ACTIVITY_PORT}
volumes: volumes:
- ./redis.conf:/etc/redis/redis.conf - ./redis.conf:/etc/redis/redis.conf
@ -48,7 +48,7 @@ services:
- main - main
restart: on-failure restart: on-failure
redis_broker: redis_broker:
image: redis image: redis:7.2.1
command: redis-server --requirepass ${REDIS_BROKER_PASSWORD} --appendonly yes --port ${REDIS_BROKER_PORT} command: redis-server --requirepass ${REDIS_BROKER_PASSWORD} --appendonly yes --port ${REDIS_BROKER_PORT}
volumes: volumes:
- ./redis.conf:/etc/redis/redis.conf - ./redis.conf:/etc/redis/redis.conf

View file

@ -16,6 +16,9 @@ ignore_errors = False
[mypy-bookwyrm.importers.*] [mypy-bookwyrm.importers.*]
ignore_errors = False ignore_errors = False
[mypy-bookwyrm.isbn.*]
ignore_errors = False
[mypy-celerywyrm.*] [mypy-celerywyrm.*]
ignore_errors = False ignore_errors = False