Associate suggestions with works instead of editions

This commit is contained in:
Mouse Reeve 2024-08-27 11:31:05 -07:00
parent 299ac0631d
commit 5deb779efd
13 changed files with 47 additions and 58 deletions

View file

@ -1,13 +0,0 @@
# Generated by Django 4.2.15 on 2024-08-27 14:53
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0209_suggestionlist_alter_listitem_options_and_more"),
("bookwyrm", "0209_user_show_ratings"),
]
operations = []

View file

@ -1,4 +1,4 @@
# Generated by Django 4.2.15 on 2024-08-27 01:20 # Generated by Django 4.2.15 on 2024-08-27 17:27
import bookwyrm.models.activitypub_mixin import bookwyrm.models.activitypub_mixin
import bookwyrm.models.fields import bookwyrm.models.fields
@ -10,7 +10,7 @@ import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("bookwyrm", "0208_merge_0207_merge_20240629_0626_0207_sqlparse_update"), ("bookwyrm", "0209_user_show_ratings"),
] ]
operations = [ operations = [
@ -140,7 +140,7 @@ class Migration(migrations.Migration):
field=bookwyrm.models.fields.OneToOneField( field=bookwyrm.models.fields.OneToOneField(
on_delete=django.db.models.deletion.PROTECT, on_delete=django.db.models.deletion.PROTECT,
related_name="suggestion_list", related_name="suggestion_list",
to="bookwyrm.edition", to="bookwyrm.work",
), ),
), ),
migrations.AddField( migrations.AddField(

View file

@ -63,7 +63,7 @@ class SuggestionList(AbstractList):
) )
suggests_for = fields.OneToOneField( suggests_for = fields.OneToOneField(
"Edition", "Work",
on_delete=models.PROTECT, on_delete=models.PROTECT,
activitypub_field="book", activitypub_field="book",
related_name="suggestion_list", related_name="suggestion_list",

View file

@ -423,7 +423,7 @@
</div> </div>
<section class="block"> <section class="block">
{% include "book/suggestion_list/list.html" %} {% include "book/suggestion_list/list.html" with list=suggstion_list %}
</section> </section>
{% endwith %} {% endwith %}
{% endblock %} {% endblock %}

View file

@ -1,5 +1,5 @@
{% load i18n %} {% load i18n %}
<form class="" action="{% url "suggestion-endorse" book.id item.id %}" method="POST"> <form class="" action="{% url "suggestion-endorse" work.id item.id %}" method="POST">
{% csrf_token %} {% csrf_token %}
<button class="tag is-info {% if request.user not in item.endorsement.all and request.user != item.user %}is-light{% endif %}" type="submit" {% if request.user == item.user %}disabled{% endif %}> <button class="tag is-info {% if request.user not in item.endorsement.all and request.user != item.user %}is-light{% endif %}" type="submit" {% if request.user == item.user %}disabled{% endif %}>
<span class="icon icon-star-empty" aria-label="{% trans 'Endorsements' %}"></span> <span class="icon icon-star-empty" aria-label="{% trans 'Endorsements' %}"></span>

View file

@ -3,38 +3,35 @@
<h2 class="title is-3" id="suggestions-section"> <h2 class="title is-3" id="suggestions-section">
{% trans "Suggestions" %} {% trans "Suggestions" %}
<a class="help has-text-weight-normal" href="{% url 'suggestion-list' book_id=book.id %}"> <a class="help has-text-weight-normal" href="{% url 'suggestion-list' book_id=work.id %}">
{% blocktrans trimmed with count=book.suggestion_list.suggestionlistitem_set.count|intcomma %} {% trans "View all suggestions" %}
View all {{ count }} suggestions
{% endblocktrans %}
</a> </a>
</h2> </h2>
{% if book.suggestion_list %} {% if suggestion_list %}
<p class="subtitle"> <p class="subtitle">
{% blocktrans trimmed with title=book.title %} {% blocktrans trimmed with title=book.title %}
Readers who liked <em>{{ title }}</em> recommend giving these books a try: Readers who liked <em>{{ title }}</em> recommend giving these books a try:
{% endblocktrans %} {% endblocktrans %}
</p> </p>
{% if items|length == 0 %} {% if items|length == 0 %}
<section class="section has-background-light is-flex is-flex-direction-column is-align-items-center"> <section class="section has-background-light is-flex is-flex-direction-column is-align-items-center">
<div class="column is-full is-centered"> <div class="column is-full is-centered">
{% include "book/suggestion_list/search.html" %} {% include "book/suggestion_list/search.html" with list=suggestion_list is_suggestion=True %}
</div> </div>
</section> </section>
{% else %} {% else %}
<ol class="columns is-multiline"> <ol class="columns is-multiline">
{% for item in items %} {% for item in items %}
<li class="mb-5 column is-one-third"> <li class="mb-5 column is-one-third">
{% include "book/suggestion_list/book_card.html" %} {% include "book/suggestion_list/book_card.html" with list=suggestion_list %}
</li> </li>
{% endfor %} {% endfor %}
</ol> </ol>
<div class="mb-5 column is-full"> <div class="mb-5 column is-full">
{% include "book/suggestion_list/search.html" %} {% include "book/suggestion_list/search.html" with list=suggestion_list is_suggestion=True %}
</div> </div>
{% endif %} {% endif %}
{% else %} {% else %}
@ -42,7 +39,7 @@
<form name="create-list" method="post" action="{% url 'suggestion-list' book_id=book.id %}#suggestions-section" class="has-text-centered"> <form name="create-list" method="post" action="{% url 'suggestion-list' book_id=book.id %}#suggestions-section" class="has-text-centered">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="user" value="{{ request.user.id }}"> <input type="hidden" name="user" value="{{ request.user.id }}">
<input type="hidden" name="suggests_for" value="{{ book.id }}"> <input type="hidden" name="suggests_for" value="{{ work.id }}">
<button type="submit" class="button is-medium">{% trans "Create suggestion list" %}</button> <button type="submit" class="button is-medium">{% trans "Create suggestion list" %}</button>
</form> </form>
</section> </section>

View file

@ -2,7 +2,6 @@
{% load utilities %} {% load utilities %}
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
{% with book.suggestion_list as list %}
<details class="details-panel box" {% if query %}open{% endif %} id="add-suggestions"> <details class="details-panel box" {% if query %}open{% endif %} id="add-suggestions">
<summary> <summary>
<span class="title is-5" role="heading" aria-level="3"> <span class="title is-5" role="heading" aria-level="3">
@ -10,11 +9,9 @@
</span> </span>
<span class="details-close icon icon-x" aria-hidden="true"></span> <span class="details-close icon icon-x" aria-hidden="true"></span>
</summary> </summary>
{% url 'book' book_id=book.id as partial_url %} {% with search_url=request.path|add:"#add-suggestions" %}
{% with search_url=partial_url|add:"#add-suggestions" %}
{% include "lists/suggestion_search.html" with is_suggestion=True query_param="suggestion_query" columns=True %} {% include "lists/suggestion_search.html" with is_suggestion=True query_param="suggestion_query" columns=True %}
{% endwith %} {% endwith %}
</details> </details>
{% endwith %}
{% endif %} {% endif %}

View file

@ -4,7 +4,7 @@
{% load group_tags %} {% load group_tags %}
{% block modal-title %} {% block modal-title %}
{% if list.suggests_for or list.curation == 'open' or request.user == list.user or list.group|is_member:request.user %} {% if is_suggestion or list.curation == 'open' or request.user == list.user or list.group|is_member:request.user %}
{% blocktrans trimmed with title=book|book_title %} {% blocktrans trimmed with title=book|book_title %}
Add "<em>{{ title }}</em>" to this list Add "<em>{{ title }}</em>" to this list
{% endblocktrans %} {% endblocktrans %}
@ -19,10 +19,10 @@
<form <form
name="add-book-{{ book.id }}" name="add-book-{{ book.id }}"
method="POST" method="POST"
{% if list.suggests_for %} {% if is_suggestion %}
action="{% url 'book-add-suggestion' book_id=list.suggests_for.id %}{% if query %}?suggestion_query={{ query }}#suggestions-section{% endif %}" action="{% url 'book-add-suggestion' book_id=list.suggests_for.id %}#suggestions-section"
{% else %} {% else %}
action="{% url 'list-add-book' %}{% if query %}?q={{ query }}{% endif %}" action="{% url 'list-add-book' %}"
{% endif %} {% endif %}
> >
{% endblock %} {% endblock %}
@ -39,7 +39,7 @@
<div class="buttons is-right is-flex-grow-1"> <div class="buttons is-right is-flex-grow-1">
<button type="button" class="button" data-modal-close>{% trans "Cancel" %}</button> <button type="button" class="button" data-modal-close>{% trans "Cancel" %}</button>
<button type="submit" class="button is-link"> <button type="submit" class="button is-link">
{% if list.suggests_for or list.curation == 'open' or request.user == list.user or list.group|is_member:request.user %} {% if is_suggestion or list.curation == 'open' or request.user == list.user or list.group|is_member:request.user %}
{% trans "Add" %} {% trans "Add" %}
{% else %} {% else %}
{% trans "Suggest" %} {% trans "Suggest" %}

View file

@ -93,7 +93,7 @@
</div> </div>
{% if list.suggests_for %} {% if list.suggests_for %}
<div class="card-footer-item"> <div class="card-footer-item">
{% include "book/suggestion_list/endorsement_button.html" with book=list.suggests_for %} {% include "book/suggestion_list/endorsement_button.html" with work=list.suggests_for %}
</div> </div>
{% endif %} {% endif %}
{% if list.user == request.user or list.group|is_member:request.user %} {% if list.user == request.user or list.group|is_member:request.user %}
@ -172,7 +172,7 @@
{% trans "Suggest Books" %} {% trans "Suggest Books" %}
{% endif %} {% endif %}
</h2> </h2>
{% include "lists/suggestion_search.html" with query_param="q" search_url=add_book_url %} {% include "lists/suggestion_search.html" with query_param="q" search_url=request.path %}
{% endif %} {% endif %}
<div> <div>
<h2 class="title is-5 mt-6" id="embed-label"> <h2 class="title is-5 mt-6" id="embed-label">

View file

@ -48,13 +48,13 @@
class="button is-small is-link" class="button is-small is-link"
data-modal-open="{{ modal_id }}" data-modal-open="{{ modal_id }}"
> >
{% if list.suggests_for or list.curation == 'open' or request.user == list.user or list.group|is_member:request.user %} {% if is_suggestion or list.curation == 'open' or request.user == list.user or list.group|is_member:request.user %}
{% trans "Add" %} {% trans "Add" %}
{% else %} {% else %}
{% trans "Suggest" %} {% trans "Suggest" %}
{% endif %} {% endif %}
</button> </button>
{% include "lists/add_item_modal.html" with id=modal_id is_suggestion=is_suggestion %} {% include "lists/add_item_modal.html" with id=modal_id is_suggestion=is_suggestion list=list %}
</div> </div>
</div> </div>
</div> </div>

View file

@ -90,6 +90,7 @@ class Book(View):
) )
data = { data = {
"book": book, "book": book,
"work": book.parent_work,
"statuses": paginated.get_page(request.GET.get("page")), "statuses": paginated.get_page(request.GET.get("page")),
"review_count": reviews.count(), "review_count": reviews.count(),
"ratings": ( "ratings": (
@ -135,21 +136,22 @@ class Book(View):
"comment_count": book.comment_set.filter(**filters).count(), "comment_count": book.comment_set.filter(**filters).count(),
"quotation_count": book.quotation_set.filter(**filters).count(), "quotation_count": book.quotation_set.filter(**filters).count(),
} }
if hasattr(book, "suggestion_list"): if hasattr(book.parent_work, "suggestion_list"):
suggestion_list = book.parent_work.suggestion_list
data["suggestion_list"] = suggestion_list
data["items"] = ( data["items"] = (
book.suggestion_list.suggestionlistitem_set.prefetch_related( suggestion_list.suggestionlistitem_set.prefetch_related(
"user", "book", "book__authors" "user", "book", "book__authors", "endorsement"
) )
.annotate(endorsement_count=Count("endorsement")) .annotate(endorsement_count=Count("endorsement"))
.order_by("-endorsement_count")[:3] .order_by("-endorsement_count")[:3]
) )
data["suggested_books"] = get_list_suggestions( data["suggested_books"] = get_list_suggestions(
book.suggestion_list, suggestion_list,
request.user, request.user,
query=request.GET.get("suggestion_query", ""), query=request.GET.get("suggestion_query", ""),
ignore_book=book, ignore_book=book.parent_work,
) )
return TemplateResponse(request, "book/book.html", data) return TemplateResponse(request, "book/book.html", data)

View file

@ -112,13 +112,13 @@ def get_list_suggestions(
query, query,
filters=[ filters=[
~Q(parent_work__editions__in=book_list.books.all()), ~Q(parent_work__editions__in=book_list.books.all()),
~Q(parent_work__editions__in=[ignore_book]), ~Q(parent_work=ignore_book),
], ],
) )
# just suggest whatever books are nearby # just suggest whatever books are nearby
suggestions = ( suggestions = (
user.shelfbook_set.filter(~Q(book__in=book_list.books.all())) user.shelfbook_set.filter(~Q(book__in=book_list.books.all()))
.exclude(book=ignore_book) .exclude(book__parent_work=ignore_book)
.distinct()[:num_suggestions] .distinct()[:num_suggestions]
) )
suggestions = [s.book for s in suggestions[:num_suggestions]] suggestions = [s.book for s in suggestions[:num_suggestions]]
@ -127,7 +127,7 @@ def get_list_suggestions(
s.default_edition s.default_edition
for s in models.Work.objects.filter( for s in models.Work.objects.filter(
~Q(editions__in=book_list.books.all()), ~Q(editions__in=book_list.books.all()),
~Q(editions__in=[ignore_book]), ~Q(id=ignore_book.id),
) )
.distinct() .distinct()
.order_by("-updated_date")[:num_suggestions] .order_by("-updated_date")[:num_suggestions]

View file

@ -2,8 +2,8 @@
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator from django.core.paginator import Paginator
from django.db import transaction from django.db import transaction
from django.db.models import Count from django.db.models import Count, Q
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.urls import reverse from django.urls import reverse
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
@ -26,7 +26,13 @@ class SuggestionList(View):
add_failed = kwargs.get("add_failed", False) add_failed = kwargs.get("add_failed", False)
add_succeeded = kwargs.get("add_succeeded", False) add_succeeded = kwargs.get("add_succeeded", False)
book_list = get_object_or_404(models.SuggestionList, suggests_for=book_id) work = models.Work.objects.filter(
Q(id=book_id) | Q(editions=book_id)
).distinct()
print(work.count())
work = work.first()
book_list = get_object_or_404(models.SuggestionList, suggests_for=work)
if is_api_request(request): if is_api_request(request):
return ActivitypubResponse(book_list.to_activity(**request.GET)) return ActivitypubResponse(book_list.to_activity(**request.GET))
@ -53,6 +59,7 @@ class SuggestionList(View):
query = request.GET.get("q", "") query = request.GET.get("q", "")
data = { data = {
"list": book_list, "list": book_list,
"work": book_list.suggests_for,
"items": page, "items": page,
"page_range": paginated.get_elided_page_range( "page_range": paginated.get_elided_page_range(
page.number, on_each_side=2, on_ends=1 page.number, on_each_side=2, on_ends=1
@ -72,19 +79,18 @@ class SuggestionList(View):
return TemplateResponse(request, "lists/list.html", data) return TemplateResponse(request, "lists/list.html", data)
@method_decorator(login_required, name="dispatch") @method_decorator(login_required, name="dispatch")
def post(self, request, book_id): def post(self, request, book_id): # pylint: disable=unused-argument
"""create a suggestion_list""" """create a suggestion_list"""
form = forms.SuggestionListForm(request.POST) form = forms.SuggestionListForm(request.POST)
book = get_object_or_404(models.Edition, id=book_id)
if not form.is_valid(): if not form.is_valid():
return redirect("book", book.id) return redirect_to_referer(request)
# saving in two steps means django uses the model's custom save functionality, # saving in two steps means django uses the model's custom save functionality,
# which adds an embed key and fixes the privacy and curation settings # which adds an embed key and fixes the privacy and curation settings
suggestion_list = form.save(request, commit=False) suggestion_list = form.save(request, commit=False)
suggestion_list.save() suggestion_list.save()
return redirect("book", book.id) return redirect_to_referer(request)
@login_required @login_required