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

Issue:
Ability to link to about: pages



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
@@ -583,6 +583,7 @@ class ASRSnippetAdmin(admin.ModelAdmin):
                   <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 69.</li>
+                  <li><code>special:about:ABOUT_PAGE</code> link to an About page. Get a list of About pages by typing <code>about:about</code> in your awesome bar. Example: <code>special:about:protections</code>.
                 </ol><br/>
                 <strong>Content Variables:</strong><br/>
                 You can use <code>[[snippet_id]]</code> in any field and it
diff --git a/snippets/base/models.py b/snippets/base/models.py
--- a/snippets/base/models.py
+++ b/snippets/base/models.py
@@ -681,6 +681,9 @@ def _convert_special_buttons(self, data):
                 elif value == 'special:accounts':
                     to_append['button_action'] = 'SHOW_FIREFOX_ACCOUNTS'
                     to_delete.append(key)
+                elif value.startswith('special:about'):
+                    to_append['button_action'] = 'OPEN_ABOUT_PAGE'
+                    to_append['button_action_args'] = value.rsplit(':', 1)[1]
                 elif value == 'special:monitor':
                     to_append['button_action'] = 'ENABLE_FIREFOX_MONITOR'
                     to_append['button_action_args'] = {
diff --git a/snippets/base/util.py b/snippets/base/util.py
--- a/snippets/base/util.py
+++ b/snippets/base/util.py
@@ -128,6 +128,11 @@ def __call__(self, matchobj):
                     'action': 'OPEN_APPLICATIONS_MENU',
                     'args': 'appMenu',
                 }
+            elif url.startswith('special:about'):
+                self.links[keyname] = {
+                    'action': 'OPEN_ABOUT_PAGE',
+                    'args': url.rsplit(':', 1)[1],
+                }
             elif url == 'special:accounts':
                 self.links[keyname] = {
                     'action': 'SHOW_FIREFOX_ACCOUNTS',


Test patch:
Repository: mozmeao/snippets-service

Issue:
Make Job Ids searchable in snippet admin
For Snippets, we can search using snippet ids. It doesn't work when in jobs. 
![Screen Shot 2019-09-27 at 9 00 21 AM](https://user-images.githubusercontent.com/6807556/65783734-45952880-e105-11e9-8e2b-2c7a370f1036.png)



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
@@ -975,10 +975,11 @@ class JobAdmin(admin.ModelAdmin):
         ('targets', RelatedOnlyDropdownFilter),
         ('snippet__locale', RelatedOnlyDropdownFilter),
         filters.ChannelFilter,
-
     ]
     search_fields = [
+        'id',
         'uuid',
+        'snippet__id',
         'snippet__name',
         'campaign__name',
     ]


Test patch:
Repository: mozmeao/snippets-service

Issue:
Fix ASRSnippetBundle.empty calculation to work with Jobs



Discussion:

Resolving patch:
diff --git a/snippets/base/bundles.py b/snippets/base/bundles.py
--- a/snippets/base/bundles.py
+++ b/snippets/base/bundles.py
@@ -205,6 +205,10 @@ def key(self):
         key_string = '_'.join(key_properties)
         return hashlib.sha1(key_string.encode('utf-8')).hexdigest()
 
+    @property
+    def empty(self):
+        return len(self.jobs) == 0
+
     @property
     def filename(self):
         return urljoin(settings.MEDIA_BUNDLES_ROOT, 'bundle_{0}.json'.format(self.key))


Test patch:
diff --git a/snippets/base/tests/test_bundles.py b/snippets/base/tests/test_bundles.py
--- a/snippets/base/tests/test_bundles.py
+++ b/snippets/base/tests/test_bundles.py
@@ -259,6 +259,15 @@ def _client(self, **kwargs):
         client_kwargs.update(kwargs)
         return Client(**client_kwargs)
 
+    def test_empty(self):
+        client = self._client(locale='en-US')
+        bundle = ASRSnippetBundle(client)
+        self.assertFalse(bundle.empty)
+
+        client = self._client(locale='it')
+        bundle = ASRSnippetBundle(client)
+        self.assertTrue(bundle.empty)
+
     def test_key_jobs(self):
         """
         bundle.key must be different between bundles if they have
Repository: mozmeao/snippets-service

Issue:
Remove `special:appMenu`
After #1181 has been deployed to prod we want to 
 - [x] Convert snippets that use `special:appMenu` to use `special:menu:appMenu` 
 - [x] Delete `special:appMenu` code 




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
@@ -584,7 +584,6 @@ class ASRSnippetAdmin(admin.ModelAdmin):
                 <strong>Available deep links:</strong><br/>
                 <ol>
                   <li><code>special:accounts</code> opens Firefox Accounts</li>
-                  <li><code>special:appMenu</code> opens the hamburger menu</li>
                   <li><code>special:monitor</code> links User to Firefox Monitor and directly authenticates them. Works only in buttons. Works only after Firefox 69.</li>
                   <li><code>special:about:ABOUT_PAGE</code> links to an About page. Get a list of About pages by typing <code>about:about</code> in your awesome bar. Example: <code>special:about:protections</code>.
                   <li><code>special:preferences</code> opens the Firefox Preferences tab. Example: <code>special:preferences</code>.
diff --git a/snippets/base/util.py b/snippets/base/util.py
--- a/snippets/base/util.py
+++ b/snippets/base/util.py
@@ -101,10 +101,7 @@ def urlparams(url_, fragment=None, query_dict=None, replace=True, **query):
 
 def convert_special_link(url):
     action = args = None
-    if url == 'special:appMenu':
-        action = 'OPEN_APPLICATIONS_MENU'
-        args = 'appMenu'
-    elif url.startswith('special:menu:'):
+    if url.startswith('special:menu:'):
         action = 'OPEN_APPLICATIONS_MENU'
         args = url.rsplit(':', 1)[1]
     elif url.startswith('special:about:'):


Test patch:
diff --git a/snippets/base/tests/test_util.py b/snippets/base/tests/test_util.py
--- a/snippets/base/tests/test_util.py
+++ b/snippets/base/tests/test_util.py
@@ -51,7 +51,7 @@ def test_multiple_links_with_metrics(self):
             'title': ('And this another variable with <a href="https://snippets.mozilla.org">more '
                       'links</a>'),
             'special_account': 'With <a href="special:accounts">special accounts link</a>.',
-            'special_appMenu': 'and another <a href="special:appMenu">special menu link</a>.',
+            'special_appMenu': 'and another <a href="special:menu:appMenu">special menu link</a>.',
             'nolinks': 'And finally one with no links.',
         }
         final_data = {
@@ -115,7 +115,6 @@ def test_base(self):
         }
         inputs = [
             ('https://example.com', (None, None)),
-            ('special:appMenu', ('OPEN_APPLICATIONS_MENU', 'appMenu')),
             ('special:menu:foo', ('OPEN_APPLICATIONS_MENU', 'foo')),
             ('special:about:login', ('OPEN_ABOUT_PAGE', 'login')),
             ('special:highlight:foo', ('HIGHLIGHT_FEATURE', 'foo')),
Repository: mozmeao/snippets-service

Issue:
FieldError on /admin/base/job/add/
https://sentry.prod.mozaws.net/operations/snippets/issues/6453618/?query=is:unresolved

"'completed_on' cannot be specified for Job model form as it is a non-editable field. Check fields/fieldsets/exclude attributes of class JobAdmin."
FieldError on /admin/base/job/add/
https://sentry.prod.mozaws.net/operations/snippets/issues/6453618/?query=is:unresolved

"'completed_on' cannot be specified for Job model form as it is a non-editable field. Check fields/fieldsets/exclude attributes of class JobAdmin."


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
@@ -1019,7 +1019,7 @@ class JobAdmin(admin.ModelAdmin):
     ]
     fieldsets = [
         ('ID', {
-            'fields': ('id', ('job_status', 'completed_on'), 'snippet_name_linked', 'creator')
+            'fields': ('id', 'job_status', 'snippet_name_linked', 'creator')
         }),
         ('Content', {
             'fields': ('snippet', 'campaign')


Test patch:
Repository: mozmeao/snippets-service

Issue:
FieldError on /admin/base/job/add/
https://sentry.prod.mozaws.net/operations/snippets/issues/6453618/?query=is:unresolved

"'completed_on' cannot be specified for Job model form as it is a non-editable field. Check fields/fieldsets/exclude attributes of class JobAdmin."


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
@@ -1016,10 +1016,11 @@ class JobAdmin(admin.ModelAdmin):
         'metric_blocks_humanized',
         'metric_last_update',
         'redash_link',
+        'completed_on',
     ]
     fieldsets = [
         ('ID', {
-            'fields': ('id', 'job_status', 'snippet_name_linked', 'creator')
+            'fields': ('id', ('job_status', 'completed_on'), 'snippet_name_linked', 'creator')
         }),
         ('Content', {
             'fields': ('snippet', 'campaign')


Test patch:
Repository: mozmeao/snippets-service

Issue:
When duplicating Job, remove metric_*



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
@@ -1994,6 +1994,9 @@ def duplicate(self, creator):
         job_copy.status = self.DRAFT
         job_copy.creator = creator
         job_copy.uuid = uuid.uuid4()
+        job_copy.metric_impressions = 0
+        job_copy.metric_clicks = 0
+        job_copy.metric_blocks = 0
         job_copy.save()
 
         for field in self._meta.get_fields():


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
@@ -881,6 +881,9 @@ def test_duplicate(self):
             self.assertNotEqual(getattr(job, attr), getattr(duplicate_job, attr))
 
         self.assertEqual(duplicate_job.status, Job.DRAFT)
+        self.assertEqual(duplicate_job.metric_impressions, 0)
+        self.assertEqual(duplicate_job.metric_clicks, 0)
+        self.assertEqual(duplicate_job.metric_blocks, 0)
 
     def test_analytics_export(self):
         snippet = ASRSnippetFactory.create(
Repository: mozmeao/snippets-service

Issue:
Remove deprecated permissions from ASRSnippet model
Remove deprecated permissions from ASRSnippet model

https://github.com/mozmeao/snippets-service/blob/master/snippets/base/models.py#L1999


Discussion:

Resolving patch:
diff --git a/snippets/base/migrations/0024_auto_20191119_1424.py b/snippets/base/migrations/0024_auto_20191119_1424.py
new file mode 100644
--- /dev/null
+++ b/snippets/base/migrations/0024_auto_20191119_1424.py
@@ -0,0 +1,17 @@
+# Generated by Django 2.2.6 on 2019-11-19 14:24
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('base', '0023_dailyjobmetrics'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='asrsnippet',
+            options={'ordering': ['-modified'], 'verbose_name': 'ASR Snippet', 'verbose_name_plural': 'ASR Snippets'},
+        ),
+    ]
diff --git a/snippets/base/models.py b/snippets/base/models.py
--- a/snippets/base/models.py
+++ b/snippets/base/models.py
@@ -2052,13 +2052,6 @@ class Meta:
         ordering = ['-modified']
         verbose_name = 'ASR Snippet'
         verbose_name_plural = 'ASR Snippets'
-        permissions = (
-            ('publish_on_release', 'Publish snippets on Release'),
-            ('publish_on_beta', 'Publish snippets on Beta'),
-            ('publish_on_aurora', 'Publish snippets on Aurora'),
-            ('publish_on_nightly', 'Publish snippets on Nightly'),
-            ('publish_on_esr', 'Publish snippets on ESR'),
-        )
 
     def __str__(self):
         return self.name


Test patch:
Repository: mozmeao/snippets-service

Issue:
Just published jobs with global limits get prematurely completed due to missing metrics.



Discussion:

Resolving patch:
diff --git a/snippets/base/management/commands/update_jobs.py b/snippets/base/management/commands/update_jobs.py
--- a/snippets/base/management/commands/update_jobs.py
+++ b/snippets/base/management/commands/update_jobs.py
@@ -67,6 +67,8 @@ def handle(self, *args, **options):
         jobs = (Job.objects
                 .filter(status=Job.PUBLISHED)
                 .exclude(limit_impressions=0, limit_clicks=0, limit_blocks=0)
+                # Exclude Jobs with limits which haven't been updated once yet.
+                .exclude(metric_last_update='1970-01-01')
                 .filter(metric_last_update__lt=yesterday))
         for job in jobs:
             job.change_status(


Test patch:
diff --git a/snippets/base/tests/test_commands.py b/snippets/base/tests/test_commands.py
--- a/snippets/base/tests/test_commands.py
+++ b/snippets/base/tests/test_commands.py
@@ -233,6 +233,11 @@ def test_base(self):
             metric_blocks=10,
             metric_last_update=now - timedelta(hours=40),
             publish_end=now + timedelta(days=1))
+        job_block_limit_not_reached_just_created = JobFactory(
+            status=models.Job.PUBLISHED,
+            limit_blocks=100,
+            metric_blocks=10,
+            publish_end=now + timedelta(days=1))
 
         job_cancelled = JobFactory(status=models.Job.CANCELED)
         job_completed = JobFactory(status=models.Job.COMPLETED)
@@ -255,6 +260,7 @@ def test_base(self):
         job_impression_limit_not_reached_but_no_data.refresh_from_db()
         job_click_limit_not_reached_but_no_data.refresh_from_db()
         job_block_limit_not_reached_but_no_data.refresh_from_db()
+        job_block_limit_not_reached_just_created.refresh_from_db()
 
         self.assertEqual(job_without_end_date.status, models.Job.PUBLISHED)
         self.assertEqual(job_that_has_ended.status, models.Job.COMPLETED)
@@ -272,6 +278,7 @@ def test_base(self):
         self.assertEqual(job_impression_limit_not_reached_but_no_data.status, models.Job.COMPLETED)
         self.assertEqual(job_click_limit_not_reached_but_no_data.status, models.Job.COMPLETED)
         self.assertEqual(job_block_limit_not_reached_but_no_data.status, models.Job.COMPLETED)
+        self.assertEqual(job_block_limit_not_reached_just_created.status, models.Job.PUBLISHED)
 
 
 class GenerateBundles(TestCase):
Repository: mozmeao/snippets-service

Issue:
Use ADMIN_REDIRECT_URL for ASRSnippet preview link if available



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
@@ -2093,7 +2093,7 @@ def get_preview_url(self, dark=False):
         if dark:
             theme = 'dark'
         url = reverse('asr-preview', kwargs={'uuid': self.uuid})
-        full_url = urljoin(settings.SITE_URL, url)
+        full_url = urljoin(settings.ADMIN_REDIRECT_URL or settings.SITE_URL, url)
         return 'about:newtab?theme={}&endpoint={}'.format(theme, full_url)
 
     def get_admin_url(self, full=True):


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
@@ -558,6 +558,14 @@ def test_get_preview_url(self):
         expected_result += reverse('asr-preview', kwargs={'uuid': snippet.uuid})
         self.assertEqual(snippet.get_preview_url(), expected_result)
 
+    @override_settings(SITE_URL='http://example.com',
+                       ADMIN_REDIRECT_URL='http://admin.example.com')
+    def test_get_preview_url_admin(self):
+        snippet = ASRSnippetFactory.create()
+        expected_result = 'about:newtab?theme=light&endpoint=http://admin.example.com'
+        expected_result += reverse('asr-preview', kwargs={'uuid': snippet.uuid})
+        self.assertEqual(snippet.get_preview_url(), expected_result)
+
     @override_settings(SITE_URL='http://example.com')
     def test_get_preview_url_dark(self):
         snippet = ASRSnippetFactory.create()
Repository: mozmeao/snippets-service

Issue:
Users with view-only permissions cannot see the populated inline template



Discussion:

Resolving patch:
diff --git a/snippets/base/forms.py b/snippets/base/forms.py
--- a/snippets/base/forms.py
+++ b/snippets/base/forms.py
@@ -615,7 +615,12 @@ class ASRSnippetAdminForm(forms.ModelForm):
             ('simple_below_search_snippet', 'Simple Below Search'),
         ),
         widget=TemplateChooserWidget,
-        required=False,
+        label='Template',
+    )
+    locale = forms.ModelChoiceField(
+        queryset=models.Locale.objects.all(),
+        empty_label='Select Locale',
+        widget=AutoTranslatorWidget,
     )
 
     def __init__(self, *args, **kwargs):
@@ -628,9 +633,6 @@ def __init__(self, *args, **kwargs):
     class Meta:
         model = models.ASRSnippet
         exclude = ['creator', 'created', 'modified']
-        widgets = {
-            'locale': AutoTranslatorWidget,
-        }
 
     class Media:
         js = [


Test patch:
Repository: mozmeao/snippets-service

Issue:
Fix `Explore in Redash` link when Job has no end date



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,5 +1,6 @@
 import copy
 import re
+from datetime import datetime, timedelta
 
 from django.conf import settings
 from django.contrib import admin, messages
@@ -1150,11 +1151,13 @@ def metric_blocks_humanized(self, obj):
     metric_blocks_humanized.short_description = 'Blocks'
 
     def redash_link(self, obj):
-
+        publish_end = (
+            obj.publish_end or datetime.utcnow() + timedelta(days=1)
+        ).strftime("%Y-%m-%d")
         link_legacy = (
             f'{settings.REDASH_ENDPOINT}/queries/{settings.REDASH_JOB_QUERY_ID}/'
             f'?p_start_date_{settings.REDASH_JOB_QUERY_ID}={obj.publish_start.strftime("%Y-%m-%d")}'
-            f'&p_end_date_{settings.REDASH_JOB_QUERY_ID}={obj.publish_end.strftime("%Y-%m-%d")}'
+            f'&p_end_date_{settings.REDASH_JOB_QUERY_ID}={publish_end}'
             f'&p_message_id_{settings.REDASH_JOB_QUERY_ID}={obj.id}#161888'
         )
         link_bigquery = (
@@ -1162,7 +1165,7 @@ def redash_link(self, obj):
             f'?p_start_date_{settings.REDASH_JOB_QUERY_BIGQUERY_ID}='
             f'{obj.publish_start.strftime("%Y-%m-%d")}'
             f'&p_end_date_{settings.REDASH_JOB_QUERY_BIGQUERY_ID}='
-            f'{obj.publish_end.strftime("%Y-%m-%d")}'
+            f'{publish_end}'
             f'&p_message_id_{settings.REDASH_JOB_QUERY_BIGQUERY_ID}={obj.id}#169041'
         )
 


Test patch:
Repository: mozmeao/snippets-service

Issue:
Add reason of completion in slack messages



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
@@ -1975,7 +1975,7 @@ def change_status(self, status, user=None, send_slack=True, reason=''):
 
         if send_slack:
             template = 'slack/job_{}.jinja.json'.format(self.get_status_display().lower())
-            data = render_to_string(template, context={'job': self})
+            data = render_to_string(template, context={'job': self, 'reason': reason})
             slack._send_slack(data)
 
     def get_admin_url(self, full=True):


Test patch:
Repository: mozmeao/snippets-service

Issue:
Add action to bulk delete Draft Jobs
/cc @mindy-moz


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
@@ -1084,6 +1084,7 @@ class JobAdmin(admin.ModelAdmin):
     actions = [
         'action_schedule_job',
         'action_cancel_job',
+        'action_delete_job',
     ]
 
     class Media:
@@ -1295,6 +1296,22 @@ def action_cancel_job(self, request, queryset):
         'publish',
     )
 
+    def action_delete_job(self, request, queryset):
+        original_job_count = queryset.count()
+        queryset.filter(status=models.Job.DRAFT).delete()
+        new_job_count = queryset.count()
+
+        if original_job_count != new_job_count:
+            message = f'Successfully deleted {original_job_count - new_job_count} Draft Jobs.'
+            messages.success(request, message)
+
+        if new_job_count:
+            messages.warning(request, f'Cannot remove {new_job_count} Jobs not in Draft state.')
+    action_delete_job.short_description = 'Delete selected Draft Jobs'
+    action_delete_job.allowed_permissions = (
+        'publish',
+    )
+
 
 class DistributionAdmin(admin.ModelAdmin):
     save_on_top = True


Test patch:
diff --git a/snippets/base/tests/test_admin.py b/snippets/base/tests/test_admin.py
--- a/snippets/base/tests/test_admin.py
+++ b/snippets/base/tests/test_admin.py
@@ -265,6 +265,32 @@ def test_action_cancel_job(self):
         self.assertTrue(message_mocks['warning'].called)
         self.assertTrue(message_mocks['success'].called)
 
+    def test_action_delete_job(self):
+        to_get_deleted = [
+            JobFactory.create(status=Job.DRAFT),
+            JobFactory.create(status=Job.DRAFT),
+        ]
+        to_remain = [
+            JobFactory(status=Job.CANCELED),
+            JobFactory(status=Job.COMPLETED),
+            JobFactory(status=Job.PUBLISHED),
+        ]
+        queryset = Job.objects.filter(id__in=[x.id for x in to_get_deleted + to_remain])
+
+        request = Mock()
+        request.user = UserFactory.create()
+        with patch.multiple('snippets.base.admin.adminmodels.messages',
+                            warning=DEFAULT_MOCK,
+                            success=DEFAULT_MOCK) as message_mocks:
+            JobAdmin(Job, None).action_delete_job(request, queryset)
+
+        self.assertEqual(
+            set(Job.objects.all()),
+            set(to_remain)
+        )
+        self.assertTrue(message_mocks['warning'].called)
+        self.assertTrue(message_mocks['success'].called)
+
 
 class DailyMetricsAdminTests(TestCase):
     def test_channel_redash_link(self):