From 2faf5cea2a723c43b09b5ffba164e951b23f9682 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Tue, 2 Mar 2021 09:01:31 -0800 Subject: [PATCH 01/18] modifies edit book code to allow creation as well --- bookwyrm/templates/edit_book.html | 80 +++++++++++++++++++------------ bookwyrm/urls.py | 1 + bookwyrm/views/books.py | 34 ++++++++----- 3 files changed, 73 insertions(+), 42 deletions(-) diff --git a/bookwyrm/templates/edit_book.html b/bookwyrm/templates/edit_book.html index 4d2159497..aab9e1035 100644 --- a/bookwyrm/templates/edit_book.html +++ b/bookwyrm/templates/edit_book.html @@ -2,12 +2,16 @@ {% load i18n %} {% load humanize %} -{% block title %}{% trans "Edit Book" %}{% endblock %} +{% block title %}{% if book %}{% blocktrans with book_title=book.title %}Edit "{{ book_title }}"{% endblocktrans %}{% else %}{% trans "Add Book" %}{% endif %}{% endblock %} {% block content %}

- Edit "{{ book.title }}" + {% if book %} + {% blocktrans with book_title=book.title %}Edit "{{ book.title }}"{% endblocktrans %} + {% else %} + {% trans "Add Book" %} + {% endif %}

{% trans "Added:" %} {{ book.created_date | naturaltime }}

@@ -27,35 +31,49 @@
-

{% trans "Metadata" %}

-

{{ form.title }}

- {% for error in form.title.errors %} -

{{ error | escape }}

- {% endfor %} -

{{ form.subtitle }}

- {% for error in form.subtitle.errors %} -

{{ error | escape }}

- {% endfor %} -

{{ form.description }}

- {% for error in form.description.errors %} -

{{ error | escape }}

- {% endfor %} -

{{ form.series }}

- {% for error in form.series.errors %} -

{{ error | escape }}

- {% endfor %} -

{{ form.series_number }}

- {% for error in form.series_number.errors %} -

{{ error | escape }}

- {% endfor %} -

{{ form.first_published_date }}

- {% for error in form.first_published_date.errors %} -

{{ error | escape }}

- {% endfor %} -

{{ form.published_date }}

- {% for error in form.published_date.errors %} -

{{ error | escape }}

- {% endfor %} +
+

{% trans "Metadata" %}

+

{{ form.title }}

+ {% for error in form.title.errors %} +

{{ error | escape }}

+ {% endfor %} +

{{ form.subtitle }}

+ {% for error in form.subtitle.errors %} +

{{ error | escape }}

+ {% endfor %} +

{{ form.description }}

+ {% for error in form.description.errors %} +

{{ error | escape }}

+ {% endfor %} +

{{ form.series }}

+ {% for error in form.series.errors %} +

{{ error | escape }}

+ {% endfor %} +

{{ form.series_number }}

+ {% for error in form.series_number.errors %} +

{{ error | escape }}

+ {% endfor %} +

{{ form.first_published_date }}

+ {% for error in form.first_published_date.errors %} +

{{ error | escape }}

+ {% endfor %} +

{{ form.published_date }}

+ {% for error in form.published_date.errors %} +

{{ error | escape }}

+ {% endfor %} +
+ +
+

{% trans "Authors" %}

+ {% for author in book.authors.all %} +

{{ author.name }} + + {% endfor %} + + +

diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index a741088a2..dfb64c234 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -134,6 +134,7 @@ urlpatterns = [ re_path(r'^add-description/(?P\d+)/?$', views.add_description), re_path(r'^resolve-book/?$', views.resolve_book), re_path(r'^switch-edition/?$', views.switch_edition), + re_path(r'^create-book/?$', views.EditBook.as_view()), # author re_path(r'^author/(?P\d+)(.json)?/?$', views.Author.as_view()), diff --git a/bookwyrm/views/books.py b/bookwyrm/views/books.py index 4d6afba96..1754982e8 100644 --- a/bookwyrm/views/books.py +++ b/bookwyrm/views/books.py @@ -106,28 +106,40 @@ class Book(View): name='dispatch') class EditBook(View): ''' edit a book ''' - def get(self, request, book_id): + def get(self, request, book_id=None): ''' info about a book ''' - book = get_edition(book_id) - if not book.description: - book.description = book.parent_work.description + book = None + if book_id: + book = get_edition(book_id) + if not book.description: + book.description = book.parent_work.description data = { 'book': book, 'form': forms.EditionForm(instance=book) } return TemplateResponse(request, 'edit_book.html', data) - def post(self, request, book_id): + def post(self, request, book_id=None): ''' edit a book cool ''' - book = get_object_or_404(models.Edition, id=book_id) - + book = get_object_or_404(models.Edition, id=book_id) if book_id \ + else None form = forms.EditionForm(request.POST, request.FILES, instance=book) + + data = { + 'book': book, + 'form': form + } if not form.is_valid(): - data = { - 'book': book, - 'form': form - } return TemplateResponse(request, 'edit_book.html', data) + + if not book or form.author: + # creting a book or adding an author to a book needs another step + return TemplateResponse(request, 'confirm_book.html', data) + + # remove authors + if request.POST.get('remove-author'): + import pdb;pdb.set_trace() + author = get_object_or_404(id=author_id) book = form.save() return redirect('/book/%s' % book.id) From b2d1384bc5065725ec51c4204cc97f64a9a751f6 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 4 Mar 2021 13:48:50 -0800 Subject: [PATCH 02/18] UI for adding and removing authors --- bookwyrm/templates/edit_book.html | 43 ++++++++++++++++++++++++++++--- bookwyrm/tests/views/test_book.py | 16 ++++++++++++ bookwyrm/views/books.py | 33 ++++++++++++++++++------ 3 files changed, 81 insertions(+), 11 deletions(-) diff --git a/bookwyrm/templates/edit_book.html b/bookwyrm/templates/edit_book.html index aab9e1035..d0eb50bec 100644 --- a/bookwyrm/templates/edit_book.html +++ b/bookwyrm/templates/edit_book.html @@ -26,7 +26,8 @@
{% endif %} -
+ +
{% csrf_token %}
@@ -68,11 +69,11 @@ {% for author in book.authors.all %}

{{ author.name }} {% endfor %} - +

@@ -138,6 +139,42 @@ {% trans "Cancel" %}
+ +{% if author_matches or book_matches %} +
+
+

{% trans "Confirm Book Info" %}

+
+ {% if author_matches.exists %} +
+ {% blocktrans %}Is "{{ add_author }}" an existing author?{% endblocktrans %} + {% for match in author_matches %} + +

+ {% blocktrans with book_title=match.book_set.first.title %}Author of {{ book_title }}{% endblocktrans %} +

+ {% endfor %} + +
+ {% else %} +

{% blocktrans %}Creating a new author: {{ add_author }}{% endblocktrans %}

+ {% endif %} + + {% if not book %} +
+ {% trans "Is this an editions of an existing work?" %} + {% for match in book_matches %} + + {% endfor %} + +
+ {% endif %} +
+ + +
+{% endif %} + {% endblock %} diff --git a/bookwyrm/tests/views/test_book.py b/bookwyrm/tests/views/test_book.py index b3360200d..b7eaac4bc 100644 --- a/bookwyrm/tests/views/test_book.py +++ b/bookwyrm/tests/views/test_book.py @@ -83,6 +83,22 @@ class BookViews(TestCase): self.assertEqual(self.book.title, 'New Title') + def test_edit_book_add_author(self): + ''' lets a user edit a book ''' + view = views.EditBook.as_view() + self.local_user.groups.add(self.group) + form = forms.EditionForm(instance=self.book) + form.data['title'] = 'New Title' + form.data['last_edited_by'] = self.local_user.id + form.data['add_author'] = "John Doe" + request = self.factory.post('', form.data) + request.user = self.local_user + with patch('bookwyrm.models.activitypub_mixin.broadcast_task.delay'): + view(request, self.book.id) + self.book.refresh_from_db() + self.assertEqual(self.book.title, 'New Title') + + def test_switch_edition(self): ''' updates user's relationships to a book ''' work = models.Work.objects.create(title='test work') diff --git a/bookwyrm/views/books.py b/bookwyrm/views/books.py index 1754982e8..174ddaa30 100644 --- a/bookwyrm/views/books.py +++ b/bookwyrm/views/books.py @@ -1,6 +1,7 @@ ''' the good stuff! the books! ''' -from django.core.paginator import Paginator from django.contrib.auth.decorators import login_required, permission_required +from django.contrib.postgres.search import SearchRank, SearchVector +from django.core.paginator import Paginator from django.db import transaction from django.db.models import Avg, Q from django.http import HttpResponseNotFound @@ -132,16 +133,32 @@ class EditBook(View): if not form.is_valid(): return TemplateResponse(request, 'edit_book.html', data) - if not book or form.author: + add_author = request.POST.get('add_author') + if not book or add_author: # creting a book or adding an author to a book needs another step - return TemplateResponse(request, 'confirm_book.html', data) + data['confirm_mode'] = True + data['add_author'] = add_author + # check for existing authors + vector = SearchVector('name', weight='A') +\ + SearchVector('aliases', weight='B') + + data['author_matches'] = models.Author.objects.annotate( + search=vector + ).annotate( + rank=SearchRank(vector, add_author) + ).filter(rank__gt=0.8).order_by('-rank')[:5] + + # check if this is an edition of an existing work + author_text = book.author_text if book else add_author + data['book_matches'] = connector_manager.local_search( + '%s %s' % (form.cleaned_data.get('title'), author_text), + min_confidence=0.5, + raw=True + )[:5] + + return TemplateResponse(request, 'edit_book.html', data) - # remove authors - if request.POST.get('remove-author'): - import pdb;pdb.set_trace() - author = get_object_or_404(id=author_id) book = form.save() - return redirect('/book/%s' % book.id) From 5c089db086f6d85748cfff84ea04d54aababd5fa Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 4 Mar 2021 17:09:49 -0800 Subject: [PATCH 03/18] Full add author flow --- bookwyrm/templates/edit_book.html | 84 +++++++++++++++++-------------- bookwyrm/tests/views/test_book.py | 13 +---- bookwyrm/urls.py | 1 + bookwyrm/views/__init__.py | 2 +- bookwyrm/views/books.py | 53 +++++++++++++++++-- 5 files changed, 96 insertions(+), 57 deletions(-) diff --git a/bookwyrm/templates/edit_book.html b/bookwyrm/templates/edit_book.html index d0eb50bec..0637bb79c 100644 --- a/bookwyrm/templates/edit_book.html +++ b/bookwyrm/templates/edit_book.html @@ -8,7 +8,7 @@

{% if book %} - {% blocktrans with book_title=book.title %}Edit "{{ book.title }}"{% endblocktrans %} + {% blocktrans with book_title=book.title %}Edit "{{ book_title }}"{% endblocktrans %} {% else %} {% trans "Add Book" %} {% endif %} @@ -26,9 +26,46 @@

{% endif %} -
-
+ {% csrf_token %} + {% if confirm_mode %} +
+

{% trans "Confirm Book Info" %}

+
+ {% if author_matches.exists %} +
+ {% blocktrans with name=add_author %}Is "{{ name }}" an existing author?{% endblocktrans %} + {% for match in author_matches %} + +

+ {% blocktrans with book_title=match.book_set.first.title %}Author of {{ book_title }}{% endblocktrans %} +

+ {% endfor %} + +
+ {% else %} +

{% blocktrans with name=add_author %}Creating a new author: {{ name }}{% endblocktrans %}

+ {% endif %} + + {% if not book %} +
+ {% trans "Is this an editions of an existing work?" %} + {% for match in book_matches %} + + {% endfor %} + +
+ {% endif %} +
+ + + +
+ +
+ {% endif %} + +
@@ -72,8 +109,12 @@ {% trans "Remove this author" %} {% endfor %} + {% if confirm_mode %} + + {% else %} - + + {% endif %}
@@ -142,39 +183,4 @@
-{% if author_matches or book_matches %} -
-
-

{% trans "Confirm Book Info" %}

-
- {% if author_matches.exists %} -
- {% blocktrans %}Is "{{ add_author }}" an existing author?{% endblocktrans %} - {% for match in author_matches %} - -

- {% blocktrans with book_title=match.book_set.first.title %}Author of {{ book_title }}{% endblocktrans %} -

- {% endfor %} - -
- {% else %} -

{% blocktrans %}Creating a new author: {{ add_author }}{% endblocktrans %}

- {% endif %} - - {% if not book %} -
- {% trans "Is this an editions of an existing work?" %} - {% for match in book_matches %} - - {% endfor %} - -
- {% endif %} -
- - -
-{% endif %} - {% endblock %} diff --git a/bookwyrm/tests/views/test_book.py b/bookwyrm/tests/views/test_book.py index b7eaac4bc..8a6c66cb8 100644 --- a/bookwyrm/tests/views/test_book.py +++ b/bookwyrm/tests/views/test_book.py @@ -85,18 +85,7 @@ class BookViews(TestCase): def test_edit_book_add_author(self): ''' lets a user edit a book ''' - view = views.EditBook.as_view() - self.local_user.groups.add(self.group) - form = forms.EditionForm(instance=self.book) - form.data['title'] = 'New Title' - form.data['last_edited_by'] = self.local_user.id - form.data['add_author'] = "John Doe" - request = self.factory.post('', form.data) - request.user = self.local_user - with patch('bookwyrm.models.activitypub_mixin.broadcast_task.delay'): - view(request, self.book.id) - self.book.refresh_from_db() - self.assertEqual(self.book.title, 'New Title') + # TODO def test_switch_edition(self): diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index dfb64c234..9e0d46300 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -129,6 +129,7 @@ urlpatterns = [ # books re_path(r'%s(.json)?/?$' % book_path, views.Book.as_view()), re_path(r'%s/edit/?$' % book_path, views.EditBook.as_view()), + re_path(r'%s/confirm/?$' % book_path, views.ConfirmEditBook.as_view()), re_path(r'%s/editions(.json)?/?$' % book_path, views.Editions.as_view()), re_path(r'^upload-cover/(?P\d+)/?$', views.upload_cover), re_path(r'^add-description/(?P\d+)/?$', views.add_description), diff --git a/bookwyrm/views/__init__.py b/bookwyrm/views/__init__.py index 2c7cdc461..c3d17da76 100644 --- a/bookwyrm/views/__init__.py +++ b/bookwyrm/views/__init__.py @@ -2,7 +2,7 @@ from .authentication import Login, Register, Logout from .author import Author, EditAuthor from .block import Block, unblock -from .books import Book, EditBook, Editions +from .books import Book, EditBook, ConfirmEditBook, Editions from .books import upload_cover, add_description, switch_edition, resolve_book from .error import not_found_page, server_error_page from .federation import Federation diff --git a/bookwyrm/views/books.py b/bookwyrm/views/books.py index 174ddaa30..0097eb9f6 100644 --- a/bookwyrm/views/books.py +++ b/bookwyrm/views/books.py @@ -122,8 +122,8 @@ class EditBook(View): def post(self, request, book_id=None): ''' edit a book cool ''' - book = get_object_or_404(models.Edition, id=book_id) if book_id \ - else None + # returns None if no match is found + book = models.Edition.objects.filter(id=book_id).first() form = forms.EditionForm(request.POST, request.FILES, instance=book) data = { @@ -134,9 +134,8 @@ class EditBook(View): return TemplateResponse(request, 'edit_book.html', data) add_author = request.POST.get('add_author') - if not book or add_author: - # creting a book or adding an author to a book needs another step - data['confirm_mode'] = True + # we're adding an author through a free text field + if add_author: data['add_author'] = add_author # check for existing authors vector = SearchVector('name', weight='A') +\ @@ -148,6 +147,8 @@ class EditBook(View): rank=SearchRank(vector, add_author) ).filter(rank__gt=0.8).order_by('-rank')[:5] + # we're creating a new book + if not book: # check if this is an edition of an existing work author_text = book.author_text if book else add_author data['book_matches'] = connector_manager.local_search( @@ -156,12 +157,54 @@ class EditBook(View): raw=True )[:5] + # either of the above cases requires additional confirmation + if add_author or not book: + # creting a book or adding an author to a book needs another step + data['confirm_mode'] = True return TemplateResponse(request, 'edit_book.html', data) book = form.save() return redirect('/book/%s' % book.id) +@method_decorator(login_required, name='dispatch') +@method_decorator( + permission_required('bookwyrm.edit_book', raise_exception=True), + name='dispatch') +class ConfirmEditBook(View): + ''' confirm edits to a book ''' + def post(self, request, book_id=None): + ''' edit a book cool ''' + # returns None if no match is found + book = models.Edition.objects.filter(id=book_id).first() + form = forms.EditionForm(request.POST, request.FILES, instance=book) + + data = { + 'book': book, + 'form': form + } + if not form.is_valid(): + return TemplateResponse(request, 'edit_book.html', data) + + # create work, if needed + # TODO + + # save book + book = form.save() + + # get or create author as needed + if request.POST.get('add_author'): + if request.POST.get('author_match'): + author = get_object_or_404( + models.Author, id=request.POST['author_match']) + else: + author = models.Author.objects.create( + name=request.POST.get('add_author')) + book.authors.add(author) + + return redirect('/book/%s' % book.id) + + class Editions(View): ''' list of editions ''' def get(self, request, book_id): From bbd3ac7242662964b055c89344de25c745c77019 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sun, 7 Mar 2021 13:17:11 -0800 Subject: [PATCH 04/18] Removes ID field from hideen form value --- bookwyrm/templates/edit_book.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/templates/edit_book.html b/bookwyrm/templates/edit_book.html index 0637bb79c..dc927e8ab 100644 --- a/bookwyrm/templates/edit_book.html +++ b/bookwyrm/templates/edit_book.html @@ -110,7 +110,7 @@ {% endfor %} {% if confirm_mode %} - + {% else %} From f1b699d8105c96dbb83f2b88102dffd75e83ddd0 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sun, 7 Mar 2021 13:59:27 -0800 Subject: [PATCH 05/18] Tests adding author to book --- bookwyrm/tests/views/test_book.py | 36 +++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/bookwyrm/tests/views/test_book.py b/bookwyrm/tests/views/test_book.py index 8a6c66cb8..0ba09e3b9 100644 --- a/bookwyrm/tests/views/test_book.py +++ b/bookwyrm/tests/views/test_book.py @@ -84,8 +84,40 @@ class BookViews(TestCase): def test_edit_book_add_author(self): - ''' lets a user edit a book ''' - # TODO + ''' lets a user edit a book with new authors ''' + view = views.EditBook.as_view() + self.local_user.groups.add(self.group) + form = forms.EditionForm(instance=self.book) + form.data['title'] = 'New Title' + form.data['last_edited_by'] = self.local_user.id + form.data['add_author'] = 'Sappho' + request = self.factory.post('', form.data) + request.user = self.local_user + + result = view(request, self.book.id) + result.render() + + # the changes haven't been saved yet + self.book.refresh_from_db() + self.assertEqual(self.book.title, 'Example Edition') + + def test_edit_book_add_new_author_confirm(self): + ''' lets a user edit a book confirmed with new authors ''' + view = views.ConfirmEditBook.as_view() + self.local_user.groups.add(self.group) + form = forms.EditionForm(instance=self.book) + form.data['title'] = 'New Title' + form.data['last_edited_by'] = self.local_user.id + form.data['add_author'] = 'Sappho' + request = self.factory.post('', form.data) + request.user = self.local_user + + view(request, self.book.id) + + # the changes haven't been saved yet + self.book.refresh_from_db() + self.assertEqual(self.book.title, 'New Title') + self.assertEqual(self.book.authors.first().name, 'Sappho') def test_switch_edition(self): From 79d9c493f7fcc8b64d9e3c878f8358f331c29d11 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sun, 7 Mar 2021 14:19:22 -0800 Subject: [PATCH 06/18] Remove author flow --- bookwyrm/templates/edit_book.html | 14 ++++++++------ bookwyrm/views/books.py | 4 ++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/bookwyrm/templates/edit_book.html b/bookwyrm/templates/edit_book.html index dc927e8ab..a4d62efda 100644 --- a/bookwyrm/templates/edit_book.html +++ b/bookwyrm/templates/edit_book.html @@ -103,12 +103,14 @@

{% trans "Authors" %}

- {% for author in book.authors.all %} -

{{ author.name }} - - {% endfor %} +

+ {% for author in book.authors.all %} +

{{ author.name }} + + {% endfor %} +

{% if confirm_mode %} {% else %} diff --git a/bookwyrm/views/books.py b/bookwyrm/views/books.py index 0097eb9f6..cc05a79c3 100644 --- a/bookwyrm/views/books.py +++ b/bookwyrm/views/books.py @@ -163,6 +163,10 @@ class EditBook(View): data['confirm_mode'] = True return TemplateResponse(request, 'edit_book.html', data) + remove_authors = request.POST.getlist('remove_authors') + for author_id in remove_authors: + book.authors.remove(author_id) + book = form.save() return redirect('/book/%s' % book.id) From 1eac2b938618f6dcf8ed63989ed39b560e6cd826 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sun, 7 Mar 2021 15:14:57 -0800 Subject: [PATCH 07/18] Test for deleting authors --- bookwyrm/tests/views/test_book.py | 20 +++++++++++++++++++- bookwyrm/views/books.py | 4 ++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/bookwyrm/tests/views/test_book.py b/bookwyrm/tests/views/test_book.py index 0ba09e3b9..6a28ba9d6 100644 --- a/bookwyrm/tests/views/test_book.py +++ b/bookwyrm/tests/views/test_book.py @@ -114,11 +114,29 @@ class BookViews(TestCase): view(request, self.book.id) - # the changes haven't been saved yet self.book.refresh_from_db() self.assertEqual(self.book.title, 'New Title') self.assertEqual(self.book.authors.first().name, 'Sappho') + def test_edit_book_remove_author(self): + ''' remove an author from a book ''' + author = models.Author.objects.create(name='Sappho') + self.book.authors.add(author) + form = forms.EditionForm(instance=self.book) + view = views.EditBook.as_view() + self.local_user.groups.add(self.group) + form = forms.EditionForm(instance=self.book) + form.data['title'] = 'New Title' + form.data['last_edited_by'] = self.local_user.id + form.data['remove_authors'] = [author.id] + request = self.factory.post('', form.data) + request.user = self.local_user + + view(request, self.book.id) + self.book.refresh_from_db() + self.assertEqual(self.book.title, 'New Title') + self.assertFalse(self.book.authors.exists()) + def test_switch_edition(self): ''' updates user's relationships to a book ''' diff --git a/bookwyrm/views/books.py b/bookwyrm/views/books.py index cc05a79c3..07a1d4372 100644 --- a/bookwyrm/views/books.py +++ b/bookwyrm/views/books.py @@ -206,6 +206,10 @@ class ConfirmEditBook(View): name=request.POST.get('add_author')) book.authors.add(author) + remove_authors = request.POST.getlist('remove_authors') + for author_id in remove_authors: + book.authors.remove(author_id) + return redirect('/book/%s' % book.id) From d3162e12db9d4f9ae975253f5fe10e21747f6c0a Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Mon, 8 Mar 2021 08:51:54 -0800 Subject: [PATCH 08/18] Adds broadcast mock to edit book tests --- bookwyrm/tests/views/test_book.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bookwyrm/tests/views/test_book.py b/bookwyrm/tests/views/test_book.py index 6a28ba9d6..484c695b0 100644 --- a/bookwyrm/tests/views/test_book.py +++ b/bookwyrm/tests/views/test_book.py @@ -112,7 +112,8 @@ class BookViews(TestCase): request = self.factory.post('', form.data) request.user = self.local_user - view(request, self.book.id) + with patch('bookwyrm.models.activitypub_mixin.broadcast_task.delay'): + view(request, self.book.id) self.book.refresh_from_db() self.assertEqual(self.book.title, 'New Title') @@ -132,7 +133,8 @@ class BookViews(TestCase): request = self.factory.post('', form.data) request.user = self.local_user - view(request, self.book.id) + with patch('bookwyrm.models.activitypub_mixin.broadcast_task.delay'): + view(request, self.book.id) self.book.refresh_from_db() self.assertEqual(self.book.title, 'New Title') self.assertFalse(self.book.authors.exists()) From a5baa1f5c618e393b8f8e791c3730868044324da Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Mon, 8 Mar 2021 09:28:22 -0800 Subject: [PATCH 09/18] Create new books --- bookwyrm/forms.py | 2 +- bookwyrm/templates/edit_book.html | 11 ++++--- bookwyrm/templates/search_results.html | 4 +++ bookwyrm/urls.py | 3 +- bookwyrm/views/books.py | 42 +++++++++++++++----------- 5 files changed, 39 insertions(+), 23 deletions(-) diff --git a/bookwyrm/forms.py b/bookwyrm/forms.py index b920fc9c0..1ab6e0eef 100644 --- a/bookwyrm/forms.py +++ b/bookwyrm/forms.py @@ -134,7 +134,7 @@ class EditionForm(CustomForm): 'updated_date', 'edition_rank', - 'authors',# TODO + 'authors', 'parent_work', 'shelves', diff --git a/bookwyrm/templates/edit_book.html b/bookwyrm/templates/edit_book.html index a4d62efda..1ebcc6b9a 100644 --- a/bookwyrm/templates/edit_book.html +++ b/bookwyrm/templates/edit_book.html @@ -13,11 +13,13 @@ {% trans "Add Book" %} {% endif %} + {% if book %}

{% trans "Added:" %} {{ book.created_date | naturaltime }}

{% trans "Updated:" %} {{ book.updated_date | naturaltime }}

{% trans "Last edited by:" %} {{ book.last_edited_by.display_name }}

+ {% endif %}
{% if form.non_field_errors %} @@ -26,7 +28,12 @@ {% endif %} +{% if book %}
+{% else %} + +{% endif %} + {% csrf_token %} {% if confirm_mode %}
@@ -111,12 +118,8 @@ {% endfor %} - {% if confirm_mode %} - - {% else %} - {% endif %}
diff --git a/bookwyrm/templates/search_results.html b/bookwyrm/templates/search_results.html index 4e8481f09..13497df83 100644 --- a/bookwyrm/templates/search_results.html +++ b/bookwyrm/templates/search_results.html @@ -68,6 +68,10 @@ {% include 'snippets/toggle/close_button.html' with text=button_text small=True controls_text="more-results" %} {% endif %} + + {% endif %}
diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index 25b44fb4a..0000015e5 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -130,12 +130,13 @@ urlpatterns = [ re_path(r'%s(.json)?/?$' % book_path, views.Book.as_view()), re_path(r'%s/edit/?$' % book_path, views.EditBook.as_view()), re_path(r'%s/confirm/?$' % book_path, views.ConfirmEditBook.as_view()), + re_path(r'^create-book/?$', views.EditBook.as_view()), + re_path(r'^create-book/confirm?$', views.ConfirmEditBook.as_view()), re_path(r'%s/editions(.json)?/?$' % book_path, views.Editions.as_view()), re_path(r'^upload-cover/(?P\d+)/?$', views.upload_cover), re_path(r'^add-description/(?P\d+)/?$', views.add_description), re_path(r'^resolve-book/?$', views.resolve_book), re_path(r'^switch-edition/?$', views.switch_edition), - re_path(r'^create-book/?$', views.EditBook.as_view()), # isbn re_path(r'^isbn/(?P\d+)(.json)?/?$', views.Isbn.as_view()), diff --git a/bookwyrm/views/books.py b/bookwyrm/views/books.py index 5f03e1a91..a115ac493 100644 --- a/bookwyrm/views/books.py +++ b/bookwyrm/views/books.py @@ -145,7 +145,7 @@ class EditBook(View): search=vector ).annotate( rank=SearchRank(vector, add_author) - ).filter(rank__gt=0.8).order_by('-rank')[:5] + ).filter(rank__gt=0.4).order_by('-rank')[:5] # we're creating a new book if not book: @@ -190,25 +190,33 @@ class ConfirmEditBook(View): if not form.is_valid(): return TemplateResponse(request, 'edit_book.html', data) - # create work, if needed - # TODO + with transaction.atomic(): + # save book + book = form.save() - # save book - book = form.save() + # get or create author as needed + if request.POST.get('add_author'): + if request.POST.get('author_match'): + author = get_object_or_404( + models.Author, id=request.POST['author_match']) + else: + author = models.Author.objects.create( + name=request.POST.get('add_author')) + book.authors.add(author) - # get or create author as needed - if request.POST.get('add_author'): - if request.POST.get('author_match'): - author = get_object_or_404( - models.Author, id=request.POST['author_match']) - else: - author = models.Author.objects.create( - name=request.POST.get('add_author')) - book.authors.add(author) + # create work, if needed + if not book_id: + work_match = request.POST.get('parent_work') + if work_match: + work = get_object_or_404(models.Work, id=work_match) + else: + work = models.Work.objects.create(title=form.cleaned_data.title) + work.authors.set(book.authors.all()) + book.parent_work = work + book.save() - remove_authors = request.POST.getlist('remove_authors') - for author_id in remove_authors: - book.authors.remove(author_id) + for author_id in request.POST.getlist('remove_authors'): + book.authors.remove(author_id) return redirect('/book/%s' % book.id) From acbebbe94768bdefe763424ff157a6f3a13059bd Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Mon, 8 Mar 2021 10:10:30 -0800 Subject: [PATCH 10/18] Formats code changes --- bookwyrm/forms.py | 1 - bookwyrm/tests/views/test_book.py | 45 ++++++++-------- bookwyrm/urls.py | 21 ++++---- bookwyrm/views/books.py | 87 +++++++++++++++---------------- bw-dev | 2 +- 5 files changed, 75 insertions(+), 81 deletions(-) diff --git a/bookwyrm/forms.py b/bookwyrm/forms.py index 09e13ae50..99c45ed22 100644 --- a/bookwyrm/forms.py +++ b/bookwyrm/forms.py @@ -138,7 +138,6 @@ class EditionForm(CustomForm): class Meta: model = models.Edition exclude = [ - "remote_id", "origin_id", "created_date", diff --git a/bookwyrm/tests/views/test_book.py b/bookwyrm/tests/views/test_book.py index feadf8622..024937691 100644 --- a/bookwyrm/tests/views/test_book.py +++ b/bookwyrm/tests/views/test_book.py @@ -85,14 +85,14 @@ class BookViews(TestCase): self.assertEqual(self.book.title, "New Title") def test_edit_book_add_author(self): - ''' lets a user edit a book with new authors ''' + """ lets a user edit a book with new authors """ view = views.EditBook.as_view() self.local_user.groups.add(self.group) form = forms.EditionForm(instance=self.book) - form.data['title'] = 'New Title' - form.data['last_edited_by'] = self.local_user.id - form.data['add_author'] = 'Sappho' - request = self.factory.post('', form.data) + form.data["title"] = "New Title" + form.data["last_edited_by"] = self.local_user.id + form.data["add_author"] = "Sappho" + request = self.factory.post("", form.data) request.user = self.local_user result = view(request, self.book.id) @@ -100,47 +100,46 @@ class BookViews(TestCase): # the changes haven't been saved yet self.book.refresh_from_db() - self.assertEqual(self.book.title, 'Example Edition') + self.assertEqual(self.book.title, "Example Edition") def test_edit_book_add_new_author_confirm(self): - ''' lets a user edit a book confirmed with new authors ''' + """ lets a user edit a book confirmed with new authors """ view = views.ConfirmEditBook.as_view() self.local_user.groups.add(self.group) form = forms.EditionForm(instance=self.book) - form.data['title'] = 'New Title' - form.data['last_edited_by'] = self.local_user.id - form.data['add_author'] = 'Sappho' - request = self.factory.post('', form.data) + form.data["title"] = "New Title" + form.data["last_edited_by"] = self.local_user.id + form.data["add_author"] = "Sappho" + request = self.factory.post("", form.data) request.user = self.local_user - with patch('bookwyrm.models.activitypub_mixin.broadcast_task.delay'): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): view(request, self.book.id) self.book.refresh_from_db() - self.assertEqual(self.book.title, 'New Title') - self.assertEqual(self.book.authors.first().name, 'Sappho') + self.assertEqual(self.book.title, "New Title") + self.assertEqual(self.book.authors.first().name, "Sappho") def test_edit_book_remove_author(self): - ''' remove an author from a book ''' - author = models.Author.objects.create(name='Sappho') + """ remove an author from a book """ + author = models.Author.objects.create(name="Sappho") self.book.authors.add(author) form = forms.EditionForm(instance=self.book) view = views.EditBook.as_view() self.local_user.groups.add(self.group) form = forms.EditionForm(instance=self.book) - form.data['title'] = 'New Title' - form.data['last_edited_by'] = self.local_user.id - form.data['remove_authors'] = [author.id] - request = self.factory.post('', form.data) + form.data["title"] = "New Title" + form.data["last_edited_by"] = self.local_user.id + form.data["remove_authors"] = [author.id] + request = self.factory.post("", form.data) request.user = self.local_user - with patch('bookwyrm.models.activitypub_mixin.broadcast_task.delay'): + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): view(request, self.book.id) self.book.refresh_from_db() - self.assertEqual(self.book.title, 'New Title') + self.assertEqual(self.book.title, "New Title") self.assertFalse(self.book.authors.exists()) - def test_switch_edition(self): """ updates user's relationships to a book """ work = models.Work.objects.create(title="test work") diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index 246ac8af3..ed752e75a 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -117,17 +117,16 @@ urlpatterns = [ re_path(r"^boost/(?P\d+)/?$", views.Boost.as_view()), re_path(r"^unboost/(?P\d+)/?$", views.Unboost.as_view()), # books - re_path(r'%s(.json)?/?$' % book_path, views.Book.as_view()), - re_path(r'%s/edit/?$' % book_path, views.EditBook.as_view()), - re_path(r'%s/confirm/?$' % book_path, views.ConfirmEditBook.as_view()), - re_path(r'^create-book/?$', views.EditBook.as_view()), - re_path(r'^create-book/confirm?$', views.ConfirmEditBook.as_view()), - re_path(r'%s/editions(.json)?/?$' % book_path, views.Editions.as_view()), - re_path(r'^upload-cover/(?P\d+)/?$', views.upload_cover), - re_path(r'^add-description/(?P\d+)/?$', views.add_description), - re_path(r'^resolve-book/?$', views.resolve_book), - re_path(r'^switch-edition/?$', views.switch_edition), - + re_path(r"%s(.json)?/?$" % book_path, views.Book.as_view()), + re_path(r"%s/edit/?$" % book_path, views.EditBook.as_view()), + re_path(r"%s/confirm/?$" % book_path, views.ConfirmEditBook.as_view()), + re_path(r"^create-book/?$", views.EditBook.as_view()), + re_path(r"^create-book/confirm?$", views.ConfirmEditBook.as_view()), + re_path(r"%s/editions(.json)?/?$" % book_path, views.Editions.as_view()), + re_path(r"^upload-cover/(?P\d+)/?$", views.upload_cover), + re_path(r"^add-description/(?P\d+)/?$", views.add_description), + re_path(r"^resolve-book/?$", views.resolve_book), + re_path(r"^switch-edition/?$", views.switch_edition), # isbn re_path(r"^isbn/(?P\d+)(.json)?/?$", views.Isbn.as_view()), # author diff --git a/bookwyrm/views/books.py b/bookwyrm/views/books.py index 7c3ec3487..55cc75b43 100644 --- a/bookwyrm/views/books.py +++ b/bookwyrm/views/books.py @@ -106,107 +106,104 @@ class Book(View): permission_required("bookwyrm.edit_book", raise_exception=True), name="dispatch" ) class EditBook(View): - ''' edit a book ''' + """ edit a book """ + def get(self, request, book_id=None): - ''' info about a book ''' + """ info about a book """ book = None if book_id: book = get_edition(book_id) if not book.description: book.description = book.parent_work.description - data = { - 'book': book, - 'form': forms.EditionForm(instance=book) - } - return TemplateResponse(request, 'edit_book.html', data) + data = {"book": book, "form": forms.EditionForm(instance=book)} + return TemplateResponse(request, "edit_book.html", data) def post(self, request, book_id=None): - ''' edit a book cool ''' + """ edit a book cool """ # returns None if no match is found book = models.Edition.objects.filter(id=book_id).first() form = forms.EditionForm(request.POST, request.FILES, instance=book) - data = { - 'book': book, - 'form': form - } + data = {"book": book, "form": form} if not form.is_valid(): - return TemplateResponse(request, 'edit_book.html', data) + return TemplateResponse(request, "edit_book.html", data) - add_author = request.POST.get('add_author') + add_author = request.POST.get("add_author") # we're adding an author through a free text field if add_author: - data['add_author'] = add_author + data["add_author"] = add_author # check for existing authors - vector = SearchVector('name', weight='A') +\ - SearchVector('aliases', weight='B') + vector = SearchVector("name", weight="A") + SearchVector( + "aliases", weight="B" + ) - data['author_matches'] = models.Author.objects.annotate( - search=vector - ).annotate( - rank=SearchRank(vector, add_author) - ).filter(rank__gt=0.4).order_by('-rank')[:5] + data["author_matches"] = ( + models.Author.objects.annotate(search=vector) + .annotate(rank=SearchRank(vector, add_author)) + .filter(rank__gt=0.4) + .order_by("-rank")[:5] + ) # we're creating a new book if not book: # check if this is an edition of an existing work author_text = book.author_text if book else add_author - data['book_matches'] = connector_manager.local_search( - '%s %s' % (form.cleaned_data.get('title'), author_text), + data["book_matches"] = connector_manager.local_search( + "%s %s" % (form.cleaned_data.get("title"), author_text), min_confidence=0.5, - raw=True + raw=True, )[:5] # either of the above cases requires additional confirmation if add_author or not book: # creting a book or adding an author to a book needs another step - data['confirm_mode'] = True - return TemplateResponse(request, 'edit_book.html', data) + data["confirm_mode"] = True + return TemplateResponse(request, "edit_book.html", data) - remove_authors = request.POST.getlist('remove_authors') + remove_authors = request.POST.getlist("remove_authors") for author_id in remove_authors: book.authors.remove(author_id) book = form.save() - return redirect('/book/%s' % book.id) + return redirect("/book/%s" % book.id) -@method_decorator(login_required, name='dispatch') +@method_decorator(login_required, name="dispatch") @method_decorator( - permission_required('bookwyrm.edit_book', raise_exception=True), - name='dispatch') + permission_required("bookwyrm.edit_book", raise_exception=True), name="dispatch" +) class ConfirmEditBook(View): - ''' confirm edits to a book ''' + """ confirm edits to a book """ + def post(self, request, book_id=None): - ''' edit a book cool ''' + """ edit a book cool """ # returns None if no match is found book = models.Edition.objects.filter(id=book_id).first() form = forms.EditionForm(request.POST, request.FILES, instance=book) - data = { - 'book': book, - 'form': form - } + data = {"book": book, "form": form} if not form.is_valid(): - return TemplateResponse(request, 'edit_book.html', data) + return TemplateResponse(request, "edit_book.html", data) with transaction.atomic(): # save book book = form.save() # get or create author as needed - if request.POST.get('add_author'): - if request.POST.get('author_match'): + if request.POST.get("add_author"): + if request.POST.get("author_match"): author = get_object_or_404( - models.Author, id=request.POST['author_match']) + models.Author, id=request.POST["author_match"] + ) else: author = models.Author.objects.create( - name=request.POST.get('add_author')) + name=request.POST.get("add_author") + ) book.authors.add(author) # create work, if needed if not book_id: - work_match = request.POST.get('parent_work') + work_match = request.POST.get("parent_work") if work_match: work = get_object_or_404(models.Work, id=work_match) else: @@ -215,7 +212,7 @@ class ConfirmEditBook(View): book.parent_work = work book.save() - for author_id in request.POST.getlist('remove_authors'): + for author_id in request.POST.getlist("remove_authors"): book.authors.remove(author_id) return redirect("/book/%s" % book.id) diff --git a/bw-dev b/bw-dev index 74c42fbbc..712b80287 100755 --- a/bw-dev +++ b/bw-dev @@ -36,7 +36,7 @@ function initdb { } function makeitblack { - runweb black celerywyrm bookwyrm + docker-compose run --rm web black celerywyrm bookwyrm } CMD=$1 From 58b48faff870723a8aba0bf745756c82c266c6ed Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Mon, 8 Mar 2021 10:48:45 -0800 Subject: [PATCH 11/18] Tests create books flow --- bookwyrm/tests/views/test_book.py | 29 +++++++++++++++++++++++++++++ bookwyrm/views/books.py | 3 +-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/bookwyrm/tests/views/test_book.py b/bookwyrm/tests/views/test_book.py index 024937691..90b7359d4 100644 --- a/bookwyrm/tests/views/test_book.py +++ b/bookwyrm/tests/views/test_book.py @@ -140,6 +140,35 @@ class BookViews(TestCase): self.assertEqual(self.book.title, "New Title") self.assertFalse(self.book.authors.exists()) + def test_create_book(self): + """ create an entirely new book and work """ + view = views.ConfirmEditBook.as_view() + self.local_user.groups.add(self.group) + form = forms.EditionForm() + form.data["title"] = "New Title" + form.data["last_edited_by"] = self.local_user.id + request = self.factory.post("", form.data) + request.user = self.local_user + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + view(request) + book = models.Edition.objects.get(title="New Title") + self.assertEqual(book.parent_work.title, "New Title") + + def test_create_book_existing_work(self): + """ create an entirely new book and work """ + view = views.ConfirmEditBook.as_view() + self.local_user.groups.add(self.group) + form = forms.EditionForm() + form.data["title"] = "New Title" + form.data["parent_work"] = self.work.id + form.data["last_edited_by"] = self.local_user.id + request = self.factory.post("", form.data) + request.user = self.local_user + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + view(request) + book = models.Edition.objects.get(title="New Title") + self.assertEqual(book.parent_work, self.work) + def test_switch_edition(self): """ updates user's relationships to a book """ work = models.Work.objects.create(title="test work") diff --git a/bookwyrm/views/books.py b/bookwyrm/views/books.py index 55cc75b43..ecba43766 100644 --- a/bookwyrm/views/books.py +++ b/bookwyrm/views/books.py @@ -1,5 +1,4 @@ """ the good stuff! the books! """ -from django.core.paginator import Paginator from django.contrib.auth.decorators import login_required, permission_required from django.contrib.postgres.search import SearchRank, SearchVector from django.core.paginator import Paginator @@ -207,7 +206,7 @@ class ConfirmEditBook(View): if work_match: work = get_object_or_404(models.Work, id=work_match) else: - work = models.Work.objects.create(title=form.cleaned_data.title) + work = models.Work.objects.create(title=form.cleaned_data["title"]) work.authors.set(book.authors.all()) book.parent_work = work book.save() From 37e29cc735f8b4c01fb225ab78265656f145bca7 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Mon, 8 Mar 2021 11:11:05 -0800 Subject: [PATCH 12/18] Adds tests of creating book with author --- bookwyrm/tests/views/test_book.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/bookwyrm/tests/views/test_book.py b/bookwyrm/tests/views/test_book.py index 90b7359d4..bba83714e 100644 --- a/bookwyrm/tests/views/test_book.py +++ b/bookwyrm/tests/views/test_book.py @@ -169,6 +169,23 @@ class BookViews(TestCase): book = models.Edition.objects.get(title="New Title") self.assertEqual(book.parent_work, self.work) + def test_create_book_with_author(self): + """ create an entirely new book and work """ + view = views.ConfirmEditBook.as_view() + self.local_user.groups.add(self.group) + form = forms.EditionForm() + form.data["title"] = "New Title" + form.data["add_author"] = "Sappho" + form.data["last_edited_by"] = self.local_user.id + request = self.factory.post("", form.data) + request.user = self.local_user + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + view(request) + book = models.Edition.objects.get(title="New Title") + self.assertEqual(book.parent_work.title, "New Title") + self.assertEqual(book.authors.first().name, "Sappho") + self.assertEqual(book.authors.first(), book.parent_work.authors.first()) + def test_switch_edition(self): """ updates user's relationships to a book """ work = models.Work.objects.create(title="test work") From 500394fc52bc5b75393d5632fa6412c01fa244ac Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Mon, 8 Mar 2021 11:54:26 -0800 Subject: [PATCH 13/18] Make sure creating books doesn't broadcast in tests --- bookwyrm/tests/views/test_book.py | 12 ++++++------ bookwyrm/views/books.py | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/bookwyrm/tests/views/test_book.py b/bookwyrm/tests/views/test_book.py index bba83714e..1549bdc64 100644 --- a/bookwyrm/tests/views/test_book.py +++ b/bookwyrm/tests/views/test_book.py @@ -149,8 +149,8 @@ class BookViews(TestCase): form.data["last_edited_by"] = self.local_user.id request = self.factory.post("", form.data) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): - view(request) + + view(request) book = models.Edition.objects.get(title="New Title") self.assertEqual(book.parent_work.title, "New Title") @@ -164,8 +164,8 @@ class BookViews(TestCase): form.data["last_edited_by"] = self.local_user.id request = self.factory.post("", form.data) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): - view(request) + + view(request) book = models.Edition.objects.get(title="New Title") self.assertEqual(book.parent_work, self.work) @@ -179,8 +179,8 @@ class BookViews(TestCase): form.data["last_edited_by"] = self.local_user.id request = self.factory.post("", form.data) request.user = self.local_user - with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): - view(request) + + view(request) book = models.Edition.objects.get(title="New Title") self.assertEqual(book.parent_work.title, "New Title") self.assertEqual(book.authors.first().name, "Sappho") diff --git a/bookwyrm/views/books.py b/bookwyrm/views/books.py index ecba43766..ae60c677d 100644 --- a/bookwyrm/views/books.py +++ b/bookwyrm/views/books.py @@ -209,7 +209,8 @@ class ConfirmEditBook(View): work = models.Work.objects.create(title=form.cleaned_data["title"]) work.authors.set(book.authors.all()) book.parent_work = work - book.save() + # we don't tell the world when creating a book + book.save(broadcast=False) for author_id in request.POST.getlist("remove_authors"): book.authors.remove(author_id) From a29d6a5f16ba1f0f75d0d283c4d01917bc0e4d82 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Mon, 8 Mar 2021 14:11:08 -0800 Subject: [PATCH 14/18] Hide secondary save button in confirm mode --- bookwyrm/templates/edit_book.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bookwyrm/templates/edit_book.html b/bookwyrm/templates/edit_book.html index 1ebcc6b9a..700dc5704 100644 --- a/bookwyrm/templates/edit_book.html +++ b/bookwyrm/templates/edit_book.html @@ -72,7 +72,6 @@
{% endif %} -
@@ -181,11 +180,12 @@
+ {% if not confirm_mode %}
{% trans "Cancel" %}
-
+ {% endif %} {% endblock %} From 965d84f86f4e9bef3eabeac6512058183229c462 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 11 Mar 2021 15:41:12 -0800 Subject: [PATCH 15/18] Fixes creating news works --- bookwyrm/templates/edit_book.html | 2 +- bookwyrm/views/books.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bookwyrm/templates/edit_book.html b/bookwyrm/templates/edit_book.html index 700dc5704..e42f77f2d 100644 --- a/bookwyrm/templates/edit_book.html +++ b/bookwyrm/templates/edit_book.html @@ -60,7 +60,7 @@ {% for match in book_matches %} {% endfor %} - + {% endif %}
diff --git a/bookwyrm/views/books.py b/bookwyrm/views/books.py index ae60c677d..9048f43dd 100644 --- a/bookwyrm/views/books.py +++ b/bookwyrm/views/books.py @@ -203,7 +203,7 @@ class ConfirmEditBook(View): # create work, if needed if not book_id: work_match = request.POST.get("parent_work") - if work_match: + if work_match and work_match != "0": work = get_object_or_404(models.Work, id=work_match) else: work = models.Work.objects.create(title=form.cleaned_data["title"]) From c1976dbd62bf5491b4e5036e2005ddd1ba61db12 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 11 Mar 2021 16:33:49 -0800 Subject: [PATCH 16/18] Add multiple authors --- bookwyrm/templates/edit_book.html | 55 ++++++++++++++++++------------- bookwyrm/views/books.py | 47 +++++++++++++++----------- 2 files changed, 61 insertions(+), 41 deletions(-) diff --git a/bookwyrm/templates/edit_book.html b/bookwyrm/templates/edit_book.html index e42f77f2d..107e75c56 100644 --- a/bookwyrm/templates/edit_book.html +++ b/bookwyrm/templates/edit_book.html @@ -39,29 +39,37 @@

{% trans "Confirm Book Info" %}

- {% if author_matches.exists %} -
- {% blocktrans with name=add_author %}Is "{{ name }}" an existing author?{% endblocktrans %} - {% for match in author_matches %} - -

- {% blocktrans with book_title=match.book_set.first.title %}Author of {{ book_title }}{% endblocktrans %} -

+ {% if author_matches %} +
+ {% for author in author_matches %} +
+ {% blocktrans with name=author.name %}Is "{{ name }}" an existing author?{% endblocktrans %} + {% with forloop.counter as counter %} + {% for match in author.matches %} + +

+ {% blocktrans with book_title=match.book_set.first.title %}Author of {{ book_title }}{% endblocktrans %} +

+ {% endfor %} + + {% endwith %} +
{% endfor %} - -
+
{% else %}

{% blocktrans with name=add_author %}Creating a new author: {{ name }}{% endblocktrans %}

{% endif %} {% if not book %} -
- {% trans "Is this an editions of an existing work?" %} - {% for match in book_matches %} - - {% endfor %} - -
+
+
+ {% trans "Is this an edition of an existing work?" %} + {% for match in book_matches %} + + {% endfor %} + +
+
{% endif %}
@@ -109,16 +117,19 @@

{% trans "Authors" %}

+ {% if book.authors.exists %}
{% for author in book.authors.all %} -

{{ author.name }} -

- - + {% endif %} + +

Separate multiple author names with commas.

+
diff --git a/bookwyrm/views/books.py b/bookwyrm/views/books.py index 9048f43dd..ff0d67647 100644 --- a/bookwyrm/views/books.py +++ b/bookwyrm/views/books.py @@ -131,17 +131,22 @@ class EditBook(View): # we're adding an author through a free text field if add_author: data["add_author"] = add_author - # check for existing authors - vector = SearchVector("name", weight="A") + SearchVector( - "aliases", weight="B" - ) + data['author_matches'] = [] + for author in add_author.split(','): + # check for existing authors + vector = SearchVector("name", weight="A") + SearchVector( + "aliases", weight="B" + ) - data["author_matches"] = ( - models.Author.objects.annotate(search=vector) - .annotate(rank=SearchRank(vector, add_author)) - .filter(rank__gt=0.4) - .order_by("-rank")[:5] - ) + data["author_matches"].append({ + 'name': author.strip(), + 'matches': ( + models.Author.objects.annotate(search=vector) + .annotate(rank=SearchRank(vector, add_author)) + .filter(rank__gt=0.4) + .order_by("-rank")[:5] + ) + }) # we're creating a new book if not book: @@ -157,6 +162,8 @@ class EditBook(View): if add_author or not book: # creting a book or adding an author to a book needs another step data["confirm_mode"] = True + # this isn't preserved because it isn't part of the form obj + data["remove_authors"] = request.POST.getlist("remove_authors") return TemplateResponse(request, "edit_book.html", data) remove_authors = request.POST.getlist("remove_authors") @@ -190,15 +197,17 @@ class ConfirmEditBook(View): # get or create author as needed if request.POST.get("add_author"): - if request.POST.get("author_match"): - author = get_object_or_404( - models.Author, id=request.POST["author_match"] - ) - else: - author = models.Author.objects.create( - name=request.POST.get("add_author") - ) - book.authors.add(author) + for (i, author) in enumerate(request.POST.get("add_author").split(',')): + match = request.POST.get("author_match-%d" % i) + if match and match != "0": + author = get_object_or_404( + models.Author, id=request.POST["author_match-%d" % i] + ) + else: + author = models.Author.objects.create( + name=author.strip() + ) + book.authors.add(author) # create work, if needed if not book_id: From 28db3e2733d57e9b7a5988e22d2e6c376587496a Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 11 Mar 2021 16:40:35 -0800 Subject: [PATCH 17/18] Formatting --- bookwyrm/views/books.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/bookwyrm/views/books.py b/bookwyrm/views/books.py index ff0d67647..1cb21f5e7 100644 --- a/bookwyrm/views/books.py +++ b/bookwyrm/views/books.py @@ -131,22 +131,24 @@ class EditBook(View): # we're adding an author through a free text field if add_author: data["add_author"] = add_author - data['author_matches'] = [] - for author in add_author.split(','): + data["author_matches"] = [] + for author in add_author.split(","): # check for existing authors vector = SearchVector("name", weight="A") + SearchVector( "aliases", weight="B" ) - data["author_matches"].append({ - 'name': author.strip(), - 'matches': ( - models.Author.objects.annotate(search=vector) - .annotate(rank=SearchRank(vector, add_author)) - .filter(rank__gt=0.4) - .order_by("-rank")[:5] - ) - }) + data["author_matches"].append( + { + "name": author.strip(), + "matches": ( + models.Author.objects.annotate(search=vector) + .annotate(rank=SearchRank(vector, add_author)) + .filter(rank__gt=0.4) + .order_by("-rank")[:5] + ), + } + ) # we're creating a new book if not book: @@ -197,16 +199,14 @@ class ConfirmEditBook(View): # get or create author as needed if request.POST.get("add_author"): - for (i, author) in enumerate(request.POST.get("add_author").split(',')): + for (i, author) in enumerate(request.POST.get("add_author").split(",")): match = request.POST.get("author_match-%d" % i) if match and match != "0": author = get_object_or_404( models.Author, id=request.POST["author_match-%d" % i] ) else: - author = models.Author.objects.create( - name=author.strip() - ) + author = models.Author.objects.create(name=author.strip()) book.authors.add(author) # create work, if needed From b42c761b0b986402c973d53697e937cd692e1adf Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 12 Mar 2021 09:46:28 -0800 Subject: [PATCH 18/18] Safer author add logic --- bookwyrm/templates/edit_book.html | 6 ++++-- bookwyrm/views/books.py | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/bookwyrm/templates/edit_book.html b/bookwyrm/templates/edit_book.html index 107e75c56..61f04e370 100644 --- a/bookwyrm/templates/edit_book.html +++ b/bookwyrm/templates/edit_book.html @@ -74,7 +74,9 @@ - + + {% trans "Back" %} +
@@ -129,7 +131,7 @@ {% endif %}

Separate multiple author names with commas.

- + diff --git a/bookwyrm/views/books.py b/bookwyrm/views/books.py index 1cb21f5e7..0a11b87cf 100644 --- a/bookwyrm/views/books.py +++ b/bookwyrm/views/books.py @@ -133,6 +133,8 @@ class EditBook(View): data["add_author"] = add_author data["author_matches"] = [] for author in add_author.split(","): + if not author: + continue # check for existing authors vector = SearchVector("name", weight="A") + SearchVector( "aliases", weight="B" @@ -200,6 +202,8 @@ class ConfirmEditBook(View): # get or create author as needed if request.POST.get("add_author"): for (i, author) in enumerate(request.POST.get("add_author").split(",")): + if not author: + continue match = request.POST.get("author_match-%d" % i) if match and match != "0": author = get_object_or_404(