Merge branch 'main' into isni-poc

This commit is contained in:
Hugh Rundle
2021-11-22 08:53:58 +11:00
142 changed files with 14141 additions and 3230 deletions

View File

@ -43,6 +43,16 @@ from .shelf.shelf import Shelf
from .shelf.shelf_actions import create_shelf, delete_shelf
from .shelf.shelf_actions import shelve, unshelve
# csv import
from .imports.import_data import Import
from .imports.import_status import ImportStatus, retry_item
from .imports.troubleshoot import ImportTroubleshoot
from .imports.manually_review import (
ImportManualReview,
approve_import_item,
delete_import_item,
)
# misc views
from .author import Author, EditAuthor
from .directory import Directory
@ -62,7 +72,6 @@ from .group import (
accept_membership,
reject_membership,
)
from .import_data import Import, ImportStatus
from .inbox import Inbox
from .interaction import Favorite, Unfavorite, Boost, Unboost
from .isbn import Isbn

View File

@ -7,7 +7,7 @@ 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 import emailing, forms, models
# pylint: disable=no-self-use
@ -142,5 +142,6 @@ def make_report(request):
if not form.is_valid():
raise ValueError(form.errors)
form.save()
report = form.save()
emailing.moderation_report_email(report)
return redirect(request.headers.get("Referer", "/"))

View File

@ -48,4 +48,7 @@ def email_preview(request):
data["invite_link"] = "https://example.com/link"
data["confirmation_link"] = "https://example.com/link"
data["confirmation_code"] = "AKJHKDGKJSDFG"
data["reporter"] = "ConcernedUser"
data["reportee"] = "UserName"
data["report_link"] = "https://example.com/link"
return TemplateResponse(request, "email/preview.html", data)

View File

View File

@ -2,9 +2,8 @@
from io import TextIOWrapper
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
from django.http import HttpResponseBadRequest
from django.shortcuts import get_object_or_404, redirect
from django.shortcuts import redirect
from django.template.response import TemplateResponse
from django.utils.decorators import method_decorator
from django.utils.translation import gettext_lazy as _
@ -12,12 +11,10 @@ from django.views import View
from bookwyrm import forms, models
from bookwyrm.importers import (
Importer,
LibrarythingImporter,
GoodreadsImporter,
StorygraphImporter,
)
from bookwyrm.tasks import app
# pylint: disable= no-self-use
@method_decorator(login_required, name="dispatch")
@ -70,46 +67,3 @@ class Import(View):
return redirect(f"/import/{job.id}")
return HttpResponseBadRequest()
@method_decorator(login_required, name="dispatch")
class ImportStatus(View):
"""status of an existing import"""
def get(self, request, job_id):
"""status of an import job"""
job = get_object_or_404(models.ImportJob, id=job_id)
if job.user != request.user:
raise PermissionDenied()
try:
task = app.AsyncResult(job.task_id)
# triggers attribute error if the task won't load
task.status # pylint: disable=pointless-statement
except (ValueError, AttributeError):
task = None
items = job.items.order_by("index").all()
failed_items = [i for i in items if i.fail_reason]
items = [i for i in items if not i.fail_reason]
return TemplateResponse(
request,
"import/import_status.html",
{"job": job, "items": items, "failed_items": failed_items, "task": task},
)
def post(self, request, job_id):
"""retry lines from an import"""
job = get_object_or_404(models.ImportJob, id=job_id)
items = []
for item in request.POST.getlist("import_item"):
items.append(get_object_or_404(models.ImportItem, id=item))
importer = Importer()
job = importer.create_retry_job(
request.user,
job,
items,
)
importer.start_import(job)
return redirect(f"/import/{job.id}")

View File

@ -0,0 +1,79 @@
""" import books from another app """
import math
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
from django.core.paginator import Paginator
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
from bookwyrm import models
from bookwyrm.importers import GoodreadsImporter
from bookwyrm.importers.importer import import_item_task
from bookwyrm.settings import PAGE_LENGTH
# pylint: disable= no-self-use
@method_decorator(login_required, name="dispatch")
class ImportStatus(View):
"""status of an existing import"""
def get(self, request, job_id):
"""status of an import job"""
job = get_object_or_404(models.ImportJob, id=job_id)
if job.user != request.user:
raise PermissionDenied()
items = job.items.order_by("index")
item_count = items.count() or 1
paginated = Paginator(items, PAGE_LENGTH)
page = paginated.get_page(request.GET.get("page"))
manual_review_count = items.filter(
fail_reason__isnull=False, book_guess__isnull=False, book__isnull=True
).count()
fail_count = items.filter(
fail_reason__isnull=False, book_guess__isnull=True
).count()
pending_item_count = job.pending_items.count()
data = {
"job": job,
"items": page,
"manual_review_count": manual_review_count,
"fail_count": fail_count,
"page_range": paginated.get_elided_page_range(
page.number, on_each_side=2, on_ends=1
),
"item_count": item_count,
"complete_count": item_count - pending_item_count,
"percent": math.floor( # pylint: disable=c-extension-no-member
(item_count - pending_item_count) / item_count * 100
),
# hours since last import item update
"inactive_time": (job.updated_date - timezone.now()).seconds / 60 / 60,
"legacy": not job.mappings,
}
return TemplateResponse(request, "import/import_status.html", data)
def post(self, request, job_id):
"""bring a legacy import into the latest format"""
job = get_object_or_404(models.ImportJob, id=job_id)
if job.user != request.user:
raise PermissionDenied()
GoodreadsImporter().update_legacy_job(job)
return redirect("import-status", job_id)
@login_required
@require_POST
def retry_item(request, job_id, item_id):
"""retry an item"""
item = get_object_or_404(
models.ImportItem, id=item_id, job__id=job_id, job__user=request.user
)
import_item_task.delay(item.id)
return redirect("import-status", job_id)

View File

@ -0,0 +1,72 @@
""" verify books we're unsure about """
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
from django.core.paginator import Paginator
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 models
from bookwyrm.importers.importer import import_item_task
from bookwyrm.settings import PAGE_LENGTH
# pylint: disable= no-self-use
@method_decorator(login_required, name="dispatch")
class ImportManualReview(View):
"""problems items in an existing import"""
def get(self, request, job_id):
"""status of an import job"""
job = get_object_or_404(models.ImportJob, id=job_id)
if job.user != request.user:
raise PermissionDenied()
items = job.items.order_by("index").filter(
book__isnull=True, book_guess__isnull=False
)
paginated = Paginator(items, PAGE_LENGTH)
page = paginated.get_page(request.GET.get("page"))
data = {
"job": job,
"items": page,
"page_range": paginated.get_elided_page_range(
page.number, on_each_side=2, on_ends=1
),
"complete": True,
}
return TemplateResponse(request, "import/manual_review.html", data)
@login_required
@require_POST
# pylint: disable=unused-argument
def approve_import_item(request, job_id, item_id):
"""we guessed right"""
item = get_object_or_404(
models.ImportItem, id=item_id, job__id=job_id, book_guess__isnull=False
)
item.fail_reason = None
item.book = item.book_guess
item.book_guess = None
item.save()
# the good stuff - actually import the data
import_item_task.delay(item.id)
return redirect("import-review", job_id)
@login_required
@require_POST
# pylint: disable=unused-argument
def delete_import_item(request, job_id, item_id):
"""we guessed right"""
item = get_object_or_404(
models.ImportItem, id=item_id, job__id=job_id, book_guess__isnull=False
)
item.book_guess = None
item.save()
return redirect("import-review", job_id)

View File

@ -0,0 +1,54 @@
""" import books from another app """
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
from django.core.paginator import Paginator
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 models
from bookwyrm.importers import Importer
from bookwyrm.settings import PAGE_LENGTH
# pylint: disable= no-self-use
@method_decorator(login_required, name="dispatch")
class ImportTroubleshoot(View):
"""problems items in an existing import"""
def get(self, request, job_id):
"""status of an import job"""
job = get_object_or_404(models.ImportJob, id=job_id)
if job.user != request.user:
raise PermissionDenied()
items = job.items.order_by("index").filter(
fail_reason__isnull=False, book_guess__isnull=True
)
paginated = Paginator(items, PAGE_LENGTH)
page = paginated.get_page(request.GET.get("page"))
data = {
"job": job,
"items": page,
"page_range": paginated.get_elided_page_range(
page.number, on_each_side=2, on_ends=1
),
"complete": True,
}
return TemplateResponse(request, "import/troubleshoot.html", data)
def post(self, request, job_id):
"""retry lines from an import"""
job = get_object_or_404(models.ImportJob, id=job_id)
items = job.items.filter(fail_reason__isnull=False)
importer = Importer()
job = importer.create_retry_job(
request.user,
job,
items,
)
importer.start_import(job)
return redirect(f"/import/{job.id}")

View File

@ -1,6 +1,7 @@
""" non-interactive pages """
from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator
from django.db.models import Q, Count
from django.http import Http404
from django.shortcuts import redirect
from django.template.response import TemplateResponse
@ -105,9 +106,8 @@ class Followers(View):
if is_api_request(request):
return ActivitypubResponse(user.to_followers_activity(**request.GET))
paginated = Paginator(
user.followers.order_by("-created_date").all(), PAGE_LENGTH
)
followers = annotate_if_follows(request.user, user.followers)
paginated = Paginator(followers.all(), PAGE_LENGTH)
data = {
"user": user,
"is_self": request.user.id == user.id,
@ -126,9 +126,8 @@ class Following(View):
if is_api_request(request):
return ActivitypubResponse(user.to_following_activity(**request.GET))
paginated = Paginator(
user.following.order_by("-created_date").all(), PAGE_LENGTH
)
following = annotate_if_follows(request.user, user.following)
paginated = Paginator(following.all(), PAGE_LENGTH)
data = {
"user": user,
"is_self": request.user.id == user.id,
@ -137,6 +136,16 @@ class Following(View):
return TemplateResponse(request, "user/relationships/following.html", data)
def annotate_if_follows(user, queryset):
"""Sort a list of users by if you follow them"""
if not user.is_authenticated:
return queryset.order_by("-created_date")
return queryset.annotate(
request_user_follows=Count("followers", filter=Q(followers=user))
).order_by("-request_user_follows", "-created_date")
class Groups(View):
"""list of user's groups view"""

View File

@ -9,7 +9,7 @@ from django.utils import timezone
from django.views.decorators.http import require_GET
from bookwyrm import models
from bookwyrm.settings import DOMAIN, VERSION, MEDIA_FULL_URL, STATIC_FULL_URL
from bookwyrm.settings import DOMAIN, VERSION
@require_GET
@ -93,7 +93,7 @@ def instance_info(_):
status_count = models.Status.objects.filter(user__local=True, deleted=False).count()
site = models.SiteSettings.get()
logo = get_image_url(site.logo, "logo.png")
logo = site.logo_url
return JsonResponse(
{
"uri": DOMAIN,
@ -108,7 +108,8 @@ def instance_info(_):
"thumbnail": logo,
"languages": ["en"],
"registrations": site.allow_registration,
"approval_required": site.allow_registration and site.allow_invite_requests,
"approval_required": not site.allow_registration
and site.allow_invite_requests,
"email": site.admin_email,
}
)
@ -133,14 +134,7 @@ def host_meta(request):
def opensearch(request):
"""Open Search xml spec"""
site = models.SiteSettings.get()
image = get_image_url(site.favicon, "favicon.png")
image = site.favicon_url
return TemplateResponse(
request, "opensearch.xml", {"image": image, "DOMAIN": DOMAIN}
)
def get_image_url(obj, fallback):
"""helper for loading the full path to an image"""
if obj:
return f"{MEDIA_FULL_URL}{obj}"
return f"{STATIC_FULL_URL}images/{fallback}"