prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Import from Goodreads doesn't work correctly
  **Describe the bug**
  
  Import  from goodreads csv imports only first line of csv and stops with 'success' status. If user tries to reimport same csv again importer takes the same first imported line yet again. 
  
  Broken import examples https://bookwyrm.social/import/775 https://bookwyrm.social/import/776
  
  **Expected behavior**
  Importer correctly imports all lines of csv or returns error message to user
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `bookwyrm/views/import_data.py`
  Content:
  ```
  1 """ import books from another app """
  2 from io import TextIOWrapper
  3 
  4 from django.contrib.auth.decorators import login_required
  5 from django.core.exceptions import PermissionDenied
  6 from django.http import HttpResponseBadRequest
  7 from django.shortcuts import get_object_or_404, redirect
  8 from django.template.response import TemplateResponse
  9 from django.utils.decorators import method_decorator
  10 from django.utils.translation import gettext_lazy as _
  11 from django.views import View
  12 
  13 from bookwyrm import forms, models
  14 from bookwyrm.importers import (
  15     Importer,
  16     LibrarythingImporter,
  17     GoodreadsImporter,
  18     StorygraphImporter,
  19 )
  20 from bookwyrm.tasks import app
  21 
  22 # pylint: disable= no-self-use
  23 @method_decorator(login_required, name="dispatch")
  24 class Import(View):
  25     """import view"""
  26 
  27     def get(self, request):
  28         """load import page"""
  29         return TemplateResponse(
  30             request,
  31             "import.html",
  32             {
  33                 "import_form": forms.ImportForm(),
  34                 "jobs": models.ImportJob.objects.filter(user=request.user).order_by(
  35                     "-created_date"
  36                 ),
  37             },
  38         )
  39 
  40     def post(self, request):
  41         """ingest a goodreads csv"""
  42         form = forms.ImportForm(request.POST, request.FILES)
  43         if form.is_valid():
  44             include_reviews = request.POST.get("include_reviews") == "on"
  45             privacy = request.POST.get("privacy")
  46             source = request.POST.get("source")
  47 
  48             importer = None
  49             if source == "LibraryThing":
  50                 importer = LibrarythingImporter()
  51             elif source == "Storygraph":
  52                 importer = StorygraphImporter()
  53             else:
  54                 # Default : GoodReads
  55                 importer = GoodreadsImporter()
  56 
  57             try:
  58                 job = importer.create_job(
  59                     request.user,
  60                     TextIOWrapper(
  61                         request.FILES["csv_file"], encoding=importer.encoding
  62                     ),
  63                     include_reviews,
  64                     privacy,
  65                 )
  66             except (UnicodeDecodeError, ValueError, KeyError):
  67                 return HttpResponseBadRequest(_("Not a valid csv file"))
  68 
  69             importer.start_import(job)
  70 
  71             return redirect("/import/%d" % job.id)
  72         return HttpResponseBadRequest()
  73 
  74 
  75 @method_decorator(login_required, name="dispatch")
  76 class ImportStatus(View):
  77     """status of an existing import"""
  78 
  79     def get(self, request, job_id):
  80         """status of an import job"""
  81         job = get_object_or_404(models.ImportJob, id=job_id)
  82         if job.user != request.user:
  83             raise PermissionDenied
  84 
  85         try:
  86             task = app.AsyncResult(job.task_id)
  87             # triggers attribute error if the task won't load
  88             task.status  # pylint: disable=pointless-statement
  89         except (ValueError, AttributeError):
  90             task = None
  91 
  92         items = job.items.order_by("index").all()
  93         failed_items = [i for i in items if i.fail_reason]
  94         items = [i for i in items if not i.fail_reason]
  95         return TemplateResponse(
  96             request,
  97             "import_status.html",
  98             {"job": job, "items": items, "failed_items": failed_items, "task": task},
  99         )
  100 
  101     def post(self, request, job_id):
  102         """retry lines from an import"""
  103         job = get_object_or_404(models.ImportJob, id=job_id)
  104         items = []
  105         for item in request.POST.getlist("import_item"):
  106             items.append(get_object_or_404(models.ImportItem, id=item))
  107 
  108         importer = Importer()
  109         job = importer.create_retry_job(
  110             request.user,
  111             job,
  112             items,
  113         )
  114         importer.start_import(job)
  115         return redirect("/import/%d" % job.id)
  116
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/bookwyrm/views/import_data.py b/bookwyrm/views/import_data.py
  --- a/bookwyrm/views/import_data.py
  +++ b/bookwyrm/views/import_data.py
  @@ -28,7 +28,7 @@
           """load import page"""
           return TemplateResponse(
               request,
  -            "import.html",
  +            "import/import.html",
               {
                   "import_form": forms.ImportForm(),
                   "jobs": models.ImportJob.objects.filter(user=request.user).order_by(
  @@ -94,7 +94,7 @@
           items = [i for i in items if not i.fail_reason]
           return TemplateResponse(
               request,
  -            "import_status.html",
  +            "import/import_status.html",
               {"job": job, "items": items, "failed_items": failed_items, "task": task},
           )
  
verification_info: {"golden_diff": "diff --git a/bookwyrm/views/import_data.py b/bookwyrm/views/import_data.py\n--- a/bookwyrm/views/import_data.py\n+++ b/bookwyrm/views/import_data.py\n@@ -28,7 +28,7 @@\n         \"\"\"load import page\"\"\"\n         return TemplateResponse(\n             request,\n-            \"import.html\",\n+            \"import/import.html\",\n             {\n                 \"import_form\": forms.ImportForm(),\n                 \"jobs\": models.ImportJob.objects.filter(user=request.user).order_by(\n@@ -94,7 +94,7 @@\n         items = [i for i in items if not i.fail_reason]\n         return TemplateResponse(\n             request,\n-            \"import_status.html\",\n+            \"import/import_status.html\",\n             {\"job\": job, \"items\": items, \"failed_items\": failed_items, \"task\": task},\n         )\n", "issue": "Import from Goodreads doesn't work correctly\n**Describe the bug**\r\n\r\nImport  from goodreads csv imports only first line of csv and stops with 'success' status. If user tries to reimport same csv again importer takes the same first imported line yet again. \r\n\r\nBroken import examples https://bookwyrm.social/import/775 https://bookwyrm.social/import/776\r\n\r\n**Expected behavior**\r\nImporter correctly imports all lines of csv or returns error message to user\n", "before_files": [{"content": "\"\"\" import books from another app \"\"\"\nfrom io import TextIOWrapper\n\nfrom django.contrib.auth.decorators import login_required\nfrom django.core.exceptions import PermissionDenied\nfrom django.http import HttpResponseBadRequest\nfrom django.shortcuts import get_object_or_404, redirect\nfrom django.template.response import TemplateResponse\nfrom django.utils.decorators import method_decorator\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views import View\n\nfrom bookwyrm import forms, models\nfrom bookwyrm.importers import (\n    Importer,\n    LibrarythingImporter,\n    GoodreadsImporter,\n    StorygraphImporter,\n)\nfrom bookwyrm.tasks import app\n\n# pylint: disable= no-self-use\n@method_decorator(login_required, name=\"dispatch\")\nclass Import(View):\n    \"\"\"import view\"\"\"\n\n    def get(self, request):\n        \"\"\"load import page\"\"\"\n        return TemplateResponse(\n            request,\n            \"import.html\",\n            {\n                \"import_form\": forms.ImportForm(),\n                \"jobs\": models.ImportJob.objects.filter(user=request.user).order_by(\n                    \"-created_date\"\n                ),\n            },\n        )\n\n    def post(self, request):\n        \"\"\"ingest a goodreads csv\"\"\"\n        form = forms.ImportForm(request.POST, request.FILES)\n        if form.is_valid():\n            include_reviews = request.POST.get(\"include_reviews\") == \"on\"\n            privacy = request.POST.get(\"privacy\")\n            source = request.POST.get(\"source\")\n\n            importer = None\n            if source == \"LibraryThing\":\n                importer = LibrarythingImporter()\n            elif source == \"Storygraph\":\n                importer = StorygraphImporter()\n            else:\n                # Default : GoodReads\n                importer = GoodreadsImporter()\n\n            try:\n                job = importer.create_job(\n                    request.user,\n                    TextIOWrapper(\n                        request.FILES[\"csv_file\"], encoding=importer.encoding\n                    ),\n                    include_reviews,\n                    privacy,\n                )\n            except (UnicodeDecodeError, ValueError, KeyError):\n                return HttpResponseBadRequest(_(\"Not a valid csv file\"))\n\n            importer.start_import(job)\n\n            return redirect(\"/import/%d\" % job.id)\n        return HttpResponseBadRequest()\n\n\n@method_decorator(login_required, name=\"dispatch\")\nclass ImportStatus(View):\n    \"\"\"status of an existing import\"\"\"\n\n    def get(self, request, job_id):\n        \"\"\"status of an import job\"\"\"\n        job = get_object_or_404(models.ImportJob, id=job_id)\n        if job.user != request.user:\n            raise PermissionDenied\n\n        try:\n            task = app.AsyncResult(job.task_id)\n            # triggers attribute error if the task won't load\n            task.status  # pylint: disable=pointless-statement\n        except (ValueError, AttributeError):\n            task = None\n\n        items = job.items.order_by(\"index\").all()\n        failed_items = [i for i in items if i.fail_reason]\n        items = [i for i in items if not i.fail_reason]\n        return TemplateResponse(\n            request,\n            \"import_status.html\",\n            {\"job\": job, \"items\": items, \"failed_items\": failed_items, \"task\": task},\n        )\n\n    def post(self, request, job_id):\n        \"\"\"retry lines from an import\"\"\"\n        job = get_object_or_404(models.ImportJob, id=job_id)\n        items = []\n        for item in request.POST.getlist(\"import_item\"):\n            items.append(get_object_or_404(models.ImportItem, id=item))\n\n        importer = Importer()\n        job = importer.create_retry_job(\n            request.user,\n            job,\n            items,\n        )\n        importer.start_import(job)\n        return redirect(\"/import/%d\" % job.id)\n", "path": "bookwyrm/views/import_data.py"}], "after_files": [{"content": "\"\"\" import books from another app \"\"\"\nfrom io import TextIOWrapper\n\nfrom django.contrib.auth.decorators import login_required\nfrom django.core.exceptions import PermissionDenied\nfrom django.http import HttpResponseBadRequest\nfrom django.shortcuts import get_object_or_404, redirect\nfrom django.template.response import TemplateResponse\nfrom django.utils.decorators import method_decorator\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views import View\n\nfrom bookwyrm import forms, models\nfrom bookwyrm.importers import (\n    Importer,\n    LibrarythingImporter,\n    GoodreadsImporter,\n    StorygraphImporter,\n)\nfrom bookwyrm.tasks import app\n\n# pylint: disable= no-self-use\n@method_decorator(login_required, name=\"dispatch\")\nclass Import(View):\n    \"\"\"import view\"\"\"\n\n    def get(self, request):\n        \"\"\"load import page\"\"\"\n        return TemplateResponse(\n            request,\n            \"import/import.html\",\n            {\n                \"import_form\": forms.ImportForm(),\n                \"jobs\": models.ImportJob.objects.filter(user=request.user).order_by(\n                    \"-created_date\"\n                ),\n            },\n        )\n\n    def post(self, request):\n        \"\"\"ingest a goodreads csv\"\"\"\n        form = forms.ImportForm(request.POST, request.FILES)\n        if form.is_valid():\n            include_reviews = request.POST.get(\"include_reviews\") == \"on\"\n            privacy = request.POST.get(\"privacy\")\n            source = request.POST.get(\"source\")\n\n            importer = None\n            if source == \"LibraryThing\":\n                importer = LibrarythingImporter()\n            elif source == \"Storygraph\":\n                importer = StorygraphImporter()\n            else:\n                # Default : GoodReads\n                importer = GoodreadsImporter()\n\n            try:\n                job = importer.create_job(\n                    request.user,\n                    TextIOWrapper(\n                        request.FILES[\"csv_file\"], encoding=importer.encoding\n                    ),\n                    include_reviews,\n                    privacy,\n                )\n            except (UnicodeDecodeError, ValueError, KeyError):\n                return HttpResponseBadRequest(_(\"Not a valid csv file\"))\n\n            importer.start_import(job)\n\n            return redirect(\"/import/%d\" % job.id)\n        return HttpResponseBadRequest()\n\n\n@method_decorator(login_required, name=\"dispatch\")\nclass ImportStatus(View):\n    \"\"\"status of an existing import\"\"\"\n\n    def get(self, request, job_id):\n        \"\"\"status of an import job\"\"\"\n        job = get_object_or_404(models.ImportJob, id=job_id)\n        if job.user != request.user:\n            raise PermissionDenied\n\n        try:\n            task = app.AsyncResult(job.task_id)\n            # triggers attribute error if the task won't load\n            task.status  # pylint: disable=pointless-statement\n        except (ValueError, AttributeError):\n            task = None\n\n        items = job.items.order_by(\"index\").all()\n        failed_items = [i for i in items if i.fail_reason]\n        items = [i for i in items if not i.fail_reason]\n        return TemplateResponse(\n            request,\n            \"import/import_status.html\",\n            {\"job\": job, \"items\": items, \"failed_items\": failed_items, \"task\": task},\n        )\n\n    def post(self, request, job_id):\n        \"\"\"retry lines from an import\"\"\"\n        job = get_object_or_404(models.ImportJob, id=job_id)\n        items = []\n        for item in request.POST.getlist(\"import_item\"):\n            items.append(get_object_or_404(models.ImportItem, id=item))\n\n        importer = Importer()\n        job = importer.create_retry_job(\n            request.user,\n            job,\n            items,\n        )\n        importer.start_import(job)\n        return redirect(\"/import/%d\" % job.id)\n", "path": "bookwyrm/views/import_data.py"}]}