prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Bad matches in works Mass tagging system
  I was trying to add subject "Comedy" to the works and these are the first matches I get 
  ![зображення](https://github.com/internetarchive/openlibrary/assets/58372007/47771127-28bc-4373-8945-e483dba97c00)
  And when I try to manually search for the exact match there are many more subjects with word "coming" that have less works than the actual "Comedy". I think the exact matches should be the first one in the list or at least be boosted.
  
  ### Evidence / Screenshot (if possible)
  
  ### Relevant url?
  <!-- `https://openlibrary.org/...` -->
  
  ### Steps to Reproduce
  <!-- What steps caused you to find the bug? -->
  1. Go to ...
  2. Do ...
  
  <!-- What actually happened after these steps? What did you expect to happen? -->
  * Actual:
  * Expected:
  
  ### Details
  
  - **Logged in (Y/N)?**
  - **Browser type/version?**
  - **Operating system?**
  - **Environment (prod/dev/local)?** prod
  <!-- If not sure, put prod -->
  
  ### Proposal & Constraints
  <!-- What is the proposed solution / implementation? Is there a precedent of this approach succeeding elsewhere? -->
  
  ### Related files
  <!-- Files related to this issue; this is super useful for new contributors who might want to help! If you're not sure, leave this blank; a maintainer will add them. -->
  
  ### Stakeholders
  <!-- @ tag stakeholders of this bug -->
  
  
  </issue>
  <code>
  [start of openlibrary/plugins/openlibrary/bulk_tag.py]
  1 from infogami.utils import delegate
  2 from infogami.utils.view import render_template, public
  3 from openlibrary.utils import uniq
  4 import web
  5 import json
  6 
  7 
  8 class tags_partials(delegate.page):
  9     path = "/tags/partials"
  10     encoding = "json"
  11 
  12     def GET(self):
  13         i = web.input(key=None)
  14 
  15         works = i.work_ids
  16 
  17         tagging_menu = render_template('subjects/tagging_menu', works)
  18 
  19         partials = {
  20             'tagging_menu': str(tagging_menu),
  21         }
  22 
  23         return delegate.RawText(json.dumps(partials))
  24 
  25 
  26 class bulk_tag_works(delegate.page):
  27     path = "/tags/bulk_tag_works"
  28 
  29     def POST(self):
  30         i = web.input(work_ids='', tag_subjects='{}')
  31         works = i.work_ids.split(',')
  32         incoming_subjects = json.loads(i.tag_subjects)
  33         docs_to_update = []
  34 
  35         for work in works:
  36             w = web.ctx.site.get(f"/works/{work}")
  37             current_subjects = {
  38                 'subjects': uniq(w.get('subjects', '')),
  39                 'subject_people': uniq(w.get('subject_people', '')),
  40                 'subject_places': uniq(w.get('subject_places', '')),
  41                 'subject_times': uniq(w.get('subject_times', '')),
  42             }
  43             for subject_type, subject_list in incoming_subjects.items():
  44                 if subject_list:
  45                     current_subjects[subject_type] = uniq(  # dedupe incoming subjects
  46                         current_subjects[subject_type] + subject_list
  47                     )
  48                     w[subject_type] = current_subjects[subject_type]
  49 
  50             docs_to_update.append(
  51                 w.dict()
  52             )  # need to convert class to raw dict in order for save_many to work
  53 
  54         web.ctx.site.save_many(docs_to_update, comment="Bulk tagging works")
  55 
  56         def response(msg, status="success"):
  57             return delegate.RawText(
  58                 json.dumps({status: msg}), content_type="application/json"
  59             )
  60 
  61         return response('Tagged works successfully')
  62 
  63 
  64 def setup():
  65     pass
  66
  [end of openlibrary/plugins/openlibrary/bulk_tag.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/openlibrary/plugins/openlibrary/bulk_tag.py b/openlibrary/plugins/openlibrary/bulk_tag.py
  --- a/openlibrary/plugins/openlibrary/bulk_tag.py
  +++ b/openlibrary/plugins/openlibrary/bulk_tag.py
  @@ -5,31 +5,16 @@
   import json
   
   
  -class tags_partials(delegate.page):
  -    path = "/tags/partials"
  -    encoding = "json"
  -
  -    def GET(self):
  -        i = web.input(key=None)
  -
  -        works = i.work_ids
  -
  -        tagging_menu = render_template('subjects/tagging_menu', works)
  -
  -        partials = {
  -            'tagging_menu': str(tagging_menu),
  -        }
  -
  -        return delegate.RawText(json.dumps(partials))
  -
  -
   class bulk_tag_works(delegate.page):
       path = "/tags/bulk_tag_works"
   
       def POST(self):
  -        i = web.input(work_ids='', tag_subjects='{}')
  +        i = web.input(work_ids='', tags_to_add='', tags_to_remove='')
  +
           works = i.work_ids.split(',')
  -        incoming_subjects = json.loads(i.tag_subjects)
  +        tags_to_add = json.loads(i.tags_to_add or '{}')
  +        tags_to_remove = json.loads(i.tags_to_remove or '{}')
  +
           docs_to_update = []
   
           for work in works:
  @@ -40,13 +25,22 @@
                   'subject_places': uniq(w.get('subject_places', '')),
                   'subject_times': uniq(w.get('subject_times', '')),
               }
  -            for subject_type, subject_list in incoming_subjects.items():
  -                if subject_list:
  +            for subject_type, add_list in tags_to_add.items():
  +                if add_list:
                       current_subjects[subject_type] = uniq(  # dedupe incoming subjects
  -                        current_subjects[subject_type] + subject_list
  +                        current_subjects[subject_type] + add_list
                       )
                       w[subject_type] = current_subjects[subject_type]
   
  +            for subject_type, remove_list in tags_to_remove.items():
  +                if remove_list:
  +                    current_subjects[subject_type] = [
  +                        item
  +                        for item in current_subjects[subject_type]
  +                        if item not in remove_list
  +                    ]
  +                    w[subject_type] = current_subjects[subject_type]
  +
               docs_to_update.append(
                   w.dict()
               )  # need to convert class to raw dict in order for save_many to work
  
verification_info: {"golden_diff": "diff --git a/openlibrary/plugins/openlibrary/bulk_tag.py b/openlibrary/plugins/openlibrary/bulk_tag.py\n--- a/openlibrary/plugins/openlibrary/bulk_tag.py\n+++ b/openlibrary/plugins/openlibrary/bulk_tag.py\n@@ -5,31 +5,16 @@\n import json\n \n \n-class tags_partials(delegate.page):\n-    path = \"/tags/partials\"\n-    encoding = \"json\"\n-\n-    def GET(self):\n-        i = web.input(key=None)\n-\n-        works = i.work_ids\n-\n-        tagging_menu = render_template('subjects/tagging_menu', works)\n-\n-        partials = {\n-            'tagging_menu': str(tagging_menu),\n-        }\n-\n-        return delegate.RawText(json.dumps(partials))\n-\n-\n class bulk_tag_works(delegate.page):\n     path = \"/tags/bulk_tag_works\"\n \n     def POST(self):\n-        i = web.input(work_ids='', tag_subjects='{}')\n+        i = web.input(work_ids='', tags_to_add='', tags_to_remove='')\n+\n         works = i.work_ids.split(',')\n-        incoming_subjects = json.loads(i.tag_subjects)\n+        tags_to_add = json.loads(i.tags_to_add or '{}')\n+        tags_to_remove = json.loads(i.tags_to_remove or '{}')\n+\n         docs_to_update = []\n \n         for work in works:\n@@ -40,13 +25,22 @@\n                 'subject_places': uniq(w.get('subject_places', '')),\n                 'subject_times': uniq(w.get('subject_times', '')),\n             }\n-            for subject_type, subject_list in incoming_subjects.items():\n-                if subject_list:\n+            for subject_type, add_list in tags_to_add.items():\n+                if add_list:\n                     current_subjects[subject_type] = uniq(  # dedupe incoming subjects\n-                        current_subjects[subject_type] + subject_list\n+                        current_subjects[subject_type] + add_list\n                     )\n                     w[subject_type] = current_subjects[subject_type]\n \n+            for subject_type, remove_list in tags_to_remove.items():\n+                if remove_list:\n+                    current_subjects[subject_type] = [\n+                        item\n+                        for item in current_subjects[subject_type]\n+                        if item not in remove_list\n+                    ]\n+                    w[subject_type] = current_subjects[subject_type]\n+\n             docs_to_update.append(\n                 w.dict()\n             )  # need to convert class to raw dict in order for save_many to work\n", "issue": "Bad matches in works Mass tagging system\nI was trying to add subject \"Comedy\" to the works and these are the first matches I get \r\n![\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043d\u044f](https://github.com/internetarchive/openlibrary/assets/58372007/47771127-28bc-4373-8945-e483dba97c00)\r\nAnd when I try to manually search for the exact match there are many more subjects with word \"coming\" that have less works than the actual \"Comedy\". I think the exact matches should be the first one in the list or at least be boosted.\r\n\r\n### Evidence / Screenshot (if possible)\r\n\r\n### Relevant url?\r\n<!-- `https://openlibrary.org/...` -->\r\n\r\n### Steps to Reproduce\r\n<!-- What steps caused you to find the bug? -->\r\n1. Go to ...\r\n2. Do ...\r\n\r\n<!-- What actually happened after these steps? What did you expect to happen? -->\r\n* Actual:\r\n* Expected:\r\n\r\n### Details\r\n\r\n- **Logged in (Y/N)?**\r\n- **Browser type/version?**\r\n- **Operating system?**\r\n- **Environment (prod/dev/local)?** prod\r\n<!-- If not sure, put prod -->\r\n\r\n### Proposal & Constraints\r\n<!-- What is the proposed solution / implementation? Is there a precedent of this approach succeeding elsewhere? -->\r\n\r\n### Related files\r\n<!-- Files related to this issue; this is super useful for new contributors who might want to help! If you're not sure, leave this blank; a maintainer will add them. -->\r\n\r\n### Stakeholders\r\n<!-- @ tag stakeholders of this bug -->\r\n\n", "before_files": [{"content": "from infogami.utils import delegate\nfrom infogami.utils.view import render_template, public\nfrom openlibrary.utils import uniq\nimport web\nimport json\n\n\nclass tags_partials(delegate.page):\n    path = \"/tags/partials\"\n    encoding = \"json\"\n\n    def GET(self):\n        i = web.input(key=None)\n\n        works = i.work_ids\n\n        tagging_menu = render_template('subjects/tagging_menu', works)\n\n        partials = {\n            'tagging_menu': str(tagging_menu),\n        }\n\n        return delegate.RawText(json.dumps(partials))\n\n\nclass bulk_tag_works(delegate.page):\n    path = \"/tags/bulk_tag_works\"\n\n    def POST(self):\n        i = web.input(work_ids='', tag_subjects='{}')\n        works = i.work_ids.split(',')\n        incoming_subjects = json.loads(i.tag_subjects)\n        docs_to_update = []\n\n        for work in works:\n            w = web.ctx.site.get(f\"/works/{work}\")\n            current_subjects = {\n                'subjects': uniq(w.get('subjects', '')),\n                'subject_people': uniq(w.get('subject_people', '')),\n                'subject_places': uniq(w.get('subject_places', '')),\n                'subject_times': uniq(w.get('subject_times', '')),\n            }\n            for subject_type, subject_list in incoming_subjects.items():\n                if subject_list:\n                    current_subjects[subject_type] = uniq(  # dedupe incoming subjects\n                        current_subjects[subject_type] + subject_list\n                    )\n                    w[subject_type] = current_subjects[subject_type]\n\n            docs_to_update.append(\n                w.dict()\n            )  # need to convert class to raw dict in order for save_many to work\n\n        web.ctx.site.save_many(docs_to_update, comment=\"Bulk tagging works\")\n\n        def response(msg, status=\"success\"):\n            return delegate.RawText(\n                json.dumps({status: msg}), content_type=\"application/json\"\n            )\n\n        return response('Tagged works successfully')\n\n\ndef setup():\n    pass\n", "path": "openlibrary/plugins/openlibrary/bulk_tag.py"}]}