aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Johannes Christ <[email protected]>2019-01-22 23:30:15 +0100
committerGravatar Johannes Christ <[email protected]>2019-01-22 23:30:15 +0100
commit475b3bcd51d5e016dcc3830d49000dda852264a7 (patch)
treedb1d6761af3e4939746a037c482c22b22e09dd15
parentChonk down horrible JOIN performance. (diff)
Add an API endpoint for reminders.
-rw-r--r--api/migrations/0030_reminder.py28
-rw-r--r--api/models.py39
-rw-r--r--api/serializers.py17
-rw-r--r--api/tests/test_models.py18
-rw-r--r--api/urls.py12
-rw-r--r--api/viewsets.py72
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