Skip to content

Commit

Permalink
🔨 User can't be Player & Manager of same tourney
Browse files Browse the repository at this point in the history
Add a constraint on `Player` and `Manager` to make it so that a `User`
cannot simultaneously be in the same tournament twice as a player and a
manager. This is also tested to avoid future regression.

Fix #41
  • Loading branch information
Lymkwi committed Oct 14, 2023
1 parent b40cf5d commit ac60697
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 33 deletions.
83 changes: 50 additions & 33 deletions insalan/tournament/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

from insalan.user.models import User


class Event(models.Model):
"""
An Event is any single event that is characterized by the following:
Expand All @@ -38,22 +39,20 @@ class Event(models.Model):
null=False,
)
description = models.CharField(
verbose_name=_("Description de l'évènement"), max_length=128, default="", blank=True
verbose_name=_("Description de l'évènement"),
max_length=128,
default="",
blank=True,
)
year = models.IntegerField(
verbose_name=_("Année"),
null=False,
validators=[MinValueValidator(2003)]
verbose_name=_("Année"), null=False, validators=[MinValueValidator(2003)]
)
month = models.IntegerField(
verbose_name=_("Mois"),
null=False,
validators=[MinValueValidator(1),MaxValueValidator(12)]
)
ongoing = models.BooleanField(
verbose_name=_("En cours"),
default=False
validators=[MinValueValidator(1), MaxValueValidator(12)],
)
ongoing = models.BooleanField(verbose_name=_("En cours"), default=False)
logo: models.FileField = models.FileField(
verbose_name=_("Logo"),
blank=True,
Expand All @@ -66,6 +65,7 @@ class Event(models.Model):

class Meta:
"""Meta options"""

verbose_name = _("Évènement")
verbose_name_plural = _("Évènements")

Expand All @@ -91,8 +91,10 @@ class Game(models.Model):
"""
A Game is the representation of a Game that is being played at InsaLan
"""

class Meta:
"""Meta options"""

verbose_name = _("Jeu")
verbose_name_plural = _("Jeux")

Expand Down Expand Up @@ -129,15 +131,9 @@ class Tournament(models.Model):
"""

event = models.ForeignKey(
Event,
verbose_name=_("Évènement"),
on_delete=models.CASCADE
)
game = models.ForeignKey(
Game,
verbose_name=_("Jeu"),
on_delete=models.CASCADE
Event, verbose_name=_("Évènement"), on_delete=models.CASCADE
)
game = models.ForeignKey(Game, verbose_name=_("Jeu"), on_delete=models.CASCADE)
name = models.CharField(
verbose_name=_("Nom du tournoi"),
validators=[MinLengthValidator(3)],
Expand All @@ -162,6 +158,7 @@ class Tournament(models.Model):

class Meta:
"""Meta options"""

verbose_name = _("Tournoi")
verbose_name_plural = _("Tournois")

Expand Down Expand Up @@ -205,7 +202,7 @@ class Team(models.Model):
null=True,
blank=True,
on_delete=models.SET_NULL,
verbose_name=_("Tournoi")
verbose_name=_("Tournoi"),
)
name = models.CharField(
max_length=42,
Expand All @@ -216,6 +213,7 @@ class Team(models.Model):

class Meta:
"""Meta Options"""

verbose_name = _("Équipe")
verbose_name_plural = _("Équipes")
constraints = [
Expand All @@ -226,7 +224,7 @@ class Meta:

def __str__(self) -> str:
"""Format this team to a str"""
if self.tournament is not None:
if self.tournament is not None:
return f"{self.name} ({self.tournament.event})"
return f"{self.name} (???)"

Expand Down Expand Up @@ -275,6 +273,23 @@ class PaymentStatus(models.TextChoices):
PAY_LATER = "LATER", _("Payera sur place")


def player_manager_user_unique_validator(user: User):
"""
Validate that a user cannot be a player and manager of the same
tournament
"""
p_regs = {
(obj.user, obj.team.tournament) for obj in Player.objects.filter(user=user)
}
m_regs = {
(obj.user, obj.team.tournament) for obj in Manager.objects.filter(user=user)
}
if len(m_regs.intersection(p_regs)) > 0:
raise ValidationError(
_("Utilisateur⋅rice déjà inscrit⋅e dans ce tournois (rôles distincts)")
)


class Player(models.Model):
"""
A Player at InsaLan is simply anyone who is registered to participate in a
Expand All @@ -288,22 +303,23 @@ class Meta:
verbose_name_plural = _("Inscription de joueurâ‹…euses")

user = models.ForeignKey(
User,
on_delete=models.CASCADE,
verbose_name=_("Utilisateurâ‹…ice")
User,
on_delete=models.CASCADE,
verbose_name=_("Utilisateurâ‹…ice"),
validators=[player_manager_user_unique_validator],
)
team = models.ForeignKey(
"tournament.Team",
on_delete=models.CASCADE,
verbose_name = _("Équipe"),
verbose_name=_("Équipe"),
)
payment_status = models.CharField(
max_length=10,
blank=True,
default=PaymentStatus.NOT_PAID,
choices=PaymentStatus.choices,
null=False,
verbose_name = _("Statut du paiement"),
verbose_name=_("Statut du paiement"),
)

def __str__(self) -> str:
Expand Down Expand Up @@ -350,13 +366,15 @@ class Manager(models.Model):
A Manager is someone in charge of heading a team of players.
"""

user = models.ForeignKey(User,
verbose_name=_("Utilisateurâ‹…ice"),
on_delete=models.CASCADE
user = models.ForeignKey(
User,
verbose_name=_("Utilisateurâ‹…ice"),
on_delete=models.CASCADE,
validators=[player_manager_user_unique_validator],
)
team = models.ForeignKey(
"tournament.Team", verbose_name=_("Équipe"), on_delete=models.CASCADE
)
team = models.ForeignKey("tournament.Team",
verbose_name=_("Équipe"),
on_delete=models.CASCADE)
payment_status = models.CharField(
verbose_name=_("Statut du paiement"),
max_length=10,
Expand All @@ -379,9 +397,7 @@ class Meta:

def __str__(self) -> str:
"""Format this manager registration as a str"""
return (
f"(Manager) {self.user.username} for {self.team}"
)
return f"(Manager) {self.user.username} for {self.team}"

def as_user(self) -> User:
"""Return the current player as a User object"""
Expand All @@ -391,4 +407,5 @@ def get_team(self):
"""Return the Team object of the current team"""
return self.team


# vim: set cc=80 tw=80:
65 changes: 65 additions & 0 deletions insalan/tournament/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,71 @@ def test_tournament_null_game(self):
IntegrityError, Tournament.objects.create, event=event, game=None
)

def test_manager_player_duplication(self):
"""Verify that a user cannot be a manager and a player on the same tournament"""
event = Event.objects.create(name="Test", year=2023, month=2, description="")
game_obj = Game.objects.create(name="Game 1", short_name="G1")
tourney_one = Tournament.objects.create(
name="Tourney 1", game=game_obj, event=event
)
tourney_two = Tournament.objects.create(
name="Tourney 2", game=game_obj, event=event
)

# This should work (Player in tourney 1, Manager in Tourney 2)
user_one = User.objects.create_user(
username="user_test_one", email="[email protected]"
)

Player.objects.create(
user=user_one,
team=Team.objects.create(name="Team One", tournament=tourney_one),
)
Manager.objects.create(
user=user_one,
team=Team.objects.create(name="Team Two", tournament=tourney_two),
)

# This should not work (Player and Manager in tourney 1 in different teams)
user_two = User.objects.create_user(
username="user_test_two", email="[email protected]"
)
team_three = Team.objects.create(name="Team Three", tournament=tourney_one)
team_four = Team.objects.create(name="Team Four", tournament=tourney_one)

Player.objects.create(user=user_two, team=team_three)
man_obj = Manager.objects.create(user=user_two, team=team_four)
self.assertRaises(ValidationError, man_obj.full_clean)

user_three = User.objects.create_user(
username="user_test_three", email="[email protected]"
)

Manager.objects.create(user=user_three, team=team_three)
play_obj = Player.objects.create(user=user_three, team=team_four)
self.assertRaises(ValidationError, play_obj.full_clean)

# This should not work (Player and Manager in tourney 1 in the same team)
team_five = Team.objects.create(name="Team Five", tournament=tourney_one)

user_four = User.objects.create_user(
username="user_test_four", email="[email protected]"
)
Player.objects.create(user=user_four, team=team_five)
self.assertRaises(
ValidationError,
Manager.objects.create(user=user_four, team=team_five).full_clean,
)

user_five = User.objects.create_user(
username="user_test_five", email="[email protected]"
)
Manager.objects.create(user=user_five, team=team_five)
self.assertRaises(
ValidationError,
Player.objects.create(user=user_five, team=team_five).full_clean,
)

def test_get_event(self):
"""Get the event for a tournament"""
event = Event.objects.get(year=2023, month=3)
Expand Down

0 comments on commit ac60697

Please sign in to comment.