From d9f6449767bfc1f9fea9e8fc353d856bdd5d8ab8 Mon Sep 17 00:00:00 2001
From: Mouse Reeve
Date: Sun, 6 Aug 2023 17:52:23 -0700
Subject: [PATCH 01/16] Pre-populate sort title in edit book form if not
provided
It's confusing to edit a book when this isn't set, so this provides the
best-guess version of the sort title if there isn't one provided, and
allows the user to change it as needed.
---
bookwyrm/models/book.py | 17 ++++++++---------
bookwyrm/views/books/edit_book.py | 4 ++++
2 files changed, 12 insertions(+), 9 deletions(-)
diff --git a/bookwyrm/models/book.py b/bookwyrm/models/book.py
index 8cb47e5c8..a53321b26 100644
--- a/bookwyrm/models/book.py
+++ b/bookwyrm/models/book.py
@@ -217,6 +217,13 @@ class Book(BookDataModel):
"""editions and works both use "book" instead of model_name"""
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):
# pylint: disable=consider-using-f-string
return "<{} key={!r} title={!r}>".format(
@@ -375,15 +382,7 @@ class Edition(Book):
# Create sort title by removing articles from title
if self.sort_title in [None, ""]:
if self.sort_title in [None, ""]:
- articles = chain(
- *(
- LANGUAGE_ARTICLES.get(language, ())
- for language in tuple(self.languages)
- )
- )
- self.sort_title = re.sub(
- f'^{" |^".join(articles)} ', "", str(self.title).lower()
- )
+ self.sort_title = self.guess_sort_title()
return super().save(*args, **kwargs)
diff --git a/bookwyrm/views/books/edit_book.py b/bookwyrm/views/books/edit_book.py
index 2a7f36dbb..ae492374f 100644
--- a/bookwyrm/views/books/edit_book.py
+++ b/bookwyrm/views/books/edit_book.py
@@ -32,6 +32,9 @@ class EditBook(View):
def get(self, request, book_id):
"""info about a book"""
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:
book.description = book.parent_work.description
data = {"book": book, "form": forms.EditionForm(instance=book)}
@@ -40,6 +43,7 @@ class EditBook(View):
def post(self, request, book_id):
"""edit a book cool"""
book = get_object_or_404(models.Edition, id=book_id)
+
form = forms.EditionForm(request.POST, request.FILES, instance=book)
data = {"book": book, "form": form}
From 1e0fe6d7c8b7fb58a02dec6b7f5070f8fd3b28e9 Mon Sep 17 00:00:00 2001
From: Mouse Reeve
Date: Sat, 19 Aug 2023 15:06:57 -0700
Subject: [PATCH 02/16] Remove duplicate if statement
---
bookwyrm/models/book.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/bookwyrm/models/book.py b/bookwyrm/models/book.py
index a53321b26..d0c3c7fd3 100644
--- a/bookwyrm/models/book.py
+++ b/bookwyrm/models/book.py
@@ -381,8 +381,7 @@ class Edition(Book):
# Create sort title by removing articles from title
if self.sort_title in [None, ""]:
- if self.sort_title in [None, ""]:
- self.sort_title = self.guess_sort_title()
+ self.sort_title = self.guess_sort_title()
return super().save(*args, **kwargs)
From 3760e3b45c0d1d81f07a501025f3d769f995b36a Mon Sep 17 00:00:00 2001
From: Joeri de Ruiter
Date: Mon, 21 Aug 2023 15:46:24 +0200
Subject: [PATCH 03/16] Tests for ISBN hyphenation
---
bookwyrm/tests/test_isbn.py | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
create mode 100644 bookwyrm/tests/test_isbn.py
diff --git a/bookwyrm/tests/test_isbn.py b/bookwyrm/tests/test_isbn.py
new file mode 100644
index 000000000..b528e9210
--- /dev/null
+++ b/bookwyrm/tests/test_isbn.py
@@ -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")
From f6d87861792e67bbc4819adeaf40f024e8502354 Mon Sep 17 00:00:00 2001
From: Joeri de Ruiter
Date: Mon, 21 Aug 2023 15:46:50 +0200
Subject: [PATCH 04/16] Type annotations for bookwyrm.isbn
---
bookwyrm/isbn/isbn.py | 81 +++++++++++++++++++++++++++++++++----------
bookwyrm/settings.py | 4 ++-
mypy.ini | 3 ++
3 files changed, 69 insertions(+), 19 deletions(-)
diff --git a/bookwyrm/isbn/isbn.py b/bookwyrm/isbn/isbn.py
index e07d2100d..4cc7f47dd 100644
--- a/bookwyrm/isbn/isbn.py
+++ b/bookwyrm/isbn/isbn.py
@@ -1,11 +1,20 @@
""" Use the range message from isbn-international to hyphenate ISBNs """
import os
+from typing import Optional
from xml.etree import ElementTree
+from xml.etree.ElementTree import Element
+
import requests
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 to manage the range message xml file and use it to hyphenate ISBNs"""
@@ -15,58 +24,94 @@ class IsbnHyphenator:
)
__element_tree = None
- def update_range_message(self):
+ def update_range_message(self) -> None:
"""Download the range message xml file and save it locally"""
response = requests.get(self.__range_message_url)
with open(self.__range_file_path, "w", encoding="utf-8") as file:
file.write(response.text)
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"""
if isbn_13 is None:
return None
+
if self.__element_tree is None:
self.__element_tree = ElementTree.parse(self.__range_file_path)
+
gs1_prefix = isbn_13[:3]
reg_group = self.__find_reg_group(isbn_13, gs1_prefix)
if reg_group is None:
return isbn_13 # failed to hyphenate
+
registrant = self.__find_registrant(isbn_13, gs1_prefix, reg_group)
if registrant is None:
return isbn_13 # failed to hyphenate
+
publication = isbn_13[len(gs1_prefix) + len(reg_group) + len(registrant) : -1]
check_digit = isbn_13[-1:]
return "-".join((gs1_prefix, reg_group, registrant, publication, check_digit))
- def __find_reg_group(self, isbn_13, gs1_prefix):
- for ean_ucc_el in self.__element_tree.find("EAN.UCCPrefixes").findall(
- "EAN.UCC"
- ):
- if ean_ucc_el.find("Prefix").text == gs1_prefix:
- for rule_el in ean_ucc_el.find("Rules").findall("Rule"):
- length = int(rule_el.find("Length").text)
+ def __find_reg_group(self, isbn_13: str, gs1_prefix: str) -> Optional[str]:
+ if self.__element_tree is None:
+ self.__element_tree = ElementTree.parse(self.__range_file_path)
+
+ ucc_prefixes_el = self.__element_tree.find("EAN.UCCPrefixes")
+ if ucc_prefixes_el is None:
+ 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:
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]
if reg_grp_range[0] <= int(reg_group) <= reg_grp_range[1]:
return reg_group
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)
- for group_el in self.__element_tree.find("RegistrationGroups").findall("Group"):
- if group_el.find("Prefix").text == "-".join((gs1_prefix, reg_group)):
- for rule_el in group_el.find("Rules").findall("Rule"):
- length = int(rule_el.find("Length").text)
+
+ if self.__element_tree is None:
+ self.__element_tree = ElementTree.parse(self.__range_file_path)
+
+ 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:
continue
+
+ range_el = rule_el.find("Range")
+ if range_el is None or range_el.text is None:
+ continue
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]
if registrant_range[0] <= int(registrant) <= registrant_range[1]:
diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py
index 829ddaef7..751ab5687 100644
--- a/bookwyrm/settings.py
+++ b/bookwyrm/settings.py
@@ -1,5 +1,7 @@
""" bookwyrm settings and configuration """
import os
+from typing import AnyStr
+
from environs import Env
import requests
@@ -37,7 +39,7 @@ EMAIL_SENDER_DOMAIN = env("EMAIL_SENDER_DOMAIN", 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__)))
+BASE_DIR: AnyStr = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
LOCALE_PATHS = [
os.path.join(BASE_DIR, "locale"),
]
diff --git a/mypy.ini b/mypy.ini
index 2a29e314f..27891f501 100644
--- a/mypy.ini
+++ b/mypy.ini
@@ -13,6 +13,9 @@ implicit_reexport = True
[mypy-bookwyrm.connectors.*]
ignore_errors = False
+[mypy-bookwyrm.isbn.*]
+ignore_errors = False
+
[mypy-celerywyrm.*]
ignore_errors = False
From 1c9da7b84b31b7e2403242fee9332a6be31bbee4 Mon Sep 17 00:00:00 2001
From: 0x29a
Date: Fri, 25 Aug 2023 14:11:29 +0200
Subject: [PATCH 05/16] chore: bump version to match the latest tag
---
bookwyrm/settings.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py
index 829ddaef7..5c562ba26 100644
--- a/bookwyrm/settings.py
+++ b/bookwyrm/settings.py
@@ -12,7 +12,7 @@ from django.core.exceptions import ImproperlyConfigured
env = Env()
env.read_env()
DOMAIN = env("DOMAIN")
-VERSION = "0.6.4"
+VERSION = "0.6.5"
RELEASE_API = env(
"RELEASE_API",
From d560a6baeff5e61cd11d19588a72cd180f147c9b Mon Sep 17 00:00:00 2001
From: Hugh Rundle
Date: Wed, 30 Aug 2023 20:15:20 +1000
Subject: [PATCH 06/16] fix opensearch template
* "method" is not a valid attribute of the `Url` element
* "ShortName" cannot be empty - fixed site_name being used before it was assigned
---
bookwyrm/templates/opensearch.xml | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/bookwyrm/templates/opensearch.xml b/bookwyrm/templates/opensearch.xml
index 3d5f124b3..fd5c8f231 100644
--- a/bookwyrm/templates/opensearch.xml
+++ b/bookwyrm/templates/opensearch.xml
@@ -3,14 +3,13 @@
xmlns="http://a9.com/-/spec/opensearch/1.1/"
xmlns:moz="http://www.mozilla.org/2006/browser/search/"
>
- {{ site_name }}
+ {{ site.name }}
{% blocktrans trimmed with site_name=site.name %}
{{ site_name }} search
{% endblocktrans %}
{{ image }}
From b0601a0958d48d098e9ee07823d6b797cde44fe0 Mon Sep 17 00:00:00 2001
From: Mouse Reeve
Date: Fri, 1 Sep 2023 16:59:56 -0700
Subject: [PATCH 07/16] Makes deleting announcements only work via POST
---
bookwyrm/views/admin/announcements.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/bookwyrm/views/admin/announcements.py b/bookwyrm/views/admin/announcements.py
index 0b5ce9fa4..c5a7c80ff 100644
--- a/bookwyrm/views/admin/announcements.py
+++ b/bookwyrm/views/admin/announcements.py
@@ -5,6 +5,7 @@ from django.shortcuts import get_object_or_404, redirect
from django.template.response import TemplateResponse
from django.utils.decorators import method_decorator
from django.views import View
+from django.views.decorators.http import require_POST
from bookwyrm import forms, models
from bookwyrm.settings import PAGE_LENGTH
@@ -108,6 +109,7 @@ class EditAnnouncement(View):
@login_required
@permission_required("bookwyrm.edit_instance_settings", raise_exception=True)
+@require_POST
def delete_announcement(_, announcement_id):
"""delete announcement"""
announcement = get_object_or_404(models.Announcement, id=announcement_id)
From 2260e14868c16d45c36324ae75a71e0bd3b1498d Mon Sep 17 00:00:00 2001
From: JJimenez71
Date: Thu, 7 Sep 2023 19:30:29 -0600
Subject: [PATCH 08/16] Pinned versions of docker containers
---
docker-compose.yml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docker-compose.yml b/docker-compose.yml
index 800690206..c327c10a9 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -2,7 +2,7 @@ version: '3'
services:
nginx:
- image: nginx:latest
+ image: nginx:1.25.2
restart: unless-stopped
ports:
- "1333:80"
@@ -38,7 +38,7 @@ services:
ports:
- "8000"
redis_activity:
- image: redis
+ image: redis:7.2.1
command: redis-server --requirepass ${REDIS_ACTIVITY_PASSWORD} --appendonly yes --port ${REDIS_ACTIVITY_PORT}
volumes:
- ./redis.conf:/etc/redis/redis.conf
@@ -48,7 +48,7 @@ services:
- main
restart: on-failure
redis_broker:
- image: redis
+ image: redis:7.2.1
command: redis-server --requirepass ${REDIS_BROKER_PASSWORD} --appendonly yes --port ${REDIS_BROKER_PORT}
volumes:
- ./redis.conf:/etc/redis/redis.conf
From d8ba1f430928d57a13c0936fd8939387771af90b Mon Sep 17 00:00:00 2001
From: FoW
Date: Fri, 8 Sep 2023 22:52:11 +0900
Subject: [PATCH 09/16] Correct EPUB spelling
---
bookwyrm/static/js/autocomplete.js | 2 +-
bookwyrm/templates/book/file_links/add_link_modal.html | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/bookwyrm/static/js/autocomplete.js b/bookwyrm/static/js/autocomplete.js
index 84474e43c..a98cd9634 100644
--- a/bookwyrm/static/js/autocomplete.js
+++ b/bookwyrm/static/js/autocomplete.js
@@ -106,7 +106,7 @@ const tries = {
e: {
p: {
u: {
- b: "ePub",
+ b: "EPUB",
},
},
},
diff --git a/bookwyrm/templates/book/file_links/add_link_modal.html b/bookwyrm/templates/book/file_links/add_link_modal.html
index 67b437bd7..8ed4389ff 100644
--- a/bookwyrm/templates/book/file_links/add_link_modal.html
+++ b/bookwyrm/templates/book/file_links/add_link_modal.html
@@ -35,7 +35,7 @@
required=""
id="id_filetype"
value="{% firstof file_link_form.filetype.value '' %}"
- placeholder="ePub"
+ placeholder="EPUB"
list="mimetypes-list"
data-autocomplete="mimetype"
>
From 05f8bd0d3c500e91585253ad9e5c393a11aba58b Mon Sep 17 00:00:00 2001
From: Joeri de Ruiter
Date: Wed, 13 Sep 2023 09:46:31 +0200
Subject: [PATCH 10/16] parent_work was not always included in work when needed
---
bookwyrm/templates/book/edit/edit_book_form.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/bookwyrm/templates/book/edit/edit_book_form.html b/bookwyrm/templates/book/edit/edit_book_form.html
index 23cc6d097..4cc3965e7 100644
--- a/bookwyrm/templates/book/edit/edit_book_form.html
+++ b/bookwyrm/templates/book/edit/edit_book_form.html
@@ -10,7 +10,7 @@
{% csrf_token %}
-{% if form.parent_work %}
+{% if book.parent_work.id or form.parent_work %}
{% endif %}
From 25fd7276ea23d69a707414eb874363601cdcc433 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adeodato=20Sim=C3=B3?=
Date: Sun, 17 Sep 2023 10:46:11 -0300
Subject: [PATCH 11/16] `pure_content()` refactor: shorter conditionals
---
bookwyrm/models/status.py | 32 +++++++++++---------------------
1 file changed, 11 insertions(+), 21 deletions(-)
diff --git a/bookwyrm/models/status.py b/bookwyrm/models/status.py
index e51f2ba07..8c98028a0 100644
--- a/bookwyrm/models/status.py
+++ b/bookwyrm/models/status.py
@@ -320,17 +320,14 @@ class Comment(BookStatus):
@property
def pure_content(self):
"""indicate the book in question for mastodon (or w/e) users"""
- if self.progress_mode == "PG" and self.progress and (self.progress > 0):
- return_value = (
- f'{self.content}(comment on '
- f'"{self.book.title}" , page {self.progress})
'
- )
- else:
- return_value = (
- f'{self.content}(comment on '
- f'"{self.book.title}" )
'
- )
- return return_value
+ progress = self.progress or 0
+ citation = (
+ f'comment on '
+ f'"{self.book.title}" '
+ )
+ if self.progress_mode == "PG" and progress > 0:
+ citation += f", page {progress}"
+ return f"{self.content}({citation})
"
activity_serializer = activitypub.Comment
@@ -359,17 +356,10 @@ class Quotation(BookStatus):
"""indicate the book in question for mastodon (or w/e) users"""
quote = re.sub(r"^", '
"', self.quote)
quote = re.sub(r"
$", '"
', quote)
+ citation = f'-- "{self.book.title}" '
if self.position_mode == "PG" and self.position and (self.position > 0):
- return_value = (
- f'{quote} -- '
- f'"{self.book.title}" , page {self.position}
{self.content}'
- )
- else:
- return_value = (
- f'{quote} -- '
- f'"{self.book.title}"
{self.content}'
- )
- return return_value
+ citation += f", page {self.position}"
+ return f"{quote} {citation}
{self.content}"
activity_serializer = activitypub.Quotation
From 1322a0c6939f364a023aa0df1c01e9685d4c31f3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adeodato=20Sim=C3=B3?=
Date: Sun, 17 Sep 2023 15:05:34 -0300
Subject: [PATCH 12/16] =?UTF-8?q?Substitute=20=E2=80=9Cp.=E2=80=9D=20for?=
=?UTF-8?q?=20=E2=80=9Cpage=E2=80=9D=20in=20page=20progress=20serializatio?=
=?UTF-8?q?n?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
bookwyrm/models/status.py | 4 ++--
bookwyrm/tests/models/test_status_model.py | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/bookwyrm/models/status.py b/bookwyrm/models/status.py
index 8c98028a0..1040ace8f 100644
--- a/bookwyrm/models/status.py
+++ b/bookwyrm/models/status.py
@@ -326,7 +326,7 @@ class Comment(BookStatus):
f'"{self.book.title}"'
)
if self.progress_mode == "PG" and progress > 0:
- citation += f", page {progress}"
+ citation += f", p. {progress}"
return f"{self.content}({citation})
"
activity_serializer = activitypub.Comment
@@ -358,7 +358,7 @@ class Quotation(BookStatus):
quote = re.sub(r"$", '"', quote)
citation = f'-- "{self.book.title}" '
if self.position_mode == "PG" and self.position and (self.position > 0):
- citation += f", page {self.position}"
+ citation += f", p. {self.position}"
return f"{quote} {citation}
{self.content}"
activity_serializer = activitypub.Quotation
diff --git a/bookwyrm/tests/models/test_status_model.py b/bookwyrm/tests/models/test_status_model.py
index 72aa0ca6c..d41b80575 100644
--- a/bookwyrm/tests/models/test_status_model.py
+++ b/bookwyrm/tests/models/test_status_model.py
@@ -249,14 +249,14 @@ class Status(TestCase):
def test_comment_to_pure_activity(self, *_):
"""subclass of the base model version with a "pure" serializer"""
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)
self.assertEqual(activity["id"], status.remote_id)
self.assertEqual(activity["type"], "Note")
self.assertEqual(
activity["content"],
- f'test content(comment on "Test Edition" )
',
+ f'test content(comment on "Test Edition" , p. 27)
',
)
self.assertEqual(activity["attachment"][0]["type"], "Document")
# self.assertTrue(
From ce3885d4f6bed6761cac6761682a8982a4170188 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adeodato=20Sim=C3=B3?=
Date: Sun, 17 Sep 2023 01:40:23 -0300
Subject: [PATCH 13/16] Use `endposition` when serializing Quotation
---
bookwyrm/models/status.py | 13 ++++++++++--
bookwyrm/tests/models/test_status_model.py | 23 ++++++++++++++++++++++
2 files changed, 34 insertions(+), 2 deletions(-)
diff --git a/bookwyrm/models/status.py b/bookwyrm/models/status.py
index 1040ace8f..d51de5278 100644
--- a/bookwyrm/models/status.py
+++ b/bookwyrm/models/status.py
@@ -1,5 +1,6 @@
""" models for storing different kinds of Activities """
from dataclasses import MISSING
+from typing import Optional
import re
from django.apps import apps
@@ -351,14 +352,22 @@ class Quotation(BookStatus):
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
def pure_content(self):
"""indicate the book in question for mastodon (or w/e) users"""
quote = re.sub(r"^", '
"', self.quote)
quote = re.sub(r"
$", '"', quote)
citation = f'-- "{self.book.title}" '
- if self.position_mode == "PG" and self.position and (self.position > 0):
- citation += f", p. {self.position}"
+ if position := self._format_position():
+ citation += f", {position}"
return f"{quote} {citation}
{self.content}"
activity_serializer = activitypub.Quotation
diff --git a/bookwyrm/tests/models/test_status_model.py b/bookwyrm/tests/models/test_status_model.py
index d41b80575..15d73de9c 100644
--- a/bookwyrm/tests/models/test_status_model.py
+++ b/bookwyrm/tests/models/test_status_model.py
@@ -306,6 +306,29 @@ class Status(TestCase):
)
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="my quote
",
+ 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'^"my quote"
-- , {pages}
$',
+ )
+
def test_review_to_activity(self, *_):
"""subclass of the base model version with a "pure" serializer"""
status = models.Review.objects.create(
From 1e495684af34a9377373e19a86c4dbaccfd3be2e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adeodato=20Sim=C3=B3?=
Date: Mon, 18 Sep 2023 18:57:52 -0300
Subject: [PATCH 14/16] Serve static files in debug mode
---
bookwyrm/urls.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py
index 0ebd7925c..05972ee73 100644
--- a/bookwyrm/urls.py
+++ b/bookwyrm/urls.py
@@ -1,6 +1,7 @@
""" url routing for the app and api """
from django.conf.urls.static import static
from django.contrib import admin
+from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.urls import path, re_path
from django.views.generic.base import TemplateView
@@ -774,5 +775,8 @@ urlpatterns = [
path("guided-tour/", views.toggle_guided_tour),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
+# Serves /static when DEBUG is true.
+urlpatterns.extend(staticfiles_urlpatterns())
+
# pylint: disable=invalid-name
handler500 = "bookwyrm.views.server_error"
From cc05cabcb57d340f87a3ae44856fe6574bec7eb7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adeodato=20Sim=C3=B3?=
Date: Sun, 17 Sep 2023 15:32:29 -0300
Subject: [PATCH 15/16] Note content: use italics for book titles + em-dash for
Quotation
---
bookwyrm/models/status.py | 7 ++++---
bookwyrm/tests/models/test_status_model.py | 18 +++++++++++++-----
2 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/bookwyrm/models/status.py b/bookwyrm/models/status.py
index d51de5278..5d6109468 100644
--- a/bookwyrm/models/status.py
+++ b/bookwyrm/models/status.py
@@ -270,7 +270,7 @@ class GeneratedNote(Status):
"""indicate the book in question for mastodon (or w/e) users"""
message = self.content
books = ", ".join(
- f'"{book.title}" '
+ f'{book.title} '
for book in self.mention_books.all()
)
return f"{self.user.display_name} {message} {books}"
@@ -324,7 +324,7 @@ class Comment(BookStatus):
progress = self.progress or 0
citation = (
f'comment on '
- f'"{self.book.title}" '
+ f"{self.book.title} "
)
if self.progress_mode == "PG" and progress > 0:
citation += f", p. {progress}"
@@ -365,7 +365,8 @@ class Quotation(BookStatus):
"""indicate the book in question for mastodon (or w/e) users"""
quote = re.sub(r"^", '
"', self.quote)
quote = re.sub(r"
$", '"', quote)
- citation = f'-- "{self.book.title}" '
+ title, href = self.book.title, self.book.remote_id
+ citation = f'— {title} '
if position := self._format_position():
citation += f", {position}"
return f"{quote} {citation}
{self.content}"
diff --git a/bookwyrm/tests/models/test_status_model.py b/bookwyrm/tests/models/test_status_model.py
index 15d73de9c..760849f28 100644
--- a/bookwyrm/tests/models/test_status_model.py
+++ b/bookwyrm/tests/models/test_status_model.py
@@ -212,7 +212,7 @@ class Status(TestCase):
def test_generated_note_to_pure_activity(self, *_):
"""subclass of the base model version with a "pure" serializer"""
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_users.set([self.local_user])
@@ -220,7 +220,7 @@ class Status(TestCase):
self.assertEqual(activity["id"], status.remote_id)
self.assertEqual(
activity["content"],
- f'mouse test content "Test Edition" ',
+ f'mouse reads Test Edition ',
)
self.assertEqual(len(activity["tag"]), 2)
self.assertEqual(activity["type"], "Note")
@@ -256,7 +256,11 @@ class Status(TestCase):
self.assertEqual(activity["type"], "Note")
self.assertEqual(
activity["content"],
- f'test content(comment on "Test Edition" , p. 27)
',
+ (
+ "test content"
+ f'(comment on '
+ "Test Edition , p. 27)
"
+ ),
)
self.assertEqual(activity["attachment"][0]["type"], "Document")
# self.assertTrue(
@@ -295,7 +299,11 @@ class Status(TestCase):
self.assertEqual(activity["type"], "Note")
self.assertEqual(
activity["content"],
- f'a sickening sense -- "Test Edition"
test content',
+ (
+ "a sickening sense "
+ f'— '
+ "Test Edition
test content"
+ ),
)
self.assertEqual(activity["attachment"][0]["type"], "Document")
self.assertTrue(
@@ -326,7 +334,7 @@ class Status(TestCase):
activity = status.to_activity(pure=True)
self.assertRegex(
activity["content"],
- f'^"my quote"
-- , {pages}
$',
+ f'^"my quote"
— , {pages}
$',
)
def test_review_to_activity(self, *_):
From fadf30b94216a407aceb3d089595fc40e8bc446e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adeodato=20Sim=C3=B3?=
Date: Sun, 17 Sep 2023 15:45:27 -0300
Subject: [PATCH 16/16] Also use italics for book title in editions.html
template
---
bookwyrm/templates/book/editions/editions.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/bookwyrm/templates/book/editions/editions.html b/bookwyrm/templates/book/editions/editions.html
index aa2b68bdb..e1766d1e1 100644
--- a/bookwyrm/templates/book/editions/editions.html
+++ b/bookwyrm/templates/book/editions/editions.html
@@ -5,7 +5,7 @@
{% block content %}
-
{% blocktrans with work_path=work.local_path work_title=work|book_title %}Editions of "{{ work_title }}" {% endblocktrans %}
+
{% blocktrans with work_path=work.local_path work_title=work|book_title %}Editions of {{ work_title }} {% endblocktrans %}
{% include 'book/editions/edition_filters.html' %}