diff options
| -rw-r--r-- | api/migrations/0030_reminder.py | 28 | ||||
| -rw-r--r-- | api/models.py | 39 | ||||
| -rw-r--r-- | api/serializers.py | 17 | ||||
| -rw-r--r-- | api/tests/test_models.py | 18 | ||||
| -rw-r--r-- | api/urls.py | 12 | ||||
| -rw-r--r-- | api/viewsets.py | 72 | 
6 files changed, 164 insertions, 22 deletions
| diff --git a/api/migrations/0030_reminder.py b/api/migrations/0030_reminder.py new file mode 100644 index 00000000..f0c14ccb --- /dev/null +++ b/api/migrations/0030_reminder.py @@ -0,0 +1,28 @@ +# Generated by Django 2.1.5 on 2019-01-22 22:17 + +import api.models +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + +    dependencies = [ +        ('api', '0029_add_infraction_type_watch'), +    ] + +    operations = [ +        migrations.CreateModel( +            name='Reminder', +            fields=[ +                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), +                ('active', models.BooleanField(default=True, help_text='Whether this reminder is still active. If not, it has been sent out to the user.')), +                ('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 that the user wants to be reminded of.', max_length=1500)), +                ('expiration', models.DateTimeField(help_text='When this reminder should be sent.')), +                ('author', models.ForeignKey(help_text='The creator of this reminder.', on_delete=django.db.models.deletion.CASCADE, to='api.User')), +            ], +            bases=(api.models.ModelReprMixin, models.Model), +        ), +    ] diff --git a/api/models.py b/api/models.py index 6dce0eb4..5ca274ce 100644 --- a/api/models.py +++ b/api/models.py @@ -369,3 +369,42 @@ class Infraction(ModelReprMixin, models.Model):          if self.hidden:              s += " (hidden)"          return s + + +class Reminder(ModelReprMixin, models.Model): +    """A reminder created by a user.""" + +    active = models.BooleanField( +        default=True, +        help_text=( +            "Whether this reminder is still active. " +            "If not, it has been sent out to the user." +        ) +    ) +    author = models.ForeignKey( +        User, +        on_delete=models.CASCADE, +        help_text="The creator of this reminder." +    ) +    channel_id = models.BigIntegerField( +        help_text=( +            "The channel ID that this message was " +            "sent in, taken from Discord." +        ), +        validators=( +            MinValueValidator( +                limit_value=0, +                message="Channel IDs cannot be negative." +            ), +        ) +    ) +    content = models.CharField( +        max_length=1500, +        help_text="The content that the user wants to be reminded of." +    ) +    expiration = models.DateTimeField( +        help_text="When this reminder should be sent." +    ) + +    def __str__(self): +        return f"{self.content} on {self.expiration} by {self.author}" diff --git a/api/serializers.py b/api/serializers.py index a3ab13e6..fca8008f 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -4,10 +4,11 @@ from rest_framework_bulk import BulkSerializerMixin  from .models import (      DeletedMessage, DocumentationLink,      Infraction, MessageDeletionContext, -    OffTopicChannelName, Role, -    SnakeFact, SnakeIdiom, -    SnakeName, SpecialSnake, -    Tag, User +    OffTopicChannelName, Reminder, +    Role, SnakeFact, +    SnakeIdiom, SnakeName, +    SpecialSnake, Tag, +    User  ) @@ -126,6 +127,14 @@ class SpecialSnakeSerializer(ModelSerializer):          fields = ('name', 'images', 'info') +class ReminderSerializer(ModelSerializer): +    author = PrimaryKeyRelatedField(queryset=User.objects.all()) + +    class Meta: +        model = Reminder +        fields = ('active', 'author', 'channel_id', 'content', 'expiration', 'id') + +  class RoleSerializer(ModelSerializer):      class Meta:          model = Role diff --git a/api/tests/test_models.py b/api/tests/test_models.py index 1fe60c0d..a958419d 100644 --- a/api/tests/test_models.py +++ b/api/tests/test_models.py @@ -6,10 +6,11 @@ from ..models import (      DeletedMessage, DocumentationLink,      Infraction, Message,      MessageDeletionContext, ModelReprMixin, -    OffTopicChannelName, Role, -    SnakeFact, SnakeIdiom, -    SnakeName, SpecialSnake, -    Tag, User +    OffTopicChannelName, Reminder, +    Role, SnakeFact, +    SnakeIdiom, SnakeName, +    SpecialSnake, Tag, +    User  ) @@ -95,6 +96,15 @@ class StringDunderMethodTests(SimpleTestCase):                  user_id=5, actor_id=5, hidden=True,                  type='kick', reason='He terk my jerb!',                  expires_at=dt(5018, 11, 20, 15, 52, tzinfo=timezone.utc) +            ), +            Reminder( +                author=User( +                    id=452, name='billy', +                    discriminator=5, avatar_hash=None +                ), +                channel_id=555, +                content="oh no", +                expiration=dt(5018, 11, 20, 15, 52, tzinfo=timezone.utc)              )          ) diff --git a/api/urls.py b/api/urls.py index 75980d75..c8548df1 100644 --- a/api/urls.py +++ b/api/urls.py @@ -5,10 +5,10 @@ from .views import HealthcheckView, RulesView  from .viewsets import (      DeletedMessageViewSet, DocumentationLinkViewSet,      InfractionViewSet, OffTopicChannelNameViewSet, -    RoleViewSet, SnakeFactViewSet, -    SnakeIdiomViewSet, SnakeNameViewSet, -    SpecialSnakeViewSet, TagViewSet, -    UserViewSet +    ReminderViewSet, RoleViewSet, +    SnakeFactViewSet, SnakeIdiomViewSet, +    SnakeNameViewSet, SpecialSnakeViewSet, +    TagViewSet, UserViewSet  ) @@ -32,8 +32,8 @@ bot_router.register(      base_name='offtopicchannelname'  )  bot_router.register( -    'roles', -    RoleViewSet +    'reminders', +    ReminderViewSet  )  bot_router.register(      'roles', diff --git a/api/viewsets.py b/api/viewsets.py index f8dd13e8..83945fe8 100644 --- a/api/viewsets.py +++ b/api/viewsets.py @@ -15,18 +15,19 @@ from rest_framework_bulk import BulkCreateModelMixin  from .models import (      DocumentationLink, Infraction,      MessageDeletionContext, OffTopicChannelName, -    Role, SnakeFact, -    SnakeIdiom, SnakeName, -    SpecialSnake, Tag, -    User +    Reminder, Role, +    SnakeFact, SnakeIdiom, +    SnakeName, SpecialSnake, +    Tag, User  )  from .serializers import (      DocumentationLinkSerializer, ExpandedInfractionSerializer,      InfractionSerializer, MessageDeletionContextSerializer, -    OffTopicChannelNameSerializer, RoleSerializer, -    SnakeFactSerializer, SnakeIdiomSerializer, -    SnakeNameSerializer, SpecialSnakeSerializer, -    TagSerializer, UserSerializer +    OffTopicChannelNameSerializer, ReminderSerializer, +    RoleSerializer, SnakeFactSerializer, +    SnakeIdiomSerializer, SnakeNameSerializer, +    SpecialSnakeSerializer, TagSerializer, +    UserSerializer  ) @@ -356,6 +357,61 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet):          return Response(serialized.data) +class ReminderViewSet(CreateModelMixin, ListModelMixin, DestroyModelMixin, GenericViewSet): +    """ +    View providing CRUD access to reminders. + +    ## Routes +    ### GET /bot/reminders +    Returns all reminders in the database. + +    #### Response format +    >>> [ +    ...     { +    ...         'active': True, +    ...         'author': 1020103901030, +    ...         'content': "Make dinner", +    ...         'expiration': '5018-11-20T15:52:00Z' +    ...         'id': 11 +    ...     }, +    ...     ... +    ... ] + +    #### Status codes +    - 200: returned on success + +    ### POST /bot/reminders +    Create a new reminder. + +    #### Request body +    >>> { +    ...     'author': int, +    ...     'content': str, +    ...     'expiration': str  # ISO-formatted datetime +    ... } + +    #### Status codes +    - 201: returned on success +    - 400: if the body format is invalid +    - 404: if no user with the given ID could be found + +    ### DELETE /bot/reminders/<id:int> +    Delete the reminder with the given `id`. + +    #### Status codes +    - 204: returned on success +    - 404: if a reminder with the given `id` does not exist + +    ## Authentication +    Requires an API token. +    """ + +    serializer_class = ReminderSerializer +    queryset = Reminder.objects.prefetch_related('author') +    filter_backends = (DjangoFilterBackend, SearchFilter) +    filter_fields = ('active', 'author__id') + +  class RoleViewSet(ModelViewSet):      """      View providing CRUD access to the roles on our server, used | 
