From 7d1e4dccc0eeef10813b91b33ca96da66bc5f2ab Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Mon, 19 Jun 2023 17:46:00 +0500 Subject: [PATCH 1/5] adding new phase in competition fixed --- src/apps/api/views/competitions.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/apps/api/views/competitions.py b/src/apps/api/views/competitions.py index d27a3fe5e..41591e861 100644 --- a/src/apps/api/views/competitions.py +++ b/src/apps/api/views/competitions.py @@ -23,7 +23,7 @@ from api.pagination import LargePagination from api.renderers import ZipRenderer from rest_framework.viewsets import ModelViewSet -from api.serializers.competitions import CompetitionSerializerSimple, PhaseSerializer, \ +from api.serializers.competitions import CompetitionSerializerSimple, PhaseSerializer, PhaseDetailSerializer, \ CompetitionCreationTaskStatusSerializer, CompetitionDetailSerializer, CompetitionParticipantSerializer, \ FrontPageCompetitionsSerializer, PhaseResultsSerializer, CompetitionUpdateSerializer, CompetitionCreateSerializer from api.serializers.leaderboards import LeaderboardPhaseSerializer, LeaderboardSerializer @@ -35,6 +35,7 @@ from leaderboards.models import Leaderboard from utils.data import make_url_sassy from api.permissions import IsOrganizerOrCollaborator +from datetime import datetime import logging logger = logging.getLogger() @@ -201,7 +202,32 @@ def update(self, request, *args, **kwargs): leaderboard.is_valid() leaderboard.save() leaderboard_id = leaderboard["id"].value + for phase in data['phases']: + # Newly added phase from front-end has no id + # Add a phase first to get id + # add this phase id in each task + if 'id' not in phase: + + # Create Phase object + # TODO: wrap this whole phase addition inside competition updation in a Transaction in case something fails after the phase id is created and not yet linked to tasks + new_phase_obj = Phase.objects.create( + status=phase["status"], + index=phase["index"], + start=datetime.strptime(phase['start'], "%B %d, %Y"), + end=datetime.strptime(phase['end'], "%B %d, %Y"), + name=phase["name"], + description=phase["description"], + auto_migrate_to_this_phase=phase["auto_migrate_to_this_phase"], + hide_output=phase["hide_output"], + competition=Competition.objects.get(id=data['id']) + ) + # Get phase id + new_phase_id = new_phase_obj.id + # loop over phase tasks to add phase id in each task + for task in phase["tasks"]: + task['phase'] = new_phase_id + phase['leaderboard'] = leaderboard_id serializer = self.get_serializer(instance, data=data, partial=partial) From c3894cc39c3e47ebe26e978c23b8356ce505eb29 Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Mon, 19 Jun 2023 17:52:27 +0500 Subject: [PATCH 2/5] unused import removed --- src/apps/api/views/competitions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apps/api/views/competitions.py b/src/apps/api/views/competitions.py index 41591e861..5ead444c1 100644 --- a/src/apps/api/views/competitions.py +++ b/src/apps/api/views/competitions.py @@ -23,7 +23,7 @@ from api.pagination import LargePagination from api.renderers import ZipRenderer from rest_framework.viewsets import ModelViewSet -from api.serializers.competitions import CompetitionSerializerSimple, PhaseSerializer, PhaseDetailSerializer, \ +from api.serializers.competitions import CompetitionSerializerSimple, PhaseSerializer, \ CompetitionCreationTaskStatusSerializer, CompetitionDetailSerializer, CompetitionParticipantSerializer, \ FrontPageCompetitionsSerializer, PhaseResultsSerializer, CompetitionUpdateSerializer, CompetitionCreateSerializer from api.serializers.leaderboards import LeaderboardPhaseSerializer, LeaderboardSerializer From b04f44ff715417d47cbb93c97a45052af02f3a6e Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Tue, 20 Jun 2023 17:29:07 +0500 Subject: [PATCH 3/5] auto_migrate_removed from new created phase --- src/apps/api/views/competitions.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/apps/api/views/competitions.py b/src/apps/api/views/competitions.py index 5ead444c1..94ba23180 100644 --- a/src/apps/api/views/competitions.py +++ b/src/apps/api/views/competitions.py @@ -208,7 +208,6 @@ def update(self, request, *args, **kwargs): # Add a phase first to get id # add this phase id in each task if 'id' not in phase: - # Create Phase object # TODO: wrap this whole phase addition inside competition updation in a Transaction in case something fails after the phase id is created and not yet linked to tasks new_phase_obj = Phase.objects.create( @@ -218,7 +217,6 @@ def update(self, request, *args, **kwargs): end=datetime.strptime(phase['end'], "%B %d, %Y"), name=phase["name"], description=phase["description"], - auto_migrate_to_this_phase=phase["auto_migrate_to_this_phase"], hide_output=phase["hide_output"], competition=Competition.objects.get(id=data['id']) ) From 9411d6426f30156086a01a592ab426708e17a6e8 Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Tue, 20 Jun 2023 18:19:13 +0500 Subject: [PATCH 4/5] transaction added --- src/apps/api/views/competitions.py | 106 ++++++++++++++--------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/src/apps/api/views/competitions.py b/src/apps/api/views/competitions.py index 94ba23180..339d69ff2 100644 --- a/src/apps/api/views/competitions.py +++ b/src/apps/api/views/competitions.py @@ -36,8 +36,7 @@ from utils.data import make_url_sassy from api.permissions import IsOrganizerOrCollaborator from datetime import datetime -import logging -logger = logging.getLogger() +from django.db import transaction class CompetitionViewSet(ModelViewSet): @@ -185,57 +184,58 @@ def create(self, request, *args, **kwargs): return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) def update(self, request, *args, **kwargs): - """Mostly a copy of the underlying base update, however we return some additional data - in the response to remove a GET from the frontend""" - partial = kwargs.pop('partial', False) - instance = self.get_object() - data = request.data - # TODO - This is Temporary. Need to change Leaderboard to Phase connect to M2M and handle this correctly. - # save leaderboard individually, then pass pk to each phase - if 'leaderboards' in data: - leaderboard_data = data['leaderboards'][0] - if(leaderboard_data['id']): - leaderboard_instance = Leaderboard.objects.get(id=leaderboard_data['id']) - leaderboard = LeaderboardSerializer(leaderboard_instance, data=data['leaderboards'][0]) - else: - leaderboard = LeaderboardSerializer(data=data['leaderboards'][0]) - leaderboard.is_valid() - leaderboard.save() - leaderboard_id = leaderboard["id"].value - - for phase in data['phases']: - # Newly added phase from front-end has no id - # Add a phase first to get id - # add this phase id in each task - if 'id' not in phase: - # Create Phase object - # TODO: wrap this whole phase addition inside competition updation in a Transaction in case something fails after the phase id is created and not yet linked to tasks - new_phase_obj = Phase.objects.create( - status=phase["status"], - index=phase["index"], - start=datetime.strptime(phase['start'], "%B %d, %Y"), - end=datetime.strptime(phase['end'], "%B %d, %Y"), - name=phase["name"], - description=phase["description"], - hide_output=phase["hide_output"], - competition=Competition.objects.get(id=data['id']) - ) - # Get phase id - new_phase_id = new_phase_obj.id - # loop over phase tasks to add phase id in each task - for task in phase["tasks"]: - task['phase'] = new_phase_id - - phase['leaderboard'] = leaderboard_id - - serializer = self.get_serializer(instance, data=data, partial=partial) - serializer.is_valid(raise_exception=True) - self.perform_update(serializer) - - if getattr(instance, '_prefetched_objects_cache', None): - # If 'prefetch_related' has been applied to a queryset, we need to - # forcibly invalidate the prefetch cache on the instance. - instance._prefetched_objects_cache = {} + # Execute everything inside atomic transaction + with transaction.atomic(): + """Mostly a copy of the underlying base update, however we return some additional data + in the response to remove a GET from the frontend""" + partial = kwargs.pop('partial', False) + instance = self.get_object() + data = request.data + # TODO - This is Temporary. Need to change Leaderboard to Phase connect to M2M and handle this correctly. + # save leaderboard individually, then pass pk to each phase + if 'leaderboards' in data: + leaderboard_data = data['leaderboards'][0] + if(leaderboard_data['id']): + leaderboard_instance = Leaderboard.objects.get(id=leaderboard_data['id']) + leaderboard = LeaderboardSerializer(leaderboard_instance, data=data['leaderboards'][0]) + else: + leaderboard = LeaderboardSerializer(data=data['leaderboards'][0]) + leaderboard.is_valid() + leaderboard.save() + leaderboard_id = leaderboard["id"].value + + for phase in data['phases']: + # Newly added phase from front-end has no id + # Add a phase first to get id + # add this phase id in each task + if 'id' not in phase: + # Create Phase object + new_phase_obj = Phase.objects.create( + status=phase["status"], + index=phase["index"], + start=datetime.strptime(phase['start'], "%B %d, %Y"), + end=datetime.strptime(phase['end'], "%B %d, %Y"), + name=phase["name"], + description=phase["description"], + hide_output=phase["hide_output"], + competition=Competition.objects.get(id=data['id']) + ) + # Get phase id + new_phase_id = new_phase_obj.id + # loop over phase tasks to add phase id in each task + for task in phase["tasks"]: + task['phase'] = new_phase_id + + phase['leaderboard'] = leaderboard_id + + serializer = self.get_serializer(instance, data=data, partial=partial) + serializer.is_valid(raise_exception=True) + self.perform_update(serializer) + + if getattr(instance, '_prefetched_objects_cache', None): + # If 'prefetch_related' has been applied to a queryset, we need to + # forcibly invalidate the prefetch cache on the instance. + instance._prefetched_objects_cache = {} # Re-do serializer in detail version (i.e. for Collaborator data) context = self.get_serializer_context() From 44eb24bed962856cc4452a7b3603273e086cf743 Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Thu, 22 Jun 2023 23:48:37 +0500 Subject: [PATCH 5/5] no end date handled --- src/apps/api/views/competitions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apps/api/views/competitions.py b/src/apps/api/views/competitions.py index 339d69ff2..ded8e3df8 100644 --- a/src/apps/api/views/competitions.py +++ b/src/apps/api/views/competitions.py @@ -214,7 +214,7 @@ def update(self, request, *args, **kwargs): status=phase["status"], index=phase["index"], start=datetime.strptime(phase['start'], "%B %d, %Y"), - end=datetime.strptime(phase['end'], "%B %d, %Y"), + end=datetime.strptime(phase['end'], "%B %d, %Y") if phase['end'] else None, name=phase["name"], description=phase["description"], hide_output=phase["hide_output"],