prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Re-generate all bundles with Distribution changes after Timestamp
  
  
  </issue>
  <code>
  [start of snippets/base/management/commands/generate_bundles.py]
  1 import os
  2 import json
  3 import itertools
  4 from datetime import datetime
  5 
  6 import brotli
  7 from product_details import product_details
  8 
  9 from django.conf import settings
  10 from django.core.files.base import ContentFile
  11 from django.core.management.base import BaseCommand
  12 from django.db.models import Q
  13 from django.core.files.storage import default_storage
  14 
  15 from snippets.base.models import DistributionBundle, Job
  16 
  17 
  18 class Command(BaseCommand):
  19     args = '(no args)'
  20     help = 'Generate bundles'
  21 
  22     def add_arguments(self, parser):
  23         # Named (optional) arguments
  24         parser.add_argument(
  25             '--timestamp',
  26             help='Parse Jobs last modified after <timestamp>',
  27         )
  28 
  29     def handle(self, *args, **options):
  30         if not options['timestamp']:
  31             self.stdout.write('Generating all bundles.')
  32             total_jobs = Job.objects.all()
  33         else:
  34             self.stdout.write(
  35                 'Generating bundles with Jobs modified on or after {}'.format(options['timestamp'])
  36             )
  37             total_jobs = Job.objects.filter(snippet__modified__gte=options['timestamp'])
  38 
  39         if not total_jobs:
  40             self.stdout.write('Nothing to do…')
  41             return
  42 
  43         self.stdout.write('Processing bundles…')
  44 
  45         combinations_to_process = set(
  46             itertools.chain.from_iterable(
  47                 itertools.product(
  48                     job.channels,
  49                     job.snippet.locale.code.strip(',').split(',')
  50                 )
  51                 for job in total_jobs
  52             )
  53         )
  54         distribution_bundles_to_process = DistributionBundle.objects.filter(
  55             distributions__jobs__in=total_jobs
  56         ).distinct().order_by('id')
  57 
  58         for distribution_bundle in distribution_bundles_to_process:
  59             distributions = distribution_bundle.distributions.all()
  60 
  61             for channel, locale in combinations_to_process:
  62                 additional_jobs = []
  63                 if channel == 'nightly' and settings.NIGHTLY_INCLUDES_RELEASE:
  64                     additional_jobs = Job.objects.filter(
  65                         status=Job.PUBLISHED).filter(**{
  66                             'targets__on_release': True,
  67                             'distribution__in': distributions,
  68                         })
  69 
  70                 channel_jobs = Job.objects.filter(
  71                     status=Job.PUBLISHED).filter(
  72                         Q(**{
  73                             'targets__on_{}'.format(channel): True,
  74                             'distribution__in': distributions,
  75                         }))
  76 
  77                 all_jobs = Job.objects.filter(
  78                     Q(id__in=additional_jobs) | Q(id__in=channel_jobs)
  79                 )
  80 
  81                 locales_to_process = [
  82                     key.lower() for key in product_details.languages.keys()
  83                     if key.lower().startswith(locale)
  84                 ]
  85 
  86                 for locale_to_process in locales_to_process:
  87                     filename = 'Firefox/{channel}/{locale}/{distribution}.json'.format(
  88                         channel=channel,
  89                         locale=locale_to_process,
  90                         distribution=distribution_bundle.code_name,
  91                     )
  92                     filename = os.path.join(settings.MEDIA_BUNDLES_PREGEN_ROOT, filename)
  93                     full_locale = ',{},'.format(locale_to_process.lower())
  94                     splitted_locale = ',{},'.format(locale_to_process.lower().split('-', 1)[0])
  95                     bundle_jobs = all_jobs.filter(
  96                         Q(snippet__locale__code__contains=splitted_locale) |
  97                         Q(snippet__locale__code__contains=full_locale)).distinct()
  98 
  99                     # If DistributionBundle is not enabled, or if there are no
  100                     # Published Jobs for the channel / locale / distribution
  101                     # combination, delete the current bundle file if it exists.
  102                     if not distribution_bundle.enabled or not bundle_jobs.exists():
  103                         if default_storage.exists(filename):
  104                             self.stdout.write('Removing {}'.format(filename))
  105                             default_storage.delete(filename)
  106                         continue
  107 
  108                     data = []
  109                     channel_job_ids = list(channel_jobs.values_list('id', flat=True))
  110                     for job in bundle_jobs:
  111                         if job.id in channel_job_ids:
  112                             render = job.render()
  113                         else:
  114                             render = job.render(always_eval_to_false=True)
  115                         data.append(render)
  116 
  117                     bundle_content = json.dumps({
  118                         'messages': data,
  119                         'metadata': {
  120                             'generated_at': datetime.utcnow().isoformat(),
  121                             'number_of_snippets': len(data),
  122                             'channel': channel,
  123                         }
  124                     })
  125 
  126                     # Convert str to bytes.
  127                     if isinstance(bundle_content, str):
  128                         bundle_content = bundle_content.encode('utf-8')
  129 
  130                     if settings.BUNDLE_BROTLI_COMPRESS:
  131                         content_file = ContentFile(brotli.compress(bundle_content))
  132                         content_file.content_encoding = 'br'
  133                     else:
  134                         content_file = ContentFile(bundle_content)
  135 
  136                     default_storage.save(filename, content_file)
  137                     self.stdout.write(self.style.SUCCESS('Writing bundle {}'.format(filename)))
  138
  [end of snippets/base/management/commands/generate_bundles.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/snippets/base/management/commands/generate_bundles.py b/snippets/base/management/commands/generate_bundles.py
  --- a/snippets/base/management/commands/generate_bundles.py
  +++ b/snippets/base/management/commands/generate_bundles.py
  @@ -34,7 +34,10 @@
               self.stdout.write(
                   'Generating bundles with Jobs modified on or after {}'.format(options['timestamp'])
               )
  -            total_jobs = Job.objects.filter(snippet__modified__gte=options['timestamp'])
  +            total_jobs = Job.objects.filter(
  +                Q(snippet__modified__gte=options['timestamp']) |
  +                Q(distribution__distributionbundle__modified__gte=options['timestamp'])
  +            ).distinct()
   
           if not total_jobs:
               self.stdout.write('Nothing to do…')
  
verification_info: {"golden_diff": "diff --git a/snippets/base/management/commands/generate_bundles.py b/snippets/base/management/commands/generate_bundles.py\n--- a/snippets/base/management/commands/generate_bundles.py\n+++ b/snippets/base/management/commands/generate_bundles.py\n@@ -34,7 +34,10 @@\n             self.stdout.write(\n                 'Generating bundles with Jobs modified on or after {}'.format(options['timestamp'])\n             )\n-            total_jobs = Job.objects.filter(snippet__modified__gte=options['timestamp'])\n+            total_jobs = Job.objects.filter(\n+                Q(snippet__modified__gte=options['timestamp']) |\n+                Q(distribution__distributionbundle__modified__gte=options['timestamp'])\n+            ).distinct()\n \n         if not total_jobs:\n             self.stdout.write('Nothing to do\u2026')\n", "issue": "Re-generate all bundles with Distribution changes after Timestamp\n\n", "before_files": [{"content": "import os\nimport json\nimport itertools\nfrom datetime import datetime\n\nimport brotli\nfrom product_details import product_details\n\nfrom django.conf import settings\nfrom django.core.files.base import ContentFile\nfrom django.core.management.base import BaseCommand\nfrom django.db.models import Q\nfrom django.core.files.storage import default_storage\n\nfrom snippets.base.models import DistributionBundle, Job\n\n\nclass Command(BaseCommand):\n    args = '(no args)'\n    help = 'Generate bundles'\n\n    def add_arguments(self, parser):\n        # Named (optional) arguments\n        parser.add_argument(\n            '--timestamp',\n            help='Parse Jobs last modified after <timestamp>',\n        )\n\n    def handle(self, *args, **options):\n        if not options['timestamp']:\n            self.stdout.write('Generating all bundles.')\n            total_jobs = Job.objects.all()\n        else:\n            self.stdout.write(\n                'Generating bundles with Jobs modified on or after {}'.format(options['timestamp'])\n            )\n            total_jobs = Job.objects.filter(snippet__modified__gte=options['timestamp'])\n\n        if not total_jobs:\n            self.stdout.write('Nothing to do\u2026')\n            return\n\n        self.stdout.write('Processing bundles\u2026')\n\n        combinations_to_process = set(\n            itertools.chain.from_iterable(\n                itertools.product(\n                    job.channels,\n                    job.snippet.locale.code.strip(',').split(',')\n                )\n                for job in total_jobs\n            )\n        )\n        distribution_bundles_to_process = DistributionBundle.objects.filter(\n            distributions__jobs__in=total_jobs\n        ).distinct().order_by('id')\n\n        for distribution_bundle in distribution_bundles_to_process:\n            distributions = distribution_bundle.distributions.all()\n\n            for channel, locale in combinations_to_process:\n                additional_jobs = []\n                if channel == 'nightly' and settings.NIGHTLY_INCLUDES_RELEASE:\n                    additional_jobs = Job.objects.filter(\n                        status=Job.PUBLISHED).filter(**{\n                            'targets__on_release': True,\n                            'distribution__in': distributions,\n                        })\n\n                channel_jobs = Job.objects.filter(\n                    status=Job.PUBLISHED).filter(\n                        Q(**{\n                            'targets__on_{}'.format(channel): True,\n                            'distribution__in': distributions,\n                        }))\n\n                all_jobs = Job.objects.filter(\n                    Q(id__in=additional_jobs) | Q(id__in=channel_jobs)\n                )\n\n                locales_to_process = [\n                    key.lower() for key in product_details.languages.keys()\n                    if key.lower().startswith(locale)\n                ]\n\n                for locale_to_process in locales_to_process:\n                    filename = 'Firefox/{channel}/{locale}/{distribution}.json'.format(\n                        channel=channel,\n                        locale=locale_to_process,\n                        distribution=distribution_bundle.code_name,\n                    )\n                    filename = os.path.join(settings.MEDIA_BUNDLES_PREGEN_ROOT, filename)\n                    full_locale = ',{},'.format(locale_to_process.lower())\n                    splitted_locale = ',{},'.format(locale_to_process.lower().split('-', 1)[0])\n                    bundle_jobs = all_jobs.filter(\n                        Q(snippet__locale__code__contains=splitted_locale) |\n                        Q(snippet__locale__code__contains=full_locale)).distinct()\n\n                    # If DistributionBundle is not enabled, or if there are no\n                    # Published Jobs for the channel / locale / distribution\n                    # combination, delete the current bundle file if it exists.\n                    if not distribution_bundle.enabled or not bundle_jobs.exists():\n                        if default_storage.exists(filename):\n                            self.stdout.write('Removing {}'.format(filename))\n                            default_storage.delete(filename)\n                        continue\n\n                    data = []\n                    channel_job_ids = list(channel_jobs.values_list('id', flat=True))\n                    for job in bundle_jobs:\n                        if job.id in channel_job_ids:\n                            render = job.render()\n                        else:\n                            render = job.render(always_eval_to_false=True)\n                        data.append(render)\n\n                    bundle_content = json.dumps({\n                        'messages': data,\n                        'metadata': {\n                            'generated_at': datetime.utcnow().isoformat(),\n                            'number_of_snippets': len(data),\n                            'channel': channel,\n                        }\n                    })\n\n                    # Convert str to bytes.\n                    if isinstance(bundle_content, str):\n                        bundle_content = bundle_content.encode('utf-8')\n\n                    if settings.BUNDLE_BROTLI_COMPRESS:\n                        content_file = ContentFile(brotli.compress(bundle_content))\n                        content_file.content_encoding = 'br'\n                    else:\n                        content_file = ContentFile(bundle_content)\n\n                    default_storage.save(filename, content_file)\n                    self.stdout.write(self.style.SUCCESS('Writing bundle {}'.format(filename)))\n", "path": "snippets/base/management/commands/generate_bundles.py"}]}