Added SubChannels backend

SubChannels are target discord channels for subscriptions to send their content into.
This commit is contained in:
Corban-Lee Jones 2024-04-28 15:39:27 +01:00
parent 1e7ce34df6
commit 14167e05be
6 changed files with 204 additions and 20 deletions

View File

@ -4,7 +4,7 @@ import logging
from rest_framework import serializers
from apps.home.models import Subscription, SavedGuilds
from apps.home.models import SubChannel, Subscription, SavedGuilds
log = logging.getLogger(__name__)
@ -108,15 +108,21 @@ class DynamicModelSerializer(serializers.ModelSerializer):
abstract = True
class SubChannelSerializer(DynamicModelSerializer):
"""
Serializer for SubChannel Model.
"""
class Meta:
model = SubChannel
fields = ("id", "channel_id", "subscription")
class SubscriptionSerializer(DynamicModelSerializer):
"""
Serializer for the Subscription Model.
"""
# image = serializers.ImageField(required=False)
# server = serializers.CharField()
# targets = SubscriptionTargetSerializer(many=True, required=False)
class Meta:
model = Subscription
fields = ("id", "name", "url", "guild_id", "channels_count", "creation_datetime", "extra_notes", "active")

View File

@ -4,6 +4,8 @@ from django.urls import path, include
from rest_framework.authtoken.views import obtain_auth_token
from .views import (
SubChannel_ListView,
SubChannel_DetailView,
Subscription_ListView,
Subscription_DetailView,
SavedGuild_ListView,
@ -15,6 +17,11 @@ urlpatterns = [
path("api-auth/", include("rest_framework.urls", namespace="rest_framework")),
path("api-token-auth/", obtain_auth_token),
path("subchannel/", include([
path("", SubChannel_ListView.as_view(), name="subchannel"),
path("<str:pk>/", SubChannel_DetailView.as_view(), name="subchannel-detail")
])),
path("subscription/", include([
path("", Subscription_ListView.as_view(), name="subscription"),
path("<str:pk>/", Subscription_DetailView.as_view(), name="subscription-detail")

View File

@ -11,8 +11,9 @@ from rest_framework.pagination import PageNumberPagination
from rest_framework.authentication import SessionAuthentication, TokenAuthentication
from rest_framework.parsers import MultiPartParser, FormParser
from apps.home.models import Subscription, SavedGuilds
from apps.home.models import SubChannel, Subscription, SavedGuilds
from .serializers import (
SubChannelSerializer,
SubscriptionSerializer,
SavedGuildSerializer
)
@ -28,6 +29,60 @@ class DefaultPagination(PageNumberPagination):
max_page_size = 25
# =================================================================================================
# SubChannel Views
class SubChannel_ListView(generics.ListCreateAPIView):
"""
View to provide a list of SubChannel model instances.
Can also be used to create a new instance.
Supports: GET, POST
"""
authentication_classes = [SessionAuthentication, TokenAuthentication]
permission_classes = [permissions.IsAuthenticated]
pagination_class = DefaultPagination
serializer_class = SubChannelSerializer
queryset = SubChannel.objects.all().order_by("id")
filter_backends = [filters.SearchFilter, rest_filters.DjangoFilterBackend, filters.OrderingFilter]
filterset_fields = ["id", "channel_id", "subscription_id"]
def post(self, request):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
try:
self.perform_create(serializer)
except IntegrityError as err:
return Response(
{"detail": str(err)},
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
exception=True
)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
class SubChannel_DetailView(generics.RetrieveUpdateDestroyAPIView):
"""
View to provide details on a particular SubChannel model instances.
Supports: GET, PUT, PATCH, DELETE
"""
authentication_classes = [SessionAuthentication, TokenAuthentication]
permission_classes = [permissions.IsAuthenticated]
parser_classes = [MultiPartParser, FormParser]
serializer_class = SubChannelSerializer
queryset = SubChannel.objects.all().order_by("id")
# =================================================================================================
# Subscription Views

View File

@ -0,0 +1,54 @@
# Generated by Django 5.0.1 on 2024-04-28 13:37
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('home', '0002_alter_savedguilds_options_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AlterField(
model_name='savedguilds',
name='added_by',
field=models.ForeignKey(help_text='The user who added created this instance.', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='added by'),
),
migrations.AlterField(
model_name='savedguilds',
name='guild_id',
field=models.CharField(help_text='Discord snowflake ID for the represented guild.', max_length=128, verbose_name='guild id'),
),
migrations.AlterField(
model_name='savedguilds',
name='icon',
field=models.CharField(help_text="Hash for the represented guild's icon.", max_length=128),
),
migrations.AlterField(
model_name='savedguilds',
name='name',
field=models.CharField(help_text='Name of the represented guild.', max_length=128),
),
migrations.AlterField(
model_name='savedguilds',
name='owner',
field=models.BooleanField(default=False, help_text="Does the 'added by' user own this guild?"),
),
migrations.AlterField(
model_name='savedguilds',
name='permissions',
field=models.CharField(help_text='Guild permissions for the user who added this instance.', max_length=64),
),
migrations.CreateModel(
name='SubChannel',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('channel_id', models.CharField(help_text='Discord snowflake ID for the represented Channel.', max_length=128, verbose_name='channel id')),
('subscription_id', models.ForeignKey(help_text='The ID of the linked Subscription, must be unique.', on_delete=django.db.models.deletion.CASCADE, to='home.subscription', unique=True, verbose_name='subscription id')),
],
),
]

View File

@ -0,0 +1,24 @@
# Generated by Django 5.0.1 on 2024-04-28 13:46
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('home', '0003_alter_savedguilds_added_by_and_more'),
]
operations = [
migrations.RemoveField(
model_name='subchannel',
name='subscription_id',
),
migrations.AddField(
model_name='subchannel',
name='subscription',
field=models.ForeignKey(default='1', help_text='The linked Subscription, must be unique.', on_delete=django.db.models.deletion.CASCADE, to='home.subscription', unique=True),
preserve_default=False,
),
]

View File

@ -41,36 +41,47 @@ class IconPathGenerator:
class SavedGuilds(models.Model):
"""
Represents a saved Discord Guild (aka Server).
These are shown in the UI on the sidebar, and can be selected
to see associated Subscriptions.
"""
id = models.AutoField(
primary_key=True
)
id = models.AutoField(primary_key=True)
# Have to use charfield instead of positiveBigIntegerField due to an Sqlite
# issue that rounds down the value
# https://github.com/sequelize/sequelize/issues/9335
guild_id = models.CharField(
verbose_name=_("guild id"),
max_length=128,
help_text=_("Discord snowflake ID for the represented guild.")
)
name = models.CharField(
max_length=128
max_length=128,
help_text=_("Name of the represented guild.")
)
icon = models.CharField(
max_length=128
max_length=128,
help_text=_("Hash for the represented guild's icon.")
)
added_by = models.ForeignKey(to="authentication.DiscordUser", on_delete=models.CASCADE)
added_by = models.ForeignKey(
verbose_name=_("added by"),
to="authentication.DiscordUser",
on_delete=models.CASCADE,
help_text=_("The user who added created this instance.")
)
permissions = models.CharField(
max_length=64
max_length=64,
help_text=_("Guild permissions for the user who added this instance.")
)
owner = models.BooleanField(
default=False
default=False,
help_text=_("Does the 'added by' user own this guild?")
)
class Meta:
@ -81,20 +92,47 @@ class SavedGuilds(models.Model):
verbose_name = "saved guild"
verbose_name_plural = "saved guilds"
get_latest_by = "-creation_datetime"
constraints = [
# Prevent servers from having subscriptions with duplicate names
models.UniqueConstraint(fields=["name", "guild_id"], name="unique name & guild_id pair")
models.UniqueConstraint(
fields=["name", "guild_id"],
name="unique name & guild_id pair"
)
]
def __str__(self):
return self.name
class Subscription(models.Model):
id = models.AutoField(
primary_key=True
class SubChannel(models.Model):
"""
Represents a Discord TextChannel, saved against a Subscription.
SubChannels are used as targets to send content from Subscriptions.
"""
id = models.AutoField(primary_key=True)
# Have to use charfield instead of positiveBigIntegerField due to an Sqlite
# issue that rounds down the value
# https://github.com/sequelize/sequelize/issues/9335
channel_id = models.CharField(
verbose_name=_("channel id"),
max_length=128,
help_text=_("Discord snowflake ID for the represented Channel.")
)
subscription = models.ForeignKey(
to="home.Subscription",
on_delete=models.CASCADE,
unique=True,
help_text=_("The linked Subscription, must be unique.")
)
class Subscription(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(
max_length=32,
null=False,
@ -140,7 +178,7 @@ class Subscription(models.Model):
@property
def channels_count(self):
return 0
return len(SubChannel.objects.filter(subscription=self))
def __str__(self):
return self.name