From 1142588abda79d1b3c5a94449cf8074964df9228 Mon Sep 17 00:00:00 2001 From: Gareth Coles Date: Sun, 21 Apr 2019 22:28:57 +0100 Subject: A whole bunch of docstrings. --- pydis_site/apps/api/viewsets/bot/bot_setting.py | 4 +--- pydis_site/apps/api/viewsets/bot/infraction.py | 28 +++++++++++++++++++++- pydis_site/apps/api/viewsets/bot/nomination.py | 6 +++++ .../api/viewsets/bot/off_topic_channel_name.py | 25 ++++++++++++++++--- pydis_site/apps/api/viewsets/bot/role.py | 5 ++-- pydis_site/apps/api/viewsets/bot/snake_name.py | 2 ++ 6 files changed, 61 insertions(+), 9 deletions(-) (limited to 'pydis_site/apps/api/viewsets/bot') diff --git a/pydis_site/apps/api/viewsets/bot/bot_setting.py b/pydis_site/apps/api/viewsets/bot/bot_setting.py index 5464018a..07f5b170 100644 --- a/pydis_site/apps/api/viewsets/bot/bot_setting.py +++ b/pydis_site/apps/api/viewsets/bot/bot_setting.py @@ -6,9 +6,7 @@ from pydis_site.apps.api.serializers import BotSettingSerializer class BotSettingViewSet(RetrieveModelMixin, UpdateModelMixin, GenericViewSet): - """ - View providing update operations on bot setting routes. - """ + """View providing update operations on bot setting routes.""" serializer_class = BotSettingSerializer queryset = BotSetting.objects.all() diff --git a/pydis_site/apps/api/viewsets/bot/infraction.py b/pydis_site/apps/api/viewsets/bot/infraction.py index 8eacf5c1..4be153e1 100644 --- a/pydis_site/apps/api/viewsets/bot/infraction.py +++ b/pydis_site/apps/api/viewsets/bot/infraction.py @@ -122,7 +122,9 @@ class InfractionViewSet(CreateModelMixin, RetrieveModelMixin, ListModelMixin, Ge search_fields = ('$reason',) frozen_fields = ('id', 'inserted_at', 'type', 'user', 'actor', 'hidden') - def partial_update(self, request, *args, **kwargs): + def partial_update(self, request, *_args, **_kwargs): + """Method that handles the nuts and bolts of updating an Infraction.""" + for field in request.data: if field in self.frozen_fields: raise ValidationError({field: ['This field cannot be updated.']}) @@ -136,20 +138,44 @@ class InfractionViewSet(CreateModelMixin, RetrieveModelMixin, ListModelMixin, Ge @action(url_path='expanded', detail=False) def list_expanded(self, *args, **kwargs): + """ + DRF method for listing Infraction entries. + + Called by the Django Rest Framework in response to the corresponding HTTP request. + """ + self.serializer_class = ExpandedInfractionSerializer return self.list(*args, **kwargs) @list_expanded.mapping.post def create_expanded(self, *args, **kwargs): + """ + DRF method for creating an Infraction. + + Called by the Django Rest Framework in response to the corresponding HTTP request. + """ + self.serializer_class = ExpandedInfractionSerializer return self.create(*args, **kwargs) @action(url_path='expanded', url_name='detail-expanded', detail=True) def retrieve_expanded(self, *args, **kwargs): + """ + DRF method for retrieving a specific Infraction. + + Called by the Django Rest Framework in response to the corresponding HTTP request. + """ + self.serializer_class = ExpandedInfractionSerializer return self.retrieve(*args, **kwargs) @retrieve_expanded.mapping.patch def partial_update_expanded(self, *args, **kwargs): + """ + DRF method for updating an Infraction. + + Called by the Django Rest Framework in response to the corresponding HTTP request. + """ + self.serializer_class = ExpandedInfractionSerializer return self.partial_update(*args, **kwargs) diff --git a/pydis_site/apps/api/viewsets/bot/nomination.py b/pydis_site/apps/api/viewsets/bot/nomination.py index 725ae176..2b75f10b 100644 --- a/pydis_site/apps/api/viewsets/bot/nomination.py +++ b/pydis_site/apps/api/viewsets/bot/nomination.py @@ -13,6 +13,12 @@ class NominationViewSet(ModelViewSet): frozen_fields = ('author', 'inserted_at', 'user') def update(self, request, *args, **kwargs): + """ + DRF method for updating a Nomination. + + Called by the Django Rest Framework in response to the corresponding HTTP request. + """ + for field in request.data: if field in self.frozen_fields: raise ValidationError({field: ['This field cannot be updated.']}) 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 df51917d..4976c291 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 @@ -11,8 +11,7 @@ from pydis_site.apps.api.serializers import OffTopicChannelNameSerializer class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet): """ - View of off-topic channel names used by the bot - to rotate our off-topic names on a daily basis. + View of off-topic channel names used by the bot to rotate our off-topic names on a daily basis. ## Routes ### GET /bot/off-topic-channel-names @@ -56,14 +55,28 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet): serializer_class = OffTopicChannelNameSerializer def get_object(self): + """ + Returns the OffTopicChannelName entry for this request, if it exists. + + 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) def get_queryset(self): + """Returns a queryset that covers the entire OffTopicChannelName table.""" + return OffTopicChannelName.objects.all() def create(self, request): + """ + DRF method for creating a new OffTopicChannelName. + + Called by the Django Rest Framework in response to the corresponding HTTP request. + """ + if 'name' in request.query_params: create_data = {'name': request.query_params['name']} serializer = OffTopicChannelNameSerializer(data=create_data) @@ -76,7 +89,13 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet): 'name': ["This query parameter is required."] }) - def list(self, request): # noqa + def list(self, request): + """ + DRF method for listing OffTopicChannelName entries. + + Called by the Django Rest Framework in response to the corresponding HTTP request. + """ + if 'random_items' in request.query_params: param = request.query_params['random_items'] try: diff --git a/pydis_site/apps/api/viewsets/bot/role.py b/pydis_site/apps/api/viewsets/bot/role.py index 0131b374..213f0a19 100644 --- a/pydis_site/apps/api/viewsets/bot/role.py +++ b/pydis_site/apps/api/viewsets/bot/role.py @@ -6,8 +6,9 @@ from pydis_site.apps.api.serializers import RoleSerializer class RoleViewSet(ModelViewSet): """ - View providing CRUD access to the roles on our server, used - by the bot to keep a mirror of our server's roles on the site. + View providing CRUD access to the roles on our server. + + This is used by the bot to keep a mirror of our server's roles on the site. ## Routes ### GET /bot/roles diff --git a/pydis_site/apps/api/viewsets/bot/snake_name.py b/pydis_site/apps/api/viewsets/bot/snake_name.py index 991706f5..91adae00 100644 --- a/pydis_site/apps/api/viewsets/bot/snake_name.py +++ b/pydis_site/apps/api/viewsets/bot/snake_name.py @@ -41,6 +41,8 @@ class SnakeNameViewSet(ViewSet): serializer_class = SnakeNameSerializer def get_queryset(self): + """Returns a queryset that covers the entire SnakeName table.""" + return SnakeName.objects.all() def list(self, request): # noqa -- cgit v1.2.3 From 3a0878d8947e864dcf92e7e317e9b5e7c57c3356 Mon Sep 17 00:00:00 2001 From: Johannes Christ Date: Mon, 22 Apr 2019 10:54:46 +0200 Subject: Add remaining docstrings. --- pydis_site/apps/api/serializers.py | 67 ++++++++++++++++++++++++++ pydis_site/apps/api/viewsets/bot/nomination.py | 3 +- 2 files changed, 69 insertions(+), 1 deletion(-) (limited to 'pydis_site/apps/api/viewsets/bot') diff --git a/pydis_site/apps/api/serializers.py b/pydis_site/apps/api/serializers.py index 1f19a371..905a7f82 100644 --- a/pydis_site/apps/api/serializers.py +++ b/pydis_site/apps/api/serializers.py @@ -1,3 +1,5 @@ +"""Converters from Django models to data interchange formats and back.""" + from rest_framework.serializers import ModelSerializer, PrimaryKeyRelatedField, ValidationError from rest_framework_bulk import BulkSerializerMixin @@ -14,6 +16,8 @@ from .models import ( class BotSettingSerializer(ModelSerializer): + """A class providing (de-)serialization of `BotSetting` instances.""" + class Meta: """Metadata defined for the Django REST Framework.""" @@ -22,6 +26,14 @@ class BotSettingSerializer(ModelSerializer): class DeletedMessageSerializer(ModelSerializer): + """ + A class providing (de-)serialization of `DeletedMessage` instances. + + The serializer generally requires a valid `deletion_context` to be + given, which should be created beforehand. See the `DeletedMessage` + model for more information. + """ + author = PrimaryKeyRelatedField( queryset=User.objects.all() ) @@ -44,6 +56,8 @@ class DeletedMessageSerializer(ModelSerializer): class MessageDeletionContextSerializer(ModelSerializer): + """A class providing (de-)serialization of `MessageDeletionContext` instances.""" + deletedmessage_set = DeletedMessageSerializer(many=True) class Meta: @@ -54,6 +68,14 @@ class MessageDeletionContextSerializer(ModelSerializer): depth = 1 def create(self, validated_data): + """ + Return a `MessageDeletionContext` based on the given data. + + In addition to the normal attributes expected by the `MessageDeletionContext` model + itself, this serializer also allows for passing the `deletedmessage_set` element + which contains messages that were deleted as part of this context. + """ + messages = validated_data.pop('deletedmessage_set') deletion_context = MessageDeletionContext.objects.create(**validated_data) for message in messages: @@ -66,6 +88,8 @@ class MessageDeletionContextSerializer(ModelSerializer): class DocumentationLinkSerializer(ModelSerializer): + """A class providing (de-)serialization of `DocumentationLink` instances.""" + class Meta: """Metadata defined for the Django REST Framework.""" @@ -74,6 +98,8 @@ class DocumentationLinkSerializer(ModelSerializer): class InfractionSerializer(ModelSerializer): + """A class providing (de-)serialization of `Infraction` instances.""" + class Meta: """Metadata defined for the Django REST Framework.""" @@ -83,6 +109,8 @@ class InfractionSerializer(ModelSerializer): ) def validate(self, attrs): + """Validate data constraints for the given data and abort if it is invalid.""" + infr_type = attrs.get('type') expires_at = attrs.get('expires_at') @@ -97,7 +125,15 @@ class InfractionSerializer(ModelSerializer): class ExpandedInfractionSerializer(InfractionSerializer): + """A class providing expanded (de-)serialization of `Infraction` instances. + + In addition to the fields of `Infraction` objects themselves, this + serializer also attaches the `user` and `actor` fields when serializing. + """ + def to_representation(self, instance): + """Return the dictionary representation of this infraction.""" + ret = super().to_representation(instance) user = User.objects.get(id=ret['user']) @@ -112,6 +148,8 @@ class ExpandedInfractionSerializer(InfractionSerializer): class LogEntrySerializer(ModelSerializer): + """A class providing (de-)serialization of `LogEntry` instances.""" + class Meta: """Metadata defined for the Django REST Framework.""" @@ -123,6 +161,8 @@ class LogEntrySerializer(ModelSerializer): class OffTopicChannelNameSerializer(ModelSerializer): + """A class providing (de-)serialization of `OffTopicChannelName` instances.""" + class Meta: """Metadata defined for the Django REST Framework.""" @@ -130,10 +170,21 @@ class OffTopicChannelNameSerializer(ModelSerializer): fields = ('name',) def to_representation(self, obj): + """ + 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 class SnakeFactSerializer(ModelSerializer): + """A class providing (de-)serialization of `SnakeFact` instances.""" + class Meta: """Metadata defined for the Django REST Framework.""" @@ -142,6 +193,8 @@ class SnakeFactSerializer(ModelSerializer): class SnakeIdiomSerializer(ModelSerializer): + """A class providing (de-)serialization of `SnakeIdiom` instances.""" + class Meta: """Metadata defined for the Django REST Framework.""" @@ -150,6 +203,8 @@ class SnakeIdiomSerializer(ModelSerializer): class SnakeNameSerializer(ModelSerializer): + """A class providing (de-)serialization of `SnakeName` instances.""" + class Meta: """Metadata defined for the Django REST Framework.""" @@ -158,6 +213,8 @@ class SnakeNameSerializer(ModelSerializer): class SpecialSnakeSerializer(ModelSerializer): + """A class providing (de-)serialization of `SpecialSnake` instances.""" + class Meta: """Metadata defined for the Django REST Framework.""" @@ -166,6 +223,8 @@ class SpecialSnakeSerializer(ModelSerializer): class ReminderSerializer(ModelSerializer): + """A class providing (de-)serialization of `Reminder` instances.""" + author = PrimaryKeyRelatedField(queryset=User.objects.all()) class Meta: @@ -176,6 +235,8 @@ class ReminderSerializer(ModelSerializer): class RoleSerializer(ModelSerializer): + """A class providing (de-)serialization of `Role` instances.""" + class Meta: """Metadata defined for the Django REST Framework.""" @@ -184,6 +245,8 @@ class RoleSerializer(ModelSerializer): class TagSerializer(ModelSerializer): + """A class providing (de-)serialization of `Tag` instances.""" + class Meta: """Metadata defined for the Django REST Framework.""" @@ -192,6 +255,8 @@ class TagSerializer(ModelSerializer): class UserSerializer(BulkSerializerMixin, ModelSerializer): + """A class providing (de-)serialization of `User` instances.""" + roles = PrimaryKeyRelatedField(many=True, queryset=Role.objects.all(), required=False) class Meta: @@ -203,6 +268,8 @@ class UserSerializer(BulkSerializerMixin, ModelSerializer): class NominationSerializer(ModelSerializer): + """A class providing (de-)serialization of `Nomination` instances.""" + author = PrimaryKeyRelatedField(queryset=User.objects.all()) user = PrimaryKeyRelatedField(queryset=User.objects.all()) diff --git a/pydis_site/apps/api/viewsets/bot/nomination.py b/pydis_site/apps/api/viewsets/bot/nomination.py index 2b75f10b..62f5dd48 100644 --- a/pydis_site/apps/api/viewsets/bot/nomination.py +++ b/pydis_site/apps/api/viewsets/bot/nomination.py @@ -7,7 +7,8 @@ from pydis_site.apps.api.serializers import NominationSerializer class NominationViewSet(ModelViewSet): - # TODO: doc me + """View providing CRUD operations on helper nominations done through the bot.""" + serializer_class = NominationSerializer queryset = Nomination.objects.prefetch_related('author', 'user') frozen_fields = ('author', 'inserted_at', 'user') -- cgit v1.2.3 From 341e12e66f56928cebf29be3137ec7f4558cd421 Mon Sep 17 00:00:00 2001 From: Gareth Coles Date: Mon, 22 Apr 2019 19:24:58 +0100 Subject: Address the latest review. ✈ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pydis_site/apps/api/models/bot/bot_setting.py | 1 + pydis_site/apps/api/models/bot/documentation_link.py | 1 + pydis_site/apps/api/models/bot/infraction.py | 1 + pydis_site/apps/api/models/bot/message.py | 1 + pydis_site/apps/api/models/bot/off_topic_channel_name.py | 1 + pydis_site/apps/api/models/bot/reminder.py | 1 + pydis_site/apps/api/models/bot/role.py | 1 + pydis_site/apps/api/models/bot/snake_fact.py | 1 + pydis_site/apps/api/models/bot/snake_idiom.py | 1 + pydis_site/apps/api/models/bot/snake_name.py | 1 + pydis_site/apps/api/models/bot/special_snake.py | 1 + pydis_site/apps/api/models/bot/tag.py | 1 + pydis_site/apps/api/models/bot/user.py | 1 + pydis_site/apps/api/viewsets/bot/snake_name.py | 8 +++++++- 14 files changed, 20 insertions(+), 1 deletion(-) (limited to 'pydis_site/apps/api/viewsets/bot') diff --git a/pydis_site/apps/api/models/bot/bot_setting.py b/pydis_site/apps/api/models/bot/bot_setting.py index a52f3e34..ee9838b7 100644 --- a/pydis_site/apps/api/models/bot/bot_setting.py +++ b/pydis_site/apps/api/models/bot/bot_setting.py @@ -7,6 +7,7 @@ from pydis_site.apps.api.models.utils import ModelReprMixin def validate_bot_setting_name(name): """Raises a ValidationError if the given name is not a known setting.""" + known_settings = ( 'defcon', ) diff --git a/pydis_site/apps/api/models/bot/documentation_link.py b/pydis_site/apps/api/models/bot/documentation_link.py index f844ae04..30379396 100644 --- a/pydis_site/apps/api/models/bot/documentation_link.py +++ b/pydis_site/apps/api/models/bot/documentation_link.py @@ -23,4 +23,5 @@ class DocumentationLink(ModelReprMixin, models.Model): def __str__(self): """Returns the package and URL for the current documentation link, for display purposes.""" + return f"{self.package} - {self.base_url}" diff --git a/pydis_site/apps/api/models/bot/infraction.py b/pydis_site/apps/api/models/bot/infraction.py index da91d6c2..7669352f 100644 --- a/pydis_site/apps/api/models/bot/infraction.py +++ b/pydis_site/apps/api/models/bot/infraction.py @@ -60,6 +60,7 @@ class Infraction(ModelReprMixin, models.Model): def __str__(self): """Returns some info on the current infraction, for display purposes.""" + s = f"#{self.id}: {self.type} on {self.user_id}" if self.expires_at: s += f" until {self.expires_at}" diff --git a/pydis_site/apps/api/models/bot/message.py b/pydis_site/apps/api/models/bot/message.py index 7332cc8d..6b566620 100644 --- a/pydis_site/apps/api/models/bot/message.py +++ b/pydis_site/apps/api/models/bot/message.py @@ -51,4 +51,5 @@ class Message(ModelReprMixin, models.Model): class Meta: """Metadata provided for Django's ORM.""" + abstract = True 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 0891f811..2f55a131 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,4 +16,5 @@ class OffTopicChannelName(ModelReprMixin, models.Model): def __str__(self): """Returns the current off-topic name, for display purposes.""" + return self.name diff --git a/pydis_site/apps/api/models/bot/reminder.py b/pydis_site/apps/api/models/bot/reminder.py index decc9391..ae45b5de 100644 --- a/pydis_site/apps/api/models/bot/reminder.py +++ b/pydis_site/apps/api/models/bot/reminder.py @@ -42,4 +42,5 @@ class Reminder(ModelReprMixin, models.Model): def __str__(self): """Returns some info on the current reminder, for display purposes.""" + return f"{self.content} on {self.expiration} by {self.author}" diff --git a/pydis_site/apps/api/models/bot/role.py b/pydis_site/apps/api/models/bot/role.py index 34e74009..ad043bd6 100644 --- a/pydis_site/apps/api/models/bot/role.py +++ b/pydis_site/apps/api/models/bot/role.py @@ -46,4 +46,5 @@ class Role(ModelReprMixin, models.Model): def __str__(self): """Returns the name of the current role, for display purposes.""" + return self.name diff --git a/pydis_site/apps/api/models/bot/snake_fact.py b/pydis_site/apps/api/models/bot/snake_fact.py index e4486d41..c960cbc4 100644 --- a/pydis_site/apps/api/models/bot/snake_fact.py +++ b/pydis_site/apps/api/models/bot/snake_fact.py @@ -14,4 +14,5 @@ class SnakeFact(ModelReprMixin, models.Model): def __str__(self): """Returns the current snake fact, for display purposes.""" + return self.fact diff --git a/pydis_site/apps/api/models/bot/snake_idiom.py b/pydis_site/apps/api/models/bot/snake_idiom.py index 73ce25eb..0e8f5e94 100644 --- a/pydis_site/apps/api/models/bot/snake_idiom.py +++ b/pydis_site/apps/api/models/bot/snake_idiom.py @@ -14,4 +14,5 @@ class SnakeIdiom(ModelReprMixin, models.Model): def __str__(self): """Returns the current idiom, for display purposes.""" + return self.idiom diff --git a/pydis_site/apps/api/models/bot/snake_name.py b/pydis_site/apps/api/models/bot/snake_name.py index 6d33f872..b6ea6202 100644 --- a/pydis_site/apps/api/models/bot/snake_name.py +++ b/pydis_site/apps/api/models/bot/snake_name.py @@ -21,4 +21,5 @@ class SnakeName(ModelReprMixin, models.Model): def __str__(self): """Returns the regular and scientific name of the current snake, for display purposes.""" + return f"{self.name} ({self.scientific})" diff --git a/pydis_site/apps/api/models/bot/special_snake.py b/pydis_site/apps/api/models/bot/special_snake.py index 5d38ab6f..662ff8e3 100644 --- a/pydis_site/apps/api/models/bot/special_snake.py +++ b/pydis_site/apps/api/models/bot/special_snake.py @@ -24,4 +24,5 @@ class SpecialSnake(ModelReprMixin, models.Model): def __str__(self): """Returns the name of the current snake, for display purposes.""" + return self.name diff --git a/pydis_site/apps/api/models/bot/tag.py b/pydis_site/apps/api/models/bot/tag.py index b75ccec9..99819e42 100644 --- a/pydis_site/apps/api/models/bot/tag.py +++ b/pydis_site/apps/api/models/bot/tag.py @@ -184,4 +184,5 @@ class Tag(ModelReprMixin, models.Model): def __str__(self): """Returns the title of this tag, for display purposes.""" + return self.title diff --git a/pydis_site/apps/api/models/bot/user.py b/pydis_site/apps/api/models/bot/user.py index d4deb630..8b995b59 100644 --- a/pydis_site/apps/api/models/bot/user.py +++ b/pydis_site/apps/api/models/bot/user.py @@ -50,4 +50,5 @@ class User(ModelReprMixin, models.Model): def __str__(self): """Returns the name and discriminator for the current user, for display purposes.""" + return f"{self.name}#{self.discriminator}" diff --git a/pydis_site/apps/api/viewsets/bot/snake_name.py b/pydis_site/apps/api/viewsets/bot/snake_name.py index 91adae00..8e63a542 100644 --- a/pydis_site/apps/api/viewsets/bot/snake_name.py +++ b/pydis_site/apps/api/viewsets/bot/snake_name.py @@ -45,7 +45,13 @@ class SnakeNameViewSet(ViewSet): return SnakeName.objects.all() - def list(self, request): # noqa + def list(self, request): + """ + DRF method for listing SnakeName entries. + + Called by the Django Rest Framework in response to the corresponding HTTP request. + """ + if request.query_params.get('get_all'): queryset = self.get_queryset() serialized = self.serializer_class(queryset, many=True) -- cgit v1.2.3