fix calling stuff and round lag efficiency

paca_championship
Raz 1 year ago
parent 138551c32a
commit 17f371e450
  1. 15
      PadelClub/Data/GroupStage.swift
  2. 21
      PadelClub/Data/Match.swift
  3. 35
      PadelClub/Data/Round.swift
  4. 23
      PadelClub/Data/Tournament.swift
  5. 10
      PadelClub/Utils/ContactManager.swift
  6. 34
      PadelClub/Views/Calling/BracketCallingView.swift
  7. 12
      PadelClub/Views/Calling/CallMessageCustomizationView.swift
  8. 3
      PadelClub/Views/Calling/CallView.swift
  9. 12
      PadelClub/Views/Calling/TeamsCallingView.swift
  10. 2
      PadelClub/Views/GroupStage/GroupStagesSettingsView.swift
  11. 4
      PadelClub/Views/GroupStage/LoserBracketFromGroupStageView.swift
  12. 2
      PadelClub/Views/Match/MatchSetupView.swift
  13. 2
      PadelClub/Views/Planning/PlanningView.swift
  14. 4
      PadelClub/Views/Round/LoserRoundSettingsView.swift
  15. 4
      PadelClub/Views/Round/LoserRoundView.swift
  16. 10
      PadelClub/Views/Round/RoundSettingsView.swift
  17. 28
      PadelClub/Views/Round/RoundView.swift
  18. 13
      PadelClub/Views/Tournament/Screen/TableStructureView.swift
  19. 6
      PadelClub/Views/Tournament/Screen/TournamentCallView.swift

@ -115,11 +115,12 @@ final class GroupStage: ModelObject, Storable {
return match
}
func buildMatches() {
_removeMatches()
var matches = [Match]()
func buildMatches(keepExistingMatches: Bool = false) {
var teamScores = [TeamScore]()
var matches = [Match]()
if keepExistingMatches == false {
_removeMatches()
for i in 0..<_numberOfMatchesToBuild() {
let newMatch = self._createMatch(index: i)
@ -127,6 +128,12 @@ final class GroupStage: ModelObject, Storable {
teamScores.append(contentsOf: newMatch.createTeamScores())
matches.append(newMatch)
}
} else {
for match in _matches() {
match.resetTeamScores(outsideOf: [])
teamScores.append(contentsOf: match.createTeamScores())
}
}
do {
try self.tournamentStore.matches.addOrUpdate(contentOfs: matches)

@ -63,6 +63,20 @@ final class Match: ModelObject, Storable, Equatable {
// self.order = order
}
func setMatchName(_ serverName: String?) {
self.name = serverName
}
func isFromLastRound() -> Bool {
guard let roundObject, roundObject.parent == nil else { return false }
guard let currentTournament = currentTournament() else { return false }
if currentTournament.rounds().count - 1 == roundObject.index {
return true
} else {
return false
}
}
var tournamentStore: TournamentStore {
if let store = self.store as? TournamentStore {
return store
@ -380,16 +394,17 @@ defer {
func _toggleMatchDisableState(_ state: Bool, forward: Bool = false, single: Bool = false) {
//if disabled == state { return }
let currentState = disabled
disabled = state
if disabled {
if disabled != currentState {
do {
try self.tournamentStore.teamScores.delete(contentOfs: teamScores)
} catch {
Logger.error(error)
}
}
if state == true {
if state == true, state != currentState {
let teams = teams()
for team in teams {
if isSeededBy(team: team) {
@ -403,6 +418,8 @@ defer {
}
}
//byeState = false
roundObject?._cachedSeedInterval = nil
name = nil
do {
try self.tournamentStore.matches.addOrUpdate(instance: self)
} catch {

@ -24,6 +24,7 @@ final class Round: ModelObject, Storable {
var startDate: Date?
var groupStageLoserBracket: Bool = false
var loserBracketMode: LoserBracketMode = .automatic
var _cachedSeedInterval: SeedInterval?
internal init(tournament: String, index: Int, parent: String? = nil, matchFormat: MatchFormat? = nil, startDate: Date? = nil, groupStageLoserBracket: Bool = false, loserBracketMode: LoserBracketMode = .automatic) {
self.tournament = tournament
@ -451,6 +452,8 @@ defer {
func correspondingLoserRoundTitle(_ displayStyle: DisplayStyle = .wide) -> String {
if let _cachedSeedInterval { return _cachedSeedInterval.localizedLabel(displayStyle) }
#if _DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
@ -469,8 +472,16 @@ defer {
// && $0.bracketPosition != nil
// && ($0.bracketPosition! / 2) < initialMatchIndexFromRoundIndex
// })
var seedsCount = seedsAfterThisRound.count
if seedsAfterThisRound.isEmpty {
let nextRoundsDisableMatches = nextRoundsDisableMatches()
seedsCount = disabledMatches().count - nextRoundsDisableMatches
}
let playedMatches = playedMatches()
let seedInterval = SeedInterval(first: playedMatches.count + seedsAfterThisRound.count + 1, last: playedMatches.count * 2 + seedsAfterThisRound.count)
let seedInterval = SeedInterval(first: playedMatches.count + seedsCount + 1, last: playedMatches.count * 2 + seedsCount)
_cachedSeedInterval = seedInterval
return seedInterval.localizedLabel(displayStyle)
}
@ -492,6 +503,8 @@ defer {
}
func seedInterval(initialMode: Bool = false) -> SeedInterval? {
if initialMode == false, let _cachedSeedInterval { return _cachedSeedInterval }
#if _DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
@ -515,11 +528,17 @@ defer {
&& ($0.bracketPosition! / 2) < initialMatchIndexFromRoundIndex
}
let playedMatches = playedMatches().count
let minimumMatches = playedMatches * 2
var seedsCount = seedsAfterThisRound.count
if seedsAfterThisRound.isEmpty {
let nextRoundsDisableMatches = nextRoundsDisableMatches()
seedsCount = disabledMatches().count - nextRoundsDisableMatches
}
let playedMatches = playedMatches()
//print("playedMatches \(playedMatches)", initialMode, parent, parentRound?.roundTitle(), seedsAfterThisRound.count)
let seedInterval = SeedInterval(first: playedMatches + seedsAfterThisRound.count + 1, last: minimumMatches + seedsAfterThisRound.count)
let seedInterval = SeedInterval(first: playedMatches.count + seedsCount + 1, last: playedMatches.count * 2 + seedsCount)
//print(seedInterval.localizedLabel())
_cachedSeedInterval = seedInterval
return seedInterval
}
@ -660,6 +679,14 @@ defer {
return self.tournamentStore.rounds.findById(parent)
}
func nextRoundsDisableMatches() -> Int {
if parent == nil, index > 0 {
return tournamentObject()?.rounds().suffix(index).flatMap { $0.disabledMatches() }.count ?? 0
} else {
return 0
}
}
func updateMatchFormat(_ updatedMatchFormat: MatchFormat, checkIfPossible: Bool, andLoserBracket: Bool) {
if updatedMatchFormat.weight < self.matchFormat.weight {
updateMatchFormatAndAllMatches(updatedMatchFormat)

@ -1112,9 +1112,10 @@ defer {
return callDateIssue.count + duplicates.count + problematicPlayers.count + inadequatePlayers.count + playersWithoutValidLicense.count + playersMissing.count + waitingListInBracket.count + waitingListInGroupStage.count + ageInadequatePlayers.count + homonyms.count
}
func isStartDateIsDifferentThanCallDate(_ team: TeamRegistration) -> Bool {
func isStartDateIsDifferentThanCallDate(_ team: TeamRegistration, expectedSummonDate: Date? = nil) -> Bool {
guard let summonDate = team.callDate else { return true }
guard let expectedSummonDate = team.expectedSummonDate() else { return true }
let expectedSummonDate : Date? = team.expectedSummonDate() ?? expectedSummonDate
guard let expectedSummonDate else { return true }
return Calendar.current.compare(summonDate, to: expectedSummonDate, toGranularity: .minute) != ComparisonResult.orderedSame
}
@ -1609,7 +1610,9 @@ defer {
func callStatus() async -> TournamentStatus {
let selectedSortedTeams = selectedSortedTeams()
let called = selectedSortedTeams.filter { isStartDateIsDifferentThanCallDate($0) == false }
let label = "\(called.count.formatted()) / \(selectedSortedTeams.count.formatted()) convoquées au bon horaire"
let justCalled = selectedSortedTeams.filter { $0.called() }
let label = "\(justCalled.count.formatted()) / \(selectedSortedTeams.count.formatted()) (\(called.count.formatted()) au bon horaire)"
let completion = (Double(called.count) / Double(selectedSortedTeams.count))
let completionLabel = completion.isNaN ? "" : completion.formatted(.percent.precision(.fractionLength(0)))
return TournamentStatus(label: label, completion: completionLabel)
@ -1852,7 +1855,7 @@ defer {
}
}
func refreshGroupStages() {
func refreshGroupStages(keepExistingMatches: Bool = false) {
unsortedTeams().forEach { team in
team.groupStage = nil
team.groupStagePosition = nil
@ -1861,16 +1864,16 @@ defer {
if groupStageCount > 0 {
switch groupStageOrderingMode {
case .random:
setGroupStage(randomize: true)
setGroupStage(randomize: true, keepExistingMatches: keepExistingMatches)
case .snake:
setGroupStage(randomize: false)
setGroupStage(randomize: false, keepExistingMatches: keepExistingMatches)
case .swiss:
setGroupStage(randomize: true)
setGroupStage(randomize: true, keepExistingMatches: keepExistingMatches)
}
}
}
func setGroupStage(randomize: Bool) {
func setGroupStage(randomize: Bool, keepExistingMatches: Bool = false) {
let groupStages = groupStages()
let numberOfBracketsAsInt = groupStages.count
// let teamsPerBracket = teamsPerBracket
@ -1879,7 +1882,7 @@ defer {
buildGroupStages()
} else {
setGroupStageTeams(randomize: randomize)
groupStages.forEach { $0.buildMatches() }
groupStages.forEach { $0.buildMatches(keepExistingMatches: keepExistingMatches) }
}
}
@ -2195,7 +2198,7 @@ defer {
groupStages().chunked(into: 2).forEach { gss in
let placeCount = i * 2 + 1
let match = Match(round: groupStageLoserBracket.id, index: placeCount, matchFormat: groupStageLoserBracket.matchFormat)
match.name = "\(placeCount)\(placeCount.ordinalFormattedSuffix(feminine: true)) place"
match.setMatchName("\(placeCount)\(placeCount.ordinalFormattedSuffix(feminine: true)) place")
do {
try tournamentStore.matches.addOrUpdate(instance: match)
} catch {

@ -117,8 +117,16 @@ Il est conseillé de vous présenter 10 minutes avant de jouer.\n\nMerci de me c
(DataStore.shared.user.summonsDisplayEntryFee) ? tournament?.entryFeeMessage : nil
}
var linkMessage: String? {
if let tournament, tournament.isPrivate == false, let shareLink = tournament.shareURL(.matches)?.absoluteString {
return "Vous pourrez suivre tous les résultats de ce tournoi sur le site :\n\n".appending(shareLink)
} else {
return nil
}
}
var computedMessage: String {
[entryFeeMessage, message].compacted().map { $0.trimmedMultiline }.joined(separator: "\n\n")
[entryFeeMessage, message, linkMessage].compacted().map { $0.trimmedMultiline }.joined(separator: "\n\n")
}
let intro = reSummon ? "Suite à des forfaits, vous êtes finalement" : "Vous êtes"

@ -86,19 +86,21 @@ struct BracketCallingView: View {
} label: {
Text("Têtes de série")
}
} footer: {
Text("Permet de convoquer par tour du tableau sans avoir tirer au sort les tétes de série. Vous pourrez ensuite confirmer leur horaire plus précis si le tour se joue sur plusieurs rotations. Les équipes ne peuvent pas être considéré comme convoqué au bon horaire en dehors de cet écran tant qu'elles n'ont pas été placé dans le tableau.")
}
ForEach(filteredRounds()) { round in
let seeds = seeds(forRoundIndex: round.index)
let startDate = round.startDate ?? round.playedMatches().first?.startDate
let callSeeds = seeds.filter({ $0.callDate == startDate })
let callSeeds = seeds.filter({ tournament.isStartDateIsDifferentThanCallDate($0, expectedSummonDate: startDate) == false })
if seeds.isEmpty == false {
Section {
NavigationLink {
_roundView(round: round, seeds: seeds)
.environment(tournament)
} label: {
CallView.CallStatusView(count: callSeeds.count, total: seeds.count, startDate: startDate)
CallView.CallStatusView(count: callSeeds.count, total: seeds.count, startDate: startDate, title: "convoquées")
}
} header: {
Text(round.roundTitle())
@ -111,7 +113,7 @@ struct BracketCallingView: View {
}
}
.headerProminence(.increased)
.navigationTitle("Prévision")
.navigationTitle("Pré-convocation")
}
@ViewBuilder
@ -120,12 +122,38 @@ struct BracketCallingView: View {
NavigationLink("Équipes non contactées") {
TeamsCallingView(teams: seeds.filter({ $0.callDate == nil }))
}
let startDate = round.startDate ?? round.playedMatches().first?.startDate
let badCalled = seeds.filter({ tournament.isStartDateIsDifferentThanCallDate($0, expectedSummonDate: startDate) })
if badCalled.isEmpty == false {
Section {
ForEach(badCalled) { team in
CallView.TeamView(team: team)
}
} header: {
HStack {
Text("Mauvais horaire")
Spacer()
Text(badCalled.count.formatted() + " équipe\(badCalled.count.pluralSuffix)")
}
} footer: {
if let startDate {
CallView(teams: badCalled, callDate: startDate, matchFormat: round.matchFormat, roundLabel: round.roundTitle())
}
}
}
Section {
ForEach(seeds) { team in
CallView.TeamView(team: team)
}
} header: {
HStack {
Text(round.roundTitle())
Spacer()
Text(seeds.count.formatted() + " équipe\(seeds.count.pluralSuffix)")
}
}
}
.overlay {

@ -43,7 +43,15 @@ struct CallMessageCustomizationView: View {
}
var computedMessage: String {
[entryFeeMessage, customCallMessageBody].compacted().map { $0.trimmedMultiline }.joined(separator: "\n")
var linkMessage: String? {
if tournament.isPrivate == false, let shareLink = tournament.shareURL(.matches)?.absoluteString {
return "Vous pourrez suivre tous les résultats de ce tournoi sur le site :\n\n".appending(shareLink)
} else {
return nil
}
}
return [entryFeeMessage, customCallMessageBody, linkMessage].compacted().map { $0.trimmedMultiline }.joined(separator: "\n")
}
var finalMessage: String? {
@ -259,7 +267,7 @@ struct CallMessageCustomizationView: View {
}
}.italic().foregroundStyle(.gray)
} header: {
Text("Rendu généré automatiquement")
Text("Exemple généré automatiquement")
}
}

@ -14,6 +14,7 @@ struct CallView: View {
let count: Int
let total: Int
let startDate: Date?
var title: String = "convoquées au bon horaire"
var body: some View {
VStack(spacing: 0) {
@ -32,7 +33,7 @@ struct CallView: View {
Text(startDate.formatted(.dateTime.weekday().day(.twoDigits).month().year()))
}
Spacer()
Text("convoquées au bon horaire")
Text(title)
}
.font(.caption)
.foregroundColor(.secondary)

@ -16,6 +16,13 @@ struct TeamsCallingView: View {
List {
PlayersWithoutContactView(players: teams.flatMap({ $0.unsortedPlayers() }).sorted(by: \.computedRank))
let called = teams.filter { tournament.isStartDateIsDifferentThanCallDate($0) == false }
let justCalled = teams.filter { $0.called() }
let label = "\(justCalled.count.formatted()) / \(teams.count.formatted()) convoquées (dont \(called.count.formatted()) au bon horaire)"
Text(label)
Section {
ForEach(teams) { team in
Menu {
@ -34,6 +41,11 @@ struct TeamsCallingView: View {
.buttonStyle(.plain)
.listRowView(isActive: team.confirmed(), color: .green, hideColorVariation: true)
}
} footer: {
HStack {
Text("Vous pouvez indiquer si une équipe vous a confirmé sa convocation en appuyant sur ")
Image(systemName: "ellipsis.circle").font(.footnote)
}
}
}
.headerProminence(.increased)

@ -248,7 +248,7 @@ struct GroupStagesSettingsView: View {
func menuGenerateGroupStage(_ mode: GroupStageOrderingMode) -> some View {
RowButtonView("Poule \(mode.localizedLabel().lowercased())", role: .destructive, systemImage: mode.systemImage) {
tournament.groupStageOrderingMode = mode
tournament.refreshGroupStages()
tournament.refreshGroupStages(keepExistingMatches: true)
generationDone = true
tournament.shouldVerifyGroupStage = false
_save()

@ -107,7 +107,7 @@ struct LoserBracketFromGroupStageView: View {
let currentGroupStageLoserBracketsInitialPlace = tournament.groupStageLoserBracketsInitialPlace()
let placeCount = displayableMatches.isEmpty ? currentGroupStageLoserBracketsInitialPlace : max(currentGroupStageLoserBracketsInitialPlace, displayableMatches.map({ $0.index }).max()! + 2)
let match = Match(round: loserBracket.id, index: placeCount, matchFormat: loserBracket.matchFormat)
match.name = "\(placeCount)\(placeCount.ordinalFormattedSuffix()) place"
match.setMatchName("\(placeCount)\(placeCount.ordinalFormattedSuffix()) place")
do {
try tournamentStore.matches.addOrUpdate(instance: match)
} catch {
@ -202,7 +202,7 @@ struct GroupStageLoserBracketMatchFooterView: View {
match.index = newIndexValidated
match.name = "\(newIndexValidated)\(newIndexValidated.ordinalFormattedSuffix()) place"
match.setMatchName("\(newIndexValidated)\(newIndexValidated.ordinalFormattedSuffix()) place")
do {

@ -166,7 +166,7 @@ struct MatchSetupView: View {
Text("Libérer")
.underline()
}
} else {
} else if match.isFromLastRound() == false {
ConfirmButtonView(shouldConfirm: shouldConfirm, message: MatchSetupView.confirmationMessage) {
_ = match.lockAndGetSeedPosition(atTeamPosition: teamPosition)
do {

@ -210,7 +210,7 @@ struct PlanningView: View {
uniqueNames.append(name)
}
}
Text(names.joined(separator: ", "))
Text(names.joined(separator: ", ")).lineLimit(1).truncationMode(.tail)
} else {
Text(matches.count.formatted().appending(" matchs"))
}

@ -62,7 +62,7 @@ struct LoserRoundSettingsView: View {
RowButtonView("Synchroniser les noms des matchs") {
let allRoundMatches = upperBracketRound.loserRounds.flatMap({ $0.allMatches
})
allRoundMatches.forEach({ $0.name = $0.roundTitle() })
allRoundMatches.forEach({ $0.setMatchName($0.roundTitle()) })
do {
try self.tournament.tournamentStore.matches.addOrUpdate(contentOfs: allRoundMatches)
} catch {
@ -82,7 +82,7 @@ struct LoserRoundSettingsView: View {
RowButtonView("Créer les matchs de classements", role: .destructive) {
upperBracketRound.round.buildLoserBracket()
upperBracketRound.round.disabledMatches().forEach { match in
match.disableMatch()
match._toggleLoserMatchDisableState(true)
}
do {
try self.tournament.tournamentStore.matches.addOrUpdate(contentOfs: upperBracketRound.round.allLoserRoundMatches())

@ -129,8 +129,8 @@ struct LoserRoundView: View {
private func _refreshNames() {
DispatchQueue.global(qos: .background).async {
let allRoundMatches = loserBracket.allMatches
allRoundMatches.forEach({ $0.name = $0.roundTitle() })
let allRoundMatches = loserBracket.allMatches.filter({ $0.name == nil })
allRoundMatches.forEach({ $0.setMatchName($0.roundTitle()) })
do {
try self.tournament.tournamentStore.matches.addOrUpdate(contentOfs: allRoundMatches)
} catch {

@ -111,11 +111,11 @@ struct RoundSettingsView: View {
//index du match courant pair = position basse du prochain match
match.disabled = true
} else {
match.name = Match.setServerTitle(upperRound: round, matchIndex: currentIndex)
match.setMatchName(Match.setServerTitle(upperRound: round, matchIndex: currentIndex))
currentIndex += 1
}
} else {
match.name = Match.setServerTitle(upperRound: round, matchIndex: currentIndex)
match.setMatchName(Match.setServerTitle(upperRound: round, matchIndex: currentIndex))
currentIndex += 1
}
@ -157,14 +157,16 @@ struct RoundSettingsView: View {
Section {
RowButtonView("Synchroniser les noms des matchs") {
let allRoundMatches = tournament.allRoundMatches()
allRoundMatches.forEach({ $0.name = $0.roundTitle() })
let allRoundMatches = tournament.allLoserRoundMatches()
allRoundMatches.forEach({ $0.setMatchName($0.roundTitle()) })
do {
try self.tournament.tournamentStore.matches.addOrUpdate(contentOfs: allRoundMatches)
} catch {
Logger.error(error)
}
}
} header: {
Text("Matchs de classement")
}
}
.toolbar {

@ -80,7 +80,7 @@ struct RoundView: View {
LabeledContent {
Text(leftToPlay.formatted())
} label: {
Text("Match\(leftToPlay.pluralSuffix) à jouer \(upperRound.title)")
Text("Match\(leftToPlay.pluralSuffix) à jouer en \(upperRound.title)")
}
} footer: {
Text("\(disabledMatchesCount) match\(disabledMatchesCount.pluralSuffix) désactivé\(disabledMatchesCount.pluralSuffix) automatiquement")
@ -332,12 +332,12 @@ struct RoundView: View {
}
private func _save() {
do {
try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: tournament.unsortedTeams())
} catch {
Logger.error(error)
}
// do {
// try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: tournament.unsortedTeams())
// } catch {
// Logger.error(error)
// }
//
if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty {
self.isEditingTournamentSeed.wrappedValue = false
}
@ -349,22 +349,22 @@ struct RoundView: View {
DispatchQueue.global(qos: .background).async {
//todo should be done server side
let rounds = tournament.rounds()
var matchesToUpdate: [Match] = [Match]()
rounds.forEach { round in
let matches = round.playedMatches()
let matches = round.playedMatches().filter({ $0.name == nil })
matches.forEach { match in
match.name = Match.setServerTitle(upperRound: round, matchIndex: match.indexInRound(in: matches))
match.setMatchName(Match.setServerTitle(upperRound: round, matchIndex: match.indexInRound(in: matches)))
}
matchesToUpdate.append(contentsOf: matches)
}
let loserMatches = self.upperRound.loserMatches()
let loserMatches = self.upperRound.loserMatches().filter({ $0.name == nil })
loserMatches.forEach { match in
match.name = match.roundTitle()
match.setMatchName(match.roundTitle())
}
let allRoundMatches = tournament.allRoundMatches()
do {
try tournament.tournamentStore.matches.addOrUpdate(contentOfs: allRoundMatches)
try tournament.tournamentStore.matches.addOrUpdate(contentOfs: matchesToUpdate + loserMatches)
} catch {
Logger.error(error)
}

@ -369,6 +369,19 @@ struct TableStructureView: View {
groupStage.updateGroupStageState()
}
}
if groupStageCount == 0 {
let teams = tournament.unsortedTeams().filter({ $0.inGroupStage() })
teams.forEach { team in
team.groupStagePosition = nil
team.groupStage = nil
}
do {
try tournament.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams)
} catch {
Logger.error(error)
}
}
do {
try dataStore.tournaments.addOrUpdate(instance: tournament)
} catch {

@ -53,8 +53,7 @@ enum CallDestination: Identifiable, Selectable, Equatable {
let allSeedCalled = tournament.seeds().filter({ tournament.isStartDateIsDifferentThanCallDate($0) || $0.callDate == nil })
return allSeedCalled.count
case .brackets(let tournament):
let availableSeeds = tournament.availableSeeds().filter({ $0.callDate == nil })
return availableSeeds.count
return nil
case .groupStages(let tournament):
let allSeedCalled = tournament.groupStageTeams().filter({ tournament.isStartDateIsDifferentThanCallDate($0) || $0.callDate == nil })
return allSeedCalled.count
@ -74,8 +73,7 @@ enum CallDestination: Identifiable, Selectable, Equatable {
let allSeedCalled = tournament.seeds().allSatisfy({ tournament.isStartDateIsDifferentThanCallDate($0) == false })
return allSeedCalled ? .checkmark : nil
case .brackets(let tournament):
let availableSeeds = tournament.availableSeeds().allSatisfy({ $0.called() })
return availableSeeds ? .checkmark : nil
return nil
case .groupStages(let tournament):
let allSeedCalled = tournament.groupStageTeams().allSatisfy({ tournament.isStartDateIsDifferentThanCallDate($0) == false })
return allSeedCalled ? .checkmark : nil

Loading…
Cancel
Save