From 30b7b4204b7e4b711960c952cccc15f667e2252f Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Sat, 19 Feb 2022 17:57:18 +0000 Subject: Move FilterList imports down so they're sorted --- pydis_site/apps/api/models/__init__.py | 2 +- pydis_site/apps/api/models/bot/__init__.py | 2 +- pydis_site/apps/api/urls.py | 16 ++++++++-------- pydis_site/apps/api/viewsets/__init__.py | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) (limited to 'pydis_site/apps/api') diff --git a/pydis_site/apps/api/models/__init__.py b/pydis_site/apps/api/models/__init__.py index 4f616986..e83473c9 100644 --- a/pydis_site/apps/api/models/__init__.py +++ b/pydis_site/apps/api/models/__init__.py @@ -1,9 +1,9 @@ # flake8: noqa from .bot import ( - FilterList, BotSetting, DocumentationLink, DeletedMessage, + FilterList, Infraction, Message, MessageDeletionContext, diff --git a/pydis_site/apps/api/models/bot/__init__.py b/pydis_site/apps/api/models/bot/__init__.py index ec0e701c..64676fdb 100644 --- a/pydis_site/apps/api/models/bot/__init__.py +++ b/pydis_site/apps/api/models/bot/__init__.py @@ -1,8 +1,8 @@ # flake8: noqa -from .filter_list import FilterList from .bot_setting import BotSetting from .deleted_message import DeletedMessage from .documentation_link import DocumentationLink +from .filter_list import FilterList from .infraction import Infraction from .message import Message from .aoc_completionist_block import AocCompletionistBlock diff --git a/pydis_site/apps/api/urls.py b/pydis_site/apps/api/urls.py index 7c55fc92..6b881fac 100644 --- a/pydis_site/apps/api/urls.py +++ b/pydis_site/apps/api/urls.py @@ -21,8 +21,12 @@ from .viewsets import ( # https://www.django-rest-framework.org/api-guide/routers/#defaultrouter bot_router = DefaultRouter(trailing_slash=False) bot_router.register( - 'filter-lists', - FilterListViewSet + "aoc-account-links", + AocAccountLinkViewSet +) +bot_router.register( + "aoc-completionist-blocks", + AocCompletionistBlockViewSet ) bot_router.register( 'bot-settings', @@ -37,12 +41,8 @@ bot_router.register( DocumentationLinkViewSet ) bot_router.register( - "aoc-account-links", - AocAccountLinkViewSet -) -bot_router.register( - "aoc-completionist-blocks", - AocCompletionistBlockViewSet + 'filter-lists', + FilterListViewSet ) bot_router.register( 'infractions', diff --git a/pydis_site/apps/api/viewsets/__init__.py b/pydis_site/apps/api/viewsets/__init__.py index 5fc1d64f..a62a9c01 100644 --- a/pydis_site/apps/api/viewsets/__init__.py +++ b/pydis_site/apps/api/viewsets/__init__.py @@ -1,9 +1,9 @@ # flake8: noqa from .bot import ( - FilterListViewSet, BotSettingViewSet, DeletedMessageViewSet, DocumentationLinkViewSet, + FilterListViewSet, InfractionViewSet, NominationViewSet, OffensiveMessageViewSet, -- cgit v1.2.3 From 0aed5f7913e7ce268ddb56127f84a5386ede5739 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Sat, 19 Feb 2022 17:59:26 +0000 Subject: Add support for BumpedThreads to be stored in site Following our move to use Redis as just a cache, this PR allows the site to store a list of threads that need to be bumped. The bot will interact with this within the ThreadBumper cog. --- .../apps/api/migrations/0081_bumpedthread.py | 22 ++++++++ pydis_site/apps/api/models/__init__.py | 1 + pydis_site/apps/api/models/bot/__init__.py | 1 + pydis_site/apps/api/models/bot/bumped_thread.py | 22 ++++++++ pydis_site/apps/api/serializers.py | 11 ++++ pydis_site/apps/api/urls.py | 5 ++ pydis_site/apps/api/viewsets/__init__.py | 1 + pydis_site/apps/api/viewsets/bot/__init__.py | 1 + pydis_site/apps/api/viewsets/bot/bumped_thread.py | 65 ++++++++++++++++++++++ 9 files changed, 129 insertions(+) create mode 100644 pydis_site/apps/api/migrations/0081_bumpedthread.py create mode 100644 pydis_site/apps/api/models/bot/bumped_thread.py create mode 100644 pydis_site/apps/api/viewsets/bot/bumped_thread.py (limited to 'pydis_site/apps/api') diff --git a/pydis_site/apps/api/migrations/0081_bumpedthread.py b/pydis_site/apps/api/migrations/0081_bumpedthread.py new file mode 100644 index 00000000..03e66cc1 --- /dev/null +++ b/pydis_site/apps/api/migrations/0081_bumpedthread.py @@ -0,0 +1,22 @@ +# Generated by Django 3.1.14 on 2022-02-19 16:26 + +import django.core.validators +from django.db import migrations, models +import pydis_site.apps.api.models.mixins + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0080_add_aoc_tables'), + ] + + operations = [ + migrations.CreateModel( + name='BumpedThread', + fields=[ + ('thread_id', models.BigIntegerField(help_text='The thread ID that should be bumped.', primary_key=True, serialize=False, validators=[django.core.validators.MinValueValidator(limit_value=0, message='Thread IDs cannot be negative.')], verbose_name='Thread ID')), + ], + bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), + ), + ] diff --git a/pydis_site/apps/api/models/__init__.py b/pydis_site/apps/api/models/__init__.py index e83473c9..a197e988 100644 --- a/pydis_site/apps/api/models/__init__.py +++ b/pydis_site/apps/api/models/__init__.py @@ -1,6 +1,7 @@ # flake8: noqa from .bot import ( BotSetting, + BumpedThread, DocumentationLink, DeletedMessage, FilterList, diff --git a/pydis_site/apps/api/models/bot/__init__.py b/pydis_site/apps/api/models/bot/__init__.py index 64676fdb..013bb85e 100644 --- a/pydis_site/apps/api/models/bot/__init__.py +++ b/pydis_site/apps/api/models/bot/__init__.py @@ -1,5 +1,6 @@ # flake8: noqa from .bot_setting import BotSetting +from .bumped_thread import BumpedThread from .deleted_message import DeletedMessage from .documentation_link import DocumentationLink from .filter_list import FilterList diff --git a/pydis_site/apps/api/models/bot/bumped_thread.py b/pydis_site/apps/api/models/bot/bumped_thread.py new file mode 100644 index 00000000..cdf9a950 --- /dev/null +++ b/pydis_site/apps/api/models/bot/bumped_thread.py @@ -0,0 +1,22 @@ +from django.core.validators import MinValueValidator +from django.db import models + +from pydis_site.apps.api.models.mixins import ModelReprMixin + + +class BumpedThread(ModelReprMixin, models.Model): + """A list of thread IDs to be bumped.""" + + thread_id = models.BigIntegerField( + primary_key=True, + help_text=( + "The thread ID that should be bumped." + ), + validators=( + MinValueValidator( + limit_value=0, + message="Thread IDs cannot be negative." + ), + ), + verbose_name="Thread ID", + ) diff --git a/pydis_site/apps/api/serializers.py b/pydis_site/apps/api/serializers.py index c97f7dba..b9e06081 100644 --- a/pydis_site/apps/api/serializers.py +++ b/pydis_site/apps/api/serializers.py @@ -16,6 +16,7 @@ from .models import ( AocAccountLink, AocCompletionistBlock, BotSetting, + BumpedThread, DeletedMessage, DocumentationLink, FilterList, @@ -41,6 +42,16 @@ class BotSettingSerializer(ModelSerializer): fields = ('name', 'data') +class BumpedThreadSerializer(ModelSerializer): + """A class providing (de-)serialization of `BumpedThread` instances.""" + + class Meta: + """Metadata defined for the Django REST Framework.""" + + model = BumpedThread + fields = ('thread_id',) + + class DeletedMessageSerializer(ModelSerializer): """ A class providing (de-)serialization of `DeletedMessage` instances. diff --git a/pydis_site/apps/api/urls.py b/pydis_site/apps/api/urls.py index 6b881fac..1e564b29 100644 --- a/pydis_site/apps/api/urls.py +++ b/pydis_site/apps/api/urls.py @@ -6,6 +6,7 @@ from .viewsets import ( AocAccountLinkViewSet, AocCompletionistBlockViewSet, BotSettingViewSet, + BumpedThreadViewSet, DeletedMessageViewSet, DocumentationLinkViewSet, FilterListViewSet, @@ -32,6 +33,10 @@ bot_router.register( 'bot-settings', BotSettingViewSet ) +bot_router.register( + 'bumped-threads', + BumpedThreadViewSet +) bot_router.register( 'deleted-messages', DeletedMessageViewSet diff --git a/pydis_site/apps/api/viewsets/__init__.py b/pydis_site/apps/api/viewsets/__init__.py index a62a9c01..ec52416a 100644 --- a/pydis_site/apps/api/viewsets/__init__.py +++ b/pydis_site/apps/api/viewsets/__init__.py @@ -1,6 +1,7 @@ # flake8: noqa from .bot import ( BotSettingViewSet, + BumpedThreadViewSet, DeletedMessageViewSet, DocumentationLinkViewSet, FilterListViewSet, diff --git a/pydis_site/apps/api/viewsets/bot/__init__.py b/pydis_site/apps/api/viewsets/bot/__init__.py index f1d84729..262aa59f 100644 --- a/pydis_site/apps/api/viewsets/bot/__init__.py +++ b/pydis_site/apps/api/viewsets/bot/__init__.py @@ -1,6 +1,7 @@ # flake8: noqa from .filter_list import FilterListViewSet from .bot_setting import BotSettingViewSet +from .bumped_thread import BumpedThreadViewSet from .deleted_message import DeletedMessageViewSet from .documentation_link import DocumentationLinkViewSet from .infraction import InfractionViewSet diff --git a/pydis_site/apps/api/viewsets/bot/bumped_thread.py b/pydis_site/apps/api/viewsets/bot/bumped_thread.py new file mode 100644 index 00000000..6594ac6e --- /dev/null +++ b/pydis_site/apps/api/viewsets/bot/bumped_thread.py @@ -0,0 +1,65 @@ +from rest_framework.mixins import ( + CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin +) +from rest_framework.viewsets import GenericViewSet + +from pydis_site.apps.api.models.bot import BumpedThread +from pydis_site.apps.api.serializers import BumpedThreadSerializer + + +class BumpedThreadViewSet( + GenericViewSet, CreateModelMixin, DestroyModelMixin, RetrieveModelMixin, ListModelMixin +): + """ + View providing CRUD (Minus the U) operations on threads to be bumped. + + ## Routes + ### GET /bot/bumped-threads + Returns all BumpedThread items in the database. + + #### Response format + >>> [ + ... { + ... 'thread_id': "941705627405811793", + ... }, + ... ... + ... ] + + #### Status codes + - 200: returned on success + - 401: returned if unauthenticated + + ### GET /bot/bumped-threads/ + Returns a specific BumpedThread item from the database. + + #### Response format + >>> { + ... 'thread_id': "941705627405811793", + ... } + + #### Status codes + - 200: returned on success + - 404: returned if a BumpedThread with the given thread_id was not found. + + ### POST /bot/bumped-threads + Adds a single BumpedThread item to the database. + + #### Request body + >>> { + ... 'thread_id': int, + ... } + + #### Status codes + - 201: returned on success + - 400: if one of the given fields is invalid + + ### DELETE /bot/bumped-threads/ + Deletes the BumpedThread item with the given `thread_id`. + + #### Status codes + - 204: returned on success + - 404: if a BumpedThread with the given `thread_id` does not exist + """ + + serializer_class = BumpedThreadSerializer + queryset = BumpedThread.objects.all() -- cgit v1.2.3 From 3e9557056c06a39c077b76d718eb35b99a365711 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Wed, 16 Mar 2022 21:46:46 +0000 Subject: Only return list of ints when retrieving all BumpedThreads --- pydis_site/apps/api/serializers.py | 17 +++++++++++++++++ pydis_site/apps/api/viewsets/bot/bumped_thread.py | 7 +------ 2 files changed, 18 insertions(+), 6 deletions(-) (limited to 'pydis_site/apps/api') diff --git a/pydis_site/apps/api/serializers.py b/pydis_site/apps/api/serializers.py index b9e06081..dfdda915 100644 --- a/pydis_site/apps/api/serializers.py +++ b/pydis_site/apps/api/serializers.py @@ -42,12 +42,29 @@ class BotSettingSerializer(ModelSerializer): fields = ('name', 'data') +class ListBumpedThreadSerializer(ListSerializer): + """Custom ListSerializer to override to_representation() when list views are triggered.""" + + def to_representation(self, objects: list[BumpedThread]) -> int: + """ + Used by the `ListModelMixin` to return just the list of bumped thread ids. + + We want to only return the thread_id attribute, hence it is unnecessary + to create a nested dictionary. + + Additionally, this allows bumped thread routes to simply return an + array of thread_id ints instead of objects, saving on bandwidth. + """ + return [obj.thread_id for obj in objects] + + class BumpedThreadSerializer(ModelSerializer): """A class providing (de-)serialization of `BumpedThread` instances.""" class Meta: """Metadata defined for the Django REST Framework.""" + list_serializer_class = ListBumpedThreadSerializer model = BumpedThread fields = ('thread_id',) diff --git a/pydis_site/apps/api/viewsets/bot/bumped_thread.py b/pydis_site/apps/api/viewsets/bot/bumped_thread.py index 6594ac6e..0972379b 100644 --- a/pydis_site/apps/api/viewsets/bot/bumped_thread.py +++ b/pydis_site/apps/api/viewsets/bot/bumped_thread.py @@ -18,12 +18,7 @@ class BumpedThreadViewSet( Returns all BumpedThread items in the database. #### Response format - >>> [ - ... { - ... 'thread_id': "941705627405811793", - ... }, - ... ... - ... ] + >>> list[int] #### Status codes - 200: returned on success -- cgit v1.2.3 From a0180619e77d884aeedb1a89748c0115c8ec8c56 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Wed, 16 Mar 2022 21:47:57 +0000 Subject: Don't return the BumpedThread object when retrieving single We only need to check for existence, so sending the full object isn't needed. --- pydis_site/apps/api/viewsets/bot/bumped_thread.py | 24 ++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'pydis_site/apps/api') diff --git a/pydis_site/apps/api/viewsets/bot/bumped_thread.py b/pydis_site/apps/api/viewsets/bot/bumped_thread.py index 0972379b..9d77bb6b 100644 --- a/pydis_site/apps/api/viewsets/bot/bumped_thread.py +++ b/pydis_site/apps/api/viewsets/bot/bumped_thread.py @@ -1,6 +1,8 @@ from rest_framework.mixins import ( - CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin + CreateModelMixin, DestroyModelMixin, ListModelMixin ) +from rest_framework.request import Request +from rest_framework.response import Response from rest_framework.viewsets import GenericViewSet from pydis_site.apps.api.models.bot import BumpedThread @@ -8,7 +10,7 @@ from pydis_site.apps.api.serializers import BumpedThreadSerializer class BumpedThreadViewSet( - GenericViewSet, CreateModelMixin, DestroyModelMixin, RetrieveModelMixin, ListModelMixin + GenericViewSet, CreateModelMixin, DestroyModelMixin, ListModelMixin ): """ View providing CRUD (Minus the U) operations on threads to be bumped. @@ -25,15 +27,10 @@ class BumpedThreadViewSet( - 401: returned if unauthenticated ### GET /bot/bumped-threads/ - Returns a specific BumpedThread item from the database. - - #### Response format - >>> { - ... 'thread_id': "941705627405811793", - ... } + Returns whether a specific BumpedThread exists in the database. #### Status codes - - 200: returned on success + - 204: returned on success - 404: returned if a BumpedThread with the given thread_id was not found. ### POST /bot/bumped-threads @@ -58,3 +55,12 @@ class BumpedThreadViewSet( serializer_class = BumpedThreadSerializer queryset = BumpedThread.objects.all() + + def retrieve(self, request: Request, *args, **kwargs) -> Response: + """ + DRF method for checking if the given BumpedThread exists. + + Called by the Django Rest Framework in response to the corresponding HTTP request. + """ + self.get_object() + return Response(status=204) -- cgit v1.2.3 From cb52e60a631041a08edb213f41cca8befba4bf7e Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Wed, 16 Mar 2022 23:17:46 +0000 Subject: Add tests for custom BumpedThread impl --- pydis_site/apps/api/tests/test_bumped_threads.py | 63 ++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 pydis_site/apps/api/tests/test_bumped_threads.py (limited to 'pydis_site/apps/api') diff --git a/pydis_site/apps/api/tests/test_bumped_threads.py b/pydis_site/apps/api/tests/test_bumped_threads.py new file mode 100644 index 00000000..316e3f0b --- /dev/null +++ b/pydis_site/apps/api/tests/test_bumped_threads.py @@ -0,0 +1,63 @@ +from django.urls import reverse + +from .base import AuthenticatedAPITestCase +from ..models import BumpedThread + + +class UnauthedBumpedThreadAPITests(AuthenticatedAPITestCase): + def setUp(self): + super().setUp() + self.client.force_authenticate(user=None) + + def test_detail_lookup_returns_401(self): + url = reverse('api:bot:bumpedthread-detail', args=(1,)) + response = self.client.get(url) + + self.assertEqual(response.status_code, 401) + + def test_list_returns_401(self): + url = reverse('api:bot:bumpedthread-list') + response = self.client.get(url) + + self.assertEqual(response.status_code, 401) + + def test_create_returns_401(self): + url = reverse('api:bot:bumpedthread-list') + response = self.client.post(url, {"thread_id": 3}) + + self.assertEqual(response.status_code, 401) + + def test_delete_returns_401(self): + url = reverse('api:bot:bumpedthread-detail', args=(1,)) + response = self.client.delete(url) + + self.assertEqual(response.status_code, 401) + + +class BumpedThreadAPITests(AuthenticatedAPITestCase): + @classmethod + def setUpTestData(cls): + cls.thread1 = BumpedThread.objects.create( + thread_id=1234, + ) + + def test_returns_bumped_threads_as_flat_list(self): + url = reverse('api:bot:bumpedthread-list') + + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json(), [1234]) + + def test_returns_204_for_existing_data(self): + url = reverse('api:bot:bumpedthread-detail', args=(1234,)) + + response = self.client.get(url) + self.assertEqual(response.status_code, 204) + self.assertEqual(response.content, b"") + + def test_returns_404_for_non_existing_data(self): + url = reverse('api:bot:bumpedthread-detail', args=(42,)) + + response = self.client.get(url) + self.assertEqual(response.status_code, 404) + self.assertEqual(response.json(), {"detail": "Not found."}) -- cgit v1.2.3 From 429d98a5349b55e63f93e6192d8b2b35262dc60b Mon Sep 17 00:00:00 2001 From: ChrisJL Date: Fri, 18 Mar 2022 15:11:34 +0000 Subject: fixup: don't use "We" in docstring Co-authored-by: Mark <1515135+MarkKoz@users.noreply.github.com> --- pydis_site/apps/api/serializers.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'pydis_site/apps/api') diff --git a/pydis_site/apps/api/serializers.py b/pydis_site/apps/api/serializers.py index dfdda915..e53ccffa 100644 --- a/pydis_site/apps/api/serializers.py +++ b/pydis_site/apps/api/serializers.py @@ -49,8 +49,7 @@ class ListBumpedThreadSerializer(ListSerializer): """ Used by the `ListModelMixin` to return just the list of bumped thread ids. - We want to only return the thread_id attribute, hence it is unnecessary - to create a nested dictionary. + Only the thread_id field is useful, hence it is unnecessary to create a nested dictionary. Additionally, this allows bumped thread routes to simply return an array of thread_id ints instead of objects, saving on bandwidth. -- cgit v1.2.3 From a4eda4a2da65a1a7f903a8c5b1da37c366baee0b Mon Sep 17 00:00:00 2001 From: D0rs4n <41237606+D0rs4n@users.noreply.github.com> Date: Wed, 13 Apr 2022 23:13:02 +0200 Subject: Adjust filtering settings for the AoC link viewset --- pydis_site/apps/api/viewsets/bot/aoc_link.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pydis_site/apps/api') diff --git a/pydis_site/apps/api/viewsets/bot/aoc_link.py b/pydis_site/apps/api/viewsets/bot/aoc_link.py index 9f22c1a1..c7a96629 100644 --- a/pydis_site/apps/api/viewsets/bot/aoc_link.py +++ b/pydis_site/apps/api/viewsets/bot/aoc_link.py @@ -68,4 +68,4 @@ class AocAccountLinkViewSet( serializer_class = AocAccountLinkSerializer queryset = AocAccountLink.objects.all() filter_backends = (DjangoFilterBackend,) - filter_fields = ("user__id",) + filter_fields = ("user__id", "aoc_username") -- cgit v1.2.3 From ebad433d4fa299085fd3bbba4a349de3088fb5d4 Mon Sep 17 00:00:00 2001 From: mina Date: Thu, 21 Apr 2022 17:51:57 -0400 Subject: Add support for BIG SOLIDUS unicode characters for off topic names We must add support for all B I G S O L I D U S. This is necessary. --- .../apps/api/migrations/0082_otn_allow_big_solidus.py | 19 +++++++++++++++++++ .../apps/api/models/bot/off_topic_channel_name.py | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 pydis_site/apps/api/migrations/0082_otn_allow_big_solidus.py (limited to 'pydis_site/apps/api') diff --git a/pydis_site/apps/api/migrations/0082_otn_allow_big_solidus.py b/pydis_site/apps/api/migrations/0082_otn_allow_big_solidus.py new file mode 100644 index 00000000..abbb98ec --- /dev/null +++ b/pydis_site/apps/api/migrations/0082_otn_allow_big_solidus.py @@ -0,0 +1,19 @@ +# Generated by Django 3.1.14 on 2022-04-21 23:29 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0081_bumpedthread'), + ] + + operations = [ + migrations.AlterField( + model_name='offtopicchannelname', + name='name', + field=models.CharField(help_text='The actual channel name that will be used on our Discord server.', max_length=96, primary_key=True, serialize=False, validators=[django.core.validators.RegexValidator(regex="^[a-z0-9\\U0001d5a0-\\U0001d5b9-ǃ?’'<>⧹⧸]+$")]), + ), + ] 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 e9fec114..b380efad 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 @@ -11,7 +11,7 @@ class OffTopicChannelName(ModelReprMixin, models.Model): primary_key=True, max_length=96, validators=( - RegexValidator(regex=r"^[a-z0-9\U0001d5a0-\U0001d5b9-ǃ?’'<>]+$"), + RegexValidator(regex=r"^[a-z0-9\U0001d5a0-\U0001d5b9-ǃ?’'<>⧹⧸]+$"), ), help_text="The actual channel name that will be used on our Discord server." ) -- cgit v1.2.3