diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index b895d69a3..6d0f65531 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -15,7 +15,7 @@ logger = logging.getLogger(__name__) class Importer: """Generic class for csv data import from an outside service""" - service = "Unknown" + service = "Import" delimiter = "," encoding = "UTF-8" @@ -50,6 +50,7 @@ class Importer: include_reviews=include_reviews, privacy=privacy, mappings=self.create_row_mappings(csv_reader.fieldnames), + source=self.service, ) for index, entry in rows: @@ -108,16 +109,16 @@ class Importer: @app.task(queue="low_priority") -def start_import_task(source, job_id): +def start_import_task(job_id): """trigger the child tasks for each row""" job = ImportJob.objects.get(id=job_id) # these are sub-tasks so that one big task doesn't use up all the memory in celery for item in job.items.values_list("id", flat=True).all(): - import_item_task.delay(source, item) + import_item_task.delay(item) @app.task(queue="low_priority") -def import_item_task(source, item_id): +def import_item_task(item_id): """resolve a row into a book""" item = models.ImportItem.objects.get(id=item_id) try: @@ -128,17 +129,18 @@ def import_item_task(source, item_id): raise err if item.book: - job = item.job # shelves book and handles reviews - handle_imported_book(source, job.user, item, job.include_reviews, job.privacy) + handle_imported_book(item) else: item.fail_reason = _("Could not find a match for book") item.save() -def handle_imported_book(source, user, item, include_reviews, privacy): +def handle_imported_book(item): """process a csv and then post about it""" + job = item.job + user = job.user if isinstance(item.book, models.Work): item.book = item.book.default_edition if not item.book: @@ -167,7 +169,7 @@ def handle_imported_book(source, user, item, include_reviews, privacy): read.user = user read.save() - if include_reviews and (item.rating or item.review): + if job.include_reviews and (item.rating or item.review): # we don't know the publication date of the review, # but "now" is a bad guess published_date_guess = item.date_read or item.date_added @@ -176,7 +178,7 @@ def handle_imported_book(source, user, item, include_reviews, privacy): review_title = ( "Review of {!r} on {!r}".format( item.book.title, - source, + job.source, ) if item.review else "" @@ -188,7 +190,7 @@ def handle_imported_book(source, user, item, include_reviews, privacy): content=item.review, rating=item.rating, published_date=published_date_guess, - privacy=privacy, + privacy=job.privacy, ) else: # just a rating @@ -197,7 +199,7 @@ def handle_imported_book(source, user, item, include_reviews, privacy): book=item.book, rating=item.rating, published_date=published_date_guess, - privacy=privacy, + privacy=job.privacy, ) # only broadcast this review to other bookwyrm instances review.save(software="bookwyrm", priority=LOW) diff --git a/bookwyrm/migrations/0114_importjob_source.py b/bookwyrm/migrations/0114_importjob_source.py new file mode 100644 index 000000000..3ec1432e3 --- /dev/null +++ b/bookwyrm/migrations/0114_importjob_source.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.5 on 2021-11-13 00:56 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookwyrm", "0113_auto_20211110_2104"), + ] + + operations = [ + migrations.AddField( + model_name="importjob", + name="source", + field=models.CharField(default="Import", max_length=100), + preserve_default=False, + ), + ] diff --git a/bookwyrm/models/import_job.py b/bookwyrm/models/import_job.py index aa86910c2..6b8f0b460 100644 --- a/bookwyrm/models/import_job.py +++ b/bookwyrm/models/import_job.py @@ -37,6 +37,7 @@ class ImportJob(models.Model): include_reviews = models.BooleanField(default=True) mappings = models.JSONField() complete = models.BooleanField(default=False) + source = models.CharField(max_length=100) privacy = models.CharField( max_length=255, default="public", choices=PrivacyLevels.choices ) @@ -62,6 +63,11 @@ class ImportItem(models.Model): def resolve(self): """try various ways to lookup a book""" + # we might be calling this after manually adding the book, + # so no need to do searches + if self.book: + return + if self.isbn: self.book = self.get_book_from_isbn() else: diff --git a/bookwyrm/templates/import/import_status.html b/bookwyrm/templates/import/import_status.html index b2e21d22c..12465bd73 100644 --- a/bookwyrm/templates/import/import_status.html +++ b/bookwyrm/templates/import/import_status.html @@ -40,10 +40,11 @@ {% if manual_review_count %}
{% blocktrans trimmed count counter=manual_review_count with display_counter=manual_review_count|intcomma %} - {{ display_counter }} item needs manual review. + {{ display_counter }} item needs manual approval. {% plural %} - {{ display_counter }} items need manual review. + {{ display_counter }} items need manual approval. {% endblocktrans %} + {% trans "Review items" %}
{% endif %} @@ -55,7 +56,7 @@ {{ display_counter }} items failed to import. {% endblocktrans %} - {% trans "View and troubleshoot failed items." %} + {% trans "View and troubleshoot failed items" %} {% endif %} diff --git a/bookwyrm/templates/import/manual_review.html b/bookwyrm/templates/import/manual_review.html index c9c038698..30274ca02 100644 --- a/bookwyrm/templates/import/manual_review.html +++ b/bookwyrm/templates/import/manual_review.html @@ -52,9 +52,22 @@ {{ item.normalized_data.authors }} - - - + +
+ {% csrf_token %} + +
+ +
+ {% csrf_token %} + +
diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index 1004e30bc..d6a79c202 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -237,17 +237,36 @@ urlpatterns = [ re_path(r"^search/?$", views.Search.as_view(), name="search"), # imports re_path(r"^import/?$", views.Import.as_view(), name="import"), - re_path(r"^import/(\d+)/?$", views.ImportStatus.as_view(), name="import-status"), re_path( - r"^import/(\d+)/failed/?$", + r"^import/(?P\d+)/?$", + views.ImportStatus.as_view(), + name="import-status", + ), + re_path( + r"^import/(?P\d+)/failed/?$", views.ImportTroubleshoot.as_view(), name="import-troubleshoot", ), re_path( - r"^import/(\d+)/review/?$", + r"^import/(?P\d+)/review/?$", views.ImportManualReview.as_view(), name="import-review", ), + re_path( + r"^import/(?P\d+)/review/?$", + views.ImportManualReview.as_view(), + name="import-review", + ), + re_path( + r"^import/(?P\d+)/review/(?P\d+)/approve/?$", + views.approve_import_item, + name="import-approve", + ), + re_path( + r"^import/(?P\d+)/review/(?P\d+)/delete/?$", + views.delete_import_item, + name="import-delete", + ), # users re_path(rf"{USER_PATH}\.json$", views.User.as_view()), re_path(rf"{USER_PATH}/?$", views.User.as_view(), name="user-feed"), diff --git a/bookwyrm/views/__init__.py b/bookwyrm/views/__init__.py index 9fe09795a..1a6fbdc69 100644 --- a/bookwyrm/views/__init__.py +++ b/bookwyrm/views/__init__.py @@ -47,7 +47,11 @@ from .shelf.shelf_actions import shelve, unshelve from .imports.import_data import Import from .imports.import_status import ImportStatus from .imports.troubleshoot import ImportTroubleshoot -from .imports.manually_review import ImportManualReview +from .imports.manually_review import ( + ImportManualReview, + approve_import_item, + delete_import_item, +) # misc views from .author import Author, EditAuthor diff --git a/bookwyrm/views/imports/manually_review.py b/bookwyrm/views/imports/manually_review.py index 286251d44..877e3ffc9 100644 --- a/bookwyrm/views/imports/manually_review.py +++ b/bookwyrm/views/imports/manually_review.py @@ -2,12 +2,14 @@ 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 +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 @@ -37,3 +39,33 @@ class ImportManualReview(View): } 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.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)