Merge branch 'main' into bookwyrm-groups
There are database migrations in main ahead of this branch so they need to be merged in to the branch before we can merge back into main.
This commit is contained in:
@ -70,7 +70,7 @@ from .search import Search
|
||||
from .shelf import Shelf
|
||||
from .shelf import create_shelf, delete_shelf
|
||||
from .shelf import shelve, unshelve
|
||||
from .status import CreateStatus, DeleteStatus, DeleteAndRedraft, update_progress
|
||||
from .status import CreateStatus, EditStatus, DeleteStatus, update_progress
|
||||
from .status import edit_readthrough
|
||||
from .updates import get_notification_count, get_unread_status_count
|
||||
from .user import User, Followers, Following, hide_suggestions
|
||||
|
@ -51,7 +51,7 @@ class Import(View):
|
||||
elif source == "Storygraph":
|
||||
importer = StorygraphImporter()
|
||||
else:
|
||||
# Default : GoodReads
|
||||
# Default : Goodreads
|
||||
importer = GoodreadsImporter()
|
||||
|
||||
try:
|
||||
|
@ -68,16 +68,30 @@ class Shelf(View):
|
||||
deleted=False,
|
||||
).order_by("-published_date")
|
||||
|
||||
reading = models.ReadThrough.objects
|
||||
|
||||
reading = reading.filter(user=user, book__id=OuterRef("id")).order_by(
|
||||
"start_date"
|
||||
)
|
||||
|
||||
books = books.annotate(
|
||||
rating=Subquery(reviews.values("rating")[:1]),
|
||||
shelved_date=F("shelfbook__shelved_date"),
|
||||
start_date=Subquery(reading.values("start_date")[:1]),
|
||||
finish_date=Subquery(reading.values("finish_date")[:1]),
|
||||
author=Subquery(
|
||||
models.Book.objects.filter(id=OuterRef("id")).values("authors__name")[
|
||||
:1
|
||||
]
|
||||
),
|
||||
).prefetch_related("authors")
|
||||
|
||||
books = sort_books(books, request.GET.get("sort"))
|
||||
|
||||
paginated = Paginator(
|
||||
books.order_by("-shelfbook__updated_date"),
|
||||
books,
|
||||
PAGE_LENGTH,
|
||||
)
|
||||
|
||||
page = paginated.get_page(request.GET.get("page"))
|
||||
data = {
|
||||
"user": user,
|
||||
@ -87,6 +101,7 @@ class Shelf(View):
|
||||
"books": page,
|
||||
"edit_form": forms.ShelfForm(instance=shelf if shelf_identifier else None),
|
||||
"create_form": forms.ShelfForm(),
|
||||
"sort": request.GET.get("sort"),
|
||||
"page_range": paginated.get_elided_page_range(
|
||||
page.number, on_each_side=2, on_ends=1
|
||||
),
|
||||
@ -207,3 +222,23 @@ def unshelve(request):
|
||||
|
||||
shelf_book.delete()
|
||||
return redirect(request.headers.get("Referer", "/"))
|
||||
|
||||
|
||||
def sort_books(books, sort):
|
||||
"""Books in shelf sorting"""
|
||||
sort_fields = [
|
||||
"title",
|
||||
"author",
|
||||
"shelved_date",
|
||||
"start_date",
|
||||
"finish_date",
|
||||
"rating",
|
||||
]
|
||||
|
||||
if sort in sort_fields:
|
||||
books = books.order_by(sort)
|
||||
elif sort and sort[1:] in sort_fields:
|
||||
books = books.order_by(F(sort[1:]).desc(nulls_last=True))
|
||||
else:
|
||||
books = books.order_by("-shelved_date")
|
||||
return books
|
||||
|
@ -8,6 +8,7 @@ from django.core.exceptions import ValidationError
|
||||
from django.http import HttpResponse, HttpResponseBadRequest, Http404
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils import timezone
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views import View
|
||||
from django.views.decorators.http import require_POST
|
||||
@ -21,23 +22,55 @@ from .helpers import handle_remote_webfinger, is_api_request
|
||||
from .helpers import load_date_in_user_tz_as_utc
|
||||
|
||||
|
||||
# pylint: disable= no-self-use
|
||||
@method_decorator(login_required, name="dispatch")
|
||||
class EditStatus(View):
|
||||
"""the view for *posting*"""
|
||||
|
||||
def get(self, request, status_id): # pylint: disable=unused-argument
|
||||
"""load the edit panel"""
|
||||
status = get_object_or_404(
|
||||
models.Status.objects.select_subclasses(), id=status_id
|
||||
)
|
||||
status.raise_not_editable(request.user)
|
||||
|
||||
status_type = "reply" if status.reply_parent else status.status_type.lower()
|
||||
data = {
|
||||
"type": status_type,
|
||||
"book": getattr(status, "book", None),
|
||||
"draft": status,
|
||||
}
|
||||
return TemplateResponse(request, "compose.html", data)
|
||||
|
||||
|
||||
# pylint: disable= no-self-use
|
||||
@method_decorator(login_required, name="dispatch")
|
||||
class CreateStatus(View):
|
||||
"""the view for *posting*"""
|
||||
|
||||
def get(self, request, status_type): # pylint: disable=unused-argument
|
||||
"""compose view (used for delete-and-redraft)"""
|
||||
"""compose view (...not used?)"""
|
||||
book = get_object_or_404(models.Edition, id=request.GET.get("book"))
|
||||
data = {"book": book}
|
||||
return TemplateResponse(request, "compose.html", data)
|
||||
|
||||
def post(self, request, status_type):
|
||||
"""create status of whatever type"""
|
||||
def post(self, request, status_type, existing_status_id=None):
|
||||
"""create status of whatever type"""
|
||||
created = not existing_status_id
|
||||
existing_status = None
|
||||
if existing_status_id:
|
||||
existing_status = get_object_or_404(
|
||||
models.Status.objects.select_subclasses(), id=existing_status_id
|
||||
)
|
||||
existing_status.raise_not_editable(request.user)
|
||||
existing_status.edited_date = timezone.now()
|
||||
|
||||
status_type = status_type[0].upper() + status_type[1:]
|
||||
|
||||
try:
|
||||
form = getattr(forms, f"{status_type}Form")(request.POST)
|
||||
form = getattr(forms, f"{status_type}Form")(
|
||||
request.POST, instance=existing_status
|
||||
)
|
||||
except AttributeError:
|
||||
return HttpResponseBadRequest()
|
||||
if not form.is_valid():
|
||||
@ -46,6 +79,11 @@ class CreateStatus(View):
|
||||
return redirect(request.headers.get("Referer", "/"))
|
||||
|
||||
status = form.save(commit=False)
|
||||
# save the plain, unformatted version of the status for future editing
|
||||
status.raw_content = status.content
|
||||
if hasattr(status, "quote"):
|
||||
status.raw_quote = status.quote
|
||||
|
||||
if not status.sensitive and status.content_warning:
|
||||
# the cw text field remains populated when you click "remove"
|
||||
status.content_warning = None
|
||||
@ -77,7 +115,7 @@ class CreateStatus(View):
|
||||
if hasattr(status, "quote"):
|
||||
status.quote = to_markdown(status.quote)
|
||||
|
||||
status.save(created=True)
|
||||
status.save(created=created)
|
||||
|
||||
# update a readthorugh, if needed
|
||||
try:
|
||||
@ -106,36 +144,6 @@ class DeleteStatus(View):
|
||||
return redirect(request.headers.get("Referer", "/"))
|
||||
|
||||
|
||||
@method_decorator(login_required, name="dispatch")
|
||||
class DeleteAndRedraft(View):
|
||||
"""delete a status but let the user re-create it"""
|
||||
|
||||
def post(self, request, status_id):
|
||||
"""delete and tombstone a status"""
|
||||
status = get_object_or_404(
|
||||
models.Status.objects.select_subclasses(), id=status_id
|
||||
)
|
||||
# don't let people redraft other people's statuses
|
||||
status.raise_not_editable(request.user)
|
||||
|
||||
status_type = status.status_type.lower()
|
||||
if status.reply_parent:
|
||||
status_type = "reply"
|
||||
|
||||
data = {
|
||||
"draft": status,
|
||||
"type": status_type,
|
||||
}
|
||||
if hasattr(status, "book"):
|
||||
data["book"] = status.book
|
||||
elif status.mention_books:
|
||||
data["book"] = status.mention_books.first()
|
||||
|
||||
# perform deletion
|
||||
status.delete()
|
||||
return TemplateResponse(request, "compose.html", data)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_POST
|
||||
def update_progress(request, book_id): # pylint: disable=unused-argument
|
||||
|
Reference in New Issue
Block a user