Skip to content

Commit 6a1478e

Browse files
committed
first draft for managing brackets
1 parent 7ade9c0 commit 6a1478e

File tree

6 files changed

+146
-4
lines changed

6 files changed

+146
-4
lines changed

insalan/tournament/admin.py

+62-1
Original file line numberDiff line numberDiff line change
@@ -528,4 +528,65 @@ def launch_group_matchs_action(self,request,queryset):
528528
self.message_user(request,_("Les matchs ont bien été lancés"))
529529

530530

531-
admin.site.register(GroupMatch, GroupMatchAdmin)
531+
admin.site.register(GroupMatch, GroupMatchAdmin)
532+
533+
534+
class BracketAdmin(admin.ModelAdmin):
535+
"""Admin handle for Brackets"""
536+
537+
list_display = ("id", "name", "tournament")
538+
search_fields = ["name","tournament","tournament__event","tournament__game"]
539+
actions = ["create_empty_knockout_matchs_action","fill_knockout_matchs_action"]
540+
541+
list_filter = ["tournament","tournament__event"]
542+
543+
@admin.action(description=_("Créer les matchs"))
544+
def create_empty_knockout_matchs_action(self,request,queryset):
545+
for bracket in queryset:
546+
if any([MatchStatus.SCHEDULED != m.status for m in KnockoutMatch.objects.filter(bracket=bracket)]):
547+
self.message_user(request,_("Des matchs existent déjà et sont en cours ou terminés"))
548+
549+
create_empty_knockout_matchs(bracket)
550+
self.message_user(request,_("Matchs créer avec succès"))
551+
552+
@admin.action(description=_("Remplir les matchs"))
553+
def fill_knockout_matchs_action(self,request,queryset):
554+
for bracket in queryset:
555+
# fill_knockout_matchs(bracket)
556+
pass
557+
558+
admin.site.register(Bracket, BracketAdmin)
559+
560+
class KnockoutMatchAdmin(admin.ModelAdmin):
561+
"""Admin handle for Knockout matchs"""
562+
563+
list_display = ("id", "bracket", "status")
564+
filter_horizontal = ("teams",)
565+
actions = [""]
566+
567+
list_filter = ["bracket", "bracket__tournament", "round_number", "index_in_round"]
568+
569+
@admin.action(description=_("Lancer les matchs"))
570+
def launch_knockout_matchs_action(self,request,queryset):
571+
for match in queryset:
572+
for team in match.get_teams():
573+
team_matchs = KnockoutMatch.objects.filter(teams=team,round_index__gt=match.round_number)
574+
for team_match in team_matchs:
575+
if team_match.status == MatchStatus.ONGOING:
576+
self.message_user(request,_(f"L'équipe {team.name} est encore dans un match en cours"), messages.ERROR)
577+
return
578+
if team_match.status == MatchStatus.SCHEDULED:
579+
self.message_user(request,_(f"L'équipe {team.name} n'a pas encore joué un ou des matchs des rounds précédent"), messages.ERROR)
580+
return
581+
582+
if len(match.get_teams()) == 1:
583+
match.status = MatchStatus.COMPLETED
584+
# score
585+
else:
586+
match.status = MatchStatus.ONGOING
587+
588+
match.save()
589+
self.message_user(request,_("Les matchs ont bien été lancés"))
590+
591+
592+
admin.site.register(KnockoutMatch, KnockoutMatchAdmin)

insalan/tournament/manage/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
from .group import *
1+
from .group import *
2+
from .bracket import *

insalan/tournament/manage/bracket.py

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from ..models import Bracket, KnockoutMatch, BracketType, BracketSet
2+
3+
def create_empty_knockout_matchs(bracket: Bracket):
4+
depth = bracket.depth
5+
6+
for match in KnockoutMatch.objects.filter(bracket=bracket):
7+
match.delete()
8+
9+
for round_n in range(1,depth+1):
10+
for match_id in range(1,2**(round_n-1)+1):
11+
KnockoutMatch.objects.create(round_number=round_n,index_in_round=match_id,bracket=bracket)
12+
if bracket.bracket_type == BracketType.DOUBLE:
13+
for round_n in range(1,2*depth-1):
14+
for match_id in range(1,2**((round_n-1)//2)+1):
15+
KnockoutMatch.objects.create(round_number=round_n,index_in_round=match_id,bracket=bracket,bracket_set=BracketSet.LOOSER)
16+
KnockoutMatch.objects.create(round_number=0,index_in_round=1,bracket=bracket)

insalan/tournament/models/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from .bracket import Bracket, KnockoutMatch
1+
from .bracket import Bracket, KnockoutMatch, BracketSet, BracketType
22
from .event import Event
33
from .caster import Caster
44
from .game import Game

insalan/tournament/models/bracket.py

+61-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
from django.db import models
22
from django.utils.translation import gettext_lazy as _
33

4+
from . import match
5+
46
class BracketType(models.TextChoices):
57
"""Information about the type of a bracket, single or double elimination"""
68

79
SINGLE = "SINGLE", _("Simple élimination")
810
DOUBLE = "DOUBLE", _("Double élimination")
911

12+
class BracketSet(models.TextChoices):
13+
"""Information on the bracket set, winner or looser"""
14+
15+
WINNER = "WINNER"
16+
LOOSER = "LOOSER"
17+
1018
class Bracket(models.Model):
1119
name = models.CharField(
1220
max_length=40,
@@ -22,7 +30,59 @@ class Bracket(models.Model):
2230
choices=BracketType.choices,
2331
max_length=10
2432
)
33+
depth = models.IntegerField(
34+
default=1
35+
)
2536

2637
class Meta:
2738
verbose_name = _("Arbre de tournoi")
28-
verbose_name_plural = _("Arbres de tournoi")
39+
verbose_name_plural = _("Arbres de tournoi")
40+
41+
# def __init__(self, *args, **kwargs) -> None:
42+
# super().__init__(*args,**kwargs)
43+
# self.cache_depth = self.depth
44+
45+
# def save(self, *args, **kwargs):
46+
# """
47+
# Override default save method to automatically create matchs when the Bracket is created or modified.
48+
# """
49+
# need_save = False
50+
51+
# # if no match exist, then create them
52+
# if len(KnockoutMatch.objects.filter(bracket=self)) == 0:
53+
# for round_n in range(1,depth+1):
54+
# for match_id in range(1,2**(round_n-1)+1):
55+
# KnockoutMatch.objects.create(round_number=round_n,index_in_round=match_id,bracket=self)
56+
# if self.bracket_type == BracketType.DOUBLE:
57+
# for round_n in range(1,2*depth-1):
58+
# for match_id in range(1,2**((round_n-1)//2)+1):
59+
# KnockoutMatch.objects.create(round_number=round_n,index_in_round=match_id,bracket=self,bracket_set=BracketSet.LOOSER)
60+
# KnockoutMatch.objects.create(round_number=0,index_in_round=1,bracket=self)
61+
# need_save = True
62+
# # if the depth has changed and no game is started, create or delete matchs
63+
# elif self.cache_depth < self.depth and all([match.MatchStatus.SCHEDULED == m.status for m in KnockoutMatch.objects.filter(bracket=self)]):
64+
65+
# pass
66+
# elif self.cache_depth > self.depth and all([match.MatchStatus.SCHEDULED == m.status for m in KnockoutMatch.objects.filter(bracket=self)]):
67+
# #
68+
# need_save = True
69+
70+
# if need_save:
71+
# super().save()
72+
73+
class KnockoutMatch(match.Match):
74+
bracket = models.ForeignKey(
75+
"Bracket",
76+
verbose_name=_("Arbre de tournoi"),
77+
on_delete=models.CASCADE
78+
)
79+
bracket_set = models.CharField(
80+
max_length=10,
81+
default=BracketSet.WINNER,
82+
choices=BracketSet.choices,
83+
verbose_name=_("Type de tableau, gagnant ou perdant")
84+
)
85+
86+
class Meta:
87+
verbose_name = _("Match dans un arbre")
88+
verbose_name_plural = _("Matchs dans un arbre")

insalan/tournament/models/match.py

+4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from django.db import models
22
from django.utils.translation import gettext_lazy as _
33
from django.contrib.postgres.fields import ArrayField
4+
from typing import List
45

56
class MatchStatus(models.TextChoices):
67
"""Information about the status of a match"""
@@ -52,6 +53,9 @@ class Match(models.Model):
5253
class Meta:
5354
abstract = True
5455

56+
def get_teams(self) -> List["Team"]:
57+
return self.teams.all()
58+
5559
class Score(models.Model):
5660
score = models.IntegerField(
5761
verbose_name=_("Score")

0 commit comments

Comments
 (0)