- diff --git a/tournaments/models.py b/tournaments/models.py deleted file mode 100644 index 8530149..0000000 --- a/tournaments/models.py +++ /dev/null @@ -1,441 +0,0 @@ -from os import WEXITED, preadv -from django.db import models -from django.contrib.auth.models import AbstractUser -from django.conf import settings -import uuid -import os, json -from django.utils import timezone - -from django.utils.encoding import Promise - -import tournaments - -class FederalCategory(models.IntegerChoices): - MEN = 0, 'Men' - WOMEN = 1, 'Women' - MIXED = 2, 'Mixed' - -class FederalLevelCategory(models.IntegerChoices): - P25 = 25, 'P25' - P100 = 100, 'P100' - P250 = 250, 'P250' - P500 = 500, 'P500' - P1000 = 1000, 'P1000' - P1500 = 1500, 'P1500' - P2000 = 2000, 'P2000' - -class FederalAgeCategory(models.IntegerChoices): - A11_12 = 120, 'A11_12' - A13_14 = 140, 'A13_14' - A15_16 = 160, 'A15_16' - A17_18 = 180, 'A17_18' - SENIOR = 200, 'SENIOR' - A45 = 450, 'A45' - A55 = 550, 'A55' - -class FederalMatchCategory(models.IntegerChoices): - TWO_SETS = 0, 'Two sets' - TWO_SETS_SUPER_TIE = 1, 'Two sets super tie' - TWO_SETS_FOUR_GAME = 2, 'Two sets of four games' - NINE_GAMES = 3, 'Nine games' - SUPER_TIE = 4, 'Super Tie-Break' - MEGA_TIE = 5, 'Mega Tie-Break' - TWO_SETS_DECISIVE_POINT = 6, 'Two Sets with decisive point' - TWO_SETS_DECISIVE_POINT_SUPER_TIE = 7, 'Two Sets with decisive point and super tie-break' - TWO_SETS_FOUR_GAME_DECISIVE_POINT = 8, 'Two sets of four games with decisive point' - NINE_GAMES_DECISIVE_POINT = 9, 'Nine games with decisive point' - -class Club(models.Model): - id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - name = models.CharField(max_length=50) - acronym = models.CharField(max_length=10) - phone = models.CharField(max_length=15, null=True, blank=True) - code = models.CharField(max_length=10, null=True, blank=True) - federal_club_data = models.JSONField(null=True, blank=True) - - address = models.CharField(max_length=200, null=True, blank=True) - city = models.CharField(max_length=100, null=True, blank=True) - zip_code = models.CharField(max_length=10, null=True, blank=True) - latitude = models.FloatField(null=True, blank=True) - longitude = models.FloatField(null=True, blank=True) - - def __str__(self): - return self.name - -class CustomUser(AbstractUser): - pass - id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - umpire_code = models.CharField(max_length=50, blank=True, null=True) - clubs = models.ManyToManyField(Club) - phone = models.CharField(max_length=15, null=True, blank=True) - first_name = models.CharField(max_length=50) - last_name = models.CharField(max_length=50) - licence_id = models.CharField(max_length=10, null=True, blank=True) - - def __str__(self): - return self.username - -class Event(models.Model): - id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - club = models.ForeignKey(Club, on_delete=models.CASCADE) - date = models.DateTimeField() - name = models.CharField(max_length=200, null=True, blank=True) - federal_tournament_data = models.JSONField(null=True, blank=True) - court_count = models.IntegerField(null=True, blank=True) - tenup_id = models.IntegerField(null=True, blank=True) - group_stage_format = models.IntegerField(default=FederalMatchCategory.NINE_GAMES, choices=FederalMatchCategory.choices, null=True, blank=True) - round_format = models.IntegerField(default=FederalMatchCategory.NINE_GAMES, choices=FederalMatchCategory.choices, null=True, blank=True) - loser_round_format = models.IntegerField(default=FederalMatchCategory.NINE_GAMES, choices=FederalMatchCategory.choices, null=True, blank=True) - - def __str__(self): - return self.name - -class Tournament(models.Model): - id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - event = models.ForeignKey(Event, blank=True, null=True, on_delete=models.CASCADE) - creator = models.ForeignKey(CustomUser, blank=True, null=True, on_delete=models.CASCADE) - name = models.CharField(max_length=200, null=True, blank=True) - start_date = models.DateTimeField() - end_date = models.DateTimeField(null=True, blank=True) - creation_date = models.DateTimeField() - is_private = models.BooleanField() - format = models.IntegerField(default=FederalMatchCategory.NINE_GAMES, choices=FederalMatchCategory.choices, null=True, blank=True) - group_stage_format = models.IntegerField(default=FederalMatchCategory.NINE_GAMES, choices=FederalMatchCategory.choices, null=True, blank=True) - round_format = models.IntegerField(default=FederalMatchCategory.NINE_GAMES, choices=FederalMatchCategory.choices, null=True, blank=True) - loser_round_format = models.IntegerField(default=FederalMatchCategory.NINE_GAMES, choices=FederalMatchCategory.choices, null=True, blank=True) - bracket_sort_mode = models.IntegerField(default=0) - group_stage_count = models.IntegerField(default=0) - rank_source_date = models.DateTimeField(null=True, blank=True) - # custom_name = models.CharField(max_length=100, null=True, blank=True) - day_duration = models.IntegerField(default=0) - team_count = models.IntegerField(default=0) - team_sorting = models.IntegerField(default=0) - federal_category = models.IntegerField(default=FederalCategory.MEN, choices=FederalCategory.choices) # optional ? - federal_level_category = models.IntegerField(default=FederalLevelCategory.P100, choices=FederalLevelCategory.choices) - federal_age_category = models.IntegerField(default=FederalAgeCategory.SENIOR, choices=FederalAgeCategory.choices) - group_stage_court_count = models.IntegerField(null=True, blank=True) - seed_count = models.IntegerField(default=0) - closed_registration_date = models.DateTimeField(null=True, blank=True) - group_stage_additional_qualified = models.IntegerField(default=0) - court_count = models.IntegerField(null=True, blank=True) - prioritize_club_members = models.BooleanField() - qualified_per_group_stage = models.IntegerField(default=0) - teams_per_group_stage = models.IntegerField(default=0) - #estimated_end_date = models.DateTimeField(null=True, blank=True) - - def __str__(self): - return self.name - - def level(self): - return self.get_federal_level_category_display() - - def category(self): - return self.get_federal_category_display() - - def formatted_start_date(self): - return self.start_date.strftime("%d/%m/%y") - - def team_calls(self): - - team_calls = [] - for team_registration in self.teamregistration_set.all(): - call_date = team_registration.call_date - if call_date: - names = team_registration.team_names() - stage = team_registration.next_stage() - weight = team_registration.weight() - team_call = TeamCall(names, call_date, weight, stage, team_registration.logo) - team_calls.append(team_call) - - return team_calls - - def live_matches(self): - matches = [] - for group_stage in self.groupstage_set.all(): - for match in group_stage.match_set.all(): - matches.append(match) - for round in self.round_set.all(): - for match in round.match_set.all(): - matches.append(match) - - # matches = [m for m in matches if m.broadcasted==True] - - print(len(matches)) - - return map(lambda match: match.live_match(), matches) - - # # Convert object attributes to a dictionary - # dict = self.__dict__ - # # Convert dictionary to JSON - # return json.dumps(dict) - -class Round(models.Model): - id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE) - index = models.IntegerField(null=True, blank=True) - loser = models.ForeignKey('self', blank=True, null=True, on_delete=models.CASCADE) - format = models.IntegerField(default=FederalMatchCategory.NINE_GAMES, choices=FederalMatchCategory.choices, null=True, blank=True) - - def __str__(self): - return f"{self.tournament.name} - {self.name()}" - - # def stage_call(self): - # stage_call = StageCall(f"1/{self.index}") - # for match in self.match_set.all(): - # names = map(lambda ts: ts.player_names(), match.teamstate_set.all()) - # if names: - # team_call = TeamCall(names, match.formatted_start_date) - # stage_call.add_team(team_call) - - # return stage_call - - def name(self): - if self.index == 0: - return "Finale" - elif self.index == 1: - return "Demi-Finales" - elif self.index == 2: - return "Quarts de finale" - elif self.index == 3: - return "Huitième de finale" - elif self.index == 4: - return "Seizième de finale" - else: - return "" - -class GroupStage(models.Model): - id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE) - index = models.IntegerField(null=True, blank=True) - format = models.IntegerField(default=FederalMatchCategory.NINE_GAMES, choices=FederalMatchCategory.choices, null=True, blank=True) - - def __str__(self): - return f"{self.tournament.name} - {self.name()}" - - def name(self): - return f"Poule {self.index}" - - def next_match(self, player_registration): - matches = self.matches_for_registration(player_registration).filter(end_date__isnull=False).order_by('start_date') - return matches[0] - - def matches_for_registration(self, player_registration): - team_scores = TeamScore.objects.filter(player_registrations=player_registration) - return map(lambda ts: ts.match, team_scores) - -class Match(models.Model): - id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - 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) - start_date = models.DateTimeField(null=True, blank=True) - end_date = models.DateTimeField(null=True, blank=True) - index = models.IntegerField(null=True, blank=True) - format = models.IntegerField(default=FederalMatchCategory.NINE_GAMES, choices=FederalMatchCategory.choices, null=True, blank=True) - court = models.CharField(max_length=50, null=True, blank=True) - serving_team_id = models.UUIDField(null=True, blank=True) - winning_team_id = models.UUIDField(null=True, blank=True) - losing_team_id = models.UUIDField(null=True, blank=True) - broadcasted = models.BooleanField(default=False) - - def __str__(self): - - items = [f"Match {self.index}", self.formatted_start_date()] - desc = " - ".join(items) - player_names = " / ".join(self.player_names()) - if self.round: - return f"{str(self.round)} > {desc} > {player_names}" - elif self.group_stage: - return f"{str(self.group_stage)} > {desc} > {player_names}" - - def player_names(self): - return map(lambda ts: ts.player_names(), self.team_scores.all()) - - def formatted_start_date(self): - if self.start_date: - return self.start_date.strftime("%H:%M") - else: - return "no date" - # return str(self.start_date) #.strftime("%H:%M") - - def current_duration(self): - if self.end_date: - return (self.end_date - self.start_date).total_seconds() - else: - return (timezone.now() - self.start_date).total_seconds() - - def durationPrefix(self): - if self.current_duration() > 0: - return "Temps de jeu" - else: - return "Démarrage prévu dans" - - def formatted_duration(self): - - _seconds = self.current_duration() - - if _seconds > 0: - _hours = int(_seconds / 3600) - _minutes = int((_seconds % 3600) / 60) - return f"{_hours:02d}h{_minutes:02d}min" - else : - _seconds = _seconds * -1 - _hours = int(_seconds / 3600) - _minutes = int((_seconds % 3600) / 60) - return f"{_hours:02d}h{_minutes:02d}min" - - # def seconds(self): - # return (timezone.now() - self.start_date).total_seconds() - - def live_match(self): - title = f"Match {self.index}" - date = self.formatted_start_date() - duration = self.formatted_duration() - court = "" - if self.court: - court = f"Terrain {self.court}" - livematch = LiveMatch(title, date, duration, court) - - for team_score in self.team_scores.all(): - image = team_score.team_registration.logo - names = team_score.team_names() - scores = team_score.scores_array() - weight = team_score.team_registration.weight() - is_winner = team_score.team_registration == self.winning_team_id - team = Team(image, names, scores, weight, is_winner) - livematch.add_team(team) - - return livematch - -class TeamRegistration(models.Model): - id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE) - group_stage = models.ForeignKey(GroupStage, null=True, blank=True, on_delete=models.SET_NULL) - registration_date = models.DateTimeField(null=True, blank=True) - call_date = models.DateTimeField(null=True, blank=True) - bracket_position = models.IntegerField(null=True, blank=True) - group_stage_position = models.IntegerField(null=True, blank=True) - comment = models.CharField(max_length=200, null=True, blank=True) - source = models.CharField(max_length=20, null=True, blank=True) - source_value = models.CharField(max_length=200, null=True, blank=True) - logo = models.CharField(max_length=200, null=True, blank=True) #models.FilePathField(path=os.path.join(settings.STATIC_ROOT, "images"), null=True, blank=True) - name = models.CharField(max_length=200, null=True, blank=True) - - def __str__(self): - return self.player_names() - - def team_names(self): - if self.name: - return [self.name] - else: - return map(lambda pr: pr.name(), self.playerregistration_set.all()) - - def player_names(self): - names = map(lambda pr: pr.name(), self.playerregistration_set.all()) - str = " - ".join(names) - if len(str) > 0: - return str - else: - return "no players" - - def next_stage(self): - matches = Match.objects.filter(group_stage__isnull=True, team_scores__player_registrations__id=self.id).order_by('round__index') - if matches: - return matches[0].round.name() - elif self.group_stage: - return self.group_stage.name() - else: - return "--" - - def weight(self): - top_two_players = self.playerregistration_set.all().order_by('rank')[:2] - weight = 0 - for player in top_two_players: - rank = player.rank if player.rank is not None else 0 - weight += rank - return weight - -class PlayerRegistration(models.Model): - id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - team_registration = models.ForeignKey(TeamRegistration, on_delete=models.CASCADE) - first_name = models.CharField(max_length=50) - last_name = models.CharField(max_length=50) - licence_id = models.CharField(max_length=20, null=True, blank=True) - rank = models.IntegerField(null=True, blank=True) - has_paid = models.BooleanField(default=False) - unranked = models.BooleanField(default=False) - - def __str__(self): - return self.name() - - def name(self): - return f"{self.first_name} {self.last_name}" - -class TeamScore(models.Model): - id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - 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) - player_registrations = models.ManyToManyField(PlayerRegistration, blank=True) - score = models.CharField(max_length=50, null=True, blank=True) - walk_out = models.IntegerField(null=True, blank=True) #id, int of the walked_out team - lucky_loser = models.BooleanField() - - def __str__(self): - return f"{str(self.match)}: {self.player_names()}" - - def player_names(self): - names = map(lambda player: player.name(), self.player_registrations.all()) - return " - ".join(names) - - def team_names(self): - names = [] - if self.team_registration.name: - names.append(self.team_registration.name) - else: - names = list(map(lambda player: player.name(), self.player_registrations.all())) - return names - - def scores_array(self): - return [int(x) for x in self.score.split(',')] - -class Team: - def __init__(self, image, names, scores, weight, is_winner): - self.image = image - self.names = names - self.scores = scores - self.weight = weight - self.is_winner = is_winner - -class LiveMatch: - def __init__(self, title, date, duration, court): - self.title = title - self.date = date - self.teams = [] - self.duration = duration - self.court = court - - def add_team(self, team): - self.teams.append(team) - - def toJSON(self): - return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4) - -class TeamCall: - def __init__(self, names, date, weight, stage, image): - self.names = [] - self.names = names - self.date = date - self.weight = weight - self.stage = stage - self.image = image - -# class Set(models.Model): - -# class Game(models.Model): -# set = models.ForeignKey(Set, on_delete=models.CASCADE) -# format = models.IntegerField() - -# class Point(models.Model): -# game = models.ForeignKey(Game, on_delete=models.CASCADE) -# action = models.IntegerField() -# date = models.DateTimeField() -# team_id = models.IntegerField() diff --git a/tournaments/models/__init__.py b/tournaments/models/__init__.py new file mode 100644 index 0000000..43669e7 --- /dev/null +++ b/tournaments/models/__init__.py @@ -0,0 +1,11 @@ +from .club import Club +from .custom_user import CustomUser +from .enums import FederalCategory, FederalLevelCategory, FederalAgeCategory, FederalMatchCategory +from .event import Event +from .tournament import Tournament, TeamCall +from .group_stage import GroupStage +from .round import Round +from .match import Match, LiveMatch +from .team_registration import TeamRegistration +from .player_registration import PlayerRegistration +from .team_score import TeamScore diff --git a/tournaments/models/club.py b/tournaments/models/club.py new file mode 100644 index 0000000..3f5d624 --- /dev/null +++ b/tournaments/models/club.py @@ -0,0 +1,19 @@ +from django.db import models +import uuid + +class Club(models.Model): + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + name = models.CharField(max_length=50) + acronym = models.CharField(max_length=10) + phone = models.CharField(max_length=15, null=True, blank=True) + code = models.CharField(max_length=10, null=True, blank=True) + federal_club_data = models.JSONField(null=True, blank=True) + + address = models.CharField(max_length=200, null=True, blank=True) + city = models.CharField(max_length=100, null=True, blank=True) + zip_code = models.CharField(max_length=10, null=True, blank=True) + latitude = models.FloatField(null=True, blank=True) + longitude = models.FloatField(null=True, blank=True) + + def __str__(self): + return self.name diff --git a/tournaments/models/custom_user.py b/tournaments/models/custom_user.py new file mode 100644 index 0000000..001b6da --- /dev/null +++ b/tournaments/models/custom_user.py @@ -0,0 +1,17 @@ +from django.db import models +from django.contrib.auth.models import AbstractUser +from . import Club +import uuid + +class CustomUser(AbstractUser): + pass + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + umpire_code = models.CharField(max_length=50, blank=True, null=True) + clubs = models.ManyToManyField(Club) + phone = models.CharField(max_length=15, null=True, blank=True) + first_name = models.CharField(max_length=50) + last_name = models.CharField(max_length=50) + licence_id = models.CharField(max_length=10, null=True, blank=True) + + def __str__(self): + return self.username diff --git a/tournaments/models/enums.py b/tournaments/models/enums.py new file mode 100644 index 0000000..ff1c9b7 --- /dev/null +++ b/tournaments/models/enums.py @@ -0,0 +1,37 @@ +from django.db import models +import uuid + +class FederalCategory(models.IntegerChoices): + MEN = 0, 'Men' + WOMEN = 1, 'Women' + MIXED = 2, 'Mixed' + +class FederalLevelCategory(models.IntegerChoices): + P25 = 25, 'P25' + P100 = 100, 'P100' + P250 = 250, 'P250' + P500 = 500, 'P500' + P1000 = 1000, 'P1000' + P1500 = 1500, 'P1500' + P2000 = 2000, 'P2000' + +class FederalAgeCategory(models.IntegerChoices): + A11_12 = 120, 'A11_12' + A13_14 = 140, 'A13_14' + A15_16 = 160, 'A15_16' + A17_18 = 180, 'A17_18' + SENIOR = 200, 'SENIOR' + A45 = 450, 'A45' + A55 = 550, 'A55' + +class FederalMatchCategory(models.IntegerChoices): + TWO_SETS = 0, 'Two sets' + TWO_SETS_SUPER_TIE = 1, 'Two sets super tie' + TWO_SETS_FOUR_GAME = 2, 'Two sets of four games' + NINE_GAMES = 3, 'Nine games' + SUPER_TIE = 4, 'Super Tie-Break' + MEGA_TIE = 5, 'Mega Tie-Break' + TWO_SETS_DECISIVE_POINT = 6, 'Two Sets with decisive point' + TWO_SETS_DECISIVE_POINT_SUPER_TIE = 7, 'Two Sets with decisive point and super tie-break' + TWO_SETS_FOUR_GAME_DECISIVE_POINT = 8, 'Two sets of four games with decisive point' + NINE_GAMES_DECISIVE_POINT = 9, 'Nine games with decisive point' diff --git a/tournaments/models/event.py b/tournaments/models/event.py new file mode 100644 index 0000000..8bdbc9a --- /dev/null +++ b/tournaments/models/event.py @@ -0,0 +1,18 @@ +from django.db import models +from . import Club, FederalMatchCategory +import uuid + +class Event(models.Model): + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + club = models.ForeignKey(Club, on_delete=models.CASCADE) + date = models.DateTimeField() + name = models.CharField(max_length=200, null=True, blank=True) + federal_tournament_data = models.JSONField(null=True, blank=True) + court_count = models.IntegerField(null=True, blank=True) + tenup_id = models.IntegerField(null=True, blank=True) + group_stage_format = models.IntegerField(default=FederalMatchCategory.NINE_GAMES, choices=FederalMatchCategory.choices, null=True, blank=True) + round_format = models.IntegerField(default=FederalMatchCategory.NINE_GAMES, choices=FederalMatchCategory.choices, null=True, blank=True) + loser_round_format = models.IntegerField(default=FederalMatchCategory.NINE_GAMES, choices=FederalMatchCategory.choices, null=True, blank=True) + + def __str__(self): + return self.name diff --git a/tournaments/models/group_stage.py b/tournaments/models/group_stage.py new file mode 100644 index 0000000..e0e15c3 --- /dev/null +++ b/tournaments/models/group_stage.py @@ -0,0 +1,23 @@ +from django.db import models +from . import Tournament, FederalMatchCategory +import uuid + +class GroupStage(models.Model): + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE) + index = models.IntegerField(null=True, blank=True) + format = models.IntegerField(default=FederalMatchCategory.NINE_GAMES, choices=FederalMatchCategory.choices, null=True, blank=True) + + def __str__(self): + return f"{self.tournament.name} - {self.name()}" + + def name(self): + return f"Poule {self.index}" + + def next_match(self, player_registration): + matches = self.matches_for_registration(player_registration).filter(end_date__isnull=False).order_by('start_date') + return matches[0] + + def matches_for_registration(self, player_registration): + team_scores = TeamScore.objects.filter(player_registrations=player_registration) + return map(lambda ts: ts.match, team_scores) diff --git a/tournaments/models/match.py b/tournaments/models/match.py new file mode 100644 index 0000000..1f7a029 --- /dev/null +++ b/tournaments/models/match.py @@ -0,0 +1,109 @@ +from django.db import models +from . import Round, GroupStage, FederalMatchCategory +from django.utils import timezone +import uuid + +class Match(models.Model): + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + 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) + start_date = models.DateTimeField(null=True, blank=True) + end_date = models.DateTimeField(null=True, blank=True) + index = models.IntegerField(null=True, blank=True) + format = models.IntegerField(default=FederalMatchCategory.NINE_GAMES, choices=FederalMatchCategory.choices, null=True, blank=True) + court = models.CharField(max_length=50, null=True, blank=True) + serving_team_id = models.UUIDField(null=True, blank=True) + winning_team_id = models.UUIDField(null=True, blank=True) + losing_team_id = models.UUIDField(null=True, blank=True) + broadcasted = models.BooleanField(default=False) + + def __str__(self): + + items = [f"Match {self.index}", self.formatted_start_date()] + desc = " - ".join(items) + player_names = " / ".join(self.player_names()) + if self.round: + return f"{str(self.round)} > {desc} > {player_names}" + elif self.group_stage: + return f"{str(self.group_stage)} > {desc} > {player_names}" + + def player_names(self): + return map(lambda ts: ts.player_names(), self.team_scores.all()) + + def formatted_start_date(self): + if self.start_date: + return self.start_date.strftime("%H:%M") + else: + return "no date" + # return str(self.start_date) #.strftime("%H:%M") + + def current_duration(self): + if self.end_date: + return (self.end_date - self.start_date).total_seconds() + else: + return (timezone.now() - self.start_date).total_seconds() + + def durationPrefix(self): + if self.current_duration() > 0: + return "Temps de jeu" + else: + return "Démarrage prévu dans" + + def formatted_duration(self): + + _seconds = self.current_duration() + + if _seconds > 0: + _hours = int(_seconds / 3600) + _minutes = int((_seconds % 3600) / 60) + return f"{_hours:02d}h{_minutes:02d}min" + else : + _seconds = _seconds * -1 + _hours = int(_seconds / 3600) + _minutes = int((_seconds % 3600) / 60) + return f"{_hours:02d}h{_minutes:02d}min" + + # def seconds(self): + # return (timezone.now() - self.start_date).total_seconds() + + def live_match(self): + title = f"Match {self.index}" + date = self.formatted_start_date() + duration = self.formatted_duration() + court = "" + if self.court: + court = f"Terrain {self.court}" + livematch = LiveMatch(title, date, duration, court) + + for team_score in self.team_scores.all(): + image = team_score.team_registration.logo + names = team_score.team_names() + scores = team_score.scores_array() + weight = team_score.team_registration.weight() + is_winner = team_score.team_registration == self.winning_team_id + team = Team(image, names, scores, weight, is_winner) + livematch.add_team(team) + + return livematch + +class Team: + def __init__(self, image, names, scores, weight, is_winner): + self.image = image + self.names = names + self.scores = scores + self.weight = weight + self.is_winner = is_winner + +class LiveMatch: + def __init__(self, title, date, duration, court): + self.title = title + self.date = date + self.teams = [] + self.duration = duration + self.court = court + + def add_team(self, team): + self.teams.append(team) + + def toJSON(self): + return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4) diff --git a/tournaments/models/player_registration.py b/tournaments/models/player_registration.py new file mode 100644 index 0000000..8a9b5e4 --- /dev/null +++ b/tournaments/models/player_registration.py @@ -0,0 +1,19 @@ +from django.db import models +from . import TeamRegistration +import uuid + +class PlayerRegistration(models.Model): + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + team_registration = models.ForeignKey(TeamRegistration, on_delete=models.CASCADE) + first_name = models.CharField(max_length=50) + last_name = models.CharField(max_length=50) + licence_id = models.CharField(max_length=20, null=True, blank=True) + rank = models.IntegerField(null=True, blank=True) + has_paid = models.BooleanField(default=False) + unranked = models.BooleanField(default=False) + + def __str__(self): + return self.name() + + def name(self): + return f"{self.first_name} {self.last_name}" diff --git a/tournaments/models/round.py b/tournaments/models/round.py new file mode 100644 index 0000000..716bb99 --- /dev/null +++ b/tournaments/models/round.py @@ -0,0 +1,34 @@ +from django.db import models +from . import Tournament, FederalMatchCategory +import uuid + +class Round(models.Model): + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE) + index = models.IntegerField(null=True, blank=True) + loser = models.ForeignKey('self', blank=True, null=True, on_delete=models.CASCADE) + format = models.IntegerField(default=FederalMatchCategory.NINE_GAMES, choices=FederalMatchCategory.choices, null=True, blank=True) + + def __str__(self): + return f"{self.tournament.name} - {self.name()}" + + # def stage_call(self): + # stage_call = StageCall(f"1/{self.index}") + # for match in self.match_set.all(): + # names = map(lambda ts: ts.player_names(), match.teamstate_set.all()) + # if names: + # team_call = TeamCall(names, match.formatted_start_date) + # stage_call.add_team(team_call) + + # return stage_call + + def name(self): + if self.index == 0: + return "Finale" + elif self.index == 1: + return "Demi-Finales" + elif self.index == 2: + return "Quarts de finale" + else: + squared = 2 ** self.index + return f"{squared}ème" diff --git a/tournaments/models/team_registration.py b/tournaments/models/team_registration.py new file mode 100644 index 0000000..05460fb --- /dev/null +++ b/tournaments/models/team_registration.py @@ -0,0 +1,57 @@ +from django.db import models +from . import Tournament, GroupStage +import uuid + +class TeamRegistration(models.Model): + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE) + group_stage = models.ForeignKey(GroupStage, null=True, blank=True, on_delete=models.SET_NULL) + registration_date = models.DateTimeField(null=True, blank=True) + call_date = models.DateTimeField(null=True, blank=True) + bracket_position = models.IntegerField(null=True, blank=True) + group_stage_position = models.IntegerField(null=True, blank=True) + comment = models.CharField(max_length=200, null=True, blank=True) + source = models.CharField(max_length=20, null=True, blank=True) + source_value = models.CharField(max_length=200, null=True, blank=True) + logo = models.CharField(max_length=200, null=True, blank=True) #models.FilePathField(path=os.path.join(settings.STATIC_ROOT, "images"), null=True, blank=True) + name = models.CharField(max_length=200, null=True, blank=True) + + def __str__(self): + return self.player_names() + + def team_names(self): + if self.name: + return [self.name] + else: + return map(lambda pr: pr.name(), self.playerregistration_set.all()) + + def player_names(self): + names = map(lambda pr: pr.name(), self.playerregistration_set.all()) + str = " - ".join(names) + if len(str) > 0: + return str + else: + return "no players" + + def next_stage(self): + matches = map(lambda ts: ts.match, self.teamscore_set.all()) + matches = [m for m in matches if m.group_stage is None] + matches = sorted(matches, key=lambda m: m.round.index) + + # matches = self.teamscore_set + # matches = Match.objects.filter(group_stage__isnull=True, team_scores__player_registrations__id=self.id).order_by('round__index') + print(f"matches = {len(matches)}") + if matches: + return matches[0].round.name() + elif self.group_stage: + return self.group_stage.name() + else: + return "--" + + def weight(self): + top_two_players = self.playerregistration_set.all().order_by('rank')[:2] + weight = 0 + for player in top_two_players: + rank = player.rank if player.rank is not None else 0 + weight += rank + return weight diff --git a/tournaments/models/team_score.py b/tournaments/models/team_score.py new file mode 100644 index 0000000..4aa6008 --- /dev/null +++ b/tournaments/models/team_score.py @@ -0,0 +1,30 @@ +from django.db import models +from . import Match, TeamRegistration, PlayerRegistration +import uuid + +class TeamScore(models.Model): + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + 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) + player_registrations = models.ManyToManyField(PlayerRegistration, blank=True) + score = models.CharField(max_length=50, null=True, blank=True) + walk_out = models.IntegerField(null=True, blank=True) #id, int of the walked_out team + lucky_loser = models.BooleanField() + + def __str__(self): + return f"{str(self.match)}: {self.player_names()}" + + def player_names(self): + names = map(lambda player: player.name(), self.player_registrations.all()) + return " - ".join(names) + + def team_names(self): + names = [] + if self.team_registration.name: + names.append(self.team_registration.name) + else: + names = list(map(lambda player: player.name(), self.player_registrations.all())) + return names + + def scores_array(self): + return [int(x) for x in self.score.split(',')] diff --git a/tournaments/models/tournament.py b/tournaments/models/tournament.py new file mode 100644 index 0000000..1a8e4ff --- /dev/null +++ b/tournaments/models/tournament.py @@ -0,0 +1,91 @@ +from django.db import models +from . import Event, CustomUser, FederalMatchCategory, FederalCategory, FederalLevelCategory, FederalAgeCategory +import uuid + +class Tournament(models.Model): + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + event = models.ForeignKey(Event, blank=True, null=True, on_delete=models.CASCADE) + creator = models.ForeignKey(CustomUser, blank=True, null=True, on_delete=models.CASCADE) + name = models.CharField(max_length=200, null=True, blank=True) + start_date = models.DateTimeField() + end_date = models.DateTimeField(null=True, blank=True) + creation_date = models.DateTimeField() + is_private = models.BooleanField() + format = models.IntegerField(default=FederalMatchCategory.NINE_GAMES, choices=FederalMatchCategory.choices, null=True, blank=True) + group_stage_format = models.IntegerField(default=FederalMatchCategory.NINE_GAMES, choices=FederalMatchCategory.choices, null=True, blank=True) + round_format = models.IntegerField(default=FederalMatchCategory.NINE_GAMES, choices=FederalMatchCategory.choices, null=True, blank=True) + loser_round_format = models.IntegerField(default=FederalMatchCategory.NINE_GAMES, choices=FederalMatchCategory.choices, null=True, blank=True) + bracket_sort_mode = models.IntegerField(default=0) + group_stage_count = models.IntegerField(default=0) + rank_source_date = models.DateTimeField(null=True, blank=True) + # custom_name = models.CharField(max_length=100, null=True, blank=True) + day_duration = models.IntegerField(default=0) + team_count = models.IntegerField(default=0) + team_sorting = models.IntegerField(default=0) + federal_category = models.IntegerField(default=FederalCategory.MEN, choices=FederalCategory.choices) # optional ? + federal_level_category = models.IntegerField(default=FederalLevelCategory.P100, choices=FederalLevelCategory.choices) + federal_age_category = models.IntegerField(default=FederalAgeCategory.SENIOR, choices=FederalAgeCategory.choices) + group_stage_court_count = models.IntegerField(null=True, blank=True) + seed_count = models.IntegerField(default=0) + closed_registration_date = models.DateTimeField(null=True, blank=True) + group_stage_additional_qualified = models.IntegerField(default=0) + court_count = models.IntegerField(null=True, blank=True) + prioritize_club_members = models.BooleanField() + qualified_per_group_stage = models.IntegerField(default=0) + teams_per_group_stage = models.IntegerField(default=0) + #estimated_end_date = models.DateTimeField(null=True, blank=True) + + def __str__(self): + return self.name + + def level(self): + return self.get_federal_level_category_display() + + def category(self): + return self.get_federal_category_display() + + def formatted_start_date(self): + return self.start_date.strftime("%d/%m/%y") + + def team_calls(self): + + team_calls = [] + for team_registration in self.teamregistration_set.all(): + call_date = team_registration.call_date + if call_date: + names = team_registration.team_names() + stage = team_registration.next_stage() + weight = team_registration.weight() + team_call = TeamCall(names, call_date, weight, stage, team_registration.logo) + team_calls.append(team_call) + + return team_calls + + def live_matches(self): + matches = [] + for group_stage in self.groupstage_set.all(): + for match in group_stage.match_set.all(): + matches.append(match) + for round in self.round_set.all(): + for match in round.match_set.all(): + matches.append(match) + + # matches = [m for m in matches if m.broadcasted==True] + + print(len(matches)) + + return map(lambda match: match.live_match(), matches) + + # # Convert object attributes to a dictionary + # dict = self.__dict__ + # # Convert dictionary to JSON + # return json.dumps(dict) + +class TeamCall: + def __init__(self, names, date, weight, stage, image): + self.names = [] + self.names = names + self.date = date + self.weight = weight + self.stage = stage + self.image = image diff --git a/tournaments/static/tournaments/css/style.css b/tournaments/static/tournaments/css/style.css index 6df9919..0eb1728 100644 --- a/tournaments/static/tournaments/css/style.css +++ b/tournaments/static/tournaments/css/style.css @@ -34,6 +34,8 @@ body { height: 100%; padding: 0; margin: 0; + + overflow: hidden; } label { @@ -420,16 +422,23 @@ tr { width: 100px; } -.table-row { +.table-row-3-colums { display: grid; grid-template-columns: auto 1fr auto; align-items: center; /* Vertically center the content within each column */ padding: 5px 0px; } +.table-row-5-colums { + display: grid; + grid-template-columns: 60px auto 60px 60px 130px; + align-items: center; /* Vertically center the content within each column */ + padding: 5px 0px; +} + .table-cell { flex-grow: 1; - text-align: center; + /* text-align: center; */ /* padding: 5px; */ } @@ -437,3 +446,19 @@ tr { grid-column: 2 / span 1; /* Center column spans from column 2 to column 3 */ text-align: left; } + +.wrap { + display: flex; + flex-direction: column; + flex-wrap: wrap; + height: 100vh; /*the height will need to be customized*/ + width: 50px; +} + +#xrow { + background: #000; + color: #fff; + height: 50px; + width: 50px; + margin-left: 10px; +} diff --git a/tournaments/static/tournaments/test.html b/tournaments/static/tournaments/test.html index 1af9897..b7c0c0f 100644 --- a/tournaments/static/tournaments/test.html +++ b/tournaments/static/tournaments/test.html @@ -11,154 +11,88 @@
-
-