prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Removing Module Bays with Children Power Ports breaks Power Ports count
  ### NetBox version
  
  v3.6.4
  
  ### Python version
  
  3.8
  
  ### Steps to Reproduce
  
  1. Create module types for device (for example PWR-MX960-4100-AC with 2 power ports):
  ![image](https://github.com/netbox-community/netbox/assets/122742913/a3f14cf5-ffd4-4c1f-987a-77893388b62a)
  2. In Devices add to chassis device (for example mx960) PEM module:
  ![image](https://github.com/netbox-community/netbox/assets/122742913/83ff33db-399c-4180-a834-b38d29ca56a4)
  3. Check count Power Ports:
  ![image](https://github.com/netbox-community/netbox/assets/122742913/6c005529-2066-41e0-b529-7e47489494e2)
  4. Delete module from device:
  ![image](https://github.com/netbox-community/netbox/assets/122742913/58478124-b73a-4a7a-b9a2-040b2c36ab4d)
  
  
  ### Expected Behavior
  
  For the Power Ports count to be correct
  
  ### Observed Behavior
  
  The port counter has not decreased
  
  </issue>
  <code>
  [start of netbox/utilities/counters.py]
  1 from django.apps import apps
  2 from django.db.models import F, Count, OuterRef, Subquery
  3 from django.db.models.signals import post_delete, post_save
  4 
  5 from netbox.registry import registry
  6 from .fields import CounterCacheField
  7 
  8 
  9 def get_counters_for_model(model):
  10     """
  11     Return field mappings for all counters registered to the given model.
  12     """
  13     return registry['counter_fields'][model].items()
  14 
  15 
  16 def update_counter(model, pk, counter_name, value):
  17     """
  18     Increment or decrement a counter field on an object identified by its model and primary key (PK). Positive values
  19     will increment; negative values will decrement.
  20     """
  21     model.objects.filter(pk=pk).update(
  22         **{counter_name: F(counter_name) + value}
  23     )
  24 
  25 
  26 def update_counts(model, field_name, related_query):
  27     """
  28     Perform a bulk update for the given model and counter field. For example,
  29 
  30         update_counts(Device, '_interface_count', 'interfaces')
  31 
  32     will effectively set
  33 
  34         Device.objects.update(_interface_count=Count('interfaces'))
  35     """
  36     subquery = Subquery(
  37         model.objects.filter(pk=OuterRef('pk')).annotate(_count=Count(related_query)).values('_count')
  38     )
  39     return model.objects.update(**{
  40         field_name: subquery
  41     })
  42 
  43 
  44 #
  45 # Signal handlers
  46 #
  47 
  48 def post_save_receiver(sender, instance, created, **kwargs):
  49     """
  50     Update counter fields on related objects when a TrackingModelMixin subclass is created or modified.
  51     """
  52     for field_name, counter_name in get_counters_for_model(sender):
  53         parent_model = sender._meta.get_field(field_name).related_model
  54         new_pk = getattr(instance, field_name, None)
  55         has_old_field = field_name in instance.tracker
  56         old_pk = instance.tracker.get(field_name) if has_old_field else None
  57 
  58         # Update the counters on the old and/or new parents as needed
  59         if old_pk is not None:
  60             update_counter(parent_model, old_pk, counter_name, -1)
  61         if new_pk is not None and (has_old_field or created):
  62             update_counter(parent_model, new_pk, counter_name, 1)
  63 
  64 
  65 def post_delete_receiver(sender, instance, origin, **kwargs):
  66     """
  67     Update counter fields on related objects when a TrackingModelMixin subclass is deleted.
  68     """
  69     for field_name, counter_name in get_counters_for_model(sender):
  70         parent_model = sender._meta.get_field(field_name).related_model
  71         parent_pk = getattr(instance, field_name, None)
  72 
  73         # Decrement the parent's counter by one
  74         if parent_pk is not None:
  75             # MPTT sends two delete signals for child elements so guard against multiple decrements
  76             if not origin or origin == instance:
  77                 update_counter(parent_model, parent_pk, counter_name, -1)
  78 
  79 
  80 #
  81 # Registration
  82 #
  83 
  84 def connect_counters(*models):
  85     """
  86     Register counter fields and connect post_save & post_delete signal handlers for the affected models.
  87     """
  88     for model in models:
  89 
  90         # Find all CounterCacheFields on the model
  91         counter_fields = [
  92             field for field in model._meta.get_fields() if type(field) is CounterCacheField
  93         ]
  94 
  95         for field in counter_fields:
  96             to_model = apps.get_model(field.to_model_name)
  97 
  98             # Register the counter in the registry
  99             change_tracking_fields = registry['counter_fields'][to_model]
  100             change_tracking_fields[f"{field.to_field_name}_id"] = field.name
  101 
  102             # Connect the post_save and post_delete handlers
  103             post_save.connect(
  104                 post_save_receiver,
  105                 sender=to_model,
  106                 weak=False,
  107                 dispatch_uid=f'{model._meta.label}.{field.name}'
  108             )
  109             post_delete.connect(
  110                 post_delete_receiver,
  111                 sender=to_model,
  112                 weak=False,
  113                 dispatch_uid=f'{model._meta.label}.{field.name}'
  114             )
  115
  [end of netbox/utilities/counters.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/netbox/utilities/counters.py b/netbox/utilities/counters.py
  --- a/netbox/utilities/counters.py
  +++ b/netbox/utilities/counters.py
  @@ -1,6 +1,6 @@
   from django.apps import apps
   from django.db.models import F, Count, OuterRef, Subquery
  -from django.db.models.signals import post_delete, post_save
  +from django.db.models.signals import post_delete, post_save, pre_delete
   
   from netbox.registry import registry
   from .fields import CounterCacheField
  @@ -62,6 +62,12 @@
               update_counter(parent_model, new_pk, counter_name, 1)
   
   
  +def pre_delete_receiver(sender, instance, origin, **kwargs):
  +    model = instance._meta.model
  +    if not model.objects.filter(pk=instance.pk).exists():
  +        instance._previously_removed = True
  +
  +
   def post_delete_receiver(sender, instance, origin, **kwargs):
       """
       Update counter fields on related objects when a TrackingModelMixin subclass is deleted.
  @@ -71,10 +77,8 @@
           parent_pk = getattr(instance, field_name, None)
   
           # Decrement the parent's counter by one
  -        if parent_pk is not None:
  -            # MPTT sends two delete signals for child elements so guard against multiple decrements
  -            if not origin or origin == instance:
  -                update_counter(parent_model, parent_pk, counter_name, -1)
  +        if parent_pk is not None and not hasattr(instance, "_previously_removed"):
  +            update_counter(parent_model, parent_pk, counter_name, -1)
   
   
   #
  @@ -106,6 +110,12 @@
                   weak=False,
                   dispatch_uid=f'{model._meta.label}.{field.name}'
               )
  +            pre_delete.connect(
  +                pre_delete_receiver,
  +                sender=to_model,
  +                weak=False,
  +                dispatch_uid=f'{model._meta.label}.{field.name}'
  +            )
               post_delete.connect(
                   post_delete_receiver,
                   sender=to_model,
  
verification_info: {"golden_diff": "diff --git a/netbox/utilities/counters.py b/netbox/utilities/counters.py\n--- a/netbox/utilities/counters.py\n+++ b/netbox/utilities/counters.py\n@@ -1,6 +1,6 @@\n from django.apps import apps\n from django.db.models import F, Count, OuterRef, Subquery\n-from django.db.models.signals import post_delete, post_save\n+from django.db.models.signals import post_delete, post_save, pre_delete\n \n from netbox.registry import registry\n from .fields import CounterCacheField\n@@ -62,6 +62,12 @@\n             update_counter(parent_model, new_pk, counter_name, 1)\n \n \n+def pre_delete_receiver(sender, instance, origin, **kwargs):\n+    model = instance._meta.model\n+    if not model.objects.filter(pk=instance.pk).exists():\n+        instance._previously_removed = True\n+\n+\n def post_delete_receiver(sender, instance, origin, **kwargs):\n     \"\"\"\n     Update counter fields on related objects when a TrackingModelMixin subclass is deleted.\n@@ -71,10 +77,8 @@\n         parent_pk = getattr(instance, field_name, None)\n \n         # Decrement the parent's counter by one\n-        if parent_pk is not None:\n-            # MPTT sends two delete signals for child elements so guard against multiple decrements\n-            if not origin or origin == instance:\n-                update_counter(parent_model, parent_pk, counter_name, -1)\n+        if parent_pk is not None and not hasattr(instance, \"_previously_removed\"):\n+            update_counter(parent_model, parent_pk, counter_name, -1)\n \n \n #\n@@ -106,6 +110,12 @@\n                 weak=False,\n                 dispatch_uid=f'{model._meta.label}.{field.name}'\n             )\n+            pre_delete.connect(\n+                pre_delete_receiver,\n+                sender=to_model,\n+                weak=False,\n+                dispatch_uid=f'{model._meta.label}.{field.name}'\n+            )\n             post_delete.connect(\n                 post_delete_receiver,\n                 sender=to_model,\n", "issue": "Removing Module Bays with Children Power Ports breaks Power Ports count\n### NetBox version\r\n\r\nv3.6.4\r\n\r\n### Python version\r\n\r\n3.8\r\n\r\n### Steps to Reproduce\r\n\r\n1. Create module types for device (for example PWR-MX960-4100-AC with 2 power ports):\r\n![image](https://github.com/netbox-community/netbox/assets/122742913/a3f14cf5-ffd4-4c1f-987a-77893388b62a)\r\n2. In Devices add to chassis device (for example mx960) PEM module:\r\n![image](https://github.com/netbox-community/netbox/assets/122742913/83ff33db-399c-4180-a834-b38d29ca56a4)\r\n3. Check count Power Ports:\r\n![image](https://github.com/netbox-community/netbox/assets/122742913/6c005529-2066-41e0-b529-7e47489494e2)\r\n4. Delete module from device:\r\n![image](https://github.com/netbox-community/netbox/assets/122742913/58478124-b73a-4a7a-b9a2-040b2c36ab4d)\r\n\r\n\r\n### Expected Behavior\r\n\r\nFor the Power Ports count to be correct\r\n\r\n### Observed Behavior\r\n\r\nThe port counter has not decreased\n", "before_files": [{"content": "from django.apps import apps\nfrom django.db.models import F, Count, OuterRef, Subquery\nfrom django.db.models.signals import post_delete, post_save\n\nfrom netbox.registry import registry\nfrom .fields import CounterCacheField\n\n\ndef get_counters_for_model(model):\n    \"\"\"\n    Return field mappings for all counters registered to the given model.\n    \"\"\"\n    return registry['counter_fields'][model].items()\n\n\ndef update_counter(model, pk, counter_name, value):\n    \"\"\"\n    Increment or decrement a counter field on an object identified by its model and primary key (PK). Positive values\n    will increment; negative values will decrement.\n    \"\"\"\n    model.objects.filter(pk=pk).update(\n        **{counter_name: F(counter_name) + value}\n    )\n\n\ndef update_counts(model, field_name, related_query):\n    \"\"\"\n    Perform a bulk update for the given model and counter field. For example,\n\n        update_counts(Device, '_interface_count', 'interfaces')\n\n    will effectively set\n\n        Device.objects.update(_interface_count=Count('interfaces'))\n    \"\"\"\n    subquery = Subquery(\n        model.objects.filter(pk=OuterRef('pk')).annotate(_count=Count(related_query)).values('_count')\n    )\n    return model.objects.update(**{\n        field_name: subquery\n    })\n\n\n#\n# Signal handlers\n#\n\ndef post_save_receiver(sender, instance, created, **kwargs):\n    \"\"\"\n    Update counter fields on related objects when a TrackingModelMixin subclass is created or modified.\n    \"\"\"\n    for field_name, counter_name in get_counters_for_model(sender):\n        parent_model = sender._meta.get_field(field_name).related_model\n        new_pk = getattr(instance, field_name, None)\n        has_old_field = field_name in instance.tracker\n        old_pk = instance.tracker.get(field_name) if has_old_field else None\n\n        # Update the counters on the old and/or new parents as needed\n        if old_pk is not None:\n            update_counter(parent_model, old_pk, counter_name, -1)\n        if new_pk is not None and (has_old_field or created):\n            update_counter(parent_model, new_pk, counter_name, 1)\n\n\ndef post_delete_receiver(sender, instance, origin, **kwargs):\n    \"\"\"\n    Update counter fields on related objects when a TrackingModelMixin subclass is deleted.\n    \"\"\"\n    for field_name, counter_name in get_counters_for_model(sender):\n        parent_model = sender._meta.get_field(field_name).related_model\n        parent_pk = getattr(instance, field_name, None)\n\n        # Decrement the parent's counter by one\n        if parent_pk is not None:\n            # MPTT sends two delete signals for child elements so guard against multiple decrements\n            if not origin or origin == instance:\n                update_counter(parent_model, parent_pk, counter_name, -1)\n\n\n#\n# Registration\n#\n\ndef connect_counters(*models):\n    \"\"\"\n    Register counter fields and connect post_save & post_delete signal handlers for the affected models.\n    \"\"\"\n    for model in models:\n\n        # Find all CounterCacheFields on the model\n        counter_fields = [\n            field for field in model._meta.get_fields() if type(field) is CounterCacheField\n        ]\n\n        for field in counter_fields:\n            to_model = apps.get_model(field.to_model_name)\n\n            # Register the counter in the registry\n            change_tracking_fields = registry['counter_fields'][to_model]\n            change_tracking_fields[f\"{field.to_field_name}_id\"] = field.name\n\n            # Connect the post_save and post_delete handlers\n            post_save.connect(\n                post_save_receiver,\n                sender=to_model,\n                weak=False,\n                dispatch_uid=f'{model._meta.label}.{field.name}'\n            )\n            post_delete.connect(\n                post_delete_receiver,\n                sender=to_model,\n                weak=False,\n                dispatch_uid=f'{model._meta.label}.{field.name}'\n            )\n", "path": "netbox/utilities/counters.py"}]}