From 75800ee9ee31bcfc3769202cd22907f0de96e456 Mon Sep 17 00:00:00 2001 From: Justus Ojala Date: Mon, 13 Oct 2025 19:25:40 +0300 Subject: [PATCH] Add submit_id to signup model --- .../0012_alter_baserole_category_and_more.py | 65 +++++++++++++++++++ ..._id_alter_basewebhook_polymorphic_ctype.py | 32 +++++++++ .../migrations/0084_alter_signup_submit_id.py | 18 +++++ .../migrations/0085_alter_signup_submit_id.py | 18 +++++ .../migrations/0086_alter_signup_submit_id.py | 18 +++++ .../migrations/0087_alter_signup_submit_id.py | 18 +++++ webapp/models.py | 2 + webapp/serializers.py | 3 +- webapp/views.py | 34 ++-------- 9 files changed, 178 insertions(+), 30 deletions(-) create mode 100644 kaehmy/migrations/0012_alter_baserole_category_and_more.py create mode 100644 webapp/migrations/0083_signup_submit_id_alter_basewebhook_polymorphic_ctype.py create mode 100644 webapp/migrations/0084_alter_signup_submit_id.py create mode 100644 webapp/migrations/0085_alter_signup_submit_id.py create mode 100644 webapp/migrations/0086_alter_signup_submit_id.py create mode 100644 webapp/migrations/0087_alter_signup_submit_id.py diff --git a/kaehmy/migrations/0012_alter_baserole_category_and_more.py b/kaehmy/migrations/0012_alter_baserole_category_and_more.py new file mode 100644 index 0000000..6527b21 --- /dev/null +++ b/kaehmy/migrations/0012_alter_baserole_category_and_more.py @@ -0,0 +1,65 @@ +# Generated by Django 4.2.24 on 2025-10-13 14:48 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("kaehmy", "0011_delete_kaehmybaserole"), + ] + + operations = [ + migrations.AlterField( + model_name="baserole", + name="category", + field=models.CharField( + choices=[ + ("board", "Board"), + ("corporate", "Corporate affairs"), + ("freshman", "Freshmen"), + ("international", "International"), + ("siwa", "SIK's free time"), + ("media", "Media"), + ("tech", "Technology"), + ("wellbeing", "Wellbeing"), + ("sikpaja", "Sik-paja"), + ("ceremonies", "Ceremonies"), + ("studies", "Studies"), + ("sosso", "Sössö magazine"), + ("pota", "PoTa"), + ("alumni", "Alumni relations"), + ("n", "N"), + ("others", "Others"), + ], + default="others", + max_length=255, + verbose_name="Category", + ), + ), + migrations.AlterField( + model_name="customrole", + name="baserole_ptr", + field=models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="kaehmy.baserole", + ), + ), + migrations.AlterField( + model_name="presetrole", + name="baserole_ptr", + field=models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="kaehmy.baserole", + ), + ), + ] diff --git a/webapp/migrations/0083_signup_submit_id_alter_basewebhook_polymorphic_ctype.py b/webapp/migrations/0083_signup_submit_id_alter_basewebhook_polymorphic_ctype.py new file mode 100644 index 0000000..0478a85 --- /dev/null +++ b/webapp/migrations/0083_signup_submit_id_alter_basewebhook_polymorphic_ctype.py @@ -0,0 +1,32 @@ +# Generated by Django 4.2.24 on 2025-10-13 14:48 + +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ("contenttypes", "0002_remove_content_type_name"), + ("webapp", "0082_delete_baserole"), + ] + + operations = [ + migrations.AddField( + model_name="signup", + name="submit_id", + field=models.UUIDField(default=uuid.uuid4, editable=False, null=True), + ), + migrations.AlterField( + model_name="basewebhook", + name="polymorphic_ctype", + field=models.ForeignKey( + editable=False, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="polymorphic_%(app_label)s.%(class)s_set+", + to="contenttypes.contenttype", + ), + ), + ] diff --git a/webapp/migrations/0084_alter_signup_submit_id.py b/webapp/migrations/0084_alter_signup_submit_id.py new file mode 100644 index 0000000..05db16e --- /dev/null +++ b/webapp/migrations/0084_alter_signup_submit_id.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.24 on 2025-10-13 15:19 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("webapp", "0083_signup_submit_id_alter_basewebhook_polymorphic_ctype"), + ] + + operations = [ + migrations.AlterField( + model_name="signup", + name="submit_id", + field=models.UUIDField(null=True), + ), + ] diff --git a/webapp/migrations/0085_alter_signup_submit_id.py b/webapp/migrations/0085_alter_signup_submit_id.py new file mode 100644 index 0000000..2c91f2d --- /dev/null +++ b/webapp/migrations/0085_alter_signup_submit_id.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.24 on 2025-10-13 15:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("webapp", "0084_alter_signup_submit_id"), + ] + + operations = [ + migrations.AlterField( + model_name="signup", + name="submit_id", + field=models.CharField(null=True), + ), + ] diff --git a/webapp/migrations/0086_alter_signup_submit_id.py b/webapp/migrations/0086_alter_signup_submit_id.py new file mode 100644 index 0000000..8afc208 --- /dev/null +++ b/webapp/migrations/0086_alter_signup_submit_id.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.24 on 2025-10-13 15:51 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("webapp", "0085_alter_signup_submit_id"), + ] + + operations = [ + migrations.AlterField( + model_name="signup", + name="submit_id", + field=models.UUIDField(editable=False, null=True), + ), + ] diff --git a/webapp/migrations/0087_alter_signup_submit_id.py b/webapp/migrations/0087_alter_signup_submit_id.py new file mode 100644 index 0000000..be3ee7a --- /dev/null +++ b/webapp/migrations/0087_alter_signup_submit_id.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.24 on 2025-10-13 15:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("webapp", "0086_alter_signup_submit_id"), + ] + + operations = [ + migrations.AlterField( + model_name="signup", + name="submit_id", + field=models.UUIDField(editable=False, null=True, unique=True), + ), + ] diff --git a/webapp/models.py b/webapp/models.py index 400bef6..c1e0096 100644 --- a/webapp/models.py +++ b/webapp/models.py @@ -195,6 +195,8 @@ class Signup(models.Model): email = models.EmailField(blank=True, null=True) # Random unique identifier. Used for signup editing by the user. uuid = models.UUIDField(default=uuid4, editable=False) + # Random unique identifier generated by browser upon opening signup form. Used to prevent duplicate signups. + submit_id = models.UUIDField(null=True, editable=False, unique=True) deleted = models.BooleanField(default=False) def __str__(self): diff --git a/webapp/serializers.py b/webapp/serializers.py index b6d8321..cdcdd1e 100644 --- a/webapp/serializers.py +++ b/webapp/serializers.py @@ -7,6 +7,7 @@ class SignupSerializer(serializers.ModelSerializer): source="signupForm", queryset=SignupForm.objects.all() ) list_name = serializers.CharField(read_only=True) + submit_id = serializers.UUIDField(required=False) def add_extra_fields(self, validated_data): questions = validated_data["signupForm"].questions @@ -34,7 +35,7 @@ class SignupSerializer(serializers.ModelSerializer): class Meta: model = Signup - fields = ("id", "signupForm_id", "answer", "list_name") + fields = ("id", "submit_id", "signupForm_id", "answer", "list_name") extra_kwargs = { "url": { "view_name": "signup-detail", diff --git a/webapp/views.py b/webapp/views.py index d2188ab..8ab4767 100644 --- a/webapp/views.py +++ b/webapp/views.py @@ -12,6 +12,7 @@ from django.views.decorators.http import require_http_methods from django_filters import rest_framework as filters from django.db.models import Prefetch from django.core.exceptions import ObjectDoesNotExist +from django.db.utils import IntegrityError from rest_framework import routers from rest_framework.response import Response from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet @@ -201,29 +202,6 @@ class SignupViewSet(ModelViewSet): serializer_class = SignupSerializer permission_classes = [SignupPermission] - submit_keys = ( - {} - ) # Dictionary for currently invalid submission keys; {key: timestamp} - - def key_is_unique(self, submitKey): - current_time = time.time() - print("Checking", submitKey) - # Remove expired keys from dict (older than 1 h) - # A key that expires as the function is called is considered valid - print("Keys", SignupViewSet.submit_keys) - SignupViewSet.submit_keys = { - key: time - for key, time in SignupViewSet.submit_keys.items() - if time + 3600 >= current_time - } - print("After", SignupViewSet.submit_keys) - if submitKey not in SignupViewSet.submit_keys: # Key is unique; valid - SignupViewSet.submit_keys[submitKey] = current_time - return True - else: # Key is not unique; invalid, refresh timestamp - SignupViewSet.submit_keys[submitKey] = current_time - return False - @action(detail=True, methods=["get", "put"], permission_classes=[AllowAny]) def edit(self, request, pk=None, *args, **kwargs): uuid = request.query_params.get("uuid", None) @@ -238,12 +216,6 @@ class SignupViewSet(ModelViewSet): def create(self, request, *args, **kwargs): id = request.data["signupForm_id"] try: - submitKey = request.data.get("submitKey") - if submitKey is not None and not self.key_is_unique(submitKey): - return JsonResponse( - status=200, data={"message": "Ignored repeated request"} - ) - answer = request.data["answer"] form = SignupForm.objects.get(id=id) if form.isOpen: @@ -256,6 +228,10 @@ class SignupViewSet(ModelViewSet): return JsonResponse( status=404, data={"error": f"SignupForm {id} not found"} ) + except IntegrityError: + return JsonResponse( + status=200, data={"message": "The submission had already been received"} + ) else: return JsonResponse( status=404, data={"error": f"SignupForm {id} not found"}