From 46ef497fd028531ffac8ebc19dec41a3272f72a3 Mon Sep 17 00:00:00 2001 From: ks123 Date: Mon, 30 Mar 2020 13:30:00 +0300 Subject: (Off-topic Channel Names): Added new field to model: `used` that show is this name already used on this round of names, added migration for this. --- .../api/migrations/0051_offtopicchannelname_used.py | 18 ++++++++++++++++++ .../apps/api/models/bot/off_topic_channel_name.py | 5 +++++ 2 files changed, 23 insertions(+) create mode 100644 pydis_site/apps/api/migrations/0051_offtopicchannelname_used.py diff --git a/pydis_site/apps/api/migrations/0051_offtopicchannelname_used.py b/pydis_site/apps/api/migrations/0051_offtopicchannelname_used.py new file mode 100644 index 00000000..74836d8c --- /dev/null +++ b/pydis_site/apps/api/migrations/0051_offtopicchannelname_used.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.11 on 2020-03-30 10:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0050_remove_infractions_active_default_value'), + ] + + operations = [ + migrations.AddField( + model_name='offtopicchannelname', + name='used', + field=models.BooleanField(default=False, help_text='Show is channel already used as channel name in this round.'), + ), + ] diff --git a/pydis_site/apps/api/models/bot/off_topic_channel_name.py b/pydis_site/apps/api/models/bot/off_topic_channel_name.py index 29280c27..3345754d 100644 --- a/pydis_site/apps/api/models/bot/off_topic_channel_name.py +++ b/pydis_site/apps/api/models/bot/off_topic_channel_name.py @@ -16,6 +16,11 @@ class OffTopicChannelName(ModelReprMixin, models.Model): help_text="The actual channel name that will be used on our Discord server." ) + used = models.BooleanField( + default=False, + help_text="Show is channel already used as channel name in this round." + ) + def __str__(self): """Returns the current off-topic name, for display purposes.""" return self.name -- cgit v1.2.3 From a3d2938d63443ee6fa43751f42c935b22d4efb47 Mon Sep 17 00:00:00 2001 From: ks123 Date: Mon, 30 Mar 2020 17:28:44 +0300 Subject: (Off-topic Channel Names Viewset): Added documentation about new `mark_used` query parameter, added implementation of this param. --- .../api/viewsets/bot/off_topic_channel_name.py | 29 ++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py b/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py index d6da2399..4328c894 100644 --- a/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py +++ b/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py @@ -21,6 +21,10 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet): If the `random_items` query parameter is given, for example using... $ curl api.pythondiscord.local:8000/bot/off-topic-channel-names?random_items=5 ... then the API will return `5` random items from the database. + If the `mark_used` query parameter is given like... + $ curl api.pydis.local:8000/bot/off-topic-channel-names?random_items=5&mark_used=true + ... then the API will mark returned `5` items `used`. + When running out of names, API will mark all names to not used and start new round. #### Response format Return a list of off-topic-channel names: @@ -106,6 +110,31 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet): 'random_items': ["Must be a positive integer."] }) + if 'mark_used' in request.query_params and request.query_params['mark_used']: + queryset = self.get_queryset().order_by('?').exclude(used=True)[:random_count] + self.get_queryset().filter( + name__in=(query.name for query in queryset) + ).update(used=True) + + # When client request more channel names than non-used names is available, start + # new round of names. + if len(queryset) < random_count: + # Get how much names still missing and don't fetch duplicate names. + need_more = random_count - len(queryset) + ext = self.get_queryset().order_by('?').exclude( + name__in=(query.name for query in queryset) + )[:need_more] + + # Set all names `used` field to False except these that we just used. + self.get_queryset().exclude(name__in=( + query.name for query in ext) + ).update(used=False) + # Join original queryset (that had missing names) + # and extension with these missing names. + queryset = list(queryset) + list(ext) + serialized = self.serializer_class(queryset, many=True) + return Response(serialized.data) + queryset = self.get_queryset().order_by('?')[:random_count] serialized = self.serializer_class(queryset, many=True) return Response(serialized.data) -- cgit v1.2.3 From 81516be86a22ba1ea6df419f0e4b4277adecae09 Mon Sep 17 00:00:00 2001 From: ks123 Date: Tue, 31 Mar 2020 13:44:38 +0300 Subject: (Off-topic Channel Names Viewset Tests): Added test for not authenticated request. --- pydis_site/apps/api/tests/test_off_topic_channel_names.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pydis_site/apps/api/tests/test_off_topic_channel_names.py b/pydis_site/apps/api/tests/test_off_topic_channel_names.py index bd42cd81..9697f1f6 100644 --- a/pydis_site/apps/api/tests/test_off_topic_channel_names.py +++ b/pydis_site/apps/api/tests/test_off_topic_channel_names.py @@ -21,6 +21,12 @@ class UnauthenticatedTests(APISubdomainTestCase): self.assertEqual(response.status_code, 401) + def test_cannot_read_off_topic_channel_name_list_with_random_item_and_mark_used_param(self): + url = reverse('bot:offtopicchannelname-list', host='api') + response = self.client.get(f'{url}?random_items=no&mark_used=true') + + self.assertEqual(response.status_code, 401) + class EmptyDatabaseTests(APISubdomainTestCase): def test_returns_empty_object(self): -- cgit v1.2.3 From 1f66694835013b1025a1630afe3290adbc03b94f Mon Sep 17 00:00:00 2001 From: ks123 Date: Tue, 31 Mar 2020 13:45:46 +0300 Subject: (Off-topic Channel Names Viewset Tests): Added used parameter to list tests. --- pydis_site/apps/api/tests/test_off_topic_channel_names.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pydis_site/apps/api/tests/test_off_topic_channel_names.py b/pydis_site/apps/api/tests/test_off_topic_channel_names.py index 9697f1f6..fea3932b 100644 --- a/pydis_site/apps/api/tests/test_off_topic_channel_names.py +++ b/pydis_site/apps/api/tests/test_off_topic_channel_names.py @@ -65,8 +65,8 @@ class EmptyDatabaseTests(APISubdomainTestCase): class ListTests(APISubdomainTestCase): @classmethod def setUpTestData(cls): - cls.test_name = OffTopicChannelName.objects.create(name='lemons-lemonade-stand') - cls.test_name_2 = OffTopicChannelName.objects.create(name='bbq-with-bisk') + cls.test_name = OffTopicChannelName.objects.create(name='lemons-lemonade-stand', used=False) + cls.test_name_2 = OffTopicChannelName.objects.create(name='bbq-with-bisk', used=True) def test_returns_name_in_list(self): url = reverse('bot:offtopicchannelname-list', host='api') -- cgit v1.2.3 From 02315e7339858ae6a11bc742446a14b7220894ca Mon Sep 17 00:00:00 2001 From: ks123 Date: Tue, 31 Mar 2020 13:50:39 +0300 Subject: (Off-topic Channel Names Viewset Tests): Added test for default handling `mark_used` parameter. --- pydis_site/apps/api/tests/test_off_topic_channel_names.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pydis_site/apps/api/tests/test_off_topic_channel_names.py b/pydis_site/apps/api/tests/test_off_topic_channel_names.py index fea3932b..be0d5001 100644 --- a/pydis_site/apps/api/tests/test_off_topic_channel_names.py +++ b/pydis_site/apps/api/tests/test_off_topic_channel_names.py @@ -88,6 +88,13 @@ class ListTests(APISubdomainTestCase): self.assertEqual(response.status_code, 200) self.assertEqual(len(response.json()), 1) + def test_returns_single_correct_item_with_mark_used_parameter_true_and_random_items_1(self): + url = reverse('bot:offtopicchannelname-list', host='api') + response = self.client.get(f'{url}?random_items=1&mark_used=true') + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json(), [self.test_name.name]) + class CreationTests(APISubdomainTestCase): def setUp(self): -- cgit v1.2.3 From d6f657143fa38657fb0432b9adb779969e920c64 Mon Sep 17 00:00:00 2001 From: ks123 Date: Tue, 31 Mar 2020 13:54:59 +0300 Subject: (Off-topic Channel Names Viewset Tests): Added test for handling running out of names. --- pydis_site/apps/api/tests/test_off_topic_channel_names.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pydis_site/apps/api/tests/test_off_topic_channel_names.py b/pydis_site/apps/api/tests/test_off_topic_channel_names.py index be0d5001..06624d89 100644 --- a/pydis_site/apps/api/tests/test_off_topic_channel_names.py +++ b/pydis_site/apps/api/tests/test_off_topic_channel_names.py @@ -95,6 +95,13 @@ class ListTests(APISubdomainTestCase): self.assertEqual(response.status_code, 200) self.assertEqual(response.json(), [self.test_name.name]) + def test_running_out_of_names_with_mark_used_parameter(self): + url = reverse('bot:offtopicchannelname-list', host='api') + response = self.client.get(f'{url}?random_items=2&mark_used=true') + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json(), [self.test_name.name, self.test_name_2.name]) + class CreationTests(APISubdomainTestCase): def setUp(self): -- cgit v1.2.3 From e321a25c05875e82073470429708b37849947b16 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 28 May 2020 08:23:14 +0300 Subject: OT: Replace help text of `used` field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Leon Sandøy --- pydis_site/apps/api/migrations/0051_offtopicchannelname_used.py | 2 +- pydis_site/apps/api/models/bot/off_topic_channel_name.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pydis_site/apps/api/migrations/0051_offtopicchannelname_used.py b/pydis_site/apps/api/migrations/0051_offtopicchannelname_used.py index 74836d8c..1b838aec 100644 --- a/pydis_site/apps/api/migrations/0051_offtopicchannelname_used.py +++ b/pydis_site/apps/api/migrations/0051_offtopicchannelname_used.py @@ -13,6 +13,6 @@ class Migration(migrations.Migration): migrations.AddField( model_name='offtopicchannelname', name='used', - field=models.BooleanField(default=False, help_text='Show is channel already used as channel name in this round.'), + field=models.BooleanField(default=False, help_text='Whether or not this name has already been used during this rotation'), ), ] diff --git a/pydis_site/apps/api/models/bot/off_topic_channel_name.py b/pydis_site/apps/api/models/bot/off_topic_channel_name.py index 3345754d..413cbfae 100644 --- a/pydis_site/apps/api/models/bot/off_topic_channel_name.py +++ b/pydis_site/apps/api/models/bot/off_topic_channel_name.py @@ -18,7 +18,7 @@ class OffTopicChannelName(ModelReprMixin, models.Model): used = models.BooleanField( default=False, - help_text="Show is channel already used as channel name in this round." + help_text="Whether or not this name has already been used during this rotation", ) def __str__(self): -- cgit v1.2.3 From 1e3501b2f27d7e09a873dc3348d196ac2edda68b Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 28 May 2020 08:49:49 +0300 Subject: OT: Fix migrations nr conflict --- .../api/migrations/0051_offtopicchannelname_used.py | 18 ------------------ .../api/migrations/0052_offtopicchannelname_used.py | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 18 deletions(-) delete mode 100644 pydis_site/apps/api/migrations/0051_offtopicchannelname_used.py create mode 100644 pydis_site/apps/api/migrations/0052_offtopicchannelname_used.py diff --git a/pydis_site/apps/api/migrations/0051_offtopicchannelname_used.py b/pydis_site/apps/api/migrations/0051_offtopicchannelname_used.py deleted file mode 100644 index 1b838aec..00000000 --- a/pydis_site/apps/api/migrations/0051_offtopicchannelname_used.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.11 on 2020-03-30 10:24 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0050_remove_infractions_active_default_value'), - ] - - operations = [ - migrations.AddField( - model_name='offtopicchannelname', - name='used', - field=models.BooleanField(default=False, help_text='Whether or not this name has already been used during this rotation'), - ), - ] diff --git a/pydis_site/apps/api/migrations/0052_offtopicchannelname_used.py b/pydis_site/apps/api/migrations/0052_offtopicchannelname_used.py new file mode 100644 index 00000000..dfdf3835 --- /dev/null +++ b/pydis_site/apps/api/migrations/0052_offtopicchannelname_used.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.11 on 2020-03-30 10:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0051_create_news_setting'), + ] + + operations = [ + migrations.AddField( + model_name='offtopicchannelname', + name='used', + field=models.BooleanField(default=False, help_text='Whether or not this name has already been used during this rotation'), + ), + ] -- cgit v1.2.3 From 7d5e1f9c60007b230fecdb2b649c5574462fdeb1 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 28 May 2020 09:06:21 +0300 Subject: OT: Refactor off-topic-names random items getting Remove `mark_used` parameter and move this functionality to `random_items` parameter. Update docstring of class --- .../api/viewsets/bot/off_topic_channel_name.py | 55 ++++++++++------------ 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py b/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py index 4328c894..e6cf8172 100644 --- a/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py +++ b/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py @@ -20,11 +20,9 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet): Return all known off-topic channel names from the database. If the `random_items` query parameter is given, for example using... $ curl api.pythondiscord.local:8000/bot/off-topic-channel-names?random_items=5 - ... then the API will return `5` random items from the database. - If the `mark_used` query parameter is given like... - $ curl api.pydis.local:8000/bot/off-topic-channel-names?random_items=5&mark_used=true - ... then the API will mark returned `5` items `used`. - When running out of names, API will mark all names to not used and start new round. + ... then the API will return `5` random items from the database + that is not used in current rotation. + When running out of names, API will mark all names to not used and start new rotation. #### Response format Return a list of off-topic-channel names: @@ -110,32 +108,29 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet): 'random_items': ["Must be a positive integer."] }) - if 'mark_used' in request.query_params and request.query_params['mark_used']: - queryset = self.get_queryset().order_by('?').exclude(used=True)[:random_count] - self.get_queryset().filter( + queryset = self.get_queryset().order_by('?').exclude(used=True)[:random_count] + self.get_queryset().filter( + name__in=(query.name for query in queryset) + ).update(used=True) + + # When client request more channel names than non-used names is available, start + # new round of names. + if len(queryset) < random_count: + # Get how much names still missing and don't fetch duplicate names. + need_more = random_count - len(queryset) + ext = self.get_queryset().order_by('?').exclude( name__in=(query.name for query in queryset) - ).update(used=True) - - # When client request more channel names than non-used names is available, start - # new round of names. - if len(queryset) < random_count: - # Get how much names still missing and don't fetch duplicate names. - need_more = random_count - len(queryset) - ext = self.get_queryset().order_by('?').exclude( - name__in=(query.name for query in queryset) - )[:need_more] - - # Set all names `used` field to False except these that we just used. - self.get_queryset().exclude(name__in=( - query.name for query in ext) - ).update(used=False) - # Join original queryset (that had missing names) - # and extension with these missing names. - queryset = list(queryset) + list(ext) - serialized = self.serializer_class(queryset, many=True) - return Response(serialized.data) - - queryset = self.get_queryset().order_by('?')[:random_count] + )[:need_more] + + # Set all names `used` field to False except these that we just used. + self.get_queryset().exclude(name__in=( + query.name for query in ext) + ).update(used=False) + + # Join original queryset (that had missing names) + # and extension with these missing names. + queryset = list(queryset) + list(ext) + serialized = self.serializer_class(queryset, many=True) return Response(serialized.data) -- cgit v1.2.3 From 8584aa2a409922c60b9c4a8e0874bc92cd01b9a1 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 28 May 2020 09:12:03 +0300 Subject: OT Tests: Refactor tests to latest change --- .../apps/api/tests/test_off_topic_channel_names.py | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/pydis_site/apps/api/tests/test_off_topic_channel_names.py b/pydis_site/apps/api/tests/test_off_topic_channel_names.py index 06624d89..51372651 100644 --- a/pydis_site/apps/api/tests/test_off_topic_channel_names.py +++ b/pydis_site/apps/api/tests/test_off_topic_channel_names.py @@ -21,12 +21,6 @@ class UnauthenticatedTests(APISubdomainTestCase): self.assertEqual(response.status_code, 401) - def test_cannot_read_off_topic_channel_name_list_with_random_item_and_mark_used_param(self): - url = reverse('bot:offtopicchannelname-list', host='api') - response = self.client.get(f'{url}?random_items=no&mark_used=true') - - self.assertEqual(response.status_code, 401) - class EmptyDatabaseTests(APISubdomainTestCase): def test_returns_empty_object(self): @@ -87,17 +81,11 @@ class ListTests(APISubdomainTestCase): self.assertEqual(response.status_code, 200) self.assertEqual(len(response.json()), 1) - - def test_returns_single_correct_item_with_mark_used_parameter_true_and_random_items_1(self): - url = reverse('bot:offtopicchannelname-list', host='api') - response = self.client.get(f'{url}?random_items=1&mark_used=true') - - self.assertEqual(response.status_code, 200) self.assertEqual(response.json(), [self.test_name.name]) - def test_running_out_of_names_with_mark_used_parameter(self): + def test_running_out_of_names_with_random_parameter(self): url = reverse('bot:offtopicchannelname-list', host='api') - response = self.client.get(f'{url}?random_items=2&mark_used=true') + response = self.client.get(f'{url}?random_items=2') self.assertEqual(response.status_code, 200) self.assertEqual(response.json(), [self.test_name.name, self.test_name_2.name]) -- cgit v1.2.3 From 72e3ee0645d8017bfdac27f9fd876137164f3e3e Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 28 May 2020 09:30:05 +0300 Subject: OT Tests: Add docstring to tests --- pydis_site/apps/api/tests/test_off_topic_channel_names.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pydis_site/apps/api/tests/test_off_topic_channel_names.py b/pydis_site/apps/api/tests/test_off_topic_channel_names.py index 51372651..cac9405a 100644 --- a/pydis_site/apps/api/tests/test_off_topic_channel_names.py +++ b/pydis_site/apps/api/tests/test_off_topic_channel_names.py @@ -10,12 +10,14 @@ class UnauthenticatedTests(APISubdomainTestCase): self.client.force_authenticate(user=None) def test_cannot_read_off_topic_channel_name_list(self): + """Test does this return 401 response code when not authenticated.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(url) self.assertEqual(response.status_code, 401) def test_cannot_read_off_topic_channel_name_list_with_random_item_param(self): + """Test does this give 401 code when `random_items` provided and not authenticated.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(f'{url}?random_items=no') @@ -24,6 +26,7 @@ class UnauthenticatedTests(APISubdomainTestCase): class EmptyDatabaseTests(APISubdomainTestCase): def test_returns_empty_object(self): + """Test does this return empty list when no names in database.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(url) @@ -31,6 +34,7 @@ class EmptyDatabaseTests(APISubdomainTestCase): self.assertEqual(response.json(), []) def test_returns_empty_list_with_get_all_param(self): + """Test does this return empty list when no names and `random_items` param provided.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(f'{url}?random_items=5') @@ -38,6 +42,7 @@ class EmptyDatabaseTests(APISubdomainTestCase): self.assertEqual(response.json(), []) def test_returns_400_for_bad_random_items_param(self): + """Test does this return error message when passing not integer as `random_items`.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(f'{url}?random_items=totally-a-valid-integer') @@ -47,6 +52,7 @@ class EmptyDatabaseTests(APISubdomainTestCase): }) def test_returns_400_for_negative_random_items_param(self): + """Test does this return error message when passing negative int as `random_items`.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(f'{url}?random_items=-5') @@ -63,6 +69,7 @@ class ListTests(APISubdomainTestCase): cls.test_name_2 = OffTopicChannelName.objects.create(name='bbq-with-bisk', used=True) def test_returns_name_in_list(self): + """Test does this return all off-topic channel names.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(url) @@ -76,6 +83,7 @@ class ListTests(APISubdomainTestCase): ) def test_returns_single_item_with_random_items_param_set_to_1(self): + """Test does this return not-used name instead used.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(f'{url}?random_items=1') @@ -84,6 +92,7 @@ class ListTests(APISubdomainTestCase): self.assertEqual(response.json(), [self.test_name.name]) def test_running_out_of_names_with_random_parameter(self): + """Test does this reset names `used` parameter to `False` when running out of names.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(f'{url}?random_items=2') @@ -101,6 +110,7 @@ class CreationTests(APISubdomainTestCase): self.assertEqual(response.status_code, 201) def test_returns_201_for_unicode_chars(self): + """Test does this accept all valid characters.""" url = reverse('bot:offtopicchannelname-list', host='api') names = ( '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹', @@ -112,6 +122,7 @@ class CreationTests(APISubdomainTestCase): self.assertEqual(response.status_code, 201) def test_returns_400_for_missing_name_param(self): + """Test does this return error message when name not provided.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.post(url) self.assertEqual(response.status_code, 400) @@ -120,6 +131,7 @@ class CreationTests(APISubdomainTestCase): }) def test_returns_400_for_bad_name_param(self): + """Test does this return error message when invalid characters provided.""" url = reverse('bot:offtopicchannelname-list', host='api') invalid_names = ( 'space between words', @@ -142,18 +154,21 @@ class DeletionTests(APISubdomainTestCase): cls.test_name_2 = OffTopicChannelName.objects.create(name='bbq-with-bisk') def test_deleting_unknown_name_returns_404(self): + """Test does this return 404 code when trying to delete unknown name.""" url = reverse('bot:offtopicchannelname-detail', args=('unknown-name',), host='api') response = self.client.delete(url) self.assertEqual(response.status_code, 404) def test_deleting_known_name_returns_204(self): + """Test does this return 204 code when deleting was successful.""" url = reverse('bot:offtopicchannelname-detail', args=(self.test_name.name,), host='api') response = self.client.delete(url) self.assertEqual(response.status_code, 204) def test_name_gets_deleted(self): + """Test does name gets actually deleted.""" url = reverse('bot:offtopicchannelname-detail', args=(self.test_name_2.name,), host='api') response = self.client.delete(url) -- cgit v1.2.3 From 5c53af373f9f868c5603a903a2c2d9636c2d0982 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 28 May 2020 09:35:17 +0300 Subject: OT: Fix comments --- pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py b/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py index e6cf8172..c4520a48 100644 --- a/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py +++ b/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py @@ -113,16 +113,16 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet): name__in=(query.name for query in queryset) ).update(used=True) - # When client request more channel names than non-used names is available, start - # new round of names. + # When the client requests more channel names than are available, + # we reset all names to used=False and start a new round of names. if len(queryset) < random_count: - # Get how much names still missing and don't fetch duplicate names. + # Figure out how many additional names we need, and don't fetch duplicate names. need_more = random_count - len(queryset) ext = self.get_queryset().order_by('?').exclude( name__in=(query.name for query in queryset) )[:need_more] - # Set all names `used` field to False except these that we just used. + # Reset the `used` field to False for all names except the ones we just used. self.get_queryset().exclude(name__in=( query.name for query in ext) ).update(used=False) -- cgit v1.2.3 From e046ac1d764d80a6a04c3c2d70fb8b4d718b6210 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 28 May 2020 09:36:24 +0300 Subject: OT: Rename variable `need_more` to `names_needed` --- pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py b/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py index c4520a48..1099922c 100644 --- a/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py +++ b/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py @@ -117,10 +117,10 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet): # we reset all names to used=False and start a new round of names. if len(queryset) < random_count: # Figure out how many additional names we need, and don't fetch duplicate names. - need_more = random_count - len(queryset) + names_needed = random_count - len(queryset) ext = self.get_queryset().order_by('?').exclude( name__in=(query.name for query in queryset) - )[:need_more] + )[:names_needed] # Reset the `used` field to False for all names except the ones we just used. self.get_queryset().exclude(name__in=( -- cgit v1.2.3 From 9cedba3870947ca10218c65c4106f5dd095d230c Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 28 May 2020 09:37:29 +0300 Subject: OT: Rename variable `ext` to `other_names` --- pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py b/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py index 1099922c..9af69ae4 100644 --- a/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py +++ b/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py @@ -118,18 +118,18 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet): if len(queryset) < random_count: # Figure out how many additional names we need, and don't fetch duplicate names. names_needed = random_count - len(queryset) - ext = self.get_queryset().order_by('?').exclude( + other_names = self.get_queryset().order_by('?').exclude( name__in=(query.name for query in queryset) )[:names_needed] # Reset the `used` field to False for all names except the ones we just used. self.get_queryset().exclude(name__in=( - query.name for query in ext) + query.name for query in other_names) ).update(used=False) # Join original queryset (that had missing names) # and extension with these missing names. - queryset = list(queryset) + list(ext) + queryset = list(queryset) + list(other_names) serialized = self.serializer_class(queryset, many=True) return Response(serialized.data) -- cgit v1.2.3 From 9e70bb65627f0b934aa1d22195a18778fba2d592 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sat, 22 Aug 2020 01:35:36 +0200 Subject: Add gitpython ~=3.1.7 as a dependency. We'll need this to fetch the hash. --- Pipfile | 1 + Pipfile.lock | 206 +++++++++++++++++++++++++++++++++-------------------------- 2 files changed, 117 insertions(+), 90 deletions(-) diff --git a/Pipfile b/Pipfile index 0f794078..d23d28e6 100644 --- a/Pipfile +++ b/Pipfile @@ -20,6 +20,7 @@ pyyaml = "~=5.1" pyuwsgi = {version = "~=2.0", sys_platform = "!='win32'"} django-allauth = "~=0.41" sentry-sdk = "~=0.14" +gitpython = "~=3.1.7" [dev-packages] coverage = "~=5.0" diff --git a/Pipfile.lock b/Pipfile.lock index 02d81d76..b8c85d33 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "edc59f1c711954bd22606d68e00f44c21c68a7b3193b20e44a86438e24c0f54b" + "sha256": "ad586b840e82b4ae87eed1af70adde0b2c8b7f862a832c4cfa87748b97add3bd" }, "pipfile-spec": 6, "requires": { @@ -56,11 +56,11 @@ }, "django": { "hashes": [ - "sha256:31a5fbbea5fc71c99e288ec0b2f00302a0a92c44b13ede80b73a6a4d6d205582", - "sha256:5457fc953ec560c5521b41fad9e6734a4668b7ba205832191bbdff40ec61073c" + "sha256:96fbe04e8ba0df289171e7f6970e0ff8b472bf4f909ed9e0e5beccbac7e1dbbe", + "sha256:c22b4cd8e388f8219dc121f091e53a8701f9f5bca9aa132b5254263cab516215" ], "index": "pypi", - "version": "==3.0.8" + "version": "==3.0.9" }, "django-allauth": { "hashes": [ @@ -129,19 +129,19 @@ }, "django-simple-bulma": { "hashes": [ - "sha256:a1462088791af5c65d2ea3b5a517a481dd8afb35b324979cdeefa6f3e6c58d3d", - "sha256:a93daf425353834db96840ca4aa7744c796899243f114e73b8159724ce4573c1" + "sha256:79928fa983151947c635acf65fa5177ca775db98c8d53ddf1c785fe48c727466", + "sha256:e5cff3fc5f0d45558362ab8d0e11f92887c4fc85616f77daa6174940f94b12c7" ], "index": "pypi", - "version": "==1.2.0" + "version": "==1.3.2" }, "djangorestframework": { "hashes": [ - "sha256:05809fc66e1c997fd9a32ea5730d9f4ba28b109b9da71fccfa5ff241201fd0a4", - "sha256:e782087823c47a26826ee5b6fa0c542968219263fb3976ec3c31edab23a4001f" + "sha256:6dd02d5a4bd2516fb93f80360673bf540c3b6641fec8766b1da2870a5aa00b32", + "sha256:8b1ac62c581dbc5799b03e535854b92fc4053ecfe74bad3f9c05782063d4196b" ], "index": "pypi", - "version": "==3.11.0" + "version": "==3.11.1" }, "djangorestframework-bulk": { "hashes": [ @@ -150,6 +150,22 @@ "index": "pypi", "version": "==0.2.1" }, + "gitdb": { + "hashes": [ + "sha256:91f36bfb1ab7949b3b40e23736db18231bf7593edada2ba5c3a174a7b23657ac", + "sha256:c9e1f2d0db7ddb9a704c2a0217be31214e91a4fe1dea1efad19ae42ba0c285c9" + ], + "markers": "python_version >= '3.4'", + "version": "==4.0.5" + }, + "gitpython": { + "hashes": [ + "sha256:2db287d71a284e22e5c2846042d0602465c7434d910406990d5b74df4afb0858", + "sha256:fa3b92da728a457dd75d62bb5f3eb2816d99a7fe6c67398e260637a40e3fafb5" + ], + "index": "pypi", + "version": "==3.1.7" + }, "idna": { "hashes": [ "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", @@ -210,32 +226,34 @@ }, "pillow": { "hashes": [ - "sha256:0295442429645fa16d05bd567ef5cff178482439c9aad0411d3f0ce9b88b3a6f", - "sha256:06aba4169e78c439d528fdeb34762c3b61a70813527a2c57f0540541e9f433a8", - "sha256:09d7f9e64289cb40c2c8d7ad674b2ed6105f55dc3b09aa8e4918e20a0311e7ad", - "sha256:0a80dd307a5d8440b0a08bd7b81617e04d870e40a3e46a32d9c246e54705e86f", - "sha256:1ca594126d3c4def54babee699c055a913efb01e106c309fa6b04405d474d5ae", + "sha256:c92302a33138409e8f1ad16731568c55c9053eee71bb05b6b744067e1b62380f", "sha256:25930fadde8019f374400f7986e8404c8b781ce519da27792cbe46eabec00c4d", - "sha256:431b15cffbf949e89df2f7b48528be18b78bfa5177cb3036284a5508159492b5", + "sha256:1ca594126d3c4def54babee699c055a913efb01e106c309fa6b04405d474d5ae", "sha256:52125833b070791fcb5710fabc640fc1df07d087fc0c0f02d3661f76c23c5b8b", - "sha256:5e51ee2b8114def244384eda1c82b10e307ad9778dac5c83fb0943775a653cd8", - "sha256:612cfda94e9c8346f239bf1a4b082fdd5c8143cf82d685ba2dba76e7adeeb233", "sha256:6d7741e65835716ceea0fd13a7d0192961212fd59e741a46bbed7a473c634ed6", - "sha256:6edb5446f44d901e8683ffb25ebdfc26988ee813da3bf91e12252b57ac163727", - "sha256:725aa6cfc66ce2857d585f06e9519a1cc0ef6d13f186ff3447ab6dff0a09bc7f", - "sha256:8dad18b69f710bf3a001d2bf3afab7c432785d94fcf819c16b5207b1cfd17d38", + "sha256:9c87ef410a58dd54b92424ffd7e28fd2ec65d2f7fc02b76f5e9b2067e355ebf6", "sha256:94cf49723928eb6070a892cb39d6c156f7b5a2db4e8971cb958f7b6b104fb4c4", - "sha256:97f9e7953a77d5a70f49b9a48da7776dc51e9b738151b22dacf101641594a626", - "sha256:9ad7f865eebde135d526bb3163d0b23ffff365cf87e767c649550964ad72785d", + "sha256:edf31f1150778abd4322444c393ab9c7bd2af271dd4dafb4208fb613b1f3cdc9", + "sha256:612cfda94e9c8346f239bf1a4b082fdd5c8143cf82d685ba2dba76e7adeeb233", "sha256:a060cf8aa332052df2158e5a119303965be92c3da6f2d93b6878f0ebca80b2f6", + "sha256:ffe538682dc19cc542ae7c3e504fdf54ca7f86fb8a135e59dd6bc8627eae6cce", + "sha256:8dad18b69f710bf3a001d2bf3afab7c432785d94fcf819c16b5207b1cfd17d38", "sha256:c79f9c5fb846285f943aafeafda3358992d64f0ef58566e23484132ecd8d7d63", - "sha256:c92302a33138409e8f1ad16731568c55c9053eee71bb05b6b744067e1b62380f", - "sha256:d08b23fdb388c0715990cbc06866db554e1822c4bdcf6d4166cf30ac82df8c41", - "sha256:d350f0f2c2421e65fbc62690f26b59b0bcda1b614beb318c81e38647e0f673a1", "sha256:ec29604081f10f16a7aea809ad42e27764188fc258b02259a03a8ff7ded3808d", - "sha256:edf31f1150778abd4322444c393ab9c7bd2af271dd4dafb4208fb613b1f3cdc9", + "sha256:725aa6cfc66ce2857d585f06e9519a1cc0ef6d13f186ff3447ab6dff0a09bc7f", + "sha256:06aba4169e78c439d528fdeb34762c3b61a70813527a2c57f0540541e9f433a8", + "sha256:9ad7f865eebde135d526bb3163d0b23ffff365cf87e767c649550964ad72785d", + "sha256:09d7f9e64289cb40c2c8d7ad674b2ed6105f55dc3b09aa8e4918e20a0311e7ad", + "sha256:0a80dd307a5d8440b0a08bd7b81617e04d870e40a3e46a32d9c246e54705e86f", + "sha256:97f9e7953a77d5a70f49b9a48da7776dc51e9b738151b22dacf101641594a626", "sha256:f7e30c27477dffc3e85c2463b3e649f751789e0f6c8456099eea7ddd53be4a8a", - "sha256:ffe538682dc19cc542ae7c3e504fdf54ca7f86fb8a135e59dd6bc8627eae6cce" + "sha256:6edb5446f44d901e8683ffb25ebdfc26988ee813da3bf91e12252b57ac163727", + "sha256:5e51ee2b8114def244384eda1c82b10e307ad9778dac5c83fb0943775a653cd8", + "sha256:0295442429645fa16d05bd567ef5cff178482439c9aad0411d3f0ce9b88b3a6f", + "sha256:431b15cffbf949e89df2f7b48528be18b78bfa5177cb3036284a5508159492b5", + "sha256:d08b23fdb388c0715990cbc06866db554e1822c4bdcf6d4166cf30ac82df8c41", + "sha256:d350f0f2c2421e65fbc62690f26b59b0bcda1b614beb318c81e38647e0f673a1", + "sha256:e901964262a56d9ea3c2693df68bc9860b8bdda2b04768821e4c44ae797de117" ], "markers": "python_version >= '3.5'", "version": "==7.2.0" @@ -360,11 +378,11 @@ }, "sentry-sdk": { "hashes": [ - "sha256:2f023ff348359ec5f0b73a840e8b08e6a8d3b2613a98c57d11c222ef43879237", - "sha256:380a280cfc7c4ade5912294e6d9aa71ce776b5fca60a3782e9331b0bcd2866bf" + "sha256:d359609e23ec9360b61e5ffdfa417e2f6bca281bfb869608c98c169c7e64acd5", + "sha256:e12eb1c2c01cd9e9cfe70608dbda4ef451f37ef0b7cbb92e5d43f87c341d6334" ], "index": "pypi", - "version": "==0.16.1" + "version": "==0.16.5" }, "six": { "hashes": [ @@ -374,6 +392,14 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.15.0" }, + "smmap": { + "hashes": [ + "sha256:54c44c197c819d5ef1991799a7e30b662d1e520f2ac75c9efbeb54a742214cf4", + "sha256:9c98bbd1f9786d22f14b3d4126894d56befb835ec90cef151af566c7e19b5d24" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==3.0.4" + }, "sorl-thumbnail": { "hashes": [ "sha256:66771521f3c0ed771e1ce8e1aaf1639ebff18f7f5a40cfd3083da8f0fe6c7c99", @@ -392,11 +418,11 @@ }, "urllib3": { "hashes": [ - "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527", - "sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115" + "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a", + "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", - "version": "==1.25.9" + "version": "==1.25.10" }, "webencodings": { "hashes": [ @@ -407,11 +433,11 @@ }, "whitenoise": { "hashes": [ - "sha256:60154b976a13901414a25b0273a841145f77eb34a141f9ae032a0ace3e4d5b27", - "sha256:6dd26bfda3af29177d8ab7333a0c7b7642eb615ce83764f4d15a9aecda3201c4" + "sha256:05ce0be39ad85740a78750c86a93485c40f08ad8c62a6006de0233765996e5c7", + "sha256:05d00198c777028d72d8b0bbd234db605ef6d60e9410125124002518a48e515d" ], "index": "pypi", - "version": "==5.1.0" + "version": "==5.2.0" }, "wiki": { "hashes": [ @@ -440,11 +466,11 @@ }, "attrs": { "hashes": [ - "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", - "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72" + "sha256:0ef97238856430dcf9228e07f316aefc17e8939fc8507e18c6501b761ef1a42a", + "sha256:2867b7b9f8326499ab5b0e2d12801fa5c98842d2cbd22b35112ae04bf85b4dff" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==19.3.0" + "version": "==20.1.0" }, "bandit": { "hashes": [ @@ -455,51 +481,51 @@ }, "cfgv": { "hashes": [ - "sha256:1ccf53320421aeeb915275a196e23b3b8ae87dea8ac6698b1638001d4a486d53", - "sha256:c8e8f552ffcc6194f4e18dd4f68d9aef0c0d58ae7e7be8c82bee3c5e9edfa513" + "sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d", + "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1" ], "markers": "python_full_version >= '3.6.1'", - "version": "==3.1.0" + "version": "==3.2.0" }, "coverage": { "hashes": [ - "sha256:0fc4e0d91350d6f43ef6a61f64a48e917637e1dcfcba4b4b7d543c628ef82c2d", - "sha256:10f2a618a6e75adf64329f828a6a5b40244c1c50f5ef4ce4109e904e69c71bd2", - "sha256:12eaccd86d9a373aea59869bc9cfa0ab6ba8b1477752110cb4c10d165474f703", - "sha256:1874bdc943654ba46d28f179c1846f5710eda3aeb265ff029e0ac2b52daae404", - "sha256:1dcebae667b73fd4aa69237e6afb39abc2f27520f2358590c1b13dd90e32abe7", - "sha256:1e58fca3d9ec1a423f1b7f2aa34af4f733cbfa9020c8fe39ca451b6071237405", - "sha256:214eb2110217f2636a9329bc766507ab71a3a06a8ea30cdeebb47c24dce5972d", - "sha256:25fe74b5b2f1b4abb11e103bb7984daca8f8292683957d0738cd692f6a7cc64c", - "sha256:32ecee61a43be509b91a526819717d5e5650e009a8d5eda8631a59c721d5f3b6", - "sha256:3740b796015b889e46c260ff18b84683fa2e30f0f75a171fb10d2bf9fb91fc70", - "sha256:3b2c34690f613525672697910894b60d15800ac7e779fbd0fccf532486c1ba40", - "sha256:41d88736c42f4a22c494c32cc48a05828236e37c991bd9760f8923415e3169e4", - "sha256:42fa45a29f1059eda4d3c7b509589cc0343cd6bbf083d6118216830cd1a51613", - "sha256:4bb385a747e6ae8a65290b3df60d6c8a692a5599dc66c9fa3520e667886f2e10", - "sha256:509294f3e76d3f26b35083973fbc952e01e1727656d979b11182f273f08aa80b", - "sha256:5c74c5b6045969b07c9fb36b665c9cac84d6c174a809fc1b21bdc06c7836d9a0", - "sha256:60a3d36297b65c7f78329b80120f72947140f45b5c7a017ea730f9112b40f2ec", - "sha256:6f91b4492c5cde83bfe462f5b2b997cdf96a138f7c58b1140f05de5751623cf1", - "sha256:7403675df5e27745571aba1c957c7da2dacb537c21e14007ec3a417bf31f7f3d", - "sha256:87bdc8135b8ee739840eee19b184804e5d57f518578ffc797f5afa2c3c297913", - "sha256:8a3decd12e7934d0254939e2bf434bf04a5890c5bf91a982685021786a08087e", - "sha256:9702e2cb1c6dec01fb8e1a64c015817c0800a6eca287552c47a5ee0ebddccf62", - "sha256:a4d511012beb967a39580ba7d2549edf1e6865a33e5fe51e4dce550522b3ac0e", - "sha256:bbb387811f7a18bdc61a2ea3d102be0c7e239b0db9c83be7bfa50f095db5b92a", - "sha256:bfcc811883699ed49afc58b1ed9f80428a18eb9166422bce3c31a53dba00fd1d", - "sha256:c32aa13cc3fe86b0f744dfe35a7f879ee33ac0a560684fef0f3e1580352b818f", - "sha256:ca63dae130a2e788f2b249200f01d7fa240f24da0596501d387a50e57aa7075e", - "sha256:d54d7ea74cc00482a2410d63bf10aa34ebe1c49ac50779652106c867f9986d6b", - "sha256:d67599521dff98ec8c34cd9652cbcfe16ed076a2209625fca9dc7419b6370e5c", - "sha256:d82db1b9a92cb5c67661ca6616bdca6ff931deceebb98eecbd328812dab52032", - "sha256:d9ad0a988ae20face62520785ec3595a5e64f35a21762a57d115dae0b8fb894a", - "sha256:ebf2431b2d457ae5217f3a1179533c456f3272ded16f8ed0b32961a6d90e38ee", - "sha256:ed9a21502e9223f563e071759f769c3d6a2e1ba5328c31e86830368e8d78bc9c", - "sha256:f50632ef2d749f541ca8e6c07c9928a37f87505ce3a9f20c8446ad310f1aa87b" - ], - "index": "pypi", - "version": "==5.2" + "sha256:098a703d913be6fbd146a8c50cc76513d726b022d170e5e98dc56d958fd592fb", + "sha256:16042dc7f8e632e0dcd5206a5095ebd18cb1d005f4c89694f7f8aafd96dd43a3", + "sha256:1adb6be0dcef0cf9434619d3b892772fdb48e793300f9d762e480e043bd8e716", + "sha256:27ca5a2bc04d68f0776f2cdcb8bbd508bbe430a7bf9c02315cd05fb1d86d0034", + "sha256:28f42dc5172ebdc32622a2c3f7ead1b836cdbf253569ae5673f499e35db0bac3", + "sha256:2fcc8b58953d74d199a1a4d633df8146f0ac36c4e720b4a1997e9b6327af43a8", + "sha256:304fbe451698373dc6653772c72c5d5e883a4aadaf20343592a7abb2e643dae0", + "sha256:30bc103587e0d3df9e52cd9da1dd915265a22fad0b72afe54daf840c984b564f", + "sha256:40f70f81be4d34f8d491e55936904db5c527b0711b2a46513641a5729783c2e4", + "sha256:4186fc95c9febeab5681bc3248553d5ec8c2999b8424d4fc3a39c9cba5796962", + "sha256:46794c815e56f1431c66d81943fa90721bb858375fb36e5903697d5eef88627d", + "sha256:4869ab1c1ed33953bb2433ce7b894a28d724b7aa76c19b11e2878034a4e4680b", + "sha256:4f6428b55d2916a69f8d6453e48a505c07b2245653b0aa9f0dee38785939f5e4", + "sha256:52f185ffd3291196dc1aae506b42e178a592b0b60a8610b108e6ad892cfc1bb3", + "sha256:538f2fd5eb64366f37c97fdb3077d665fa946d2b6d95447622292f38407f9258", + "sha256:64c4f340338c68c463f1b56e3f2f0423f7b17ba6c3febae80b81f0e093077f59", + "sha256:675192fca634f0df69af3493a48224f211f8db4e84452b08d5fcebb9167adb01", + "sha256:700997b77cfab016533b3e7dbc03b71d33ee4df1d79f2463a318ca0263fc29dd", + "sha256:8505e614c983834239f865da2dd336dcf9d72776b951d5dfa5ac36b987726e1b", + "sha256:962c44070c281d86398aeb8f64e1bf37816a4dfc6f4c0f114756b14fc575621d", + "sha256:9e536783a5acee79a9b308be97d3952b662748c4037b6a24cbb339dc7ed8eb89", + "sha256:9ea749fd447ce7fb1ac71f7616371f04054d969d412d37611716721931e36efd", + "sha256:a34cb28e0747ea15e82d13e14de606747e9e484fb28d63c999483f5d5188e89b", + "sha256:a3ee9c793ffefe2944d3a2bd928a0e436cd0ac2d9e3723152d6fd5398838ce7d", + "sha256:aab75d99f3f2874733946a7648ce87a50019eb90baef931698f96b76b6769a46", + "sha256:b1ed2bdb27b4c9fc87058a1cb751c4df8752002143ed393899edb82b131e0546", + "sha256:b360d8fd88d2bad01cb953d81fd2edd4be539df7bfec41e8753fe9f4456a5082", + "sha256:b8f58c7db64d8f27078cbf2a4391af6aa4e4767cc08b37555c4ae064b8558d9b", + "sha256:c1bbb628ed5192124889b51204de27c575b3ffc05a5a91307e7640eff1d48da4", + "sha256:c2ff24df02a125b7b346c4c9078c8936da06964cc2d276292c357d64378158f8", + "sha256:c890728a93fffd0407d7d37c1e6083ff3f9f211c83b4316fae3778417eab9811", + "sha256:c96472b8ca5dc135fb0aa62f79b033f02aa434fb03a8b190600a5ae4102df1fd", + "sha256:ce7866f29d3025b5b34c2e944e66ebef0d92e4a4f2463f7266daa03a1332a651", + "sha256:e26c993bd4b220429d4ec8c1468eca445a4064a61c74ca08da7429af9bc53bb0" + ], + "index": "pypi", + "version": "==5.2.1" }, "distlib": { "hashes": [ @@ -605,16 +631,16 @@ "sha256:2db287d71a284e22e5c2846042d0602465c7434d910406990d5b74df4afb0858", "sha256:fa3b92da728a457dd75d62bb5f3eb2816d99a7fe6c67398e260637a40e3fafb5" ], - "markers": "python_version >= '3.4'", + "index": "pypi", "version": "==3.1.7" }, "identify": { "hashes": [ - "sha256:06b4373546ae55eaaefdac54f006951dbd968fe2912846c00e565b09cfaed101", - "sha256:5519601b70c831011fb425ffd214101df7639ba3980f24dc283f7675b19127b3" + "sha256:69c4769f085badafd0e04b1763e847258cbbf6d898e8678ebffc91abdb86f6c6", + "sha256:d6ae6daee50ba1b493e9ca4d36a5edd55905d2cf43548fdc20b2a14edef102e7" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.4.24" + "version": "==1.4.28" }, "importlib-metadata": { "hashes": [ @@ -769,19 +795,19 @@ }, "unittest-xml-reporting": { "hashes": [ - "sha256:74eaf7739a7957a74f52b8187c5616f61157372189bef0a32ba5c30bbc00e58a", - "sha256:e09b8ae70cce9904cdd331f53bf929150962869a5324ab7ff3dd6c8b87e01f7d" + "sha256:7bf515ea8cb244255a25100cd29db611a73f8d3d0aaf672ed3266307e14cc1ca", + "sha256:984cebba69e889401bfe3adb9088ca376b3a1f923f0590d005126c1bffd1a695" ], "index": "pypi", - "version": "==3.0.2" + "version": "==3.0.4" }, "virtualenv": { "hashes": [ - "sha256:26cdd725a57fef4c7c22060dba4647ebd8ca377e30d1c1cf547b30a0b79c43b4", - "sha256:c51f1ba727d1614ce8fd62457748b469fbedfdab2c7e5dd480c9ae3fbe1233f1" + "sha256:43add625c53c596d38f971a465553f6318decc39d98512bc100fa1b1e839c8dc", + "sha256:e0305af10299a7fb0d69393d8f04cb2965dda9351140d11ac8db4e5e3970451b" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==20.0.27" + "version": "==20.0.31" }, "zipp": { "hashes": [ -- cgit v1.2.3 From 1d4db565aedb1b13b8594f4586748698d85d6139 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sat, 22 Aug 2020 01:36:05 +0200 Subject: Install git in the docker environment. This is required for gitpython to work. --- docker/Dockerfile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index aa427947..d9da7d14 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -8,6 +8,11 @@ ENV PIP_NO_CACHE_DIR=false \ PIPENV_HIDE_EMOJIS=1 \ PIPENV_NOSPIN=1 +# Install git +RUN apt-get update && \ + apt-get upgrade -y && \ + apt-get install -y git + # Create non-root user. RUN useradd --system --shell /bin/false --uid 1500 pysite -- cgit v1.2.3 From 1a55e92824d56a34a677c1f764a5393038e66955 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sat, 22 Aug 2020 01:40:55 +0200 Subject: Add a context processor that outputs the git SHA. This will make the SHA available in all templates. --- pydis_site/context_processors.py | 10 ++++++++++ pydis_site/settings.py | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 pydis_site/context_processors.py diff --git a/pydis_site/context_processors.py b/pydis_site/context_processors.py new file mode 100644 index 00000000..4ae0dbb3 --- /dev/null +++ b/pydis_site/context_processors.py @@ -0,0 +1,10 @@ +import git +from django.template import RequestContext + +REPO = git.Repo(search_parent_directories=True) +SHA = REPO.head.object.hexsha + + +def git_sha_processor(_: RequestContext) -> dict: + """Expose the git SHA for this repo to all views.""" + return {'git_sha': SHA} diff --git a/pydis_site/settings.py b/pydis_site/settings.py index 2c87007c..afa097be 100644 --- a/pydis_site/settings.py +++ b/pydis_site/settings.py @@ -157,8 +157,8 @@ TEMPLATES = [ 'django.template.context_processors.static', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', - "sekizai.context_processors.sekizai", + "pydis_site.context_processors.git_sha_processor" ], }, }, -- cgit v1.2.3 From 75181a55977abe2eb6f3ac782b51bf13b8945024 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sat, 22 Aug 2020 01:41:20 +0200 Subject: Add a comment with the git SHA to base templates. --- pydis_site/templates/base/base.html | 1 + pydis_site/templates/wiki/base.html | 1 + 2 files changed, 2 insertions(+) diff --git a/pydis_site/templates/base/base.html b/pydis_site/templates/base/base.html index 4c70d778..70426dc1 100644 --- a/pydis_site/templates/base/base.html +++ b/pydis_site/templates/base/base.html @@ -37,6 +37,7 @@ {% render_block "css" %} +
{% if messages %} diff --git a/pydis_site/templates/wiki/base.html b/pydis_site/templates/wiki/base.html index 846492ab..c9faf914 100644 --- a/pydis_site/templates/wiki/base.html +++ b/pydis_site/templates/wiki/base.html @@ -19,6 +19,7 @@ {% endblock %} {% block content %} + {% block site_navbar %} {% include "base/navbar.html" %} {% endblock %} -- cgit v1.2.3 From 09538085645d623b8cb685c602b3a4e524b6408f Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sat, 22 Aug 2020 02:03:19 +0200 Subject: Optimize the git install in Dockerfile. --- docker/Dockerfile | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index d9da7d14..d3d0319d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -9,9 +9,13 @@ ENV PIP_NO_CACHE_DIR=false \ PIPENV_NOSPIN=1 # Install git -RUN apt-get update && \ - apt-get upgrade -y && \ - apt-get install -y git +RUN apt-get update +RUN apt-get install -y git + +RUN apt-get -y update \ + && apt-get install -y \ + git \ + && rm -rf /var/lib/apt/lists/* # Create non-root user. RUN useradd --system --shell /bin/false --uid 1500 pysite -- cgit v1.2.3 From 60bc529e4b1cfa59e82ede102b88fca08f8f93b4 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sat, 22 Aug 2020 02:04:44 +0200 Subject: Move git SHA initialization to __init__.py. This will make it easier to use in multiple places. --- pydis_site/__init__.py | 5 +++++ pydis_site/context_processors.py | 6 ++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/pydis_site/__init__.py b/pydis_site/__init__.py index df67cf71..f0702577 100644 --- a/pydis_site/__init__.py +++ b/pydis_site/__init__.py @@ -1,3 +1,4 @@ +import git from wiki.plugins.macros.mdx import toc # Remove the toc header prefix. There's no option for this, so we gotta monkey patch it. @@ -7,3 +8,7 @@ toc.HEADER_ID_PREFIX = '' # by a string because Allauth won't let us just give it a list _there_, we have to point # at a list _somewhere else_ instead. VALIDATORS = [] + +# Git SHA +repo = git.Repo(search_parent_directories=True) +GIT_SHA = repo.head.object.hexsha diff --git a/pydis_site/context_processors.py b/pydis_site/context_processors.py index 4ae0dbb3..bb66f21d 100644 --- a/pydis_site/context_processors.py +++ b/pydis_site/context_processors.py @@ -1,10 +1,8 @@ -import git from django.template import RequestContext -REPO = git.Repo(search_parent_directories=True) -SHA = REPO.head.object.hexsha +from pydis_site import GIT_SHA def git_sha_processor(_: RequestContext) -> dict: """Expose the git SHA for this repo to all views.""" - return {'git_sha': SHA} + return {'git_sha': GIT_SHA} -- cgit v1.2.3 From 212a06f400bfce95281e4c8d11baf6cb09e70861 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sat, 22 Aug 2020 02:05:24 +0200 Subject: Set the sentry_sdk.init release to git_sha. This is the first step in implementing releases for Sentry. --- pydis_site/settings.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pydis_site/settings.py b/pydis_site/settings.py index afa097be..ff2942f0 100644 --- a/pydis_site/settings.py +++ b/pydis_site/settings.py @@ -20,6 +20,7 @@ import sentry_sdk from django.contrib.messages import constants as messages from sentry_sdk.integrations.django import DjangoIntegration +from pydis_site import GIT_SHA if typing.TYPE_CHECKING: from django.contrib.auth.models import User @@ -33,7 +34,8 @@ env = environ.Env( sentry_sdk.init( dsn=env('SITE_SENTRY_DSN'), integrations=[DjangoIntegration()], - send_default_pii=True + send_default_pii=True, + release=f"pydis-site@{GIT_SHA}" ) # Build paths inside the project like this: os.path.join(BASE_DIR, ...) -- cgit v1.2.3 From 34ad66914d14043b653f3e2245a06a5613b6e975 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sat, 22 Aug 2020 02:17:49 +0200 Subject: Create a GitHub Action to make a Sentry release. This should be all we need to enable the releases feature in Sentry for this project. --- .github/workflows/sentry-release.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/sentry-release.yml diff --git a/.github/workflows/sentry-release.yml b/.github/workflows/sentry-release.yml new file mode 100644 index 00000000..87c85277 --- /dev/null +++ b/.github/workflows/sentry-release.yml @@ -0,0 +1,23 @@ +name: Create Sentry release + +on: + push: + branches: + - master + +jobs: + createSentryRelease: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@master + - name: Create a Sentry.io release + uses: tclindner/sentry-releases-action@v1.2.0 + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: python-discord + SENTRY_PROJECT: site + with: + tagName: ${{ github.sha }} + environment: production + releaseNamePrefix: pydis-site@ -- cgit v1.2.3 From 8ee3d0be942b081df22e7d72244edee46d2bb2c1 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sat, 22 Aug 2020 02:22:13 +0200 Subject: Oops, remove double git install from Dockerfile. --- docker/Dockerfile | 3 --- 1 file changed, 3 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index d3d0319d..97cb73d5 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -9,9 +9,6 @@ ENV PIP_NO_CACHE_DIR=false \ PIPENV_NOSPIN=1 # Install git -RUN apt-get update -RUN apt-get install -y git - RUN apt-get -y update \ && apt-get install -y \ git \ -- cgit v1.2.3 From 80bdfa7d25fef6b565371409ba81a4e3abae0d61 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sat, 22 Aug 2020 02:38:30 +0200 Subject: We need .git in the docker container for the SHA. --- .dockerignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.dockerignore b/.dockerignore index 236295ca..61fa291a 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,7 +1,6 @@ .cache .coverage .coveragerc -.git .github .gitignore .gitlab -- cgit v1.2.3 From 416a0874187fdf96346e3f504d6542b3214957db Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sat, 22 Aug 2020 02:49:28 +0200 Subject: Move git SHA fetcher into utils. --- pydis_site/__init__.py | 5 ----- pydis_site/context_processors.py | 4 ++-- pydis_site/settings.py | 4 ++-- pydis_site/utils/resources.py | 10 ++++++++++ 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/pydis_site/__init__.py b/pydis_site/__init__.py index f0702577..df67cf71 100644 --- a/pydis_site/__init__.py +++ b/pydis_site/__init__.py @@ -1,4 +1,3 @@ -import git from wiki.plugins.macros.mdx import toc # Remove the toc header prefix. There's no option for this, so we gotta monkey patch it. @@ -8,7 +7,3 @@ toc.HEADER_ID_PREFIX = '' # by a string because Allauth won't let us just give it a list _there_, we have to point # at a list _somewhere else_ instead. VALIDATORS = [] - -# Git SHA -repo = git.Repo(search_parent_directories=True) -GIT_SHA = repo.head.object.hexsha diff --git a/pydis_site/context_processors.py b/pydis_site/context_processors.py index bb66f21d..e905d9c7 100644 --- a/pydis_site/context_processors.py +++ b/pydis_site/context_processors.py @@ -1,8 +1,8 @@ from django.template import RequestContext -from pydis_site import GIT_SHA +from pydis_site.utils.resources import get_git_sha def git_sha_processor(_: RequestContext) -> dict: """Expose the git SHA for this repo to all views.""" - return {'git_sha': GIT_SHA} + return {'git_sha': get_git_sha()} diff --git a/pydis_site/settings.py b/pydis_site/settings.py index ff2942f0..e707a526 100644 --- a/pydis_site/settings.py +++ b/pydis_site/settings.py @@ -20,7 +20,7 @@ import sentry_sdk from django.contrib.messages import constants as messages from sentry_sdk.integrations.django import DjangoIntegration -from pydis_site import GIT_SHA +from pydis_site.utils.resources import get_git_sha if typing.TYPE_CHECKING: from django.contrib.auth.models import User @@ -35,7 +35,7 @@ sentry_sdk.init( dsn=env('SITE_SENTRY_DSN'), integrations=[DjangoIntegration()], send_default_pii=True, - release=f"pydis-site@{GIT_SHA}" + release=f"pydis-site@{get_git_sha()}" ) # Build paths inside the project like this: os.path.join(BASE_DIR, ...) diff --git a/pydis_site/utils/resources.py b/pydis_site/utils/resources.py index 637fd785..d36c4b77 100644 --- a/pydis_site/utils/resources.py +++ b/pydis_site/utils/resources.py @@ -4,8 +4,13 @@ import glob import typing from dataclasses import dataclass +import git import yaml +# Git SHA +repo = git.Repo(search_parent_directories=True) +GIT_SHA = repo.head.object.hexsha + @dataclass class URL: @@ -89,3 +94,8 @@ def load_categories(order: typing.List[str]) -> typing.List[Category]: categories.append(Category.construct_from_directory(direc)) return categories + + +def get_git_sha() -> str: + """Get the Git SHA for this repo.""" + return GIT_SHA -- cgit v1.2.3 From 503bfba6855d3019a2739bb1adce69c457a8b26e Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sat, 22 Aug 2020 03:18:25 +0200 Subject: Move git SHA fetcher into its own file. Fix tests. --- pydis_site/context_processors.py | 2 +- pydis_site/settings.py | 2 +- pydis_site/tests/test_utils.py | 11 +++++++++++ pydis_site/utils/__init__.py | 3 +++ pydis_site/utils/resources.py | 10 ---------- pydis_site/utils/utils.py | 10 ++++++++++ 6 files changed, 26 insertions(+), 12 deletions(-) create mode 100644 pydis_site/tests/test_utils.py create mode 100644 pydis_site/utils/__init__.py create mode 100644 pydis_site/utils/utils.py diff --git a/pydis_site/context_processors.py b/pydis_site/context_processors.py index e905d9c7..ab5a4168 100644 --- a/pydis_site/context_processors.py +++ b/pydis_site/context_processors.py @@ -1,6 +1,6 @@ from django.template import RequestContext -from pydis_site.utils.resources import get_git_sha +from pydis_site.utils import get_git_sha def git_sha_processor(_: RequestContext) -> dict: diff --git a/pydis_site/settings.py b/pydis_site/settings.py index e707a526..0a5b0eed 100644 --- a/pydis_site/settings.py +++ b/pydis_site/settings.py @@ -20,7 +20,7 @@ import sentry_sdk from django.contrib.messages import constants as messages from sentry_sdk.integrations.django import DjangoIntegration -from pydis_site.utils.resources import get_git_sha +from pydis_site.utils import get_git_sha if typing.TYPE_CHECKING: from django.contrib.auth.models import User diff --git a/pydis_site/tests/test_utils.py b/pydis_site/tests/test_utils.py new file mode 100644 index 00000000..f1419860 --- /dev/null +++ b/pydis_site/tests/test_utils.py @@ -0,0 +1,11 @@ +from django.test import TestCase + +from pydis_site.utils import get_git_sha +from pydis_site.utils.utils import GIT_SHA + + +class UtilsTests(TestCase): + + def test_git_sha(self): + """Test that the get_git_sha returns the correct SHA.""" + self.assertEqual(get_git_sha(), GIT_SHA) diff --git a/pydis_site/utils/__init__.py b/pydis_site/utils/__init__.py new file mode 100644 index 00000000..bb91b3d8 --- /dev/null +++ b/pydis_site/utils/__init__.py @@ -0,0 +1,3 @@ +from .utils import get_git_sha + +__all__ = ['get_git_sha'] diff --git a/pydis_site/utils/resources.py b/pydis_site/utils/resources.py index d36c4b77..637fd785 100644 --- a/pydis_site/utils/resources.py +++ b/pydis_site/utils/resources.py @@ -4,13 +4,8 @@ import glob import typing from dataclasses import dataclass -import git import yaml -# Git SHA -repo = git.Repo(search_parent_directories=True) -GIT_SHA = repo.head.object.hexsha - @dataclass class URL: @@ -94,8 +89,3 @@ def load_categories(order: typing.List[str]) -> typing.List[Category]: categories.append(Category.construct_from_directory(direc)) return categories - - -def get_git_sha() -> str: - """Get the Git SHA for this repo.""" - return GIT_SHA diff --git a/pydis_site/utils/utils.py b/pydis_site/utils/utils.py new file mode 100644 index 00000000..2033ea19 --- /dev/null +++ b/pydis_site/utils/utils.py @@ -0,0 +1,10 @@ +import git + +# Git SHA +repo = git.Repo(search_parent_directories=True) +GIT_SHA = repo.head.object.hexsha + + +def get_git_sha() -> str: + """Get the Git SHA for this repo.""" + return GIT_SHA -- cgit v1.2.3 From 52d7ae050d32df7ceda6888f8a60590ceb0e37dc Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sat, 22 Aug 2020 03:23:07 +0200 Subject: omit resources.py from coverage reports. I'm not writing the tests for that, it has nothing to do with this branch. --- .coveragerc | 1 + 1 file changed, 1 insertion(+) diff --git a/.coveragerc b/.coveragerc index a49af74e..f5ddf08d 100644 --- a/.coveragerc +++ b/.coveragerc @@ -14,6 +14,7 @@ omit = */urls.py pydis_site/wsgi.py pydis_site/settings.py + pydis_site/utils/resources.py [report] fail_under = 100 -- cgit v1.2.3 From 88d364bfc809d20c6c4645abba75b889a526c804 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sat, 22 Aug 2020 03:46:08 +0200 Subject: Remove the SHA from the wiki base.html This extends the regular base.html, so this would cause wiki pages to have two SHA's. --- pydis_site/templates/wiki/base.html | 1 - 1 file changed, 1 deletion(-) diff --git a/pydis_site/templates/wiki/base.html b/pydis_site/templates/wiki/base.html index c9faf914..846492ab 100644 --- a/pydis_site/templates/wiki/base.html +++ b/pydis_site/templates/wiki/base.html @@ -19,7 +19,6 @@ {% endblock %} {% block content %} - {% block site_navbar %} {% include "base/navbar.html" %} {% endblock %} -- cgit v1.2.3 From a71c38f07bba0191ba28eef12eea56e8a9265349 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sat, 22 Aug 2020 03:54:51 +0200 Subject: Move the SHA into constants.py. The util was redundant. Thanks @MarkKoz --- pydis_site/constants.py | 5 +++++ pydis_site/context_processors.py | 4 ++-- pydis_site/settings.py | 4 ++-- pydis_site/tests/test_utils.py | 11 ----------- pydis_site/utils/__init__.py | 3 --- pydis_site/utils/utils.py | 10 ---------- 6 files changed, 9 insertions(+), 28 deletions(-) create mode 100644 pydis_site/constants.py delete mode 100644 pydis_site/tests/test_utils.py delete mode 100644 pydis_site/utils/__init__.py delete mode 100644 pydis_site/utils/utils.py diff --git a/pydis_site/constants.py b/pydis_site/constants.py new file mode 100644 index 00000000..0b76694a --- /dev/null +++ b/pydis_site/constants.py @@ -0,0 +1,5 @@ +import git + +# Git SHA +repo = git.Repo(search_parent_directories=True) +GIT_SHA = repo.head.object.hexsha diff --git a/pydis_site/context_processors.py b/pydis_site/context_processors.py index ab5a4168..6937a3db 100644 --- a/pydis_site/context_processors.py +++ b/pydis_site/context_processors.py @@ -1,8 +1,8 @@ from django.template import RequestContext -from pydis_site.utils import get_git_sha +from pydis_site.constants import GIT_SHA def git_sha_processor(_: RequestContext) -> dict: """Expose the git SHA for this repo to all views.""" - return {'git_sha': get_git_sha()} + return {'git_sha': GIT_SHA} diff --git a/pydis_site/settings.py b/pydis_site/settings.py index 0a5b0eed..1f042c1b 100644 --- a/pydis_site/settings.py +++ b/pydis_site/settings.py @@ -20,7 +20,7 @@ import sentry_sdk from django.contrib.messages import constants as messages from sentry_sdk.integrations.django import DjangoIntegration -from pydis_site.utils import get_git_sha +from pydis_site.constants import GIT_SHA if typing.TYPE_CHECKING: from django.contrib.auth.models import User @@ -35,7 +35,7 @@ sentry_sdk.init( dsn=env('SITE_SENTRY_DSN'), integrations=[DjangoIntegration()], send_default_pii=True, - release=f"pydis-site@{get_git_sha()}" + release=f"pydis-site@{GIT_SHA}" ) # Build paths inside the project like this: os.path.join(BASE_DIR, ...) diff --git a/pydis_site/tests/test_utils.py b/pydis_site/tests/test_utils.py deleted file mode 100644 index f1419860..00000000 --- a/pydis_site/tests/test_utils.py +++ /dev/null @@ -1,11 +0,0 @@ -from django.test import TestCase - -from pydis_site.utils import get_git_sha -from pydis_site.utils.utils import GIT_SHA - - -class UtilsTests(TestCase): - - def test_git_sha(self): - """Test that the get_git_sha returns the correct SHA.""" - self.assertEqual(get_git_sha(), GIT_SHA) diff --git a/pydis_site/utils/__init__.py b/pydis_site/utils/__init__.py deleted file mode 100644 index bb91b3d8..00000000 --- a/pydis_site/utils/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .utils import get_git_sha - -__all__ = ['get_git_sha'] diff --git a/pydis_site/utils/utils.py b/pydis_site/utils/utils.py deleted file mode 100644 index 2033ea19..00000000 --- a/pydis_site/utils/utils.py +++ /dev/null @@ -1,10 +0,0 @@ -import git - -# Git SHA -repo = git.Repo(search_parent_directories=True) -GIT_SHA = repo.head.object.hexsha - - -def get_git_sha() -> str: - """Get the Git SHA for this repo.""" - return GIT_SHA -- cgit v1.2.3 From d52a20962bbda8f94646ff12c38eb71e2a5241ab Mon Sep 17 00:00:00 2001 From: ks123 Date: Wed, 1 Apr 2020 08:38:25 +0300 Subject: (Tag Cleanup): Removed Tags viewset. --- pydis_site/apps/api/viewsets/bot/__init__.py | 1 - pydis_site/apps/api/viewsets/bot/tag.py | 105 --------------------------- 2 files changed, 106 deletions(-) delete mode 100644 pydis_site/apps/api/viewsets/bot/tag.py diff --git a/pydis_site/apps/api/viewsets/bot/__init__.py b/pydis_site/apps/api/viewsets/bot/__init__.py index e64e3988..84b87eab 100644 --- a/pydis_site/apps/api/viewsets/bot/__init__.py +++ b/pydis_site/apps/api/viewsets/bot/__init__.py @@ -9,5 +9,4 @@ from .off_topic_channel_name import OffTopicChannelNameViewSet from .offensive_message import OffensiveMessageViewSet from .reminder import ReminderViewSet from .role import RoleViewSet -from .tag import TagViewSet from .user import UserViewSet diff --git a/pydis_site/apps/api/viewsets/bot/tag.py b/pydis_site/apps/api/viewsets/bot/tag.py deleted file mode 100644 index 7e9ba117..00000000 --- a/pydis_site/apps/api/viewsets/bot/tag.py +++ /dev/null @@ -1,105 +0,0 @@ -from rest_framework.viewsets import ModelViewSet - -from pydis_site.apps.api.models.bot.tag import Tag -from pydis_site.apps.api.serializers import TagSerializer - - -class TagViewSet(ModelViewSet): - """ - View providing CRUD operations on tags shown by our bot. - - ## Routes - ### GET /bot/tags - Returns all tags in the database. - - #### Response format - >>> [ - ... { - ... 'title': "resources", - ... 'embed': { - ... 'content': "Did you really think I'd put something useful here?" - ... } - ... } - ... ] - - #### Status codes - - 200: returned on success - - ### GET /bot/tags/ - Gets a single tag by its title. - - #### Response format - >>> { - ... 'title': "My awesome tag", - ... 'embed': { - ... 'content': "totally not filler words" - ... } - ... } - - #### Status codes - - 200: returned on success - - 404: if a tag with the given `title` could not be found - - ### POST /bot/tags - Adds a single tag to the database. - - #### Request body - >>> { - ... 'title': str, - ... 'embed': dict - ... } - - The embed structure is the same as the embed structure that the Discord API - expects. You can view the documentation for it here: - https://discordapp.com/developers/docs/resources/channel#embed-object - - #### Status codes - - 201: returned on success - - 400: if one of the given fields is invalid - - ### PUT /bot/tags/ - Update the tag with the given `title`. - - #### Request body - >>> { - ... 'title': str, - ... 'embed': dict - ... } - - The embed structure is the same as the embed structure that the Discord API - expects. You can view the documentation for it here: - https://discordapp.com/developers/docs/resources/channel#embed-object - - #### Status codes - - 200: returned on success - - 400: if the request body was invalid, see response body for details - - 404: if the tag with the given `title` could not be found - - ### PATCH /bot/tags/ - Update the tag with the given `title`. - - #### Request body - >>> { - ... 'title': str, - ... 'embed': dict - ... } - - The embed structure is the same as the embed structure that the Discord API - expects. You can view the documentation for it here: - https://discordapp.com/developers/docs/resources/channel#embed-object - - #### Status codes - - 200: returned on success - - 400: if the request body was invalid, see response body for details - - 404: if the tag with the given `title` could not be found - - ### DELETE /bot/tags/ - Deletes the tag with the given `title`. - - #### Status codes - - 204: returned on success - - 404: if a tag with the given `title` does not exist - """ - - serializer_class = TagSerializer - queryset = Tag.objects.all() -- cgit v1.2.3 From 86409cf41469bd2e5b8d69988a729b3b88bb0a26 Mon Sep 17 00:00:00 2001 From: ks123 Date: Wed, 1 Apr 2020 08:39:37 +0300 Subject: (Tag Cleanup): Removed Tags from Administration --- pydis_site/apps/api/admin.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pydis_site/apps/api/admin.py b/pydis_site/apps/api/admin.py index 0333fefc..dd1291b8 100644 --- a/pydis_site/apps/api/admin.py +++ b/pydis_site/apps/api/admin.py @@ -14,7 +14,6 @@ from .models import ( OffTopicChannelName, OffensiveMessage, Role, - Tag, User ) @@ -64,5 +63,4 @@ admin.site.register(Nomination) admin.site.register(OffensiveMessage) admin.site.register(OffTopicChannelName) admin.site.register(Role) -admin.site.register(Tag) admin.site.register(User) -- cgit v1.2.3 From 14453c9607ed83e040c9b1ed000a31e47568fd69 Mon Sep 17 00:00:00 2001 From: ks123 Date: Wed, 1 Apr 2020 08:41:16 +0300 Subject: (Tag Cleanup): Removed Tags serializer. --- pydis_site/apps/api/serializers.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/pydis_site/apps/api/serializers.py b/pydis_site/apps/api/serializers.py index 52e0d972..90bd6f91 100644 --- a/pydis_site/apps/api/serializers.py +++ b/pydis_site/apps/api/serializers.py @@ -16,7 +16,6 @@ from .models import ( OffensiveMessage, Reminder, Role, - Tag, User ) @@ -250,16 +249,6 @@ class RoleSerializer(ModelSerializer): fields = ('id', 'name', 'colour', 'permissions', 'position') -class TagSerializer(ModelSerializer): - """A class providing (de-)serialization of `Tag` instances.""" - - class Meta: - """Metadata defined for the Django REST Framework.""" - - model = Tag - fields = ('title', 'embed') - - class UserSerializer(BulkSerializerMixin, ModelSerializer): """A class providing (de-)serialization of `User` instances.""" -- cgit v1.2.3 From 05b0c725b45dd87c755a8475d08442a0603a515e Mon Sep 17 00:00:00 2001 From: ks123 Date: Wed, 1 Apr 2020 08:41:58 +0300 Subject: (Tag Cleanup): Removed Tags URL --- pydis_site/apps/api/urls.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pydis_site/apps/api/urls.py b/pydis_site/apps/api/urls.py index a4fd5b2e..4dbf93db 100644 --- a/pydis_site/apps/api/urls.py +++ b/pydis_site/apps/api/urls.py @@ -14,7 +14,6 @@ from .viewsets import ( OffensiveMessageViewSet, ReminderViewSet, RoleViewSet, - TagViewSet, UserViewSet ) @@ -61,10 +60,6 @@ bot_router.register( 'roles', RoleViewSet ) -bot_router.register( - 'tags', - TagViewSet -) bot_router.register( 'users', UserViewSet -- cgit v1.2.3 From bb55875dcdaf838c41b1e3b2c3dc4fdf368b0f09 Mon Sep 17 00:00:00 2001 From: ks123 Date: Wed, 1 Apr 2020 08:43:33 +0300 Subject: (Tag Cleanup): Removed Tags Model test. --- pydis_site/apps/api/tests/test_models.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pydis_site/apps/api/tests/test_models.py b/pydis_site/apps/api/tests/test_models.py index e0e347bb..853e6621 100644 --- a/pydis_site/apps/api/tests/test_models.py +++ b/pydis_site/apps/api/tests/test_models.py @@ -14,7 +14,6 @@ from pydis_site.apps.api.models import ( OffensiveMessage, Reminder, Role, - Tag, User ) from pydis_site.apps.api.models.mixins import ModelReprMixin @@ -104,10 +103,6 @@ class StringDunderMethodTests(SimpleTestCase): ), creation=dt.utcnow() ), - Tag( - title='bob', - embed={'content': "the builder"} - ), User( id=5, name='bob', -- cgit v1.2.3 From 790c50cbc2d45134fc0b668bc21ca7e01e4af2a2 Mon Sep 17 00:00:00 2001 From: ks123 Date: Wed, 1 Apr 2020 08:46:19 +0300 Subject: (Tag Cleanup): Removed Tags Model import from models __init__.py --- pydis_site/apps/api/models/bot/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pydis_site/apps/api/models/bot/__init__.py b/pydis_site/apps/api/models/bot/__init__.py index efd98184..1673b434 100644 --- a/pydis_site/apps/api/models/bot/__init__.py +++ b/pydis_site/apps/api/models/bot/__init__.py @@ -11,5 +11,4 @@ from .off_topic_channel_name import OffTopicChannelName from .offensive_message import OffensiveMessage from .reminder import Reminder from .role import Role -from .tag import Tag from .user import User -- cgit v1.2.3 From b16809620f444872f4d295ee55e4e8e9cdde12c8 Mon Sep 17 00:00:00 2001 From: ks123 Date: Wed, 1 Apr 2020 08:49:49 +0300 Subject: (Tag Cleanup): Moved embed validators from Tag model to utils.py --- pydis_site/apps/api/models/mixins.py | 173 +++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) diff --git a/pydis_site/apps/api/models/mixins.py b/pydis_site/apps/api/models/mixins.py index 5d75b78b..2d658cb9 100644 --- a/pydis_site/apps/api/models/mixins.py +++ b/pydis_site/apps/api/models/mixins.py @@ -1,4 +1,177 @@ +from collections.abc import Mapping from operator import itemgetter +from typing import Any, Dict + +from django.core.exceptions import ValidationError +from django.core.validators import MaxLengthValidator, MinLengthValidator + + +def is_bool_validator(value: Any) -> None: + """Validates if a given value is of type bool.""" + if not isinstance(value, bool): + raise ValidationError(f"This field must be of type bool, not {type(value)}.") + + +def validate_tag_embed_fields(fields: dict) -> None: + """Raises a ValidationError if any of the given embed fields is invalid.""" + field_validators = { + 'name': (MaxLengthValidator(limit_value=256),), + 'value': (MaxLengthValidator(limit_value=1024),), + 'inline': (is_bool_validator,), + } + + required_fields = ('name', 'value') + + for field in fields: + if not isinstance(field, Mapping): + raise ValidationError("Embed fields must be a mapping.") + + if not all(required_field in field for required_field in required_fields): + raise ValidationError( + f"Embed fields must contain the following fields: {', '.join(required_fields)}." + ) + + for field_name, value in field.items(): + if field_name not in field_validators: + raise ValidationError(f"Unknown embed field field: {field_name!r}.") + + for validator in field_validators[field_name]: + validator(value) + + +def validate_tag_embed_footer(footer: Dict[str, str]) -> None: + """Raises a ValidationError if the given footer is invalid.""" + field_validators = { + 'text': ( + MinLengthValidator( + limit_value=1, + message="Footer text must not be empty." + ), + MaxLengthValidator(limit_value=2048) + ), + 'icon_url': (), + 'proxy_icon_url': () + } + + if not isinstance(footer, Mapping): + raise ValidationError("Embed footer must be a mapping.") + + for field_name, value in footer.items(): + if field_name not in field_validators: + raise ValidationError(f"Unknown embed footer field: {field_name!r}.") + + for validator in field_validators[field_name]: + validator(value) + + +def validate_tag_embed_author(author: Any) -> None: + """Raises a ValidationError if the given author is invalid.""" + field_validators = { + 'name': ( + MinLengthValidator( + limit_value=1, + message="Embed author name must not be empty." + ), + MaxLengthValidator(limit_value=256) + ), + 'url': (), + 'icon_url': (), + 'proxy_icon_url': () + } + + if not isinstance(author, Mapping): + raise ValidationError("Embed author must be a mapping.") + + for field_name, value in author.items(): + if field_name not in field_validators: + raise ValidationError(f"Unknown embed author field: {field_name!r}.") + + for validator in field_validators[field_name]: + validator(value) + + +def validate_tag_embed(embed: Any) -> None: + """ + Validate a JSON document containing an embed as possible to send on Discord. + + This attempts to rebuild the validation used by Discord + as well as possible by checking for various embed limits so we can + ensure that any embed we store here will also be accepted as a + valid embed by the Discord API. + + Using this directly is possible, although not intended - you usually + stick this onto the `validators` keyword argument of model fields. + + Example: + + >>> from django.contrib.postgres import fields as pgfields + >>> from django.db import models + >>> from pydis_site.apps.api.models.bot.tag import validate_tag_embed + >>> class MyMessage(models.Model): + ... embed = pgfields.JSONField( + ... validators=( + ... validate_tag_embed, + ... ) + ... ) + ... # ... + ... + + Args: + embed (Any): + A dictionary describing the contents of this embed. + See the official documentation for a full reference + of accepted keys by this dictionary: + https://discordapp.com/developers/docs/resources/channel#embed-object + + Raises: + ValidationError: + In case the given embed is deemed invalid, a `ValidationError` + is raised which in turn will allow Django to display errors + as appropriate. + """ + all_keys = { + 'title', 'type', 'description', 'url', 'timestamp', + 'color', 'footer', 'image', 'thumbnail', 'video', + 'provider', 'author', 'fields' + } + one_required_of = {'description', 'fields', 'image', 'title', 'video'} + field_validators = { + 'title': ( + MinLengthValidator( + limit_value=1, + message="Embed title must not be empty." + ), + MaxLengthValidator(limit_value=256) + ), + 'description': (MaxLengthValidator(limit_value=2048),), + 'fields': ( + MaxLengthValidator(limit_value=25), + validate_tag_embed_fields + ), + 'footer': (validate_tag_embed_footer,), + 'author': (validate_tag_embed_author,) + } + + if not embed: + raise ValidationError("Tag embed must not be empty.") + + elif not isinstance(embed, Mapping): + raise ValidationError("Tag embed must be a mapping.") + + elif not any(field in embed for field in one_required_of): + raise ValidationError(f"Tag embed must contain one of the fields {one_required_of}.") + + for required_key in one_required_of: + if required_key in embed and not embed[required_key]: + raise ValidationError(f"Key {required_key!r} must not be empty.") + + for field_name, value in embed.items(): + if field_name not in all_keys: + raise ValidationError(f"Unknown field name: {field_name!r}") + + if field_name in field_validators: + for validator in field_validators[field_name]: + validator(value) from django.db import models -- cgit v1.2.3 From 1f623b6629bdbc854323dc71d149a647355031c6 Mon Sep 17 00:00:00 2001 From: ks123 Date: Wed, 1 Apr 2020 08:52:05 +0300 Subject: (Tag Cleanup): Removed `tag` from embed validator names due Tags will be removed. --- pydis_site/apps/api/models/mixins.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pydis_site/apps/api/models/mixins.py b/pydis_site/apps/api/models/mixins.py index 2d658cb9..e95888b7 100644 --- a/pydis_site/apps/api/models/mixins.py +++ b/pydis_site/apps/api/models/mixins.py @@ -12,7 +12,7 @@ def is_bool_validator(value: Any) -> None: raise ValidationError(f"This field must be of type bool, not {type(value)}.") -def validate_tag_embed_fields(fields: dict) -> None: +def validate_embed_fields(fields: dict) -> None: """Raises a ValidationError if any of the given embed fields is invalid.""" field_validators = { 'name': (MaxLengthValidator(limit_value=256),), @@ -39,7 +39,7 @@ def validate_tag_embed_fields(fields: dict) -> None: validator(value) -def validate_tag_embed_footer(footer: Dict[str, str]) -> None: +def validate_embed_footer(footer: Dict[str, str]) -> None: """Raises a ValidationError if the given footer is invalid.""" field_validators = { 'text': ( @@ -64,7 +64,7 @@ def validate_tag_embed_footer(footer: Dict[str, str]) -> None: validator(value) -def validate_tag_embed_author(author: Any) -> None: +def validate_embed_author(author: Any) -> None: """Raises a ValidationError if the given author is invalid.""" field_validators = { 'name': ( @@ -90,7 +90,7 @@ def validate_tag_embed_author(author: Any) -> None: validator(value) -def validate_tag_embed(embed: Any) -> None: +def validate_embed(embed: Any) -> None: """ Validate a JSON document containing an embed as possible to send on Discord. @@ -106,11 +106,11 @@ def validate_tag_embed(embed: Any) -> None: >>> from django.contrib.postgres import fields as pgfields >>> from django.db import models - >>> from pydis_site.apps.api.models.bot.tag import validate_tag_embed + >>> from pydis_site.apps.api.models.utils import validate_embed >>> class MyMessage(models.Model): ... embed = pgfields.JSONField( ... validators=( - ... validate_tag_embed, + ... validate_embed, ... ) ... ) ... # ... @@ -146,10 +146,10 @@ def validate_tag_embed(embed: Any) -> None: 'description': (MaxLengthValidator(limit_value=2048),), 'fields': ( MaxLengthValidator(limit_value=25), - validate_tag_embed_fields + validate_embed_fields ), - 'footer': (validate_tag_embed_footer,), - 'author': (validate_tag_embed_author,) + 'footer': (validate_embed_footer,), + 'author': (validate_embed_author,) } if not embed: -- cgit v1.2.3 From 9b6b9d9d01811bbe2d9a1964970e3b4a38180623 Mon Sep 17 00:00:00 2001 From: ks123 Date: Wed, 1 Apr 2020 08:55:09 +0300 Subject: (Tag Cleanup): Replaced import from Tags model with utils.py validator import. --- pydis_site/apps/api/models/bot/message.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pydis_site/apps/api/models/bot/message.py b/pydis_site/apps/api/models/bot/message.py index 78dcbf1d..e929ea25 100644 --- a/pydis_site/apps/api/models/bot/message.py +++ b/pydis_site/apps/api/models/bot/message.py @@ -5,9 +5,8 @@ from django.core.validators import MinValueValidator from django.db import models from django.utils import timezone -from pydis_site.apps.api.models.bot.tag import validate_tag_embed from pydis_site.apps.api.models.bot.user import User -from pydis_site.apps.api.models.mixins import ModelReprMixin +from pydis_site.apps.api.models.utils import ModelReprMixin, validate_embed class Message(ModelReprMixin, models.Model): @@ -47,7 +46,7 @@ class Message(ModelReprMixin, models.Model): ) embeds = pgfields.ArrayField( pgfields.JSONField( - validators=(validate_tag_embed,) + validators=(validate_embed,) ), blank=True, help_text="Embeds attached to this message." -- cgit v1.2.3 From 1029c56eb7e1f986a540a58bc2e657db6925a93a Mon Sep 17 00:00:00 2001 From: ks123 Date: Wed, 1 Apr 2020 08:57:27 +0300 Subject: (Tag Cleanup): Replaced import from Tags model with utils.py validator import in validators test. --- pydis_site/apps/api/tests/test_validators.py | 56 ++++++++++++++-------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/pydis_site/apps/api/tests/test_validators.py b/pydis_site/apps/api/tests/test_validators.py index 241af08c..8bb7b917 100644 --- a/pydis_site/apps/api/tests/test_validators.py +++ b/pydis_site/apps/api/tests/test_validators.py @@ -5,7 +5,7 @@ from django.test import TestCase from ..models.bot.bot_setting import validate_bot_setting_name from ..models.bot.offensive_message import future_date_validator -from ..models.bot.tag import validate_tag_embed +from ..models.utils import validate_embed REQUIRED_KEYS = ( @@ -25,77 +25,77 @@ class BotSettingValidatorTests(TestCase): class TagEmbedValidatorTests(TestCase): def test_rejects_non_mapping(self): with self.assertRaises(ValidationError): - validate_tag_embed('non-empty non-mapping') + validate_embed('non-empty non-mapping') def test_rejects_missing_required_keys(self): with self.assertRaises(ValidationError): - validate_tag_embed({ + validate_embed({ 'unknown': "key" }) def test_rejects_one_correct_one_incorrect(self): with self.assertRaises(ValidationError): - validate_tag_embed({ + validate_embed({ 'provider': "??", 'title': "" }) def test_rejects_empty_required_key(self): with self.assertRaises(ValidationError): - validate_tag_embed({ + validate_embed({ 'title': '' }) def test_rejects_list_as_embed(self): with self.assertRaises(ValidationError): - validate_tag_embed([]) + validate_embed([]) def test_rejects_required_keys_and_unknown_keys(self): with self.assertRaises(ValidationError): - validate_tag_embed({ + validate_embed({ 'title': "the duck walked up to the lemonade stand", 'and': "he said to the man running the stand" }) def test_rejects_too_long_title(self): with self.assertRaises(ValidationError): - validate_tag_embed({ + validate_embed({ 'title': 'a' * 257 }) def test_rejects_too_many_fields(self): with self.assertRaises(ValidationError): - validate_tag_embed({ + validate_embed({ 'fields': [{} for _ in range(26)] }) def test_rejects_too_long_description(self): with self.assertRaises(ValidationError): - validate_tag_embed({ + validate_embed({ 'description': 'd' * 2049 }) def test_allows_valid_embed(self): - validate_tag_embed({ + validate_embed({ 'title': "My embed", 'description': "look at my embed, my embed is amazing" }) def test_allows_unvalidated_fields(self): - validate_tag_embed({ + validate_embed({ 'title': "My embed", 'provider': "what am I??" }) def test_rejects_fields_as_list_of_non_mappings(self): with self.assertRaises(ValidationError): - validate_tag_embed({ + validate_embed({ 'fields': ['abc'] }) def test_rejects_fields_with_unknown_fields(self): with self.assertRaises(ValidationError): - validate_tag_embed({ + validate_embed({ 'fields': [ { 'what': "is this field" @@ -105,7 +105,7 @@ class TagEmbedValidatorTests(TestCase): def test_rejects_fields_with_too_long_name(self): with self.assertRaises(ValidationError): - validate_tag_embed({ + validate_embed({ 'fields': [ { 'name': "a" * 257 @@ -115,7 +115,7 @@ class TagEmbedValidatorTests(TestCase): def test_rejects_one_correct_one_incorrect_field(self): with self.assertRaises(ValidationError): - validate_tag_embed({ + validate_embed({ 'fields': [ { 'name': "Totally valid", @@ -131,7 +131,7 @@ class TagEmbedValidatorTests(TestCase): def test_rejects_missing_required_field_field(self): with self.assertRaises(ValidationError): - validate_tag_embed({ + validate_embed({ 'fields': [ { 'name': "Totally valid", @@ -142,7 +142,7 @@ class TagEmbedValidatorTests(TestCase): def test_rejects_invalid_inline_field_field(self): with self.assertRaises(ValidationError): - validate_tag_embed({ + validate_embed({ 'fields': [ { 'name': "Totally valid", @@ -153,7 +153,7 @@ class TagEmbedValidatorTests(TestCase): }) def test_allows_valid_fields(self): - validate_tag_embed({ + validate_embed({ 'fields': [ { 'name': "valid", @@ -174,14 +174,14 @@ class TagEmbedValidatorTests(TestCase): def test_rejects_footer_as_non_mapping(self): with self.assertRaises(ValidationError): - validate_tag_embed({ + validate_embed({ 'title': "whatever", 'footer': [] }) def test_rejects_footer_with_unknown_fields(self): with self.assertRaises(ValidationError): - validate_tag_embed({ + validate_embed({ 'title': "whatever", 'footer': { 'duck': "quack" @@ -190,7 +190,7 @@ class TagEmbedValidatorTests(TestCase): def test_rejects_footer_with_empty_text(self): with self.assertRaises(ValidationError): - validate_tag_embed({ + validate_embed({ 'title': "whatever", 'footer': { 'text': "" @@ -198,7 +198,7 @@ class TagEmbedValidatorTests(TestCase): }) def test_allows_footer_with_proper_values(self): - validate_tag_embed({ + validate_embed({ 'title': "whatever", 'footer': { 'text': "django good" @@ -207,14 +207,14 @@ class TagEmbedValidatorTests(TestCase): def test_rejects_author_as_non_mapping(self): with self.assertRaises(ValidationError): - validate_tag_embed({ + validate_embed({ 'title': "whatever", 'author': [] }) def test_rejects_author_with_unknown_field(self): with self.assertRaises(ValidationError): - validate_tag_embed({ + validate_embed({ 'title': "whatever", 'author': { 'field': "that is unknown" @@ -223,7 +223,7 @@ class TagEmbedValidatorTests(TestCase): def test_rejects_author_with_empty_name(self): with self.assertRaises(ValidationError): - validate_tag_embed({ + validate_embed({ 'title': "whatever", 'author': { 'name': "" @@ -232,7 +232,7 @@ class TagEmbedValidatorTests(TestCase): def test_rejects_author_with_one_correct_one_incorrect(self): with self.assertRaises(ValidationError): - validate_tag_embed({ + validate_embed({ 'title': "whatever", 'author': { # Relies on "dictionary insertion order remembering" (D.I.O.R.) behaviour @@ -242,7 +242,7 @@ class TagEmbedValidatorTests(TestCase): }) def test_allows_author_with_proper_values(self): - validate_tag_embed({ + validate_embed({ 'title': "whatever", 'author': { 'name': "Bob" -- cgit v1.2.3 From 5df2d613bc72aad60fed73d2703dff55093def03 Mon Sep 17 00:00:00 2001 From: ks123 Date: Wed, 1 Apr 2020 09:02:48 +0300 Subject: (Tag Cleanup): Removed tag import in models __init__.py --- pydis_site/apps/api/models/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pydis_site/apps/api/models/__init__.py b/pydis_site/apps/api/models/__init__.py index 1d0ab7ea..e3f928e1 100644 --- a/pydis_site/apps/api/models/__init__.py +++ b/pydis_site/apps/api/models/__init__.py @@ -12,7 +12,6 @@ from .bot import ( OffTopicChannelName, Reminder, Role, - Tag, User ) from .log_entry import LogEntry -- cgit v1.2.3 From 494b735f762bf1780448db4591a4be4850dcdd4a Mon Sep 17 00:00:00 2001 From: ks123 Date: Wed, 1 Apr 2020 09:17:43 +0300 Subject: (Tag Cleanup): Removed Tag model --- .../apps/api/migrations/0019_deletedmessage.py | 2 +- pydis_site/apps/api/models/bot/tag.py | 25 ---------------------- 2 files changed, 1 insertion(+), 26 deletions(-) diff --git a/pydis_site/apps/api/migrations/0019_deletedmessage.py b/pydis_site/apps/api/migrations/0019_deletedmessage.py index 33746253..6b848d64 100644 --- a/pydis_site/apps/api/migrations/0019_deletedmessage.py +++ b/pydis_site/apps/api/migrations/0019_deletedmessage.py @@ -18,7 +18,7 @@ class Migration(migrations.Migration): ('id', models.BigIntegerField(help_text='The message ID as taken from Discord.', primary_key=True, serialize=False, validators=[django.core.validators.MinValueValidator(limit_value=0, message='Message IDs cannot be negative.')])), ('channel_id', models.BigIntegerField(help_text='The channel ID that this message was sent in, taken from Discord.', validators=[django.core.validators.MinValueValidator(limit_value=0, message='Channel IDs cannot be negative.')])), ('content', models.CharField(help_text='The content of this message, taken from Discord.', max_length=2000)), - ('embeds', django.contrib.postgres.fields.ArrayField(base_field=django.contrib.postgres.fields.jsonb.JSONField(validators=[pydis_site.apps.api.models.bot.tag.validate_tag_embed]), help_text='Embeds attached to this message.', size=None)), + ('embeds', django.contrib.postgres.fields.ArrayField(base_field=django.contrib.postgres.fields.jsonb.JSONField(validators=[pydis_site.apps.api.models.utils.validate_embed]), help_text='Embeds attached to this message.', size=None)), ('author', models.ForeignKey(help_text='The author of this message.', on_delete=django.db.models.deletion.CASCADE, to='api.User')), ('deletion_context', models.ForeignKey(help_text='The deletion context this message is part of.', on_delete=django.db.models.deletion.CASCADE, to='api.MessageDeletionContext')), ], diff --git a/pydis_site/apps/api/models/bot/tag.py b/pydis_site/apps/api/models/bot/tag.py index 5e53582f..790ad37a 100644 --- a/pydis_site/apps/api/models/bot/tag.py +++ b/pydis_site/apps/api/models/bot/tag.py @@ -1,12 +1,8 @@ from collections.abc import Mapping from typing import Any, Dict -from django.contrib.postgres import fields as pgfields from django.core.exceptions import ValidationError from django.core.validators import MaxLengthValidator, MinLengthValidator -from django.db import models - -from pydis_site.apps.api.models.mixins import ModelReprMixin def is_bool_validator(value: Any) -> None: @@ -175,24 +171,3 @@ def validate_tag_embed(embed: Any) -> None: if field_name in field_validators: for validator in field_validators[field_name]: validator(value) - - -class Tag(ModelReprMixin, models.Model): - """A tag providing (hopefully) useful information.""" - - title = models.CharField( - max_length=100, - help_text=( - "The title of this tag, shown in searches and providing " - "a quick overview over what this embed contains." - ), - primary_key=True - ) - embed = pgfields.JSONField( - help_text="The actual embed shown by this tag.", - validators=(validate_tag_embed,) - ) - - def __str__(self): - """Returns the title of this tag, for display purposes.""" - return self.title -- cgit v1.2.3 From ad5183a3956321c8956defa3f32e399400662bf6 Mon Sep 17 00:00:00 2001 From: ks123 Date: Wed, 1 Apr 2020 09:20:48 +0300 Subject: (Tag Cleanup): Removed unnecessary tag validation migration. --- pydis_site/apps/api/migrations/0008_tag_embed_validator.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/pydis_site/apps/api/migrations/0008_tag_embed_validator.py b/pydis_site/apps/api/migrations/0008_tag_embed_validator.py index d53ddb90..d92042d2 100644 --- a/pydis_site/apps/api/migrations/0008_tag_embed_validator.py +++ b/pydis_site/apps/api/migrations/0008_tag_embed_validator.py @@ -1,7 +1,5 @@ # Generated by Django 2.1.1 on 2018-09-23 10:07 -import pydis_site.apps.api.models.bot.tag -import django.contrib.postgres.fields.jsonb from django.db import migrations @@ -12,9 +10,4 @@ class Migration(migrations.Migration): ] operations = [ - migrations.AlterField( - model_name='tag', - name='embed', - field=django.contrib.postgres.fields.jsonb.JSONField(help_text='The actual embed shown by this tag.', validators=[pydis_site.apps.api.models.bot.tag.validate_tag_embed]), - ), ] -- cgit v1.2.3 From a11d3a38a03b63a2018785917a56fe54c1b1f027 Mon Sep 17 00:00:00 2001 From: ks123 Date: Wed, 1 Apr 2020 09:21:17 +0300 Subject: (Tag Cleanup): Removed Tag model file. --- pydis_site/apps/api/models/bot/tag.py | 173 ---------------------------------- 1 file changed, 173 deletions(-) delete mode 100644 pydis_site/apps/api/models/bot/tag.py diff --git a/pydis_site/apps/api/models/bot/tag.py b/pydis_site/apps/api/models/bot/tag.py deleted file mode 100644 index 790ad37a..00000000 --- a/pydis_site/apps/api/models/bot/tag.py +++ /dev/null @@ -1,173 +0,0 @@ -from collections.abc import Mapping -from typing import Any, Dict - -from django.core.exceptions import ValidationError -from django.core.validators import MaxLengthValidator, MinLengthValidator - - -def is_bool_validator(value: Any) -> None: - """Validates if a given value is of type bool.""" - if not isinstance(value, bool): - raise ValidationError(f"This field must be of type bool, not {type(value)}.") - - -def validate_tag_embed_fields(fields: dict) -> None: - """Raises a ValidationError if any of the given embed fields is invalid.""" - field_validators = { - 'name': (MaxLengthValidator(limit_value=256),), - 'value': (MaxLengthValidator(limit_value=1024),), - 'inline': (is_bool_validator,), - } - - required_fields = ('name', 'value') - - for field in fields: - if not isinstance(field, Mapping): - raise ValidationError("Embed fields must be a mapping.") - - if not all(required_field in field for required_field in required_fields): - raise ValidationError( - f"Embed fields must contain the following fields: {', '.join(required_fields)}." - ) - - for field_name, value in field.items(): - if field_name not in field_validators: - raise ValidationError(f"Unknown embed field field: {field_name!r}.") - - for validator in field_validators[field_name]: - validator(value) - - -def validate_tag_embed_footer(footer: Dict[str, str]) -> None: - """Raises a ValidationError if the given footer is invalid.""" - field_validators = { - 'text': ( - MinLengthValidator( - limit_value=1, - message="Footer text must not be empty." - ), - MaxLengthValidator(limit_value=2048) - ), - 'icon_url': (), - 'proxy_icon_url': () - } - - if not isinstance(footer, Mapping): - raise ValidationError("Embed footer must be a mapping.") - - for field_name, value in footer.items(): - if field_name not in field_validators: - raise ValidationError(f"Unknown embed footer field: {field_name!r}.") - - for validator in field_validators[field_name]: - validator(value) - - -def validate_tag_embed_author(author: Any) -> None: - """Raises a ValidationError if the given author is invalid.""" - field_validators = { - 'name': ( - MinLengthValidator( - limit_value=1, - message="Embed author name must not be empty." - ), - MaxLengthValidator(limit_value=256) - ), - 'url': (), - 'icon_url': (), - 'proxy_icon_url': () - } - - if not isinstance(author, Mapping): - raise ValidationError("Embed author must be a mapping.") - - for field_name, value in author.items(): - if field_name not in field_validators: - raise ValidationError(f"Unknown embed author field: {field_name!r}.") - - for validator in field_validators[field_name]: - validator(value) - - -def validate_tag_embed(embed: Any) -> None: - """ - Validate a JSON document containing an embed as possible to send on Discord. - - This attempts to rebuild the validation used by Discord - as well as possible by checking for various embed limits so we can - ensure that any embed we store here will also be accepted as a - valid embed by the Discord API. - - Using this directly is possible, although not intended - you usually - stick this onto the `validators` keyword argument of model fields. - - Example: - - >>> from django.contrib.postgres import fields as pgfields - >>> from django.db import models - >>> from pydis_site.apps.api.models.bot.tag import validate_tag_embed - >>> class MyMessage(models.Model): - ... embed = pgfields.JSONField( - ... validators=( - ... validate_tag_embed, - ... ) - ... ) - ... # ... - ... - - Args: - embed (Any): - A dictionary describing the contents of this embed. - See the official documentation for a full reference - of accepted keys by this dictionary: - https://discordapp.com/developers/docs/resources/channel#embed-object - - Raises: - ValidationError: - In case the given embed is deemed invalid, a `ValidationError` - is raised which in turn will allow Django to display errors - as appropriate. - """ - all_keys = { - 'title', 'type', 'description', 'url', 'timestamp', - 'color', 'footer', 'image', 'thumbnail', 'video', - 'provider', 'author', 'fields' - } - one_required_of = {'description', 'fields', 'image', 'title', 'video'} - field_validators = { - 'title': ( - MinLengthValidator( - limit_value=1, - message="Embed title must not be empty." - ), - MaxLengthValidator(limit_value=256) - ), - 'description': (MaxLengthValidator(limit_value=2048),), - 'fields': ( - MaxLengthValidator(limit_value=25), - validate_tag_embed_fields - ), - 'footer': (validate_tag_embed_footer,), - 'author': (validate_tag_embed_author,) - } - - if not embed: - raise ValidationError("Tag embed must not be empty.") - - elif not isinstance(embed, Mapping): - raise ValidationError("Tag embed must be a mapping.") - - elif not any(field in embed for field in one_required_of): - raise ValidationError(f"Tag embed must contain one of the fields {one_required_of}.") - - for required_key in one_required_of: - if required_key in embed and not embed[required_key]: - raise ValidationError(f"Key {required_key!r} must not be empty.") - - for field_name, value in embed.items(): - if field_name not in all_keys: - raise ValidationError(f"Unknown field name: {field_name!r}") - - if field_name in field_validators: - for validator in field_validators[field_name]: - validator(value) -- cgit v1.2.3 From 5357d5baf3234550c09f88fa38a9f436503f570c Mon Sep 17 00:00:00 2001 From: ks123 Date: Wed, 1 Apr 2020 09:21:48 +0300 Subject: (Tag Cleanup): Added Tag removal migration --- pydis_site/apps/api/migrations/0051_delete_tag.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 pydis_site/apps/api/migrations/0051_delete_tag.py diff --git a/pydis_site/apps/api/migrations/0051_delete_tag.py b/pydis_site/apps/api/migrations/0051_delete_tag.py new file mode 100644 index 00000000..bada5788 --- /dev/null +++ b/pydis_site/apps/api/migrations/0051_delete_tag.py @@ -0,0 +1,16 @@ +# Generated by Django 2.2.11 on 2020-04-01 06:15 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0050_remove_infractions_active_default_value'), + ] + + operations = [ + migrations.DeleteModel( + name='Tag', + ), + ] -- cgit v1.2.3 From 99485facba10d1f0d918e8c0ab152372db89c7b9 Mon Sep 17 00:00:00 2001 From: ks123 Date: Wed, 1 Apr 2020 09:25:15 +0300 Subject: (Tag Cleanup): Removed Tag viewset from viewsets __init__.py --- pydis_site/apps/api/viewsets/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pydis_site/apps/api/viewsets/__init__.py b/pydis_site/apps/api/viewsets/__init__.py index 8699517e..dfbb880d 100644 --- a/pydis_site/apps/api/viewsets/__init__.py +++ b/pydis_site/apps/api/viewsets/__init__.py @@ -10,7 +10,6 @@ from .bot import ( OffTopicChannelNameViewSet, ReminderViewSet, RoleViewSet, - TagViewSet, UserViewSet ) from .log_entry import LogEntryViewSet -- cgit v1.2.3 From 2b2f491e476c4564899c8716fde189da4a493287 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 27 Aug 2020 07:34:41 +0300 Subject: Move import to beginning of models mixins file --- pydis_site/apps/api/models/mixins.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pydis_site/apps/api/models/mixins.py b/pydis_site/apps/api/models/mixins.py index e95888b7..692d14f7 100644 --- a/pydis_site/apps/api/models/mixins.py +++ b/pydis_site/apps/api/models/mixins.py @@ -4,6 +4,7 @@ from typing import Any, Dict from django.core.exceptions import ValidationError from django.core.validators import MaxLengthValidator, MinLengthValidator +from django.db import models def is_bool_validator(value: Any) -> None: @@ -173,8 +174,6 @@ def validate_embed(embed: Any) -> None: for validator in field_validators[field_name]: validator(value) -from django.db import models - class ModelReprMixin: """Mixin providing a `__repr__()` to display model class name and initialisation parameters.""" -- cgit v1.2.3 From 2011d2f2c30dc67c1f34e82e5593f8e4ce1243dd Mon Sep 17 00:00:00 2001 From: Karlis S Date: Thu, 27 Aug 2020 04:44:08 +0000 Subject: Move some parts from Mixins file to utils --- pydis_site/apps/api/models/mixins.py | 172 ---------------------------------- pydis_site/apps/api/models/utils.py | 174 +++++++++++++++++++++++++++++++++++ 2 files changed, 174 insertions(+), 172 deletions(-) create mode 100644 pydis_site/apps/api/models/utils.py diff --git a/pydis_site/apps/api/models/mixins.py b/pydis_site/apps/api/models/mixins.py index 692d14f7..5d75b78b 100644 --- a/pydis_site/apps/api/models/mixins.py +++ b/pydis_site/apps/api/models/mixins.py @@ -1,180 +1,8 @@ -from collections.abc import Mapping from operator import itemgetter -from typing import Any, Dict -from django.core.exceptions import ValidationError -from django.core.validators import MaxLengthValidator, MinLengthValidator from django.db import models -def is_bool_validator(value: Any) -> None: - """Validates if a given value is of type bool.""" - if not isinstance(value, bool): - raise ValidationError(f"This field must be of type bool, not {type(value)}.") - - -def validate_embed_fields(fields: dict) -> None: - """Raises a ValidationError if any of the given embed fields is invalid.""" - field_validators = { - 'name': (MaxLengthValidator(limit_value=256),), - 'value': (MaxLengthValidator(limit_value=1024),), - 'inline': (is_bool_validator,), - } - - required_fields = ('name', 'value') - - for field in fields: - if not isinstance(field, Mapping): - raise ValidationError("Embed fields must be a mapping.") - - if not all(required_field in field for required_field in required_fields): - raise ValidationError( - f"Embed fields must contain the following fields: {', '.join(required_fields)}." - ) - - for field_name, value in field.items(): - if field_name not in field_validators: - raise ValidationError(f"Unknown embed field field: {field_name!r}.") - - for validator in field_validators[field_name]: - validator(value) - - -def validate_embed_footer(footer: Dict[str, str]) -> None: - """Raises a ValidationError if the given footer is invalid.""" - field_validators = { - 'text': ( - MinLengthValidator( - limit_value=1, - message="Footer text must not be empty." - ), - MaxLengthValidator(limit_value=2048) - ), - 'icon_url': (), - 'proxy_icon_url': () - } - - if not isinstance(footer, Mapping): - raise ValidationError("Embed footer must be a mapping.") - - for field_name, value in footer.items(): - if field_name not in field_validators: - raise ValidationError(f"Unknown embed footer field: {field_name!r}.") - - for validator in field_validators[field_name]: - validator(value) - - -def validate_embed_author(author: Any) -> None: - """Raises a ValidationError if the given author is invalid.""" - field_validators = { - 'name': ( - MinLengthValidator( - limit_value=1, - message="Embed author name must not be empty." - ), - MaxLengthValidator(limit_value=256) - ), - 'url': (), - 'icon_url': (), - 'proxy_icon_url': () - } - - if not isinstance(author, Mapping): - raise ValidationError("Embed author must be a mapping.") - - for field_name, value in author.items(): - if field_name not in field_validators: - raise ValidationError(f"Unknown embed author field: {field_name!r}.") - - for validator in field_validators[field_name]: - validator(value) - - -def validate_embed(embed: Any) -> None: - """ - Validate a JSON document containing an embed as possible to send on Discord. - - This attempts to rebuild the validation used by Discord - as well as possible by checking for various embed limits so we can - ensure that any embed we store here will also be accepted as a - valid embed by the Discord API. - - Using this directly is possible, although not intended - you usually - stick this onto the `validators` keyword argument of model fields. - - Example: - - >>> from django.contrib.postgres import fields as pgfields - >>> from django.db import models - >>> from pydis_site.apps.api.models.utils import validate_embed - >>> class MyMessage(models.Model): - ... embed = pgfields.JSONField( - ... validators=( - ... validate_embed, - ... ) - ... ) - ... # ... - ... - - Args: - embed (Any): - A dictionary describing the contents of this embed. - See the official documentation for a full reference - of accepted keys by this dictionary: - https://discordapp.com/developers/docs/resources/channel#embed-object - - Raises: - ValidationError: - In case the given embed is deemed invalid, a `ValidationError` - is raised which in turn will allow Django to display errors - as appropriate. - """ - all_keys = { - 'title', 'type', 'description', 'url', 'timestamp', - 'color', 'footer', 'image', 'thumbnail', 'video', - 'provider', 'author', 'fields' - } - one_required_of = {'description', 'fields', 'image', 'title', 'video'} - field_validators = { - 'title': ( - MinLengthValidator( - limit_value=1, - message="Embed title must not be empty." - ), - MaxLengthValidator(limit_value=256) - ), - 'description': (MaxLengthValidator(limit_value=2048),), - 'fields': ( - MaxLengthValidator(limit_value=25), - validate_embed_fields - ), - 'footer': (validate_embed_footer,), - 'author': (validate_embed_author,) - } - - if not embed: - raise ValidationError("Tag embed must not be empty.") - - elif not isinstance(embed, Mapping): - raise ValidationError("Tag embed must be a mapping.") - - elif not any(field in embed for field in one_required_of): - raise ValidationError(f"Tag embed must contain one of the fields {one_required_of}.") - - for required_key in one_required_of: - if required_key in embed and not embed[required_key]: - raise ValidationError(f"Key {required_key!r} must not be empty.") - - for field_name, value in embed.items(): - if field_name not in all_keys: - raise ValidationError(f"Unknown field name: {field_name!r}") - - if field_name in field_validators: - for validator in field_validators[field_name]: - validator(value) - - class ModelReprMixin: """Mixin providing a `__repr__()` to display model class name and initialisation parameters.""" diff --git a/pydis_site/apps/api/models/utils.py b/pydis_site/apps/api/models/utils.py new file mode 100644 index 00000000..97a79507 --- /dev/null +++ b/pydis_site/apps/api/models/utils.py @@ -0,0 +1,174 @@ +from collections.abc import Mapping +from operator import itemgetter +from typing import Any, Dict + +from django.core.exceptions import ValidationError +from django.core.validators import MaxLengthValidator, MinLengthValidator + + +def is_bool_validator(value: Any) -> None: + """Validates if a given value is of type bool.""" + if not isinstance(value, bool): + raise ValidationError(f"This field must be of type bool, not {type(value)}.") + + +def validate_embed_fields(fields: dict) -> None: + """Raises a ValidationError if any of the given embed fields is invalid.""" + field_validators = { + 'name': (MaxLengthValidator(limit_value=256),), + 'value': (MaxLengthValidator(limit_value=1024),), + 'inline': (is_bool_validator,), + } + + required_fields = ('name', 'value') + + for field in fields: + if not isinstance(field, Mapping): + raise ValidationError("Embed fields must be a mapping.") + + if not all(required_field in field for required_field in required_fields): + raise ValidationError( + f"Embed fields must contain the following fields: {', '.join(required_fields)}." + ) + + for field_name, value in field.items(): + if field_name not in field_validators: + raise ValidationError(f"Unknown embed field field: {field_name!r}.") + + for validator in field_validators[field_name]: + validator(value) + + +def validate_embed_footer(footer: Dict[str, str]) -> None: + """Raises a ValidationError if the given footer is invalid.""" + field_validators = { + 'text': ( + MinLengthValidator( + limit_value=1, + message="Footer text must not be empty." + ), + MaxLengthValidator(limit_value=2048) + ), + 'icon_url': (), + 'proxy_icon_url': () + } + + if not isinstance(footer, Mapping): + raise ValidationError("Embed footer must be a mapping.") + + for field_name, value in footer.items(): + if field_name not in field_validators: + raise ValidationError(f"Unknown embed footer field: {field_name!r}.") + + for validator in field_validators[field_name]: + validator(value) + + +def validate_embed_author(author: Any) -> None: + """Raises a ValidationError if the given author is invalid.""" + field_validators = { + 'name': ( + MinLengthValidator( + limit_value=1, + message="Embed author name must not be empty." + ), + MaxLengthValidator(limit_value=256) + ), + 'url': (), + 'icon_url': (), + 'proxy_icon_url': () + } + + if not isinstance(author, Mapping): + raise ValidationError("Embed author must be a mapping.") + + for field_name, value in author.items(): + if field_name not in field_validators: + raise ValidationError(f"Unknown embed author field: {field_name!r}.") + + for validator in field_validators[field_name]: + validator(value) + + +def validate_embed(embed: Any) -> None: + """ + Validate a JSON document containing an embed as possible to send on Discord. + + This attempts to rebuild the validation used by Discord + as well as possible by checking for various embed limits so we can + ensure that any embed we store here will also be accepted as a + valid embed by the Discord API. + + Using this directly is possible, although not intended - you usually + stick this onto the `validators` keyword argument of model fields. + + Example: + + >>> from django.contrib.postgres import fields as pgfields + >>> from django.db import models + >>> from pydis_site.apps.api.models.utils import validate_embed + >>> class MyMessage(models.Model): + ... embed = pgfields.JSONField( + ... validators=( + ... validate_embed, + ... ) + ... ) + ... # ... + ... + + Args: + embed (Any): + A dictionary describing the contents of this embed. + See the official documentation for a full reference + of accepted keys by this dictionary: + https://discordapp.com/developers/docs/resources/channel#embed-object + + Raises: + ValidationError: + In case the given embed is deemed invalid, a `ValidationError` + is raised which in turn will allow Django to display errors + as appropriate. + """ + all_keys = { + 'title', 'type', 'description', 'url', 'timestamp', + 'color', 'footer', 'image', 'thumbnail', 'video', + 'provider', 'author', 'fields' + } + one_required_of = {'description', 'fields', 'image', 'title', 'video'} + field_validators = { + 'title': ( + MinLengthValidator( + limit_value=1, + message="Embed title must not be empty." + ), + MaxLengthValidator(limit_value=256) + ), + 'description': (MaxLengthValidator(limit_value=2048),), + 'fields': ( + MaxLengthValidator(limit_value=25), + validate_embed_fields + ), + 'footer': (validate_embed_footer,), + 'author': (validate_embed_author,) + } + + if not embed: + raise ValidationError("Tag embed must not be empty.") + + elif not isinstance(embed, Mapping): + raise ValidationError("Tag embed must be a mapping.") + + elif not any(field in embed for field in one_required_of): + raise ValidationError(f"Tag embed must contain one of the fields {one_required_of}.") + + for required_key in one_required_of: + if required_key in embed and not embed[required_key]: + raise ValidationError(f"Key {required_key!r} must not be empty.") + + for field_name, value in embed.items(): + if field_name not in all_keys: + raise ValidationError(f"Unknown field name: {field_name!r}") + + if field_name in field_validators: + for validator in field_validators[field_name]: + validator(value) -- cgit v1.2.3 From 1ab4ccbbbcf38dddace5855ed9521219821b7698 Mon Sep 17 00:00:00 2001 From: Karlis S Date: Thu, 27 Aug 2020 04:48:09 +0000 Subject: Remove unused import from models utils --- pydis_site/apps/api/models/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pydis_site/apps/api/models/utils.py b/pydis_site/apps/api/models/utils.py index 97a79507..107231ba 100644 --- a/pydis_site/apps/api/models/utils.py +++ b/pydis_site/apps/api/models/utils.py @@ -1,5 +1,4 @@ from collections.abc import Mapping -from operator import itemgetter from typing import Any, Dict from django.core.exceptions import ValidationError -- cgit v1.2.3 From fd2909c936efb9ab2285896b297b405c4dd7a1fb Mon Sep 17 00:00:00 2001 From: Karlis S Date: Thu, 27 Aug 2020 05:35:01 +0000 Subject: Move last parts from mixins to utils and delete mixins --- pydis_site/apps/api/models/mixins.py | 31 ------------------------------- pydis_site/apps/api/models/utils.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 31 deletions(-) delete mode 100644 pydis_site/apps/api/models/mixins.py diff --git a/pydis_site/apps/api/models/mixins.py b/pydis_site/apps/api/models/mixins.py deleted file mode 100644 index 5d75b78b..00000000 --- a/pydis_site/apps/api/models/mixins.py +++ /dev/null @@ -1,31 +0,0 @@ -from operator import itemgetter - -from django.db import models - - -class ModelReprMixin: - """Mixin providing a `__repr__()` to display model class name and initialisation parameters.""" - - def __repr__(self): - """Returns the current model class name and initialisation parameters.""" - attributes = ' '.join( - f'{attribute}={value!r}' - for attribute, value in sorted( - self.__dict__.items(), - key=itemgetter(0) - ) - if not attribute.startswith('_') - ) - return f'<{self.__class__.__name__}({attributes})>' - - -class ModelTimestampMixin(models.Model): - """Mixin providing created_at and updated_at fields.""" - - created_at = models.DateTimeField(auto_now_add=True) - updated_at = models.DateTimeField(auto_now=True) - - class Meta: - """Metaconfig for the mixin.""" - - abstract = True diff --git a/pydis_site/apps/api/models/utils.py b/pydis_site/apps/api/models/utils.py index 107231ba..692d14f7 100644 --- a/pydis_site/apps/api/models/utils.py +++ b/pydis_site/apps/api/models/utils.py @@ -1,8 +1,10 @@ from collections.abc import Mapping +from operator import itemgetter from typing import Any, Dict from django.core.exceptions import ValidationError from django.core.validators import MaxLengthValidator, MinLengthValidator +from django.db import models def is_bool_validator(value: Any) -> None: @@ -171,3 +173,31 @@ def validate_embed(embed: Any) -> None: if field_name in field_validators: for validator in field_validators[field_name]: validator(value) + + +class ModelReprMixin: + """Mixin providing a `__repr__()` to display model class name and initialisation parameters.""" + + def __repr__(self): + """Returns the current model class name and initialisation parameters.""" + attributes = ' '.join( + f'{attribute}={value!r}' + for attribute, value in sorted( + self.__dict__.items(), + key=itemgetter(0) + ) + if not attribute.startswith('_') + ) + return f'<{self.__class__.__name__}({attributes})>' + + +class ModelTimestampMixin(models.Model): + """Mixin providing created_at and updated_at fields.""" + + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + class Meta: + """Metaconfig for the mixin.""" + + abstract = True -- cgit v1.2.3 From 4eeb841724e7603dffc5334a6e4aa7b7b7ced997 Mon Sep 17 00:00:00 2001 From: Karlis S Date: Thu, 27 Aug 2020 05:37:55 +0000 Subject: Fix FilterList model mixins import path --- pydis_site/apps/api/models/bot/filter_list.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pydis_site/apps/api/models/bot/filter_list.py b/pydis_site/apps/api/models/bot/filter_list.py index d279e137..cb4acb68 100644 --- a/pydis_site/apps/api/models/bot/filter_list.py +++ b/pydis_site/apps/api/models/bot/filter_list.py @@ -1,6 +1,6 @@ from django.db import models -from pydis_site.apps.api.models.mixins import ModelReprMixin, ModelTimestampMixin +from pydis_site.apps.api.models.utils import ModelReprMixin, ModelTimestampMixin class FilterList(ModelTimestampMixin, ModelReprMixin, models.Model): -- cgit v1.2.3 From feafa0950d9132350e36f58fbae57121363d3277 Mon Sep 17 00:00:00 2001 From: Karlis S Date: Thu, 27 Aug 2020 05:47:47 +0000 Subject: Still move mixins back to its own file --- pydis_site/apps/api/models/bot/filter_list.py | 2 +- pydis_site/apps/api/models/mixins.py | 31 +++++++++++++++++++++++++++ pydis_site/apps/api/models/utils.py | 30 -------------------------- 3 files changed, 32 insertions(+), 31 deletions(-) create mode 100644 pydis_site/apps/api/models/mixins.py diff --git a/pydis_site/apps/api/models/bot/filter_list.py b/pydis_site/apps/api/models/bot/filter_list.py index cb4acb68..d279e137 100644 --- a/pydis_site/apps/api/models/bot/filter_list.py +++ b/pydis_site/apps/api/models/bot/filter_list.py @@ -1,6 +1,6 @@ from django.db import models -from pydis_site.apps.api.models.utils import ModelReprMixin, ModelTimestampMixin +from pydis_site.apps.api.models.mixins import ModelReprMixin, ModelTimestampMixin class FilterList(ModelTimestampMixin, ModelReprMixin, models.Model): diff --git a/pydis_site/apps/api/models/mixins.py b/pydis_site/apps/api/models/mixins.py new file mode 100644 index 00000000..5d75b78b --- /dev/null +++ b/pydis_site/apps/api/models/mixins.py @@ -0,0 +1,31 @@ +from operator import itemgetter + +from django.db import models + + +class ModelReprMixin: + """Mixin providing a `__repr__()` to display model class name and initialisation parameters.""" + + def __repr__(self): + """Returns the current model class name and initialisation parameters.""" + attributes = ' '.join( + f'{attribute}={value!r}' + for attribute, value in sorted( + self.__dict__.items(), + key=itemgetter(0) + ) + if not attribute.startswith('_') + ) + return f'<{self.__class__.__name__}({attributes})>' + + +class ModelTimestampMixin(models.Model): + """Mixin providing created_at and updated_at fields.""" + + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + class Meta: + """Metaconfig for the mixin.""" + + abstract = True diff --git a/pydis_site/apps/api/models/utils.py b/pydis_site/apps/api/models/utils.py index 692d14f7..107231ba 100644 --- a/pydis_site/apps/api/models/utils.py +++ b/pydis_site/apps/api/models/utils.py @@ -1,10 +1,8 @@ from collections.abc import Mapping -from operator import itemgetter from typing import Any, Dict from django.core.exceptions import ValidationError from django.core.validators import MaxLengthValidator, MinLengthValidator -from django.db import models def is_bool_validator(value: Any) -> None: @@ -173,31 +171,3 @@ def validate_embed(embed: Any) -> None: if field_name in field_validators: for validator in field_validators[field_name]: validator(value) - - -class ModelReprMixin: - """Mixin providing a `__repr__()` to display model class name and initialisation parameters.""" - - def __repr__(self): - """Returns the current model class name and initialisation parameters.""" - attributes = ' '.join( - f'{attribute}={value!r}' - for attribute, value in sorted( - self.__dict__.items(), - key=itemgetter(0) - ) - if not attribute.startswith('_') - ) - return f'<{self.__class__.__name__}({attributes})>' - - -class ModelTimestampMixin(models.Model): - """Mixin providing created_at and updated_at fields.""" - - created_at = models.DateTimeField(auto_now_add=True) - updated_at = models.DateTimeField(auto_now=True) - - class Meta: - """Metaconfig for the mixin.""" - - abstract = True -- cgit v1.2.3 From e1f75feb74c24b262b276534070e4ffb3bf295b3 Mon Sep 17 00:00:00 2001 From: Karlis S Date: Thu, 27 Aug 2020 05:53:09 +0000 Subject: Fix import paths of mixins in message model --- pydis_site/apps/api/models/bot/message.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pydis_site/apps/api/models/bot/message.py b/pydis_site/apps/api/models/bot/message.py index e929ea25..f6ae55a5 100644 --- a/pydis_site/apps/api/models/bot/message.py +++ b/pydis_site/apps/api/models/bot/message.py @@ -6,7 +6,8 @@ from django.db import models from django.utils import timezone from pydis_site.apps.api.models.bot.user import User -from pydis_site.apps.api.models.utils import ModelReprMixin, validate_embed +from pydis_site.apps.api.models.mixins import ModelReprMixin +from pydis_site.apps.api.models.utils import validate_embed class Message(ModelReprMixin, models.Model): -- cgit v1.2.3 From ec689ad42d28de55d47f9d3730389ae7e179d565 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 27 Aug 2020 08:57:28 +0300 Subject: Fix embed validator location in migration --- pydis_site/apps/api/migrations/0051_allow_blank_message_embeds.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pydis_site/apps/api/migrations/0051_allow_blank_message_embeds.py b/pydis_site/apps/api/migrations/0051_allow_blank_message_embeds.py index e617e1c9..b03c8a18 100644 --- a/pydis_site/apps/api/migrations/0051_allow_blank_message_embeds.py +++ b/pydis_site/apps/api/migrations/0051_allow_blank_message_embeds.py @@ -16,6 +16,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='deletedmessage', name='embeds', - field=django.contrib.postgres.fields.ArrayField(base_field=django.contrib.postgres.fields.jsonb.JSONField(validators=[pydis_site.apps.api.models.bot.tag.validate_tag_embed]), blank=True, help_text='Embeds attached to this message.', size=None), + field=django.contrib.postgres.fields.ArrayField(base_field=django.contrib.postgres.fields.jsonb.JSONField(validators=[pydis_site.apps.api.models.utils.validate_embed]), blank=True, help_text='Embeds attached to this message.', size=None), ), ] -- cgit v1.2.3 From b43477faa59970adffc93c58743a4abce53b1547 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 27 Aug 2020 09:01:28 +0300 Subject: Replace bad import on migration --- pydis_site/apps/api/migrations/0051_allow_blank_message_embeds.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pydis_site/apps/api/migrations/0051_allow_blank_message_embeds.py b/pydis_site/apps/api/migrations/0051_allow_blank_message_embeds.py index b03c8a18..124c6a57 100644 --- a/pydis_site/apps/api/migrations/0051_allow_blank_message_embeds.py +++ b/pydis_site/apps/api/migrations/0051_allow_blank_message_embeds.py @@ -3,7 +3,7 @@ import django.contrib.postgres.fields import django.contrib.postgres.fields.jsonb from django.db import migrations -import pydis_site.apps.api.models.bot.tag +import pydis_site.apps.api.models.utils class Migration(migrations.Migration): -- cgit v1.2.3 From 32b76b192d72f4235a64b8024131988b4e4c0c36 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 29 Aug 2020 08:55:57 +0300 Subject: Simplify non-random off-topic names selection --- .../api/viewsets/bot/off_topic_channel_name.py | 39 ++++++++++------------ 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py b/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py index 9af69ae4..29978015 100644 --- a/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py +++ b/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py @@ -1,3 +1,4 @@ +from django.db.models import Case, When, Value from django.db.models.query import QuerySet from django.http.request import HttpRequest from django.shortcuts import get_object_or_404 @@ -108,28 +109,22 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet): 'random_items': ["Must be a positive integer."] }) - queryset = self.get_queryset().order_by('?').exclude(used=True)[:random_count] - self.get_queryset().filter( - name__in=(query.name for query in queryset) - ).update(used=True) - - # When the client requests more channel names than are available, - # we reset all names to used=False and start a new round of names. - if len(queryset) < random_count: - # Figure out how many additional names we need, and don't fetch duplicate names. - names_needed = random_count - len(queryset) - other_names = self.get_queryset().order_by('?').exclude( - name__in=(query.name for query in queryset) - )[:names_needed] - - # Reset the `used` field to False for all names except the ones we just used. - self.get_queryset().exclude(name__in=( - query.name for query in other_names) - ).update(used=False) - - # Join original queryset (that had missing names) - # and extension with these missing names. - queryset = list(queryset) + list(other_names) + queryset = self.get_queryset().order_by('used', '?')[:random_count] + + # When any name is used in our listing then this means we reached end of round + # and we need to reset all other names `used` to False + if any(offtopic_name.used for offtopic_name in queryset): + self.get_queryset().update( + used=Case( # These names that we just got have to be excluded from updating to False + When(name__in=(offtopic_name.name for offtopic_name in queryset), then=Value(True)), + default=Value(False) + ) + ) + else: + # Otherwise mark selected names `used` to True + self.get_queryset().filter( + name__in=(offtopic_name.name for offtopic_name in queryset) + ).update(used=True) serialized = self.serializer_class(queryset, many=True) return Response(serialized.data) -- cgit v1.2.3 From d5945b3d82124214d1bbbf8e156f3516fd3a70cc Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 29 Aug 2020 09:06:11 +0300 Subject: Fix off-topic names test docstrings mood --- .../apps/api/tests/test_off_topic_channel_names.py | 30 +++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/pydis_site/apps/api/tests/test_off_topic_channel_names.py b/pydis_site/apps/api/tests/test_off_topic_channel_names.py index cac9405a..3ab8b22d 100644 --- a/pydis_site/apps/api/tests/test_off_topic_channel_names.py +++ b/pydis_site/apps/api/tests/test_off_topic_channel_names.py @@ -10,14 +10,14 @@ class UnauthenticatedTests(APISubdomainTestCase): self.client.force_authenticate(user=None) def test_cannot_read_off_topic_channel_name_list(self): - """Test does this return 401 response code when not authenticated.""" + """Return a 401 response when not authenticated.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(url) self.assertEqual(response.status_code, 401) def test_cannot_read_off_topic_channel_name_list_with_random_item_param(self): - """Test does this give 401 code when `random_items` provided and not authenticated.""" + """Return a 401 response when `random_items` provided and not authenticated.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(f'{url}?random_items=no') @@ -26,7 +26,7 @@ class UnauthenticatedTests(APISubdomainTestCase): class EmptyDatabaseTests(APISubdomainTestCase): def test_returns_empty_object(self): - """Test does this return empty list when no names in database.""" + """Return empty list when no names in database.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(url) @@ -34,7 +34,7 @@ class EmptyDatabaseTests(APISubdomainTestCase): self.assertEqual(response.json(), []) def test_returns_empty_list_with_get_all_param(self): - """Test does this return empty list when no names and `random_items` param provided.""" + """Return empty list when no names and `random_items` param provided.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(f'{url}?random_items=5') @@ -42,7 +42,7 @@ class EmptyDatabaseTests(APISubdomainTestCase): self.assertEqual(response.json(), []) def test_returns_400_for_bad_random_items_param(self): - """Test does this return error message when passing not integer as `random_items`.""" + """Return error message when passing not integer as `random_items`.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(f'{url}?random_items=totally-a-valid-integer') @@ -52,7 +52,7 @@ class EmptyDatabaseTests(APISubdomainTestCase): }) def test_returns_400_for_negative_random_items_param(self): - """Test does this return error message when passing negative int as `random_items`.""" + """Return error message when passing negative int as `random_items`.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(f'{url}?random_items=-5') @@ -69,7 +69,7 @@ class ListTests(APISubdomainTestCase): cls.test_name_2 = OffTopicChannelName.objects.create(name='bbq-with-bisk', used=True) def test_returns_name_in_list(self): - """Test does this return all off-topic channel names.""" + """Return all off-topic channel names.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(url) @@ -83,7 +83,7 @@ class ListTests(APISubdomainTestCase): ) def test_returns_single_item_with_random_items_param_set_to_1(self): - """Test does this return not-used name instead used.""" + """Return not-used name instead used.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(f'{url}?random_items=1') @@ -92,7 +92,7 @@ class ListTests(APISubdomainTestCase): self.assertEqual(response.json(), [self.test_name.name]) def test_running_out_of_names_with_random_parameter(self): - """Test does this reset names `used` parameter to `False` when running out of names.""" + """Reset names `used` parameter to `False` when running out of names.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(f'{url}?random_items=2') @@ -110,7 +110,7 @@ class CreationTests(APISubdomainTestCase): self.assertEqual(response.status_code, 201) def test_returns_201_for_unicode_chars(self): - """Test does this accept all valid characters.""" + """Accept all valid characters.""" url = reverse('bot:offtopicchannelname-list', host='api') names = ( '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹', @@ -122,7 +122,7 @@ class CreationTests(APISubdomainTestCase): self.assertEqual(response.status_code, 201) def test_returns_400_for_missing_name_param(self): - """Test does this return error message when name not provided.""" + """Return error message when name not provided.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.post(url) self.assertEqual(response.status_code, 400) @@ -131,7 +131,7 @@ class CreationTests(APISubdomainTestCase): }) def test_returns_400_for_bad_name_param(self): - """Test does this return error message when invalid characters provided.""" + """Return error message when invalid characters provided.""" url = reverse('bot:offtopicchannelname-list', host='api') invalid_names = ( 'space between words', @@ -154,21 +154,21 @@ class DeletionTests(APISubdomainTestCase): cls.test_name_2 = OffTopicChannelName.objects.create(name='bbq-with-bisk') def test_deleting_unknown_name_returns_404(self): - """Test does this return 404 code when trying to delete unknown name.""" + """Return 404 reponse when trying to delete unknown name.""" url = reverse('bot:offtopicchannelname-detail', args=('unknown-name',), host='api') response = self.client.delete(url) self.assertEqual(response.status_code, 404) def test_deleting_known_name_returns_204(self): - """Test does this return 204 code when deleting was successful.""" + """Return 204 response when deleting was successful.""" url = reverse('bot:offtopicchannelname-detail', args=(self.test_name.name,), host='api') response = self.client.delete(url) self.assertEqual(response.status_code, 204) def test_name_gets_deleted(self): - """Test does name gets actually deleted.""" + """Name gets actually deleted.""" url = reverse('bot:offtopicchannelname-detail', args=(self.test_name_2.name,), host='api') response = self.client.delete(url) -- cgit v1.2.3 From 32342aca3309b4fd482841cb84e4f166152d5c33 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 29 Aug 2020 09:17:15 +0300 Subject: Fix mess with migrations --- .../api/migrations/0051_create_news_setting.py | 25 ---- .../api/migrations/0052_create_news_setting.py | 25 ++++ .../migrations/0052_offtopicchannelname_used.py | 18 --- .../api/migrations/0052_remove_user_avatar_hash.py | 17 --- .../migrations/0053_offtopicchannelname_used.py | 18 +++ .../api/migrations/0053_user_roles_to_array.py | 24 ---- .../api/migrations/0054_remove_user_avatar_hash.py | 17 +++ .../0054_user_invalidate_unknown_role.py | 21 --- .../api/migrations/0055_merge_20200714_2027.py | 14 -- .../apps/api/migrations/0055_reminder_mentions.py | 20 --- .../api/migrations/0055_user_roles_to_array.py | 24 ++++ .../api/migrations/0056_allow_blank_user_roles.py | 21 --- .../0056_user_invalidate_unknown_role.py | 21 +++ .../api/migrations/0057_merge_20200716_0751.py | 14 -- .../apps/api/migrations/0057_reminder_mentions.py | 20 +++ .../api/migrations/0058_allow_blank_user_roles.py | 21 +++ .../migrations/0058_create_new_filterlist_model.py | 33 ----- .../migrations/0059_create_new_filterlist_model.py | 33 +++++ .../api/migrations/0059_populate_filterlists.py | 153 --------------------- .../api/migrations/0060_populate_filterlists.py | 153 +++++++++++++++++++++ .../migrations/0060_populate_filterlists_fix.py | 85 ------------ .../migrations/0061_populate_filterlists_fix.py | 85 ++++++++++++ 22 files changed, 417 insertions(+), 445 deletions(-) delete mode 100644 pydis_site/apps/api/migrations/0051_create_news_setting.py create mode 100644 pydis_site/apps/api/migrations/0052_create_news_setting.py delete mode 100644 pydis_site/apps/api/migrations/0052_offtopicchannelname_used.py delete mode 100644 pydis_site/apps/api/migrations/0052_remove_user_avatar_hash.py create mode 100644 pydis_site/apps/api/migrations/0053_offtopicchannelname_used.py delete mode 100644 pydis_site/apps/api/migrations/0053_user_roles_to_array.py create mode 100644 pydis_site/apps/api/migrations/0054_remove_user_avatar_hash.py delete mode 100644 pydis_site/apps/api/migrations/0054_user_invalidate_unknown_role.py delete mode 100644 pydis_site/apps/api/migrations/0055_merge_20200714_2027.py delete mode 100644 pydis_site/apps/api/migrations/0055_reminder_mentions.py create mode 100644 pydis_site/apps/api/migrations/0055_user_roles_to_array.py delete mode 100644 pydis_site/apps/api/migrations/0056_allow_blank_user_roles.py create mode 100644 pydis_site/apps/api/migrations/0056_user_invalidate_unknown_role.py delete mode 100644 pydis_site/apps/api/migrations/0057_merge_20200716_0751.py create mode 100644 pydis_site/apps/api/migrations/0057_reminder_mentions.py create mode 100644 pydis_site/apps/api/migrations/0058_allow_blank_user_roles.py delete mode 100644 pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py create mode 100644 pydis_site/apps/api/migrations/0059_create_new_filterlist_model.py delete mode 100644 pydis_site/apps/api/migrations/0059_populate_filterlists.py create mode 100644 pydis_site/apps/api/migrations/0060_populate_filterlists.py delete mode 100644 pydis_site/apps/api/migrations/0060_populate_filterlists_fix.py create mode 100644 pydis_site/apps/api/migrations/0061_populate_filterlists_fix.py diff --git a/pydis_site/apps/api/migrations/0051_create_news_setting.py b/pydis_site/apps/api/migrations/0051_create_news_setting.py deleted file mode 100644 index f18fdfb1..00000000 --- a/pydis_site/apps/api/migrations/0051_create_news_setting.py +++ /dev/null @@ -1,25 +0,0 @@ -from django.db import migrations - - -def up(apps, schema_editor): - BotSetting = apps.get_model('api', 'BotSetting') - setting = BotSetting( - name='news', - data={} - ).save() - - -def down(apps, schema_editor): - BotSetting = apps.get_model('api', 'BotSetting') - BotSetting.objects.get(name='news').delete() - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0050_remove_infractions_active_default_value'), - ] - - operations = [ - migrations.RunPython(up, down) - ] diff --git a/pydis_site/apps/api/migrations/0052_create_news_setting.py b/pydis_site/apps/api/migrations/0052_create_news_setting.py new file mode 100644 index 00000000..b101d19d --- /dev/null +++ b/pydis_site/apps/api/migrations/0052_create_news_setting.py @@ -0,0 +1,25 @@ +from django.db import migrations + + +def up(apps, schema_editor): + BotSetting = apps.get_model('api', 'BotSetting') + setting = BotSetting( + name='news', + data={} + ).save() + + +def down(apps, schema_editor): + BotSetting = apps.get_model('api', 'BotSetting') + BotSetting.objects.get(name='news').delete() + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0051_allow_blank_message_embeds'), + ] + + operations = [ + migrations.RunPython(up, down) + ] diff --git a/pydis_site/apps/api/migrations/0052_offtopicchannelname_used.py b/pydis_site/apps/api/migrations/0052_offtopicchannelname_used.py deleted file mode 100644 index dfdf3835..00000000 --- a/pydis_site/apps/api/migrations/0052_offtopicchannelname_used.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.11 on 2020-03-30 10:24 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0051_create_news_setting'), - ] - - operations = [ - migrations.AddField( - model_name='offtopicchannelname', - name='used', - field=models.BooleanField(default=False, help_text='Whether or not this name has already been used during this rotation'), - ), - ] diff --git a/pydis_site/apps/api/migrations/0052_remove_user_avatar_hash.py b/pydis_site/apps/api/migrations/0052_remove_user_avatar_hash.py deleted file mode 100644 index 26b3b954..00000000 --- a/pydis_site/apps/api/migrations/0052_remove_user_avatar_hash.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 2.2.11 on 2020-05-27 07:17 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0051_create_news_setting'), - ] - - operations = [ - migrations.RemoveField( - model_name='user', - name='avatar_hash', - ), - ] diff --git a/pydis_site/apps/api/migrations/0053_offtopicchannelname_used.py b/pydis_site/apps/api/migrations/0053_offtopicchannelname_used.py new file mode 100644 index 00000000..b51ce1d2 --- /dev/null +++ b/pydis_site/apps/api/migrations/0053_offtopicchannelname_used.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.11 on 2020-03-30 10:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0052_create_news_setting'), + ] + + operations = [ + migrations.AddField( + model_name='offtopicchannelname', + name='used', + field=models.BooleanField(default=False, help_text='Whether or not this name has already been used during this rotation'), + ), + ] diff --git a/pydis_site/apps/api/migrations/0053_user_roles_to_array.py b/pydis_site/apps/api/migrations/0053_user_roles_to_array.py deleted file mode 100644 index 7ff3a548..00000000 --- a/pydis_site/apps/api/migrations/0053_user_roles_to_array.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 2.2.11 on 2020-06-02 13:42 - -import django.contrib.postgres.fields -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0052_remove_user_avatar_hash'), - ] - - operations = [ - migrations.RemoveField( - model_name='user', - name='roles', - ), - migrations.AddField( - model_name='user', - name='roles', - field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.')]), default=list, help_text='IDs of roles the user has on the server', size=None), - ), - ] diff --git a/pydis_site/apps/api/migrations/0054_remove_user_avatar_hash.py b/pydis_site/apps/api/migrations/0054_remove_user_avatar_hash.py new file mode 100644 index 00000000..be9fd948 --- /dev/null +++ b/pydis_site/apps/api/migrations/0054_remove_user_avatar_hash.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2.11 on 2020-05-27 07:17 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0053_offtopicchannelname_used'), + ] + + operations = [ + migrations.RemoveField( + model_name='user', + name='avatar_hash', + ), + ] diff --git a/pydis_site/apps/api/migrations/0054_user_invalidate_unknown_role.py b/pydis_site/apps/api/migrations/0054_user_invalidate_unknown_role.py deleted file mode 100644 index 96230015..00000000 --- a/pydis_site/apps/api/migrations/0054_user_invalidate_unknown_role.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 2.2.11 on 2020-06-02 20:08 - -import django.contrib.postgres.fields -import django.core.validators -from django.db import migrations, models -import pydis_site.apps.api.models.bot.user - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0053_user_roles_to_array'), - ] - - operations = [ - migrations.AlterField( - model_name='user', - name='roles', - field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.'), pydis_site.apps.api.models.bot.user._validate_existing_role]), default=list, help_text='IDs of roles the user has on the server', size=None), - ), - ] diff --git a/pydis_site/apps/api/migrations/0055_merge_20200714_2027.py b/pydis_site/apps/api/migrations/0055_merge_20200714_2027.py deleted file mode 100644 index f2a0e638..00000000 --- a/pydis_site/apps/api/migrations/0055_merge_20200714_2027.py +++ /dev/null @@ -1,14 +0,0 @@ -# Generated by Django 3.0.8 on 2020-07-14 20:27 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0051_allow_blank_message_embeds'), - ('api', '0054_user_invalidate_unknown_role'), - ] - - operations = [ - ] diff --git a/pydis_site/apps/api/migrations/0055_reminder_mentions.py b/pydis_site/apps/api/migrations/0055_reminder_mentions.py deleted file mode 100644 index d73b450d..00000000 --- a/pydis_site/apps/api/migrations/0055_reminder_mentions.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 2.2.14 on 2020-07-15 07:37 - -import django.contrib.postgres.fields -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0054_user_invalidate_unknown_role'), - ] - - operations = [ - migrations.AddField( - model_name='reminder', - name='mentions', - field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Mention IDs cannot be negative.')]), blank=True, default=list, help_text='IDs of roles or users to ping with the reminder.', size=None), - ), - ] diff --git a/pydis_site/apps/api/migrations/0055_user_roles_to_array.py b/pydis_site/apps/api/migrations/0055_user_roles_to_array.py new file mode 100644 index 00000000..e7b4a983 --- /dev/null +++ b/pydis_site/apps/api/migrations/0055_user_roles_to_array.py @@ -0,0 +1,24 @@ +# Generated by Django 2.2.11 on 2020-06-02 13:42 + +import django.contrib.postgres.fields +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0054_remove_user_avatar_hash'), + ] + + operations = [ + migrations.RemoveField( + model_name='user', + name='roles', + ), + migrations.AddField( + model_name='user', + name='roles', + field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.')]), default=list, help_text='IDs of roles the user has on the server', size=None), + ), + ] diff --git a/pydis_site/apps/api/migrations/0056_allow_blank_user_roles.py b/pydis_site/apps/api/migrations/0056_allow_blank_user_roles.py deleted file mode 100644 index 489941c7..00000000 --- a/pydis_site/apps/api/migrations/0056_allow_blank_user_roles.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 3.0.8 on 2020-07-14 20:35 - -import django.contrib.postgres.fields -import django.core.validators -from django.db import migrations, models -import pydis_site.apps.api.models.bot.user - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0055_merge_20200714_2027'), - ] - - operations = [ - migrations.AlterField( - model_name='user', - name='roles', - field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.'), pydis_site.apps.api.models.bot.user._validate_existing_role]), blank=True, default=list, help_text='IDs of roles the user has on the server', size=None), - ), - ] diff --git a/pydis_site/apps/api/migrations/0056_user_invalidate_unknown_role.py b/pydis_site/apps/api/migrations/0056_user_invalidate_unknown_role.py new file mode 100644 index 00000000..ab2696aa --- /dev/null +++ b/pydis_site/apps/api/migrations/0056_user_invalidate_unknown_role.py @@ -0,0 +1,21 @@ +# Generated by Django 2.2.11 on 2020-06-02 20:08 + +import django.contrib.postgres.fields +import django.core.validators +from django.db import migrations, models +import pydis_site.apps.api.models.bot.user + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0055_user_roles_to_array'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='roles', + field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.'), pydis_site.apps.api.models.bot.user._validate_existing_role]), default=list, help_text='IDs of roles the user has on the server', size=None), + ), + ] diff --git a/pydis_site/apps/api/migrations/0057_merge_20200716_0751.py b/pydis_site/apps/api/migrations/0057_merge_20200716_0751.py deleted file mode 100644 index 47a6d2d4..00000000 --- a/pydis_site/apps/api/migrations/0057_merge_20200716_0751.py +++ /dev/null @@ -1,14 +0,0 @@ -# Generated by Django 2.2.14 on 2020-07-16 07:51 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0055_reminder_mentions'), - ('api', '0056_allow_blank_user_roles'), - ] - - operations = [ - ] diff --git a/pydis_site/apps/api/migrations/0057_reminder_mentions.py b/pydis_site/apps/api/migrations/0057_reminder_mentions.py new file mode 100644 index 00000000..fb829a17 --- /dev/null +++ b/pydis_site/apps/api/migrations/0057_reminder_mentions.py @@ -0,0 +1,20 @@ +# Generated by Django 2.2.14 on 2020-07-15 07:37 + +import django.contrib.postgres.fields +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0056_user_invalidate_unknown_role'), + ] + + operations = [ + migrations.AddField( + model_name='reminder', + name='mentions', + field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Mention IDs cannot be negative.')]), blank=True, default=list, help_text='IDs of roles or users to ping with the reminder.', size=None), + ), + ] diff --git a/pydis_site/apps/api/migrations/0058_allow_blank_user_roles.py b/pydis_site/apps/api/migrations/0058_allow_blank_user_roles.py new file mode 100644 index 00000000..8f7fddfc --- /dev/null +++ b/pydis_site/apps/api/migrations/0058_allow_blank_user_roles.py @@ -0,0 +1,21 @@ +# Generated by Django 3.0.8 on 2020-07-14 20:35 + +import django.contrib.postgres.fields +import django.core.validators +from django.db import migrations, models +import pydis_site.apps.api.models.bot.user + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0057_reminder_mentions'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='roles', + field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.'), pydis_site.apps.api.models.bot.user._validate_existing_role]), blank=True, default=list, help_text='IDs of roles the user has on the server', size=None), + ), + ] diff --git a/pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py b/pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py deleted file mode 100644 index aecfdad7..00000000 --- a/pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py +++ /dev/null @@ -1,33 +0,0 @@ -# Generated by Django 3.0.8 on 2020-07-15 11:23 - -from django.db import migrations, models -import pydis_site.apps.api.models.mixins - - -class Migration(migrations.Migration): - dependencies = [ - ('api', '0057_merge_20200716_0751'), - ] - - operations = [ - migrations.CreateModel( - name='FilterList', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('type', models.CharField( - choices=[('GUILD_INVITE', 'Guild Invite'), ('FILE_FORMAT', 'File Format'), - ('DOMAIN_NAME', 'Domain Name'), ('FILTER_TOKEN', 'Filter Token')], - help_text='The type of allowlist this is on.', max_length=50)), - ('allowed', models.BooleanField(help_text='Whether this item is on the allowlist or the denylist.')), - ('content', models.TextField(help_text='The data to add to the allow or denylist.')), - ('comment', models.TextField(help_text="Optional comment on this entry.", null=True)), - ], - bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), - ), - migrations.AddConstraint( - model_name='filterlist', - constraint=models.UniqueConstraint(fields=('content', 'type'), name='unique_filter_list') - ) - ] diff --git a/pydis_site/apps/api/migrations/0059_create_new_filterlist_model.py b/pydis_site/apps/api/migrations/0059_create_new_filterlist_model.py new file mode 100644 index 00000000..eac5542d --- /dev/null +++ b/pydis_site/apps/api/migrations/0059_create_new_filterlist_model.py @@ -0,0 +1,33 @@ +# Generated by Django 3.0.8 on 2020-07-15 11:23 + +from django.db import migrations, models +import pydis_site.apps.api.models.mixins + + +class Migration(migrations.Migration): + dependencies = [ + ('api', '0058_allow_blank_user_roles'), + ] + + operations = [ + migrations.CreateModel( + name='FilterList', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('type', models.CharField( + choices=[('GUILD_INVITE', 'Guild Invite'), ('FILE_FORMAT', 'File Format'), + ('DOMAIN_NAME', 'Domain Name'), ('FILTER_TOKEN', 'Filter Token')], + help_text='The type of allowlist this is on.', max_length=50)), + ('allowed', models.BooleanField(help_text='Whether this item is on the allowlist or the denylist.')), + ('content', models.TextField(help_text='The data to add to the allow or denylist.')), + ('comment', models.TextField(help_text="Optional comment on this entry.", null=True)), + ], + bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), + ), + migrations.AddConstraint( + model_name='filterlist', + constraint=models.UniqueConstraint(fields=('content', 'type'), name='unique_filter_list') + ) + ] diff --git a/pydis_site/apps/api/migrations/0059_populate_filterlists.py b/pydis_site/apps/api/migrations/0059_populate_filterlists.py deleted file mode 100644 index 8c550191..00000000 --- a/pydis_site/apps/api/migrations/0059_populate_filterlists.py +++ /dev/null @@ -1,153 +0,0 @@ -from django.db import migrations - -guild_invite_whitelist = [ - ("discord.gg/python", "Python Discord", True), - ("discord.gg/4JJdJKb", "RLBot", True), - ("discord.gg/djPtTRJ", "Kivy", True), - ("discord.gg/QXyegWe", "Pyglet", True), - ("discord.gg/9XsucTT", "Panda3D", True), - ("discord.gg/AP3rq2k", "PyWeek", True), - ("discord.gg/vSPsP9t", "Microsoft Python", True), - ("discord.gg/bRCvFy9", "Discord.js Official", True), - ("discord.gg/9zT7NHP", "Programming Discussions", True), - ("discord.gg/ysd6M4r", "JetBrains Community", True), - ("discord.gg/4xJeCgy", "Raspberry Pie", True), - ("discord.gg/AStb3kZ", "Ren'Py", True), - ("discord.gg/t655QNV", "Python Discord: Emojis 1", True), - ("discord.gg/vRZPkqC", "Python Discord: Emojis 2", True), - ("discord.gg/jTtgWuy", "Django", True), - ("discord.gg/W9BypZF", "STEM", True), - ("discord.gg/dpy", "discord.py", True), - ("discord.gg/programming", "Programmers Hangout", True), - ("discord.gg/qhGUjGD", "SpeakJS", True), - ("discord.gg/eTbWSZj", "Functional Programming", True), - ("discord.gg/r8yreB6", "PyGame", True), - ("discord.gg/5UBnR3P", "Python Atlanta", True), - ("discord.gg/ccyrDKv", "C#", True), -] - -domain_name_blacklist = [ - ("pornhub.com", None, False), - ("liveleak.com", None, False), - ("grabify.link", None, False), - ("bmwforum.co", None, False), - ("leancoding.co", None, False), - ("spottyfly.com", None, False), - ("stopify.co", None, False), - ("yoütu.be", None, False), - ("discörd.com", None, False), - ("minecräft.com", None, False), - ("freegiftcards.co", None, False), - ("disçordapp.com", None, False), - ("fortnight.space", None, False), - ("fortnitechat.site", None, False), - ("joinmy.site", None, False), - ("curiouscat.club", None, False), - ("catsnthings.fun", None, False), - ("yourtube.site", None, False), - ("youtubeshort.watch", None, False), - ("catsnthing.com", None, False), - ("youtubeshort.pro", None, False), - ("canadianlumberjacks.online", None, False), - ("poweredbydialup.club", None, False), - ("poweredbydialup.online", None, False), - ("poweredbysecurity.org", None, False), - ("poweredbysecurity.online", None, False), - ("ssteam.site", None, False), - ("steamwalletgift.com", None, False), - ("discord.gift", None, False), - ("lmgtfy.com", None, False), -] - -filter_token_blacklist = [ - ("\bgoo+ks*\b", None, False), - ("\bky+s+\b", None, False), - ("\bki+ke+s*\b", None, False), - ("\bbeaner+s?\b", None, False), - ("\bcoo+ns*\b", None, False), - ("\bnig+lets*\b", None, False), - ("\bslant-eyes*\b", None, False), - ("\btowe?l-?head+s*\b", None, False), - ("\bchi*n+k+s*\b", None, False), - ("\bspick*s*\b", None, False), - ("\bkill* +(?:yo)?urself+\b", None, False), - ("\bjew+s*\b", None, False), - ("\bsuicide\b", None, False), - ("\brape\b", None, False), - ("\b(re+)tar+(d+|t+)(ed)?\b", None, False), - ("\bta+r+d+\b", None, False), - ("\bcunts*\b", None, False), - ("\btrann*y\b", None, False), - ("\bshemale\b", None, False), - ("fa+g+s*", None, False), - ("卐", None, False), - ("卍", None, False), - ("࿖", None, False), - ("࿕", None, False), - ("࿘", None, False), - ("࿗", None, False), - ("cuck(?!oo+)", None, False), - ("nigg+(?:e*r+|a+h*?|u+h+)s?", None, False), - ("fag+o+t+s*", None, False), -] - -file_format_whitelist = [ - (".3gp", None, True), - (".3g2", None, True), - (".avi", None, True), - (".bmp", None, True), - (".gif", None, True), - (".h264", None, True), - (".jpg", None, True), - (".jpeg", None, True), - (".m4v", None, True), - (".mkv", None, True), - (".mov", None, True), - (".mp4", None, True), - (".mpeg", None, True), - (".mpg", None, True), - (".png", None, True), - (".tiff", None, True), - (".wmv", None, True), - (".svg", None, True), - (".psd", "Photoshop", True), - (".ai", "Illustrator", True), - (".aep", "After Effects", True), - (".xcf", "GIMP", True), - (".mp3", None, True), - (".wav", None, True), - (".ogg", None, True), - (".webm", None, True), - (".webp", None, True), -] - -populate_data = { - "FILTER_TOKEN": filter_token_blacklist, - "DOMAIN_NAME": domain_name_blacklist, - "FILE_FORMAT": file_format_whitelist, - "GUILD_INVITE": guild_invite_whitelist, -} - - -class Migration(migrations.Migration): - dependencies = [("api", "0058_create_new_filterlist_model")] - - def populate_filterlists(app, _): - FilterList = app.get_model("api", "FilterList") - - for filterlist_type, metadata in populate_data.items(): - for content, comment, allowed in metadata: - FilterList.objects.create( - type=filterlist_type, - allowed=allowed, - content=content, - comment=comment, - ) - - def clear_filterlists(app, _): - FilterList = app.get_model("api", "FilterList") - FilterList.objects.all().delete() - - operations = [ - migrations.RunPython(populate_filterlists, clear_filterlists) - ] diff --git a/pydis_site/apps/api/migrations/0060_populate_filterlists.py b/pydis_site/apps/api/migrations/0060_populate_filterlists.py new file mode 100644 index 00000000..35fde95a --- /dev/null +++ b/pydis_site/apps/api/migrations/0060_populate_filterlists.py @@ -0,0 +1,153 @@ +from django.db import migrations + +guild_invite_whitelist = [ + ("discord.gg/python", "Python Discord", True), + ("discord.gg/4JJdJKb", "RLBot", True), + ("discord.gg/djPtTRJ", "Kivy", True), + ("discord.gg/QXyegWe", "Pyglet", True), + ("discord.gg/9XsucTT", "Panda3D", True), + ("discord.gg/AP3rq2k", "PyWeek", True), + ("discord.gg/vSPsP9t", "Microsoft Python", True), + ("discord.gg/bRCvFy9", "Discord.js Official", True), + ("discord.gg/9zT7NHP", "Programming Discussions", True), + ("discord.gg/ysd6M4r", "JetBrains Community", True), + ("discord.gg/4xJeCgy", "Raspberry Pie", True), + ("discord.gg/AStb3kZ", "Ren'Py", True), + ("discord.gg/t655QNV", "Python Discord: Emojis 1", True), + ("discord.gg/vRZPkqC", "Python Discord: Emojis 2", True), + ("discord.gg/jTtgWuy", "Django", True), + ("discord.gg/W9BypZF", "STEM", True), + ("discord.gg/dpy", "discord.py", True), + ("discord.gg/programming", "Programmers Hangout", True), + ("discord.gg/qhGUjGD", "SpeakJS", True), + ("discord.gg/eTbWSZj", "Functional Programming", True), + ("discord.gg/r8yreB6", "PyGame", True), + ("discord.gg/5UBnR3P", "Python Atlanta", True), + ("discord.gg/ccyrDKv", "C#", True), +] + +domain_name_blacklist = [ + ("pornhub.com", None, False), + ("liveleak.com", None, False), + ("grabify.link", None, False), + ("bmwforum.co", None, False), + ("leancoding.co", None, False), + ("spottyfly.com", None, False), + ("stopify.co", None, False), + ("yoütu.be", None, False), + ("discörd.com", None, False), + ("minecräft.com", None, False), + ("freegiftcards.co", None, False), + ("disçordapp.com", None, False), + ("fortnight.space", None, False), + ("fortnitechat.site", None, False), + ("joinmy.site", None, False), + ("curiouscat.club", None, False), + ("catsnthings.fun", None, False), + ("yourtube.site", None, False), + ("youtubeshort.watch", None, False), + ("catsnthing.com", None, False), + ("youtubeshort.pro", None, False), + ("canadianlumberjacks.online", None, False), + ("poweredbydialup.club", None, False), + ("poweredbydialup.online", None, False), + ("poweredbysecurity.org", None, False), + ("poweredbysecurity.online", None, False), + ("ssteam.site", None, False), + ("steamwalletgift.com", None, False), + ("discord.gift", None, False), + ("lmgtfy.com", None, False), +] + +filter_token_blacklist = [ + ("\bgoo+ks*\b", None, False), + ("\bky+s+\b", None, False), + ("\bki+ke+s*\b", None, False), + ("\bbeaner+s?\b", None, False), + ("\bcoo+ns*\b", None, False), + ("\bnig+lets*\b", None, False), + ("\bslant-eyes*\b", None, False), + ("\btowe?l-?head+s*\b", None, False), + ("\bchi*n+k+s*\b", None, False), + ("\bspick*s*\b", None, False), + ("\bkill* +(?:yo)?urself+\b", None, False), + ("\bjew+s*\b", None, False), + ("\bsuicide\b", None, False), + ("\brape\b", None, False), + ("\b(re+)tar+(d+|t+)(ed)?\b", None, False), + ("\bta+r+d+\b", None, False), + ("\bcunts*\b", None, False), + ("\btrann*y\b", None, False), + ("\bshemale\b", None, False), + ("fa+g+s*", None, False), + ("卐", None, False), + ("卍", None, False), + ("࿖", None, False), + ("࿕", None, False), + ("࿘", None, False), + ("࿗", None, False), + ("cuck(?!oo+)", None, False), + ("nigg+(?:e*r+|a+h*?|u+h+)s?", None, False), + ("fag+o+t+s*", None, False), +] + +file_format_whitelist = [ + (".3gp", None, True), + (".3g2", None, True), + (".avi", None, True), + (".bmp", None, True), + (".gif", None, True), + (".h264", None, True), + (".jpg", None, True), + (".jpeg", None, True), + (".m4v", None, True), + (".mkv", None, True), + (".mov", None, True), + (".mp4", None, True), + (".mpeg", None, True), + (".mpg", None, True), + (".png", None, True), + (".tiff", None, True), + (".wmv", None, True), + (".svg", None, True), + (".psd", "Photoshop", True), + (".ai", "Illustrator", True), + (".aep", "After Effects", True), + (".xcf", "GIMP", True), + (".mp3", None, True), + (".wav", None, True), + (".ogg", None, True), + (".webm", None, True), + (".webp", None, True), +] + +populate_data = { + "FILTER_TOKEN": filter_token_blacklist, + "DOMAIN_NAME": domain_name_blacklist, + "FILE_FORMAT": file_format_whitelist, + "GUILD_INVITE": guild_invite_whitelist, +} + + +class Migration(migrations.Migration): + dependencies = [("api", "0059_create_new_filterlist_model")] + + def populate_filterlists(app, _): + FilterList = app.get_model("api", "FilterList") + + for filterlist_type, metadata in populate_data.items(): + for content, comment, allowed in metadata: + FilterList.objects.create( + type=filterlist_type, + allowed=allowed, + content=content, + comment=comment, + ) + + def clear_filterlists(app, _): + FilterList = app.get_model("api", "FilterList") + FilterList.objects.all().delete() + + operations = [ + migrations.RunPython(populate_filterlists, clear_filterlists) + ] diff --git a/pydis_site/apps/api/migrations/0060_populate_filterlists_fix.py b/pydis_site/apps/api/migrations/0060_populate_filterlists_fix.py deleted file mode 100644 index 53846f02..00000000 --- a/pydis_site/apps/api/migrations/0060_populate_filterlists_fix.py +++ /dev/null @@ -1,85 +0,0 @@ -from django.db import migrations - -bad_guild_invite_whitelist = [ - ("discord.gg/python", "Python Discord", True), - ("discord.gg/4JJdJKb", "RLBot", True), - ("discord.gg/djPtTRJ", "Kivy", True), - ("discord.gg/QXyegWe", "Pyglet", True), - ("discord.gg/9XsucTT", "Panda3D", True), - ("discord.gg/AP3rq2k", "PyWeek", True), - ("discord.gg/vSPsP9t", "Microsoft Python", True), - ("discord.gg/bRCvFy9", "Discord.js Official", True), - ("discord.gg/9zT7NHP", "Programming Discussions", True), - ("discord.gg/ysd6M4r", "JetBrains Community", True), - ("discord.gg/4xJeCgy", "Raspberry Pie", True), - ("discord.gg/AStb3kZ", "Ren'Py", True), - ("discord.gg/t655QNV", "Python Discord: Emojis 1", True), - ("discord.gg/vRZPkqC", "Python Discord: Emojis 2", True), - ("discord.gg/jTtgWuy", "Django", True), - ("discord.gg/W9BypZF", "STEM", True), - ("discord.gg/dpy", "discord.py", True), - ("discord.gg/programming", "Programmers Hangout", True), - ("discord.gg/qhGUjGD", "SpeakJS", True), - ("discord.gg/eTbWSZj", "Functional Programming", True), - ("discord.gg/r8yreB6", "PyGame", True), - ("discord.gg/5UBnR3P", "Python Atlanta", True), - ("discord.gg/ccyrDKv", "C#", True), -] - -guild_invite_whitelist = [ - ("267624335836053506", "Python Discord", True), - ("348658686962696195", "RLBot", True), - ("423249981340778496", "Kivy", True), - ("438622377094414346", "Pyglet", True), - ("524691714909274162", "Panda3D", True), - ("666560367173828639", "PyWeek", True), - ("702724176489873509", "Microsoft Python", True), - ("222078108977594368", "Discord.js Official", True), - ("238666723824238602", "Programming Discussions", True), - ("433980600391696384", "JetBrains Community", True), - ("204621105720328193", "Raspberry Pie", True), - ("286633898581164032", "Ren'Py", True), - ("440186186024222721", "Python Discord: Emojis 1", True), - ("578587418123304970", "Python Discord: Emojis 2", True), - ("159039020565790721", "Django", True), - ("273944235143593984", "STEM", True), - ("336642139381301249", "discord.py", True), - ("244230771232079873", "Programmers Hangout", True), - ("239433591950540801", "SpeakJS", True), - ("280033776820813825", "Functional Programming", True), - ("349505959032389632", "PyGame", True), - ("488751051629920277", "Python Atlanta", True), - ("143867839282020352", "C#", True), -] - - -class Migration(migrations.Migration): - dependencies = [("api", "0059_populate_filterlists")] - - def fix_filterlist(app, _): - FilterList = app.get_model("api", "FilterList") - FilterList.objects.filter(type="GUILD_INVITE").delete() # Clear out the stuff added in 0059. - - for content, comment, allowed in guild_invite_whitelist: - FilterList.objects.create( - type="GUILD_INVITE", - allowed=allowed, - content=content, - comment=comment, - ) - - def restore_bad_filterlist(app, _): - FilterList = app.get_model("api", "FilterList") - FilterList.objects.filter(type="GUILD_INVITE").delete() - - for content, comment, allowed in bad_guild_invite_whitelist: - FilterList.objects.create( - type="GUILD_INVITE", - allowed=allowed, - content=content, - comment=comment, - ) - - operations = [ - migrations.RunPython(fix_filterlist, restore_bad_filterlist) - ] diff --git a/pydis_site/apps/api/migrations/0061_populate_filterlists_fix.py b/pydis_site/apps/api/migrations/0061_populate_filterlists_fix.py new file mode 100644 index 00000000..eaaafb38 --- /dev/null +++ b/pydis_site/apps/api/migrations/0061_populate_filterlists_fix.py @@ -0,0 +1,85 @@ +from django.db import migrations + +bad_guild_invite_whitelist = [ + ("discord.gg/python", "Python Discord", True), + ("discord.gg/4JJdJKb", "RLBot", True), + ("discord.gg/djPtTRJ", "Kivy", True), + ("discord.gg/QXyegWe", "Pyglet", True), + ("discord.gg/9XsucTT", "Panda3D", True), + ("discord.gg/AP3rq2k", "PyWeek", True), + ("discord.gg/vSPsP9t", "Microsoft Python", True), + ("discord.gg/bRCvFy9", "Discord.js Official", True), + ("discord.gg/9zT7NHP", "Programming Discussions", True), + ("discord.gg/ysd6M4r", "JetBrains Community", True), + ("discord.gg/4xJeCgy", "Raspberry Pie", True), + ("discord.gg/AStb3kZ", "Ren'Py", True), + ("discord.gg/t655QNV", "Python Discord: Emojis 1", True), + ("discord.gg/vRZPkqC", "Python Discord: Emojis 2", True), + ("discord.gg/jTtgWuy", "Django", True), + ("discord.gg/W9BypZF", "STEM", True), + ("discord.gg/dpy", "discord.py", True), + ("discord.gg/programming", "Programmers Hangout", True), + ("discord.gg/qhGUjGD", "SpeakJS", True), + ("discord.gg/eTbWSZj", "Functional Programming", True), + ("discord.gg/r8yreB6", "PyGame", True), + ("discord.gg/5UBnR3P", "Python Atlanta", True), + ("discord.gg/ccyrDKv", "C#", True), +] + +guild_invite_whitelist = [ + ("267624335836053506", "Python Discord", True), + ("348658686962696195", "RLBot", True), + ("423249981340778496", "Kivy", True), + ("438622377094414346", "Pyglet", True), + ("524691714909274162", "Panda3D", True), + ("666560367173828639", "PyWeek", True), + ("702724176489873509", "Microsoft Python", True), + ("222078108977594368", "Discord.js Official", True), + ("238666723824238602", "Programming Discussions", True), + ("433980600391696384", "JetBrains Community", True), + ("204621105720328193", "Raspberry Pie", True), + ("286633898581164032", "Ren'Py", True), + ("440186186024222721", "Python Discord: Emojis 1", True), + ("578587418123304970", "Python Discord: Emojis 2", True), + ("159039020565790721", "Django", True), + ("273944235143593984", "STEM", True), + ("336642139381301249", "discord.py", True), + ("244230771232079873", "Programmers Hangout", True), + ("239433591950540801", "SpeakJS", True), + ("280033776820813825", "Functional Programming", True), + ("349505959032389632", "PyGame", True), + ("488751051629920277", "Python Atlanta", True), + ("143867839282020352", "C#", True), +] + + +class Migration(migrations.Migration): + dependencies = [("api", "0060_populate_filterlists")] + + def fix_filterlist(app, _): + FilterList = app.get_model("api", "FilterList") + FilterList.objects.filter(type="GUILD_INVITE").delete() # Clear out the stuff added in 0059. + + for content, comment, allowed in guild_invite_whitelist: + FilterList.objects.create( + type="GUILD_INVITE", + allowed=allowed, + content=content, + comment=comment, + ) + + def restore_bad_filterlist(app, _): + FilterList = app.get_model("api", "FilterList") + FilterList.objects.filter(type="GUILD_INVITE").delete() + + for content, comment, allowed in bad_guild_invite_whitelist: + FilterList.objects.create( + type="GUILD_INVITE", + allowed=allowed, + content=content, + comment=comment, + ) + + operations = [ + migrations.RunPython(fix_filterlist, restore_bad_filterlist) + ] -- cgit v1.2.3 From ac7745e4ea79fc129447e9b472d3dbd49bf00fd6 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 29 Aug 2020 09:21:15 +0300 Subject: Fix linting issues on off-topic viewset --- pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py b/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py index 29978015..826ad25e 100644 --- a/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py +++ b/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py @@ -1,4 +1,4 @@ -from django.db.models import Case, When, Value +from django.db.models import Case, Value, When from django.db.models.query import QuerySet from django.http.request import HttpRequest from django.shortcuts import get_object_or_404 @@ -114,9 +114,13 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet): # When any name is used in our listing then this means we reached end of round # and we need to reset all other names `used` to False if any(offtopic_name.used for offtopic_name in queryset): + # These names that we just got have to be excluded from updating used to False self.get_queryset().update( - used=Case( # These names that we just got have to be excluded from updating to False - When(name__in=(offtopic_name.name for offtopic_name in queryset), then=Value(True)), + used=Case( + When( + name__in=(offtopic_name.name for offtopic_name in queryset), + then=Value(True) + ), default=Value(False) ) ) -- cgit v1.2.3 From dae68c46831ef14e6a0715add1025f9af1b5a73d Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Sat, 29 Aug 2020 10:00:45 -0700 Subject: Revert pull request #348 Revert commit 330a27926a7f2a9bdae56a5928cd81e55ccb38ed to reverse changes made to 6c08da2f3e48db9c9dd452f2e0505c4a61e46592. Migrations were failing when those changes were deployed. --- .../api/migrations/0051_create_news_setting.py | 25 ++++ .../api/migrations/0052_create_news_setting.py | 25 ---- .../api/migrations/0052_remove_user_avatar_hash.py | 17 +++ .../migrations/0053_offtopicchannelname_used.py | 18 --- .../api/migrations/0053_user_roles_to_array.py | 24 ++++ .../api/migrations/0054_remove_user_avatar_hash.py | 17 --- .../0054_user_invalidate_unknown_role.py | 21 +++ .../api/migrations/0055_merge_20200714_2027.py | 14 ++ .../apps/api/migrations/0055_reminder_mentions.py | 20 +++ .../api/migrations/0055_user_roles_to_array.py | 24 ---- .../api/migrations/0056_allow_blank_user_roles.py | 21 +++ .../0056_user_invalidate_unknown_role.py | 21 --- .../api/migrations/0057_merge_20200716_0751.py | 14 ++ .../apps/api/migrations/0057_reminder_mentions.py | 20 --- .../api/migrations/0058_allow_blank_user_roles.py | 21 --- .../migrations/0058_create_new_filterlist_model.py | 33 +++++ .../migrations/0059_create_new_filterlist_model.py | 33 ----- .../api/migrations/0059_populate_filterlists.py | 153 +++++++++++++++++++++ .../api/migrations/0060_populate_filterlists.py | 153 --------------------- .../migrations/0060_populate_filterlists_fix.py | 85 ++++++++++++ .../migrations/0061_populate_filterlists_fix.py | 85 ------------ .../apps/api/models/bot/off_topic_channel_name.py | 5 - .../apps/api/tests/test_off_topic_channel_names.py | 27 +--- .../api/viewsets/bot/off_topic_channel_name.py | 27 +--- 24 files changed, 431 insertions(+), 472 deletions(-) create mode 100644 pydis_site/apps/api/migrations/0051_create_news_setting.py delete mode 100644 pydis_site/apps/api/migrations/0052_create_news_setting.py create mode 100644 pydis_site/apps/api/migrations/0052_remove_user_avatar_hash.py delete mode 100644 pydis_site/apps/api/migrations/0053_offtopicchannelname_used.py create mode 100644 pydis_site/apps/api/migrations/0053_user_roles_to_array.py delete mode 100644 pydis_site/apps/api/migrations/0054_remove_user_avatar_hash.py create mode 100644 pydis_site/apps/api/migrations/0054_user_invalidate_unknown_role.py create mode 100644 pydis_site/apps/api/migrations/0055_merge_20200714_2027.py create mode 100644 pydis_site/apps/api/migrations/0055_reminder_mentions.py delete mode 100644 pydis_site/apps/api/migrations/0055_user_roles_to_array.py create mode 100644 pydis_site/apps/api/migrations/0056_allow_blank_user_roles.py delete mode 100644 pydis_site/apps/api/migrations/0056_user_invalidate_unknown_role.py create mode 100644 pydis_site/apps/api/migrations/0057_merge_20200716_0751.py delete mode 100644 pydis_site/apps/api/migrations/0057_reminder_mentions.py delete mode 100644 pydis_site/apps/api/migrations/0058_allow_blank_user_roles.py create mode 100644 pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py delete mode 100644 pydis_site/apps/api/migrations/0059_create_new_filterlist_model.py create mode 100644 pydis_site/apps/api/migrations/0059_populate_filterlists.py delete mode 100644 pydis_site/apps/api/migrations/0060_populate_filterlists.py create mode 100644 pydis_site/apps/api/migrations/0060_populate_filterlists_fix.py delete mode 100644 pydis_site/apps/api/migrations/0061_populate_filterlists_fix.py diff --git a/pydis_site/apps/api/migrations/0051_create_news_setting.py b/pydis_site/apps/api/migrations/0051_create_news_setting.py new file mode 100644 index 00000000..f18fdfb1 --- /dev/null +++ b/pydis_site/apps/api/migrations/0051_create_news_setting.py @@ -0,0 +1,25 @@ +from django.db import migrations + + +def up(apps, schema_editor): + BotSetting = apps.get_model('api', 'BotSetting') + setting = BotSetting( + name='news', + data={} + ).save() + + +def down(apps, schema_editor): + BotSetting = apps.get_model('api', 'BotSetting') + BotSetting.objects.get(name='news').delete() + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0050_remove_infractions_active_default_value'), + ] + + operations = [ + migrations.RunPython(up, down) + ] diff --git a/pydis_site/apps/api/migrations/0052_create_news_setting.py b/pydis_site/apps/api/migrations/0052_create_news_setting.py deleted file mode 100644 index b101d19d..00000000 --- a/pydis_site/apps/api/migrations/0052_create_news_setting.py +++ /dev/null @@ -1,25 +0,0 @@ -from django.db import migrations - - -def up(apps, schema_editor): - BotSetting = apps.get_model('api', 'BotSetting') - setting = BotSetting( - name='news', - data={} - ).save() - - -def down(apps, schema_editor): - BotSetting = apps.get_model('api', 'BotSetting') - BotSetting.objects.get(name='news').delete() - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0051_allow_blank_message_embeds'), - ] - - operations = [ - migrations.RunPython(up, down) - ] diff --git a/pydis_site/apps/api/migrations/0052_remove_user_avatar_hash.py b/pydis_site/apps/api/migrations/0052_remove_user_avatar_hash.py new file mode 100644 index 00000000..26b3b954 --- /dev/null +++ b/pydis_site/apps/api/migrations/0052_remove_user_avatar_hash.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2.11 on 2020-05-27 07:17 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0051_create_news_setting'), + ] + + operations = [ + migrations.RemoveField( + model_name='user', + name='avatar_hash', + ), + ] diff --git a/pydis_site/apps/api/migrations/0053_offtopicchannelname_used.py b/pydis_site/apps/api/migrations/0053_offtopicchannelname_used.py deleted file mode 100644 index b51ce1d2..00000000 --- a/pydis_site/apps/api/migrations/0053_offtopicchannelname_used.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.11 on 2020-03-30 10:24 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0052_create_news_setting'), - ] - - operations = [ - migrations.AddField( - model_name='offtopicchannelname', - name='used', - field=models.BooleanField(default=False, help_text='Whether or not this name has already been used during this rotation'), - ), - ] diff --git a/pydis_site/apps/api/migrations/0053_user_roles_to_array.py b/pydis_site/apps/api/migrations/0053_user_roles_to_array.py new file mode 100644 index 00000000..7ff3a548 --- /dev/null +++ b/pydis_site/apps/api/migrations/0053_user_roles_to_array.py @@ -0,0 +1,24 @@ +# Generated by Django 2.2.11 on 2020-06-02 13:42 + +import django.contrib.postgres.fields +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0052_remove_user_avatar_hash'), + ] + + operations = [ + migrations.RemoveField( + model_name='user', + name='roles', + ), + migrations.AddField( + model_name='user', + name='roles', + field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.')]), default=list, help_text='IDs of roles the user has on the server', size=None), + ), + ] diff --git a/pydis_site/apps/api/migrations/0054_remove_user_avatar_hash.py b/pydis_site/apps/api/migrations/0054_remove_user_avatar_hash.py deleted file mode 100644 index be9fd948..00000000 --- a/pydis_site/apps/api/migrations/0054_remove_user_avatar_hash.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 2.2.11 on 2020-05-27 07:17 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0053_offtopicchannelname_used'), - ] - - operations = [ - migrations.RemoveField( - model_name='user', - name='avatar_hash', - ), - ] diff --git a/pydis_site/apps/api/migrations/0054_user_invalidate_unknown_role.py b/pydis_site/apps/api/migrations/0054_user_invalidate_unknown_role.py new file mode 100644 index 00000000..96230015 --- /dev/null +++ b/pydis_site/apps/api/migrations/0054_user_invalidate_unknown_role.py @@ -0,0 +1,21 @@ +# Generated by Django 2.2.11 on 2020-06-02 20:08 + +import django.contrib.postgres.fields +import django.core.validators +from django.db import migrations, models +import pydis_site.apps.api.models.bot.user + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0053_user_roles_to_array'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='roles', + field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.'), pydis_site.apps.api.models.bot.user._validate_existing_role]), default=list, help_text='IDs of roles the user has on the server', size=None), + ), + ] diff --git a/pydis_site/apps/api/migrations/0055_merge_20200714_2027.py b/pydis_site/apps/api/migrations/0055_merge_20200714_2027.py new file mode 100644 index 00000000..f2a0e638 --- /dev/null +++ b/pydis_site/apps/api/migrations/0055_merge_20200714_2027.py @@ -0,0 +1,14 @@ +# Generated by Django 3.0.8 on 2020-07-14 20:27 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0051_allow_blank_message_embeds'), + ('api', '0054_user_invalidate_unknown_role'), + ] + + operations = [ + ] diff --git a/pydis_site/apps/api/migrations/0055_reminder_mentions.py b/pydis_site/apps/api/migrations/0055_reminder_mentions.py new file mode 100644 index 00000000..d73b450d --- /dev/null +++ b/pydis_site/apps/api/migrations/0055_reminder_mentions.py @@ -0,0 +1,20 @@ +# Generated by Django 2.2.14 on 2020-07-15 07:37 + +import django.contrib.postgres.fields +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0054_user_invalidate_unknown_role'), + ] + + operations = [ + migrations.AddField( + model_name='reminder', + name='mentions', + field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Mention IDs cannot be negative.')]), blank=True, default=list, help_text='IDs of roles or users to ping with the reminder.', size=None), + ), + ] diff --git a/pydis_site/apps/api/migrations/0055_user_roles_to_array.py b/pydis_site/apps/api/migrations/0055_user_roles_to_array.py deleted file mode 100644 index e7b4a983..00000000 --- a/pydis_site/apps/api/migrations/0055_user_roles_to_array.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 2.2.11 on 2020-06-02 13:42 - -import django.contrib.postgres.fields -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0054_remove_user_avatar_hash'), - ] - - operations = [ - migrations.RemoveField( - model_name='user', - name='roles', - ), - migrations.AddField( - model_name='user', - name='roles', - field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.')]), default=list, help_text='IDs of roles the user has on the server', size=None), - ), - ] diff --git a/pydis_site/apps/api/migrations/0056_allow_blank_user_roles.py b/pydis_site/apps/api/migrations/0056_allow_blank_user_roles.py new file mode 100644 index 00000000..489941c7 --- /dev/null +++ b/pydis_site/apps/api/migrations/0056_allow_blank_user_roles.py @@ -0,0 +1,21 @@ +# Generated by Django 3.0.8 on 2020-07-14 20:35 + +import django.contrib.postgres.fields +import django.core.validators +from django.db import migrations, models +import pydis_site.apps.api.models.bot.user + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0055_merge_20200714_2027'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='roles', + field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.'), pydis_site.apps.api.models.bot.user._validate_existing_role]), blank=True, default=list, help_text='IDs of roles the user has on the server', size=None), + ), + ] diff --git a/pydis_site/apps/api/migrations/0056_user_invalidate_unknown_role.py b/pydis_site/apps/api/migrations/0056_user_invalidate_unknown_role.py deleted file mode 100644 index ab2696aa..00000000 --- a/pydis_site/apps/api/migrations/0056_user_invalidate_unknown_role.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 2.2.11 on 2020-06-02 20:08 - -import django.contrib.postgres.fields -import django.core.validators -from django.db import migrations, models -import pydis_site.apps.api.models.bot.user - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0055_user_roles_to_array'), - ] - - operations = [ - migrations.AlterField( - model_name='user', - name='roles', - field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.'), pydis_site.apps.api.models.bot.user._validate_existing_role]), default=list, help_text='IDs of roles the user has on the server', size=None), - ), - ] diff --git a/pydis_site/apps/api/migrations/0057_merge_20200716_0751.py b/pydis_site/apps/api/migrations/0057_merge_20200716_0751.py new file mode 100644 index 00000000..47a6d2d4 --- /dev/null +++ b/pydis_site/apps/api/migrations/0057_merge_20200716_0751.py @@ -0,0 +1,14 @@ +# Generated by Django 2.2.14 on 2020-07-16 07:51 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0055_reminder_mentions'), + ('api', '0056_allow_blank_user_roles'), + ] + + operations = [ + ] diff --git a/pydis_site/apps/api/migrations/0057_reminder_mentions.py b/pydis_site/apps/api/migrations/0057_reminder_mentions.py deleted file mode 100644 index fb829a17..00000000 --- a/pydis_site/apps/api/migrations/0057_reminder_mentions.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 2.2.14 on 2020-07-15 07:37 - -import django.contrib.postgres.fields -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0056_user_invalidate_unknown_role'), - ] - - operations = [ - migrations.AddField( - model_name='reminder', - name='mentions', - field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Mention IDs cannot be negative.')]), blank=True, default=list, help_text='IDs of roles or users to ping with the reminder.', size=None), - ), - ] diff --git a/pydis_site/apps/api/migrations/0058_allow_blank_user_roles.py b/pydis_site/apps/api/migrations/0058_allow_blank_user_roles.py deleted file mode 100644 index 8f7fddfc..00000000 --- a/pydis_site/apps/api/migrations/0058_allow_blank_user_roles.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 3.0.8 on 2020-07-14 20:35 - -import django.contrib.postgres.fields -import django.core.validators -from django.db import migrations, models -import pydis_site.apps.api.models.bot.user - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0057_reminder_mentions'), - ] - - operations = [ - migrations.AlterField( - model_name='user', - name='roles', - field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.'), pydis_site.apps.api.models.bot.user._validate_existing_role]), blank=True, default=list, help_text='IDs of roles the user has on the server', size=None), - ), - ] diff --git a/pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py b/pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py new file mode 100644 index 00000000..aecfdad7 --- /dev/null +++ b/pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py @@ -0,0 +1,33 @@ +# Generated by Django 3.0.8 on 2020-07-15 11:23 + +from django.db import migrations, models +import pydis_site.apps.api.models.mixins + + +class Migration(migrations.Migration): + dependencies = [ + ('api', '0057_merge_20200716_0751'), + ] + + operations = [ + migrations.CreateModel( + name='FilterList', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('type', models.CharField( + choices=[('GUILD_INVITE', 'Guild Invite'), ('FILE_FORMAT', 'File Format'), + ('DOMAIN_NAME', 'Domain Name'), ('FILTER_TOKEN', 'Filter Token')], + help_text='The type of allowlist this is on.', max_length=50)), + ('allowed', models.BooleanField(help_text='Whether this item is on the allowlist or the denylist.')), + ('content', models.TextField(help_text='The data to add to the allow or denylist.')), + ('comment', models.TextField(help_text="Optional comment on this entry.", null=True)), + ], + bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), + ), + migrations.AddConstraint( + model_name='filterlist', + constraint=models.UniqueConstraint(fields=('content', 'type'), name='unique_filter_list') + ) + ] diff --git a/pydis_site/apps/api/migrations/0059_create_new_filterlist_model.py b/pydis_site/apps/api/migrations/0059_create_new_filterlist_model.py deleted file mode 100644 index eac5542d..00000000 --- a/pydis_site/apps/api/migrations/0059_create_new_filterlist_model.py +++ /dev/null @@ -1,33 +0,0 @@ -# Generated by Django 3.0.8 on 2020-07-15 11:23 - -from django.db import migrations, models -import pydis_site.apps.api.models.mixins - - -class Migration(migrations.Migration): - dependencies = [ - ('api', '0058_allow_blank_user_roles'), - ] - - operations = [ - migrations.CreateModel( - name='FilterList', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('type', models.CharField( - choices=[('GUILD_INVITE', 'Guild Invite'), ('FILE_FORMAT', 'File Format'), - ('DOMAIN_NAME', 'Domain Name'), ('FILTER_TOKEN', 'Filter Token')], - help_text='The type of allowlist this is on.', max_length=50)), - ('allowed', models.BooleanField(help_text='Whether this item is on the allowlist or the denylist.')), - ('content', models.TextField(help_text='The data to add to the allow or denylist.')), - ('comment', models.TextField(help_text="Optional comment on this entry.", null=True)), - ], - bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), - ), - migrations.AddConstraint( - model_name='filterlist', - constraint=models.UniqueConstraint(fields=('content', 'type'), name='unique_filter_list') - ) - ] diff --git a/pydis_site/apps/api/migrations/0059_populate_filterlists.py b/pydis_site/apps/api/migrations/0059_populate_filterlists.py new file mode 100644 index 00000000..8c550191 --- /dev/null +++ b/pydis_site/apps/api/migrations/0059_populate_filterlists.py @@ -0,0 +1,153 @@ +from django.db import migrations + +guild_invite_whitelist = [ + ("discord.gg/python", "Python Discord", True), + ("discord.gg/4JJdJKb", "RLBot", True), + ("discord.gg/djPtTRJ", "Kivy", True), + ("discord.gg/QXyegWe", "Pyglet", True), + ("discord.gg/9XsucTT", "Panda3D", True), + ("discord.gg/AP3rq2k", "PyWeek", True), + ("discord.gg/vSPsP9t", "Microsoft Python", True), + ("discord.gg/bRCvFy9", "Discord.js Official", True), + ("discord.gg/9zT7NHP", "Programming Discussions", True), + ("discord.gg/ysd6M4r", "JetBrains Community", True), + ("discord.gg/4xJeCgy", "Raspberry Pie", True), + ("discord.gg/AStb3kZ", "Ren'Py", True), + ("discord.gg/t655QNV", "Python Discord: Emojis 1", True), + ("discord.gg/vRZPkqC", "Python Discord: Emojis 2", True), + ("discord.gg/jTtgWuy", "Django", True), + ("discord.gg/W9BypZF", "STEM", True), + ("discord.gg/dpy", "discord.py", True), + ("discord.gg/programming", "Programmers Hangout", True), + ("discord.gg/qhGUjGD", "SpeakJS", True), + ("discord.gg/eTbWSZj", "Functional Programming", True), + ("discord.gg/r8yreB6", "PyGame", True), + ("discord.gg/5UBnR3P", "Python Atlanta", True), + ("discord.gg/ccyrDKv", "C#", True), +] + +domain_name_blacklist = [ + ("pornhub.com", None, False), + ("liveleak.com", None, False), + ("grabify.link", None, False), + ("bmwforum.co", None, False), + ("leancoding.co", None, False), + ("spottyfly.com", None, False), + ("stopify.co", None, False), + ("yoütu.be", None, False), + ("discörd.com", None, False), + ("minecräft.com", None, False), + ("freegiftcards.co", None, False), + ("disçordapp.com", None, False), + ("fortnight.space", None, False), + ("fortnitechat.site", None, False), + ("joinmy.site", None, False), + ("curiouscat.club", None, False), + ("catsnthings.fun", None, False), + ("yourtube.site", None, False), + ("youtubeshort.watch", None, False), + ("catsnthing.com", None, False), + ("youtubeshort.pro", None, False), + ("canadianlumberjacks.online", None, False), + ("poweredbydialup.club", None, False), + ("poweredbydialup.online", None, False), + ("poweredbysecurity.org", None, False), + ("poweredbysecurity.online", None, False), + ("ssteam.site", None, False), + ("steamwalletgift.com", None, False), + ("discord.gift", None, False), + ("lmgtfy.com", None, False), +] + +filter_token_blacklist = [ + ("\bgoo+ks*\b", None, False), + ("\bky+s+\b", None, False), + ("\bki+ke+s*\b", None, False), + ("\bbeaner+s?\b", None, False), + ("\bcoo+ns*\b", None, False), + ("\bnig+lets*\b", None, False), + ("\bslant-eyes*\b", None, False), + ("\btowe?l-?head+s*\b", None, False), + ("\bchi*n+k+s*\b", None, False), + ("\bspick*s*\b", None, False), + ("\bkill* +(?:yo)?urself+\b", None, False), + ("\bjew+s*\b", None, False), + ("\bsuicide\b", None, False), + ("\brape\b", None, False), + ("\b(re+)tar+(d+|t+)(ed)?\b", None, False), + ("\bta+r+d+\b", None, False), + ("\bcunts*\b", None, False), + ("\btrann*y\b", None, False), + ("\bshemale\b", None, False), + ("fa+g+s*", None, False), + ("卐", None, False), + ("卍", None, False), + ("࿖", None, False), + ("࿕", None, False), + ("࿘", None, False), + ("࿗", None, False), + ("cuck(?!oo+)", None, False), + ("nigg+(?:e*r+|a+h*?|u+h+)s?", None, False), + ("fag+o+t+s*", None, False), +] + +file_format_whitelist = [ + (".3gp", None, True), + (".3g2", None, True), + (".avi", None, True), + (".bmp", None, True), + (".gif", None, True), + (".h264", None, True), + (".jpg", None, True), + (".jpeg", None, True), + (".m4v", None, True), + (".mkv", None, True), + (".mov", None, True), + (".mp4", None, True), + (".mpeg", None, True), + (".mpg", None, True), + (".png", None, True), + (".tiff", None, True), + (".wmv", None, True), + (".svg", None, True), + (".psd", "Photoshop", True), + (".ai", "Illustrator", True), + (".aep", "After Effects", True), + (".xcf", "GIMP", True), + (".mp3", None, True), + (".wav", None, True), + (".ogg", None, True), + (".webm", None, True), + (".webp", None, True), +] + +populate_data = { + "FILTER_TOKEN": filter_token_blacklist, + "DOMAIN_NAME": domain_name_blacklist, + "FILE_FORMAT": file_format_whitelist, + "GUILD_INVITE": guild_invite_whitelist, +} + + +class Migration(migrations.Migration): + dependencies = [("api", "0058_create_new_filterlist_model")] + + def populate_filterlists(app, _): + FilterList = app.get_model("api", "FilterList") + + for filterlist_type, metadata in populate_data.items(): + for content, comment, allowed in metadata: + FilterList.objects.create( + type=filterlist_type, + allowed=allowed, + content=content, + comment=comment, + ) + + def clear_filterlists(app, _): + FilterList = app.get_model("api", "FilterList") + FilterList.objects.all().delete() + + operations = [ + migrations.RunPython(populate_filterlists, clear_filterlists) + ] diff --git a/pydis_site/apps/api/migrations/0060_populate_filterlists.py b/pydis_site/apps/api/migrations/0060_populate_filterlists.py deleted file mode 100644 index 35fde95a..00000000 --- a/pydis_site/apps/api/migrations/0060_populate_filterlists.py +++ /dev/null @@ -1,153 +0,0 @@ -from django.db import migrations - -guild_invite_whitelist = [ - ("discord.gg/python", "Python Discord", True), - ("discord.gg/4JJdJKb", "RLBot", True), - ("discord.gg/djPtTRJ", "Kivy", True), - ("discord.gg/QXyegWe", "Pyglet", True), - ("discord.gg/9XsucTT", "Panda3D", True), - ("discord.gg/AP3rq2k", "PyWeek", True), - ("discord.gg/vSPsP9t", "Microsoft Python", True), - ("discord.gg/bRCvFy9", "Discord.js Official", True), - ("discord.gg/9zT7NHP", "Programming Discussions", True), - ("discord.gg/ysd6M4r", "JetBrains Community", True), - ("discord.gg/4xJeCgy", "Raspberry Pie", True), - ("discord.gg/AStb3kZ", "Ren'Py", True), - ("discord.gg/t655QNV", "Python Discord: Emojis 1", True), - ("discord.gg/vRZPkqC", "Python Discord: Emojis 2", True), - ("discord.gg/jTtgWuy", "Django", True), - ("discord.gg/W9BypZF", "STEM", True), - ("discord.gg/dpy", "discord.py", True), - ("discord.gg/programming", "Programmers Hangout", True), - ("discord.gg/qhGUjGD", "SpeakJS", True), - ("discord.gg/eTbWSZj", "Functional Programming", True), - ("discord.gg/r8yreB6", "PyGame", True), - ("discord.gg/5UBnR3P", "Python Atlanta", True), - ("discord.gg/ccyrDKv", "C#", True), -] - -domain_name_blacklist = [ - ("pornhub.com", None, False), - ("liveleak.com", None, False), - ("grabify.link", None, False), - ("bmwforum.co", None, False), - ("leancoding.co", None, False), - ("spottyfly.com", None, False), - ("stopify.co", None, False), - ("yoütu.be", None, False), - ("discörd.com", None, False), - ("minecräft.com", None, False), - ("freegiftcards.co", None, False), - ("disçordapp.com", None, False), - ("fortnight.space", None, False), - ("fortnitechat.site", None, False), - ("joinmy.site", None, False), - ("curiouscat.club", None, False), - ("catsnthings.fun", None, False), - ("yourtube.site", None, False), - ("youtubeshort.watch", None, False), - ("catsnthing.com", None, False), - ("youtubeshort.pro", None, False), - ("canadianlumberjacks.online", None, False), - ("poweredbydialup.club", None, False), - ("poweredbydialup.online", None, False), - ("poweredbysecurity.org", None, False), - ("poweredbysecurity.online", None, False), - ("ssteam.site", None, False), - ("steamwalletgift.com", None, False), - ("discord.gift", None, False), - ("lmgtfy.com", None, False), -] - -filter_token_blacklist = [ - ("\bgoo+ks*\b", None, False), - ("\bky+s+\b", None, False), - ("\bki+ke+s*\b", None, False), - ("\bbeaner+s?\b", None, False), - ("\bcoo+ns*\b", None, False), - ("\bnig+lets*\b", None, False), - ("\bslant-eyes*\b", None, False), - ("\btowe?l-?head+s*\b", None, False), - ("\bchi*n+k+s*\b", None, False), - ("\bspick*s*\b", None, False), - ("\bkill* +(?:yo)?urself+\b", None, False), - ("\bjew+s*\b", None, False), - ("\bsuicide\b", None, False), - ("\brape\b", None, False), - ("\b(re+)tar+(d+|t+)(ed)?\b", None, False), - ("\bta+r+d+\b", None, False), - ("\bcunts*\b", None, False), - ("\btrann*y\b", None, False), - ("\bshemale\b", None, False), - ("fa+g+s*", None, False), - ("卐", None, False), - ("卍", None, False), - ("࿖", None, False), - ("࿕", None, False), - ("࿘", None, False), - ("࿗", None, False), - ("cuck(?!oo+)", None, False), - ("nigg+(?:e*r+|a+h*?|u+h+)s?", None, False), - ("fag+o+t+s*", None, False), -] - -file_format_whitelist = [ - (".3gp", None, True), - (".3g2", None, True), - (".avi", None, True), - (".bmp", None, True), - (".gif", None, True), - (".h264", None, True), - (".jpg", None, True), - (".jpeg", None, True), - (".m4v", None, True), - (".mkv", None, True), - (".mov", None, True), - (".mp4", None, True), - (".mpeg", None, True), - (".mpg", None, True), - (".png", None, True), - (".tiff", None, True), - (".wmv", None, True), - (".svg", None, True), - (".psd", "Photoshop", True), - (".ai", "Illustrator", True), - (".aep", "After Effects", True), - (".xcf", "GIMP", True), - (".mp3", None, True), - (".wav", None, True), - (".ogg", None, True), - (".webm", None, True), - (".webp", None, True), -] - -populate_data = { - "FILTER_TOKEN": filter_token_blacklist, - "DOMAIN_NAME": domain_name_blacklist, - "FILE_FORMAT": file_format_whitelist, - "GUILD_INVITE": guild_invite_whitelist, -} - - -class Migration(migrations.Migration): - dependencies = [("api", "0059_create_new_filterlist_model")] - - def populate_filterlists(app, _): - FilterList = app.get_model("api", "FilterList") - - for filterlist_type, metadata in populate_data.items(): - for content, comment, allowed in metadata: - FilterList.objects.create( - type=filterlist_type, - allowed=allowed, - content=content, - comment=comment, - ) - - def clear_filterlists(app, _): - FilterList = app.get_model("api", "FilterList") - FilterList.objects.all().delete() - - operations = [ - migrations.RunPython(populate_filterlists, clear_filterlists) - ] diff --git a/pydis_site/apps/api/migrations/0060_populate_filterlists_fix.py b/pydis_site/apps/api/migrations/0060_populate_filterlists_fix.py new file mode 100644 index 00000000..53846f02 --- /dev/null +++ b/pydis_site/apps/api/migrations/0060_populate_filterlists_fix.py @@ -0,0 +1,85 @@ +from django.db import migrations + +bad_guild_invite_whitelist = [ + ("discord.gg/python", "Python Discord", True), + ("discord.gg/4JJdJKb", "RLBot", True), + ("discord.gg/djPtTRJ", "Kivy", True), + ("discord.gg/QXyegWe", "Pyglet", True), + ("discord.gg/9XsucTT", "Panda3D", True), + ("discord.gg/AP3rq2k", "PyWeek", True), + ("discord.gg/vSPsP9t", "Microsoft Python", True), + ("discord.gg/bRCvFy9", "Discord.js Official", True), + ("discord.gg/9zT7NHP", "Programming Discussions", True), + ("discord.gg/ysd6M4r", "JetBrains Community", True), + ("discord.gg/4xJeCgy", "Raspberry Pie", True), + ("discord.gg/AStb3kZ", "Ren'Py", True), + ("discord.gg/t655QNV", "Python Discord: Emojis 1", True), + ("discord.gg/vRZPkqC", "Python Discord: Emojis 2", True), + ("discord.gg/jTtgWuy", "Django", True), + ("discord.gg/W9BypZF", "STEM", True), + ("discord.gg/dpy", "discord.py", True), + ("discord.gg/programming", "Programmers Hangout", True), + ("discord.gg/qhGUjGD", "SpeakJS", True), + ("discord.gg/eTbWSZj", "Functional Programming", True), + ("discord.gg/r8yreB6", "PyGame", True), + ("discord.gg/5UBnR3P", "Python Atlanta", True), + ("discord.gg/ccyrDKv", "C#", True), +] + +guild_invite_whitelist = [ + ("267624335836053506", "Python Discord", True), + ("348658686962696195", "RLBot", True), + ("423249981340778496", "Kivy", True), + ("438622377094414346", "Pyglet", True), + ("524691714909274162", "Panda3D", True), + ("666560367173828639", "PyWeek", True), + ("702724176489873509", "Microsoft Python", True), + ("222078108977594368", "Discord.js Official", True), + ("238666723824238602", "Programming Discussions", True), + ("433980600391696384", "JetBrains Community", True), + ("204621105720328193", "Raspberry Pie", True), + ("286633898581164032", "Ren'Py", True), + ("440186186024222721", "Python Discord: Emojis 1", True), + ("578587418123304970", "Python Discord: Emojis 2", True), + ("159039020565790721", "Django", True), + ("273944235143593984", "STEM", True), + ("336642139381301249", "discord.py", True), + ("244230771232079873", "Programmers Hangout", True), + ("239433591950540801", "SpeakJS", True), + ("280033776820813825", "Functional Programming", True), + ("349505959032389632", "PyGame", True), + ("488751051629920277", "Python Atlanta", True), + ("143867839282020352", "C#", True), +] + + +class Migration(migrations.Migration): + dependencies = [("api", "0059_populate_filterlists")] + + def fix_filterlist(app, _): + FilterList = app.get_model("api", "FilterList") + FilterList.objects.filter(type="GUILD_INVITE").delete() # Clear out the stuff added in 0059. + + for content, comment, allowed in guild_invite_whitelist: + FilterList.objects.create( + type="GUILD_INVITE", + allowed=allowed, + content=content, + comment=comment, + ) + + def restore_bad_filterlist(app, _): + FilterList = app.get_model("api", "FilterList") + FilterList.objects.filter(type="GUILD_INVITE").delete() + + for content, comment, allowed in bad_guild_invite_whitelist: + FilterList.objects.create( + type="GUILD_INVITE", + allowed=allowed, + content=content, + comment=comment, + ) + + operations = [ + migrations.RunPython(fix_filterlist, restore_bad_filterlist) + ] diff --git a/pydis_site/apps/api/migrations/0061_populate_filterlists_fix.py b/pydis_site/apps/api/migrations/0061_populate_filterlists_fix.py deleted file mode 100644 index eaaafb38..00000000 --- a/pydis_site/apps/api/migrations/0061_populate_filterlists_fix.py +++ /dev/null @@ -1,85 +0,0 @@ -from django.db import migrations - -bad_guild_invite_whitelist = [ - ("discord.gg/python", "Python Discord", True), - ("discord.gg/4JJdJKb", "RLBot", True), - ("discord.gg/djPtTRJ", "Kivy", True), - ("discord.gg/QXyegWe", "Pyglet", True), - ("discord.gg/9XsucTT", "Panda3D", True), - ("discord.gg/AP3rq2k", "PyWeek", True), - ("discord.gg/vSPsP9t", "Microsoft Python", True), - ("discord.gg/bRCvFy9", "Discord.js Official", True), - ("discord.gg/9zT7NHP", "Programming Discussions", True), - ("discord.gg/ysd6M4r", "JetBrains Community", True), - ("discord.gg/4xJeCgy", "Raspberry Pie", True), - ("discord.gg/AStb3kZ", "Ren'Py", True), - ("discord.gg/t655QNV", "Python Discord: Emojis 1", True), - ("discord.gg/vRZPkqC", "Python Discord: Emojis 2", True), - ("discord.gg/jTtgWuy", "Django", True), - ("discord.gg/W9BypZF", "STEM", True), - ("discord.gg/dpy", "discord.py", True), - ("discord.gg/programming", "Programmers Hangout", True), - ("discord.gg/qhGUjGD", "SpeakJS", True), - ("discord.gg/eTbWSZj", "Functional Programming", True), - ("discord.gg/r8yreB6", "PyGame", True), - ("discord.gg/5UBnR3P", "Python Atlanta", True), - ("discord.gg/ccyrDKv", "C#", True), -] - -guild_invite_whitelist = [ - ("267624335836053506", "Python Discord", True), - ("348658686962696195", "RLBot", True), - ("423249981340778496", "Kivy", True), - ("438622377094414346", "Pyglet", True), - ("524691714909274162", "Panda3D", True), - ("666560367173828639", "PyWeek", True), - ("702724176489873509", "Microsoft Python", True), - ("222078108977594368", "Discord.js Official", True), - ("238666723824238602", "Programming Discussions", True), - ("433980600391696384", "JetBrains Community", True), - ("204621105720328193", "Raspberry Pie", True), - ("286633898581164032", "Ren'Py", True), - ("440186186024222721", "Python Discord: Emojis 1", True), - ("578587418123304970", "Python Discord: Emojis 2", True), - ("159039020565790721", "Django", True), - ("273944235143593984", "STEM", True), - ("336642139381301249", "discord.py", True), - ("244230771232079873", "Programmers Hangout", True), - ("239433591950540801", "SpeakJS", True), - ("280033776820813825", "Functional Programming", True), - ("349505959032389632", "PyGame", True), - ("488751051629920277", "Python Atlanta", True), - ("143867839282020352", "C#", True), -] - - -class Migration(migrations.Migration): - dependencies = [("api", "0060_populate_filterlists")] - - def fix_filterlist(app, _): - FilterList = app.get_model("api", "FilterList") - FilterList.objects.filter(type="GUILD_INVITE").delete() # Clear out the stuff added in 0059. - - for content, comment, allowed in guild_invite_whitelist: - FilterList.objects.create( - type="GUILD_INVITE", - allowed=allowed, - content=content, - comment=comment, - ) - - def restore_bad_filterlist(app, _): - FilterList = app.get_model("api", "FilterList") - FilterList.objects.filter(type="GUILD_INVITE").delete() - - for content, comment, allowed in bad_guild_invite_whitelist: - FilterList.objects.create( - type="GUILD_INVITE", - allowed=allowed, - content=content, - comment=comment, - ) - - operations = [ - migrations.RunPython(fix_filterlist, restore_bad_filterlist) - ] diff --git a/pydis_site/apps/api/models/bot/off_topic_channel_name.py b/pydis_site/apps/api/models/bot/off_topic_channel_name.py index 403c7465..20e77b9f 100644 --- a/pydis_site/apps/api/models/bot/off_topic_channel_name.py +++ b/pydis_site/apps/api/models/bot/off_topic_channel_name.py @@ -16,11 +16,6 @@ class OffTopicChannelName(ModelReprMixin, models.Model): help_text="The actual channel name that will be used on our Discord server." ) - used = models.BooleanField( - default=False, - help_text="Whether or not this name has already been used during this rotation", - ) - def __str__(self): """Returns the current off-topic name, for display purposes.""" return self.name diff --git a/pydis_site/apps/api/tests/test_off_topic_channel_names.py b/pydis_site/apps/api/tests/test_off_topic_channel_names.py index 3ab8b22d..bd42cd81 100644 --- a/pydis_site/apps/api/tests/test_off_topic_channel_names.py +++ b/pydis_site/apps/api/tests/test_off_topic_channel_names.py @@ -10,14 +10,12 @@ class UnauthenticatedTests(APISubdomainTestCase): self.client.force_authenticate(user=None) def test_cannot_read_off_topic_channel_name_list(self): - """Return a 401 response when not authenticated.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(url) self.assertEqual(response.status_code, 401) def test_cannot_read_off_topic_channel_name_list_with_random_item_param(self): - """Return a 401 response when `random_items` provided and not authenticated.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(f'{url}?random_items=no') @@ -26,7 +24,6 @@ class UnauthenticatedTests(APISubdomainTestCase): class EmptyDatabaseTests(APISubdomainTestCase): def test_returns_empty_object(self): - """Return empty list when no names in database.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(url) @@ -34,7 +31,6 @@ class EmptyDatabaseTests(APISubdomainTestCase): self.assertEqual(response.json(), []) def test_returns_empty_list_with_get_all_param(self): - """Return empty list when no names and `random_items` param provided.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(f'{url}?random_items=5') @@ -42,7 +38,6 @@ class EmptyDatabaseTests(APISubdomainTestCase): self.assertEqual(response.json(), []) def test_returns_400_for_bad_random_items_param(self): - """Return error message when passing not integer as `random_items`.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(f'{url}?random_items=totally-a-valid-integer') @@ -52,7 +47,6 @@ class EmptyDatabaseTests(APISubdomainTestCase): }) def test_returns_400_for_negative_random_items_param(self): - """Return error message when passing negative int as `random_items`.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(f'{url}?random_items=-5') @@ -65,11 +59,10 @@ class EmptyDatabaseTests(APISubdomainTestCase): class ListTests(APISubdomainTestCase): @classmethod def setUpTestData(cls): - cls.test_name = OffTopicChannelName.objects.create(name='lemons-lemonade-stand', used=False) - cls.test_name_2 = OffTopicChannelName.objects.create(name='bbq-with-bisk', used=True) + cls.test_name = OffTopicChannelName.objects.create(name='lemons-lemonade-stand') + cls.test_name_2 = OffTopicChannelName.objects.create(name='bbq-with-bisk') def test_returns_name_in_list(self): - """Return all off-topic channel names.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(url) @@ -83,21 +76,11 @@ class ListTests(APISubdomainTestCase): ) def test_returns_single_item_with_random_items_param_set_to_1(self): - """Return not-used name instead used.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(f'{url}?random_items=1') self.assertEqual(response.status_code, 200) self.assertEqual(len(response.json()), 1) - self.assertEqual(response.json(), [self.test_name.name]) - - def test_running_out_of_names_with_random_parameter(self): - """Reset names `used` parameter to `False` when running out of names.""" - url = reverse('bot:offtopicchannelname-list', host='api') - response = self.client.get(f'{url}?random_items=2') - - self.assertEqual(response.status_code, 200) - self.assertEqual(response.json(), [self.test_name.name, self.test_name_2.name]) class CreationTests(APISubdomainTestCase): @@ -110,7 +93,6 @@ class CreationTests(APISubdomainTestCase): self.assertEqual(response.status_code, 201) def test_returns_201_for_unicode_chars(self): - """Accept all valid characters.""" url = reverse('bot:offtopicchannelname-list', host='api') names = ( '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹', @@ -122,7 +104,6 @@ class CreationTests(APISubdomainTestCase): self.assertEqual(response.status_code, 201) def test_returns_400_for_missing_name_param(self): - """Return error message when name not provided.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.post(url) self.assertEqual(response.status_code, 400) @@ -131,7 +112,6 @@ class CreationTests(APISubdomainTestCase): }) def test_returns_400_for_bad_name_param(self): - """Return error message when invalid characters provided.""" url = reverse('bot:offtopicchannelname-list', host='api') invalid_names = ( 'space between words', @@ -154,21 +134,18 @@ class DeletionTests(APISubdomainTestCase): cls.test_name_2 = OffTopicChannelName.objects.create(name='bbq-with-bisk') def test_deleting_unknown_name_returns_404(self): - """Return 404 reponse when trying to delete unknown name.""" url = reverse('bot:offtopicchannelname-detail', args=('unknown-name',), host='api') response = self.client.delete(url) self.assertEqual(response.status_code, 404) def test_deleting_known_name_returns_204(self): - """Return 204 response when deleting was successful.""" url = reverse('bot:offtopicchannelname-detail', args=(self.test_name.name,), host='api') response = self.client.delete(url) self.assertEqual(response.status_code, 204) def test_name_gets_deleted(self): - """Name gets actually deleted.""" url = reverse('bot:offtopicchannelname-detail', args=(self.test_name_2.name,), host='api') response = self.client.delete(url) diff --git a/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py b/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py index 826ad25e..d6da2399 100644 --- a/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py +++ b/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py @@ -1,4 +1,3 @@ -from django.db.models import Case, Value, When from django.db.models.query import QuerySet from django.http.request import HttpRequest from django.shortcuts import get_object_or_404 @@ -21,9 +20,7 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet): Return all known off-topic channel names from the database. If the `random_items` query parameter is given, for example using... $ curl api.pythondiscord.local:8000/bot/off-topic-channel-names?random_items=5 - ... then the API will return `5` random items from the database - that is not used in current rotation. - When running out of names, API will mark all names to not used and start new rotation. + ... then the API will return `5` random items from the database. #### Response format Return a list of off-topic-channel names: @@ -109,27 +106,7 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet): 'random_items': ["Must be a positive integer."] }) - queryset = self.get_queryset().order_by('used', '?')[:random_count] - - # When any name is used in our listing then this means we reached end of round - # and we need to reset all other names `used` to False - if any(offtopic_name.used for offtopic_name in queryset): - # These names that we just got have to be excluded from updating used to False - self.get_queryset().update( - used=Case( - When( - name__in=(offtopic_name.name for offtopic_name in queryset), - then=Value(True) - ), - default=Value(False) - ) - ) - else: - # Otherwise mark selected names `used` to True - self.get_queryset().filter( - name__in=(offtopic_name.name for offtopic_name in queryset) - ).update(used=True) - + queryset = self.get_queryset().order_by('?')[:random_count] serialized = self.serializer_class(queryset, many=True) return Response(serialized.data) -- cgit v1.2.3 From c1ec2f1f9fe443cdde3750b1df1c3d969df3e67e Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sun, 30 Aug 2020 08:20:25 +0300 Subject: Revert "Revert pull request #348" This reverts commit dae68c46831ef14e6a0715add1025f9af1b5a73d. --- .../api/migrations/0051_create_news_setting.py | 25 ---- .../api/migrations/0052_create_news_setting.py | 25 ++++ .../api/migrations/0052_remove_user_avatar_hash.py | 17 --- .../migrations/0053_offtopicchannelname_used.py | 18 +++ .../api/migrations/0053_user_roles_to_array.py | 24 ---- .../api/migrations/0054_remove_user_avatar_hash.py | 17 +++ .../0054_user_invalidate_unknown_role.py | 21 --- .../api/migrations/0055_merge_20200714_2027.py | 14 -- .../apps/api/migrations/0055_reminder_mentions.py | 20 --- .../api/migrations/0055_user_roles_to_array.py | 24 ++++ .../api/migrations/0056_allow_blank_user_roles.py | 21 --- .../0056_user_invalidate_unknown_role.py | 21 +++ .../api/migrations/0057_merge_20200716_0751.py | 14 -- .../apps/api/migrations/0057_reminder_mentions.py | 20 +++ .../api/migrations/0058_allow_blank_user_roles.py | 21 +++ .../migrations/0058_create_new_filterlist_model.py | 33 ----- .../migrations/0059_create_new_filterlist_model.py | 33 +++++ .../api/migrations/0059_populate_filterlists.py | 153 --------------------- .../api/migrations/0060_populate_filterlists.py | 153 +++++++++++++++++++++ .../migrations/0060_populate_filterlists_fix.py | 85 ------------ .../migrations/0061_populate_filterlists_fix.py | 85 ++++++++++++ .../apps/api/models/bot/off_topic_channel_name.py | 5 + .../apps/api/tests/test_off_topic_channel_names.py | 27 +++- .../api/viewsets/bot/off_topic_channel_name.py | 27 +++- 24 files changed, 472 insertions(+), 431 deletions(-) delete mode 100644 pydis_site/apps/api/migrations/0051_create_news_setting.py create mode 100644 pydis_site/apps/api/migrations/0052_create_news_setting.py delete mode 100644 pydis_site/apps/api/migrations/0052_remove_user_avatar_hash.py create mode 100644 pydis_site/apps/api/migrations/0053_offtopicchannelname_used.py delete mode 100644 pydis_site/apps/api/migrations/0053_user_roles_to_array.py create mode 100644 pydis_site/apps/api/migrations/0054_remove_user_avatar_hash.py delete mode 100644 pydis_site/apps/api/migrations/0054_user_invalidate_unknown_role.py delete mode 100644 pydis_site/apps/api/migrations/0055_merge_20200714_2027.py delete mode 100644 pydis_site/apps/api/migrations/0055_reminder_mentions.py create mode 100644 pydis_site/apps/api/migrations/0055_user_roles_to_array.py delete mode 100644 pydis_site/apps/api/migrations/0056_allow_blank_user_roles.py create mode 100644 pydis_site/apps/api/migrations/0056_user_invalidate_unknown_role.py delete mode 100644 pydis_site/apps/api/migrations/0057_merge_20200716_0751.py create mode 100644 pydis_site/apps/api/migrations/0057_reminder_mentions.py create mode 100644 pydis_site/apps/api/migrations/0058_allow_blank_user_roles.py delete mode 100644 pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py create mode 100644 pydis_site/apps/api/migrations/0059_create_new_filterlist_model.py delete mode 100644 pydis_site/apps/api/migrations/0059_populate_filterlists.py create mode 100644 pydis_site/apps/api/migrations/0060_populate_filterlists.py delete mode 100644 pydis_site/apps/api/migrations/0060_populate_filterlists_fix.py create mode 100644 pydis_site/apps/api/migrations/0061_populate_filterlists_fix.py diff --git a/pydis_site/apps/api/migrations/0051_create_news_setting.py b/pydis_site/apps/api/migrations/0051_create_news_setting.py deleted file mode 100644 index f18fdfb1..00000000 --- a/pydis_site/apps/api/migrations/0051_create_news_setting.py +++ /dev/null @@ -1,25 +0,0 @@ -from django.db import migrations - - -def up(apps, schema_editor): - BotSetting = apps.get_model('api', 'BotSetting') - setting = BotSetting( - name='news', - data={} - ).save() - - -def down(apps, schema_editor): - BotSetting = apps.get_model('api', 'BotSetting') - BotSetting.objects.get(name='news').delete() - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0050_remove_infractions_active_default_value'), - ] - - operations = [ - migrations.RunPython(up, down) - ] diff --git a/pydis_site/apps/api/migrations/0052_create_news_setting.py b/pydis_site/apps/api/migrations/0052_create_news_setting.py new file mode 100644 index 00000000..b101d19d --- /dev/null +++ b/pydis_site/apps/api/migrations/0052_create_news_setting.py @@ -0,0 +1,25 @@ +from django.db import migrations + + +def up(apps, schema_editor): + BotSetting = apps.get_model('api', 'BotSetting') + setting = BotSetting( + name='news', + data={} + ).save() + + +def down(apps, schema_editor): + BotSetting = apps.get_model('api', 'BotSetting') + BotSetting.objects.get(name='news').delete() + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0051_allow_blank_message_embeds'), + ] + + operations = [ + migrations.RunPython(up, down) + ] diff --git a/pydis_site/apps/api/migrations/0052_remove_user_avatar_hash.py b/pydis_site/apps/api/migrations/0052_remove_user_avatar_hash.py deleted file mode 100644 index 26b3b954..00000000 --- a/pydis_site/apps/api/migrations/0052_remove_user_avatar_hash.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 2.2.11 on 2020-05-27 07:17 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0051_create_news_setting'), - ] - - operations = [ - migrations.RemoveField( - model_name='user', - name='avatar_hash', - ), - ] diff --git a/pydis_site/apps/api/migrations/0053_offtopicchannelname_used.py b/pydis_site/apps/api/migrations/0053_offtopicchannelname_used.py new file mode 100644 index 00000000..b51ce1d2 --- /dev/null +++ b/pydis_site/apps/api/migrations/0053_offtopicchannelname_used.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.11 on 2020-03-30 10:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0052_create_news_setting'), + ] + + operations = [ + migrations.AddField( + model_name='offtopicchannelname', + name='used', + field=models.BooleanField(default=False, help_text='Whether or not this name has already been used during this rotation'), + ), + ] diff --git a/pydis_site/apps/api/migrations/0053_user_roles_to_array.py b/pydis_site/apps/api/migrations/0053_user_roles_to_array.py deleted file mode 100644 index 7ff3a548..00000000 --- a/pydis_site/apps/api/migrations/0053_user_roles_to_array.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 2.2.11 on 2020-06-02 13:42 - -import django.contrib.postgres.fields -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0052_remove_user_avatar_hash'), - ] - - operations = [ - migrations.RemoveField( - model_name='user', - name='roles', - ), - migrations.AddField( - model_name='user', - name='roles', - field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.')]), default=list, help_text='IDs of roles the user has on the server', size=None), - ), - ] diff --git a/pydis_site/apps/api/migrations/0054_remove_user_avatar_hash.py b/pydis_site/apps/api/migrations/0054_remove_user_avatar_hash.py new file mode 100644 index 00000000..be9fd948 --- /dev/null +++ b/pydis_site/apps/api/migrations/0054_remove_user_avatar_hash.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2.11 on 2020-05-27 07:17 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0053_offtopicchannelname_used'), + ] + + operations = [ + migrations.RemoveField( + model_name='user', + name='avatar_hash', + ), + ] diff --git a/pydis_site/apps/api/migrations/0054_user_invalidate_unknown_role.py b/pydis_site/apps/api/migrations/0054_user_invalidate_unknown_role.py deleted file mode 100644 index 96230015..00000000 --- a/pydis_site/apps/api/migrations/0054_user_invalidate_unknown_role.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 2.2.11 on 2020-06-02 20:08 - -import django.contrib.postgres.fields -import django.core.validators -from django.db import migrations, models -import pydis_site.apps.api.models.bot.user - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0053_user_roles_to_array'), - ] - - operations = [ - migrations.AlterField( - model_name='user', - name='roles', - field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.'), pydis_site.apps.api.models.bot.user._validate_existing_role]), default=list, help_text='IDs of roles the user has on the server', size=None), - ), - ] diff --git a/pydis_site/apps/api/migrations/0055_merge_20200714_2027.py b/pydis_site/apps/api/migrations/0055_merge_20200714_2027.py deleted file mode 100644 index f2a0e638..00000000 --- a/pydis_site/apps/api/migrations/0055_merge_20200714_2027.py +++ /dev/null @@ -1,14 +0,0 @@ -# Generated by Django 3.0.8 on 2020-07-14 20:27 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0051_allow_blank_message_embeds'), - ('api', '0054_user_invalidate_unknown_role'), - ] - - operations = [ - ] diff --git a/pydis_site/apps/api/migrations/0055_reminder_mentions.py b/pydis_site/apps/api/migrations/0055_reminder_mentions.py deleted file mode 100644 index d73b450d..00000000 --- a/pydis_site/apps/api/migrations/0055_reminder_mentions.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 2.2.14 on 2020-07-15 07:37 - -import django.contrib.postgres.fields -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0054_user_invalidate_unknown_role'), - ] - - operations = [ - migrations.AddField( - model_name='reminder', - name='mentions', - field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Mention IDs cannot be negative.')]), blank=True, default=list, help_text='IDs of roles or users to ping with the reminder.', size=None), - ), - ] diff --git a/pydis_site/apps/api/migrations/0055_user_roles_to_array.py b/pydis_site/apps/api/migrations/0055_user_roles_to_array.py new file mode 100644 index 00000000..e7b4a983 --- /dev/null +++ b/pydis_site/apps/api/migrations/0055_user_roles_to_array.py @@ -0,0 +1,24 @@ +# Generated by Django 2.2.11 on 2020-06-02 13:42 + +import django.contrib.postgres.fields +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0054_remove_user_avatar_hash'), + ] + + operations = [ + migrations.RemoveField( + model_name='user', + name='roles', + ), + migrations.AddField( + model_name='user', + name='roles', + field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.')]), default=list, help_text='IDs of roles the user has on the server', size=None), + ), + ] diff --git a/pydis_site/apps/api/migrations/0056_allow_blank_user_roles.py b/pydis_site/apps/api/migrations/0056_allow_blank_user_roles.py deleted file mode 100644 index 489941c7..00000000 --- a/pydis_site/apps/api/migrations/0056_allow_blank_user_roles.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 3.0.8 on 2020-07-14 20:35 - -import django.contrib.postgres.fields -import django.core.validators -from django.db import migrations, models -import pydis_site.apps.api.models.bot.user - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0055_merge_20200714_2027'), - ] - - operations = [ - migrations.AlterField( - model_name='user', - name='roles', - field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.'), pydis_site.apps.api.models.bot.user._validate_existing_role]), blank=True, default=list, help_text='IDs of roles the user has on the server', size=None), - ), - ] diff --git a/pydis_site/apps/api/migrations/0056_user_invalidate_unknown_role.py b/pydis_site/apps/api/migrations/0056_user_invalidate_unknown_role.py new file mode 100644 index 00000000..ab2696aa --- /dev/null +++ b/pydis_site/apps/api/migrations/0056_user_invalidate_unknown_role.py @@ -0,0 +1,21 @@ +# Generated by Django 2.2.11 on 2020-06-02 20:08 + +import django.contrib.postgres.fields +import django.core.validators +from django.db import migrations, models +import pydis_site.apps.api.models.bot.user + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0055_user_roles_to_array'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='roles', + field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.'), pydis_site.apps.api.models.bot.user._validate_existing_role]), default=list, help_text='IDs of roles the user has on the server', size=None), + ), + ] diff --git a/pydis_site/apps/api/migrations/0057_merge_20200716_0751.py b/pydis_site/apps/api/migrations/0057_merge_20200716_0751.py deleted file mode 100644 index 47a6d2d4..00000000 --- a/pydis_site/apps/api/migrations/0057_merge_20200716_0751.py +++ /dev/null @@ -1,14 +0,0 @@ -# Generated by Django 2.2.14 on 2020-07-16 07:51 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0055_reminder_mentions'), - ('api', '0056_allow_blank_user_roles'), - ] - - operations = [ - ] diff --git a/pydis_site/apps/api/migrations/0057_reminder_mentions.py b/pydis_site/apps/api/migrations/0057_reminder_mentions.py new file mode 100644 index 00000000..fb829a17 --- /dev/null +++ b/pydis_site/apps/api/migrations/0057_reminder_mentions.py @@ -0,0 +1,20 @@ +# Generated by Django 2.2.14 on 2020-07-15 07:37 + +import django.contrib.postgres.fields +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0056_user_invalidate_unknown_role'), + ] + + operations = [ + migrations.AddField( + model_name='reminder', + name='mentions', + field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Mention IDs cannot be negative.')]), blank=True, default=list, help_text='IDs of roles or users to ping with the reminder.', size=None), + ), + ] diff --git a/pydis_site/apps/api/migrations/0058_allow_blank_user_roles.py b/pydis_site/apps/api/migrations/0058_allow_blank_user_roles.py new file mode 100644 index 00000000..8f7fddfc --- /dev/null +++ b/pydis_site/apps/api/migrations/0058_allow_blank_user_roles.py @@ -0,0 +1,21 @@ +# Generated by Django 3.0.8 on 2020-07-14 20:35 + +import django.contrib.postgres.fields +import django.core.validators +from django.db import migrations, models +import pydis_site.apps.api.models.bot.user + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0057_reminder_mentions'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='roles', + field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.'), pydis_site.apps.api.models.bot.user._validate_existing_role]), blank=True, default=list, help_text='IDs of roles the user has on the server', size=None), + ), + ] diff --git a/pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py b/pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py deleted file mode 100644 index aecfdad7..00000000 --- a/pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py +++ /dev/null @@ -1,33 +0,0 @@ -# Generated by Django 3.0.8 on 2020-07-15 11:23 - -from django.db import migrations, models -import pydis_site.apps.api.models.mixins - - -class Migration(migrations.Migration): - dependencies = [ - ('api', '0057_merge_20200716_0751'), - ] - - operations = [ - migrations.CreateModel( - name='FilterList', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('type', models.CharField( - choices=[('GUILD_INVITE', 'Guild Invite'), ('FILE_FORMAT', 'File Format'), - ('DOMAIN_NAME', 'Domain Name'), ('FILTER_TOKEN', 'Filter Token')], - help_text='The type of allowlist this is on.', max_length=50)), - ('allowed', models.BooleanField(help_text='Whether this item is on the allowlist or the denylist.')), - ('content', models.TextField(help_text='The data to add to the allow or denylist.')), - ('comment', models.TextField(help_text="Optional comment on this entry.", null=True)), - ], - bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), - ), - migrations.AddConstraint( - model_name='filterlist', - constraint=models.UniqueConstraint(fields=('content', 'type'), name='unique_filter_list') - ) - ] diff --git a/pydis_site/apps/api/migrations/0059_create_new_filterlist_model.py b/pydis_site/apps/api/migrations/0059_create_new_filterlist_model.py new file mode 100644 index 00000000..eac5542d --- /dev/null +++ b/pydis_site/apps/api/migrations/0059_create_new_filterlist_model.py @@ -0,0 +1,33 @@ +# Generated by Django 3.0.8 on 2020-07-15 11:23 + +from django.db import migrations, models +import pydis_site.apps.api.models.mixins + + +class Migration(migrations.Migration): + dependencies = [ + ('api', '0058_allow_blank_user_roles'), + ] + + operations = [ + migrations.CreateModel( + name='FilterList', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('type', models.CharField( + choices=[('GUILD_INVITE', 'Guild Invite'), ('FILE_FORMAT', 'File Format'), + ('DOMAIN_NAME', 'Domain Name'), ('FILTER_TOKEN', 'Filter Token')], + help_text='The type of allowlist this is on.', max_length=50)), + ('allowed', models.BooleanField(help_text='Whether this item is on the allowlist or the denylist.')), + ('content', models.TextField(help_text='The data to add to the allow or denylist.')), + ('comment', models.TextField(help_text="Optional comment on this entry.", null=True)), + ], + bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), + ), + migrations.AddConstraint( + model_name='filterlist', + constraint=models.UniqueConstraint(fields=('content', 'type'), name='unique_filter_list') + ) + ] diff --git a/pydis_site/apps/api/migrations/0059_populate_filterlists.py b/pydis_site/apps/api/migrations/0059_populate_filterlists.py deleted file mode 100644 index 8c550191..00000000 --- a/pydis_site/apps/api/migrations/0059_populate_filterlists.py +++ /dev/null @@ -1,153 +0,0 @@ -from django.db import migrations - -guild_invite_whitelist = [ - ("discord.gg/python", "Python Discord", True), - ("discord.gg/4JJdJKb", "RLBot", True), - ("discord.gg/djPtTRJ", "Kivy", True), - ("discord.gg/QXyegWe", "Pyglet", True), - ("discord.gg/9XsucTT", "Panda3D", True), - ("discord.gg/AP3rq2k", "PyWeek", True), - ("discord.gg/vSPsP9t", "Microsoft Python", True), - ("discord.gg/bRCvFy9", "Discord.js Official", True), - ("discord.gg/9zT7NHP", "Programming Discussions", True), - ("discord.gg/ysd6M4r", "JetBrains Community", True), - ("discord.gg/4xJeCgy", "Raspberry Pie", True), - ("discord.gg/AStb3kZ", "Ren'Py", True), - ("discord.gg/t655QNV", "Python Discord: Emojis 1", True), - ("discord.gg/vRZPkqC", "Python Discord: Emojis 2", True), - ("discord.gg/jTtgWuy", "Django", True), - ("discord.gg/W9BypZF", "STEM", True), - ("discord.gg/dpy", "discord.py", True), - ("discord.gg/programming", "Programmers Hangout", True), - ("discord.gg/qhGUjGD", "SpeakJS", True), - ("discord.gg/eTbWSZj", "Functional Programming", True), - ("discord.gg/r8yreB6", "PyGame", True), - ("discord.gg/5UBnR3P", "Python Atlanta", True), - ("discord.gg/ccyrDKv", "C#", True), -] - -domain_name_blacklist = [ - ("pornhub.com", None, False), - ("liveleak.com", None, False), - ("grabify.link", None, False), - ("bmwforum.co", None, False), - ("leancoding.co", None, False), - ("spottyfly.com", None, False), - ("stopify.co", None, False), - ("yoütu.be", None, False), - ("discörd.com", None, False), - ("minecräft.com", None, False), - ("freegiftcards.co", None, False), - ("disçordapp.com", None, False), - ("fortnight.space", None, False), - ("fortnitechat.site", None, False), - ("joinmy.site", None, False), - ("curiouscat.club", None, False), - ("catsnthings.fun", None, False), - ("yourtube.site", None, False), - ("youtubeshort.watch", None, False), - ("catsnthing.com", None, False), - ("youtubeshort.pro", None, False), - ("canadianlumberjacks.online", None, False), - ("poweredbydialup.club", None, False), - ("poweredbydialup.online", None, False), - ("poweredbysecurity.org", None, False), - ("poweredbysecurity.online", None, False), - ("ssteam.site", None, False), - ("steamwalletgift.com", None, False), - ("discord.gift", None, False), - ("lmgtfy.com", None, False), -] - -filter_token_blacklist = [ - ("\bgoo+ks*\b", None, False), - ("\bky+s+\b", None, False), - ("\bki+ke+s*\b", None, False), - ("\bbeaner+s?\b", None, False), - ("\bcoo+ns*\b", None, False), - ("\bnig+lets*\b", None, False), - ("\bslant-eyes*\b", None, False), - ("\btowe?l-?head+s*\b", None, False), - ("\bchi*n+k+s*\b", None, False), - ("\bspick*s*\b", None, False), - ("\bkill* +(?:yo)?urself+\b", None, False), - ("\bjew+s*\b", None, False), - ("\bsuicide\b", None, False), - ("\brape\b", None, False), - ("\b(re+)tar+(d+|t+)(ed)?\b", None, False), - ("\bta+r+d+\b", None, False), - ("\bcunts*\b", None, False), - ("\btrann*y\b", None, False), - ("\bshemale\b", None, False), - ("fa+g+s*", None, False), - ("卐", None, False), - ("卍", None, False), - ("࿖", None, False), - ("࿕", None, False), - ("࿘", None, False), - ("࿗", None, False), - ("cuck(?!oo+)", None, False), - ("nigg+(?:e*r+|a+h*?|u+h+)s?", None, False), - ("fag+o+t+s*", None, False), -] - -file_format_whitelist = [ - (".3gp", None, True), - (".3g2", None, True), - (".avi", None, True), - (".bmp", None, True), - (".gif", None, True), - (".h264", None, True), - (".jpg", None, True), - (".jpeg", None, True), - (".m4v", None, True), - (".mkv", None, True), - (".mov", None, True), - (".mp4", None, True), - (".mpeg", None, True), - (".mpg", None, True), - (".png", None, True), - (".tiff", None, True), - (".wmv", None, True), - (".svg", None, True), - (".psd", "Photoshop", True), - (".ai", "Illustrator", True), - (".aep", "After Effects", True), - (".xcf", "GIMP", True), - (".mp3", None, True), - (".wav", None, True), - (".ogg", None, True), - (".webm", None, True), - (".webp", None, True), -] - -populate_data = { - "FILTER_TOKEN": filter_token_blacklist, - "DOMAIN_NAME": domain_name_blacklist, - "FILE_FORMAT": file_format_whitelist, - "GUILD_INVITE": guild_invite_whitelist, -} - - -class Migration(migrations.Migration): - dependencies = [("api", "0058_create_new_filterlist_model")] - - def populate_filterlists(app, _): - FilterList = app.get_model("api", "FilterList") - - for filterlist_type, metadata in populate_data.items(): - for content, comment, allowed in metadata: - FilterList.objects.create( - type=filterlist_type, - allowed=allowed, - content=content, - comment=comment, - ) - - def clear_filterlists(app, _): - FilterList = app.get_model("api", "FilterList") - FilterList.objects.all().delete() - - operations = [ - migrations.RunPython(populate_filterlists, clear_filterlists) - ] diff --git a/pydis_site/apps/api/migrations/0060_populate_filterlists.py b/pydis_site/apps/api/migrations/0060_populate_filterlists.py new file mode 100644 index 00000000..35fde95a --- /dev/null +++ b/pydis_site/apps/api/migrations/0060_populate_filterlists.py @@ -0,0 +1,153 @@ +from django.db import migrations + +guild_invite_whitelist = [ + ("discord.gg/python", "Python Discord", True), + ("discord.gg/4JJdJKb", "RLBot", True), + ("discord.gg/djPtTRJ", "Kivy", True), + ("discord.gg/QXyegWe", "Pyglet", True), + ("discord.gg/9XsucTT", "Panda3D", True), + ("discord.gg/AP3rq2k", "PyWeek", True), + ("discord.gg/vSPsP9t", "Microsoft Python", True), + ("discord.gg/bRCvFy9", "Discord.js Official", True), + ("discord.gg/9zT7NHP", "Programming Discussions", True), + ("discord.gg/ysd6M4r", "JetBrains Community", True), + ("discord.gg/4xJeCgy", "Raspberry Pie", True), + ("discord.gg/AStb3kZ", "Ren'Py", True), + ("discord.gg/t655QNV", "Python Discord: Emojis 1", True), + ("discord.gg/vRZPkqC", "Python Discord: Emojis 2", True), + ("discord.gg/jTtgWuy", "Django", True), + ("discord.gg/W9BypZF", "STEM", True), + ("discord.gg/dpy", "discord.py", True), + ("discord.gg/programming", "Programmers Hangout", True), + ("discord.gg/qhGUjGD", "SpeakJS", True), + ("discord.gg/eTbWSZj", "Functional Programming", True), + ("discord.gg/r8yreB6", "PyGame", True), + ("discord.gg/5UBnR3P", "Python Atlanta", True), + ("discord.gg/ccyrDKv", "C#", True), +] + +domain_name_blacklist = [ + ("pornhub.com", None, False), + ("liveleak.com", None, False), + ("grabify.link", None, False), + ("bmwforum.co", None, False), + ("leancoding.co", None, False), + ("spottyfly.com", None, False), + ("stopify.co", None, False), + ("yoütu.be", None, False), + ("discörd.com", None, False), + ("minecräft.com", None, False), + ("freegiftcards.co", None, False), + ("disçordapp.com", None, False), + ("fortnight.space", None, False), + ("fortnitechat.site", None, False), + ("joinmy.site", None, False), + ("curiouscat.club", None, False), + ("catsnthings.fun", None, False), + ("yourtube.site", None, False), + ("youtubeshort.watch", None, False), + ("catsnthing.com", None, False), + ("youtubeshort.pro", None, False), + ("canadianlumberjacks.online", None, False), + ("poweredbydialup.club", None, False), + ("poweredbydialup.online", None, False), + ("poweredbysecurity.org", None, False), + ("poweredbysecurity.online", None, False), + ("ssteam.site", None, False), + ("steamwalletgift.com", None, False), + ("discord.gift", None, False), + ("lmgtfy.com", None, False), +] + +filter_token_blacklist = [ + ("\bgoo+ks*\b", None, False), + ("\bky+s+\b", None, False), + ("\bki+ke+s*\b", None, False), + ("\bbeaner+s?\b", None, False), + ("\bcoo+ns*\b", None, False), + ("\bnig+lets*\b", None, False), + ("\bslant-eyes*\b", None, False), + ("\btowe?l-?head+s*\b", None, False), + ("\bchi*n+k+s*\b", None, False), + ("\bspick*s*\b", None, False), + ("\bkill* +(?:yo)?urself+\b", None, False), + ("\bjew+s*\b", None, False), + ("\bsuicide\b", None, False), + ("\brape\b", None, False), + ("\b(re+)tar+(d+|t+)(ed)?\b", None, False), + ("\bta+r+d+\b", None, False), + ("\bcunts*\b", None, False), + ("\btrann*y\b", None, False), + ("\bshemale\b", None, False), + ("fa+g+s*", None, False), + ("卐", None, False), + ("卍", None, False), + ("࿖", None, False), + ("࿕", None, False), + ("࿘", None, False), + ("࿗", None, False), + ("cuck(?!oo+)", None, False), + ("nigg+(?:e*r+|a+h*?|u+h+)s?", None, False), + ("fag+o+t+s*", None, False), +] + +file_format_whitelist = [ + (".3gp", None, True), + (".3g2", None, True), + (".avi", None, True), + (".bmp", None, True), + (".gif", None, True), + (".h264", None, True), + (".jpg", None, True), + (".jpeg", None, True), + (".m4v", None, True), + (".mkv", None, True), + (".mov", None, True), + (".mp4", None, True), + (".mpeg", None, True), + (".mpg", None, True), + (".png", None, True), + (".tiff", None, True), + (".wmv", None, True), + (".svg", None, True), + (".psd", "Photoshop", True), + (".ai", "Illustrator", True), + (".aep", "After Effects", True), + (".xcf", "GIMP", True), + (".mp3", None, True), + (".wav", None, True), + (".ogg", None, True), + (".webm", None, True), + (".webp", None, True), +] + +populate_data = { + "FILTER_TOKEN": filter_token_blacklist, + "DOMAIN_NAME": domain_name_blacklist, + "FILE_FORMAT": file_format_whitelist, + "GUILD_INVITE": guild_invite_whitelist, +} + + +class Migration(migrations.Migration): + dependencies = [("api", "0059_create_new_filterlist_model")] + + def populate_filterlists(app, _): + FilterList = app.get_model("api", "FilterList") + + for filterlist_type, metadata in populate_data.items(): + for content, comment, allowed in metadata: + FilterList.objects.create( + type=filterlist_type, + allowed=allowed, + content=content, + comment=comment, + ) + + def clear_filterlists(app, _): + FilterList = app.get_model("api", "FilterList") + FilterList.objects.all().delete() + + operations = [ + migrations.RunPython(populate_filterlists, clear_filterlists) + ] diff --git a/pydis_site/apps/api/migrations/0060_populate_filterlists_fix.py b/pydis_site/apps/api/migrations/0060_populate_filterlists_fix.py deleted file mode 100644 index 53846f02..00000000 --- a/pydis_site/apps/api/migrations/0060_populate_filterlists_fix.py +++ /dev/null @@ -1,85 +0,0 @@ -from django.db import migrations - -bad_guild_invite_whitelist = [ - ("discord.gg/python", "Python Discord", True), - ("discord.gg/4JJdJKb", "RLBot", True), - ("discord.gg/djPtTRJ", "Kivy", True), - ("discord.gg/QXyegWe", "Pyglet", True), - ("discord.gg/9XsucTT", "Panda3D", True), - ("discord.gg/AP3rq2k", "PyWeek", True), - ("discord.gg/vSPsP9t", "Microsoft Python", True), - ("discord.gg/bRCvFy9", "Discord.js Official", True), - ("discord.gg/9zT7NHP", "Programming Discussions", True), - ("discord.gg/ysd6M4r", "JetBrains Community", True), - ("discord.gg/4xJeCgy", "Raspberry Pie", True), - ("discord.gg/AStb3kZ", "Ren'Py", True), - ("discord.gg/t655QNV", "Python Discord: Emojis 1", True), - ("discord.gg/vRZPkqC", "Python Discord: Emojis 2", True), - ("discord.gg/jTtgWuy", "Django", True), - ("discord.gg/W9BypZF", "STEM", True), - ("discord.gg/dpy", "discord.py", True), - ("discord.gg/programming", "Programmers Hangout", True), - ("discord.gg/qhGUjGD", "SpeakJS", True), - ("discord.gg/eTbWSZj", "Functional Programming", True), - ("discord.gg/r8yreB6", "PyGame", True), - ("discord.gg/5UBnR3P", "Python Atlanta", True), - ("discord.gg/ccyrDKv", "C#", True), -] - -guild_invite_whitelist = [ - ("267624335836053506", "Python Discord", True), - ("348658686962696195", "RLBot", True), - ("423249981340778496", "Kivy", True), - ("438622377094414346", "Pyglet", True), - ("524691714909274162", "Panda3D", True), - ("666560367173828639", "PyWeek", True), - ("702724176489873509", "Microsoft Python", True), - ("222078108977594368", "Discord.js Official", True), - ("238666723824238602", "Programming Discussions", True), - ("433980600391696384", "JetBrains Community", True), - ("204621105720328193", "Raspberry Pie", True), - ("286633898581164032", "Ren'Py", True), - ("440186186024222721", "Python Discord: Emojis 1", True), - ("578587418123304970", "Python Discord: Emojis 2", True), - ("159039020565790721", "Django", True), - ("273944235143593984", "STEM", True), - ("336642139381301249", "discord.py", True), - ("244230771232079873", "Programmers Hangout", True), - ("239433591950540801", "SpeakJS", True), - ("280033776820813825", "Functional Programming", True), - ("349505959032389632", "PyGame", True), - ("488751051629920277", "Python Atlanta", True), - ("143867839282020352", "C#", True), -] - - -class Migration(migrations.Migration): - dependencies = [("api", "0059_populate_filterlists")] - - def fix_filterlist(app, _): - FilterList = app.get_model("api", "FilterList") - FilterList.objects.filter(type="GUILD_INVITE").delete() # Clear out the stuff added in 0059. - - for content, comment, allowed in guild_invite_whitelist: - FilterList.objects.create( - type="GUILD_INVITE", - allowed=allowed, - content=content, - comment=comment, - ) - - def restore_bad_filterlist(app, _): - FilterList = app.get_model("api", "FilterList") - FilterList.objects.filter(type="GUILD_INVITE").delete() - - for content, comment, allowed in bad_guild_invite_whitelist: - FilterList.objects.create( - type="GUILD_INVITE", - allowed=allowed, - content=content, - comment=comment, - ) - - operations = [ - migrations.RunPython(fix_filterlist, restore_bad_filterlist) - ] diff --git a/pydis_site/apps/api/migrations/0061_populate_filterlists_fix.py b/pydis_site/apps/api/migrations/0061_populate_filterlists_fix.py new file mode 100644 index 00000000..eaaafb38 --- /dev/null +++ b/pydis_site/apps/api/migrations/0061_populate_filterlists_fix.py @@ -0,0 +1,85 @@ +from django.db import migrations + +bad_guild_invite_whitelist = [ + ("discord.gg/python", "Python Discord", True), + ("discord.gg/4JJdJKb", "RLBot", True), + ("discord.gg/djPtTRJ", "Kivy", True), + ("discord.gg/QXyegWe", "Pyglet", True), + ("discord.gg/9XsucTT", "Panda3D", True), + ("discord.gg/AP3rq2k", "PyWeek", True), + ("discord.gg/vSPsP9t", "Microsoft Python", True), + ("discord.gg/bRCvFy9", "Discord.js Official", True), + ("discord.gg/9zT7NHP", "Programming Discussions", True), + ("discord.gg/ysd6M4r", "JetBrains Community", True), + ("discord.gg/4xJeCgy", "Raspberry Pie", True), + ("discord.gg/AStb3kZ", "Ren'Py", True), + ("discord.gg/t655QNV", "Python Discord: Emojis 1", True), + ("discord.gg/vRZPkqC", "Python Discord: Emojis 2", True), + ("discord.gg/jTtgWuy", "Django", True), + ("discord.gg/W9BypZF", "STEM", True), + ("discord.gg/dpy", "discord.py", True), + ("discord.gg/programming", "Programmers Hangout", True), + ("discord.gg/qhGUjGD", "SpeakJS", True), + ("discord.gg/eTbWSZj", "Functional Programming", True), + ("discord.gg/r8yreB6", "PyGame", True), + ("discord.gg/5UBnR3P", "Python Atlanta", True), + ("discord.gg/ccyrDKv", "C#", True), +] + +guild_invite_whitelist = [ + ("267624335836053506", "Python Discord", True), + ("348658686962696195", "RLBot", True), + ("423249981340778496", "Kivy", True), + ("438622377094414346", "Pyglet", True), + ("524691714909274162", "Panda3D", True), + ("666560367173828639", "PyWeek", True), + ("702724176489873509", "Microsoft Python", True), + ("222078108977594368", "Discord.js Official", True), + ("238666723824238602", "Programming Discussions", True), + ("433980600391696384", "JetBrains Community", True), + ("204621105720328193", "Raspberry Pie", True), + ("286633898581164032", "Ren'Py", True), + ("440186186024222721", "Python Discord: Emojis 1", True), + ("578587418123304970", "Python Discord: Emojis 2", True), + ("159039020565790721", "Django", True), + ("273944235143593984", "STEM", True), + ("336642139381301249", "discord.py", True), + ("244230771232079873", "Programmers Hangout", True), + ("239433591950540801", "SpeakJS", True), + ("280033776820813825", "Functional Programming", True), + ("349505959032389632", "PyGame", True), + ("488751051629920277", "Python Atlanta", True), + ("143867839282020352", "C#", True), +] + + +class Migration(migrations.Migration): + dependencies = [("api", "0060_populate_filterlists")] + + def fix_filterlist(app, _): + FilterList = app.get_model("api", "FilterList") + FilterList.objects.filter(type="GUILD_INVITE").delete() # Clear out the stuff added in 0059. + + for content, comment, allowed in guild_invite_whitelist: + FilterList.objects.create( + type="GUILD_INVITE", + allowed=allowed, + content=content, + comment=comment, + ) + + def restore_bad_filterlist(app, _): + FilterList = app.get_model("api", "FilterList") + FilterList.objects.filter(type="GUILD_INVITE").delete() + + for content, comment, allowed in bad_guild_invite_whitelist: + FilterList.objects.create( + type="GUILD_INVITE", + allowed=allowed, + content=content, + comment=comment, + ) + + operations = [ + migrations.RunPython(fix_filterlist, restore_bad_filterlist) + ] diff --git a/pydis_site/apps/api/models/bot/off_topic_channel_name.py b/pydis_site/apps/api/models/bot/off_topic_channel_name.py index 20e77b9f..403c7465 100644 --- a/pydis_site/apps/api/models/bot/off_topic_channel_name.py +++ b/pydis_site/apps/api/models/bot/off_topic_channel_name.py @@ -16,6 +16,11 @@ class OffTopicChannelName(ModelReprMixin, models.Model): help_text="The actual channel name that will be used on our Discord server." ) + used = models.BooleanField( + default=False, + help_text="Whether or not this name has already been used during this rotation", + ) + def __str__(self): """Returns the current off-topic name, for display purposes.""" return self.name diff --git a/pydis_site/apps/api/tests/test_off_topic_channel_names.py b/pydis_site/apps/api/tests/test_off_topic_channel_names.py index bd42cd81..3ab8b22d 100644 --- a/pydis_site/apps/api/tests/test_off_topic_channel_names.py +++ b/pydis_site/apps/api/tests/test_off_topic_channel_names.py @@ -10,12 +10,14 @@ class UnauthenticatedTests(APISubdomainTestCase): self.client.force_authenticate(user=None) def test_cannot_read_off_topic_channel_name_list(self): + """Return a 401 response when not authenticated.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(url) self.assertEqual(response.status_code, 401) def test_cannot_read_off_topic_channel_name_list_with_random_item_param(self): + """Return a 401 response when `random_items` provided and not authenticated.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(f'{url}?random_items=no') @@ -24,6 +26,7 @@ class UnauthenticatedTests(APISubdomainTestCase): class EmptyDatabaseTests(APISubdomainTestCase): def test_returns_empty_object(self): + """Return empty list when no names in database.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(url) @@ -31,6 +34,7 @@ class EmptyDatabaseTests(APISubdomainTestCase): self.assertEqual(response.json(), []) def test_returns_empty_list_with_get_all_param(self): + """Return empty list when no names and `random_items` param provided.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(f'{url}?random_items=5') @@ -38,6 +42,7 @@ class EmptyDatabaseTests(APISubdomainTestCase): self.assertEqual(response.json(), []) def test_returns_400_for_bad_random_items_param(self): + """Return error message when passing not integer as `random_items`.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(f'{url}?random_items=totally-a-valid-integer') @@ -47,6 +52,7 @@ class EmptyDatabaseTests(APISubdomainTestCase): }) def test_returns_400_for_negative_random_items_param(self): + """Return error message when passing negative int as `random_items`.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(f'{url}?random_items=-5') @@ -59,10 +65,11 @@ class EmptyDatabaseTests(APISubdomainTestCase): class ListTests(APISubdomainTestCase): @classmethod def setUpTestData(cls): - cls.test_name = OffTopicChannelName.objects.create(name='lemons-lemonade-stand') - cls.test_name_2 = OffTopicChannelName.objects.create(name='bbq-with-bisk') + cls.test_name = OffTopicChannelName.objects.create(name='lemons-lemonade-stand', used=False) + cls.test_name_2 = OffTopicChannelName.objects.create(name='bbq-with-bisk', used=True) def test_returns_name_in_list(self): + """Return all off-topic channel names.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(url) @@ -76,11 +83,21 @@ class ListTests(APISubdomainTestCase): ) def test_returns_single_item_with_random_items_param_set_to_1(self): + """Return not-used name instead used.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.get(f'{url}?random_items=1') self.assertEqual(response.status_code, 200) self.assertEqual(len(response.json()), 1) + self.assertEqual(response.json(), [self.test_name.name]) + + def test_running_out_of_names_with_random_parameter(self): + """Reset names `used` parameter to `False` when running out of names.""" + url = reverse('bot:offtopicchannelname-list', host='api') + response = self.client.get(f'{url}?random_items=2') + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json(), [self.test_name.name, self.test_name_2.name]) class CreationTests(APISubdomainTestCase): @@ -93,6 +110,7 @@ class CreationTests(APISubdomainTestCase): self.assertEqual(response.status_code, 201) def test_returns_201_for_unicode_chars(self): + """Accept all valid characters.""" url = reverse('bot:offtopicchannelname-list', host='api') names = ( '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹', @@ -104,6 +122,7 @@ class CreationTests(APISubdomainTestCase): self.assertEqual(response.status_code, 201) def test_returns_400_for_missing_name_param(self): + """Return error message when name not provided.""" url = reverse('bot:offtopicchannelname-list', host='api') response = self.client.post(url) self.assertEqual(response.status_code, 400) @@ -112,6 +131,7 @@ class CreationTests(APISubdomainTestCase): }) def test_returns_400_for_bad_name_param(self): + """Return error message when invalid characters provided.""" url = reverse('bot:offtopicchannelname-list', host='api') invalid_names = ( 'space between words', @@ -134,18 +154,21 @@ class DeletionTests(APISubdomainTestCase): cls.test_name_2 = OffTopicChannelName.objects.create(name='bbq-with-bisk') def test_deleting_unknown_name_returns_404(self): + """Return 404 reponse when trying to delete unknown name.""" url = reverse('bot:offtopicchannelname-detail', args=('unknown-name',), host='api') response = self.client.delete(url) self.assertEqual(response.status_code, 404) def test_deleting_known_name_returns_204(self): + """Return 204 response when deleting was successful.""" url = reverse('bot:offtopicchannelname-detail', args=(self.test_name.name,), host='api') response = self.client.delete(url) self.assertEqual(response.status_code, 204) def test_name_gets_deleted(self): + """Name gets actually deleted.""" url = reverse('bot:offtopicchannelname-detail', args=(self.test_name_2.name,), host='api') response = self.client.delete(url) diff --git a/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py b/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py index d6da2399..826ad25e 100644 --- a/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py +++ b/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py @@ -1,3 +1,4 @@ +from django.db.models import Case, Value, When from django.db.models.query import QuerySet from django.http.request import HttpRequest from django.shortcuts import get_object_or_404 @@ -20,7 +21,9 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet): Return all known off-topic channel names from the database. If the `random_items` query parameter is given, for example using... $ curl api.pythondiscord.local:8000/bot/off-topic-channel-names?random_items=5 - ... then the API will return `5` random items from the database. + ... then the API will return `5` random items from the database + that is not used in current rotation. + When running out of names, API will mark all names to not used and start new rotation. #### Response format Return a list of off-topic-channel names: @@ -106,7 +109,27 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet): 'random_items': ["Must be a positive integer."] }) - queryset = self.get_queryset().order_by('?')[:random_count] + queryset = self.get_queryset().order_by('used', '?')[:random_count] + + # When any name is used in our listing then this means we reached end of round + # and we need to reset all other names `used` to False + if any(offtopic_name.used for offtopic_name in queryset): + # These names that we just got have to be excluded from updating used to False + self.get_queryset().update( + used=Case( + When( + name__in=(offtopic_name.name for offtopic_name in queryset), + then=Value(True) + ), + default=Value(False) + ) + ) + else: + # Otherwise mark selected names `used` to True + self.get_queryset().filter( + name__in=(offtopic_name.name for offtopic_name in queryset) + ).update(used=True) + serialized = self.serializer_class(queryset, many=True) return Response(serialized.data) -- cgit v1.2.3 From 0ec3148228d7c13a35626059cffdad9dbb0670c2 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sun, 30 Aug 2020 08:21:21 +0300 Subject: Revert "Fix mess with migrations" This reverts commit 32342aca3309b4fd482841cb84e4f166152d5c33. --- .../api/migrations/0051_create_news_setting.py | 25 ++++ .../api/migrations/0052_create_news_setting.py | 25 ---- .../migrations/0052_offtopicchannelname_used.py | 18 +++ .../api/migrations/0052_remove_user_avatar_hash.py | 17 +++ .../migrations/0053_offtopicchannelname_used.py | 18 --- .../api/migrations/0053_user_roles_to_array.py | 24 ++++ .../api/migrations/0054_remove_user_avatar_hash.py | 17 --- .../0054_user_invalidate_unknown_role.py | 21 +++ .../api/migrations/0055_merge_20200714_2027.py | 14 ++ .../apps/api/migrations/0055_reminder_mentions.py | 20 +++ .../api/migrations/0055_user_roles_to_array.py | 24 ---- .../api/migrations/0056_allow_blank_user_roles.py | 21 +++ .../0056_user_invalidate_unknown_role.py | 21 --- .../api/migrations/0057_merge_20200716_0751.py | 14 ++ .../apps/api/migrations/0057_reminder_mentions.py | 20 --- .../api/migrations/0058_allow_blank_user_roles.py | 21 --- .../migrations/0058_create_new_filterlist_model.py | 33 +++++ .../migrations/0059_create_new_filterlist_model.py | 33 ----- .../api/migrations/0059_populate_filterlists.py | 153 +++++++++++++++++++++ .../api/migrations/0060_populate_filterlists.py | 153 --------------------- .../migrations/0060_populate_filterlists_fix.py | 85 ++++++++++++ .../migrations/0061_populate_filterlists_fix.py | 85 ------------ 22 files changed, 445 insertions(+), 417 deletions(-) create mode 100644 pydis_site/apps/api/migrations/0051_create_news_setting.py delete mode 100644 pydis_site/apps/api/migrations/0052_create_news_setting.py create mode 100644 pydis_site/apps/api/migrations/0052_offtopicchannelname_used.py create mode 100644 pydis_site/apps/api/migrations/0052_remove_user_avatar_hash.py delete mode 100644 pydis_site/apps/api/migrations/0053_offtopicchannelname_used.py create mode 100644 pydis_site/apps/api/migrations/0053_user_roles_to_array.py delete mode 100644 pydis_site/apps/api/migrations/0054_remove_user_avatar_hash.py create mode 100644 pydis_site/apps/api/migrations/0054_user_invalidate_unknown_role.py create mode 100644 pydis_site/apps/api/migrations/0055_merge_20200714_2027.py create mode 100644 pydis_site/apps/api/migrations/0055_reminder_mentions.py delete mode 100644 pydis_site/apps/api/migrations/0055_user_roles_to_array.py create mode 100644 pydis_site/apps/api/migrations/0056_allow_blank_user_roles.py delete mode 100644 pydis_site/apps/api/migrations/0056_user_invalidate_unknown_role.py create mode 100644 pydis_site/apps/api/migrations/0057_merge_20200716_0751.py delete mode 100644 pydis_site/apps/api/migrations/0057_reminder_mentions.py delete mode 100644 pydis_site/apps/api/migrations/0058_allow_blank_user_roles.py create mode 100644 pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py delete mode 100644 pydis_site/apps/api/migrations/0059_create_new_filterlist_model.py create mode 100644 pydis_site/apps/api/migrations/0059_populate_filterlists.py delete mode 100644 pydis_site/apps/api/migrations/0060_populate_filterlists.py create mode 100644 pydis_site/apps/api/migrations/0060_populate_filterlists_fix.py delete mode 100644 pydis_site/apps/api/migrations/0061_populate_filterlists_fix.py diff --git a/pydis_site/apps/api/migrations/0051_create_news_setting.py b/pydis_site/apps/api/migrations/0051_create_news_setting.py new file mode 100644 index 00000000..f18fdfb1 --- /dev/null +++ b/pydis_site/apps/api/migrations/0051_create_news_setting.py @@ -0,0 +1,25 @@ +from django.db import migrations + + +def up(apps, schema_editor): + BotSetting = apps.get_model('api', 'BotSetting') + setting = BotSetting( + name='news', + data={} + ).save() + + +def down(apps, schema_editor): + BotSetting = apps.get_model('api', 'BotSetting') + BotSetting.objects.get(name='news').delete() + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0050_remove_infractions_active_default_value'), + ] + + operations = [ + migrations.RunPython(up, down) + ] diff --git a/pydis_site/apps/api/migrations/0052_create_news_setting.py b/pydis_site/apps/api/migrations/0052_create_news_setting.py deleted file mode 100644 index b101d19d..00000000 --- a/pydis_site/apps/api/migrations/0052_create_news_setting.py +++ /dev/null @@ -1,25 +0,0 @@ -from django.db import migrations - - -def up(apps, schema_editor): - BotSetting = apps.get_model('api', 'BotSetting') - setting = BotSetting( - name='news', - data={} - ).save() - - -def down(apps, schema_editor): - BotSetting = apps.get_model('api', 'BotSetting') - BotSetting.objects.get(name='news').delete() - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0051_allow_blank_message_embeds'), - ] - - operations = [ - migrations.RunPython(up, down) - ] diff --git a/pydis_site/apps/api/migrations/0052_offtopicchannelname_used.py b/pydis_site/apps/api/migrations/0052_offtopicchannelname_used.py new file mode 100644 index 00000000..dfdf3835 --- /dev/null +++ b/pydis_site/apps/api/migrations/0052_offtopicchannelname_used.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.11 on 2020-03-30 10:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0051_create_news_setting'), + ] + + operations = [ + migrations.AddField( + model_name='offtopicchannelname', + name='used', + field=models.BooleanField(default=False, help_text='Whether or not this name has already been used during this rotation'), + ), + ] diff --git a/pydis_site/apps/api/migrations/0052_remove_user_avatar_hash.py b/pydis_site/apps/api/migrations/0052_remove_user_avatar_hash.py new file mode 100644 index 00000000..26b3b954 --- /dev/null +++ b/pydis_site/apps/api/migrations/0052_remove_user_avatar_hash.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2.11 on 2020-05-27 07:17 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0051_create_news_setting'), + ] + + operations = [ + migrations.RemoveField( + model_name='user', + name='avatar_hash', + ), + ] diff --git a/pydis_site/apps/api/migrations/0053_offtopicchannelname_used.py b/pydis_site/apps/api/migrations/0053_offtopicchannelname_used.py deleted file mode 100644 index b51ce1d2..00000000 --- a/pydis_site/apps/api/migrations/0053_offtopicchannelname_used.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.11 on 2020-03-30 10:24 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0052_create_news_setting'), - ] - - operations = [ - migrations.AddField( - model_name='offtopicchannelname', - name='used', - field=models.BooleanField(default=False, help_text='Whether or not this name has already been used during this rotation'), - ), - ] diff --git a/pydis_site/apps/api/migrations/0053_user_roles_to_array.py b/pydis_site/apps/api/migrations/0053_user_roles_to_array.py new file mode 100644 index 00000000..7ff3a548 --- /dev/null +++ b/pydis_site/apps/api/migrations/0053_user_roles_to_array.py @@ -0,0 +1,24 @@ +# Generated by Django 2.2.11 on 2020-06-02 13:42 + +import django.contrib.postgres.fields +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0052_remove_user_avatar_hash'), + ] + + operations = [ + migrations.RemoveField( + model_name='user', + name='roles', + ), + migrations.AddField( + model_name='user', + name='roles', + field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.')]), default=list, help_text='IDs of roles the user has on the server', size=None), + ), + ] diff --git a/pydis_site/apps/api/migrations/0054_remove_user_avatar_hash.py b/pydis_site/apps/api/migrations/0054_remove_user_avatar_hash.py deleted file mode 100644 index be9fd948..00000000 --- a/pydis_site/apps/api/migrations/0054_remove_user_avatar_hash.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 2.2.11 on 2020-05-27 07:17 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0053_offtopicchannelname_used'), - ] - - operations = [ - migrations.RemoveField( - model_name='user', - name='avatar_hash', - ), - ] diff --git a/pydis_site/apps/api/migrations/0054_user_invalidate_unknown_role.py b/pydis_site/apps/api/migrations/0054_user_invalidate_unknown_role.py new file mode 100644 index 00000000..96230015 --- /dev/null +++ b/pydis_site/apps/api/migrations/0054_user_invalidate_unknown_role.py @@ -0,0 +1,21 @@ +# Generated by Django 2.2.11 on 2020-06-02 20:08 + +import django.contrib.postgres.fields +import django.core.validators +from django.db import migrations, models +import pydis_site.apps.api.models.bot.user + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0053_user_roles_to_array'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='roles', + field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.'), pydis_site.apps.api.models.bot.user._validate_existing_role]), default=list, help_text='IDs of roles the user has on the server', size=None), + ), + ] diff --git a/pydis_site/apps/api/migrations/0055_merge_20200714_2027.py b/pydis_site/apps/api/migrations/0055_merge_20200714_2027.py new file mode 100644 index 00000000..f2a0e638 --- /dev/null +++ b/pydis_site/apps/api/migrations/0055_merge_20200714_2027.py @@ -0,0 +1,14 @@ +# Generated by Django 3.0.8 on 2020-07-14 20:27 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0051_allow_blank_message_embeds'), + ('api', '0054_user_invalidate_unknown_role'), + ] + + operations = [ + ] diff --git a/pydis_site/apps/api/migrations/0055_reminder_mentions.py b/pydis_site/apps/api/migrations/0055_reminder_mentions.py new file mode 100644 index 00000000..d73b450d --- /dev/null +++ b/pydis_site/apps/api/migrations/0055_reminder_mentions.py @@ -0,0 +1,20 @@ +# Generated by Django 2.2.14 on 2020-07-15 07:37 + +import django.contrib.postgres.fields +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0054_user_invalidate_unknown_role'), + ] + + operations = [ + migrations.AddField( + model_name='reminder', + name='mentions', + field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Mention IDs cannot be negative.')]), blank=True, default=list, help_text='IDs of roles or users to ping with the reminder.', size=None), + ), + ] diff --git a/pydis_site/apps/api/migrations/0055_user_roles_to_array.py b/pydis_site/apps/api/migrations/0055_user_roles_to_array.py deleted file mode 100644 index e7b4a983..00000000 --- a/pydis_site/apps/api/migrations/0055_user_roles_to_array.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 2.2.11 on 2020-06-02 13:42 - -import django.contrib.postgres.fields -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0054_remove_user_avatar_hash'), - ] - - operations = [ - migrations.RemoveField( - model_name='user', - name='roles', - ), - migrations.AddField( - model_name='user', - name='roles', - field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.')]), default=list, help_text='IDs of roles the user has on the server', size=None), - ), - ] diff --git a/pydis_site/apps/api/migrations/0056_allow_blank_user_roles.py b/pydis_site/apps/api/migrations/0056_allow_blank_user_roles.py new file mode 100644 index 00000000..489941c7 --- /dev/null +++ b/pydis_site/apps/api/migrations/0056_allow_blank_user_roles.py @@ -0,0 +1,21 @@ +# Generated by Django 3.0.8 on 2020-07-14 20:35 + +import django.contrib.postgres.fields +import django.core.validators +from django.db import migrations, models +import pydis_site.apps.api.models.bot.user + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0055_merge_20200714_2027'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='roles', + field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.'), pydis_site.apps.api.models.bot.user._validate_existing_role]), blank=True, default=list, help_text='IDs of roles the user has on the server', size=None), + ), + ] diff --git a/pydis_site/apps/api/migrations/0056_user_invalidate_unknown_role.py b/pydis_site/apps/api/migrations/0056_user_invalidate_unknown_role.py deleted file mode 100644 index ab2696aa..00000000 --- a/pydis_site/apps/api/migrations/0056_user_invalidate_unknown_role.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 2.2.11 on 2020-06-02 20:08 - -import django.contrib.postgres.fields -import django.core.validators -from django.db import migrations, models -import pydis_site.apps.api.models.bot.user - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0055_user_roles_to_array'), - ] - - operations = [ - migrations.AlterField( - model_name='user', - name='roles', - field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.'), pydis_site.apps.api.models.bot.user._validate_existing_role]), default=list, help_text='IDs of roles the user has on the server', size=None), - ), - ] diff --git a/pydis_site/apps/api/migrations/0057_merge_20200716_0751.py b/pydis_site/apps/api/migrations/0057_merge_20200716_0751.py new file mode 100644 index 00000000..47a6d2d4 --- /dev/null +++ b/pydis_site/apps/api/migrations/0057_merge_20200716_0751.py @@ -0,0 +1,14 @@ +# Generated by Django 2.2.14 on 2020-07-16 07:51 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0055_reminder_mentions'), + ('api', '0056_allow_blank_user_roles'), + ] + + operations = [ + ] diff --git a/pydis_site/apps/api/migrations/0057_reminder_mentions.py b/pydis_site/apps/api/migrations/0057_reminder_mentions.py deleted file mode 100644 index fb829a17..00000000 --- a/pydis_site/apps/api/migrations/0057_reminder_mentions.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 2.2.14 on 2020-07-15 07:37 - -import django.contrib.postgres.fields -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0056_user_invalidate_unknown_role'), - ] - - operations = [ - migrations.AddField( - model_name='reminder', - name='mentions', - field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Mention IDs cannot be negative.')]), blank=True, default=list, help_text='IDs of roles or users to ping with the reminder.', size=None), - ), - ] diff --git a/pydis_site/apps/api/migrations/0058_allow_blank_user_roles.py b/pydis_site/apps/api/migrations/0058_allow_blank_user_roles.py deleted file mode 100644 index 8f7fddfc..00000000 --- a/pydis_site/apps/api/migrations/0058_allow_blank_user_roles.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 3.0.8 on 2020-07-14 20:35 - -import django.contrib.postgres.fields -import django.core.validators -from django.db import migrations, models -import pydis_site.apps.api.models.bot.user - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0057_reminder_mentions'), - ] - - operations = [ - migrations.AlterField( - model_name='user', - name='roles', - field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.'), pydis_site.apps.api.models.bot.user._validate_existing_role]), blank=True, default=list, help_text='IDs of roles the user has on the server', size=None), - ), - ] diff --git a/pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py b/pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py new file mode 100644 index 00000000..aecfdad7 --- /dev/null +++ b/pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py @@ -0,0 +1,33 @@ +# Generated by Django 3.0.8 on 2020-07-15 11:23 + +from django.db import migrations, models +import pydis_site.apps.api.models.mixins + + +class Migration(migrations.Migration): + dependencies = [ + ('api', '0057_merge_20200716_0751'), + ] + + operations = [ + migrations.CreateModel( + name='FilterList', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('type', models.CharField( + choices=[('GUILD_INVITE', 'Guild Invite'), ('FILE_FORMAT', 'File Format'), + ('DOMAIN_NAME', 'Domain Name'), ('FILTER_TOKEN', 'Filter Token')], + help_text='The type of allowlist this is on.', max_length=50)), + ('allowed', models.BooleanField(help_text='Whether this item is on the allowlist or the denylist.')), + ('content', models.TextField(help_text='The data to add to the allow or denylist.')), + ('comment', models.TextField(help_text="Optional comment on this entry.", null=True)), + ], + bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), + ), + migrations.AddConstraint( + model_name='filterlist', + constraint=models.UniqueConstraint(fields=('content', 'type'), name='unique_filter_list') + ) + ] diff --git a/pydis_site/apps/api/migrations/0059_create_new_filterlist_model.py b/pydis_site/apps/api/migrations/0059_create_new_filterlist_model.py deleted file mode 100644 index eac5542d..00000000 --- a/pydis_site/apps/api/migrations/0059_create_new_filterlist_model.py +++ /dev/null @@ -1,33 +0,0 @@ -# Generated by Django 3.0.8 on 2020-07-15 11:23 - -from django.db import migrations, models -import pydis_site.apps.api.models.mixins - - -class Migration(migrations.Migration): - dependencies = [ - ('api', '0058_allow_blank_user_roles'), - ] - - operations = [ - migrations.CreateModel( - name='FilterList', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('type', models.CharField( - choices=[('GUILD_INVITE', 'Guild Invite'), ('FILE_FORMAT', 'File Format'), - ('DOMAIN_NAME', 'Domain Name'), ('FILTER_TOKEN', 'Filter Token')], - help_text='The type of allowlist this is on.', max_length=50)), - ('allowed', models.BooleanField(help_text='Whether this item is on the allowlist or the denylist.')), - ('content', models.TextField(help_text='The data to add to the allow or denylist.')), - ('comment', models.TextField(help_text="Optional comment on this entry.", null=True)), - ], - bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), - ), - migrations.AddConstraint( - model_name='filterlist', - constraint=models.UniqueConstraint(fields=('content', 'type'), name='unique_filter_list') - ) - ] diff --git a/pydis_site/apps/api/migrations/0059_populate_filterlists.py b/pydis_site/apps/api/migrations/0059_populate_filterlists.py new file mode 100644 index 00000000..8c550191 --- /dev/null +++ b/pydis_site/apps/api/migrations/0059_populate_filterlists.py @@ -0,0 +1,153 @@ +from django.db import migrations + +guild_invite_whitelist = [ + ("discord.gg/python", "Python Discord", True), + ("discord.gg/4JJdJKb", "RLBot", True), + ("discord.gg/djPtTRJ", "Kivy", True), + ("discord.gg/QXyegWe", "Pyglet", True), + ("discord.gg/9XsucTT", "Panda3D", True), + ("discord.gg/AP3rq2k", "PyWeek", True), + ("discord.gg/vSPsP9t", "Microsoft Python", True), + ("discord.gg/bRCvFy9", "Discord.js Official", True), + ("discord.gg/9zT7NHP", "Programming Discussions", True), + ("discord.gg/ysd6M4r", "JetBrains Community", True), + ("discord.gg/4xJeCgy", "Raspberry Pie", True), + ("discord.gg/AStb3kZ", "Ren'Py", True), + ("discord.gg/t655QNV", "Python Discord: Emojis 1", True), + ("discord.gg/vRZPkqC", "Python Discord: Emojis 2", True), + ("discord.gg/jTtgWuy", "Django", True), + ("discord.gg/W9BypZF", "STEM", True), + ("discord.gg/dpy", "discord.py", True), + ("discord.gg/programming", "Programmers Hangout", True), + ("discord.gg/qhGUjGD", "SpeakJS", True), + ("discord.gg/eTbWSZj", "Functional Programming", True), + ("discord.gg/r8yreB6", "PyGame", True), + ("discord.gg/5UBnR3P", "Python Atlanta", True), + ("discord.gg/ccyrDKv", "C#", True), +] + +domain_name_blacklist = [ + ("pornhub.com", None, False), + ("liveleak.com", None, False), + ("grabify.link", None, False), + ("bmwforum.co", None, False), + ("leancoding.co", None, False), + ("spottyfly.com", None, False), + ("stopify.co", None, False), + ("yoütu.be", None, False), + ("discörd.com", None, False), + ("minecräft.com", None, False), + ("freegiftcards.co", None, False), + ("disçordapp.com", None, False), + ("fortnight.space", None, False), + ("fortnitechat.site", None, False), + ("joinmy.site", None, False), + ("curiouscat.club", None, False), + ("catsnthings.fun", None, False), + ("yourtube.site", None, False), + ("youtubeshort.watch", None, False), + ("catsnthing.com", None, False), + ("youtubeshort.pro", None, False), + ("canadianlumberjacks.online", None, False), + ("poweredbydialup.club", None, False), + ("poweredbydialup.online", None, False), + ("poweredbysecurity.org", None, False), + ("poweredbysecurity.online", None, False), + ("ssteam.site", None, False), + ("steamwalletgift.com", None, False), + ("discord.gift", None, False), + ("lmgtfy.com", None, False), +] + +filter_token_blacklist = [ + ("\bgoo+ks*\b", None, False), + ("\bky+s+\b", None, False), + ("\bki+ke+s*\b", None, False), + ("\bbeaner+s?\b", None, False), + ("\bcoo+ns*\b", None, False), + ("\bnig+lets*\b", None, False), + ("\bslant-eyes*\b", None, False), + ("\btowe?l-?head+s*\b", None, False), + ("\bchi*n+k+s*\b", None, False), + ("\bspick*s*\b", None, False), + ("\bkill* +(?:yo)?urself+\b", None, False), + ("\bjew+s*\b", None, False), + ("\bsuicide\b", None, False), + ("\brape\b", None, False), + ("\b(re+)tar+(d+|t+)(ed)?\b", None, False), + ("\bta+r+d+\b", None, False), + ("\bcunts*\b", None, False), + ("\btrann*y\b", None, False), + ("\bshemale\b", None, False), + ("fa+g+s*", None, False), + ("卐", None, False), + ("卍", None, False), + ("࿖", None, False), + ("࿕", None, False), + ("࿘", None, False), + ("࿗", None, False), + ("cuck(?!oo+)", None, False), + ("nigg+(?:e*r+|a+h*?|u+h+)s?", None, False), + ("fag+o+t+s*", None, False), +] + +file_format_whitelist = [ + (".3gp", None, True), + (".3g2", None, True), + (".avi", None, True), + (".bmp", None, True), + (".gif", None, True), + (".h264", None, True), + (".jpg", None, True), + (".jpeg", None, True), + (".m4v", None, True), + (".mkv", None, True), + (".mov", None, True), + (".mp4", None, True), + (".mpeg", None, True), + (".mpg", None, True), + (".png", None, True), + (".tiff", None, True), + (".wmv", None, True), + (".svg", None, True), + (".psd", "Photoshop", True), + (".ai", "Illustrator", True), + (".aep", "After Effects", True), + (".xcf", "GIMP", True), + (".mp3", None, True), + (".wav", None, True), + (".ogg", None, True), + (".webm", None, True), + (".webp", None, True), +] + +populate_data = { + "FILTER_TOKEN": filter_token_blacklist, + "DOMAIN_NAME": domain_name_blacklist, + "FILE_FORMAT": file_format_whitelist, + "GUILD_INVITE": guild_invite_whitelist, +} + + +class Migration(migrations.Migration): + dependencies = [("api", "0058_create_new_filterlist_model")] + + def populate_filterlists(app, _): + FilterList = app.get_model("api", "FilterList") + + for filterlist_type, metadata in populate_data.items(): + for content, comment, allowed in metadata: + FilterList.objects.create( + type=filterlist_type, + allowed=allowed, + content=content, + comment=comment, + ) + + def clear_filterlists(app, _): + FilterList = app.get_model("api", "FilterList") + FilterList.objects.all().delete() + + operations = [ + migrations.RunPython(populate_filterlists, clear_filterlists) + ] diff --git a/pydis_site/apps/api/migrations/0060_populate_filterlists.py b/pydis_site/apps/api/migrations/0060_populate_filterlists.py deleted file mode 100644 index 35fde95a..00000000 --- a/pydis_site/apps/api/migrations/0060_populate_filterlists.py +++ /dev/null @@ -1,153 +0,0 @@ -from django.db import migrations - -guild_invite_whitelist = [ - ("discord.gg/python", "Python Discord", True), - ("discord.gg/4JJdJKb", "RLBot", True), - ("discord.gg/djPtTRJ", "Kivy", True), - ("discord.gg/QXyegWe", "Pyglet", True), - ("discord.gg/9XsucTT", "Panda3D", True), - ("discord.gg/AP3rq2k", "PyWeek", True), - ("discord.gg/vSPsP9t", "Microsoft Python", True), - ("discord.gg/bRCvFy9", "Discord.js Official", True), - ("discord.gg/9zT7NHP", "Programming Discussions", True), - ("discord.gg/ysd6M4r", "JetBrains Community", True), - ("discord.gg/4xJeCgy", "Raspberry Pie", True), - ("discord.gg/AStb3kZ", "Ren'Py", True), - ("discord.gg/t655QNV", "Python Discord: Emojis 1", True), - ("discord.gg/vRZPkqC", "Python Discord: Emojis 2", True), - ("discord.gg/jTtgWuy", "Django", True), - ("discord.gg/W9BypZF", "STEM", True), - ("discord.gg/dpy", "discord.py", True), - ("discord.gg/programming", "Programmers Hangout", True), - ("discord.gg/qhGUjGD", "SpeakJS", True), - ("discord.gg/eTbWSZj", "Functional Programming", True), - ("discord.gg/r8yreB6", "PyGame", True), - ("discord.gg/5UBnR3P", "Python Atlanta", True), - ("discord.gg/ccyrDKv", "C#", True), -] - -domain_name_blacklist = [ - ("pornhub.com", None, False), - ("liveleak.com", None, False), - ("grabify.link", None, False), - ("bmwforum.co", None, False), - ("leancoding.co", None, False), - ("spottyfly.com", None, False), - ("stopify.co", None, False), - ("yoütu.be", None, False), - ("discörd.com", None, False), - ("minecräft.com", None, False), - ("freegiftcards.co", None, False), - ("disçordapp.com", None, False), - ("fortnight.space", None, False), - ("fortnitechat.site", None, False), - ("joinmy.site", None, False), - ("curiouscat.club", None, False), - ("catsnthings.fun", None, False), - ("yourtube.site", None, False), - ("youtubeshort.watch", None, False), - ("catsnthing.com", None, False), - ("youtubeshort.pro", None, False), - ("canadianlumberjacks.online", None, False), - ("poweredbydialup.club", None, False), - ("poweredbydialup.online", None, False), - ("poweredbysecurity.org", None, False), - ("poweredbysecurity.online", None, False), - ("ssteam.site", None, False), - ("steamwalletgift.com", None, False), - ("discord.gift", None, False), - ("lmgtfy.com", None, False), -] - -filter_token_blacklist = [ - ("\bgoo+ks*\b", None, False), - ("\bky+s+\b", None, False), - ("\bki+ke+s*\b", None, False), - ("\bbeaner+s?\b", None, False), - ("\bcoo+ns*\b", None, False), - ("\bnig+lets*\b", None, False), - ("\bslant-eyes*\b", None, False), - ("\btowe?l-?head+s*\b", None, False), - ("\bchi*n+k+s*\b", None, False), - ("\bspick*s*\b", None, False), - ("\bkill* +(?:yo)?urself+\b", None, False), - ("\bjew+s*\b", None, False), - ("\bsuicide\b", None, False), - ("\brape\b", None, False), - ("\b(re+)tar+(d+|t+)(ed)?\b", None, False), - ("\bta+r+d+\b", None, False), - ("\bcunts*\b", None, False), - ("\btrann*y\b", None, False), - ("\bshemale\b", None, False), - ("fa+g+s*", None, False), - ("卐", None, False), - ("卍", None, False), - ("࿖", None, False), - ("࿕", None, False), - ("࿘", None, False), - ("࿗", None, False), - ("cuck(?!oo+)", None, False), - ("nigg+(?:e*r+|a+h*?|u+h+)s?", None, False), - ("fag+o+t+s*", None, False), -] - -file_format_whitelist = [ - (".3gp", None, True), - (".3g2", None, True), - (".avi", None, True), - (".bmp", None, True), - (".gif", None, True), - (".h264", None, True), - (".jpg", None, True), - (".jpeg", None, True), - (".m4v", None, True), - (".mkv", None, True), - (".mov", None, True), - (".mp4", None, True), - (".mpeg", None, True), - (".mpg", None, True), - (".png", None, True), - (".tiff", None, True), - (".wmv", None, True), - (".svg", None, True), - (".psd", "Photoshop", True), - (".ai", "Illustrator", True), - (".aep", "After Effects", True), - (".xcf", "GIMP", True), - (".mp3", None, True), - (".wav", None, True), - (".ogg", None, True), - (".webm", None, True), - (".webp", None, True), -] - -populate_data = { - "FILTER_TOKEN": filter_token_blacklist, - "DOMAIN_NAME": domain_name_blacklist, - "FILE_FORMAT": file_format_whitelist, - "GUILD_INVITE": guild_invite_whitelist, -} - - -class Migration(migrations.Migration): - dependencies = [("api", "0059_create_new_filterlist_model")] - - def populate_filterlists(app, _): - FilterList = app.get_model("api", "FilterList") - - for filterlist_type, metadata in populate_data.items(): - for content, comment, allowed in metadata: - FilterList.objects.create( - type=filterlist_type, - allowed=allowed, - content=content, - comment=comment, - ) - - def clear_filterlists(app, _): - FilterList = app.get_model("api", "FilterList") - FilterList.objects.all().delete() - - operations = [ - migrations.RunPython(populate_filterlists, clear_filterlists) - ] diff --git a/pydis_site/apps/api/migrations/0060_populate_filterlists_fix.py b/pydis_site/apps/api/migrations/0060_populate_filterlists_fix.py new file mode 100644 index 00000000..53846f02 --- /dev/null +++ b/pydis_site/apps/api/migrations/0060_populate_filterlists_fix.py @@ -0,0 +1,85 @@ +from django.db import migrations + +bad_guild_invite_whitelist = [ + ("discord.gg/python", "Python Discord", True), + ("discord.gg/4JJdJKb", "RLBot", True), + ("discord.gg/djPtTRJ", "Kivy", True), + ("discord.gg/QXyegWe", "Pyglet", True), + ("discord.gg/9XsucTT", "Panda3D", True), + ("discord.gg/AP3rq2k", "PyWeek", True), + ("discord.gg/vSPsP9t", "Microsoft Python", True), + ("discord.gg/bRCvFy9", "Discord.js Official", True), + ("discord.gg/9zT7NHP", "Programming Discussions", True), + ("discord.gg/ysd6M4r", "JetBrains Community", True), + ("discord.gg/4xJeCgy", "Raspberry Pie", True), + ("discord.gg/AStb3kZ", "Ren'Py", True), + ("discord.gg/t655QNV", "Python Discord: Emojis 1", True), + ("discord.gg/vRZPkqC", "Python Discord: Emojis 2", True), + ("discord.gg/jTtgWuy", "Django", True), + ("discord.gg/W9BypZF", "STEM", True), + ("discord.gg/dpy", "discord.py", True), + ("discord.gg/programming", "Programmers Hangout", True), + ("discord.gg/qhGUjGD", "SpeakJS", True), + ("discord.gg/eTbWSZj", "Functional Programming", True), + ("discord.gg/r8yreB6", "PyGame", True), + ("discord.gg/5UBnR3P", "Python Atlanta", True), + ("discord.gg/ccyrDKv", "C#", True), +] + +guild_invite_whitelist = [ + ("267624335836053506", "Python Discord", True), + ("348658686962696195", "RLBot", True), + ("423249981340778496", "Kivy", True), + ("438622377094414346", "Pyglet", True), + ("524691714909274162", "Panda3D", True), + ("666560367173828639", "PyWeek", True), + ("702724176489873509", "Microsoft Python", True), + ("222078108977594368", "Discord.js Official", True), + ("238666723824238602", "Programming Discussions", True), + ("433980600391696384", "JetBrains Community", True), + ("204621105720328193", "Raspberry Pie", True), + ("286633898581164032", "Ren'Py", True), + ("440186186024222721", "Python Discord: Emojis 1", True), + ("578587418123304970", "Python Discord: Emojis 2", True), + ("159039020565790721", "Django", True), + ("273944235143593984", "STEM", True), + ("336642139381301249", "discord.py", True), + ("244230771232079873", "Programmers Hangout", True), + ("239433591950540801", "SpeakJS", True), + ("280033776820813825", "Functional Programming", True), + ("349505959032389632", "PyGame", True), + ("488751051629920277", "Python Atlanta", True), + ("143867839282020352", "C#", True), +] + + +class Migration(migrations.Migration): + dependencies = [("api", "0059_populate_filterlists")] + + def fix_filterlist(app, _): + FilterList = app.get_model("api", "FilterList") + FilterList.objects.filter(type="GUILD_INVITE").delete() # Clear out the stuff added in 0059. + + for content, comment, allowed in guild_invite_whitelist: + FilterList.objects.create( + type="GUILD_INVITE", + allowed=allowed, + content=content, + comment=comment, + ) + + def restore_bad_filterlist(app, _): + FilterList = app.get_model("api", "FilterList") + FilterList.objects.filter(type="GUILD_INVITE").delete() + + for content, comment, allowed in bad_guild_invite_whitelist: + FilterList.objects.create( + type="GUILD_INVITE", + allowed=allowed, + content=content, + comment=comment, + ) + + operations = [ + migrations.RunPython(fix_filterlist, restore_bad_filterlist) + ] diff --git a/pydis_site/apps/api/migrations/0061_populate_filterlists_fix.py b/pydis_site/apps/api/migrations/0061_populate_filterlists_fix.py deleted file mode 100644 index eaaafb38..00000000 --- a/pydis_site/apps/api/migrations/0061_populate_filterlists_fix.py +++ /dev/null @@ -1,85 +0,0 @@ -from django.db import migrations - -bad_guild_invite_whitelist = [ - ("discord.gg/python", "Python Discord", True), - ("discord.gg/4JJdJKb", "RLBot", True), - ("discord.gg/djPtTRJ", "Kivy", True), - ("discord.gg/QXyegWe", "Pyglet", True), - ("discord.gg/9XsucTT", "Panda3D", True), - ("discord.gg/AP3rq2k", "PyWeek", True), - ("discord.gg/vSPsP9t", "Microsoft Python", True), - ("discord.gg/bRCvFy9", "Discord.js Official", True), - ("discord.gg/9zT7NHP", "Programming Discussions", True), - ("discord.gg/ysd6M4r", "JetBrains Community", True), - ("discord.gg/4xJeCgy", "Raspberry Pie", True), - ("discord.gg/AStb3kZ", "Ren'Py", True), - ("discord.gg/t655QNV", "Python Discord: Emojis 1", True), - ("discord.gg/vRZPkqC", "Python Discord: Emojis 2", True), - ("discord.gg/jTtgWuy", "Django", True), - ("discord.gg/W9BypZF", "STEM", True), - ("discord.gg/dpy", "discord.py", True), - ("discord.gg/programming", "Programmers Hangout", True), - ("discord.gg/qhGUjGD", "SpeakJS", True), - ("discord.gg/eTbWSZj", "Functional Programming", True), - ("discord.gg/r8yreB6", "PyGame", True), - ("discord.gg/5UBnR3P", "Python Atlanta", True), - ("discord.gg/ccyrDKv", "C#", True), -] - -guild_invite_whitelist = [ - ("267624335836053506", "Python Discord", True), - ("348658686962696195", "RLBot", True), - ("423249981340778496", "Kivy", True), - ("438622377094414346", "Pyglet", True), - ("524691714909274162", "Panda3D", True), - ("666560367173828639", "PyWeek", True), - ("702724176489873509", "Microsoft Python", True), - ("222078108977594368", "Discord.js Official", True), - ("238666723824238602", "Programming Discussions", True), - ("433980600391696384", "JetBrains Community", True), - ("204621105720328193", "Raspberry Pie", True), - ("286633898581164032", "Ren'Py", True), - ("440186186024222721", "Python Discord: Emojis 1", True), - ("578587418123304970", "Python Discord: Emojis 2", True), - ("159039020565790721", "Django", True), - ("273944235143593984", "STEM", True), - ("336642139381301249", "discord.py", True), - ("244230771232079873", "Programmers Hangout", True), - ("239433591950540801", "SpeakJS", True), - ("280033776820813825", "Functional Programming", True), - ("349505959032389632", "PyGame", True), - ("488751051629920277", "Python Atlanta", True), - ("143867839282020352", "C#", True), -] - - -class Migration(migrations.Migration): - dependencies = [("api", "0060_populate_filterlists")] - - def fix_filterlist(app, _): - FilterList = app.get_model("api", "FilterList") - FilterList.objects.filter(type="GUILD_INVITE").delete() # Clear out the stuff added in 0059. - - for content, comment, allowed in guild_invite_whitelist: - FilterList.objects.create( - type="GUILD_INVITE", - allowed=allowed, - content=content, - comment=comment, - ) - - def restore_bad_filterlist(app, _): - FilterList = app.get_model("api", "FilterList") - FilterList.objects.filter(type="GUILD_INVITE").delete() - - for content, comment, allowed in bad_guild_invite_whitelist: - FilterList.objects.create( - type="GUILD_INVITE", - allowed=allowed, - content=content, - comment=comment, - ) - - operations = [ - migrations.RunPython(fix_filterlist, restore_bad_filterlist) - ] -- cgit v1.2.3 From ab634c6ef1d4b7cec3b8471262eec3ec727478d8 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sun, 30 Aug 2020 08:27:07 +0300 Subject: Create merge migration to fix conflicts --- pydis_site/apps/api/migrations/0061_merge_20200830_0526.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 pydis_site/apps/api/migrations/0061_merge_20200830_0526.py diff --git a/pydis_site/apps/api/migrations/0061_merge_20200830_0526.py b/pydis_site/apps/api/migrations/0061_merge_20200830_0526.py new file mode 100644 index 00000000..f0668696 --- /dev/null +++ b/pydis_site/apps/api/migrations/0061_merge_20200830_0526.py @@ -0,0 +1,14 @@ +# Generated by Django 3.0.8 on 2020-08-30 05:26 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0060_populate_filterlists_fix'), + ('api', '0052_offtopicchannelname_used'), + ] + + operations = [ + ] -- cgit v1.2.3 From b184e3bd943ad05b1d75449fa4bc564c62a31726 Mon Sep 17 00:00:00 2001 From: Derek Date: Sun, 30 Aug 2020 16:42:31 -0400 Subject: Update README.md update server's user count --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ec2f0af3..616f2edc 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Python Discord: Site -[![Discord](https://img.shields.io/static/v1?label=Python%20Discord&logo=discord&message=%3E30k%20members&color=%237289DA&logoColor=white)](https://discord.gg/2B963hn) +[![Discord](https://img.shields.io/static/v1?label=Python%20Discord&logo=discord&message=%3E95k%20members&color=%237289DA&logoColor=white)](https://discord.gg/2B963hn) [![Build Status](https://dev.azure.com/python-discord/Python%20Discord/_apis/build/status/Site?branchName=master)](https://dev.azure.com/python-discord/Python%20Discord/_build/latest?definitionId=2&branchName=master) [![Tests](https://img.shields.io/azure-devops/tests/python-discord/Python%20Discord/2?compact_message)](https://dev.azure.com/python-discord/Python%20Discord/_apis/build/status/Site?branchName=master) [![Coverage](https://img.shields.io/azure-devops/coverage/python-discord/Python%20Discord/2/master)](https://dev.azure.com/python-discord/Python%20Discord/_apis/build/status/Site?branchName=master) -- cgit v1.2.3 From a9e1f6f398ca4cac42c01a211513c02881e7541d Mon Sep 17 00:00:00 2001 From: Karlis S Date: Tue, 1 Sep 2020 14:59:23 +0000 Subject: Create new merge migration to fix conflicts between migrations --- pydis_site/apps/api/migrations/0062_merge_20200901_1459.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 pydis_site/apps/api/migrations/0062_merge_20200901_1459.py diff --git a/pydis_site/apps/api/migrations/0062_merge_20200901_1459.py b/pydis_site/apps/api/migrations/0062_merge_20200901_1459.py new file mode 100644 index 00000000..d162acf1 --- /dev/null +++ b/pydis_site/apps/api/migrations/0062_merge_20200901_1459.py @@ -0,0 +1,14 @@ +# Generated by Django 3.0.8 on 2020-09-01 14:59 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0051_delete_tag'), + ('api', '0061_merge_20200830_0526'), + ] + + operations = [ + ] -- cgit v1.2.3 From 01a0e825333812dff21351201fce8c25031cc8fa Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Thu, 3 Sep 2020 00:39:25 +0200 Subject: Update landing page. Replace flake8-annotations with metricity. --- pydis_site/apps/home/tests/mock_github_api_response.json | 2 +- pydis_site/apps/home/views/home.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pydis_site/apps/home/tests/mock_github_api_response.json b/pydis_site/apps/home/tests/mock_github_api_response.json index 35604a85..10be4f99 100644 --- a/pydis_site/apps/home/tests/mock_github_api_response.json +++ b/pydis_site/apps/home/tests/mock_github_api_response.json @@ -28,7 +28,7 @@ "forks_count": 31 }, { - "full_name": "python-discord/flake8-annotations", + "full_name": "python-discord/metricity", "description": "test", "stargazers_count": 97, "language": "Python", diff --git a/pydis_site/apps/home/views/home.py b/pydis_site/apps/home/views/home.py index 20e38ab0..3b5cd5ac 100644 --- a/pydis_site/apps/home/views/home.py +++ b/pydis_site/apps/home/views/home.py @@ -23,7 +23,7 @@ class HomeView(View): "python-discord/bot", "python-discord/snekbox", "python-discord/seasonalbot", - "python-discord/flake8-annotations", + "python-discord/metricity", "python-discord/django-simple-bulma", ] -- cgit v1.2.3