Adds related_name for inverse relationship + ViewSet now use the parameter store_id to filter by store_id/tournament

sync
Laurent 1 year ago
parent 0829a1e246
commit 2d2e17eb70
  1. 47
      api/views.py
  2. 46
      tournaments/models/base.py
  3. 6
      tournaments/models/club.py
  4. 2
      tournaments/models/court.py
  5. 8
      tournaments/models/custom_user.py
  6. 2
      tournaments/models/date_interval.py
  7. 10
      tournaments/models/event.py
  8. 2
      tournaments/models/failed_api_call.py
  9. 15
      tournaments/models/group_stage.py
  10. 2
      tournaments/models/log.py
  11. 9
      tournaments/models/match.py
  12. 2
      tournaments/models/model_log.py
  13. 2
      tournaments/models/player_registration.py
  14. 2
      tournaments/models/purchase.py
  15. 11
      tournaments/models/round.py
  16. 17
      tournaments/models/team_registration.py
  17. 2
      tournaments/models/team_score.py
  18. 70
      tournaments/models/tournament.py
  19. 472
      tournaments/models/views.py
  20. 4
      tournaments/views.py

@ -1,5 +1,5 @@
from .serializers import ClubSerializer, CourtSerializer, DateIntervalSerializer, TournamentSerializer, UserSerializer, ChangePasswordSerializer, EventSerializer, RoundSerializer, GroupStageSerializer, MatchSerializer, TeamScoreSerializer, TeamRegistrationSerializer, PlayerRegistrationSerializer, LiveMatchSerializer, PurchaseSerializer, CustomUserSerializer, FailedApiCallSerializer, LogSerializer, DeviceTokenSerializer, DataAccessSerializer
from tournaments.models import Club, Tournament, CustomUser, Event, Round, GroupStage, Match, TeamScore, TeamRegistration, PlayerRegistration, Court, DateInterval, Purchase, FailedApiCall, Log, DeviceToken, ModelLog, DataAccess
from tournaments.models import Club, Tournament, CustomUser, Event, Round, GroupStage, Match, TeamScore, TeamRegistration, PlayerRegistration, Court, DateInterval, Purchase, FailedApiCall, Log, DeviceToken, ModelLog, DataAccess, BaseModel
from rest_framework import viewsets, permissions
from rest_framework.authtoken.models import Token
@ -131,7 +131,8 @@ class DataApi(APIView):
except ValueError:
return Response({"error": f"Invalid date format for last_update: {last_update}"}, status=status.HTTP_400_BAD_REQUEST)
data_access = DataAccess.objects.filter(shared_with=request.user).values_list('model_id', flat=True)
data_access_query = Q(shared_with=request.user) | Q(owner=request.user)
data_access = DataAccess.objects.filter(data_access_query).values_list('model_id', flat=True)
log_query = Q(date__gt=last_update) & (Q(user=request.user) | Q(model_id__in=data_access) | Q(parent_model_id__in=data_access))
logs = ModelLog.objects.filter(log_query).order_by('date')
@ -157,6 +158,7 @@ class DataApi(APIView):
updates[log.model_name][log.model_id] = data
instance = model.objects.get(id=log.model_id)
self.add_children_recursively(instance, updates)
self.add_parents_recursively(instance, updates)
elif log.operation == 'REVOKE_ACCESS':
print(f'revoke access {log.model_id} - {log.store_id}')
# data = self.get_data(model, log)
@ -184,19 +186,26 @@ class DataApi(APIView):
"""
Recursively add all children of an instance to the updates dictionary.
"""
child_models = instance.get_child_models()
for child_model_name, related_name in child_models.items():
child_model = apps.get_model(app_label='tournaments', model_name=child_model_name)
children = getattr(instance, related_name).all()
child_models = instance.get_children_by_model()
for child_model_name, children in child_models.items():
for child in children:
serializer_class = build_serializer_class(child_model_name)
serializer = serializer_class(child)
updates[child_model_name][child.id] = serializer.data
# Recursive call for each child
self.add_children_recursively(child, updates)
if isinstance(child, BaseModel):
serializer_class = build_serializer_class(child_model_name)
serializer = serializer_class(child)
updates[child_model_name][child.id] = serializer.data
self.add_children_recursively(child, updates)
def add_parents_recursively(self, instance, updates):
parent_models = instance.get_parents_by_model()
for parent_model_name, parent in parent_models.items():
# print(f'parent = {parent_model_name}')
if isinstance(parent, BaseModel):
serializer_class = build_serializer_class(parent_model_name)
serializer = serializer_class(parent)
updates[parent_model_name][parent.id] = serializer.data
self.add_parents_recursively(parent, updates)
def get_data(self, model, log):
instance = model.objects.get(id=log.model_id)
@ -357,7 +366,7 @@ class RoundViewSet(viewsets.ModelViewSet):
serializer_class = RoundSerializer
def get_queryset(self):
tournament_id = self.request.query_params.get('tournament')
tournament_id = self.request.query_params.get('store_id')
if tournament_id:
return self.queryset.filter(tournament=tournament_id)
if self.request.user:
@ -369,7 +378,7 @@ class GroupStageViewSet(viewsets.ModelViewSet):
serializer_class = GroupStageSerializer
def get_queryset(self):
tournament_id = self.request.query_params.get('tournament')
tournament_id = self.request.query_params.get('store_id')
if tournament_id:
return self.queryset.filter(tournament=tournament_id)
if self.request.user:
@ -381,7 +390,7 @@ class MatchViewSet(viewsets.ModelViewSet):
serializer_class = MatchSerializer
def get_queryset(self):
tournament_id = self.request.query_params.get('tournament')
tournament_id = self.request.query_params.get('store_id')
if tournament_id:
return self.queryset.filter(Q(group_stage__tournament=tournament_id) | Q(round__tournament=tournament_id))
if self.request.user:
@ -393,7 +402,7 @@ class TeamScoreViewSet(viewsets.ModelViewSet):
serializer_class = TeamScoreSerializer
def get_queryset(self):
tournament_id = self.request.query_params.get('tournament')
tournament_id = self.request.query_params.get('store_id')
if tournament_id:
return self.queryset.filter(team_registration__tournament=tournament_id)
if self.request.user:
@ -405,7 +414,7 @@ class TeamRegistrationViewSet(viewsets.ModelViewSet):
serializer_class = TeamRegistrationSerializer
def get_queryset(self):
tournament_id = self.request.query_params.get('tournament')
tournament_id = self.request.query_params.get('store_id')
if tournament_id:
return self.queryset.filter(tournament=tournament_id)
if self.request.user:
@ -417,7 +426,7 @@ class PlayerRegistrationViewSet(viewsets.ModelViewSet):
serializer_class = PlayerRegistrationSerializer
def get_queryset(self):
tournament_id = self.request.query_params.get('tournament')
tournament_id = self.request.query_params.get('store_id')
if tournament_id:
return self.queryset.filter(team_registration__tournament=tournament_id)
if self.request.user:

@ -12,8 +12,50 @@ class BaseModel(models.Model):
"""Override in child models to provide parent reference"""
return None, None
def get_child_models(self):
return {}
def get_children_by_model(self):
"""
Returns a dictionary where:
- keys are the model names
- values are QuerySets of related objects
"""
children = self._meta.get_fields(include_parents=False, include_hidden=False)
related_objects = {}
for child in children:
if (child.one_to_many or child.one_to_one) and child.auto_created:
model_name = child.related_model.__name__
related_objects[model_name] = getattr(self, child.name).all()
return related_objects
def get_parents_by_model(self):
"""
Returns a dictionary where:
- keys are the model names
- values are the parent model instances
"""
parents = {}
# Get all fields including parent links
fields = self._meta.get_fields(include_parents=True, include_hidden=True)
for field in fields:
# Check if the field is a parent link (OneToOne field that points to a parent model)
if isinstance(field, models.OneToOneRel) and field.parent_link:
model_name = field.related_model.__name__
# Get the parent instance using the related name
parent_instance = getattr(self, field.get_accessor_name())
if parent_instance:
parents[model_name] = parent_instance
# Also check for direct foreign key relationships that might represent parent relationships
elif isinstance(field, models.ForeignKey):
model_name = field.related_model.__name__
parent_instance = getattr(self, field.name)
if parent_instance:
parents[model_name] = parent_instance
return parents
class SideStoreModel(BaseModel):
store_id = models.CharField(max_length=100)

@ -4,7 +4,7 @@ from . import BaseModel
class Club(BaseModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True)
creator = models.ForeignKey('CustomUser', blank=True, null=True, on_delete=models.SET_NULL) # string to avoid circular import
creator = models.ForeignKey('CustomUser', blank=True, null=True, on_delete=models.SET_NULL)
name = models.CharField(max_length=50)
acronym = models.CharField(max_length=10)
phone = models.CharField(max_length=15, null=True, blank=True)
@ -24,10 +24,10 @@ class Club(BaseModel):
return self.name
def events_count(self):
return len(self.event_set.all())
return len(self.events.all())
def court_name(self, index):
for court in self.court_set.all():
for court in self.courts.all():
if court.index == index and court.name is not None and len(court.name) > 0:
return court.name

@ -5,7 +5,7 @@ from . import BaseModel, Club
class Court(BaseModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True)
index = models.IntegerField(default=0)
club = models.ForeignKey(Club, on_delete=models.CASCADE)
club = models.ForeignKey(Club, on_delete=models.CASCADE, related_name='courts')
name = models.CharField(max_length=50, null=True, blank=True)
exit_allowed = models.BooleanField(default=False)
indoor = models.BooleanField(default=False)

@ -11,7 +11,7 @@ class CustomUser(AbstractUser):
last_update = models.DateTimeField(default=now)
email = models.EmailField(unique=True)
umpire_code = models.CharField(max_length=50, blank=True, null=True)
clubs = models.ManyToManyField(club.Club, blank=True)
clubs = models.ManyToManyField(club.Club, blank=True, related_name='creators')
phone = models.CharField(max_length=15, null=True, blank=True)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
@ -47,6 +47,10 @@ class CustomUser(AbstractUser):
'summons_use_full_custom_message', 'match_formats_default_duration', 'bracket_match_format_preference',
'group_stage_match_format_preference', 'loser_bracket_match_format_preference', 'device_id']
def get_parent_reference(self):
"""Override in child models to provide parent reference"""
return None, None
def __str__(self):
return self.username
@ -54,7 +58,7 @@ class CustomUser(AbstractUser):
return f"{self.username} : {self.first_name} {self.last_name} | {self.email} | {self.phone}"
def event_count(self):
return len(self.event_set.all())
return len(self.events.all())
def full_name(self):
return f"{self.first_name} {self.last_name}"

@ -4,7 +4,7 @@ from . import BaseModel
class DateInterval(BaseModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True)
event = models.ForeignKey('Event', on_delete=models.CASCADE)
event = models.ForeignKey('Event', on_delete=models.CASCADE, related_name='date_intervals')
court_index = models.IntegerField()
start_date = models.DateTimeField()
end_date = models.DateTimeField()

@ -4,20 +4,14 @@ import uuid
class Event(BaseModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True)
club = models.ForeignKey(Club, on_delete=models.SET_NULL, null=True, blank=True)
club = models.ForeignKey(Club, on_delete=models.SET_NULL, null=True, blank=True, related_name='events')
creation_date = models.DateTimeField()
creator = models.ForeignKey(CustomUser, blank=True, null=True, on_delete=models.SET_NULL)
creator = models.ForeignKey(CustomUser, blank=True, null=True, on_delete=models.SET_NULL, related_name='events')
name = models.CharField(max_length=200, null=True, blank=True)
federal_tournament_data = models.JSONField(null=True, blank=True)
tenup_id = models.CharField(max_length=20, null=True, blank=True)
creator_full_name = models.CharField(max_length=200, null=True, blank=True)
# Data Access
def get_child_models(self):
return {
'Tournament': 'tournament_set',
}
def __str__(self):
return self.display_name()

@ -5,7 +5,7 @@ import uuid
class FailedApiCall(BaseModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True)
date = models.DateTimeField()
user = models.ForeignKey(CustomUser, blank=True, null=True, on_delete=models.SET_NULL)
user = models.ForeignKey(CustomUser, blank=True, null=True, on_delete=models.SET_NULL, related_name='failed_api_calls')
type = models.CharField(max_length=50)
call_id = models.UUIDField()
error = models.TextField()

@ -8,7 +8,7 @@ from django.utils import timezone
class GroupStage(SideStoreModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True)
tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE)
tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE, related_name='group_stages')
index = models.IntegerField(default=0)
size = models.IntegerField(default=4)
format = models.IntegerField(default=FederalMatchCategory.NINE_GAMES, choices=FederalMatchCategory.choices, null=True, blank=True)
@ -19,11 +19,6 @@ class GroupStage(SideStoreModel):
def get_parent_reference(self):
return 'Event', self.tournament.event.id
def get_child_models(self):
return {
'Match': 'match_set',
}
def __str__(self):
return self.display_name()
# return f"{self.tournament.display_name()} - {self.display_name()}"
@ -50,7 +45,7 @@ class GroupStage(SideStoreModel):
gs_teams = {}
# init all teams
for team_registration in self.teamregistration_set.all():
for team_registration in self.team_registrations.all():
if team_registration in gs_teams:
team = gs_teams[team_registration]
else:
@ -58,7 +53,7 @@ class GroupStage(SideStoreModel):
gs_teams[team_registration] = team
# compute matches
for match in self.match_set.all():
for match in self.matches.all():
lgs.add_match(match)
team_scores = match.team_scores.all()
if len(team_scores) == 2:
@ -99,7 +94,7 @@ class GroupStage(SideStoreModel):
team1.diff += total1 - total2
team2.diff += total2 - total1
if len(self.match_set.filter(end_date__isnull=False).all()) > 0:
if len(self.matches.filter(end_date__isnull=False).all()) > 0:
teams = sorted(gs_teams.values(), key=lambda team: -(team.wins * 100 + team.diff))
else:
teams = sorted(gs_teams.values(), key=lambda team: team.position)
@ -116,7 +111,7 @@ class GroupStage(SideStoreModel):
return False
def has_at_least_one_started_match(self):
for match in self.match_set.all():
for match in self.matches.all():
if match.start_date:
return True
return False

@ -5,7 +5,7 @@ import uuid
class Log(BaseModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True)
date = models.DateTimeField()
user = models.ForeignKey(CustomUser, blank=True, null=True, on_delete=models.SET_NULL)
user = models.ForeignKey(CustomUser, blank=True, null=True, on_delete=models.SET_NULL, related_name='logs')
message = models.TextField(blank=True, null=True)
def __str__(self):

@ -9,8 +9,8 @@ from ..utils.extensions import format_seconds
class Match(SideStoreModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True)
round = models.ForeignKey(Round, null=True, blank=True, on_delete=models.CASCADE)
group_stage = models.ForeignKey(GroupStage, null=True, blank=True, on_delete=models.CASCADE)
round = models.ForeignKey(Round, null=True, blank=True, on_delete=models.CASCADE, related_name='matches')
group_stage = models.ForeignKey(GroupStage, null=True, blank=True, on_delete=models.CASCADE, related_name='matches')
name = models.CharField(max_length=200, null=True, blank=True)
start_date = models.DateTimeField(null=True, blank=True)
end_date = models.DateTimeField(null=True, blank=True)
@ -30,11 +30,6 @@ class Match(SideStoreModel):
def get_parent_reference(self):
return 'Event', self.tournament().event.id
def get_child_models(self):
return {
'TeamScore': 'team_scores',
}
def __str__(self):
names = " / ".join(self.player_names())
return f"{self.stage_name()} #{self.index}: {names}"

@ -4,7 +4,7 @@ from . import ModelOperation
class ModelLog(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True)
user = models.ForeignKey('CustomUser', blank=True, null=True, on_delete=models.SET_NULL)
user = models.ForeignKey('CustomUser', blank=True, null=True, on_delete=models.SET_NULL, related_name='model_logs')
model_id = models.UUIDField()
operation = models.CharField(choices=ModelOperation.choices, max_length=50)
date = models.DateTimeField()

@ -4,7 +4,7 @@ import uuid
class PlayerRegistration(SideStoreModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True)
team_registration = models.ForeignKey(TeamRegistration, on_delete=models.CASCADE)
team_registration = models.ForeignKey(TeamRegistration, on_delete=models.CASCADE, related_name='player_registrations')
first_name = models.CharField(max_length=50, blank=True)
last_name = models.CharField(max_length=50, blank=True)
licence_id = models.CharField(max_length=20, null=True, blank=True)

@ -4,7 +4,7 @@ from . import BaseModel, CustomUser
class Purchase(BaseModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True)
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='purchases')
identifier = models.BigIntegerField()
purchase_date = models.DateTimeField()
product_id = models.CharField(max_length=100)

@ -4,7 +4,7 @@ import uuid
class Round(SideStoreModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True)
tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE)
tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE, related_name='rounds')
index = models.IntegerField(default=0)
parent = models.ForeignKey('self', blank=True, null=True, on_delete=models.CASCADE, related_name='children')
format = models.IntegerField(default=FederalMatchCategory.NINE_GAMES, choices=FederalMatchCategory.choices, null=True, blank=True)
@ -14,11 +14,6 @@ class Round(SideStoreModel):
def get_parent_reference(self):
return 'Event', self.tournament.event.id
def get_child_models(self):
return {
'Match': 'match_set',
}
def __str__(self):
if self.parent:
return f"LB: {self.name()}"
@ -50,7 +45,7 @@ class Round(SideStoreModel):
matches = []
for child in self.children.all():
child_matches = child.match_set.all()
child_matches = child.matches.all()
if hide_empty_matches:
child_matches = [m for m in child_matches if m.should_appear()]
else:
@ -66,7 +61,7 @@ class Round(SideStoreModel):
return matches
def get_matches_recursive(self, hide_empty_matches):
matches = list(self.match_set.all()) # Retrieve matches associated with the current round
matches = list(self.matches.all()) # Retrieve matches associated with the current round
matches = [m for m in matches if m.should_appear()]
if hide_empty_matches:
matches = [m for m in matches if m.should_appear()]

@ -6,8 +6,8 @@ from django.utils import timezone
class TeamRegistration(SideStoreModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True)
tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE)
group_stage = models.ForeignKey(GroupStage, null=True, blank=True, on_delete=models.SET_NULL)
tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE, related_name='team_registrations')
group_stage = models.ForeignKey(GroupStage, null=True, blank=True, on_delete=models.SET_NULL, related_name='team_registrations')
registration_date = models.DateTimeField(null=True, blank=True)
call_date = models.DateTimeField(null=True, blank=True)
bracket_position = models.IntegerField(null=True, blank=True)
@ -34,11 +34,6 @@ class TeamRegistration(SideStoreModel):
def get_parent_reference(self):
return 'Event', self.tournament.event.id
def get_child_models(self):
return {
'PlayerRegistration': 'playerregistration_set',
}
def __str__(self):
if self.name:
return self.name
@ -52,16 +47,16 @@ class TeamRegistration(SideStoreModel):
if self.name:
return [self.name]
else:
return [pr.name() for pr in self.playerregistration_set.all()]
return [pr.name() for pr in self.player_registrations.all()]
def shortened_team_names(self):
if self.name:
return [self.name]
else:
return [pr.shortened_name() for pr in self.playerregistration_set.all()]
return [pr.shortened_name() for pr in self.player_registrations.all()]
def player_names(self):
names = [pr.name() for pr in self.playerregistration_set.all()]
names = [pr.name() for pr in self.player_registrations.all()]
str = " - ".join(names)
if len(str) > 0:
return str
@ -94,7 +89,7 @@ class TeamRegistration(SideStoreModel):
return "--"
def is_valid_for_summon(self):
return len(self.playerregistration_set.all()) > 0
return len(self.player_registrations.all()) > 0
def initial_weight(self):
if self.locked_weight is None:

@ -5,7 +5,7 @@ import uuid
class TeamScore(SideStoreModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True)
match = models.ForeignKey(Match, on_delete=models.CASCADE, related_name="team_scores")
team_registration = models.ForeignKey(TeamRegistration, on_delete=models.CASCADE, null=True, blank=True)
team_registration = models.ForeignKey(TeamRegistration, on_delete=models.CASCADE, null=True, blank=True, related_name="team_scores")
score = models.CharField(max_length=50, null=True, blank=True)
walk_out = models.IntegerField(null=True, blank=True) # TODO type of WO: forfeit, injury...
lucky_loser = models.IntegerField(null=True, blank=True)

@ -17,7 +17,7 @@ class TeamSortingType(models.IntegerChoices):
class Tournament(BaseModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True)
event = models.ForeignKey(Event, blank=True, null=True, on_delete=models.CASCADE)
event = models.ForeignKey(Event, blank=True, null=True, on_delete=models.CASCADE, related_name='tournaments')
name = models.CharField(max_length=200, null=True, blank=True)
start_date = models.DateTimeField()
end_date = models.DateTimeField(null=True, blank=True)
@ -63,16 +63,6 @@ class Tournament(BaseModel):
def get_parent_reference(self):
return 'Event', self.event.id
def get_child_models(self):
"""
Returns a dictionary of child model names and their related_names.
"""
return {
'Round': 'round_set',
'GroupStage': 'groupstage_set',
'TeamRegistration': 'teamregistration_set'
}
def __str__(self):
if self.name:
return self.name
@ -206,7 +196,7 @@ class Tournament(BaseModel):
def team_summons(self):
summons = []
for team_registration in self.teamregistration_set.all():
for team_registration in self.team_registrations.all():
if team_registration.is_valid_for_summon():
next_match = team_registration.next_match()
if next_match and next_match.start_date is not None:
@ -221,7 +211,7 @@ class Tournament(BaseModel):
def rankings(self):
rankings = []
for team_registration in self.teamregistration_set.all():
for team_registration in self.team_registrations.all():
if team_registration.walk_out is False and team_registration.final_ranking is not None:
names = team_registration.team_names()
ranking = team_registration.final_ranking
@ -242,7 +232,7 @@ class Tournament(BaseModel):
complete_teams = []
closed_registration_date = self.closed_registration_date
for team_registration in self.teamregistration_set.all():
for team_registration in self.team_registrations.all():
is_valid = False
if closed_registration_date is not None and team_registration.registration_date is not None and team_registration.registration_date <= closed_registration_date:
is_valid = True
@ -339,10 +329,10 @@ class Tournament(BaseModel):
match_groups = []
if group_stage_id:
group_stage = self.groupstage_set.filter(id=group_stage_id).first()
group_stage = self.group_stages.filter(id=group_stage_id).first()
match_groups.append(self.group_stage_match_group(group_stage, broadcasted, hide_empty_matches=False))
elif round_id:
round = self.round_set.filter(id=round_id).first()
round = self.rounds.filter(id=round_id).first()
if round:
match_groups = self.round_match_groups(round, broadcasted, hide_empty_matches=False)
else:
@ -352,11 +342,11 @@ class Tournament(BaseModel):
def all_groups(self, broadcasted):
groups = []
for round in self.round_set.filter(parent=None).all().order_by('index'):
for round in self.rounds.filter(parent=None).all().order_by('index'):
groups.extend(self.round_match_groups(round, broadcasted, hide_empty_matches=True))
if self.display_group_stages():
for group_stage in self.groupstage_set.all().order_by('index'):
for group_stage in self.group_stages.all().order_by('index'):
group = self.group_stage_match_group(group_stage, broadcasted, hide_empty_matches=True)
if group:
groups.append(group)
@ -364,7 +354,7 @@ class Tournament(BaseModel):
return groups
def group_stage_match_group(self, group_stage, broadcasted, hide_empty_matches):
matches = group_stage.match_set.all()
matches = group_stage.matches.all()
if hide_empty_matches:
matches = [m for m in matches if m.should_appear()]
else:
@ -378,7 +368,7 @@ class Tournament(BaseModel):
def round_match_groups(self, round, broadcasted, hide_empty_matches):
groups = []
matches = round.match_set.order_by('index').all()
matches = round.matches.order_by('index').all()
if hide_empty_matches:
matches = [m for m in matches if m.should_appear()]
else:
@ -407,7 +397,7 @@ class Tournament(BaseModel):
return MatchGroup(name, live_matches)
def live_group_stages(self):
group_stages = list(self.groupstage_set.all())
group_stages = list(self.group_stages.all())
group_stages.sort(key=lambda gs: gs.index)
return [gs.live_group_stages() for gs in group_stages]
@ -445,7 +435,7 @@ class Tournament(BaseModel):
matches = []
group_stages = []
if len(self.groupstage_set.all()) > 0 and self.no_bracket_match_has_started():
if len(self.group_stages.all()) > 0 and self.no_bracket_match_has_started():
group_stages = self.live_group_stages()
matches = self.broadcasted_group_stages_matches()
first_round = self.first_round()
@ -473,18 +463,18 @@ class Tournament(BaseModel):
def no_bracket_match_has_started(self):
matches = []
for round in self.round_set.all():
for match in round.match_set.all():
for round in self.rounds.all():
for match in round.matches.all():
if match.started():
return False
return True
def all_matches(self, hide_empty_matches):
matches = []
for round in self.round_set.all():
for round in self.rounds.all():
matches.extend(round.all_matches(hide_empty_matches))
for group_stage in self.groupstage_set.all():
matches.extend(group_stage.match_set.all())
for group_stage in self.group_stages.all():
matches.extend(group_stage.matches.all())
matches = [m for m in matches if m.should_appear()]
@ -492,12 +482,12 @@ class Tournament(BaseModel):
def group_stage_matches(self):
matches = []
for group_stage in self.groupstage_set.all():
matches.extend(group_stage.match_set.all())
for group_stage in self.group_stages.all():
matches.extend(group_stage.matches.all())
return matches
def group_stages_running(self):
if len(self.groupstage_set.all()) > 0:
if len(self.group_stages.all()) > 0:
# check le debut des match de Round
matches = self.group_stage_matches()
running_group_stage_matches = [m for m in matches if m.end_date is None]
@ -519,7 +509,7 @@ class Tournament(BaseModel):
current_round = last_started_match.round.root_round()
if current_round:
return current_round
main_rounds = list(self.round_set.filter(parent=None).all())
main_rounds = list(self.rounds.filter(parent=None).all())
main_rounds.sort(key=lambda r: r.index)
if main_rounds:
return main_rounds[0]
@ -532,10 +522,10 @@ class Tournament(BaseModel):
return matches[0]
def round_for_index(self, index):
return self.round_set.filter(index=index, parent=None).first()
return self.rounds.filter(index=index, parent=None).first()
def first_round(self):
main_rounds = list(self.round_set.filter(parent=None))
main_rounds = list(self.rounds.filter(parent=None))
main_rounds.sort(key=lambda r: r.index, reverse=True)
return main_rounds[0]
@ -544,13 +534,13 @@ class Tournament(BaseModel):
group_stages = self.elected_broadcast_group_stages()
group_stages.sort(key=lambda gs: (gs.index, gs.start_date is None, gs.start_date))
for group_stage in group_stages:
matches.extend(group_stage.match_set.all())
matches.extend(group_stage.matches.all())
matches = [m for m in matches if m.should_appear()]
matches.sort(key=lambda m: (m.start_date is None, m.end_date is not None, m.start_date, m.index))
return matches
def elected_broadcast_group_stages(self):
group_stages = list(self.groupstage_set.all())
group_stages = list(self.group_stages.all())
started = [gs for gs in group_stages if gs.starts_soon()]
if len(started) > 0:
return started
@ -598,7 +588,7 @@ class Tournament(BaseModel):
def display_group_stages(self):
if self.end_date is not None:
return True
if len(self.groupstage_set.all()) == 0:
if len(self.group_stages.all()) == 0:
return False
if self.publish_group_stages:
return True
@ -610,7 +600,7 @@ class Tournament(BaseModel):
return timezone.now() >= first_group_stage_start_date
def group_stage_start_date(self):
group_stages = [gs for gs in self.groupstage_set.all() if gs.start_date is not None]
group_stages = [gs for gs in self.group_stages.all() if gs.start_date is not None]
if len(group_stages) == 0:
return None
@ -640,7 +630,7 @@ class Tournament(BaseModel):
def bracket_matches(self):
matches = []
for round in self.round_set.all():
for round in self.rounds.all():
matches.extend(round.all_matches(False))
return matches
@ -665,7 +655,7 @@ class Tournament(BaseModel):
return self.federal_level_category == FederalLevelCategory.UNLISTED
def is_build_and_not_empty(self):
return (len(self.groupstage_set.all()) > 0 or len(self.round_set.all()) > 0) and len(self.teamregistration_set.all()) >= 4
return (len(self.group_stages.all()) > 0 or len(self.rounds.all()) > 0) and len(self.team_registrations.all()) >= 4
def day_duration_formatted(self):
return plural_format("jour", self.day_duration)
@ -677,7 +667,7 @@ class Tournament(BaseModel):
return False
def has_all_group_stages_started(self):
for group_stage in self.groupstage_set.all():
for group_stage in self.group_stages.all():
if group_stage.has_at_least_one_started_match() == False:
return False
return True

@ -1,472 +0,0 @@
from .serializers import ClubSerializer, CourtSerializer, DateIntervalSerializer, TournamentSerializer, UserSerializer, ChangePasswordSerializer, EventSerializer, RoundSerializer, GroupStageSerializer, MatchSerializer, TeamScoreSerializer, TeamRegistrationSerializer, PlayerRegistrationSerializer, LiveMatchSerializer, PurchaseSerializer, CustomUserSerializer, FailedApiCallSerializer, LogSerializer, DeviceTokenSerializer, DataAccessSerializer
from tournaments.models import Club, Tournament, CustomUser, Event, Round, GroupStage, Match, TeamScore, TeamRegistration, PlayerRegistration, Court, DateInterval, Purchase, FailedApiCall, Log, DeviceToken, ModelLog, DataAccess
from rest_framework import viewsets, permissions
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework import status
from rest_framework.generics import UpdateAPIView
from rest_framework.exceptions import MethodNotAllowed
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
from django.contrib.auth import authenticate
from django.db.models import Q
from django.core.exceptions import ObjectDoesNotExist
from django.utils import timezone
from django.apps import apps
from collections import defaultdict
from .permissions import IsClubOwner
from .utils import is_valid_email, build_serializer_class
class DataApi(APIView):
permission_classes = [IsAuthenticated]
def post(self, request, *args, **kwargs):
# unfold content
model_operation = request.data.get('operation')
model_name = request.data.get('model_name')
data = request.data.get('data')
store_id = request.data.get('store_id')
print(f"DataApi post > {model_operation} {model_name}")
serializer_class = build_serializer_class(model_name)
model = apps.get_model(app_label='tournaments', model_name=model_name)
try:
data_id = data.get('id')
instance = model.objects.get(id=data_id)
if model_operation == 'DELETE':
parent_model, parent_id = instance.get_parent_reference()
return self.delete_and_save_log(request, data_id, model_operation, model_name, store_id, parent_id, parent_model)
else: # PUT
serializer = serializer_class(instance, data=data, context={'request': request})
if serializer.is_valid():
if instance.last_update <= serializer.validated_data.get('last_update'):
print('>>> update')
return self.save_and_create_log(request, serializer, model_operation, model_name, store_id)
else:
print('>>> return 203')
return Response(serializer.data, status=status.HTTP_203_NON_AUTHORITATIVE_INFORMATION)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
except model.DoesNotExist: # POST
print('>>> insert')
serializer = serializer_class(data=data, context={'request': request})
if serializer.is_valid():
return self.save_and_create_log(request, serializer, model_operation, model_name, store_id)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def save_and_create_log(self, request, serializer, model_operation, model_name, store_id):
instance = serializer.save()
parent_model, parent_id = instance.get_parent_reference()
self.create_and_save_model_log(
user=request.user,
model_operation=model_operation,
model_name=model_name,
model_id=instance.id,
store_id=store_id,
parent_id=parent_id,
parent_model=parent_model
)
return Response(serializer.data, status=status.HTTP_201_CREATED)
def delete_and_save_log(self, request, data_id, model_operation, model_name, store_id, parent_id, parent_model):
model = apps.get_model(app_label='tournaments', model_name=model_name)
print(model)
try:
instance = model.objects.get(id=data_id)
instance.delete()
except model.DoesNotExist:
return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)
# we delete all logs linked to the instance because they won't be needed anymore
ModelLog.objects.filter(model_id=data_id).delete()
self.create_and_save_model_log(
user=request.user,
model_operation=model_operation,
model_name=model_name,
model_id=data_id,
store_id=store_id,
parent_id=parent_id,
parent_model=parent_model
)
return Response(status=status.HTTP_204_NO_CONTENT)
def create_and_save_model_log(self, user, model_operation, model_name, model_id, store_id, parent_id, parent_model):
model_log = ModelLog()
model_log.user = user
model_log.operation = model_operation
model_log.date = timezone.localtime(timezone.now())
model_log.model_name = model_name
model_log.model_id = model_id
model_log.store_id = store_id
model_log.parent_model_id = parent_id
model_log.parent_model_name = parent_model
model_log.save()
def get(self, request, *args, **kwargs):
print('/data GET YEAH!')
last_update = request.query_params.get('last_update')
if not last_update:
return Response({"error": "last_update parameter is required"}, status=status.HTTP_400_BAD_REQUEST)
print(last_update)
try:
last_update = timezone.datetime.fromisoformat(last_update)
except ValueError:
return Response({"error": f"Invalid date format for last_update: {last_update}"}, status=status.HTTP_400_BAD_REQUEST)
data_access = DataAccess.objects.filter(shared_with=request.user).values_list('model_id', flat=True)
log_query = Q(date__gt=last_update) & (Q(user=request.user) | Q(model_id__in=data_access))
logs = ModelLog.objects.filter(log_query).order_by('date')
updates = defaultdict(dict)
deletions = defaultdict(list)
for log in logs:
model = apps.get_model(app_label='tournaments', model_name=log.model_name)
if log.operation in ['POST', 'PUT']:
try:
instance = model.objects.get(id=log.model_id)
serializer_class = build_serializer_class(log.model_name)
serializer = serializer_class(instance)
updates[log.model_name][log.model_id] = serializer.data
except model.DoesNotExist:
# If the instance doesn't exist, it might have been deleted after this log was created
pass
elif log.operation == 'DELETE':
deletions[log.model_name].append({'model_id': log.model_id, 'store_id': log.store_id})
# Convert updates dict to list for each model
for model_name in updates:
updates[model_name] = list(updates[model_name].values())
# Convert deletions set to list for each model
for model_name in deletions:
deletions[model_name] = deletions[model_name]
date = logs.last().date.astimezone().isoformat(timespec='seconds') if logs else None
response_data = {
"updates": dict(updates),
"deletions": dict(deletions),
"date": date
}
return Response(response_data, status=status.HTTP_200_OK)
class CustomAuthToken(APIView):
permission_classes = []
def post(self, request, *args, **kwargs):
username = request.data.get('username')
password = request.data.get('password')
device_id = request.data.get('device_id')
user = authenticate(username=username, password=password)
if user is None and is_valid_email(username) == True:
true_username = self.get_username_from_email(username)
user = authenticate(username=true_username, password=password)
if user is not None:
# if user.device_id is None or user.device_id == device_id or user.username == 'apple-test':
# if user.device_id is None or user.device_id == device_id or user.username == 'apple-test':
user.device_id = device_id
user.save()
token, created = Token.objects.get_or_create(user=user)
return Response({'token': token.key})
# else:
# return Response({'error': 'Vous ne pouvez pour l\'instant vous connecter sur plusieurs appareils en même temps. Veuillez vous déconnecter du précédent appareil. Autrement, veuillez contacter le support.'}, status=status.HTTP_403_FORBIDDEN)
else:
return Response({'error': 'L\'utilisateur et le mot de passe de correspondent pas'}, status=status.HTTP_401_UNAUTHORIZED)
def get_username_from_email(self, email):
try:
user = CustomUser.objects.get(email=email)
return user.username
except ObjectDoesNotExist:
return None # or handle the case where the user doesn't exist
class Logout(APIView):
permission_classes = (IsAuthenticated,)
def post(self, request, *args, **kwargs):
# request.user.auth_token.delete()
device_id = request.data.get('device_id')
if request.user.device_id == device_id:
request.user.device_id = None
request.user.save()
return Response(status=status.HTTP_200_OK)
@api_view(['GET'])
def user_by_token(request):
serializer = UserSerializer(request.user)
return Response(serializer.data, status=status.HTTP_200_OK)
class UserViewSet(viewsets.ModelViewSet):
queryset = CustomUser.objects.all()
serializer_class = CustomUserSerializer
permission_classes = [] # Users are public whereas the other requests are only for logged users
def get_serializer_class(self):
# Use UserSerializer for other actions (e.g., create, retrieve)
if self.action in ['create', 'retrieve']:
return UserSerializer
return self.serializer_class
class ClubViewSet(viewsets.ModelViewSet):
queryset = Club.objects.all()
serializer_class = ClubSerializer
permission_classes = [IsClubOwner] # Clubs are public whereas the other requests are only for logged users
def perform_create(self, serializer):
serializer.save(creator=self.request.user)
class TournamentViewSet(viewsets.ModelViewSet):
queryset = Tournament.objects.all()
serializer_class = TournamentSerializer
def get_queryset(self):
if self.request.user.is_anonymous:
return []
return self.queryset.filter(
Q(event__creator=self.request.user) |
Q(id__in=DataAccess.objects.filter(
shared_with=self.request.user,
model_name=self.queryset.model.__name__
).values_list('model_id', flat=True))
)
class PurchaseViewSet(viewsets.ModelViewSet):
queryset = Purchase.objects.all()
serializer_class = PurchaseSerializer
def get_queryset(self):
if self.request.user.is_anonymous:
return []
# return self.queryset.filter(user=self.request.user)
return self.queryset.filter(
Q(user=self.request.user) |
Q(id__in=DataAccess.objects.filter(
shared_with=self.request.user,
model_name=self.queryset.model.__name__
).values_list('model_id', flat=True))
)
def put(self, request, pk):
raise MethodNotAllowed('PUT')
def patch(self, request, pk):
raise MethodNotAllowed('PATCH')
def delete(self, request, pk):
raise MethodNotAllowed('DELETE')
# class ExpandedTournamentViewSet(viewsets.ModelViewSet):
# queryset = Tournament.objects.all()
# serializer_class = ExpandedTournamentSerializer
class ChangePasswordView(UpdateAPIView):
serializer_class = ChangePasswordSerializer
def update(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
# if using drf authtoken, create a new token
if hasattr(user, 'auth_token'):
user.auth_token.delete()
token, created = Token.objects.get_or_create(user=user)
# return new token
return Response({'token': token.key}, status=status.HTTP_200_OK)
class EventViewSet(viewsets.ModelViewSet):
queryset = Event.objects.all()
serializer_class = EventSerializer
def get_queryset(self):
if self.request.user.is_anonymous:
return []
# return self.queryset.filter(creator=self.request.user)
return self.queryset.filter(
Q(creator=self.request.user) |
Q(id__in=DataAccess.objects.filter(
shared_with=self.request.user,
model_name=self.queryset.model.__name__
).values_list('model_id', flat=True))
)
class RoundViewSet(viewsets.ModelViewSet):
queryset = Round.objects.all()
serializer_class = RoundSerializer
def get_queryset(self):
tournament_id = self.request.query_params.get('tournament')
if tournament_id:
return self.queryset.filter(tournament=tournament_id)
if self.request.user:
return self.queryset.filter(tournament__event__creator=self.request.user)
return []
class GroupStageViewSet(viewsets.ModelViewSet):
queryset = GroupStage.objects.all()
serializer_class = GroupStageSerializer
def get_queryset(self):
tournament_id = self.request.query_params.get('tournament')
if tournament_id:
return self.queryset.filter(tournament=tournament_id)
if self.request.user:
return self.queryset.filter(tournament__event__creator=self.request.user)
return []
class MatchViewSet(viewsets.ModelViewSet):
queryset = Match.objects.all()
serializer_class = MatchSerializer
def get_queryset(self):
tournament_id = self.request.query_params.get('tournament')
if tournament_id:
return self.queryset.filter(Q(group_stage__tournament=tournament_id) | Q(round__tournament=tournament_id))
if self.request.user:
return self.queryset.filter(Q(group_stage__tournament__event__creator=self.request.user) | Q(round__tournament__event__creator=self.request.user))
return []
class TeamScoreViewSet(viewsets.ModelViewSet):
queryset = TeamScore.objects.all()
serializer_class = TeamScoreSerializer
def get_queryset(self):
tournament_id = self.request.query_params.get('tournament')
if tournament_id:
return self.queryset.filter(team_registration__tournament=tournament_id)
if self.request.user:
return self.queryset.filter(team_registration__tournament__event__creator=self.request.user)
return []
class TeamRegistrationViewSet(viewsets.ModelViewSet):
queryset = TeamRegistration.objects.all()
serializer_class = TeamRegistrationSerializer
def get_queryset(self):
tournament_id = self.request.query_params.get('tournament')
if tournament_id:
return self.queryset.filter(tournament=tournament_id)
if self.request.user:
return self.queryset.filter(tournament__event__creator=self.request.user)
return []
class PlayerRegistrationViewSet(viewsets.ModelViewSet):
queryset = PlayerRegistration.objects.all()
serializer_class = PlayerRegistrationSerializer
def get_queryset(self):
tournament_id = self.request.query_params.get('tournament')
if tournament_id:
return self.queryset.filter(team_registration__tournament=tournament_id)
if self.request.user:
return self.queryset.filter(team_registration__tournament__event__creator=self.request.user)
return []
class CourtViewSet(viewsets.ModelViewSet):
queryset = Court.objects.all()
serializer_class = CourtSerializer
class DateIntervalViewSet(viewsets.ModelViewSet):
queryset = DateInterval.objects.all()
serializer_class = DateIntervalSerializer
def get_queryset(self):
if self.request.user.is_anonymous:
return []
return self.queryset.filter(
Q(event__creator=self.request.user) |
Q(event__id__in=DataAccess.objects.filter(
shared_with=self.request.user,
model_name=self.queryset.model.__name__
).values_list('model_id', flat=True))
)
class FailedApiCallViewSet(viewsets.ModelViewSet):
queryset = FailedApiCall.objects.all()
serializer_class = FailedApiCallSerializer
permission_classes = [] # FailedApiCall are public whereas the other requests are only for logged users
def get_queryset(self):
return []
def perform_create(self, serializer):
if self.request.user.is_anonymous == False:
serializer.save(user=self.request.user)
else:
serializer.save()
class LogViewSet(viewsets.ModelViewSet):
queryset = Log.objects.all()
serializer_class = LogSerializer
permission_classes = [] # Log are public whereas the other requests are only for logged users
def get_queryset(self):
return []
def perform_create(self, serializer):
if self.request.user.is_anonymous == False:
serializer.save(user=self.request.user)
else:
serializer.save()
class DeviceTokenViewSet(viewsets.ModelViewSet):
queryset = DeviceToken.objects.all()
serializer_class = DeviceTokenSerializer
def get_queryset(self):
if self.request.user:
return self.queryset.filter(user=self.request.user)
return []
def create(self, request, *args, **kwargs):
value = request.data.get('value')
if DeviceToken.objects.filter(value=value).exists():
return Response({"detail": "This device token is already registered."}, status=208)
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def perform_create(self, serializer):
serializer.save(user=self.request.user)
class DataAccessViewSet(viewsets.ModelViewSet):
queryset = DataAccess.objects.all()
serializer_class = DataAccessSerializer
def get_queryset(self):
if self.request.user:
return self.queryset.filter(owner=self.request.user)
return []

@ -126,8 +126,8 @@ def tournament(request, tournament_id):
group_stage_id = request.GET.get('group_stage')
match_groups = tournament.match_groups(False, group_stage_id, round_id)
rounds = tournament.round_set.filter(parent=None).order_by('-index')
group_stages = tournament.groupstage_set.order_by('index')
rounds = tournament.rounds.filter(parent=None).order_by('-index')
group_stages = tournament.group_stages.order_by('index')
print(len(match_groups))
print(len(rounds))

Loading…
Cancel
Save