davidcr01 / WordlePlus

Repository to store all the documentation, files and structure of my Final Degree Project (TFG in Spanish). The main goal is to develop, as full-stack web developer, a remodel of the Wordle game, including more features and functionalities using Ionic, Django REST Api and PostgreSQL.
1 stars 0 forks source link

Create participations as an admin (S6) #42

Closed davidcr01 closed 1 year ago

davidcr01 commented 1 year ago

Description

As an administrator, is necessary to implement the logic of creating new participations for a tournament. This consists in assigning players to a tournament.

Tasks

davidcr01 commented 1 year ago

Update Report

A new model and serializer have been added to manage the participation:

class Participation(models.Model):
    tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE)
    player = models.ForeignKey(Player, on_delete=models.CASCADE)
    timestamp = models.DateTimeField(auto_now_add=True)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['tournament', 'player'],
                name='unique_participation'
            )
        ]

The model specifies the combination of the tournament and the player as unique. This combination is not specified as the primary key for performance purposes.

About the view, is necessary to check some conditions in the POST request:

Besides, when creating the participation, a new notification must be created and associated to the player. All of this is controlled in this code snippet:

def create(self, request, *args, **kwargs):
        tournament_id = request.data.get('tournament_id')
        player = request.user.player

        # Request tournament_id field
        if not tournament_id:
            return Response({'error': 'tournament_id field is required.'}, status=404)
        # Check if the user is a player
        if not player:
            return Response({'error': 'Player not found'}, status=404)

        # Case of non existing tournament
        try:
            tournament = Tournament.objects.get(id=tournament_id)
        except Tournament.DoesNotExist:
            return Response({'error': 'Invalid tournament ID'}, status=400)

        # Case of existing participation
        if Participation.objects.filter(tournament_id=tournament_id, player=player).exists():
            return Response({'error': 'You are participating in this tournament.'}, status=status.HTTP_400_BAD_REQUEST)

        # Case of the tournament is closed
        if tournament.is_closed:
            return Response({'error': 'Tournament is closed for participation'}, status=400)

        # Close the tournament if is full
        if tournament.num_players >= tournament.max_players:
            tournament.is_closed = True
            tournament.save()
            return Response({'error': 'Tournament is already full'}, status=400)

        participation = Participation.objects.create(tournament=tournament, player=player)
        tournament.num_players += 1
        # Close the tournament if is full
        if tournament.num_players >= tournament.max_players:
            tournament.is_closed = True

        tournament.save()

        # Create the related notification to the player
        message = f"You were assigned in {tournament.name}. Good luck!"
        link = "http://localhost:8100/tabs/tournaments"
        notification = Notification.objects.create(player=player, text=message, link=link)
        notification.save()

        serializer = self.get_serializer(participation)
        return Response(serializer.data, status=201)

In case of the GET request, we filter the participations by the tournament_id parameter, which is requested.

def list(self, request):
        tournament_id = int(request.query_params.get('tournament_id'),10)
        if not tournament_id:
            return Response({'error': 'tournament_id parameter is required.'}, status=status.HTTP_400_BAD_REQUEST)

        queryset = self.get_queryset().filter(tournament_id=tournament_id)
        serializer = self.serializer_class(queryset, many=True)
        return Response(serializer.data)

In the Admin site, the same logic must be implemented. Besides, is necessary to modify the num_players field of the tournament when creating (save_model) or deleting (either single delete (delete_model) or multiselection delete delete_queryset )

class TournamentAdmin(admin.ModelAdmin):
    list_display = ('id', 'name', 'description', 'num_players', 'max_players', 'word_length', 'is_closed')
    form = TournamentsForm

class ParticipationAdmin(admin.ModelAdmin):
    model = Participation

    list_display = ('id', 'tournament', 'player',)
    list_filter = ('tournament',)

    # Checks if a new participation can be added
    def save_model(self, request, obj, form, change):
        tournament = obj.tournament
        if (tournament.num_players >= tournament.max_players):
            raise forms.ValidationError("The max number of participations for this tournament has been reached.")

        tournament.num_players += 1
        if (tournament.num_players >= tournament.max_players):
            tournament.is_closed = True
        tournament.save()
        super().save_model(request, obj, form, change)

        player = obj.player
        message = f"You were assigned in {tournament.name}. Good luck!"
        link = "http://localhost:8100/tabs/tournaments"
        notification = Notification.objects.create(player=player, text=message, link=link)
        notification.save()

    # Decreases the number of the players of the tournament
    def delete_model(self, request, obj):
        tournament = obj.tournament
        tournament.num_players -= 1

        if tournament.num_players < tournament.max_players:
            tournament.is_closed = False

        tournament.save()
        super().delete_model(request, obj)

    # Updates the number of players when using the multi-selection 
    def delete_queryset(self, request, queryset):
        tournaments = set()
        for participation in queryset:
            tournaments.add(participation.tournament)

        super().delete_queryset(request, queryset)

        for tournament in tournaments:
            num_participations = Participation.objects.filter(tournament=tournament).count()
            tournament.num_players = num_participations

            if num_participations >= tournament.max_players:
                tournament.is_closed = True
            else:
                tournament.is_closed = False

            tournament.save()

The following video shows an administrator:

https://github.com/davidcr01/WordlePlus/assets/72193239/39e57d0b-7087-4b07-aa02-ad535ff2afd5

The following video shows:

https://github.com/davidcr01/WordlePlus/assets/72193239/326b659a-c65d-480f-9e0b-6a3b45adb384