From 9ed14432f8c3d8e0fc4520fcb063363b37287c65 Mon Sep 17 00:00:00 2001 From: Ivar Nakken Date: Thu, 7 Mar 2024 00:22:38 +0100 Subject: [PATCH] Introduce "too late" as an event presence status Similar to the "not present" status, the user will automatically recieve a penalty. I figured all previous penalties can be removed since only the newest one should matter. I would like some input as to how this will affect other penalties, e.g. given due to late payments (or other things?), as they will also be removed if the presence is changed to something worthy of a penalty. Should we consider to introduce "penalty types" stored on the record, e.g. "presence", "payment", "other"? Also, this resolves a bug, as it is currently not possible to auto delete previous penalties related to the event due to the `.delete()` not being at the end the atomic block. Not sure why it had to be atomic in the first place. --- lego/apps/events/constants.py | 1 + .../0040_alter_registration_presence.py | 26 +++++++++++++ lego/apps/events/models.py | 38 ++++++++++++++----- lego/apps/events/serializers/registrations.py | 12 +++--- 4 files changed, 61 insertions(+), 16 deletions(-) create mode 100644 lego/apps/events/migrations/0040_alter_registration_presence.py diff --git a/lego/apps/events/constants.py b/lego/apps/events/constants.py index 2753b7b83..1f5281116 100644 --- a/lego/apps/events/constants.py +++ b/lego/apps/events/constants.py @@ -70,6 +70,7 @@ class PRESENCE_CHOICES(models.TextChoices): UNKNOWN = "UNKNOWN" PRESENT = "PRESENT" + LATE = "LATE" NOT_PRESENT = "NOT_PRESENT" diff --git a/lego/apps/events/migrations/0040_alter_registration_presence.py b/lego/apps/events/migrations/0040_alter_registration_presence.py new file mode 100644 index 000000000..5c960e230 --- /dev/null +++ b/lego/apps/events/migrations/0040_alter_registration_presence.py @@ -0,0 +1,26 @@ +# Generated by Django 4.0.10 on 2024-03-06 22:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("events", "0039_remove_event_use_contact_tracing"), + ] + + operations = [ + migrations.AlterField( + model_name="registration", + name="presence", + field=models.CharField( + choices=[ + ("UNKNOWN", "Unknown"), + ("PRESENT", "Present"), + ("LATE", "Late"), + ("NOT_PRESENT", "Not Present"), + ], + default="UNKNOWN", + max_length=20, + ), + ), + ] diff --git a/lego/apps/events/models.py b/lego/apps/events/models.py index efb369e70..d2ecac4cd 100644 --- a/lego/apps/events/models.py +++ b/lego/apps/events/models.py @@ -930,26 +930,46 @@ def set_presence(self, presence: constants.PRESENCE_CHOICES) -> None: """Wrap this method in a transaction""" if presence not in constants.PRESENCE_CHOICES: raise ValueError("Illegal presence choice") + self.presence = presence self.handle_user_penalty(presence) self.save() + def delete_penalties_for_event(self) -> None: + for penalty in self.user.penalties.filter(source_event=self.event): + penalty.delete() + def handle_user_penalty(self, presence: constants.PRESENCE_CHOICES) -> None: + """ + Previous penalties related to the event are deleted since the + newest presence is the only one that matters + """ + if ( self.event.heed_penalties and presence == constants.PRESENCE_CHOICES.NOT_PRESENT and self.event.penalty_weight_on_not_present ): - if not self.user.penalties.filter(source_event=self.event).exists(): - Penalty.objects.create( - user=self.user, - reason=f"Møtte ikke opp på {self.event.title}.", - weight=self.event.penalty_weight_on_not_present, - source_event=self.event, - ) + self.delete_penalties_for_event() + Penalty.objects.create( + user=self.user, + reason=f"Møtte ikke opp på {self.event.title}.", + weight=self.event.penalty_weight_on_not_present, + source_event=self.event, + ) + elif ( + self.event.heed_penalties + and presence == constants.PRESENCE_CHOICES.TOO_LATE + ): + self.delete_penalties_for_event() + Penalty.objects.create( + user=self.user, + reason=f"Møtte for sent opp på {self.event.title}.", + weight=1, + source_event=self.event, + ) else: - for penalty in self.user.penalties.filter(source_event=self.event): - penalty.delete() + self.delete_penalties_for_event() def add_to_pool(self, pool: Pool) -> Registration: allowed: bool = False diff --git a/lego/apps/events/serializers/registrations.py b/lego/apps/events/serializers/registrations.py index a3ed6dde4..3ce2220c3 100644 --- a/lego/apps/events/serializers/registrations.py +++ b/lego/apps/events/serializers/registrations.py @@ -1,4 +1,3 @@ -from django.db import transaction from rest_framework import serializers from rest_framework_jwt.serializers import ImpersonateAuthTokenSerializer @@ -65,13 +64,12 @@ class Meta: ) def update(self, instance, validated_data): - with transaction.atomic(): - presence = validated_data.pop("presence", None) - super().update(instance, validated_data) - if presence: - instance.set_presence(presence) + presence = validated_data.pop("presence", None) + super().update(instance, validated_data) + if presence: + instance.set_presence(presence) - return instance + return instance class RegistrationAnonymizedReadSerializer(BasisModelSerializer):