internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: mozmeao/snippets-service

Issue:
Filter by targets used in snippet list view



Discussion:

Resolving patch:
diff --git a/snippets/base/admin/adminmodels.py b/snippets/base/admin/adminmodels.py
--- a/snippets/base/admin/adminmodels.py
+++ b/snippets/base/admin/adminmodels.py
@@ -453,6 +453,7 @@ class ASRSnippetAdmin(admin.ModelAdmin):
         filters.ModifiedFilter,
         filters.TemplateFilter,
         ('locales', RelatedOnlyDropdownFilter),
+        ('targets', RelatedOnlyDropdownFilter),
         'status',
         filters.ChannelFilter,
         ('campaign', RelatedDropdownFilter),


Test patch:
Repository: mozmeao/snippets-service

Issue:
List Snippets using the Target



Discussion:

Resolving patch:
diff --git a/snippets/base/admin/adminmodels.py b/snippets/base/admin/adminmodels.py
--- a/snippets/base/admin/adminmodels.py
+++ b/snippets/base/admin/adminmodels.py
@@ -128,7 +128,7 @@ class IconAdmin(admin.ModelAdmin):
     class Media:
         css = {
             'all': (
-                'css/admin/IconListSnippets.css',
+                'css/admin/ListSnippets.css',
             )
         }
         js = (
@@ -147,8 +147,8 @@ def preview(self, obj):
 
     def snippets(self, obj):
         """Snippets using this icon."""
-        template = get_template('base/snippets_related_with_icon.jinja')
-        return mark_safe(template.render({'snippets': obj.snippets}))
+        template = get_template('base/snippets_related_with_obj.jinja')
+        return mark_safe(template.render({'snippets': obj.snippets, 'type': 'Icon'}))
 
 
 class SimpleTemplateInline(admin.StackedInline):
@@ -733,13 +733,24 @@ def total_snippets_in_category(self, obj):
 class TargetAdmin(admin.ModelAdmin):
     form = forms.TargetAdminForm
     save_on_top = True
-    readonly_fields = ('created', 'modified', 'creator', 'jexl_expr')
+    readonly_fields = (
+        'created',
+        'modified',
+        'creator',
+        'jexl_expr',
+        'snippets',
+    )
     filter_horizontal = (
         'client_match_rules',
     )
     search_fields = (
         'name',
     )
+    list_display = (
+        'name',
+        'number_of_snippets',
+        'number_of_published_snippets',
+    )
     fieldsets = (
         ('ID', {'fields': ('name',)}),
         ('Product channels', {
@@ -775,12 +786,22 @@ class TargetAdmin(admin.ModelAdmin):
                 'client_match_rules',
             )
         }),
+        ('Snippets', {
+            'fields': (
+                'snippets',
+            )
+        }),
         ('Other Info', {
             'fields': ('creator', ('created', 'modified'), 'jexl_expr'),
         }),
     )
 
     class Media:
+        css = {
+            'all': (
+                'css/admin/ListSnippets.css',
+            )
+        }
         js = (
             'js/admin/jquery.are-you-sure.js',
             'js/admin/alert-page-leaving.js',
@@ -791,3 +812,15 @@ def save_model(self, request, obj, form, change):
             obj.creator = request.user
         statsd.incr('save.target')
         super().save_model(request, obj, form, change)
+
+    def number_of_snippets(self, obj):
+        return obj.asrsnippet_set.count()
+
+    def number_of_published_snippets(self, obj):
+        return obj.asrsnippet_set.filter(status=models.STATUS_CHOICES['Published']).count()
+
+    def snippets(self, obj):
+        """Snippets using this Target."""
+        template = get_template('base/snippets_related_with_obj.jinja')
+        return mark_safe(template.render({'snippets': obj.asrsnippet_set.all().order_by('id'),
+                                          'type': 'Target'}))


Test patch:
Repository: mozmeao/snippets-service

Issue:
Create a preview with background (light and dark) for Icons 



Discussion:

Resolving patch:
diff --git a/snippets/base/admin/adminmodels.py b/snippets/base/admin/adminmodels.py
--- a/snippets/base/admin/adminmodels.py
+++ b/snippets/base/admin/adminmodels.py
@@ -142,8 +142,8 @@ def save_model(self, request, obj, form, change):
         super().save_model(request, obj, form, change)
 
     def preview(self, obj):
-        text = f'<img style="max-width:120px; max-height:120px;" src="{obj.image.url}"/>'
-        return mark_safe(text)
+        template = get_template('base/preview_image.jinja')
+        return mark_safe(template.render({'image': obj.image}))
 
     def snippets(self, obj):
         """Snippets using this icon."""


Test patch:
Repository: mozmeao/snippets-service

Issue:
Drop ASRSnippet.locales field
When we complete the migration to ASRSnippet.locale in #749, remove the `ASRSnippet.locales` field. 

<!-- probot = {"419912":{"who":"glogiotatidis","what":"","when":"2019-07-03T09:00:00.000Z"}} -->


Discussion:
/remind me next week

@glogiotatidis set a reminder for **Jul 3rd 2019**

Resolving patch:
diff --git a/snippets/base/migrations/0095_auto_20190627_1301.py b/snippets/base/migrations/0095_auto_20190627_1301.py
new file mode 100644
--- /dev/null
+++ b/snippets/base/migrations/0095_auto_20190627_1301.py
@@ -0,0 +1,25 @@
+# Generated by Django 2.2.2 on 2019-06-27 13:01
+
+import django.core.validators
+from django.db import migrations, models
+import snippets.base.models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('base', '0094_auto_20190619_1039'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='icon',
+            name='image',
+            field=models.ImageField(height_field='height', help_text='PNG only. A reasonable file size is about 5 KiB. Note that updating the image will update all snippets using this image.', upload_to=snippets.base.models._generate_filename, width_field='width'),
+        ),
+        migrations.AlterField(
+            model_name='locale',
+            name='code',
+            field=models.CharField(help_text='Comma separated list of locales with or without territory information. E.g `en-us` and `es-mx,es-ar`', max_length=255, unique=True, validators=[django.core.validators.RegexValidator(regex='^,?([A-Za-z-]+,?)+$')]),
+        ),
+    ]
diff --git a/snippets/base/migrations/0096_remove_asrsnippet_locales.py b/snippets/base/migrations/0096_remove_asrsnippet_locales.py
new file mode 100644
--- /dev/null
+++ b/snippets/base/migrations/0096_remove_asrsnippet_locales.py
@@ -0,0 +1,17 @@
+# Generated by Django 2.2.2 on 2019-06-27 13:02
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('base', '0095_auto_20190627_1301'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='asrsnippet',
+            name='locales',
+        ),
+    ]
diff --git a/snippets/base/models.py b/snippets/base/models.py
--- a/snippets/base/models.py
+++ b/snippets/base/models.py
@@ -1483,8 +1483,6 @@ class ASRSnippet(models.Model):
         verbose_name='Publish Ends',
         help_text=format_html(
             'See the current time in <a target="_blank" href="http://time.is/UTC">UTC</a>'))
-
-    locales = models.ManyToManyField('TargetedLocale', blank=True, verbose_name='Targeted Locales')
     locale = models.ForeignKey('Locale', blank=False, null=True, on_delete=models.PROTECT)
     targets = models.ManyToManyField(Target, default=None, blank=True, related_name='snippets')
 


Test patch:
Repository: mozmeao/snippets-service

Issue:
update HostnameMiddleware for k8s
Switch away from DEIS settings for hostname in favor of k8s.
update HostnameMiddleware for k8s
Switch away from DEIS settings for hostname in favor of k8s.


Discussion:

Resolving patch:
diff --git a/scripts/cron.py b/scripts/cron.py
--- a/scripts/cron.py
+++ b/scripts/cron.py
@@ -48,7 +48,7 @@ def run(self):
     def log(self, message):
         msg = '[{}] Clock job {}@{}: {}'.format(
             datetime.datetime.utcnow(), self.name,
-            os.getenv('DEIS_APP', 'default_app'), message)
+            os.getenv('K8S_NAMESPACE', 'default_app'), message)
         print(msg, file=sys.stderr)
 
 
diff --git a/snippets/base/middleware.py b/snippets/base/middleware.py
--- a/snippets/base/middleware.py
+++ b/snippets/base/middleware.py
@@ -41,8 +41,9 @@ def __init__(self, get_response):
         if not settings.ENABLE_HOSTNAME_MIDDLEWARE:
             raise MiddlewareNotUsed
 
-        values = [getattr(settings, x) for x in ['HOSTNAME', 'DEIS_APP', 'DEIS_DOMAIN']]
-        self.backend_server = '.'.join(x for x in values if x)
+        values = [getattr(settings, x) for x in [
+            'CLUSTER_NAME', 'K8S_NAMESPACE', 'K8S_POD_NAME']]
+        self.backend_server = '/'.join(x for x in values if x)
         self.get_response = get_response
 
     def __call__(self, request):
diff --git a/snippets/settings.py b/snippets/settings.py
--- a/snippets/settings.py
+++ b/snippets/settings.py
@@ -82,10 +82,11 @@
 )
 
 HOSTNAME = platform.node()
-DEIS_APP = config('DEIS_APP', default=None)
-DEIS_DOMAIN = config('DEIS_DOMAIN', default=None)
+CLUSTER_NAME = config('CLUSTER_NAME', default=None)
+K8S_NAMESPACE = config('K8S_NAMESPACE', default=None)
+K8S_POD_NAME = config('K8S_POD_NAME', default=None)
 ENABLE_HOSTNAME_MIDDLEWARE = config('ENABLE_HOSTNAME_MIDDLEWARE',
-                                    default=bool(DEIS_APP), cast=bool)
+                                    default=bool(K8S_NAMESPACE), cast=bool)
 
 ROOT_URLCONF = 'snippets.urls'
 
@@ -276,16 +277,17 @@
 
 STATSD_HOST = config('STATSD_HOST', default='localhost')
 STATSD_PORT = config('STATSD_PORT', 8125, cast=int)
-STATSD_PREFIX = config('STATSD_PREFIX', DEIS_APP)
+STATSD_PREFIX = config('STATSD_PREFIX', K8S_NAMESPACE)
 STATSD_CLIENT = config('STATSD_CLIENT', 'django_statsd.clients.null')
 
 RAVEN_CONFIG = {
     'dsn': config('SENTRY_DSN', None),
     'release': config('GIT_SHA', None),
     'tags': {
-        'server_full_name': '.'.join(x for x in [HOSTNAME, DEIS_APP, DEIS_DOMAIN] if x),
+        'server_full_name': '.'.join(x for x in [
+            HOSTNAME, K8S_NAMESPACE, CLUSTER_NAME] if x),
         'environment': config('SENTRY_ENVIRONMENT', 'dev'),
-        'site': '.'.join(x for x in [DEIS_APP, DEIS_DOMAIN] if x),
+        'site': '.'.join(x for x in [K8S_NAMESPACE, CLUSTER_NAME] if x),
     }
 }
 


Test patch:
Repository: mozmeao/snippets-service

Issue:
Fix Snippets ICal AttributeError exception 
Snippets ICal feed raises:

`AttributeError: 'ASRSnippet' object has no attribute 'locales'`

Fix the error and add tests catch this on the unit test level. We did catch this on the acceptance tests level though :sweat_smile: 



Discussion:

Resolving patch:
diff --git a/snippets/base/feed.py b/snippets/base/feed.py
--- a/snippets/base/feed.py
+++ b/snippets/base/feed.py
@@ -77,10 +77,10 @@ def item_link(self, item):
     def item_description(self, item):
         description = dedent('''\
         Channels: {}
-        Locales: {}'
+        Locale: {}'
         Preview Link: {}
         '''.format(', '.join(item.channels),
-                   ', '.join(item.locales.values_list('name', flat=True)),
+                   item.locale,
                    item.get_preview_url()))
         return description
 
diff --git a/snippets/base/urls.py b/snippets/base/urls.py
--- a/snippets/base/urls.py
+++ b/snippets/base/urls.py
@@ -18,5 +18,5 @@
     path('csp-violation-capture', views.csp_violation_capture, name='csp-violation-capture'),
     path('healthz/', watchman_views.ping, name='watchman.ping'),
     path('readiness/', watchman_views.status, name='watchman.status'),
-    path('feeds/snippets.ics', feed.SnippetsFeed()),
+    path('feeds/snippets.ics', feed.SnippetsFeed(), name='ical-feed'),
 ]


Test patch:
diff --git a/snippets/base/tests/test_feed.py b/snippets/base/tests/test_feed.py
--- a/snippets/base/tests/test_feed.py
+++ b/snippets/base/tests/test_feed.py
@@ -2,6 +2,8 @@
 from unittest.mock import Mock, patch
 
 from django.http.request import QueryDict
+from django.test import Client
+from django.urls import reverse
 
 from snippets.base import models
 from snippets.base.feed import ASRSnippetFilter, SnippetsFeed
@@ -57,6 +59,12 @@ def test_locale(self):
 
 
 class SnippetsFeedTests(TestCase):
+    def test_base(self):
+        ASRSnippetFactory.create_batch(2)
+        client = Client()
+        response = client.get(reverse('ical-feed'), follow=True)
+        self.assertEqual(response.status_code, 200)
+
     def test_item_filtering(self):
         request = Mock()
         request.GET = {}
Repository: mozmeao/snippets-service

Issue:
Add --strip option to pngquant
In https://github.com/mozmeao/snippets-service/pull/1047 we added `pngquant` for image optimization. 

Current Debian version (9.5) has an old version of pngquant (2.5.0) . Luckily Debian 10 will be released in the next weeks and we'll get a new vesrion (2.12.0). The new version also supports `--strip` flag to remove optional metadata from images.

/remind me on July 10th

<!-- probot = {"419912":{"who":"glogiotatidis","what":"","when":"2019-07-15T09:00:00.000Z"}} -->


Discussion:
@glogiotatidis set a reminder for **Jul 10th 2019**
/remind me in a week
@glogiotatidis set a reminder for **Jul 15th 2019**

Resolving patch:
diff --git a/snippets/base/models.py b/snippets/base/models.py
--- a/snippets/base/models.py
+++ b/snippets/base/models.py
@@ -631,7 +631,7 @@ def clean(self):
         if settings.IMAGE_OPTIMIZE:
             self.image.seek(0)
             cmd = subprocess.run(
-                ['pngquant', '-', '--quality=95', '--skip-if-larger', '--speed=1'],
+                ['pngquant', '-', '--quality=95', '--skip-if-larger', '--speed=1', '--strip'],
                 input=self.image.read(),
                 stdout=subprocess.PIPE
             )


Test patch:
Repository: mozmeao/snippets-service

Issue:
Publish status should be read-only when creating a new snippet.
Publish status should be read-only when creating a new snippet. Editable only when changing.

Benefits
 - Less mistakes
 - Stop errors when checking publishing rights.


Discussion:

Resolving patch:
diff --git a/snippets/base/admin/adminmodels.py b/snippets/base/admin/adminmodels.py
--- a/snippets/base/admin/adminmodels.py
+++ b/snippets/base/admin/adminmodels.py
@@ -1,3 +1,4 @@
+import copy
 import re
 from datetime import datetime
 
@@ -462,41 +463,41 @@ class ASRSnippetAdmin(admin.ModelAdmin):
         SendToDeviceTemplateInline,
         SimpleBelowSearchTemplateInline,
     ]
-    list_display_links = (
+    list_display_links = [
         'id',
-    )
-    list_display = (
+    ]
+    list_display = [
         'id',
         'custom_name_with_tags',
         'status',
         'locale',
         'modified',
-    )
-    list_filter = (
+    ]
+    list_filter = [
         filters.TemplateFilter,
-        ('locale', RelatedDropdownFilter),
-        ('targets', RelatedOnlyDropdownFilter),
+        ['locale', RelatedDropdownFilter],
+        ['targets', RelatedOnlyDropdownFilter],
         'status',
         filters.ChannelFilter,
-        ('campaign', RelatedDropdownFilter),
+        ['campaign', RelatedDropdownFilter],
         TaggitListFilter,
-        ('category', RelatedDropdownFilter),
+        ['category', RelatedDropdownFilter],
         filters.ScheduledFilter,
         filters.ModifiedFilter,
-    )
-    search_fields = (
+    ]
+    search_fields = [
         'name',
         'id',
         'campaign__name',
         'targets__name',
         'category__name',
-    )
-    autocomplete_fields = (
+    ]
+    autocomplete_fields = [
         'campaign',
         'category',
-    )
+    ]
     preserve_filters = True
-    readonly_fields = (
+    readonly_fields = [
         'id',
         'created',
         'modified',
@@ -505,18 +506,18 @@ class ASRSnippetAdmin(admin.ModelAdmin):
         'preview_url_light_theme',
         'preview_url_dark_theme',
 
-    )
-    filter_horizontal = (
+    ]
+    filter_horizontal = [
         'targets',
-    )
+    ]
     save_on_top = True
     save_as = True
     view_on_site = False
-    actions = (
+    actions = [
         actions.duplicate_snippets_action,
         'action_publish_snippet',
         'action_unpublish_snippet',
-    )
+    ]
 
     fieldsets = (
         ('ID', {
@@ -623,9 +624,12 @@ def change_view(self, request, *args, **kwargs):
         return super().change_view(request, *args, **kwargs)
 
     def get_readonly_fields(self, request, obj):
+        fields = copy.copy(self.readonly_fields)
         if not request.user.is_superuser:
-            return self.readonly_fields + ('for_qa',)
-        return self.readonly_fields
+            fields.append('for_qa')
+        if obj is None:
+            fields.append('status')
+        return fields
 
     def get_queryset(self, request):
         queryset = super().get_queryset(request).prefetch_related('tags')


Test patch:
Repository: mozmeao/snippets-service

Issue:
Add ESR channel in automatic utm param generation



Discussion:

Resolving patch:
diff --git a/snippets/base/models.py b/snippets/base/models.py
--- a/snippets/base/models.py
+++ b/snippets/base/models.py
@@ -1581,6 +1581,7 @@ def render(self, preview=False):
         # Add Channels
         CHANNELS_MAP = {
             'release': 'REL',
+            'esr': 'ESR',
             'beta': 'BETA',
             'aurora': 'DEV',
             'nightly': 'NIGHTLY',


Test patch:
Repository: mozmeao/snippets-service

Issue:
Add [[job_id]] content variable
We have `[[snippet_id]]` content variable, used mostly to dynamically generate `utm_*` params in links in snippets, to attribute traffic to specific snippets.

Now that we have jobs, we want to have a `[[job_id]]` variable as well, to attribute traffic to specific  jobs.


Discussion:

Resolving patch:
diff --git a/snippets/base/admin/adminmodels.py b/snippets/base/admin/adminmodels.py
--- a/snippets/base/admin/adminmodels.py
+++ b/snippets/base/admin/adminmodels.py
@@ -544,8 +544,9 @@ class ASRSnippetAdmin(admin.ModelAdmin):
                 <strong>Content Variables:</strong><br/>
                 You can use <code>[[snippet_id]]</code> in any field and it
                 will be automatically replaced by Snippet ID when served to users.
-                Similarly <code>[[campaign_slug]]</code> gets replaced by Campaign Slug and
-                <code>[[channels]]</code> by targeted channels.
+                Similarly <code>[[campaign_slug]]</code> gets replaced by Campaign Slug,
+                <code>[[channels]]</code> by targeted channels, <code>[[job_id]]</code>
+                by Job ID.
                 <br/>
                 Example: This is a <code>&lt;a href=&quot;https://example.com?utm_term=[[snippet_id]]&quot;&gt;link&lt;/a&gt;</code>
                 <br/>
diff --git a/snippets/base/models.py b/snippets/base/models.py
--- a/snippets/base/models.py
+++ b/snippets/base/models.py
@@ -1698,8 +1698,12 @@ def render(self):
 
         # Add campaign info
         campaign_slug = self.campaign.slug if self.campaign else ''
-        rendered_snippet = util.deep_search_and_replace(rendered_snippet, '[[campaign_slug]]',
-                                                        campaign_slug)
+        rendered_snippet = util.deep_search_and_replace(
+            rendered_snippet, '[[campaign_slug]]', campaign_slug)
+        # Add job id info
+        rendered_snippet = util.deep_search_and_replace(
+            rendered_snippet, '[[job_id]]', str(self.id))
+
         # Include campaign key when needed
         if campaign_slug:
             rendered_snippet['campaign'] = campaign_slug


Test patch:
diff --git a/snippets/base/tests/test_models.py b/snippets/base/tests/test_models.py
--- a/snippets/base/tests/test_models.py
+++ b/snippets/base/tests/test_models.py
@@ -723,7 +723,7 @@ def test_render(self):
             'template_version': 'xx.xx',
             'content': {
                 'block_button_text': 'Block me',
-                'text': 'This is text',
+                'text': 'This is text [[job_id]]',
             }
         }
         expected_output = copy.deepcopy(snippet_render)
@@ -732,6 +732,10 @@ def test_render(self):
             'weight': 10,
             'campaign': 'demo-campaign',
             'targeting': '(la==lo) && foo==bar',
+            'content': {
+                'block_button_text': 'Block me',
+                'text': f'This is text {job.id}',
+            }
         })
         job.snippet.render = Mock()
         job.snippet.render.return_value = snippet_render
Repository: mozmeao/snippets-service

Issue:
Change wording to to `69`
Here https://github.com/mozmeao/snippets-service/blob/d28b40b17f2d953dbbfee7f00bb37dc90e9c88a5/snippets/base/admin/adminmodels.py#L543


Discussion:

Resolving patch:
diff --git a/snippets/base/admin/adminmodels.py b/snippets/base/admin/adminmodels.py
--- a/snippets/base/admin/adminmodels.py
+++ b/snippets/base/admin/adminmodels.py
@@ -558,7 +558,7 @@ class ASRSnippetAdmin(admin.ModelAdmin):
                 <ol>
                   <li><code>special:accounts</code> to open Firefox Accounts</li>
                   <li><code>special:appMenu</code> to open the hamburger menu</li>
-                  <li><code>special:monitor</code> link User to Firefox Monitor and directly authenticate them. Works only in buttons. Works only after Firefox 70.</li>
+                  <li><code>special:monitor</code> link User to Firefox Monitor and directly authenticate them. Works only in buttons. Works only after Firefox 69.</li>
                 </ol><br/>
                 <strong>Content Variables:</strong><br/>
                 You can use <code>[[snippet_id]]</code> in any field and it


Test patch:
Repository: mozmeao/snippets-service

Issue:
Allow cancelling `Draft` jobs



Discussion:

Resolving patch:
diff --git a/snippets/base/admin/adminmodels.py b/snippets/base/admin/adminmodels.py
--- a/snippets/base/admin/adminmodels.py
+++ b/snippets/base/admin/adminmodels.py
@@ -1114,7 +1114,9 @@ def _action_status_change(self, action, request, queryset):
             no_action_message = 'Skipped {} already canceled or completed Jobs.'
             success_message = 'Canceled {} Jobs.'
             clean_queryset = queryset.filter(
-                Q(status=models.Job.PUBLISHED) | Q(status=models.Job.SCHEDULED)
+                Q(status=models.Job.PUBLISHED) |
+                Q(status=models.Job.SCHEDULED) |
+                Q(status=models.Job.DRAFT)
             )
         else:
             messages.success(request, 'Error no action')
diff --git a/snippets/base/migrations/0011_auto_20190909_1027.py b/snippets/base/migrations/0011_auto_20190909_1027.py
new file mode 100644
--- /dev/null
+++ b/snippets/base/migrations/0011_auto_20190909_1027.py
@@ -0,0 +1,17 @@
+# Generated by Django 2.2.3 on 2019-09-09 10:27
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('base', '0010_auto_20190905_1551'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='job',
+            options={'ordering': ['-modified']},
+        ),
+    ]
diff --git a/snippets/base/models.py b/snippets/base/models.py
--- a/snippets/base/models.py
+++ b/snippets/base/models.py
@@ -1794,7 +1794,7 @@ class Job(models.Model):
     objects = managers.JobManager()
 
     class Meta:
-        ordering = ('-publish_start', '-publish_end')
+        ordering = ['-modified']
 
     def __str__(self):
         return str(self.uuid)


Test patch: