From a83c0782eb7c9d8d6069bcc4efe4ed9d1118b88f Mon Sep 17 00:00:00 2001 From: Raz Date: Tue, 25 Feb 2025 22:18:28 +0100 Subject: [PATCH] wip --- tournaments/models/match.py | 13 +- .../tournaments/tournament_bracket.html | 141 ++++++++++++++++-- tournaments/views.py | 55 ++++++- 3 files changed, 187 insertions(+), 22 deletions(-) diff --git a/tournaments/models/match.py b/tournaments/models/match.py index 8265ba7..a5d53a7 100644 --- a/tournaments/models/match.py +++ b/tournaments/models/match.py @@ -157,7 +157,18 @@ class Match(models.Model): return teams if self.round and self.round.parent: - return teams + print("self.round.parent.index", self.round.parent.index) + print("self.round.parent.parent", self.round.parent.parent) + if self.round.parent.index == 1 and self.round.parent.parent is None: + names = ["Perdant Demi #1", ''] + team = self.default_live_team(names) + teams.append(team) + names = ["Perdant Demi #2", ''] + team = self.default_live_team(names) + teams.append(team) + return teams + else: + return teams # No team scores at all if previous_top_match: diff --git a/tournaments/templates/tournaments/tournament_bracket.html b/tournaments/templates/tournaments/tournament_bracket.html index af6026c..28d42f1 100644 --- a/tournaments/templates/tournaments/tournament_bracket.html +++ b/tournaments/templates/tournaments/tournament_bracket.html @@ -50,7 +50,10 @@ function renderBracket() { bracket.appendChild(firstMatch); const matchHeight = firstMatch.offsetHeight; const matchSpacing = 10; + const baseDistance = matchHeight + matchSpacing; bracket.innerHTML = ''; + const roundCount = rounds.length; + const finalRoundIndex = (roundCount - 1) / 2; rounds.forEach((roundMatches, roundIndex) => { const roundDiv = document.createElement('div'); @@ -63,19 +66,74 @@ function renderBracket() { matchDiv.style.position = 'absolute'; const isDisabled = matchTemplate.dataset.disabled === 'true'; let top; - if (roundIndex === 0) { + let left; + let right; + let nextMatchDistance = baseDistance; + + // Check if this round has only one match + const isSingleMatchRound = roundMatches.length === 1; + if (isSingleMatchRound) { + matchDiv.classList.add('single-match-round'); + nextMatchDistance = 0; + } + + if (roundIndex === 0 || roundIndex === rounds.length - 1) { top = matchIndex * (matchHeight + matchSpacing); + if (roundIndex === rounds.length - 1) { + nextMatchDistance = baseDistance * Math.pow(2, rounds.length - roundIndex - 1); + matchDiv.classList.add('reverse-bracket'); + } } else { - const parentIndex1 = matchIndex * 2; - const parentIndex2 = parentIndex1 + 1; - const parentPos1 = matchPositions[roundIndex - 1][parentIndex1]; - const parentPos2 = matchPositions[roundIndex - 1][parentIndex2]; - top = (parentPos1 + parentPos2) / 2; + const previousRoundMatches = rounds[roundIndex - 1].length; + const currentRoundMatches = roundMatches.length; + if (currentRoundMatches === previousRoundMatches || roundIndex == finalRoundIndex + 1) { + // Same number of matches as previous round + top = matchPositions[roundIndex - 2][matchIndex]; + nextMatchDistance = 0; + } else if (roundIndex == finalRoundIndex) { + top = matchPositions[roundIndex - 3][1 + matchIndex]; + nextMatchDistance = baseDistance; + } else if (currentRoundMatches > previousRoundMatches) { + // Bracket is reversed - matches are splitting instead of combining + const parentIndex = Math.floor(matchIndex / 2); + if (parentIndex < matchPositions[roundIndex - 1].length) { + const parentPos = matchPositions[roundIndex - 1][parentIndex]; + // Calculate offset based on whether it's an even or odd match + const offset = (matchIndex % 2 === 0) ? -baseDistance : baseDistance; + top = parentPos + offset; + } + nextMatchDistance = baseDistance * Math.pow(2, rounds.length - roundIndex - 1); + matchDiv.classList.add('reverse-bracket'); + } else { + // Normal case where matches are combining + const parentIndex1 = matchIndex * 2; + const parentIndex2 = parentIndex1 + 1; + const parentPos1 = matchPositions[roundIndex - 1][parentIndex1]; + const parentPos2 = matchPositions[roundIndex - 1][parentIndex2]; + top = (parentPos1 + parentPos2) / 2; + nextMatchDistance = !isSingleMatchRound ? baseDistance * Math.pow(2, roundIndex) : 0; + } } - const baseDistance = matchHeight + matchSpacing; - const distance = baseDistance * Math.pow(2, roundIndex); - matchDiv.style.setProperty('--next-match-distance', `${distance}px`); + if (roundIndex >= finalRoundIndex - 1) { + matchDiv.style.transform = `translateX(-50%)`; + if (roundIndex >= finalRoundIndex + 2) { + matchDiv.style.transform = `translateX(-100%)`; + } + + if (roundIndex == finalRoundIndex - 1) { + matchDiv.classList.add('inward'); + } + if (roundIndex == finalRoundIndex + 1) { + matchDiv.classList.add('outward'); + } + + } + + if (roundIndex == 1 || roundIndex == roundCount - 2) { + nextMatchDistance = nextMatchDistance / 2; + } + matchDiv.style.setProperty('--next-match-distance', `${nextMatchDistance}px`); matchDiv.style.top = `${top}px`; matchPositions[roundIndex][matchIndex] = top; @@ -84,6 +142,19 @@ function renderBracket() {
${matchTemplate.innerHTML}
`; + if (roundIndex == finalRoundIndex - 1) { + const matchDiv2 = document.createElement('div'); + matchDiv2.className = 'butterfly-match'; + matchDiv2.style.position = 'absolute'; + matchDiv2.style.transform = `translateX(-50%)`; + matchDiv2.classList.add('inward'); + matchDiv2.classList.add('semi-final'); + matchDiv2.style.setProperty('--next-match-distance', `${baseDistance}px`); + matchDiv2.style.top = `${top}px`; + matchDiv2.innerHTML = ` + `; + roundDiv.appendChild(matchDiv2); + } roundDiv.appendChild(matchDiv); }); @@ -95,6 +166,13 @@ function renderBracket() { diff --git a/tournaments/views.py b/tournaments/views.py index a0b66e7..765fb39 100644 --- a/tournaments/views.py +++ b/tournaments/views.py @@ -966,18 +966,61 @@ def tournament_bracket(request, tournament_id): group_stage_loser_bracket=False ).order_by('-index') + main_rounds_reversed = tournament.round_set.filter( + parent=None, + group_stage_loser_bracket=False + ).order_by('index') # Removed the minus sign before 'index' + + loser_final = None + if len(main_rounds_reversed) >= 1: + semi = main_rounds_reversed[1] + loser_round = tournament.round_set.filter( + parent=semi, + group_stage_loser_bracket=False + ).order_by('index') + if len(loser_round) >= 1: + loser_final = loser_round[0] + # Create serializable match groups data serializable_match_groups = [] + # Add first half of each round (from last to semi-finals) for round in main_rounds: matches = round.match_set.all() + if matches: - # Create MatchGroup - match_group = tournament.create_match_group( - name=round.name(), - matches=matches - ) - serializable_match_groups.append(match_group) + midpoint = len(matches) // 2 + if len(matches) > 1: + first_half_matches = matches[:midpoint] + else: + first_half_matches = list(matches) # Convert QuerySet to a list + if loser_final: + loser_matches = loser_final.match_set.all() + if len(loser_matches) >= 1: + print(loser_matches[0]) + first_half_matches.append(loser_matches[0]) + + + if first_half_matches: + match_group = tournament.create_match_group( + name=round.name(), + matches=first_half_matches + ) + serializable_match_groups.append(match_group) + + for round in main_rounds_reversed: + matches = round.match_set.all() + if matches: + midpoint = len(matches) // 2 + if len(matches) > 1: + first_half_matches = matches[midpoint:] + + match_group = tournament.create_match_group( + name=round.name(), + matches=first_half_matches + ) + serializable_match_groups.append(match_group) + context = { 'tournament': tournament,