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.fields
@ -10,7 +10,7 @@ import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0208_merge_0207_merge_20240629_0626_0207_sqlparse_update"),
("bookwyrm", "0209_user_show_ratings"),
]
operations = [
@ -140,7 +140,7 @@ class Migration(migrations.Migration):
field=bookwyrm.models.fields.OneToOneField(
on_delete=django.db.models.deletion.PROTECT,
related_name="suggestion_list",
to="bookwyrm.edition",
to="bookwyrm.work",
),
),
migrations.AddField(

View file

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

View file

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

View file

@ -1,5 +1,5 @@
{% 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 %}
<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>

View file

@ -3,38 +3,35 @@
<h2 class="title is-3" id="suggestions-section">
{% trans "Suggestions" %}
<a class="help has-text-weight-normal" href="{% url 'suggestion-list' book_id=book.id %}">
{% blocktrans trimmed with count=book.suggestion_list.suggestionlistitem_set.count|intcomma %}
View all {{ count }} suggestions
{% endblocktrans %}
<a class="help has-text-weight-normal" href="{% url 'suggestion-list' book_id=work.id %}">
{% trans "View all suggestions" %}
</a>
</h2>
{% if book.suggestion_list %}
{% if suggestion_list %}
<p class="subtitle">
{% blocktrans trimmed with title=book.title %}
Readers who liked <em>{{ title }}</em> recommend giving these books a try:
{% endblocktrans %}
</p>
{% if items|length == 0 %}
<section class="section has-background-light is-flex is-flex-direction-column is-align-items-center">
<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>
</section>
{% else %}
<ol class="columns is-multiline">
{% for item in items %}
<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>
{% endfor %}
</ol>
<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>
{% endif %}
{% 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">
{% csrf_token %}
<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>
</form>
</section>

View file

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

View file

@ -4,7 +4,7 @@
{% load group_tags %}
{% 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 %}
Add "<em>{{ title }}</em>" to this list
{% endblocktrans %}
@ -19,10 +19,10 @@
<form
name="add-book-{{ book.id }}"
method="POST"
{% if list.suggests_for %}
action="{% url 'book-add-suggestion' book_id=list.suggests_for.id %}{% if query %}?suggestion_query={{ query }}#suggestions-section{% endif %}"
{% if is_suggestion %}
action="{% url 'book-add-suggestion' book_id=list.suggests_for.id %}#suggestions-section"
{% else %}
action="{% url 'list-add-book' %}{% if query %}?q={{ query }}{% endif %}"
action="{% url 'list-add-book' %}"
{% endif %}
>
{% endblock %}
@ -39,7 +39,7 @@
<div class="buttons is-right is-flex-grow-1">
<button type="button" class="button" data-modal-close>{% trans "Cancel" %}</button>
<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" %}
{% else %}
{% trans "Suggest" %}

View file

@ -93,7 +93,7 @@
</div>
{% if list.suggests_for %}
<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>
{% endif %}
{% if list.user == request.user or list.group|is_member:request.user %}
@ -172,7 +172,7 @@
{% trans "Suggest Books" %}
{% endif %}
</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 %}
<div>
<h2 class="title is-5 mt-6" id="embed-label">

View file

@ -48,13 +48,13 @@
class="button is-small is-link"
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" %}
{% else %}
{% trans "Suggest" %}
{% endif %}
</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>

View file

@ -90,6 +90,7 @@ class Book(View):
)
data = {
"book": book,
"work": book.parent_work,
"statuses": paginated.get_page(request.GET.get("page")),
"review_count": reviews.count(),
"ratings": (
@ -135,21 +136,22 @@ class Book(View):
"comment_count": book.comment_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"] = (
book.suggestion_list.suggestionlistitem_set.prefetch_related(
"user", "book", "book__authors"
suggestion_list.suggestionlistitem_set.prefetch_related(
"user", "book", "book__authors", "endorsement"
)
.annotate(endorsement_count=Count("endorsement"))
.order_by("-endorsement_count")[:3]
)
data["suggested_books"] = get_list_suggestions(
book.suggestion_list,
suggestion_list,
request.user,
query=request.GET.get("suggestion_query", ""),
ignore_book=book,
ignore_book=book.parent_work,
)
return TemplateResponse(request, "book/book.html", data)

View file

@ -112,13 +112,13 @@ def get_list_suggestions(
query,
filters=[
~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
suggestions = (
user.shelfbook_set.filter(~Q(book__in=book_list.books.all()))
.exclude(book=ignore_book)
.exclude(book__parent_work=ignore_book)
.distinct()[:num_suggestions]
)
suggestions = [s.book for s in suggestions[:num_suggestions]]
@ -127,7 +127,7 @@ def get_list_suggestions(
s.default_edition
for s in models.Work.objects.filter(
~Q(editions__in=book_list.books.all()),
~Q(editions__in=[ignore_book]),
~Q(id=ignore_book.id),
)
.distinct()
.order_by("-updated_date")[:num_suggestions]

View file

@ -2,8 +2,8 @@
from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator
from django.db import transaction
from django.db.models import Count
from django.shortcuts import get_object_or_404, redirect
from django.db.models import Count, Q
from django.shortcuts import get_object_or_404
from django.template.response import TemplateResponse
from django.urls import reverse
from django.utils.decorators import method_decorator
@ -26,7 +26,13 @@ class SuggestionList(View):
add_failed = kwargs.get("add_failed", 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):
return ActivitypubResponse(book_list.to_activity(**request.GET))
@ -53,6 +59,7 @@ class SuggestionList(View):
query = request.GET.get("q", "")
data = {
"list": book_list,
"work": book_list.suggests_for,
"items": page,
"page_range": paginated.get_elided_page_range(
page.number, on_each_side=2, on_ends=1
@ -72,19 +79,18 @@ class SuggestionList(View):
return TemplateResponse(request, "lists/list.html", data)
@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"""
form = forms.SuggestionListForm(request.POST)
book = get_object_or_404(models.Edition, id=book_id)
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,
# which adds an embed key and fixes the privacy and curation settings
suggestion_list = form.save(request, commit=False)
suggestion_list.save()
return redirect("book", book.id)
return redirect_to_referer(request)
@login_required