Admin has more options on which clips to allow and deny
authorGeorgios Atheridis <georgios@atheridis.org>
Mon, 23 Jan 2023 14:03:54 +0000 (14:03 +0000)
committerGeorgios Atheridis <georgios@atheridis.org>
Mon, 23 Jan 2023 14:03:54 +0000 (14:03 +0000)
The admin can now choose to only allow clips from specific channel,
clips that were made during a specific date or later, whether or not
the user who is uploading the clip also needs to be the one who has
clipped, and also added a cutoff point for being able to upload clips.

Fixed some timezone issues caused in the Clips model.

TODO
core/manager/admin.py
core/manager/errors.py
core/manager/migrations/0013_resetdata_clip_limit_to_channels_and_more.py [new file with mode: 0644]
core/manager/migrations/0014_allowedchannel.py [new file with mode: 0644]
core/manager/migrations/0015_remove_resetdata_clip_limit_to_channels_and_more.py [new file with mode: 0644]
core/manager/models.py
core/manager/request_clip.py
core/manager/views.py

diff --git a/TODO b/TODO
index eaaad180baa4575267636d40c7cb775e68242296..7185a54058454919d8e4d59c43e71d9d8a684f99 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,10 +1,10 @@
 - Clips should only be accepted under certain conditions layed out in the database.
--- Clip for channels
--- Clip needs to be created within a timeframe
--- Clip needs to be created by user adding the clip
+-- Allow admin site to add username instead of broadcaster id in ResetData
 
 - Implement Clip categories
 
 - Display clip info more clearly
 -- Who uploaded the clip
 -- Clip comments
+
+- Refresh tokens
index 83244a4a25e8b91fc34e81e7202e13b2bcd2ea65..9a25d48c9c2c6f9008e2c26fd8d260e9f4a8f285 100644 (file)
@@ -17,7 +17,18 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 from django.contrib import admin
 
 # Register your models here.
-from .models import Clip, ResetData
+from .models import Clip, ResetData, AllowedChannel
+
+
+class AllowedChannelInline(admin.TabularInline):
+    model = AllowedChannel
+
+
+class ResetDataAdmin(admin.ModelAdmin):
+    inlines = [
+        AllowedChannelInline,
+    ]
+
 
 admin.site.register(Clip)
-admin.site.register(ResetData)
+admin.site.register(ResetData, ResetDataAdmin)
index 4eeb6916c817d81012c46f94516bdc9216587585..702232d823536a7c19de66425d9fce6707e1e698 100644 (file)
@@ -14,5 +14,23 @@ GNU Affero General Public License for more details.
 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <https://www.gnu.org/licenses/>.
 """
-class TooManyClips(Exception):
+
+
+class TooManyClipsError(Exception):
+    pass
+
+
+class TooLateError(Exception):
+    pass
+
+
+class ChannelNotAllowedError(Exception):
+    pass
+
+
+class UserNotCreatedClipError(Exception):
+    pass
+
+
+class ClipTooOldError(Exception):
     pass
diff --git a/core/manager/migrations/0013_resetdata_clip_limit_to_channels_and_more.py b/core/manager/migrations/0013_resetdata_clip_limit_to_channels_and_more.py
new file mode 100644 (file)
index 0000000..62a60d9
--- /dev/null
@@ -0,0 +1,29 @@
+# Generated by Django 4.1.5 on 2023-01-23 11:51
+
+import datetime
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("manager", "0012_resetdata_ranks"),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name="resetdata",
+            name="clip_limit_to_channels",
+            field=models.JSONField(null=True),
+        ),
+        migrations.AddField(
+            model_name="resetdata",
+            name="clip_newer_than",
+            field=models.DateTimeField(default=datetime.datetime(1970, 1, 1, 0, 0, 1)),
+        ),
+        migrations.AddField(
+            model_name="resetdata",
+            name="user_created_clip",
+            field=models.BooleanField(default=True),
+        ),
+    ]
diff --git a/core/manager/migrations/0014_allowedchannel.py b/core/manager/migrations/0014_allowedchannel.py
new file mode 100644 (file)
index 0000000..ec93b98
--- /dev/null
@@ -0,0 +1,36 @@
+# Generated by Django 4.1.5 on 2023-01-23 12:03
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("manager", "0013_resetdata_clip_limit_to_channels_and_more"),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name="AllowedChannel",
+            fields=[
+                (
+                    "id",
+                    models.BigAutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                ("broadcaster_id", models.CharField(max_length=100)),
+                (
+                    "reset_data",
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        to="manager.resetdata",
+                    ),
+                ),
+            ],
+        ),
+    ]
diff --git a/core/manager/migrations/0015_remove_resetdata_clip_limit_to_channels_and_more.py b/core/manager/migrations/0015_remove_resetdata_clip_limit_to_channels_and_more.py
new file mode 100644 (file)
index 0000000..3ad93f1
--- /dev/null
@@ -0,0 +1,23 @@
+# Generated by Django 4.1.5 on 2023-01-23 12:14
+
+import datetime
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("manager", "0014_allowedchannel"),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name="resetdata",
+            name="clip_limit_to_channels",
+        ),
+        migrations.AddField(
+            model_name="resetdata",
+            name="end_date_time",
+            field=models.DateTimeField(default=datetime.datetime(2038, 1, 19, 3, 14)),
+        ),
+    ]
index 6c6fc882f48d6634260c1cd29215eef86dd33ebb..bd497b7f0e3fd0be00c6f4f7d5433095d45f9d45 100644 (file)
@@ -14,14 +14,26 @@ GNU Affero General Public License for more details.
 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <https://www.gnu.org/licenses/>.
 """
+from datetime import datetime
 from django.db import models
 from django.contrib.auth.models import User
 
 
 class ResetData(models.Model):
     date_time = models.DateTimeField()
+    end_date_time = models.DateTimeField(default=datetime.utcfromtimestamp(2147483640))
     max_clips = models.IntegerField(default=2)
     ranks = models.IntegerField(default=5)
+    user_created_clip = models.BooleanField(default=True)
+    clip_newer_than = models.DateTimeField(default=datetime.utcfromtimestamp(1))
+
+
+class AllowedChannel(models.Model):
+    broadcaster_id = models.CharField(max_length=100)
+    reset_data = models.ForeignKey(ResetData, on_delete=models.CASCADE)
+
+    class Meta:
+        unique_together = ('broadcaster_id', 'reset_data',)
 
 
 class Clip(models.Model):
index 47654ca942c0c73148e8e12e4c799437a017e1e7..bdfddbc9590bd16a7120f58a3b6bae69816194a7 100644 (file)
@@ -16,10 +16,11 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 """
 import requests
 import datetime
-from .errors import TooManyClips
+import pytz
+from .errors import TooManyClipsError, TooLateError, ChannelNotAllowedError, UserNotCreatedClipError, ClipTooOldError
 from .models import Clip, ResetData
 from urllib.parse import urlparse
-from allauth.socialaccount.models import SocialApp, SocialToken
+from allauth.socialaccount.models import SocialApp, SocialToken, SocialAccount
 from django.contrib.auth.models import User
 
 
@@ -28,9 +29,11 @@ def request_clip(user, clip: str):
     user = User.objects.get(id=user.id)
     reset_data = ResetData.objects.latest('date_time')
     reset_time = reset_data.date_time
-    print(reset_time)
     max_clips = reset_data.max_clips
 
+    if reset_data.end_date_time < datetime.datetime.now(pytz.utc):
+        raise TooLateError
+
     if (
         len(
             Clip.objects.filter(account=user).filter(
@@ -39,7 +42,7 @@ def request_clip(user, clip: str):
         )
         >= max_clips
     ):
-        raise TooManyClips
+        raise TooManyClipsError
 
     social_app: SocialApp = SocialApp.objects.first()
     oauth = SocialToken.objects.first().token
@@ -58,7 +61,22 @@ def request_clip(user, clip: str):
         raise Exception
 
     data = r.json()["data"][0]
-    print(data)
+
+    if reset_data.user_created_clip:
+        if not SocialAccount.objects.get(user=user).uid == data["creator_id"]:
+            raise UserNotCreatedClipError
+
+    if reset_data.allowedchannel_set.count() != 0:
+        allowed_channels = reset_data.allowedchannel_set.filter(broadcaster_id=data["broadcaster_id"])
+        if not allowed_channels.exists():
+            raise ChannelNotAllowedError
+
+    if (
+            reset_data.clip_newer_than
+            > datetime.datetime.strptime(data["created_at"], "%Y-%m-%dT%H:%M:%S%z")
+    ):
+        raise ClipTooOldError
+
     clip = Clip(
         id=data["id"],
         url=data["url"],
@@ -75,7 +93,7 @@ def request_clip(user, clip: str):
         thumbnail_url=data["thumbnail_url"],
         duration=data["duration"],
         vod_offset=data["vod_offset"],
-        date_added=datetime.datetime.now(),
+        date_added=datetime.datetime.now(pytz.utc),
         account=user,
     )
     clip.validate_unique()
index a9441ab2d6cbfe15c0071c655a569cb261fa7afb..5d66eca3a57e245978077eff518b2a65a2584158 100644 (file)
@@ -20,7 +20,7 @@ from django.contrib.admin.views.decorators import staff_member_required
 
 from .request_clip import request_clip
 from .forms import ClipForm, RankForm
-from .errors import TooManyClips
+from .errors import TooManyClipsError, TooLateError, ChannelNotAllowedError, UserNotCreatedClipError, ClipTooOldError
 from .models import Clip, ResetData
 
 
@@ -28,16 +28,15 @@ from .models import Clip, ResetData
 def show_clips(request, id):
     reset_data = ResetData.objects.latest('date_time')
     reset_time = reset_data.date_time
-    print(reset_time)
     try:
-        video = Clip.objects.filter(date_added__gt=reset_time)[id - 1]
+        video = Clip.objects.filter(
+            date_added__gt=reset_time
+        ).order_by("date_added")[id - 1]
     except IndexError:
-        print("hi")
         return redirect(final_ranking)
     if request.method == "POST":
         form = RankForm(request.POST)
         if form.is_valid():
-            print(f"post: {id} | {form.cleaned_data['value']}")
             video.rank = form.cleaned_data["value"]
             video.save()
             return redirect(show_clips, id=id + 1)
@@ -51,7 +50,6 @@ def show_clips(request, id):
 def final_ranking(request):
     reset_data = ResetData.objects.latest('date_time')
     reset_time = reset_data.date_time
-    print(reset_time)
     return render(request, "manager/final.html", context={
         "videos": Clip.objects.filter(date_added__gt=reset_time),
         "ranks": range(reset_data.ranks, 0, -1),
@@ -69,24 +67,21 @@ def get_name(request):
                 request_clip(user=request.user, clip=form.cleaned_data["clip"])
             except ValidationError:
                 message = "Sorry, clip already exists. Please send another clip."
-                return render(request, "manager/index.html", {"message": message})
-                # return HttpResponse(b"<h1>Clip already exists</h1>")
-            except TooManyClips:
+            except TooManyClipsError:
                 message = "Sorry, you have already reached the clip limit."
-                return render(request, "manager/index.html", {"message": message})
-                # return HttpResponse(b"<h1>You have submitted the maximum number of clips</h1>")
+            except TooLateError:
+                message = "Sorry, the deadline for posting has passed"
+            except ChannelNotAllowedError:
+                message = "Sorry, you're not allowed to send a clip from that channel."
+            except UserNotCreatedClipError:
+                message = "Sorry, you need to be the one who has created the clip."
+            except ClipTooOldError:
+                message = "Sorry, the clip is too old. Please send a newer clip."
             except Exception:
                 message = "Something went wrong."
-                return render(request, "manager/index.html", {"message": message})
-                # return HttpResponse(b"<h1>NOPE</h1>")
-            # process the data in form.cleaned_data as required
-            # ...
-            # redirect to a new URL:
-            message = "Thank you for submitting a clip."
+            else:
+                message = "Thank you for submitting a clip."
             return render(request, "manager/index.html", {"message": message})
-            # return HttpResponseRedirect("/thanks/")
-        else:
-            print("invalid")
 
     # if a GET (or any other method) we'll create a blank form
     else: