prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Imported ratings added as reviews
  During a goodreads import, star ratings seem to be added as Reviews, rather than ReviewRatings
  
  </issue>
  <code>
  [start of bookwyrm/importers/importer.py]
  1 """ handle reading a csv from an external service, defaults are from GoodReads """
  2 import csv
  3 import logging
  4 
  5 from bookwyrm import models
  6 from bookwyrm.models import ImportJob, ImportItem
  7 from bookwyrm.tasks import app
  8 
  9 logger = logging.getLogger(__name__)
  10 
  11 
  12 class Importer:
  13     """Generic class for csv data import from an outside service"""
  14 
  15     service = "Unknown"
  16     delimiter = ","
  17     encoding = "UTF-8"
  18     mandatory_fields = ["Title", "Author"]
  19 
  20     def create_job(self, user, csv_file, include_reviews, privacy):
  21         """check over a csv and creates a database entry for the job"""
  22         job = ImportJob.objects.create(
  23             user=user, include_reviews=include_reviews, privacy=privacy
  24         )
  25         for index, entry in enumerate(
  26             list(csv.DictReader(csv_file, delimiter=self.delimiter))
  27         ):
  28             if not all(x in entry for x in self.mandatory_fields):
  29                 raise ValueError("Author and title must be in data.")
  30             entry = self.parse_fields(entry)
  31             self.save_item(job, index, entry)
  32         return job
  33 
  34     def save_item(self, job, index, data):  # pylint: disable=no-self-use
  35         """creates and saves an import item"""
  36         ImportItem(job=job, index=index, data=data).save()
  37 
  38     def parse_fields(self, entry):
  39         """updates csv data with additional info"""
  40         entry.update({"import_source": self.service})
  41         return entry
  42 
  43     def create_retry_job(self, user, original_job, items):
  44         """retry items that didn't import"""
  45         job = ImportJob.objects.create(
  46             user=user,
  47             include_reviews=original_job.include_reviews,
  48             privacy=original_job.privacy,
  49             retry=True,
  50         )
  51         for item in items:
  52             self.save_item(job, item.index, item.data)
  53         return job
  54 
  55     def start_import(self, job):
  56         """initalizes a csv import job"""
  57         result = import_data.delay(self.service, job.id)
  58         job.task_id = result.id
  59         job.save()
  60 
  61 
  62 @app.task
  63 def import_data(source, job_id):
  64     """does the actual lookup work in a celery task"""
  65     job = ImportJob.objects.get(id=job_id)
  66     try:
  67         for item in job.items.all():
  68             try:
  69                 item.resolve()
  70             except Exception as e:  # pylint: disable=broad-except
  71                 logger.exception(e)
  72                 item.fail_reason = "Error loading book"
  73                 item.save()
  74                 continue
  75 
  76             if item.book:
  77                 item.save()
  78 
  79                 # shelves book and handles reviews
  80                 handle_imported_book(
  81                     source, job.user, item, job.include_reviews, job.privacy
  82                 )
  83             else:
  84                 item.fail_reason = "Could not find a match for book"
  85                 item.save()
  86     finally:
  87         job.complete = True
  88         job.save()
  89 
  90 
  91 def handle_imported_book(source, user, item, include_reviews, privacy):
  92     """process a csv and then post about it"""
  93     if isinstance(item.book, models.Work):
  94         item.book = item.book.default_edition
  95     if not item.book:
  96         return
  97 
  98     existing_shelf = models.ShelfBook.objects.filter(book=item.book, user=user).exists()
  99 
  100     # shelve the book if it hasn't been shelved already
  101     if item.shelf and not existing_shelf:
  102         desired_shelf = models.Shelf.objects.get(identifier=item.shelf, user=user)
  103         models.ShelfBook.objects.create(book=item.book, shelf=desired_shelf, user=user)
  104 
  105     for read in item.reads:
  106         # check for an existing readthrough with the same dates
  107         if models.ReadThrough.objects.filter(
  108             user=user,
  109             book=item.book,
  110             start_date=read.start_date,
  111             finish_date=read.finish_date,
  112         ).exists():
  113             continue
  114         read.book = item.book
  115         read.user = user
  116         read.save()
  117 
  118     if include_reviews and (item.rating or item.review):
  119         review_title = (
  120             "Review of {!r} on {!r}".format(
  121                 item.book.title,
  122                 source,
  123             )
  124             if item.review
  125             else ""
  126         )
  127 
  128         # we don't know the publication date of the review,
  129         # but "now" is a bad guess
  130         published_date_guess = item.date_read or item.date_added
  131         models.Review.objects.create(
  132             user=user,
  133             book=item.book,
  134             name=review_title,
  135             content=item.review,
  136             rating=item.rating,
  137             published_date=published_date_guess,
  138             privacy=privacy,
  139         )
  140
  [end of bookwyrm/importers/importer.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py
  --- a/bookwyrm/importers/importer.py
  +++ b/bookwyrm/importers/importer.py
  @@ -116,24 +116,33 @@
           read.save()
   
       if include_reviews and (item.rating or item.review):
  -        review_title = (
  -            "Review of {!r} on {!r}".format(
  -                item.book.title,
  -                source,
  -            )
  -            if item.review
  -            else ""
  -        )
  -
           # 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
  -        models.Review.objects.create(
  -            user=user,
  -            book=item.book,
  -            name=review_title,
  -            content=item.review,
  -            rating=item.rating,
  -            published_date=published_date_guess,
  -            privacy=privacy,
  -        )
  +        if item.review:
  +            review_title = (
  +                "Review of {!r} on {!r}".format(
  +                    item.book.title,
  +                    source,
  +                )
  +                if item.review
  +                else ""
  +            )
  +            models.Review.objects.create(
  +                user=user,
  +                book=item.book,
  +                name=review_title,
  +                content=item.review,
  +                rating=item.rating,
  +                published_date=published_date_guess,
  +                privacy=privacy,
  +            )
  +        else:
  +            # just a rating
  +            models.ReviewRating.objects.create(
  +                user=user,
  +                book=item.book,
  +                rating=item.rating,
  +                published_date=published_date_guess,
  +                privacy=privacy,
  +            )
  
verification_info: {"golden_diff": "diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py\n--- a/bookwyrm/importers/importer.py\n+++ b/bookwyrm/importers/importer.py\n@@ -116,24 +116,33 @@\n         read.save()\n \n     if include_reviews and (item.rating or item.review):\n-        review_title = (\n-            \"Review of {!r} on {!r}\".format(\n-                item.book.title,\n-                source,\n-            )\n-            if item.review\n-            else \"\"\n-        )\n-\n         # we don't know the publication date of the review,\n         # but \"now\" is a bad guess\n         published_date_guess = item.date_read or item.date_added\n-        models.Review.objects.create(\n-            user=user,\n-            book=item.book,\n-            name=review_title,\n-            content=item.review,\n-            rating=item.rating,\n-            published_date=published_date_guess,\n-            privacy=privacy,\n-        )\n+        if item.review:\n+            review_title = (\n+                \"Review of {!r} on {!r}\".format(\n+                    item.book.title,\n+                    source,\n+                )\n+                if item.review\n+                else \"\"\n+            )\n+            models.Review.objects.create(\n+                user=user,\n+                book=item.book,\n+                name=review_title,\n+                content=item.review,\n+                rating=item.rating,\n+                published_date=published_date_guess,\n+                privacy=privacy,\n+            )\n+        else:\n+            # just a rating\n+            models.ReviewRating.objects.create(\n+                user=user,\n+                book=item.book,\n+                rating=item.rating,\n+                published_date=published_date_guess,\n+                privacy=privacy,\n+            )\n", "issue": "Imported ratings added as reviews\nDuring a goodreads import, star ratings seem to be added as Reviews, rather than ReviewRatings\n", "before_files": [{"content": "\"\"\" handle reading a csv from an external service, defaults are from GoodReads \"\"\"\nimport csv\nimport logging\n\nfrom bookwyrm import models\nfrom bookwyrm.models import ImportJob, ImportItem\nfrom bookwyrm.tasks import app\n\nlogger = logging.getLogger(__name__)\n\n\nclass Importer:\n    \"\"\"Generic class for csv data import from an outside service\"\"\"\n\n    service = \"Unknown\"\n    delimiter = \",\"\n    encoding = \"UTF-8\"\n    mandatory_fields = [\"Title\", \"Author\"]\n\n    def create_job(self, user, csv_file, include_reviews, privacy):\n        \"\"\"check over a csv and creates a database entry for the job\"\"\"\n        job = ImportJob.objects.create(\n            user=user, include_reviews=include_reviews, privacy=privacy\n        )\n        for index, entry in enumerate(\n            list(csv.DictReader(csv_file, delimiter=self.delimiter))\n        ):\n            if not all(x in entry for x in self.mandatory_fields):\n                raise ValueError(\"Author and title must be in data.\")\n            entry = self.parse_fields(entry)\n            self.save_item(job, index, entry)\n        return job\n\n    def save_item(self, job, index, data):  # pylint: disable=no-self-use\n        \"\"\"creates and saves an import item\"\"\"\n        ImportItem(job=job, index=index, data=data).save()\n\n    def parse_fields(self, entry):\n        \"\"\"updates csv data with additional info\"\"\"\n        entry.update({\"import_source\": self.service})\n        return entry\n\n    def create_retry_job(self, user, original_job, items):\n        \"\"\"retry items that didn't import\"\"\"\n        job = ImportJob.objects.create(\n            user=user,\n            include_reviews=original_job.include_reviews,\n            privacy=original_job.privacy,\n            retry=True,\n        )\n        for item in items:\n            self.save_item(job, item.index, item.data)\n        return job\n\n    def start_import(self, job):\n        \"\"\"initalizes a csv import job\"\"\"\n        result = import_data.delay(self.service, job.id)\n        job.task_id = result.id\n        job.save()\n\n\n@app.task\ndef import_data(source, job_id):\n    \"\"\"does the actual lookup work in a celery task\"\"\"\n    job = ImportJob.objects.get(id=job_id)\n    try:\n        for item in job.items.all():\n            try:\n                item.resolve()\n            except Exception as e:  # pylint: disable=broad-except\n                logger.exception(e)\n                item.fail_reason = \"Error loading book\"\n                item.save()\n                continue\n\n            if item.book:\n                item.save()\n\n                # shelves book and handles reviews\n                handle_imported_book(\n                    source, job.user, item, job.include_reviews, job.privacy\n                )\n            else:\n                item.fail_reason = \"Could not find a match for book\"\n                item.save()\n    finally:\n        job.complete = True\n        job.save()\n\n\ndef handle_imported_book(source, user, item, include_reviews, privacy):\n    \"\"\"process a csv and then post about it\"\"\"\n    if isinstance(item.book, models.Work):\n        item.book = item.book.default_edition\n    if not item.book:\n        return\n\n    existing_shelf = models.ShelfBook.objects.filter(book=item.book, user=user).exists()\n\n    # shelve the book if it hasn't been shelved already\n    if item.shelf and not existing_shelf:\n        desired_shelf = models.Shelf.objects.get(identifier=item.shelf, user=user)\n        models.ShelfBook.objects.create(book=item.book, shelf=desired_shelf, user=user)\n\n    for read in item.reads:\n        # check for an existing readthrough with the same dates\n        if models.ReadThrough.objects.filter(\n            user=user,\n            book=item.book,\n            start_date=read.start_date,\n            finish_date=read.finish_date,\n        ).exists():\n            continue\n        read.book = item.book\n        read.user = user\n        read.save()\n\n    if include_reviews and (item.rating or item.review):\n        review_title = (\n            \"Review of {!r} on {!r}\".format(\n                item.book.title,\n                source,\n            )\n            if item.review\n            else \"\"\n        )\n\n        # we don't know the publication date of the review,\n        # but \"now\" is a bad guess\n        published_date_guess = item.date_read or item.date_added\n        models.Review.objects.create(\n            user=user,\n            book=item.book,\n            name=review_title,\n            content=item.review,\n            rating=item.rating,\n            published_date=published_date_guess,\n            privacy=privacy,\n        )\n", "path": "bookwyrm/importers/importer.py"}]}