storing guilds first, then subscriptions
This commit is contained in:
parent
73ee984747
commit
05e90c64e4
@ -4,7 +4,7 @@ import logging
|
|||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from apps.home.models import Subscription, TrackedContent
|
from apps.home.models import Subscription, SavedGuilds
|
||||||
from apps.authentication.models import UserServerLink
|
from apps.authentication.models import UserServerLink
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -106,16 +106,6 @@ class DynamicModelSerializer(serializers.ModelSerializer):
|
|||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
|
|
||||||
# class SubscriptionTargetSerializer(DynamicModelSerializer):
|
|
||||||
# """
|
|
||||||
# Serializer for the Subscription Target Model.
|
|
||||||
# """
|
|
||||||
|
|
||||||
# class Meta:
|
|
||||||
# model = SubscriptionTarget
|
|
||||||
# fields = ("id", "creation_datetime")
|
|
||||||
|
|
||||||
|
|
||||||
class SubscriptionSerializer(DynamicModelSerializer):
|
class SubscriptionSerializer(DynamicModelSerializer):
|
||||||
"""
|
"""
|
||||||
Serializer for the Subscription Model.
|
Serializer for the Subscription Model.
|
||||||
@ -130,16 +120,6 @@ class SubscriptionSerializer(DynamicModelSerializer):
|
|||||||
fields = ("uuid", "name", "rss_url", "image", "server", "targets", "creation_datetime", "extra_notes", "active")
|
fields = ("uuid", "name", "rss_url", "image", "server", "targets", "creation_datetime", "extra_notes", "active")
|
||||||
|
|
||||||
|
|
||||||
class TrackedContentSerializer(DynamicModelSerializer):
|
|
||||||
"""
|
|
||||||
Serializer for the TrackedContent Model.
|
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = TrackedContent
|
|
||||||
fields = ("uuid", "content_url", "subscription", "creation_datetime")
|
|
||||||
|
|
||||||
|
|
||||||
class UserServerLinkSerializer(DynamicModelSerializer):
|
class UserServerLinkSerializer(DynamicModelSerializer):
|
||||||
"""
|
"""
|
||||||
Serializer for the UserServerLink Model.
|
Serializer for the UserServerLink Model.
|
||||||
@ -148,3 +128,13 @@ class UserServerLinkSerializer(DynamicModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = UserServerLink
|
model = UserServerLink
|
||||||
fields = ("id", "server_id", "user", "name", "icon", "icon_url", "permissions")
|
fields = ("id", "server_id", "user", "name", "icon", "icon_url", "permissions")
|
||||||
|
|
||||||
|
|
||||||
|
class SavedGuildSerializer(DynamicModelSerializer):
|
||||||
|
"""
|
||||||
|
Serializer for the SavedGuild model.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = SavedGuilds
|
||||||
|
fields = ("id", "guild_id", "name", "icon")
|
||||||
|
@ -6,13 +6,13 @@ from rest_framework.authtoken.views import obtain_auth_token
|
|||||||
from .views import (
|
from .views import (
|
||||||
Subscription_ListView,
|
Subscription_ListView,
|
||||||
Subscription_DetailView,
|
Subscription_DetailView,
|
||||||
TrackedContent_ListView,
|
|
||||||
TrackedContent_DetailView,
|
|
||||||
UserServerLink_ListView,
|
UserServerLink_ListView,
|
||||||
UserServerLink_DetailView
|
UserServerLink_DetailView,
|
||||||
|
SavedGuild_ListView,
|
||||||
|
SavedGuild_DetailView
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("api-auth/", include("rest_framework.urls", namespace="rest_framework")),
|
path("api-auth/", include("rest_framework.urls", namespace="rest_framework")),
|
||||||
path("api-token-auth/", obtain_auth_token),
|
path("api-token-auth/", obtain_auth_token),
|
||||||
@ -22,13 +22,13 @@ urlpatterns = [
|
|||||||
path("<str:pk>/", Subscription_DetailView.as_view(), name="subscription-detail")
|
path("<str:pk>/", Subscription_DetailView.as_view(), name="subscription-detail")
|
||||||
])),
|
])),
|
||||||
|
|
||||||
path("tracked/", include([
|
|
||||||
path("", TrackedContent_ListView.as_view(), name="tracked"),
|
|
||||||
path("<str:pk>/", TrackedContent_DetailView.as_view(), name="tracked-detail")
|
|
||||||
])),
|
|
||||||
|
|
||||||
path("serverlink/", include([
|
path("serverlink/", include([
|
||||||
path("", UserServerLink_ListView.as_view(), name="serverlink"),
|
path("", UserServerLink_ListView.as_view(), name="serverlink"),
|
||||||
path("<int:pk>/", UserServerLink_DetailView.as_view(), name="serverlink-detail")
|
path("<int:pk>/", UserServerLink_DetailView.as_view(), name="serverlink-detail")
|
||||||
])),
|
])),
|
||||||
|
|
||||||
|
path("saved-guilds/", include([
|
||||||
|
path("", SavedGuild_ListView.as_view(), name="saved-guilds"),
|
||||||
|
path("<int:pk>/", SavedGuild_DetailView.as_view(), name="saved-guilds-detail")
|
||||||
|
])),
|
||||||
]
|
]
|
@ -11,12 +11,12 @@ from rest_framework.pagination import PageNumberPagination
|
|||||||
from rest_framework.authentication import SessionAuthentication, TokenAuthentication
|
from rest_framework.authentication import SessionAuthentication, TokenAuthentication
|
||||||
from rest_framework.parsers import MultiPartParser, FormParser
|
from rest_framework.parsers import MultiPartParser, FormParser
|
||||||
|
|
||||||
from apps.home.models import Subscription, TrackedContent
|
from apps.home.models import Subscription, SavedGuilds
|
||||||
from apps.authentication.models import UserServerLink
|
from apps.authentication.models import UserServerLink
|
||||||
from .serializers import (
|
from .serializers import (
|
||||||
SubscriptionSerializer,
|
SubscriptionSerializer,
|
||||||
TrackedContentSerializer,
|
UserServerLinkSerializer,
|
||||||
UserServerLinkSerializer
|
SavedGuildSerializer
|
||||||
)
|
)
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -92,61 +92,6 @@ class Subscription_DetailView(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
# .order_by("-creation_datetime")
|
# .order_by("-creation_datetime")
|
||||||
|
|
||||||
|
|
||||||
# =================================================================================================
|
|
||||||
# Tracked Content Views
|
|
||||||
|
|
||||||
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]
|
|
||||||
|
|
||||||
pagination_class = DefaultPagination
|
|
||||||
serializer_class = TrackedContentSerializer
|
|
||||||
queryset = TrackedContent.objects.all().order_by("-creation_datetime")
|
|
||||||
|
|
||||||
filter_backends = [filters.SearchFilter, rest_filters.DjangoFilterBackend, filters.OrderingFilter]
|
|
||||||
filterset_fields = ["uuid", "subscription", "content_url", "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)
|
|
||||||
|
|
||||||
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]
|
|
||||||
parser_classes = [MultiPartParser, FormParser]
|
|
||||||
|
|
||||||
serializer_class = TrackedContentSerializer
|
|
||||||
queryset = TrackedContent.objects.all().order_by("-creation_datetime")
|
|
||||||
|
|
||||||
|
|
||||||
# =================================================================================================
|
# =================================================================================================
|
||||||
# UserServerLinks Views
|
# UserServerLinks Views
|
||||||
|
|
||||||
@ -224,3 +169,59 @@ class UserServerLink_DetailView(generics.RetrieveDestroyAPIView):
|
|||||||
|
|
||||||
serializer_class = UserServerLinkSerializer
|
serializer_class = UserServerLinkSerializer
|
||||||
queryset = UserServerLink.objects.all()
|
queryset = UserServerLink.objects.all()
|
||||||
|
|
||||||
|
|
||||||
|
# =================================================================================================
|
||||||
|
# SavedGuild Views
|
||||||
|
|
||||||
|
class SavedGuild_ListView(generics.ListCreateAPIView):
|
||||||
|
"""
|
||||||
|
View to provide a list of SavedGuild model instances.
|
||||||
|
Can also be used to create a new instance.
|
||||||
|
|
||||||
|
Supports: GET, POST
|
||||||
|
"""
|
||||||
|
|
||||||
|
authentication_classes = [SessionAuthentication, TokenAuthentication]
|
||||||
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
pagination_class = None
|
||||||
|
serializer_class = SavedGuildSerializer
|
||||||
|
|
||||||
|
filter_backends = [filters.SearchFilter, rest_filters.DjangoFilterBackend, filters.OrderingFilter]
|
||||||
|
filterset_fields = ["id", "guild_id", "name", "icon"]
|
||||||
|
search_fields = ["name"]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return SavedGuilds.objects.all()
|
||||||
|
|
||||||
|
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": "SavedGuild 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 SavedGuild_DetailView(generics.RetrieveDestroyAPIView):
|
||||||
|
"""
|
||||||
|
View to provide details on a particular UserServerLink model instances.
|
||||||
|
|
||||||
|
Supports: GET, DELETE
|
||||||
|
"""
|
||||||
|
|
||||||
|
authentication_classes = [SessionAuthentication, TokenAuthentication]
|
||||||
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
parser_classes = [MultiPartParser, FormParser]
|
||||||
|
|
||||||
|
serializer_class = SavedGuildSerializer
|
||||||
|
queryset = SavedGuilds.objects.all()
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
from django.urls import path
|
from django.urls import path
|
||||||
from django.contrib.auth.views import LogoutView
|
from django.contrib.auth.views import LogoutView
|
||||||
|
|
||||||
from .views import DiscordLoginAction, DiscordLoginRedirect, Login, GuildsView, GuildChannelsView
|
from .views import DiscordLoginAction, DiscordLoginRedirect, Login, GuildsView, GuildChannelsView, SaveGuildView
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
@ -13,6 +13,7 @@ urlpatterns = [
|
|||||||
path("logout/", LogoutView.as_view(), name="logout"),
|
path("logout/", LogoutView.as_view(), name="logout"),
|
||||||
|
|
||||||
path("guilds/", GuildsView.as_view(), name="guilds"),
|
path("guilds/", GuildsView.as_view(), name="guilds"),
|
||||||
path("channels/", GuildChannelsView.as_view(), name="channels")
|
path("channels/", GuildChannelsView.as_view(), name="channels"),
|
||||||
|
path("save-guild/", SaveGuildView.as_view(), name="save-guild")
|
||||||
|
|
||||||
]
|
]
|
||||||
|
@ -11,6 +11,7 @@ from django.shortcuts import render, redirect
|
|||||||
from django.contrib.auth import authenticate, login
|
from django.contrib.auth import authenticate, login
|
||||||
|
|
||||||
from .models import UserServerLink
|
from .models import UserServerLink
|
||||||
|
from apps.home.models import SavedGuilds
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -97,35 +98,10 @@ class GuildsView(View):
|
|||||||
)
|
)
|
||||||
|
|
||||||
content = response.json()
|
content = response.json()
|
||||||
self.create_server_links(request.user, content)
|
print(response, content)
|
||||||
|
|
||||||
return JsonResponse(content, safe=False)
|
return JsonResponse(content, safe=False)
|
||||||
|
|
||||||
def create_server_links(self, user, content: list[dict]):
|
|
||||||
"""
|
|
||||||
Creates objects representing the user's discord servers, storing
|
|
||||||
server info and the user's permissions within.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
content : list[dict]
|
|
||||||
Raw data for the servers.
|
|
||||||
"""
|
|
||||||
|
|
||||||
servers = [
|
|
||||||
UserServerLink(
|
|
||||||
server_id=server["id"],
|
|
||||||
user=user,
|
|
||||||
name=server["name"],
|
|
||||||
permissions=server["permissions"],
|
|
||||||
icon=server["icon"]
|
|
||||||
)
|
|
||||||
for server in content
|
|
||||||
]
|
|
||||||
|
|
||||||
UserServerLink.objects.filter(user=user).delete()
|
|
||||||
UserServerLink.objects.bulk_create(servers)
|
|
||||||
|
|
||||||
|
|
||||||
class GuildChannelsView(View):
|
class GuildChannelsView(View):
|
||||||
|
|
||||||
@ -139,5 +115,30 @@ class GuildChannelsView(View):
|
|||||||
url=f"{settings.DISCORD_API_URL}/guilds/{guild_id}/channels",
|
url=f"{settings.DISCORD_API_URL}/guilds/{guild_id}/channels",
|
||||||
headers={"Authorization": f"Bot {settings.BOT_TOKEN}"}
|
headers={"Authorization": f"Bot {settings.BOT_TOKEN}"}
|
||||||
)
|
)
|
||||||
|
print(response, response.json())
|
||||||
|
|
||||||
return JsonResponse(response.json(), safe=False)
|
return JsonResponse(response.json(), safe=False)
|
||||||
|
|
||||||
|
|
||||||
|
class SaveGuildView(View):
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
|
||||||
|
guild_id = request.GET.get("id")
|
||||||
|
|
||||||
|
if guild_id:
|
||||||
|
return SavedGuilds.objects.filter(id=guild_id)
|
||||||
|
|
||||||
|
return SavedGuilds.objects.all()
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
|
||||||
|
data = request.POST
|
||||||
|
|
||||||
|
guild = SavedGuilds.objects.get_or_create(
|
||||||
|
id=data["id"],
|
||||||
|
name=data["name"],
|
||||||
|
icon=data["icon"]
|
||||||
|
)
|
||||||
|
|
||||||
|
return JsonResponse(data)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from .models import Subscription, TrackedContent
|
from .models import Subscription, SavedGuilds
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Subscription)
|
@admin.register(Subscription)
|
||||||
@ -13,17 +13,8 @@ class SubscriptionAdmin(admin.ModelAdmin):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
# @admin.register(SubscriptionTarget)
|
@admin.register(SavedGuilds)
|
||||||
# class SubscriptionTargetAdmin(admin.ModelAdmin):
|
class SavedGuildAdmin(admin.ModelAdmin):
|
||||||
# list_display = [
|
|
||||||
# "id", "creation_datetime"
|
|
||||||
# ]
|
|
||||||
|
|
||||||
|
|
||||||
@admin.register(TrackedContent)
|
|
||||||
class TrackedContentAdmin(admin.ModelAdmin):
|
|
||||||
list_display = [
|
list_display = [
|
||||||
"uuid", "content_url", "subscription",
|
"id", "name", "icon"
|
||||||
"creation_datetime"
|
|
||||||
]
|
]
|
||||||
|
|
@ -0,0 +1,24 @@
|
|||||||
|
# Generated by Django 5.0.1 on 2024-04-02 11:16
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('home', '0006_alter_subscription_image'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='SavedGuilds',
|
||||||
|
fields=[
|
||||||
|
('id', models.PositiveBigIntegerField(primary_key=True, serialize=False)),
|
||||||
|
('name', models.CharField(max_length=128)),
|
||||||
|
('icon', models.CharField(max_length=128)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='TrackedContent',
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,24 @@
|
|||||||
|
# Generated by Django 5.0.1 on 2024-04-02 16:12
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('home', '0007_savedguilds_delete_trackedcontent'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='savedguilds',
|
||||||
|
name='guild_id',
|
||||||
|
field=models.PositiveBigIntegerField(default=1),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='savedguilds',
|
||||||
|
name='id',
|
||||||
|
field=models.AutoField(primary_key=True, serialize=False),
|
||||||
|
),
|
||||||
|
]
|
18
apps/home/migrations/0009_alter_savedguilds_guild_id.py
Normal file
18
apps/home/migrations/0009_alter_savedguilds_guild_id.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.0.1 on 2024-04-02 16:25
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('home', '0008_savedguilds_guild_id_alter_savedguilds_id'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='savedguilds',
|
||||||
|
name='guild_id',
|
||||||
|
field=models.CharField(max_length=128),
|
||||||
|
),
|
||||||
|
]
|
@ -39,31 +39,26 @@ class IconPathGenerator:
|
|||||||
return Path(instance.__class__.__name__.lower()) / str(instance.uuid) / "icon.webp"
|
return Path(instance.__class__.__name__.lower()) / str(instance.uuid) / "icon.webp"
|
||||||
|
|
||||||
|
|
||||||
# class SubscriptionTarget(models.Model):
|
class SavedGuilds(models.Model):
|
||||||
# """
|
"""
|
||||||
# Represents a Discord TextChannel that should be subject to the content of a Subscription.
|
|
||||||
# """
|
"""
|
||||||
|
|
||||||
# id = models.PositiveBigIntegerField(
|
id = models.AutoField(
|
||||||
# verbose_name=_("id"),
|
primary_key=True
|
||||||
# primary_key=True,
|
)
|
||||||
# help_text=_("Discord Channel ID")
|
|
||||||
# )
|
|
||||||
|
|
||||||
# creation_datetime = models.DateTimeField(
|
guild_id = models.CharField(
|
||||||
# verbose_name=_("creation datetime"),
|
max_length=128
|
||||||
# help_text=_("when this instance was created."),
|
)
|
||||||
# default=timezone.now,
|
|
||||||
# editable=False
|
|
||||||
# )
|
|
||||||
|
|
||||||
# class Meta:
|
name = models.CharField(
|
||||||
# verbose_name = "subscription target"
|
max_length=128
|
||||||
# verbose_name_plural = "subscription targets"
|
)
|
||||||
# get_latest_by = "-creation_date"
|
|
||||||
|
|
||||||
# def __str__(self):
|
icon = models.CharField(
|
||||||
# return str(self.id)
|
max_length=128
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Subscription(models.Model):
|
class Subscription(models.Model):
|
||||||
@ -155,126 +150,3 @@ class Subscription(models.Model):
|
|||||||
log.debug("%sSubscription Saved %s", new_text, self.uuid)
|
log.debug("%sSubscription Saved %s", new_text, self.uuid)
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
# @property
|
|
||||||
# def channels(self):
|
|
||||||
# """
|
|
||||||
# Returns all SubscriptionChannel objects linked to this Subscription.
|
|
||||||
# """
|
|
||||||
|
|
||||||
# return SubscriptionChannel.objects.filter(subscription=self)
|
|
||||||
|
|
||||||
# def save(self, *args, **kwargs):
|
|
||||||
# if Subscription.objects.filter(server=self.server).count() >= 1:
|
|
||||||
# raise IntegrityError(f"Subscription limit reached for server '{self.server}'")
|
|
||||||
|
|
||||||
# super().save(*args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
# def save(self, *args, **kwargs):
|
|
||||||
# if SubscriptionChannel.objects.filter(subscription=self.subscription).count() >= 4:
|
|
||||||
# raise IntegrityError(
|
|
||||||
# f"SubscriptionChannel limit reached for subscription '{self.subscription}'"
|
|
||||||
# )
|
|
||||||
|
|
||||||
# super().save(*args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
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."),
|
|
||||||
to=Subscription,
|
|
||||||
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."),
|
|
||||||
default=timezone.now,
|
|
||||||
editable=False
|
|
||||||
)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return str(self.content_url)
|
|
||||||
|
|
||||||
|
|
||||||
@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.
|
|
||||||
"""
|
|
||||||
|
|
||||||
log.debug("checking if tracked content can be deleted.")
|
|
||||||
|
|
||||||
queryset = sender.objects.filter(subscription=instance.subscription)
|
|
||||||
total_tracked = queryset.count()
|
|
||||||
|
|
||||||
if total_tracked < 100:
|
|
||||||
log.debug("tracked content cannot be deleted, less than 100 items: %s", total_tracked)
|
|
||||||
return
|
|
||||||
|
|
||||||
oldest = queryset.earliest()
|
|
||||||
|
|
||||||
log.info("tracked content limit exceeded, deleting oldest item with uuid: %s", oldest.uuid)
|
|
||||||
|
|
||||||
oldest.delete()
|
|
||||||
|
@ -1,3 +1,59 @@
|
|||||||
|
function getSavedGuilds() {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
$.ajax({
|
||||||
|
url: "/api/saved-guilds/",
|
||||||
|
type: "GET",
|
||||||
|
beforeSend: function(xhr) {
|
||||||
|
xhr.setRequestHeader("X-CSRFToken", CSRF_MiddlewareToken);
|
||||||
|
},
|
||||||
|
success: function(response) {
|
||||||
|
resolve(response);
|
||||||
|
},
|
||||||
|
error: function(response) {
|
||||||
|
reject(response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSavedGuild(id) {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
$.ajax({
|
||||||
|
url: `/api/saved-guilds/${id}/`,
|
||||||
|
type: "GET",
|
||||||
|
beforeSend: function(xhr) {
|
||||||
|
xhr.setRequestHeader("X-CSRFToken", CSRF_MiddlewareToken);
|
||||||
|
},
|
||||||
|
success: function(response) {
|
||||||
|
resolve(response);
|
||||||
|
},
|
||||||
|
error: function(response) {
|
||||||
|
reject(response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function newSavedGuild(formData) {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
$.ajax({
|
||||||
|
url: "/api/saved-guilds/",
|
||||||
|
type: "POST",
|
||||||
|
data: formData,
|
||||||
|
processData: false,
|
||||||
|
contentType: false,
|
||||||
|
beforeSend: function(xhr) {
|
||||||
|
xhr.setRequestHeader("X-CSRFToken", CSRF_MiddlewareToken);
|
||||||
|
},
|
||||||
|
success: function(response) {
|
||||||
|
resolve(response);
|
||||||
|
},
|
||||||
|
error: function(response) {
|
||||||
|
reject(response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function getSubscriptions() {
|
function getSubscriptions() {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
|
@ -74,7 +74,7 @@ function loadGuilds() {
|
|||||||
for (i = 1; i < response.length; i++) {
|
for (i = 1; i < response.length; i++) {
|
||||||
var guild = response[i];
|
var guild = response[i];
|
||||||
$("#editSubServer").append($("<option>", {
|
$("#editSubServer").append($("<option>", {
|
||||||
value: guild.id,
|
value: guild.guild_id,
|
||||||
text: guild.name
|
text: guild.name
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
19
apps/templates/home/includes/servermodal.html
Normal file
19
apps/templates/home/includes/servermodal.html
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
<form id="serverForm" novalidate>
|
||||||
|
<div class="modal fade" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
test
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<select name="serverOptions" id="serverOptions" class="select-2" data-dropdownparent="#serverForm .modal"></select>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="submit" class="btn btn-primary">Submit</button>
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
@ -19,34 +19,9 @@
|
|||||||
|
|
||||||
<div class="peers as-s ai-s w-100">
|
<div class="peers as-s ai-s w-100">
|
||||||
<div class="peer bg-body-secondary">
|
<div class="peer bg-body-secondary">
|
||||||
<div class="p-2 layers border-end h-100">
|
<div id="serverList" class="p-2 layers border-end h-100">
|
||||||
<div class="layer mb-2">
|
<div class="layer mb-2">
|
||||||
<button type="button" class="rounded-3 p-1 bg-body bd">
|
<button type="button" id="newServerBtn" class="btn btn-secondary rounded-3 p-1 bd" onclick="javascript: openServerModal();">
|
||||||
<img src="https://cdn.discordapp.com/icons/136501320340209664/bc41eb01d667196c17e05c045f357268.webp?size=80" alt="" width="50">
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="layer mb-2">
|
|
||||||
<button type="button" class="rounded-3 p-1 bg-body bd">
|
|
||||||
<img src="https://cdn.discordapp.com/icons/136501320340209664/bc41eb01d667196c17e05c045f357268.webp?size=80" alt="" width="50">
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="layer mb-2">
|
|
||||||
<button type="button" class="rounded-3 p-1 bg-body bd">
|
|
||||||
<img src="https://cdn.discordapp.com/icons/136501320340209664/bc41eb01d667196c17e05c045f357268.webp?size=80" alt="" width="50">
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="layer mb-2">
|
|
||||||
<button type="button" class="rounded-3 p-1 bg-body bd">
|
|
||||||
<img src="https://cdn.discordapp.com/icons/136501320340209664/bc41eb01d667196c17e05c045f357268.webp?size=80" alt="" width="50">
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="layer mb-2">
|
|
||||||
<button type="button" class="rounded-3 p-1 bg-body bd">
|
|
||||||
<img src="https://cdn.discordapp.com/icons/136501320340209664/bc41eb01d667196c17e05c045f357268.webp?size=80" alt="" width="50">
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="layer mb-2">
|
|
||||||
<button type="button" class="btn btn-secondary rounded-3 p-1 bd" onclick="javascript: alert('add new server (not implemented)');">
|
|
||||||
<span class="d-flex jc-c ai-c" style="width: 50px; height: 50px;">
|
<span class="d-flex jc-c ai-c" style="width: 50px; height: 50px;">
|
||||||
<i class="bi bi-plus-lg fs-4"></i>
|
<i class="bi bi-plus-lg fs-4"></i>
|
||||||
</span>
|
</span>
|
||||||
@ -55,7 +30,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="peer-greed">
|
<div class="peer-greed">
|
||||||
<header class="px-4 py-3 bg-body-secondary">
|
<header class="px-4 py-3 border-bottom">
|
||||||
<div class="peers">
|
<div class="peers">
|
||||||
<div class="peer-greed">
|
<div class="peer-greed">
|
||||||
<div class="peers ai-c">
|
<div class="peers ai-c">
|
||||||
@ -69,7 +44,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="peer d-flex as-s ai-c">
|
<div class="peer d-flex as-s ai-c">
|
||||||
<button type="button" class="btn btn-primary">New Subscription</button>
|
<button type="button" id="newSubscriptionBtn" class="btn btn-primary">New Subscription</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
@ -115,7 +90,114 @@
|
|||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
{% include "home/includes/servermodal.html" %}
|
||||||
|
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|
||||||
<!-- Specific Page JS goes HERE -->
|
<!-- Specific Page JS goes HERE -->
|
||||||
{% block javascripts %}{% endblock javascripts %}
|
{% block javascripts %}
|
||||||
|
|
||||||
|
<script id="serverItemTemplate" type="text/template">
|
||||||
|
<div class="layer mb-2">
|
||||||
|
<button type="button" class="rounded-3 p-1 bg-body bd">
|
||||||
|
<img src="" class="rounded-3" alt="" width="50" height="50">
|
||||||
|
<!-- https://cdn.discordapp.com/icons/136501320340209664/bc41eb01d667196c17e05c045f357268.webp?size=80 -->
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script src="{% static 'js/api.js' %}"></script>
|
||||||
|
<script>
|
||||||
|
function openServerModal() {
|
||||||
|
|
||||||
|
$("#newServerBtn").prop("disabled", true);
|
||||||
|
|
||||||
|
$('#serverOptions').val(null).trigger('change');
|
||||||
|
$("#serverOptions").empty();
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: "/guilds",
|
||||||
|
type: "GET",
|
||||||
|
success: function(response) {
|
||||||
|
for (i = 1; i < response.length; i++) {
|
||||||
|
var guild = response[i];
|
||||||
|
$("#serverOptions").append($("<option>", {
|
||||||
|
value: guild.id,
|
||||||
|
text: guild.name,
|
||||||
|
"data-icon": guild.icon
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#newServerBtn").prop("disabled", false);
|
||||||
|
$("#serverForm .modal").modal("show");
|
||||||
|
},
|
||||||
|
error: function(response) {
|
||||||
|
$("#newServerBtn").prop("disabled", false);
|
||||||
|
alert(JSON.stringify(response, null, 4));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function addServer(serverName, serverId, serverIconHash) {
|
||||||
|
var formData = new FormData();
|
||||||
|
formData.append("name", serverName);
|
||||||
|
formData.append("guild_id", serverId);
|
||||||
|
formData.append("icon", serverIconHash);
|
||||||
|
|
||||||
|
newSavedGuild(formData)
|
||||||
|
.then(resp => {
|
||||||
|
alert(JSON.stringify(resp, null, 4));
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
alert(JSON.stringify(err, null, 4));
|
||||||
|
})
|
||||||
|
|
||||||
|
addServerTemplate(serverId, serverIconHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
getSavedGuilds()
|
||||||
|
.then(resp => {
|
||||||
|
alert(JSON.stringify(resp, null, 4));
|
||||||
|
for (i=0; i < resp.length; i++) {
|
||||||
|
var guild = resp[i];
|
||||||
|
addServerTemplate(guild.guild_id, guild.icon);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
alert(JSON.stringify(err, null, 4));
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
function addServerTemplate(serverId, serverIconHash) {
|
||||||
|
template = $($("#serverItemTemplate").html());
|
||||||
|
template.find("img").attr("src", `https://cdn.discordapp.com/icons/${serverId}/${serverIconHash}.webp?size=80`);
|
||||||
|
$("#serverList").prepend(template);
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#serverForm").on("submit", function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
var selectedOption = $("#serverOptions option:selected");
|
||||||
|
serverName = selectedOption.text();
|
||||||
|
serverId = selectedOption.val();
|
||||||
|
serverIconHash = selectedOption.attr("data-icon");
|
||||||
|
|
||||||
|
addServer(serverName, serverId, serverIconHash);
|
||||||
|
|
||||||
|
// $.ajax({
|
||||||
|
// url: "",
|
||||||
|
// type: "GET",
|
||||||
|
// success: function(response) {
|
||||||
|
// alert(JSON.stringify(response, null, 4));
|
||||||
|
// },
|
||||||
|
// error: function(response) {
|
||||||
|
// alert(JSON.stringify(response, null, 4));
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
$("#serverForm .modal").modal("hide");
|
||||||
|
alert(`${serverName} ${serverId} ${serverIconHash}`);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock javascripts %}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user