Merge branch 'main' into isni-poc
This commit is contained in:
@ -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
|
||||
|
@ -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", "/"))
|
||||
|
@ -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)
|
||||
|
0
bookwyrm/views/imports/__init__.py
Normal file
0
bookwyrm/views/imports/__init__.py
Normal 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}")
|
79
bookwyrm/views/imports/import_status.py
Normal file
79
bookwyrm/views/imports/import_status.py
Normal 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)
|
72
bookwyrm/views/imports/manually_review.py
Normal file
72
bookwyrm/views/imports/manually_review.py
Normal 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)
|
54
bookwyrm/views/imports/troubleshoot.py
Normal file
54
bookwyrm/views/imports/troubleshoot.py
Normal 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}")
|
@ -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"""
|
||||
|
||||
|
@ -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}"
|
||||
|
Reference in New Issue
Block a user