New model changes
This commit is contained in:
parent
7a78cf1215
commit
404aa3680f
@ -4,13 +4,14 @@ import logging
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from apps.home.models import Subscription, TrackedContent, DiscordChannel
|
||||
from apps.home.models import Subscription, SubscriptionChannel, TrackedContent
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DynamicModelSerializer(serializers.ModelSerializer):
|
||||
"""For use with GET requests, to specify which fields to include or exclude
|
||||
"""
|
||||
For use with GET requests, to specify which fields to include or exclude
|
||||
Mimics some graphql functionality.
|
||||
|
||||
Usage: Inherit your ModelSerializer with this class. Add "only_fields" or
|
||||
@ -104,33 +105,35 @@ class DynamicModelSerializer(serializers.ModelSerializer):
|
||||
abstract = True
|
||||
|
||||
|
||||
class DiscordChannelSerializer(DynamicModelSerializer):
|
||||
"""Serializer for the Discord Channel Model."""
|
||||
|
||||
class Meta:
|
||||
model = DiscordChannel
|
||||
fields = ("id", "creation_datetime")
|
||||
|
||||
|
||||
class SubscriptionSerializer(DynamicModelSerializer):
|
||||
"""Serializer for the Subscription Model."""
|
||||
"""
|
||||
Serializer for the Subscription Model.
|
||||
"""
|
||||
|
||||
image = serializers.ImageField()
|
||||
|
||||
class Meta:
|
||||
model = Subscription
|
||||
fields = (
|
||||
"uuid", "name", "rss_url", "image", "server",
|
||||
"channels", "creation_datetime"
|
||||
)
|
||||
fields = ("uuid", "name", "rss_url", "image", "server", "creation_datetime")
|
||||
|
||||
|
||||
class SubscriptionChannelSerializer(DynamicModelSerializer):
|
||||
"""
|
||||
Serializer for the SubscriptionChannel Model.
|
||||
"""
|
||||
|
||||
subscription = SubscriptionSerializer()
|
||||
|
||||
class Meta:
|
||||
model = SubscriptionChannel
|
||||
fields = ("uuid", "id", "subscription", "creation_datetime")
|
||||
|
||||
|
||||
class TrackedContentSerializer(DynamicModelSerializer):
|
||||
"""Serializer for the TrackedContent Model."""
|
||||
"""
|
||||
Serializer for the TrackedContent Model.
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
model = TrackedContent
|
||||
fields = (
|
||||
"uuid", "content_url", "subscription",
|
||||
"creation_datetime"
|
||||
)
|
||||
fields = ("uuid", "content_url", "subscription", "creation_datetime")
|
||||
|
@ -4,10 +4,10 @@ from django.urls import path, include
|
||||
from rest_framework.authtoken.views import obtain_auth_token
|
||||
|
||||
from .views import (
|
||||
DiscordChannel_ListView,
|
||||
DiscordChannel_DetailView,
|
||||
Subscription_ListView,
|
||||
Subscription_DetailView,
|
||||
SubscriptionChannel_ListView,
|
||||
SubscriptionChannel_DetailView,
|
||||
TrackedContent_ListView,
|
||||
TrackedContent_DetailView
|
||||
)
|
||||
@ -17,12 +17,11 @@ urlpatterns = [
|
||||
path("api-auth/", include("rest_framework.urls", namespace="rest_framework")),
|
||||
path("api-token-auth/", obtain_auth_token),
|
||||
|
||||
path("channel/", include([
|
||||
path("", DiscordChannel_ListView.as_view(), name="discordchannel"),
|
||||
path("<int:pk>/", DiscordChannel_DetailView.as_view(), name="discordchannel-detail")
|
||||
])),
|
||||
|
||||
path("subscription/", include([
|
||||
path("channel/", include([
|
||||
path("", SubscriptionChannel_ListView.as_view(), name="subscriptionchannel"),
|
||||
path("<str:pk>/", SubscriptionChannel_DetailView.as_view(), name="subscriptionchannel-detail")
|
||||
])),
|
||||
path("", Subscription_ListView.as_view(), name="subscription"),
|
||||
path("<str:pk>/", Subscription_DetailView.as_view(), name="subscription-detail")
|
||||
])),
|
||||
|
@ -10,8 +10,8 @@ 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 DiscordChannel, Subscription, TrackedContent
|
||||
from .serializers import DiscordChannelSerializer, SubscriptionSerializer, TrackedContentSerializer
|
||||
from apps.home.models import Subscription, SubscriptionChannel, TrackedContent
|
||||
from .serializers import SubscriptionSerializer, SubscriptionChannelSerializer, TrackedContentSerializer
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -25,38 +25,16 @@ class DefaultPagination(PageNumberPagination):
|
||||
max_page_size = 25
|
||||
|
||||
|
||||
# Discord Channel Views
|
||||
|
||||
class DiscordChannel_ListView(generics.ListAPIView, generics.CreateAPIView):
|
||||
""""""
|
||||
|
||||
authentication_classes = [SessionAuthentication, TokenAuthentication]
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
pagination_class = DefaultPagination
|
||||
serializer_class = DiscordChannelSerializer
|
||||
queryset = DiscordChannel.objects.all().order_by("-creation_datetime")
|
||||
|
||||
filter_backends = [rest_filters.DjangoFilterBackend, filters.OrderingFilter]
|
||||
filterset_fields = ["id"]
|
||||
ordering_fields = ["creation_datetime"]
|
||||
|
||||
|
||||
class DiscordChannel_DetailView(generics.RetrieveDestroyAPIView):
|
||||
""""""
|
||||
|
||||
authentication_classes = [SessionAuthentication, TokenAuthentication]
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
parser_classes = [MultiPartParser, FormParser]
|
||||
|
||||
serializer_class = DiscordChannelSerializer
|
||||
queryset = DiscordChannel.objects.all().order_by("-creation_datetime")
|
||||
|
||||
|
||||
# =================================================================================================
|
||||
# Subscription Views
|
||||
|
||||
class Subscription_ListView(generics.ListAPIView, generics.CreateAPIView):
|
||||
""""""
|
||||
class Subscription_ListView(generics.ListCreateAPIView):
|
||||
"""
|
||||
View to provide a list of Subscription model instances.
|
||||
Can also be used to create a new instance.
|
||||
|
||||
Supports: GET, POST
|
||||
"""
|
||||
|
||||
authentication_classes = [SessionAuthentication, TokenAuthentication]
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
@ -66,25 +44,33 @@ class Subscription_ListView(generics.ListAPIView, generics.CreateAPIView):
|
||||
queryset = Subscription.objects.all().order_by("-creation_datetime")
|
||||
|
||||
filter_backends = [filters.SearchFilter, rest_filters.DjangoFilterBackend, filters.OrderingFilter]
|
||||
filterset_fields = ["uuid", "name", "rss_url", "server", "channels", "creation_datetime"]
|
||||
filterset_fields = ["uuid", "name", "rss_url", "server", "creation_datetime"]
|
||||
search_fields = ["name"]
|
||||
ordering_fields = ["creation_datetime"]
|
||||
|
||||
# def post(self, request):
|
||||
# serializer = self.get_serializer(data=request.data)
|
||||
# serializer.is_valid(raise_exception=True)
|
||||
def post(self, request):
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
# try:
|
||||
# self.perform_create(serializer)
|
||||
# except IntegrityError:
|
||||
# return Response({"detail": "RSS Feed name must be unique"}, status=status.HTTP_409_CONFLICT, exception=True)
|
||||
try:
|
||||
self.perform_create(serializer)
|
||||
except IntegrityError:
|
||||
return Response(
|
||||
{"detail": "Subscription name must be unique"},
|
||||
status=status.HTTP_409_CONFLICT,
|
||||
exception=True
|
||||
)
|
||||
|
||||
# headers = self.get_success_headers(serializer.data)
|
||||
# return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
|
||||
|
||||
|
||||
class Subscription_DetailView(generics.RetrieveUpdateDestroyAPIView):
|
||||
""""""
|
||||
"""
|
||||
View to provide details on a particular Subscription model instances.
|
||||
|
||||
Supports: GET, DELETE
|
||||
"""
|
||||
|
||||
authentication_classes = [SessionAuthentication, TokenAuthentication]
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
@ -94,10 +80,86 @@ class Subscription_DetailView(generics.RetrieveUpdateDestroyAPIView):
|
||||
queryset = Subscription.objects.all().order_by("-creation_datetime")
|
||||
|
||||
|
||||
# =================================================================================================
|
||||
# SubscriptionChannel Views
|
||||
|
||||
class SubscriptionChannel_ListView(generics.ListCreateAPIView):
|
||||
"""
|
||||
View to provide a list of SubscriptionChannel 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 = SubscriptionChannelSerializer
|
||||
queryset = SubscriptionChannel.objects.all().order_by("-creation_datetime")
|
||||
|
||||
filter_backends = [rest_filters.DjangoFilterBackend, filters.OrderingFilter]
|
||||
filterset_fields = ["id", "subscription"]
|
||||
ordering_fields = ["creation_datetime"]
|
||||
|
||||
def post(self, request):
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
try:
|
||||
self.perform_create(serializer)
|
||||
except IntegrityError:
|
||||
return Response(
|
||||
{"detail": "Duplicates not allowed"},
|
||||
status=status.HTTP_409_CONFLICT,
|
||||
exception=True
|
||||
)
|
||||
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
|
||||
|
||||
|
||||
class SubscriptionChannel_DetailView(generics.RetrieveDestroyAPIView):
|
||||
"""
|
||||
View to provide details on a particular SubscriptionChannel model instances.
|
||||
|
||||
Supports: GET, DELETE
|
||||
"""
|
||||
|
||||
authentication_classes = [SessionAuthentication, TokenAuthentication]
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
parser_classes = [MultiPartParser, FormParser]
|
||||
|
||||
serializer_class = SubscriptionChannelSerializer
|
||||
queryset = SubscriptionChannel.objects.all().order_by("-creation_datetime")
|
||||
|
||||
def post(self, request):
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
try:
|
||||
self.perform_create(serializer)
|
||||
except IntegrityError:
|
||||
return Response(
|
||||
{"detail": "Channel must be unique"},
|
||||
status=status.HTTP_409_CONFLICT,
|
||||
exception=True
|
||||
)
|
||||
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
|
||||
|
||||
|
||||
# =================================================================================================
|
||||
# Tracked Content Views
|
||||
|
||||
class TrackedContent_ListView(generics.ListAPIView, generics.CreateAPIView):
|
||||
""""""
|
||||
class TrackedContent_ListView(generics.ListCreateAPIView):
|
||||
"""
|
||||
View to provide a list of TrackedContent model instances.
|
||||
Can also be used to create a new instance.
|
||||
|
||||
Supports: GET, POST
|
||||
"""
|
||||
|
||||
authentication_classes = [SessionAuthentication, TokenAuthentication]
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
@ -111,9 +173,29 @@ class TrackedContent_ListView(generics.ListAPIView, generics.CreateAPIView):
|
||||
search_fields = ["name"]
|
||||
ordering_fields = ["creation_datetime"]
|
||||
|
||||
def post(self, request):
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
try:
|
||||
self.perform_create(serializer)
|
||||
except IntegrityError:
|
||||
return Response(
|
||||
{"detail": "Tracked content must be unique"},
|
||||
status=status.HTTP_409_CONFLICT,
|
||||
exception=True
|
||||
)
|
||||
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
|
||||
|
||||
|
||||
class TrackedContent_DetailView(generics.RetrieveDestroyAPIView):
|
||||
""""""
|
||||
"""
|
||||
View to provide details on a particular TrackedContent model instances.
|
||||
|
||||
Supports: GET, DELETE
|
||||
"""
|
||||
|
||||
authentication_classes = [SessionAuthentication, TokenAuthentication]
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
@ -2,15 +2,10 @@
|
||||
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import DiscordChannel, Subscription, TrackedContent
|
||||
|
||||
|
||||
class DiscordChannelAdmin(admin.ModelAdmin):
|
||||
list_display = [
|
||||
"id", "creation_datetime"
|
||||
]
|
||||
from .models import Subscription, SubscriptionChannel, TrackedContent
|
||||
|
||||
|
||||
@admin.register(Subscription)
|
||||
class SubscriptionAdmin(admin.ModelAdmin):
|
||||
list_display = [
|
||||
"uuid", "name", "rss_url", "server",
|
||||
@ -18,14 +13,17 @@ class SubscriptionAdmin(admin.ModelAdmin):
|
||||
]
|
||||
|
||||
|
||||
@admin.register(SubscriptionChannel)
|
||||
class SubscriptionAdmin(admin.ModelAdmin):
|
||||
list_display = [
|
||||
"uuid", "id", "subscription", "creation_datetime"
|
||||
]
|
||||
|
||||
|
||||
@admin.register(TrackedContent)
|
||||
class TrackedContentAdmin(admin.ModelAdmin):
|
||||
list_display = [
|
||||
"uuid", "content_url", "subscription",
|
||||
"creation_datetime"
|
||||
]
|
||||
|
||||
|
||||
admin.site.register(DiscordChannel, DiscordChannelAdmin)
|
||||
admin.site.register(Subscription, SubscriptionAdmin)
|
||||
admin.site.register(TrackedContent, TrackedContentAdmin)
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Generated by Django 5.0.1 on 2024-02-07 23:19
|
||||
# Generated by Django 5.0.1 on 2024-02-11 20:20
|
||||
|
||||
import apps.home.models
|
||||
import django.db.models.deletion
|
||||
@ -18,7 +18,7 @@ class Migration(migrations.Migration):
|
||||
migrations.CreateModel(
|
||||
name='DiscordChannel',
|
||||
fields=[
|
||||
('id', models.PositiveBigIntegerField(editable=False, help_text='Unique identifier of the channel, provided by Discord.', primary_key=True, serialize=False, verbose_name='id')),
|
||||
('id', models.PositiveBigIntegerField(help_text='Identifier of the channel, provided by Discord.', primary_key=True, serialize=False, verbose_name='id')),
|
||||
('creation_datetime', models.DateTimeField(default=django.utils.timezone.now, editable=False, help_text='when this instance was created.', verbose_name='creation datetime')),
|
||||
],
|
||||
options={
|
||||
@ -31,12 +31,12 @@ class Migration(migrations.Migration):
|
||||
name='Subscription',
|
||||
fields=[
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('name', models.CharField(help_text='Reference name for this subscription (max %(max_length)s chars).', max_length=32, verbose_name='name')),
|
||||
('name', models.CharField(help_text='Reference name for this subscription (max 32 chars).', max_length=32, verbose_name='name')),
|
||||
('rss_url', models.URLField(help_text='URL of the subscribed to RSS feed.', verbose_name='rss url')),
|
||||
('image', models.ImageField(blank=True, default='../static/images/defaultuser.webp', help_text='image of the RSS feed.', null=True, storage=apps.home.models.OverwriteStorage(), upload_to=apps.home.models.Subscription_IconPathGenerator(), verbose_name='image')),
|
||||
('image', models.ImageField(blank=True, default='../static/images/defaultuser.webp', help_text='image of the RSS feed.', null=True, storage=apps.home.models.OverwriteStorage(), upload_to=apps.home.models.IconPathGenerator(), verbose_name='image')),
|
||||
('creation_datetime', models.DateTimeField(default=django.utils.timezone.now, editable=False, help_text='when this instance was created.', verbose_name='creation datetime')),
|
||||
('server', models.PositiveBigIntegerField(help_text='Identifier for the discord server that owns this subscription.', verbose_name='server id')),
|
||||
('channels', models.ManyToManyField(help_text='List of Discord Channels acting as targets for subscription content.', to='home.discordchannel', verbose_name='channels')),
|
||||
('channels', models.ManyToManyField(blank=True, help_text='List of Discord Channels acting as targets for subscription content.', to='home.discordchannel', verbose_name='channels')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'subscription',
|
||||
|
@ -0,0 +1,41 @@
|
||||
# Generated by Django 5.0.1 on 2024-02-11 21:59
|
||||
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
import uuid
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('home', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='subscription',
|
||||
name='channels',
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SubscriptionChannel',
|
||||
fields=[
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('id', models.PositiveBigIntegerField(help_text='Identifier of the channel, provided by Discord.', verbose_name='id')),
|
||||
('creation_datetime', models.DateTimeField(default=django.utils.timezone.now, editable=False, help_text='when this instance was created.', verbose_name='creation datetime')),
|
||||
('subscription', models.ForeignKey(help_text='The subscription of this instance.', on_delete=django.db.models.deletion.CASCADE, to='home.subscription', verbose_name='subscription')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'subscription channel',
|
||||
'verbose_name_plural': 'subscription channels',
|
||||
'get_latest_by': '-creation_date',
|
||||
},
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='DiscordChannel',
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='subscriptionchannel',
|
||||
constraint=models.UniqueConstraint(fields=('id', 'subscription'), name='unique id & sub pair'),
|
||||
),
|
||||
]
|
@ -1,110 +1,90 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import logging
|
||||
from uuid import uuid4
|
||||
from pathlib import Path
|
||||
|
||||
import httpx
|
||||
import feedparser
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
from django.dispatch import receiver
|
||||
from django.db.models.signals import pre_save
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.validators import URLValidator
|
||||
from django.core.files.storage import FileSystemStorage
|
||||
from django.utils.deconstruct import deconstructible
|
||||
from asgiref.sync import sync_to_async, async_to_sync
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OverwriteStorage(FileSystemStorage):
|
||||
"""Storage class that allows overriding files, instead of django appending random characters to prevent conflicts."""
|
||||
"""
|
||||
Storage class that allows overriding files, instead of django appending random
|
||||
characters to prevent conflicts.
|
||||
"""
|
||||
|
||||
def get_available_name(self, name, max_length=None):
|
||||
def get_available_name(self, name, max_length=None) -> str:
|
||||
if self.exists(name):
|
||||
os.remove(os.path.join(self.location, name))
|
||||
(Path(self.location) / name).unlink()
|
||||
|
||||
return name
|
||||
|
||||
|
||||
@deconstructible
|
||||
class Subscription_IconPathGenerator:
|
||||
"""Icon path generator for Subscriptions."""
|
||||
class IconPathGenerator:
|
||||
"""
|
||||
Icon path generator.
|
||||
"""
|
||||
|
||||
def __call__(self, instance, filename: str) -> str:
|
||||
return os.path.join("subscriptions", str(instance.uuid), "icon.webp")
|
||||
|
||||
|
||||
class DiscordChannel(models.Model):
|
||||
"""Represents a Discord Channel."""
|
||||
|
||||
id = models.PositiveBigIntegerField(
|
||||
verbose_name=_("id"),
|
||||
help_text=_("Unique identifier of the channel, provided by Discord."),
|
||||
primary_key=True
|
||||
)
|
||||
creation_datetime = models.DateTimeField(
|
||||
verbose_name=_("creation datetime"),
|
||||
help_text=_("when this instance was created."),
|
||||
default=timezone.now,
|
||||
editable=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("discord channel")
|
||||
verbose_name_plural = _("discord channels")
|
||||
get_latest_by = "-creation_datetime"
|
||||
|
||||
def __str__(self):
|
||||
return str(self.id)
|
||||
def __call__(self, instance, filename: str) -> Path:
|
||||
return Path(instance.__class__.__name__.lower()) / str(instance.uuid) / "icon.webp"
|
||||
|
||||
|
||||
class Subscription(models.Model):
|
||||
"""Stores relevant data for a user submitted RSS Feed."""
|
||||
"""
|
||||
Represents a stored RSS Feed.
|
||||
"""
|
||||
|
||||
uuid = models.UUIDField(
|
||||
primary_key=True,
|
||||
default=uuid4,
|
||||
editable=False
|
||||
)
|
||||
|
||||
# Name attribute acts as a human readable identification and search option.
|
||||
name = models.CharField(
|
||||
verbose_name=_("name"),
|
||||
help_text=_("Reference name for this subscription (max %(max_length)s chars)."),
|
||||
help_text=_("Reference name for this subscription (max 32 chars)."),
|
||||
max_length=32,
|
||||
null=False,
|
||||
blank=False
|
||||
)
|
||||
|
||||
rss_url = models.URLField(
|
||||
verbose_name=_("rss url"),
|
||||
help_text=_("URL of the subscribed to RSS feed.")
|
||||
)
|
||||
|
||||
image = models.ImageField(
|
||||
verbose_name=_("image"),
|
||||
help_text=_("image of the RSS feed."),
|
||||
upload_to=Subscription_IconPathGenerator(),
|
||||
upload_to=IconPathGenerator(),
|
||||
storage=OverwriteStorage(),
|
||||
default="../static/images/defaultuser.webp",
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
|
||||
# Discord Server ID
|
||||
server = models.PositiveBigIntegerField(
|
||||
verbose_name=_("server id"),
|
||||
help_text=_("Identifier for the discord server that owns this subscription.")
|
||||
)
|
||||
|
||||
creation_datetime = models.DateTimeField(
|
||||
verbose_name=_("creation datetime"),
|
||||
help_text=_("when this instance was created."),
|
||||
default=timezone.now,
|
||||
editable=False
|
||||
)
|
||||
server = models.PositiveBigIntegerField(
|
||||
verbose_name=_("server id"),
|
||||
help_text=_("Identifier for the discord server that owns this subscription.")
|
||||
)
|
||||
channels = models.ManyToManyField(
|
||||
verbose_name=_("channels"),
|
||||
help_text=_("List of Discord Channels acting as targets for subscription content."),
|
||||
to=DiscordChannel,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "subscription"
|
||||
@ -123,15 +103,71 @@ class Subscription(models.Model):
|
||||
log.debug("%sSubscription Saved %s", new_text, self.uuid)
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
@property
|
||||
def channels(self):
|
||||
"""
|
||||
Returns all SubscriptionChannel objects linked to this Subscription.
|
||||
"""
|
||||
|
||||
class TrackedContent(models.Model):
|
||||
"""Tracks content shared from an RSS Feed, to prevent duplicates."""
|
||||
return SubscriptionChannel.objects.filter(subscription=self)
|
||||
|
||||
|
||||
class SubscriptionChannel(models.Model):
|
||||
"""
|
||||
Represents a Discord TextChannel that should be subject to the content of a Subscription.
|
||||
"""
|
||||
|
||||
uuid = models.UUIDField(
|
||||
primary_key=True,
|
||||
default=uuid4,
|
||||
editable=False
|
||||
)
|
||||
|
||||
# The ID is not auto generated, but rather be obtained from discord.
|
||||
id = models.PositiveBigIntegerField(
|
||||
verbose_name=_("id"),
|
||||
help_text=_("Identifier of the channel, provided by Discord.")
|
||||
)
|
||||
|
||||
subscription = models.ForeignKey(
|
||||
verbose_name=_("subscription"),
|
||||
help_text=_("The subscription of this instance."),
|
||||
to=Subscription,
|
||||
on_delete=models.CASCADE
|
||||
)
|
||||
|
||||
creation_datetime = models.DateTimeField(
|
||||
verbose_name=_("creation datetime"),
|
||||
help_text=_("when this instance was created."),
|
||||
default=timezone.now,
|
||||
editable=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "subscription channel"
|
||||
verbose_name_plural = "subscription channels"
|
||||
get_latest_by = "-creation_date"
|
||||
constraints = [
|
||||
models.UniqueConstraint(fields=["id", "subscription"], name="unique id & sub pair")
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return str(self.id)
|
||||
|
||||
|
||||
|
||||
class TrackedContent(models.Model):
|
||||
"""
|
||||
Tracks content shared from an RSS Feed Subscription.
|
||||
Content is tracked to help prevent duplicate content.
|
||||
"""
|
||||
|
||||
uuid = models.UUIDField(
|
||||
primary_key=True,
|
||||
default=uuid4,
|
||||
editable=False
|
||||
)
|
||||
|
||||
subscription = models.ForeignKey(
|
||||
verbose_name=_("subscription"),
|
||||
help_text=_("The subscription that this content originated from."),
|
||||
@ -139,10 +175,12 @@ class TrackedContent(models.Model):
|
||||
on_delete=models.CASCADE,
|
||||
related_name="tracked_content"
|
||||
)
|
||||
|
||||
content_url = models.URLField(
|
||||
verbose_name=_("content url"),
|
||||
help_text=_("URL of the tracked content.")
|
||||
)
|
||||
|
||||
creation_datetime = models.DateTimeField(
|
||||
verbose_name=_("creation datetime"),
|
||||
help_text=_("when this instance was created."),
|
||||
@ -156,8 +194,10 @@ class TrackedContent(models.Model):
|
||||
|
||||
@receiver(pre_save, sender=TrackedContent)
|
||||
def maintain_item_cap(sender, instance, **kwargs):
|
||||
""""Delete the latest tracked content, if the total under
|
||||
the same subscription reaches 100."""
|
||||
""""
|
||||
Delete the latest tracked content, if the total under
|
||||
the same subscription reaches 100.
|
||||
"""
|
||||
|
||||
log.debug("checking if tracked content can be deleted.")
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user