diff --git a/apps/api/serializers.py b/apps/api/serializers.py index e827bd2..ab8d4b7 100644 --- a/apps/api/serializers.py +++ b/apps/api/serializers.py @@ -1,15 +1,24 @@ # -*- encoding: utf-8 -*- import logging -from urllib.parse import unquote from rest_framework import serializers -from apps.home.models import SubChannel, Filter, Subscription, SavedGuilds, TrackedContent, ArticleMutator, GuildSettings +from apps.home.models import ( + SubChannel, + Filter, + Subscription, + SavedGuilds, + TrackedContent, + ArticleMutator, + GuildSettings, + UniqueContentRule +) log = logging.getLogger(__name__) +#region Dynamic Model # This DynamicModelSerializer is from a StackOverflow user in an obscure thread. # I wish that I could remember which thread, because god bless that man. @@ -109,6 +118,8 @@ class DynamicModelSerializer(serializers.ModelSerializer): abstract = True +#region Sub Channel + class SubChannelSerializer(DynamicModelSerializer): """ Serializer for SubChannel Model. @@ -119,6 +130,8 @@ class SubChannelSerializer(DynamicModelSerializer): fields = ("id", "channel_id", "channel_name", "subscription") +#region Filter + class FilterSerializer(DynamicModelSerializer): """ Serializer for the Filter Model. @@ -129,6 +142,8 @@ class FilterSerializer(DynamicModelSerializer): fields = ("id", "name", "matching_algorithm", "match", "is_insensitive", "is_whitelist", "guild_id") +#region Article Mutator + class ArticleMutatorSerializer(DynamicModelSerializer): class Meta: @@ -136,6 +151,20 @@ class ArticleMutatorSerializer(DynamicModelSerializer): fields = ("id", "name", "value") +#region Unique Content Rule + +class UniqueContentRuleSerializer(DynamicModelSerializer): + """ + Serializer for the UniqueContentRule model. + """ + + class Meta: + model = UniqueContentRule + fields = ("id", "name", "value") + + +#region Subscription (GET) + class SubscriptionSerializer_GET(DynamicModelSerializer): """ Serializer for the Subscription Model. @@ -143,16 +172,20 @@ class SubscriptionSerializer_GET(DynamicModelSerializer): article_title_mutators = ArticleMutatorSerializer(many=True) article_desc_mutators = ArticleMutatorSerializer(many=True) + unique_content_rules = UniqueContentRuleSerializer(many=True) active = serializers.BooleanField(initial=True) class Meta: model = Subscription fields = ( "id", "name", "url", "guild_id", "channels_count", "creation_datetime", "extra_notes", "filters", - "article_title_mutators", "article_desc_mutators", "article_fetch_image", "published_threshold", "embed_colour", "active" + "article_title_mutators", "article_desc_mutators", "article_fetch_image", "published_threshold", "embed_colour", + "unique_content_rules", "active" ) +#region Subscription (POST) + class SubscriptionSerializer_POST(DynamicModelSerializer): """ Serializer for the Subscription Model. @@ -162,10 +195,13 @@ class SubscriptionSerializer_POST(DynamicModelSerializer): model = Subscription fields = ( "id", "name", "url", "guild_id", "channels_count", "creation_datetime", "extra_notes", "filters", - "article_title_mutators", "article_desc_mutators", "article_fetch_image", "published_threshold", "embed_colour", "active" + "article_title_mutators", "article_desc_mutators", "article_fetch_image", "published_threshold", "embed_colour", + "unique_content_rules", "active" ) +#region Saved Guild + class SavedGuildSerializer(DynamicModelSerializer): """ Serializer for the SavedGuild model. @@ -176,6 +212,8 @@ class SavedGuildSerializer(DynamicModelSerializer): fields = ("id", "guild_id", "name", "icon", "added_by", "permissions", "owner") +#region Guild Setting + class GuildSettingsSerializer(DynamicModelSerializer): """ Serializer for the GuildSettings model. @@ -186,6 +224,8 @@ class GuildSettingsSerializer(DynamicModelSerializer): fields = ("id", "guild_id", "default_embed_colour", "active") +#region Tracked Content (GET) + class TrackedContentSerializer_GET(DynamicModelSerializer): """ Serializer for the TrackedContent model. @@ -196,16 +236,10 @@ class TrackedContentSerializer_GET(DynamicModelSerializer): class Meta: model = TrackedContent fields = ("id", "guid", "title", "url", "subscription", "channel_id", "message_id", "blocked", "creation_datetime") - - # def to_representation(self, instance): - # representation = super().to_representation(instance) - # log.info(representation.get("guid", "nothing")) - # if 'guid' in representation: - # representation['guid'] = unquote(representation['guid']) - # log.info(representation.get("guid", "nothing")) - # return representation +#region Tracked Content (POST) + class TrackedContentSerializer_POST(DynamicModelSerializer): """ Serializer for the TrackedContent model. diff --git a/apps/api/urls.py b/apps/api/urls.py index eb75d92..9d8a758 100644 --- a/apps/api/urls.py +++ b/apps/api/urls.py @@ -18,7 +18,9 @@ from .views import ( ArticleMutator_ListView, ArticleMutator_DetailView, GuildSettings_ListView, - GuildSettings_DetailView + GuildSettings_DetailView, + UniqueContentRule_ListView, + UniqueContentRule_DetailView ) urlpatterns = [ @@ -62,4 +64,9 @@ urlpatterns = [ path("", ArticleMutator_ListView.as_view(), name="article-mutator"), path("/", ArticleMutator_DetailView.as_view(), name="article-mutator-detail") ])), + + path("unique-content-rule/", include([ + path("", UniqueContentRule_ListView.as_view(), name="unique-content-rule"), + path("/", UniqueContentRule_DetailView.as_view(), name="unique-content-rule-detail") + ])), ] diff --git a/apps/api/views.py b/apps/api/views.py index 268d997..11edb37 100644 --- a/apps/api/views.py +++ b/apps/api/views.py @@ -11,7 +11,7 @@ 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 SubChannel, Filter, Subscription, SavedGuilds, TrackedContent, ArticleMutator, GuildSettings +from apps.home.models import SubChannel, Filter, Subscription, SavedGuilds, TrackedContent, ArticleMutator, GuildSettings, UniqueContentRule from apps.authentication.models import DiscordUser from .metadata import ExpandedMetadata from .serializers import ( @@ -23,7 +23,8 @@ from .serializers import ( TrackedContentSerializer_GET, TrackedContentSerializer_POST, ArticleMutatorSerializer, - GuildSettingsSerializer + GuildSettingsSerializer, + UniqueContentRuleSerializer ) log = logging.getLogger(__name__) @@ -113,6 +114,44 @@ class SubChannel_DetailView(generics.RetrieveUpdateDestroyAPIView): return SubChannel.objects.filter(subscription__guild_id__in=guild_ids) +#region Unique Content Rule + +class UniqueContentRule_ListView(generics.ListAPIView): + """ + View to provide a list of UniqueContentRule 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 = UniqueContentRuleSerializer + metadata_class = ExpandedMetadata + queryset = UniqueContentRule.objects.all().order_by("id") + + filter_backends = [filters.SearchFilter, rest_filters.DjangoFilterBackend, filters.OrderingFilter] + filterset_fields = ["id", "name", "value"] + + +class UniqueContentRule_DetailView(generics.RetrieveAPIView): + """ + View to provide details on a particular UniqueContentRule model instances. + + Supports: GET + """ + + authentication_classes = [SessionAuthentication, TokenAuthentication] + permission_classes = [permissions.IsAuthenticated] + parser_classes = [MultiPartParser, FormParser] + + serializer_class = UniqueContentRuleSerializer + queryset = UniqueContentRule.objects.all().order_by("id") + + + # ================================================================================================= #region Filter @@ -570,7 +609,7 @@ class TrackedContent_DetailView(generics.RetrieveUpdateDestroyAPIView): # ================================================================================================= #region Article Mutator -class ArticleMutator_ListView(generics.ListCreateAPIView): +class ArticleMutator_ListView(generics.ListAPIView): """ View to provide a list of ArticleMutator model instances. Can also be used to create a new instance. @@ -589,28 +628,12 @@ class ArticleMutator_ListView(generics.ListCreateAPIView): serializer_class = ArticleMutatorSerializer - 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 ArticleMutator_DetailView(generics.RetrieveUpdateDestroyAPIView): +class ArticleMutator_DetailView(generics.RetrieveAPIView): """ View to provide details on a particular ArticleMutator model instances. - Supports: GET, PUT, PATCH, DELETE + Supports: GET """ authentication_classes = [SessionAuthentication, TokenAuthentication] diff --git a/apps/home/migrations/0023_uniquecontentrule_subscription_unique_content_rules.py b/apps/home/migrations/0023_uniquecontentrule_subscription_unique_content_rules.py new file mode 100644 index 0000000..f7737aa --- /dev/null +++ b/apps/home/migrations/0023_uniquecontentrule_subscription_unique_content_rules.py @@ -0,0 +1,26 @@ +# Generated by Django 5.0.4 on 2024-09-13 23:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0022_populate_guild_settings'), + ] + + operations = [ + migrations.CreateModel( + name='UniqueContentRule', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('name', models.CharField(max_length=64)), + ('value', models.CharField(max_length=32)), + ], + ), + migrations.AddField( + model_name='subscription', + name='unique_content_rules', + field=models.ManyToManyField(to='home.uniquecontentrule'), + ), + ] diff --git a/apps/home/migrations/0024_initial_uniquecontentrule_data.py b/apps/home/migrations/0024_initial_uniquecontentrule_data.py new file mode 100644 index 0000000..28a5532 --- /dev/null +++ b/apps/home/migrations/0024_initial_uniquecontentrule_data.py @@ -0,0 +1,22 @@ +# Generated by Django 5.0.4 on 2024-09-13 23:32 + +from django.db import migrations + +def add_rules(apps, schema_editor): + Rules = apps.get_model("home", "UniqueContentRule") + Rules.objects.create(name="GUID", value="GUID") + Rules.objects.create(name="ID", value="ID") + Rules.objects.create(name="URL", value="URL") + Rules.objects.create(name="Title", value="TITLE") + Rules.objects.create(name="Content Hash", value="CONTENT") + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0023_uniquecontentrule_subscription_unique_content_rules'), + ] + + operations = [ + migrations.RunPython(add_rules) + ] diff --git a/apps/home/models.py b/apps/home/models.py index 44ab055..c0fa950 100644 --- a/apps/home/models.py +++ b/apps/home/models.py @@ -277,6 +277,11 @@ class Subscription(models.Model): filters = models.ManyToManyField(to="home.Filter", blank=True) + unique_content_rules = models.ManyToManyField( + to="home.UniqueContentRule", + blank=False + ); + article_title_mutators = models.ManyToManyField( to="home.ArticleMutator", related_name="title_mutated_subscriptions", @@ -386,6 +391,8 @@ class TrackedContent(models.Model): return self.title +#region Article Mutator + class ArticleMutator(models.Model): id = models.AutoField(primary_key=True) @@ -394,3 +401,16 @@ class ArticleMutator(models.Model): def __str__(self) -> str: return self.name + + +#region UniqueContentRules + +class UniqueContentRule(models.Model): + + id = models.AutoField(primary_key=True) + name = models.CharField(max_length=64) + value = models.CharField(max_length=32) + + def __str__(self) -> str: + return self.name + \ No newline at end of file