diff --git a/src/apps/api/views/competitions.py b/src/apps/api/views/competitions.py index d27a3fe5e..ded8e3df8 100644 --- a/src/apps/api/views/competitions.py +++ b/src/apps/api/views/competitions.py @@ -35,8 +35,8 @@ from leaderboards.models import Leaderboard from utils.data import make_url_sassy from api.permissions import IsOrganizerOrCollaborator -import logging -logger = logging.getLogger() +from datetime import datetime +from django.db import transaction class CompetitionViewSet(ModelViewSet): @@ -184,34 +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']: - 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") if phase['end'] else None, + 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()