aboutsummaryrefslogtreecommitdiffstats
path: root/pydis_site
diff options
context:
space:
mode:
authorGravatar mbaruh <[email protected]>2022-01-01 14:28:53 +0200
committerGravatar mbaruh <[email protected]>2022-01-01 14:28:53 +0200
commit1798edc10ff5e5adaffe9879f2a9ebb8a7b5c926 (patch)
tree984fb8393fe0a8f17499cb89cc306de455841453 /pydis_site
parentMerged infraction and notification settings in JSON (diff)
parentMerge pull request #637 from python-discord/mbaruh-patch-1 (diff)
Merge branch 'main' into new-filter-schema
Diffstat (limited to 'pydis_site')
-rw-r--r--pydis_site/apps/api/migrations/0059_populate_filterlists.py58
-rw-r--r--pydis_site/apps/api/migrations/0070_auto_20210519_0545.py23
-rw-r--r--pydis_site/apps/api/migrations/0072_merge_20210724_1354.py14
-rw-r--r--pydis_site/apps/api/migrations/0074_merge_20211105_0518.py14
-rw-r--r--pydis_site/apps/api/migrations/0078_merge_20211213_0552.py14
-rw-r--r--pydis_site/apps/api/models/bot/off_topic_channel_name.py7
-rw-r--r--pydis_site/apps/api/serializers.py29
-rw-r--r--pydis_site/apps/api/tests/test_off_topic_channel_names.py57
-rw-r--r--pydis_site/apps/api/tests/test_users.py21
-rw-r--r--pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py28
-rw-r--r--pydis_site/apps/api/viewsets/bot/user.py6
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)