diff options
| author | 2022-01-01 14:28:53 +0200 | |
|---|---|---|
| committer | 2022-01-01 14:28:53 +0200 | |
| commit | 1798edc10ff5e5adaffe9879f2a9ebb8a7b5c926 (patch) | |
| tree | 984fb8393fe0a8f17499cb89cc306de455841453 | |
| parent | Merged infraction and notification settings in JSON (diff) | |
| parent | Merge pull request #637 from python-discord/mbaruh-patch-1 (diff) | |
Merge branch 'main' into new-filter-schema
11 files changed, 180 insertions, 91 deletions
| diff --git a/pydis_site/apps/api/migrations/0059_populate_filterlists.py b/pydis_site/apps/api/migrations/0059_populate_filterlists.py index 8c550191..273db3d1 100644 --- a/pydis_site/apps/api/migrations/0059_populate_filterlists.py +++ b/pydis_site/apps/api/migrations/0059_populate_filterlists.py @@ -60,35 +60,35 @@ domain_name_blacklist = [  ]  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), +    (r"\bgoo+ks*\b", None, False), +    (r"\bky+s+\b", None, False), +    (r"\bki+ke+s*\b", None, False), +    (r"\bbeaner+s?\b", None, False), +    (r"\bcoo+ns*\b", None, False), +    (r"\bnig+lets*\b", None, False), +    (r"\bslant-eyes*\b", None, False), +    (r"\btowe?l-?head+s*\b", None, False), +    (r"\bchi*n+k+s*\b", None, False), +    (r"\bspick*s*\b", None, False), +    (r"\bkill* +(?:yo)?urself+\b", None, False), +    (r"\bjew+s*\b", None, False), +    (r"\bsuicide\b", None, False), +    (r"\brape\b", None, False), +    (r"\b(re+)tar+(d+|t+)(ed)?\b", None, False), +    (r"\bta+r+d+\b", None, False), +    (r"\bcunts*\b", None, False), +    (r"\btrann*y\b", None, False), +    (r"\bshemale\b", None, False), +    (r"fa+g+s*", None, False), +    (r"卐", None, False), +    (r"卍", None, False), +    (r"࿖", None, False), +    (r"࿕", None, False), +    (r"࿘", None, False), +    (r"࿗", None, False), +    (r"cuck(?!oo+)", None, False), +    (r"nigg+(?:e*r+|a+h*?|u+h+)s?", None, False), +    (r"fag+o+t+s*", None, False),  ]  file_format_whitelist = [ diff --git a/pydis_site/apps/api/migrations/0070_auto_20210519_0545.py b/pydis_site/apps/api/migrations/0070_auto_20210519_0545.py new file mode 100644 index 00000000..dbd7ac91 --- /dev/null +++ b/pydis_site/apps/api/migrations/0070_auto_20210519_0545.py @@ -0,0 +1,23 @@ +# Generated by Django 3.0.14 on 2021-05-19 05:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + +    dependencies = [ +        ('api', '0069_documentationlink_validators'), +    ] + +    operations = [ +        migrations.AddField( +            model_name='offtopicchannelname', +            name='active', +            field=models.BooleanField(default=True, help_text='Whether or not this name should be considered for naming channels.'), +        ), +        migrations.AlterField( +            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/0072_merge_20210724_1354.py b/pydis_site/apps/api/migrations/0072_merge_20210724_1354.py new file mode 100644 index 00000000..f12efab5 --- /dev/null +++ b/pydis_site/apps/api/migrations/0072_merge_20210724_1354.py @@ -0,0 +1,14 @@ +# Generated by Django 3.0.14 on 2021-07-24 13:54 + +from django.db import migrations + + +class Migration(migrations.Migration): + +    dependencies = [ +        ('api', '0071_increase_message_content_4000'), +        ('api', '0070_auto_20210519_0545'), +    ] + +    operations = [ +    ] diff --git a/pydis_site/apps/api/migrations/0074_merge_20211105_0518.py b/pydis_site/apps/api/migrations/0074_merge_20211105_0518.py new file mode 100644 index 00000000..ebf5ae15 --- /dev/null +++ b/pydis_site/apps/api/migrations/0074_merge_20211105_0518.py @@ -0,0 +1,14 @@ +# Generated by Django 3.0.14 on 2021-11-05 05:18 + +from django.db import migrations + + +class Migration(migrations.Migration): + +    dependencies = [ +        ('api', '0072_merge_20210724_1354'), +        ('api', '0073_otn_allow_GT_and_LT'), +    ] + +    operations = [ +    ] diff --git a/pydis_site/apps/api/migrations/0078_merge_20211213_0552.py b/pydis_site/apps/api/migrations/0078_merge_20211213_0552.py new file mode 100644 index 00000000..5ce0e871 --- /dev/null +++ b/pydis_site/apps/api/migrations/0078_merge_20211213_0552.py @@ -0,0 +1,14 @@ +# Generated by Django 3.1.14 on 2021-12-13 05:52 + +from django.db import migrations + + +class Migration(migrations.Migration): + +    dependencies = [ +        ('api', '0077_use_generic_jsonfield'), +        ('api', '0074_merge_20211105_0518'), +    ] + +    operations = [ +    ] 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 8999e560..e9fec114 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,12 @@ class OffTopicChannelName(ModelReprMixin, models.Model):      used = models.BooleanField(          default=False, -        help_text="Whether or not this name has already been used during this rotation", +        help_text="Whether or not this name has already been used during this rotation.", +    ) + +    active = models.BooleanField( +        default=True, +        help_text="Whether or not this name should be considered for naming channels."      )      def __str__(self): diff --git a/pydis_site/apps/api/serializers.py b/pydis_site/apps/api/serializers.py index 91aac822..88b6e2bd 100644 --- a/pydis_site/apps/api/serializers.py +++ b/pydis_site/apps/api/serializers.py @@ -392,25 +392,30 @@ class ExpandedInfractionSerializer(InfractionSerializer):          return ret +class OffTopicChannelNameListSerializer(ListSerializer): +    """Custom ListSerializer to override to_representation() when list views are triggered.""" + +    def to_representation(self, objects: list[OffTopicChannelName]) -> list[str]: +        """ +        Return a list with all `OffTopicChannelName`s in the database. + +        This returns the list of off topic channel names. We want to only return +        the name attribute, hence it is unnecessary to create a nested dictionary. +        Additionally, this allows off topic channel name routes to simply return an +        array of names instead of objects, saving on bandwidth. +        """ +        return [obj.name for obj in objects] + +  class OffTopicChannelNameSerializer(ModelSerializer):      """A class providing (de-)serialization of `OffTopicChannelName` instances."""      class Meta:          """Metadata defined for the Django REST Framework.""" +        list_serializer_class = OffTopicChannelNameListSerializer          model = OffTopicChannelName -        fields = ('name',) - -    def to_representation(self, obj: OffTopicChannelName) -> str: -        """ -        Return the representation of this `OffTopicChannelName`. - -        This only returns the name of the off topic channel name. As the model -        only has a single attribute, it is unnecessary to create a nested dictionary. -        Additionally, this allows off topic channel name routes to simply return an -        array of names instead of objects, saving on bandwidth. -        """ -        return obj.name +        fields = ('name', 'used', 'active')  class ReminderSerializer(ModelSerializer): 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 1825f6e6..2d273756 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,15 @@ class EmptyDatabaseTests(AuthenticatedAPITestCase):  class ListTests(AuthenticatedAPITestCase):      @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', used=False, active=True +        ) +        cls.test_name_2 = OffTopicChannelName.objects.create( +            name='bbq-with-bisk', used=False, active=True +        ) +        cls.test_name_3 = OffTopicChannelName.objects.create( +            name="frozen-with-iceman", used=True, active=False +        )      def test_returns_name_in_list(self):          """Return all off-topic channel names.""" @@ -75,29 +82,55 @@ class ListTests(AuthenticatedAPITestCase):          self.assertEqual(response.status_code, 200)          self.assertEqual( -            response.json(), -            [ +            set(response.json()), +            {                  self.test_name.name, -                self.test_name_2.name -            ] +                self.test_name_2.name, +                self.test_name_3.name +            }          ) -    def test_returns_single_item_with_random_items_param_set_to_1(self): +    def test_returns_two_items_with_random_items_param_set_to_2(self):          """Return not-used name instead used."""          url = reverse('api:bot:offtopicchannelname-list') -        response = self.client.get(f'{url}?random_items=1') +        response = self.client.get(f'{url}?random_items=2')          self.assertEqual(response.status_code, 200) -        self.assertEqual(len(response.json()), 1) -        self.assertEqual(response.json(), [self.test_name.name]) +        self.assertEqual(len(response.json()), 2) +        self.assertEqual(set(response.json()), {self.test_name.name, self.test_name_2.name})      def test_running_out_of_names_with_random_parameter(self):          """Reset names `used` parameter to `False` when running out of names."""          url = reverse('api:bot:offtopicchannelname-list') -        response = self.client.get(f'{url}?random_items=2') +        response = self.client.get(f'{url}?random_items=3')          self.assertEqual(response.status_code, 200) -        self.assertEqual(response.json(), [self.test_name.name, self.test_name_2.name]) +        self.assertEqual( +            set(response.json()), +            {self.test_name.name, self.test_name_2.name, self.test_name_3.name} +        ) + +    def test_returns_inactive_ot_names(self): +        """Return inactive off topic names.""" +        url = reverse('api:bot:offtopicchannelname-list') +        response = self.client.get(f"{url}?active=false") + +        self.assertEqual(response.status_code, 200) +        self.assertEqual( +            response.json(), +            [self.test_name_3.name] +        ) + +    def test_returns_active_ot_names(self): +        """Return active off topic names.""" +        url = reverse('api:bot:offtopicchannelname-list') +        response = self.client.get(f"{url}?active=true") + +        self.assertEqual(response.status_code, 200) +        self.assertEqual( +            set(response.json()), +            {self.test_name.name, self.test_name_2.name} +        )  class CreationTests(AuthenticatedAPITestCase): diff --git a/pydis_site/apps/api/tests/test_users.py b/pydis_site/apps/api/tests/test_users.py index 81bfd43b..9b91380b 100644 --- a/pydis_site/apps/api/tests/test_users.py +++ b/pydis_site/apps/api/tests/test_users.py @@ -408,7 +408,7 @@ class UserMetricityTests(AuthenticatedAPITestCase):              in_guild=True,          ) -    def test_get_metricity_data_under_1k(self): +    def test_get_metricity_data(self):          # Given          joined_at = "foo"          total_messages = 1 @@ -428,25 +428,6 @@ class UserMetricityTests(AuthenticatedAPITestCase):              "activity_blocks": total_blocks          }) -    def test_get_metricity_data_over_1k(self): -        # Given -        joined_at = "foo" -        total_messages = 1001 -        total_blocks = 1001 -        self.mock_metricity_user(joined_at, total_messages, total_blocks, []) - -        # When -        url = reverse('api:bot:user-metricity-data', args=[0]) -        response = self.client.get(url) - -        # Then -        self.assertEqual(response.status_code, 200) -        self.assertCountEqual(response.json(), { -            "joined_at": joined_at, -            "total_messages": total_messages, -            "voice_banned": False, -        }) -      def test_no_metricity_user(self):          # Given          self.mock_no_metricity_user() 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 922e6555..78f8c340 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,18 +1,17 @@  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  from rest_framework.exceptions import ParseError -from rest_framework.mixins import DestroyModelMixin +from rest_framework.request import Request  from rest_framework.response import Response  from rest_framework.status import HTTP_201_CREATED -from rest_framework.viewsets import ViewSet +from rest_framework.viewsets import ModelViewSet  from pydis_site.apps.api.models.bot.off_topic_channel_name import OffTopicChannelName  from pydis_site.apps.api.serializers import OffTopicChannelNameSerializer -class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet): +class OffTopicChannelNameViewSet(ModelViewSet):      """      View of off-topic channel names used by the bot to rotate our off-topic names on a daily basis. @@ -58,6 +57,7 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet):      lookup_field = 'name'      serializer_class = OffTopicChannelNameSerializer +    queryset = OffTopicChannelName.objects.all()      def get_object(self) -> OffTopicChannelName:          """ @@ -65,15 +65,14 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet):          If it doesn't, a HTTP 404 is returned by way of throwing an exception.          """ -        queryset = self.get_queryset()          name = self.kwargs[self.lookup_field] -        return get_object_or_404(queryset, name=name) +        return get_object_or_404(self.queryset, name=name)      def get_queryset(self) -> QuerySet:          """Returns a queryset that covers the entire OffTopicChannelName table."""          return OffTopicChannelName.objects.all() -    def create(self, request: HttpRequest) -> Response: +    def create(self, request: Request, *args, **kwargs) -> Response:          """          DRF method for creating a new OffTopicChannelName. @@ -91,7 +90,7 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet):                  'name': ["This query parameter is required."]              }) -    def list(self, request: HttpRequest) -> Response: +    def list(self, request: Request, *args, **kwargs) -> Response:          """          DRF method for listing OffTopicChannelName entries. @@ -109,13 +108,13 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet):                      'random_items': ["Must be a positive integer."]                  }) -            queryset = self.get_queryset().order_by('used', '?')[:random_count] +            queryset = self.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( +                self.queryset.update(                      used=Case(                          When(                              name__in=(offtopic_name.name for offtopic_name in queryset), @@ -126,13 +125,18 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet):                  )              else:                  # Otherwise mark selected names `used` to True -                self.get_queryset().filter( +                self.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) -        queryset = self.get_queryset() +        params = {} + +        if active_param := request.query_params.get("active"): +            params["active"] = active_param.lower() == "true" + +        queryset = self.queryset.filter(**params)          serialized = self.serializer_class(queryset, many=True)          return Response(serialized.data) diff --git a/pydis_site/apps/api/viewsets/bot/user.py b/pydis_site/apps/api/viewsets/bot/user.py index ed661323..1a5e79f8 100644 --- a/pydis_site/apps/api/viewsets/bot/user.py +++ b/pydis_site/apps/api/viewsets/bot/user.py @@ -273,11 +273,7 @@ class UserViewSet(ModelViewSet):                  data = metricity.user(user.id)                  data["total_messages"] = metricity.total_messages(user.id) -                if data["total_messages"] < 1000: -                    # Only calculate and return activity_blocks if the user has a small amount -                    # of messages, as calculating activity_blocks is expensive. -                    # 1000 message chosen as an arbitrarily large number. -                    data["activity_blocks"] = metricity.total_message_blocks(user.id) +                data["activity_blocks"] = metricity.total_message_blocks(user.id)                  data["voice_banned"] = voice_banned                  return Response(data, status=status.HTTP_200_OK) | 
