From 35d30a41f3c0975d2c91bec13a4aa73a9b87e415 Mon Sep 17 00:00:00 2001 From: Dustin Steiner Date: Tue, 24 Jan 2023 13:00:18 +0000 Subject: [PATCH 01/11] feat: first version of a book series list by author --- bookwyrm/templates/book/book.html | 2 +- bookwyrm/templates/book/series.html | 35 +++++++++++++++++ bookwyrm/urls.py | 1 + bookwyrm/views/__init__.py | 1 + bookwyrm/views/books/series.py | 58 +++++++++++++++++++++++++++++ 5 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 bookwyrm/templates/book/series.html create mode 100644 bookwyrm/views/books/series.py diff --git a/bookwyrm/templates/book/book.html b/bookwyrm/templates/book/book.html index 6ea051ab9..d8f9110aa 100644 --- a/bookwyrm/templates/book/book.html +++ b/bookwyrm/templates/book/book.html @@ -46,7 +46,7 @@ - ({{ book.series }}{% if book.series_number %} #{{ book.series_number }}{% endif %}) + ({{ book.series }}{% if book.series_number %} #{{ book.series_number }}{% endif %}) {% endif %}

{% endif %} diff --git a/bookwyrm/templates/book/series.html b/bookwyrm/templates/book/series.html new file mode 100644 index 000000000..55cebd3c1 --- /dev/null +++ b/bookwyrm/templates/book/series.html @@ -0,0 +1,35 @@ +{% extends 'layout.html' %} +{% load i18n %} +{% load book_display_tags %} + +{% block title %}{{ series_name }}{% endblock %} + +{% block content %} +
+

{{ series_name }}

+
+ {% trans "Series by" %} +
+ +
+ {% for book in books %} + {% with book=book %} +
+
+ {% if book.series_number %}Book {{ book.series_number }}{% else %}Unsorted Book{% endif %} + {% include 'landing/small-book.html' with book=book %} +
+
+ {% endwith %} + {% endfor %} +
+
+{% endblock %} diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index ac3a80580..6d9f34de0 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -610,6 +610,7 @@ urlpatterns = [ # books re_path(rf"{BOOK_PATH}(.json)?/?$", views.Book.as_view(), name="book"), re_path(rf"{BOOK_PATH}{regex.SLUG}/?$", views.Book.as_view(), name="book"), + re_path(r"^series/by/(?P\d+)/?$", views.BookSeriesBy.as_view(), name="book-series-by"), re_path( rf"{BOOK_PATH}/(?Preview|comment|quote)/?$", views.Book.as_view(), diff --git a/bookwyrm/views/__init__.py b/bookwyrm/views/__init__.py index db88f1ae2..589ddb49c 100644 --- a/bookwyrm/views/__init__.py +++ b/bookwyrm/views/__init__.py @@ -50,6 +50,7 @@ from .books.books import ( add_description, resolve_book, ) +from .books.series import BookSeriesBy from .books.books import update_book_from_remote from .books.edit_book import ( EditBook, diff --git a/bookwyrm/views/books/series.py b/bookwyrm/views/books/series.py new file mode 100644 index 000000000..6085934d3 --- /dev/null +++ b/bookwyrm/views/books/series.py @@ -0,0 +1,58 @@ +""" books belonging to the same series """ +from django.views import View +from django.shortcuts import get_object_or_404 +from django.template.response import TemplateResponse + +from bookwyrm.views.helpers import is_api_request +from bookwyrm import models +from bookwyrm.settings import PAGE_LENGTH + + +# pylint: disable=no-self-use +class BookSeriesBy(View): + def get(self, request, author_id, **kwargs): + """lists all books in a series""" + series_name = request.GET.get("series_name") + + if is_api_request(request): + pass + + author = get_object_or_404(models.Author, id=author_id) + + results = ( + models.Edition.objects.filter(authors=author, series=series_name) + ) + + # when there are multiple editions of the same work, pick the closest + editions_of_work = results.values_list("parent_work__id", flat=True).distinct() + + # filter out multiple editions of the same work + numbered_books = [] + dated_books = [] + unsortable_books = [] + for work_id in set(editions_of_work): + result = ( + results.filter(parent_work=work_id) + .order_by("-edition_rank") + .first() + ) + if result.series_number: + numbered_books.append(result) + elif result.first_published_date or result.published_date: + dated_books.append(result) + else: + unsortable_books.append(result) + + list_results = ( + sorted(numbered_books, key=lambda book: book.series_number) + + sorted(dated_books, key=lambda book: book.first_published_date if book.first_published_date else book.published_date) + + sorted(unsortable_books, key=lambda book: book.sort_title) + ) + + data = { + "series_name": series_name, + "author": author, + "books": list_results, + } + + return TemplateResponse(request, "book/series.html", data) From cd13e6f523fc38e703631b21570fb9cc79bb163a Mon Sep 17 00:00:00 2001 From: Dustin Steiner Date: Tue, 24 Jan 2023 13:14:06 +0000 Subject: [PATCH 02/11] style: run linter --- bookwyrm/urls.py | 6 ++- bookwyrm/views/books/series.py | 83 +++++++++++++++++----------------- 2 files changed, 47 insertions(+), 42 deletions(-) diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index 6d9f34de0..c4d6b9b45 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -610,7 +610,11 @@ urlpatterns = [ # books re_path(rf"{BOOK_PATH}(.json)?/?$", views.Book.as_view(), name="book"), re_path(rf"{BOOK_PATH}{regex.SLUG}/?$", views.Book.as_view(), name="book"), - re_path(r"^series/by/(?P\d+)/?$", views.BookSeriesBy.as_view(), name="book-series-by"), + re_path( + r"^series/by/(?P\d+)/?$", + views.BookSeriesBy.as_view(), + name="book-series-by", + ), re_path( rf"{BOOK_PATH}/(?Preview|comment|quote)/?$", views.Book.as_view(), diff --git a/bookwyrm/views/books/series.py b/bookwyrm/views/books/series.py index 6085934d3..f573aac57 100644 --- a/bookwyrm/views/books/series.py +++ b/bookwyrm/views/books/series.py @@ -10,49 +10,50 @@ from bookwyrm.settings import PAGE_LENGTH # pylint: disable=no-self-use class BookSeriesBy(View): - def get(self, request, author_id, **kwargs): - """lists all books in a series""" - series_name = request.GET.get("series_name") + def get(self, request, author_id, **kwargs): + """lists all books in a series""" + series_name = request.GET.get("series_name") - if is_api_request(request): - pass - - author = get_object_or_404(models.Author, id=author_id) - - results = ( - models.Edition.objects.filter(authors=author, series=series_name) - ) + if is_api_request(request): + pass - # when there are multiple editions of the same work, pick the closest - editions_of_work = results.values_list("parent_work__id", flat=True).distinct() + author = get_object_or_404(models.Author, id=author_id) - # filter out multiple editions of the same work - numbered_books = [] - dated_books = [] - unsortable_books = [] - for work_id in set(editions_of_work): - result = ( - results.filter(parent_work=work_id) - .order_by("-edition_rank") - .first() - ) - if result.series_number: - numbered_books.append(result) - elif result.first_published_date or result.published_date: - dated_books.append(result) - else: - unsortable_books.append(result) - - list_results = ( - sorted(numbered_books, key=lambda book: book.series_number) + - sorted(dated_books, key=lambda book: book.first_published_date if book.first_published_date else book.published_date) + - sorted(unsortable_books, key=lambda book: book.sort_title) - ) + results = models.Edition.objects.filter(authors=author, series=series_name) - data = { - "series_name": series_name, - "author": author, - "books": list_results, - } + # when there are multiple editions of the same work, pick the closest + editions_of_work = results.values_list("parent_work__id", flat=True).distinct() - return TemplateResponse(request, "book/series.html", data) + # filter out multiple editions of the same work + numbered_books = [] + dated_books = [] + unsortable_books = [] + for work_id in set(editions_of_work): + result = ( + results.filter(parent_work=work_id).order_by("-edition_rank").first() + ) + if result.series_number: + numbered_books.append(result) + elif result.first_published_date or result.published_date: + dated_books.append(result) + else: + unsortable_books.append(result) + + list_results = ( + sorted(numbered_books, key=lambda book: book.series_number) + + sorted( + dated_books, + key=lambda book: book.first_published_date + if book.first_published_date + else book.published_date, + ) + + sorted(unsortable_books, key=lambda book: book.sort_title) + ) + + data = { + "series_name": series_name, + "author": author, + "books": list_results, + } + + return TemplateResponse(request, "book/series.html", data) From eb4672ff18697ef1602cd21b2ccc40e33ce225d9 Mon Sep 17 00:00:00 2001 From: Dustin Steiner Date: Thu, 26 Jan 2023 06:49:55 +0000 Subject: [PATCH 03/11] style: format --- bookwyrm/views/books/series.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bookwyrm/views/books/series.py b/bookwyrm/views/books/series.py index f573aac57..14a2a82c5 100644 --- a/bookwyrm/views/books/series.py +++ b/bookwyrm/views/books/series.py @@ -5,12 +5,13 @@ from django.template.response import TemplateResponse from bookwyrm.views.helpers import is_api_request from bookwyrm import models -from bookwyrm.settings import PAGE_LENGTH # pylint: disable=no-self-use class BookSeriesBy(View): - def get(self, request, author_id, **kwargs): + """book series by author""" + + def get(self, request, author_id): """lists all books in a series""" series_name = request.GET.get("series_name") From f2902e8de5e17757076dc497d4c5727da56d48c9 Mon Sep 17 00:00:00 2001 From: Dustin Steiner Date: Sat, 28 Jan 2023 08:38:57 +0000 Subject: [PATCH 04/11] fix: sort series number as int --- bookwyrm/views/books/series.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/views/books/series.py b/bookwyrm/views/books/series.py index 14a2a82c5..98e3841d3 100644 --- a/bookwyrm/views/books/series.py +++ b/bookwyrm/views/books/series.py @@ -41,7 +41,7 @@ class BookSeriesBy(View): unsortable_books.append(result) list_results = ( - sorted(numbered_books, key=lambda book: book.series_number) + sorted(numbered_books, key=lambda book: int(book.series_number)) + sorted( dated_books, key=lambda book: book.first_published_date From 4b64b707533f4f5de3a1c88abc6a634c0f0ff9a3 Mon Sep 17 00:00:00 2001 From: Dustin Steiner Date: Sun, 29 Jan 2023 09:35:02 +0000 Subject: [PATCH 05/11] fix: sort series by float --- bookwyrm/views/books/series.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/views/books/series.py b/bookwyrm/views/books/series.py index 98e3841d3..3017ba5b6 100644 --- a/bookwyrm/views/books/series.py +++ b/bookwyrm/views/books/series.py @@ -41,7 +41,7 @@ class BookSeriesBy(View): unsortable_books.append(result) list_results = ( - sorted(numbered_books, key=lambda book: int(book.series_number)) + sorted(numbered_books, key=lambda book: float(book.series_number)) + sorted( dated_books, key=lambda book: book.first_published_date From 0628ccad414c731eb58a8908e8e3c66533f908c3 Mon Sep 17 00:00:00 2001 From: Dustin Steiner Date: Sun, 29 Jan 2023 11:43:30 +0000 Subject: [PATCH 06/11] chore: sort editions with series number before edition rank --- bookwyrm/views/books/series.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/bookwyrm/views/books/series.py b/bookwyrm/views/books/series.py index 3017ba5b6..6e295a4e9 100644 --- a/bookwyrm/views/books/series.py +++ b/bookwyrm/views/books/series.py @@ -21,17 +21,17 @@ class BookSeriesBy(View): author = get_object_or_404(models.Author, id=author_id) results = models.Edition.objects.filter(authors=author, series=series_name) - - # when there are multiple editions of the same work, pick the closest - editions_of_work = results.values_list("parent_work__id", flat=True).distinct() - + + # when there are multiple editions of the same work, pick the one with a series number or closest + work_ids = results.values_list("parent_work__id", flat=True).distinct() + # filter out multiple editions of the same work numbered_books = [] dated_books = [] unsortable_books = [] - for work_id in set(editions_of_work): + for work_id in set(work_ids): result = ( - results.filter(parent_work=work_id).order_by("-edition_rank").first() + results.filter(parent_work=work_id).order_by("series_number", "-edition_rank").first() ) if result.series_number: numbered_books.append(result) @@ -48,7 +48,7 @@ class BookSeriesBy(View): if book.first_published_date else book.published_date, ) - + sorted(unsortable_books, key=lambda book: book.sort_title) + + sorted(unsortable_books, key=lambda book: book.sort_title if book.sort_title else book.title) ) data = { From 59a8098517fadd7d7f88ccde6370aa68f8984204 Mon Sep 17 00:00:00 2001 From: Dustin Steiner Date: Mon, 30 Jan 2023 08:26:59 +0000 Subject: [PATCH 07/11] chore: add translations --- bookwyrm/templates/book/series.html | 2 +- locale/en_US/LC_MESSAGES/django.po | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/bookwyrm/templates/book/series.html b/bookwyrm/templates/book/series.html index 55cebd3c1..dc8113813 100644 --- a/bookwyrm/templates/book/series.html +++ b/bookwyrm/templates/book/series.html @@ -24,7 +24,7 @@ {% with book=book %}
- {% if book.series_number %}Book {{ book.series_number }}{% else %}Unsorted Book{% endif %} + {% if book.series_number %}{% blocktrans with series_number=book.series_number %}Book {{ series_number }}{% endblocktrans %}{% else %}{% trans 'Unsorted Book' %}{% endif %} {% include 'landing/small-book.html' with book=book %}
diff --git a/locale/en_US/LC_MESSAGES/django.po b/locale/en_US/LC_MESSAGES/django.po index 9ba4bfe40..2806208d0 100644 --- a/locale/en_US/LC_MESSAGES/django.po +++ b/locale/en_US/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: 0.0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-01-26 16:43+0000\n" +"POT-Creation-Date: 2023-01-30 08:25+0000\n" "PO-Revision-Date: 2021-02-28 17:19-0800\n" "Last-Translator: Mouse Reeve \n" "Language-Team: English \n" @@ -1471,6 +1471,19 @@ msgstr "" msgid "rated it" msgstr "" +#: bookwyrm/templates/book/series.html:11 +msgid "Series by" +msgstr "" + +#: bookwyrm/templates/book/series.html:27 +#, python-format +msgid "Book %(series_number)s" +msgstr "" + +#: bookwyrm/templates/book/series.html:27 +msgid "Unsorted Book" +msgstr "" + #: bookwyrm/templates/book/sync_modal.html:15 #, python-format msgid "Loading data will connect to %(source_name)s and check for any metadata about this book which aren't present here. Existing metadata will not be overwritten." From e0505722e10ca4ab5089347e916bde39a66ee515 Mon Sep 17 00:00:00 2001 From: Dustin Steiner Date: Mon, 30 Jan 2023 14:45:30 +0000 Subject: [PATCH 08/11] chore: formatting --- bookwyrm/views/books/series.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/bookwyrm/views/books/series.py b/bookwyrm/views/books/series.py index 6e295a4e9..23ec924a5 100644 --- a/bookwyrm/views/books/series.py +++ b/bookwyrm/views/books/series.py @@ -21,17 +21,19 @@ class BookSeriesBy(View): author = get_object_or_404(models.Author, id=author_id) results = models.Edition.objects.filter(authors=author, series=series_name) - + # when there are multiple editions of the same work, pick the one with a series number or closest work_ids = results.values_list("parent_work__id", flat=True).distinct() - + # filter out multiple editions of the same work numbered_books = [] dated_books = [] unsortable_books = [] for work_id in set(work_ids): result = ( - results.filter(parent_work=work_id).order_by("series_number", "-edition_rank").first() + results.filter(parent_work=work_id) + .order_by("series_number", "-edition_rank") + .first() ) if result.series_number: numbered_books.append(result) @@ -48,7 +50,10 @@ class BookSeriesBy(View): if book.first_published_date else book.published_date, ) - + sorted(unsortable_books, key=lambda book: book.sort_title if book.sort_title else book.title) + + sorted( + unsortable_books, + key=lambda book: book.sort_title if book.sort_title else book.title, + ) ) data = { From d0ac65b93140c56cb9011248dfc597cbf9e18f20 Mon Sep 17 00:00:00 2001 From: Dustin Steiner Date: Tue, 31 Jan 2023 10:05:03 +0000 Subject: [PATCH 09/11] chore: reset translation files --- locale/en_US/LC_MESSAGES/django.po | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/locale/en_US/LC_MESSAGES/django.po b/locale/en_US/LC_MESSAGES/django.po index 2806208d0..5f3c62895 100644 --- a/locale/en_US/LC_MESSAGES/django.po +++ b/locale/en_US/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: 0.0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-01-30 08:25+0000\n" +"POT-Creation-Date: 2023-01-30 08:21+0000\n" "PO-Revision-Date: 2021-02-28 17:19-0800\n" "Last-Translator: Mouse Reeve \n" "Language-Team: English \n" @@ -851,7 +851,7 @@ msgstr "" #: bookwyrm/templates/settings/registration.html:96 #: bookwyrm/templates/settings/registration_limited.html:76 #: bookwyrm/templates/settings/site.html:144 -#: bookwyrm/templates/settings/users/user_moderation_actions.html:69 +#: bookwyrm/templates/settings/users/user_moderation_actions.html:75 #: bookwyrm/templates/shelf/form.html:25 #: bookwyrm/templates/snippets/reading_modals/layout.html:18 msgid "Save" @@ -1471,19 +1471,6 @@ msgstr "" msgid "rated it" msgstr "" -#: bookwyrm/templates/book/series.html:11 -msgid "Series by" -msgstr "" - -#: bookwyrm/templates/book/series.html:27 -#, python-format -msgid "Book %(series_number)s" -msgstr "" - -#: bookwyrm/templates/book/series.html:27 -msgid "Unsorted Book" -msgstr "" - #: bookwyrm/templates/book/sync_modal.html:15 #, python-format msgid "Loading data will connect to %(source_name)s and check for any metadata about this book which aren't present here. Existing metadata will not be overwritten." @@ -5461,7 +5448,7 @@ msgid "Remove theme" msgstr "" #: bookwyrm/templates/settings/users/delete_user_form.html:5 -#: bookwyrm/templates/settings/users/user_moderation_actions.html:32 +#: bookwyrm/templates/settings/users/user_moderation_actions.html:38 msgid "Permanently delete user" msgstr "" @@ -5583,14 +5570,18 @@ msgid "User Actions" msgstr "" #: bookwyrm/templates/settings/users/user_moderation_actions.html:21 +msgid "Activate user" +msgstr "" + +#: bookwyrm/templates/settings/users/user_moderation_actions.html:27 msgid "Suspend user" msgstr "" -#: bookwyrm/templates/settings/users/user_moderation_actions.html:26 +#: bookwyrm/templates/settings/users/user_moderation_actions.html:32 msgid "Un-suspend user" msgstr "" -#: bookwyrm/templates/settings/users/user_moderation_actions.html:48 +#: bookwyrm/templates/settings/users/user_moderation_actions.html:54 msgid "Access level:" msgstr "" From 90f996ac903864ad67d851d1c55dd18e484d28eb Mon Sep 17 00:00:00 2001 From: Dustin Steiner Date: Tue, 31 Jan 2023 10:16:44 +0000 Subject: [PATCH 10/11] chore: multi line comment for linter --- bookwyrm/views/books/series.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bookwyrm/views/books/series.py b/bookwyrm/views/books/series.py index 23ec924a5..ad9b08667 100644 --- a/bookwyrm/views/books/series.py +++ b/bookwyrm/views/books/series.py @@ -22,7 +22,8 @@ class BookSeriesBy(View): results = models.Edition.objects.filter(authors=author, series=series_name) - # when there are multiple editions of the same work, pick the one with a series number or closest + # when there are multiple editions of the same work + # pick the one with a series number or closest work_ids = results.values_list("parent_work__id", flat=True).distinct() # filter out multiple editions of the same work From d1f85e43176ac811c6575e7598380b724fcc2221 Mon Sep 17 00:00:00 2001 From: Dustin Steiner Date: Tue, 7 Feb 2023 12:05:16 +0000 Subject: [PATCH 11/11] fix: books can have non-decimal series numbers --- bookwyrm/views/books/series.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/bookwyrm/views/books/series.py b/bookwyrm/views/books/series.py index ad9b08667..bdc8dccab 100644 --- a/bookwyrm/views/books/series.py +++ b/bookwyrm/views/books/series.py @@ -1,4 +1,5 @@ """ books belonging to the same series """ +from sys import float_info from django.views import View from django.shortcuts import get_object_or_404 from django.template.response import TemplateResponse @@ -7,6 +8,14 @@ from bookwyrm.views.helpers import is_api_request from bookwyrm import models +def sort_by_series(book): + """sort books using their series number""" + try: + return float(book.series_number) + except ValueError: + return float_info.max + + # pylint: disable=no-self-use class BookSeriesBy(View): """book series by author""" @@ -44,7 +53,7 @@ class BookSeriesBy(View): unsortable_books.append(result) list_results = ( - sorted(numbered_books, key=lambda book: float(book.series_number)) + sorted(numbered_books, key=sort_by_series) + sorted( dated_books, key=lambda book: book.first_published_date