|
|
|
@ -53,6 +53,7 @@ final class Tournament : ModelObject, Storable { |
|
|
|
var hideTeamsWeight: Bool = false |
|
|
|
var hideTeamsWeight: Bool = false |
|
|
|
var publishTournament: Bool = false |
|
|
|
var publishTournament: Bool = false |
|
|
|
var hidePointsEarned: Bool = false |
|
|
|
var hidePointsEarned: Bool = false |
|
|
|
|
|
|
|
var publishRankings: Bool = false |
|
|
|
|
|
|
|
|
|
|
|
@ObservationIgnored |
|
|
|
@ObservationIgnored |
|
|
|
var navigationPath: [Screen] = [] |
|
|
|
var navigationPath: [Screen] = [] |
|
|
|
@ -100,9 +101,10 @@ final class Tournament : ModelObject, Storable { |
|
|
|
case _hideTeamsWeight = "hideTeamsWeight" |
|
|
|
case _hideTeamsWeight = "hideTeamsWeight" |
|
|
|
case _publishTournament = "publishTournament" |
|
|
|
case _publishTournament = "publishTournament" |
|
|
|
case _hidePointsEarned = "hidePointsEarned" |
|
|
|
case _hidePointsEarned = "hidePointsEarned" |
|
|
|
|
|
|
|
case _publishRankings = "publishRankings" |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
internal init(event: String? = nil, name: String? = nil, startDate: Date = Date(), endDate: Date? = nil, creationDate: Date = Date(), isPrivate: Bool = false, groupStageFormat: MatchFormat? = nil, roundFormat: MatchFormat? = nil, loserRoundFormat: MatchFormat? = nil, groupStageSortMode: GroupStageOrderingMode, groupStageCount: Int = 4, rankSourceDate: Date? = nil, dayDuration: Int = 1, teamCount: Int = 24, teamSorting: TeamSortingType? = nil, federalCategory: TournamentCategory, federalLevelCategory: TournamentLevel, federalAgeCategory: FederalTournamentAge, closedRegistrationDate: Date? = nil, groupStageAdditionalQualified: Int = 0, courtCount: Int = 2, prioritizeClubMembers: Bool = false, qualifiedPerGroupStage: Int = 1, teamsPerGroupStage: Int = 4, entryFee: Double? = nil, additionalEstimationDuration: Int = 0, isDeleted: Bool = false, publishTeams: Bool = false, publishSummons: Bool = false, publishGroupStages: Bool = false, publishBrackets: Bool = false, shouldVerifyBracket: Bool = false, shouldVerifyGroupStage: Bool = false, hideTeamsWeight: Bool = false, publishTournament: Bool = false, hidePointsEarned: Bool = false) { |
|
|
|
internal init(event: String? = nil, name: String? = nil, startDate: Date = Date(), endDate: Date? = nil, creationDate: Date = Date(), isPrivate: Bool = false, groupStageFormat: MatchFormat? = nil, roundFormat: MatchFormat? = nil, loserRoundFormat: MatchFormat? = nil, groupStageSortMode: GroupStageOrderingMode, groupStageCount: Int = 4, rankSourceDate: Date? = nil, dayDuration: Int = 1, teamCount: Int = 24, teamSorting: TeamSortingType? = nil, federalCategory: TournamentCategory, federalLevelCategory: TournamentLevel, federalAgeCategory: FederalTournamentAge, closedRegistrationDate: Date? = nil, groupStageAdditionalQualified: Int = 0, courtCount: Int = 2, prioritizeClubMembers: Bool = false, qualifiedPerGroupStage: Int = 1, teamsPerGroupStage: Int = 4, entryFee: Double? = nil, additionalEstimationDuration: Int = 0, isDeleted: Bool = false, publishTeams: Bool = false, publishSummons: Bool = false, publishGroupStages: Bool = false, publishBrackets: Bool = false, shouldVerifyBracket: Bool = false, shouldVerifyGroupStage: Bool = false, hideTeamsWeight: Bool = false, publishTournament: Bool = false, hidePointsEarned: Bool = false, publishRankings: Bool = false) { |
|
|
|
self.event = event |
|
|
|
self.event = event |
|
|
|
self.name = name |
|
|
|
self.name = name |
|
|
|
self.startDate = startDate |
|
|
|
self.startDate = startDate |
|
|
|
@ -139,6 +141,7 @@ final class Tournament : ModelObject, Storable { |
|
|
|
self.hideTeamsWeight = hideTeamsWeight |
|
|
|
self.hideTeamsWeight = hideTeamsWeight |
|
|
|
self.publishTournament = publishTournament |
|
|
|
self.publishTournament = publishTournament |
|
|
|
self.hidePointsEarned = hidePointsEarned |
|
|
|
self.hidePointsEarned = hidePointsEarned |
|
|
|
|
|
|
|
self.publishRankings = publishRankings |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
required init(from decoder: Decoder) throws { |
|
|
|
required init(from decoder: Decoder) throws { |
|
|
|
@ -182,6 +185,7 @@ final class Tournament : ModelObject, Storable { |
|
|
|
hideTeamsWeight = try container.decodeIfPresent(Bool.self, forKey: ._hideTeamsWeight) ?? false |
|
|
|
hideTeamsWeight = try container.decodeIfPresent(Bool.self, forKey: ._hideTeamsWeight) ?? false |
|
|
|
publishTournament = try container.decodeIfPresent(Bool.self, forKey: ._publishTournament) ?? false |
|
|
|
publishTournament = try container.decodeIfPresent(Bool.self, forKey: ._publishTournament) ?? false |
|
|
|
hidePointsEarned = try container.decodeIfPresent(Bool.self, forKey: ._hidePointsEarned) ?? false |
|
|
|
hidePointsEarned = try container.decodeIfPresent(Bool.self, forKey: ._hidePointsEarned) ?? false |
|
|
|
|
|
|
|
publishRankings = try container.decodeIfPresent(Bool.self, forKey: ._publishRankings) ?? false |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fileprivate static let _numberFormatter: NumberFormatter = NumberFormatter() |
|
|
|
fileprivate static let _numberFormatter: NumberFormatter = NumberFormatter() |
|
|
|
@ -296,6 +300,7 @@ final class Tournament : ModelObject, Storable { |
|
|
|
try container.encode(hideTeamsWeight, forKey: ._hideTeamsWeight) |
|
|
|
try container.encode(hideTeamsWeight, forKey: ._hideTeamsWeight) |
|
|
|
try container.encode(publishTournament, forKey: ._publishTournament) |
|
|
|
try container.encode(publishTournament, forKey: ._publishTournament) |
|
|
|
try container.encode(hidePointsEarned, forKey: ._hidePointsEarned) |
|
|
|
try container.encode(hidePointsEarned, forKey: ._hidePointsEarned) |
|
|
|
|
|
|
|
try container.encode(publishRankings, forKey: ._publishRankings) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fileprivate func _encodePayment(container: inout KeyedEncodingContainer<CodingKeys>) throws { |
|
|
|
fileprivate func _encodePayment(container: inout KeyedEncodingContainer<CodingKeys>) throws { |
|
|
|
@ -1091,6 +1096,17 @@ defer { |
|
|
|
return selected.sorted(by: \.finalRanking!, order: .ascending) |
|
|
|
return selected.sorted(by: \.finalRanking!, order: .ascending) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private func _removeStrings(from dictionary: inout [Int: [String]], stringsToRemove: [String]) { |
|
|
|
|
|
|
|
for key in dictionary.keys { |
|
|
|
|
|
|
|
if var stringArray = dictionary[key] { |
|
|
|
|
|
|
|
// Remove all instances of each string in stringsToRemove |
|
|
|
|
|
|
|
stringArray.removeAll { stringsToRemove.contains($0) } |
|
|
|
|
|
|
|
dictionary[key] = stringArray |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func finalRanking() async -> [Int: [String]] { |
|
|
|
func finalRanking() async -> [Int: [String]] { |
|
|
|
var teams: [Int: [String]] = [:] |
|
|
|
var teams: [Int: [String]] = [:] |
|
|
|
var ids: Set<String> = Set<String>() |
|
|
|
var ids: Set<String> = Set<String>() |
|
|
|
@ -1106,6 +1122,14 @@ defer { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
let others: [Round] = rounds.flatMap { round in |
|
|
|
let others: [Round] = rounds.flatMap { round in |
|
|
|
|
|
|
|
let losers = round.losers() |
|
|
|
|
|
|
|
let minimumFinalPosition = round.seedInterval()?.last ?? teamCount |
|
|
|
|
|
|
|
if teams[minimumFinalPosition] == nil { |
|
|
|
|
|
|
|
teams[minimumFinalPosition] = losers.map { $0.id } |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
teams[minimumFinalPosition]?.append(contentsOf: losers.map { $0.id }) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
print("round", round.roundTitle()) |
|
|
|
print("round", round.roundTitle()) |
|
|
|
let rounds = round.loserRoundsAndChildren().filter { $0.isRankDisabled() == false && $0.hasNextRound() == false } |
|
|
|
let rounds = round.loserRoundsAndChildren().filter { $0.isRankDisabled() == false && $0.hasNextRound() == false } |
|
|
|
print(rounds.count, rounds.map { $0.roundTitle() }) |
|
|
|
print(rounds.count, rounds.map { $0.roundTitle() }) |
|
|
|
@ -1124,14 +1148,21 @@ defer { |
|
|
|
print("losers", losers.count) |
|
|
|
print("losers", losers.count) |
|
|
|
if winners.isEmpty { |
|
|
|
if winners.isEmpty { |
|
|
|
let disabledIds = playedMatches.flatMap({ $0.teamScores.compactMap({ $0.teamRegistration }) }).filter({ ids.contains($0) == false }) |
|
|
|
let disabledIds = playedMatches.flatMap({ $0.teamScores.compactMap({ $0.teamRegistration }) }).filter({ ids.contains($0) == false }) |
|
|
|
|
|
|
|
if disabledIds.isEmpty == false { |
|
|
|
|
|
|
|
_removeStrings(from: &teams, stringsToRemove: disabledIds) |
|
|
|
teams[interval.computedLast] = disabledIds |
|
|
|
teams[interval.computedLast] = disabledIds |
|
|
|
let teamNames : [String] = disabledIds.compactMap { |
|
|
|
let teamNames : [String] = disabledIds.compactMap { |
|
|
|
let t : TeamRegistration? = Store.main.findById($0) |
|
|
|
let t : TeamRegistration? = Store.main.findById($0) |
|
|
|
return t |
|
|
|
return t |
|
|
|
}.map { $0.canonicalName } |
|
|
|
}.map { $0.canonicalName } |
|
|
|
print("winners.isEmpty", "\(interval.computedLast) : ", teamNames) |
|
|
|
print("winners.isEmpty", "\(interval.computedLast) : ", teamNames) |
|
|
|
disabledIds.forEach { ids.insert($0) } |
|
|
|
disabledIds.forEach { |
|
|
|
|
|
|
|
ids.insert($0) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
|
|
|
|
if winners.isEmpty == false { |
|
|
|
|
|
|
|
_removeStrings(from: &teams, stringsToRemove: winners) |
|
|
|
teams[interval.computedFirst + winners.count - 1] = winners |
|
|
|
teams[interval.computedFirst + winners.count - 1] = winners |
|
|
|
let teamNames : [String] = winners.compactMap { |
|
|
|
let teamNames : [String] = winners.compactMap { |
|
|
|
let t: TeamRegistration? = Store.main.findById($0) |
|
|
|
let t: TeamRegistration? = Store.main.findById($0) |
|
|
|
@ -1139,6 +1170,10 @@ defer { |
|
|
|
}.map { $0.canonicalName } |
|
|
|
}.map { $0.canonicalName } |
|
|
|
print("winners", "\(interval.computedFirst + winners.count - 1) : ", teamNames) |
|
|
|
print("winners", "\(interval.computedFirst + winners.count - 1) : ", teamNames) |
|
|
|
winners.forEach { ids.insert($0) } |
|
|
|
winners.forEach { ids.insert($0) } |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if losers.isEmpty == false { |
|
|
|
|
|
|
|
_removeStrings(from: &teams, stringsToRemove: losers) |
|
|
|
teams[interval.computedLast] = losers |
|
|
|
teams[interval.computedLast] = losers |
|
|
|
let loserTeamNames : [String] = losers.compactMap { |
|
|
|
let loserTeamNames : [String] = losers.compactMap { |
|
|
|
let t: TeamRegistration? = Store.main.findById($0) |
|
|
|
let t: TeamRegistration? = Store.main.findById($0) |
|
|
|
@ -1149,6 +1184,7 @@ defer { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
let groupStages = groupStages() |
|
|
|
let groupStages = groupStages() |
|
|
|
let baseRank = teamCount - groupStageSpots() + qualifiedPerGroupStage * groupStageCount + groupStageAdditionalQualified |
|
|
|
let baseRank = teamCount - groupStageSpots() + qualifiedPerGroupStage * groupStageCount + groupStageAdditionalQualified |
|
|
|
|