prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  As a plugin writer, I want to have a function for touching content units
  Author: @lubosmj (lmjachky)
  
  
  Redmine Issue: 9419, https://pulp.plan.io/issues/9419
  
  ---
  
  In the PR https://github.com/pulp/pulpcore/pull/1624, we introduced a method that uses `bulk_touch` for updating timestamps of content units. We should expose this method to all plugin writers (e.g., pulp_container currently implements the same method - this creates unnecessary duplicates).
  
  
  
  
  </issue>
  <code>
  [start of pulpcore/plugin/actions.py]
  1 from gettext import gettext as _
  2 from drf_spectacular.utils import extend_schema
  3 from rest_framework.decorators import action
  4 from rest_framework.serializers import ValidationError
  5 
  6 from pulpcore.app import tasks
  7 from pulpcore.app.models import Content, RepositoryVersion
  8 from pulpcore.app.response import OperationPostponedResponse
  9 from pulpcore.app.serializers import (
  10     AsyncOperationResponseSerializer,
  11     RepositoryAddRemoveContentSerializer,
  12 )
  13 from pulpcore.app.viewsets import NamedModelViewSet
  14 from pulpcore.tasking.tasks import dispatch
  15 
  16 
  17 __all__ = ["ModifyRepositoryActionMixin"]
  18 
  19 
  20 class ModifyRepositoryActionMixin:
  21     @extend_schema(
  22         description="Trigger an asynchronous task to create a new repository version.",
  23         summary="Modify Repository Content",
  24         responses={202: AsyncOperationResponseSerializer},
  25     )
  26     @action(detail=True, methods=["post"], serializer_class=RepositoryAddRemoveContentSerializer)
  27     def modify(self, request, pk):
  28         """
  29         Queues a task that creates a new RepositoryVersion by adding and removing content units
  30         """
  31         add_content_units = {}
  32         remove_content_units = {}
  33 
  34         repository = self.get_object()
  35         serializer = self.get_serializer(data=request.data)
  36         serializer.is_valid(raise_exception=True)
  37 
  38         if "base_version" in request.data:
  39             base_version_pk = self.get_resource(request.data["base_version"], RepositoryVersion).pk
  40         else:
  41             base_version_pk = None
  42 
  43         if "add_content_units" in request.data:
  44             for url in request.data["add_content_units"]:
  45                 add_content_units[NamedModelViewSet.extract_pk(url)] = url
  46 
  47             content_units_pks = set(add_content_units.keys())
  48             existing_content_units = Content.objects.filter(pk__in=content_units_pks)
  49             existing_content_units.touch()
  50 
  51             self.verify_content_units(existing_content_units, add_content_units)
  52 
  53             add_content_units = list(add_content_units.keys())
  54 
  55         if "remove_content_units" in request.data:
  56             if "*" in request.data["remove_content_units"]:
  57                 remove_content_units = ["*"]
  58             else:
  59                 for url in request.data["remove_content_units"]:
  60                     remove_content_units[NamedModelViewSet.extract_pk(url)] = url
  61                 content_units_pks = set(remove_content_units.keys())
  62                 existing_content_units = Content.objects.filter(pk__in=content_units_pks)
  63                 self.verify_content_units(existing_content_units, remove_content_units)
  64                 remove_content_units = list(remove_content_units.keys())
  65 
  66         task = dispatch(
  67             tasks.repository.add_and_remove,
  68             exclusive_resources=[repository],
  69             kwargs={
  70                 "repository_pk": pk,
  71                 "base_version_pk": base_version_pk,
  72                 "add_content_units": add_content_units,
  73                 "remove_content_units": remove_content_units,
  74             },
  75         )
  76         return OperationPostponedResponse(task, request)
  77 
  78     def verify_content_units(self, content_units, all_content_units):
  79         """Verify referenced content units."""
  80         existing_content_units_pks = content_units.values_list("pk", flat=True)
  81         existing_content_units_pks = {str(pk) for pk in existing_content_units_pks}
  82 
  83         missing_pks = set(all_content_units.keys()) - existing_content_units_pks
  84         if missing_pks:
  85             missing_hrefs = [all_content_units[pk] for pk in missing_pks]
  86             raise ValidationError(
  87                 _("Could not find the following content units: {}").format(missing_hrefs)
  88             )
  89
  [end of pulpcore/plugin/actions.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/pulpcore/plugin/actions.py b/pulpcore/plugin/actions.py
  --- a/pulpcore/plugin/actions.py
  +++ b/pulpcore/plugin/actions.py
  @@ -1,4 +1,5 @@
   from gettext import gettext as _
  +
   from drf_spectacular.utils import extend_schema
   from rest_framework.decorators import action
   from rest_framework.serializers import ValidationError
  @@ -48,7 +49,7 @@
               existing_content_units = Content.objects.filter(pk__in=content_units_pks)
               existing_content_units.touch()
   
  -            self.verify_content_units(existing_content_units, add_content_units)
  +            raise_for_unknown_content_units(existing_content_units, add_content_units)
   
               add_content_units = list(add_content_units.keys())
   
  @@ -60,7 +61,7 @@
                       remove_content_units[NamedModelViewSet.extract_pk(url)] = url
                   content_units_pks = set(remove_content_units.keys())
                   existing_content_units = Content.objects.filter(pk__in=content_units_pks)
  -                self.verify_content_units(existing_content_units, remove_content_units)
  +                raise_for_unknown_content_units(existing_content_units, remove_content_units)
                   remove_content_units = list(remove_content_units.keys())
   
           task = dispatch(
  @@ -75,14 +76,24 @@
           )
           return OperationPostponedResponse(task, request)
   
  -    def verify_content_units(self, content_units, all_content_units):
  -        """Verify referenced content units."""
  -        existing_content_units_pks = content_units.values_list("pk", flat=True)
  -        existing_content_units_pks = {str(pk) for pk in existing_content_units_pks}
  -
  -        missing_pks = set(all_content_units.keys()) - existing_content_units_pks
  -        if missing_pks:
  -            missing_hrefs = [all_content_units[pk] for pk in missing_pks]
  -            raise ValidationError(
  -                _("Could not find the following content units: {}").format(missing_hrefs)
  -            )
  +
  +def raise_for_unknown_content_units(existing_content_units, content_units_pks_hrefs):
  +    """Verify if all the specified content units were found in the database.
  +
  +    Args:
  +        existing_content_units (pulpcore.plugin.models.Content): Content filtered by
  +            specified_content_units.
  +        content_units_pks_hrefs (dict): An original dictionary of pk-href pairs that
  +            are used for the verification.
  +    Raises:
  +        ValidationError: If some of the referenced content units are not present in the database
  +    """
  +    existing_content_units_pks = existing_content_units.values_list("pk", flat=True)
  +    existing_content_units_pks = set(map(str, existing_content_units_pks))
  +
  +    missing_pks = set(content_units_pks_hrefs.keys()) - existing_content_units_pks
  +    if missing_pks:
  +        missing_hrefs = [content_units_pks_hrefs[pk] for pk in missing_pks]
  +        raise ValidationError(
  +            _("Could not find the following content units: {}").format(missing_hrefs)
  +        )
  
verification_info: {"golden_diff": "diff --git a/pulpcore/plugin/actions.py b/pulpcore/plugin/actions.py\n--- a/pulpcore/plugin/actions.py\n+++ b/pulpcore/plugin/actions.py\n@@ -1,4 +1,5 @@\n from gettext import gettext as _\n+\n from drf_spectacular.utils import extend_schema\n from rest_framework.decorators import action\n from rest_framework.serializers import ValidationError\n@@ -48,7 +49,7 @@\n             existing_content_units = Content.objects.filter(pk__in=content_units_pks)\n             existing_content_units.touch()\n \n-            self.verify_content_units(existing_content_units, add_content_units)\n+            raise_for_unknown_content_units(existing_content_units, add_content_units)\n \n             add_content_units = list(add_content_units.keys())\n \n@@ -60,7 +61,7 @@\n                     remove_content_units[NamedModelViewSet.extract_pk(url)] = url\n                 content_units_pks = set(remove_content_units.keys())\n                 existing_content_units = Content.objects.filter(pk__in=content_units_pks)\n-                self.verify_content_units(existing_content_units, remove_content_units)\n+                raise_for_unknown_content_units(existing_content_units, remove_content_units)\n                 remove_content_units = list(remove_content_units.keys())\n \n         task = dispatch(\n@@ -75,14 +76,24 @@\n         )\n         return OperationPostponedResponse(task, request)\n \n-    def verify_content_units(self, content_units, all_content_units):\n-        \"\"\"Verify referenced content units.\"\"\"\n-        existing_content_units_pks = content_units.values_list(\"pk\", flat=True)\n-        existing_content_units_pks = {str(pk) for pk in existing_content_units_pks}\n-\n-        missing_pks = set(all_content_units.keys()) - existing_content_units_pks\n-        if missing_pks:\n-            missing_hrefs = [all_content_units[pk] for pk in missing_pks]\n-            raise ValidationError(\n-                _(\"Could not find the following content units: {}\").format(missing_hrefs)\n-            )\n+\n+def raise_for_unknown_content_units(existing_content_units, content_units_pks_hrefs):\n+    \"\"\"Verify if all the specified content units were found in the database.\n+\n+    Args:\n+        existing_content_units (pulpcore.plugin.models.Content): Content filtered by\n+            specified_content_units.\n+        content_units_pks_hrefs (dict): An original dictionary of pk-href pairs that\n+            are used for the verification.\n+    Raises:\n+        ValidationError: If some of the referenced content units are not present in the database\n+    \"\"\"\n+    existing_content_units_pks = existing_content_units.values_list(\"pk\", flat=True)\n+    existing_content_units_pks = set(map(str, existing_content_units_pks))\n+\n+    missing_pks = set(content_units_pks_hrefs.keys()) - existing_content_units_pks\n+    if missing_pks:\n+        missing_hrefs = [content_units_pks_hrefs[pk] for pk in missing_pks]\n+        raise ValidationError(\n+            _(\"Could not find the following content units: {}\").format(missing_hrefs)\n+        )\n", "issue": "As a plugin writer, I want to have a function for touching content units\nAuthor: @lubosmj (lmjachky)\n\n\nRedmine Issue: 9419, https://pulp.plan.io/issues/9419\n\n---\n\nIn the PR https://github.com/pulp/pulpcore/pull/1624, we introduced a method that uses `bulk_touch` for updating timestamps of content units. We should expose this method to all plugin writers (e.g., pulp_container currently implements the same method - this creates unnecessary duplicates).\n\n\n\n", "before_files": [{"content": "from gettext import gettext as _\nfrom drf_spectacular.utils import extend_schema\nfrom rest_framework.decorators import action\nfrom rest_framework.serializers import ValidationError\n\nfrom pulpcore.app import tasks\nfrom pulpcore.app.models import Content, RepositoryVersion\nfrom pulpcore.app.response import OperationPostponedResponse\nfrom pulpcore.app.serializers import (\n    AsyncOperationResponseSerializer,\n    RepositoryAddRemoveContentSerializer,\n)\nfrom pulpcore.app.viewsets import NamedModelViewSet\nfrom pulpcore.tasking.tasks import dispatch\n\n\n__all__ = [\"ModifyRepositoryActionMixin\"]\n\n\nclass ModifyRepositoryActionMixin:\n    @extend_schema(\n        description=\"Trigger an asynchronous task to create a new repository version.\",\n        summary=\"Modify Repository Content\",\n        responses={202: AsyncOperationResponseSerializer},\n    )\n    @action(detail=True, methods=[\"post\"], serializer_class=RepositoryAddRemoveContentSerializer)\n    def modify(self, request, pk):\n        \"\"\"\n        Queues a task that creates a new RepositoryVersion by adding and removing content units\n        \"\"\"\n        add_content_units = {}\n        remove_content_units = {}\n\n        repository = self.get_object()\n        serializer = self.get_serializer(data=request.data)\n        serializer.is_valid(raise_exception=True)\n\n        if \"base_version\" in request.data:\n            base_version_pk = self.get_resource(request.data[\"base_version\"], RepositoryVersion).pk\n        else:\n            base_version_pk = None\n\n        if \"add_content_units\" in request.data:\n            for url in request.data[\"add_content_units\"]:\n                add_content_units[NamedModelViewSet.extract_pk(url)] = url\n\n            content_units_pks = set(add_content_units.keys())\n            existing_content_units = Content.objects.filter(pk__in=content_units_pks)\n            existing_content_units.touch()\n\n            self.verify_content_units(existing_content_units, add_content_units)\n\n            add_content_units = list(add_content_units.keys())\n\n        if \"remove_content_units\" in request.data:\n            if \"*\" in request.data[\"remove_content_units\"]:\n                remove_content_units = [\"*\"]\n            else:\n                for url in request.data[\"remove_content_units\"]:\n                    remove_content_units[NamedModelViewSet.extract_pk(url)] = url\n                content_units_pks = set(remove_content_units.keys())\n                existing_content_units = Content.objects.filter(pk__in=content_units_pks)\n                self.verify_content_units(existing_content_units, remove_content_units)\n                remove_content_units = list(remove_content_units.keys())\n\n        task = dispatch(\n            tasks.repository.add_and_remove,\n            exclusive_resources=[repository],\n            kwargs={\n                \"repository_pk\": pk,\n                \"base_version_pk\": base_version_pk,\n                \"add_content_units\": add_content_units,\n                \"remove_content_units\": remove_content_units,\n            },\n        )\n        return OperationPostponedResponse(task, request)\n\n    def verify_content_units(self, content_units, all_content_units):\n        \"\"\"Verify referenced content units.\"\"\"\n        existing_content_units_pks = content_units.values_list(\"pk\", flat=True)\n        existing_content_units_pks = {str(pk) for pk in existing_content_units_pks}\n\n        missing_pks = set(all_content_units.keys()) - existing_content_units_pks\n        if missing_pks:\n            missing_hrefs = [all_content_units[pk] for pk in missing_pks]\n            raise ValidationError(\n                _(\"Could not find the following content units: {}\").format(missing_hrefs)\n            )\n", "path": "pulpcore/plugin/actions.py"}]}