fix calling stuff and round lag efficiency

paca_championship
Raz 1 year ago
parent 138551c32a
commit 17f371e450
  1. 25
      PadelClub/Data/GroupStage.swift
  2. 21
      PadelClub/Data/Match.swift
  3. 37
      PadelClub/Data/Round.swift
  4. 23
      PadelClub/Data/Tournament.swift
  5. 10
      PadelClub/Utils/ContactManager.swift
  6. 36
      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,17 +115,24 @@ final class GroupStage: ModelObject, Storable {
return match return match
} }
func buildMatches() { func buildMatches(keepExistingMatches: Bool = false) {
_removeMatches()
var matches = [Match]()
var teamScores = [TeamScore]() var teamScores = [TeamScore]()
var matches = [Match]()
for i in 0..<_numberOfMatchesToBuild() { if keepExistingMatches == false {
let newMatch = self._createMatch(index: i) _removeMatches()
// let newMatch = Match(groupStage: self.id, index: i, matchFormat: self.matchFormat, name: localizedMatchUpLabel(for: i))
teamScores.append(contentsOf: newMatch.createTeamScores()) for i in 0..<_numberOfMatchesToBuild() {
matches.append(newMatch) let newMatch = self._createMatch(index: i)
// let newMatch = Match(groupStage: self.id, index: i, matchFormat: self.matchFormat, name: localizedMatchUpLabel(for: i))
teamScores.append(contentsOf: newMatch.createTeamScores())
matches.append(newMatch)
}
} else {
for match in _matches() {
match.resetTeamScores(outsideOf: [])
teamScores.append(contentsOf: match.createTeamScores())
}
} }
do { do {

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

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

@ -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 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 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 return Calendar.current.compare(summonDate, to: expectedSummonDate, toGranularity: .minute) != ComparisonResult.orderedSame
} }
@ -1609,7 +1610,9 @@ defer {
func callStatus() async -> TournamentStatus { func callStatus() async -> TournamentStatus {
let selectedSortedTeams = selectedSortedTeams() let selectedSortedTeams = selectedSortedTeams()
let called = selectedSortedTeams.filter { isStartDateIsDifferentThanCallDate($0) == false } 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 completion = (Double(called.count) / Double(selectedSortedTeams.count))
let completionLabel = completion.isNaN ? "" : completion.formatted(.percent.precision(.fractionLength(0))) let completionLabel = completion.isNaN ? "" : completion.formatted(.percent.precision(.fractionLength(0)))
return TournamentStatus(label: label, completion: completionLabel) return TournamentStatus(label: label, completion: completionLabel)
@ -1852,7 +1855,7 @@ defer {
} }
} }
func refreshGroupStages() { func refreshGroupStages(keepExistingMatches: Bool = false) {
unsortedTeams().forEach { team in unsortedTeams().forEach { team in
team.groupStage = nil team.groupStage = nil
team.groupStagePosition = nil team.groupStagePosition = nil
@ -1861,16 +1864,16 @@ defer {
if groupStageCount > 0 { if groupStageCount > 0 {
switch groupStageOrderingMode { switch groupStageOrderingMode {
case .random: case .random:
setGroupStage(randomize: true) setGroupStage(randomize: true, keepExistingMatches: keepExistingMatches)
case .snake: case .snake:
setGroupStage(randomize: false) setGroupStage(randomize: false, keepExistingMatches: keepExistingMatches)
case .swiss: 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 groupStages = groupStages()
let numberOfBracketsAsInt = groupStages.count let numberOfBracketsAsInt = groupStages.count
// let teamsPerBracket = teamsPerBracket // let teamsPerBracket = teamsPerBracket
@ -1879,7 +1882,7 @@ defer {
buildGroupStages() buildGroupStages()
} else { } else {
setGroupStageTeams(randomize: randomize) 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 groupStages().chunked(into: 2).forEach { gss in
let placeCount = i * 2 + 1 let placeCount = i * 2 + 1
let match = Match(round: groupStageLoserBracket.id, index: placeCount, matchFormat: groupStageLoserBracket.matchFormat) 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 { do {
try tournamentStore.matches.addOrUpdate(instance: match) try tournamentStore.matches.addOrUpdate(instance: match)
} catch { } 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 (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 { 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" let intro = reSummon ? "Suite à des forfaits, vous êtes finalement" : "Vous êtes"

@ -86,19 +86,21 @@ struct BracketCallingView: View {
} label: { } label: {
Text("Têtes de série") 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 ForEach(filteredRounds()) { round in
let seeds = seeds(forRoundIndex: round.index) let seeds = seeds(forRoundIndex: round.index)
let startDate = round.startDate ?? round.playedMatches().first?.startDate 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 { if seeds.isEmpty == false {
Section { Section {
NavigationLink { NavigationLink {
_roundView(round: round, seeds: seeds) _roundView(round: round, seeds: seeds)
.environment(tournament) .environment(tournament)
} label: { } 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: { } header: {
Text(round.roundTitle()) Text(round.roundTitle())
@ -111,7 +113,7 @@ struct BracketCallingView: View {
} }
} }
.headerProminence(.increased) .headerProminence(.increased)
.navigationTitle("Prévision") .navigationTitle("Pré-convocation")
} }
@ViewBuilder @ViewBuilder
@ -120,12 +122,38 @@ struct BracketCallingView: View {
NavigationLink("Équipes non contactées") { NavigationLink("Équipes non contactées") {
TeamsCallingView(teams: seeds.filter({ $0.callDate == nil })) 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 { Section {
ForEach(seeds) { team in ForEach(seeds) { team in
CallView.TeamView(team: team) CallView.TeamView(team: team)
} }
} header: { } header: {
Text(round.roundTitle()) HStack {
Text(round.roundTitle())
Spacer()
Text(seeds.count.formatted() + " équipe\(seeds.count.pluralSuffix)")
}
} }
} }
.overlay { .overlay {

@ -43,7 +43,15 @@ struct CallMessageCustomizationView: View {
} }
var computedMessage: String { 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? { var finalMessage: String? {
@ -259,7 +267,7 @@ struct CallMessageCustomizationView: View {
} }
}.italic().foregroundStyle(.gray) }.italic().foregroundStyle(.gray)
} header: { } header: {
Text("Rendu généré automatiquement") Text("Exemple généré automatiquement")
} }
} }

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

@ -16,6 +16,13 @@ struct TeamsCallingView: View {
List { List {
PlayersWithoutContactView(players: teams.flatMap({ $0.unsortedPlayers() }).sorted(by: \.computedRank)) 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 { Section {
ForEach(teams) { team in ForEach(teams) { team in
Menu { Menu {
@ -34,6 +41,11 @@ struct TeamsCallingView: View {
.buttonStyle(.plain) .buttonStyle(.plain)
.listRowView(isActive: team.confirmed(), color: .green, hideColorVariation: true) .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) .headerProminence(.increased)

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

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

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

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

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

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

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

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

@ -369,6 +369,19 @@ struct TableStructureView: View {
groupStage.updateGroupStageState() 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 { do {
try dataStore.tournaments.addOrUpdate(instance: tournament) try dataStore.tournaments.addOrUpdate(instance: tournament)
} catch { } catch {

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

Loading…
Cancel
Save