Author class view

This commit is contained in:
Mouse Reeve 2021-01-13 09:54:35 -08:00
parent 56e3e98bc1
commit 4dea22bef6
7 changed files with 301 additions and 64 deletions

View file

@ -27,7 +27,7 @@
</div> </div>
{% endif %} {% endif %}
<form class="block" name="edit-author" action="/edit-author/{{ author.id }}" method="post"> <form class="block" name="edit-author" action="{{ author.local_path }}/edit" method="post">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="last_edited_by" value="{{ request.user.id }}"> <input type="hidden" name="last_edited_by" value="{{ request.user.id }}">

View file

@ -96,12 +96,13 @@ urlpatterns = [
re_path(r'^resolve-book/?$', views.resolve_book), re_path(r'^resolve-book/?$', views.resolve_book),
re_path(r'^switch-edition/?$', views.switch_edition), re_path(r'^switch-edition/?$', views.switch_edition),
re_path(r'^author/(?P<author_id>[\w\-]+)/edit/?$', vviews.edit_author_page), # author
re_path(r'^author/(?P<author_id>\d+)(.json)?/?$', views.Author.as_view()),
re_path(r'^author/(?P<author_id>\d+)/edit/?$', views.EditAuthor.as_view()),
re_path(r'^tag/?$', actions.tag), re_path(r'^tag/?$', actions.tag),
re_path(r'^untag/?$', actions.untag), re_path(r'^untag/?$', actions.untag),
re_path(r'^author/(?P<author_id>[\w\-]+)(.json)?/?$', vviews.author_page),
re_path(r'^tag/(?P<tag_id>.+)\.json/?$', vviews.tag_page), re_path(r'^tag/(?P<tag_id>.+)\.json/?$', vviews.tag_page),
re_path(r'^tag/(?P<tag_id>.+)/?$', vviews.tag_page), re_path(r'^tag/(?P<tag_id>.+)/?$', vviews.tag_page),
re_path(r'^%s/shelf/(?P<shelf_identifier>[\w-]+)(.json)?/?$' % \ re_path(r'^%s/shelf/(?P<shelf_identifier>[\w-]+)(.json)?/?$' % \
@ -111,8 +112,6 @@ urlpatterns = [
re_path(r'^search/?$', vviews.search), re_path(r'^search/?$', vviews.search),
re_path(r'^edit-author/(?P<author_id>\d+)/?$', actions.edit_author),
re_path(r'^edit-readthrough/?$', actions.edit_readthrough), re_path(r'^edit-readthrough/?$', actions.edit_readthrough),
re_path(r'^delete-readthrough/?$', actions.delete_readthrough), re_path(r'^delete-readthrough/?$', actions.delete_readthrough),
re_path(r'^create-readthrough/?$', actions.create_readthrough), re_path(r'^create-readthrough/?$', actions.create_readthrough),

View file

@ -2,10 +2,9 @@
import dateutil.parser import dateutil.parser
from dateutil.parser import ParserError from dateutil.parser import ParserError
from django.contrib.auth.decorators import login_required, permission_required from django.contrib.auth.decorators import login_required
from django.http import HttpResponseBadRequest, HttpResponseNotFound from django.http import HttpResponseBadRequest, HttpResponseNotFound
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
from django.template.response import TemplateResponse
from django.utils import timezone from django.utils import timezone
from django.views.decorators.http import require_POST from django.views.decorators.http import require_POST
@ -13,27 +12,6 @@ from bookwyrm import forms, models, outgoing
from bookwyrm.broadcast import broadcast from bookwyrm.broadcast import broadcast
from bookwyrm.vviews import get_user_from_username, get_edition from bookwyrm.vviews import get_user_from_username, get_edition
@login_required
@permission_required('bookwyrm.edit_book', raise_exception=True)
@require_POST
def edit_author(request, author_id):
''' edit a author cool '''
author = get_object_or_404(models.Author, id=author_id)
form = forms.AuthorForm(request.POST, request.FILES, instance=author)
if not form.is_valid():
data = {
'title': 'Edit Author',
'author': author,
'form': form
}
return TemplateResponse(request, 'edit_author.html', data)
author = form.save()
broadcast(request.user, author.to_update_activity(request.user))
return redirect('/author/%s' % author.id)
@login_required @login_required
@require_POST @require_POST
def create_shelf(request): def create_shelf(request):

View file

@ -11,3 +11,4 @@ from .status import Status, Replies, CreateStatus, DeleteStatus
from .interaction import Favorite, Unfavorite, Boost, Unboost from .interaction import Favorite, Unfavorite, Boost, Unboost
from .books import Book, EditBook, Editions from .books import Book, EditBook, Editions
from .books import upload_cover, add_description, switch_edition, resolve_book from .books import upload_cover, add_description, switch_edition, resolve_book
from .author import Author, EditAuthor

66
bookwyrm/views/author.py Normal file
View file

@ -0,0 +1,66 @@
''' the good people stuff! the authors! '''
from django.contrib.auth.decorators import login_required, permission_required
from django.db.models import Q
from django.shortcuts import get_object_or_404, redirect
from django.template.response import TemplateResponse
from django.utils.decorators import method_decorator
from django.views import View
from bookwyrm import forms, models
from bookwyrm.activitypub import ActivitypubResponse
from bookwyrm.broadcast import broadcast
from .helpers import is_api_request
# pylint: disable= no-self-use
class Author(View):
''' this person wrote a book '''
def get(self, request, author_id):
''' landing page for an author '''
author = get_object_or_404(models.Author, id=author_id)
if is_api_request(request):
return ActivitypubResponse(author.to_activity())
books = models.Work.objects.filter(
Q(authors=author) | Q(editions__authors=author)).distinct()
data = {
'title': author.name,
'author': author,
'books': [b.get_default_edition() for b in books],
}
return TemplateResponse(request, 'author.html', data)
@method_decorator(login_required, name='dispatch')
@method_decorator(
permission_required('bookwyrm.edit_book', raise_exception=True),
name='dispatch')
class EditAuthor(View):
''' edit author info '''
def get(self, request, author_id):
''' info about a book '''
author = get_object_or_404(models.Author, id=author_id)
data = {
'title': 'Edit Author',
'author': author,
'form': forms.AuthorForm(instance=author)
}
return TemplateResponse(request, 'edit_author.html', data)
def post(self, request, author_id):
''' edit a author cool '''
author = get_object_or_404(models.Author, id=author_id)
form = forms.AuthorForm(request.POST, request.FILES, instance=author)
if not form.is_valid():
data = {
'title': 'Edit Author',
'author': author,
'form': form
}
return TemplateResponse(request, 'edit_author.html', data)
author = form.save()
broadcast(request.user, author.to_update_activity(request.user))
return redirect('/author/%s' % author.id)

228
bookwyrm/views/books.py Normal file
View file

@ -0,0 +1,228 @@
''' the good stuff! the books! '''
from django.core.paginator import Paginator
from django.contrib.auth.decorators import login_required, permission_required
from django.db import transaction
from django.db.models import Avg, Q
from django.http import HttpResponseNotFound
from django.shortcuts import get_object_or_404, redirect
from django.template.response import TemplateResponse
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.http import require_POST
from bookwyrm import forms, models
from bookwyrm.activitypub import ActivitypubResponse
from bookwyrm.broadcast import broadcast
from bookwyrm.connectors import connector_manager
from bookwyrm.settings import PAGE_LENGTH
from .helpers import is_api_request, get_activity_feed, get_edition
# pylint: disable= no-self-use
class Book(View):
''' a book! this is the stuff '''
def get(self, request, book_id):
''' info about a book '''
try:
page = int(request.GET.get('page', 1))
except ValueError:
page = 1
try:
book = models.Book.objects.select_subclasses().get(id=book_id)
except models.Book.DoesNotExist:
return HttpResponseNotFound()
if is_api_request(request):
return ActivitypubResponse(book.to_activity())
if isinstance(book, models.Work):
book = book.get_default_edition()
if not book:
return HttpResponseNotFound()
work = book.parent_work
if not work:
return HttpResponseNotFound()
reviews = models.Review.objects.filter(
book__in=work.editions.all(),
)
# all reviews for the book
reviews = get_activity_feed(
request.user,
['public', 'unlisted', 'followers', 'direct'],
queryset=reviews
)
# the reviews to show
paginated = Paginator(reviews.exclude(
Q(content__isnull=True) | Q(content='')
), PAGE_LENGTH)
reviews_page = paginated.page(page)
user_tags = readthroughs = user_shelves = other_edition_shelves = []
if request.user.is_authenticated:
user_tags = models.UserTag.objects.filter(
book=book, user=request.user
).values_list('tag__identifier', flat=True)
readthroughs = models.ReadThrough.objects.filter(
user=request.user,
book=book,
).order_by('start_date')
user_shelves = models.ShelfBook.objects.filter(
added_by=request.user, book=book
)
other_edition_shelves = models.ShelfBook.objects.filter(
~Q(book=book),
added_by=request.user,
book__parent_work=book.parent_work,
)
data = {
'title': book.title,
'book': book,
'reviews': reviews_page,
'review_count': reviews.count(),
'ratings': reviews.filter(Q(content__isnull=True) | Q(content='')),
'rating': reviews.aggregate(Avg('rating'))['rating__avg'],
'tags': models.UserTag.objects.filter(book=book),
'user_tags': user_tags,
'user_shelves': user_shelves,
'other_edition_shelves': other_edition_shelves,
'readthroughs': readthroughs,
'path': '/book/%s' % book_id,
}
return TemplateResponse(request, 'book.html', data)
@method_decorator(login_required, name='dispatch')
@method_decorator(
permission_required('bookwyrm.edit_book', raise_exception=True),
name='dispatch')
class EditBook(View):
''' edit a book '''
def get(self, request, book_id):
''' info about a book '''
book = get_edition(book_id)
if not book.description:
book.description = book.parent_work.description
data = {
'title': 'Edit Book',
'book': book,
'form': forms.EditionForm(instance=book)
}
return TemplateResponse(request, 'edit_book.html', data)
def post(self, request, book_id):
''' edit a book cool '''
book = get_object_or_404(models.Edition, id=book_id)
form = forms.EditionForm(request.POST, request.FILES, instance=book)
if not form.is_valid():
data = {
'title': 'Edit Book',
'book': book,
'form': form
}
return TemplateResponse(request, 'edit_book.html', data)
book = form.save()
broadcast(request.user, book.to_update_activity(request.user))
return redirect('/book/%s' % book.id)
class Editions(View):
''' list of editions '''
def get(self, request, book_id):
''' list of editions of a book '''
work = get_object_or_404(models.Work, id=book_id)
if is_api_request(request):
return ActivitypubResponse(work.to_edition_list(**request.GET))
data = {
'title': 'Editions of %s' % work.title,
'editions': work.editions.order_by('-edition_rank').all(),
'work': work,
}
return TemplateResponse(request, 'editions.html', data)
@login_required
@require_POST
def upload_cover(request, book_id):
''' upload a new cover '''
book = get_object_or_404(models.Edition, id=book_id)
form = forms.CoverForm(request.POST, request.FILES, instance=book)
if not form.is_valid():
return redirect('/book/%d' % book.id)
book.cover = form.files['cover']
book.save()
broadcast(request.user, book.to_update_activity(request.user))
return redirect('/book/%s' % book.id)
@login_required
@require_POST
@permission_required('bookwyrm.edit_book', raise_exception=True)
def add_description(request, book_id):
''' upload a new cover '''
if not request.method == 'POST':
return redirect('/')
book = get_object_or_404(models.Edition, id=book_id)
description = request.POST.get('description')
book.description = description
book.save()
broadcast(request.user, book.to_update_activity(request.user))
return redirect('/book/%s' % book.id)
@require_POST
def resolve_book(request):
''' figure out the local path to a book from a remote_id '''
remote_id = request.POST.get('remote_id')
connector = connector_manager.get_or_create_connector(remote_id)
book = connector.get_or_create_book(remote_id)
return redirect('/book/%d' % book.id)
@login_required
@require_POST
@transaction.atomic
def switch_edition(request):
''' switch your copy of a book to a different edition '''
edition_id = request.POST.get('edition')
new_edition = get_object_or_404(models.Edition, id=edition_id)
shelfbooks = models.ShelfBook.objects.filter(
book__parent_work=new_edition.parent_work,
shelf__user=request.user
)
for shelfbook in shelfbooks.all():
broadcast(request.user, shelfbook.to_remove_activity(request.user))
shelfbook.book = new_edition
shelfbook.save()
broadcast(request.user, shelfbook.to_add_activity(request.user))
readthroughs = models.ReadThrough.objects.filter(
book__parent_work=new_edition.parent_work,
user=request.user
)
for readthrough in readthroughs.all():
readthrough.book = new_edition
readthrough.save()
return redirect('/book/%d' % new_edition.id)

View file

@ -1,18 +1,15 @@
''' views for pages you can go to in the application ''' ''' views for pages you can go to in the application '''
import re import re
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.postgres.search import TrigramSimilarity from django.contrib.postgres.search import TrigramSimilarity
from django.db.models import Q
from django.db.models.functions import Greatest from django.db.models.functions import Greatest
from django.http import HttpResponseNotFound, JsonResponse from django.http import HttpResponseNotFound, JsonResponse
from django.shortcuts import get_object_or_404
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_GET from django.views.decorators.http import require_GET
from bookwyrm import outgoing from bookwyrm import outgoing
from bookwyrm import forms, models from bookwyrm import models
from bookwyrm.activitypub import ActivitypubResponse from bookwyrm.activitypub import ActivitypubResponse
from bookwyrm.connectors import connector_manager from bookwyrm.connectors import connector_manager
from bookwyrm.utils import regex from bookwyrm.utils import regex
@ -90,38 +87,6 @@ def search(request):
return TemplateResponse(request, 'search_results.html', data) return TemplateResponse(request, 'search_results.html', data)
@login_required
@permission_required('bookwyrm.edit_book', raise_exception=True)
@require_GET
def edit_author_page(request, author_id):
''' info about a book '''
author = get_object_or_404(models.Author, id=author_id)
data = {
'title': 'Edit Author',
'author': author,
'form': forms.AuthorForm(instance=author)
}
return TemplateResponse(request, 'edit_author.html', data)
@require_GET
def author_page(request, author_id):
''' landing page for an author '''
author = get_object_or_404(models.Author, id=author_id)
if is_api_request(request):
return ActivitypubResponse(author.to_activity())
books = models.Work.objects.filter(
Q(authors=author) | Q(editions__authors=author)).distinct()
data = {
'title': author.name,
'author': author,
'books': [b.get_default_edition() for b in books],
}
return TemplateResponse(request, 'author.html', data)
@require_GET @require_GET
def tag_page(request, tag_id): def tag_page(request, tag_id):
''' books related to a tag ''' ''' books related to a tag '''