mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-11-29 04:51:11 +00:00
Associate suggestions with works instead of editions
This commit is contained in:
parent
299ac0631d
commit
5deb779efd
13 changed files with 47 additions and 58 deletions
|
@ -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 = []
|
|
@ -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(
|
|
@ -63,7 +63,7 @@ class SuggestionList(AbstractList):
|
|||
)
|
||||
|
||||
suggests_for = fields.OneToOneField(
|
||||
"Edition",
|
||||
"Work",
|
||||
on_delete=models.PROTECT,
|
||||
activitypub_field="book",
|
||||
related_name="suggestion_list",
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
|
|
|
@ -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" %}
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue