From a865efc696c51b3fed2826e688975a15b8a8723d Mon Sep 17 00:00:00 2001 From: Raz Date: Tue, 13 May 2025 15:05:52 +0200 Subject: [PATCH 01/15] add accountGroupStageBreakTime groupStageRotationDifference in matchscheduler and uniqueRandomIndex in team reg --- .../Data/Gen/BaseMatchScheduler.swift | 16 +++- PadelClubData/Data/Gen/BaseRound.swift | 23 +++-- .../Data/Gen/BaseTeamRegistration.swift | 9 +- PadelClubData/Data/Gen/MatchScheduler.json | 10 +++ PadelClubData/Data/Gen/TeamRegistration.json | 6 ++ PadelClubData/Data/MatchScheduler.swift | 72 +++++++++++++-- PadelClubData/Data/Tournament.swift | 89 ++++++++++++++++++- 7 files changed, 200 insertions(+), 25 deletions(-) diff --git a/PadelClubData/Data/Gen/BaseMatchScheduler.swift b/PadelClubData/Data/Gen/BaseMatchScheduler.swift index 8e86c9d..a4ef27a 100644 --- a/PadelClubData/Data/Gen/BaseMatchScheduler.swift +++ b/PadelClubData/Data/Gen/BaseMatchScheduler.swift @@ -28,6 +28,8 @@ public class BaseMatchScheduler: BaseModelObject, Storable { public var shouldTryToFillUpCourtsAvailable: Bool = false public var courtsAvailable: Set = Set() public var simultaneousStart: Bool = true + public var groupStageRotationDifference: Int = 0 + public var accountGroupStageBreakTime: Bool = false public init( id: String = Store.randomId(), @@ -45,7 +47,9 @@ public class BaseMatchScheduler: BaseModelObject, Storable { overrideCourtsUnavailability: Bool = false, shouldTryToFillUpCourtsAvailable: Bool = false, courtsAvailable: Set = Set(), - simultaneousStart: Bool = true + simultaneousStart: Bool = true, + groupStageRotationDifference: Int = 0, + accountGroupStageBreakTime: Bool = false ) { super.init() self.id = id @@ -64,6 +68,8 @@ public class BaseMatchScheduler: BaseModelObject, Storable { self.shouldTryToFillUpCourtsAvailable = shouldTryToFillUpCourtsAvailable self.courtsAvailable = courtsAvailable self.simultaneousStart = simultaneousStart + self.groupStageRotationDifference = groupStageRotationDifference + self.accountGroupStageBreakTime = accountGroupStageBreakTime } required public override init() { super.init() @@ -86,6 +92,8 @@ public class BaseMatchScheduler: BaseModelObject, Storable { case _shouldTryToFillUpCourtsAvailable = "shouldTryToFillUpCourtsAvailable" case _courtsAvailable = "courtsAvailable" case _simultaneousStart = "simultaneousStart" + case _groupStageRotationDifference = "groupStageRotationDifference" + case _accountGroupStageBreakTime = "accountGroupStageBreakTime" } required init(from decoder: Decoder) throws { @@ -106,6 +114,8 @@ public class BaseMatchScheduler: BaseModelObject, Storable { self.shouldTryToFillUpCourtsAvailable = try container.decodeIfPresent(Bool.self, forKey: ._shouldTryToFillUpCourtsAvailable) ?? false self.courtsAvailable = try container.decodeIfPresent(Set.self, forKey: ._courtsAvailable) ?? Set() self.simultaneousStart = try container.decodeIfPresent(Bool.self, forKey: ._simultaneousStart) ?? true + self.groupStageRotationDifference = try container.decodeIfPresent(Int.self, forKey: ._groupStageRotationDifference) ?? 0 + self.accountGroupStageBreakTime = try container.decodeIfPresent(Bool.self, forKey: ._accountGroupStageBreakTime) ?? false try super.init(from: decoder) } @@ -127,6 +137,8 @@ public class BaseMatchScheduler: BaseModelObject, Storable { try container.encode(self.shouldTryToFillUpCourtsAvailable, forKey: ._shouldTryToFillUpCourtsAvailable) try container.encode(self.courtsAvailable, forKey: ._courtsAvailable) try container.encode(self.simultaneousStart, forKey: ._simultaneousStart) + try container.encode(self.groupStageRotationDifference, forKey: ._groupStageRotationDifference) + try container.encode(self.accountGroupStageBreakTime, forKey: ._accountGroupStageBreakTime) try super.encode(to: encoder) } @@ -152,6 +164,8 @@ public class BaseMatchScheduler: BaseModelObject, Storable { self.shouldTryToFillUpCourtsAvailable = matchscheduler.shouldTryToFillUpCourtsAvailable self.courtsAvailable = matchscheduler.courtsAvailable self.simultaneousStart = matchscheduler.simultaneousStart + self.groupStageRotationDifference = matchscheduler.groupStageRotationDifference + self.accountGroupStageBreakTime = matchscheduler.accountGroupStageBreakTime } public static func relationships() -> [Relationship] { diff --git a/PadelClubData/Data/Gen/BaseRound.swift b/PadelClubData/Data/Gen/BaseRound.swift index 398283e..0c055f2 100644 --- a/PadelClubData/Data/Gen/BaseRound.swift +++ b/PadelClubData/Data/Gen/BaseRound.swift @@ -7,11 +7,11 @@ import SwiftUI @Observable public class BaseRound: SyncedModelObject, SyncedStorable { - + public static func resourceName() -> String { return "rounds" } public static func tokenExemptedMethods() -> [HTTPMethod] { return [] } public static var copyServerResponse: Bool = false - + public var id: String = Store.randomId() public var tournament: String = "" public var index: Int = 0 @@ -19,13 +19,13 @@ public class BaseRound: SyncedModelObject, SyncedStorable { public var format: MatchFormat? = nil public var startDate: Date? = nil { didSet { - self.didSetStartDate() + self.didSetStartDate() } } public var groupStageLoserBracket: Bool = false public var loserBracketMode: LoserBracketMode = .automatic public var plannedStartDate: Date? = nil - + public init( id: String = Store.randomId(), tournament: String = "", @@ -51,9 +51,9 @@ public class BaseRound: SyncedModelObject, SyncedStorable { required public override init() { super.init() } - + public func didSetStartDate() {} - + public enum CodingKeys: String, CodingKey { case _id = "id" case _tournament = "tournament" @@ -65,7 +65,7 @@ public class BaseRound: SyncedModelObject, SyncedStorable { case _loserBracketMode = "loserBracketMode" case _plannedStartDate = "plannedStartDate" } - + required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() @@ -79,7 +79,7 @@ public class BaseRound: SyncedModelObject, SyncedStorable { self.plannedStartDate = try container.decodeIfPresent(Date.self, forKey: ._plannedStartDate) ?? nil try super.init(from: decoder) } - + public override func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) @@ -93,11 +93,11 @@ public class BaseRound: SyncedModelObject, SyncedStorable { try container.encode(self.plannedStartDate, forKey: ._plannedStartDate) try super.encode(to: encoder) } - + func tournamentValue() -> Tournament? { return Store.main.findById(tournament) } - + public func copy(from other: any Storable) { guard let round = other as? BaseRound else { return } self.id = round.id @@ -110,11 +110,10 @@ public class BaseRound: SyncedModelObject, SyncedStorable { self.loserBracketMode = round.loserBracketMode self.plannedStartDate = round.plannedStartDate } - + public static func relationships() -> [Relationship] { return [ Relationship(type: Tournament.self, keyPath: \BaseRound.tournament), ] } - } diff --git a/PadelClubData/Data/Gen/BaseTeamRegistration.swift b/PadelClubData/Data/Gen/BaseTeamRegistration.swift index b98b267..abdd3a7 100644 --- a/PadelClubData/Data/Gen/BaseTeamRegistration.swift +++ b/PadelClubData/Data/Gen/BaseTeamRegistration.swift @@ -33,6 +33,7 @@ public class BaseTeamRegistration: SyncedModelObject, SyncedStorable { public var qualified: Bool = false public var finalRanking: Int? = nil public var pointsEarned: Int? = nil + public var uniqueRandomIndex: Int = 0 public init( id: String = Store.randomId(), @@ -55,7 +56,8 @@ public class BaseTeamRegistration: SyncedModelObject, SyncedStorable { confirmationDate: Date? = nil, qualified: Bool = false, finalRanking: Int? = nil, - pointsEarned: Int? = nil + pointsEarned: Int? = nil, + uniqueRandomIndex: Int = 0 ) { super.init() self.id = id @@ -79,6 +81,7 @@ public class BaseTeamRegistration: SyncedModelObject, SyncedStorable { self.qualified = qualified self.finalRanking = finalRanking self.pointsEarned = pointsEarned + self.uniqueRandomIndex = uniqueRandomIndex } required public override init() { super.init() @@ -106,6 +109,7 @@ public class BaseTeamRegistration: SyncedModelObject, SyncedStorable { case _qualified = "qualified" case _finalRanking = "finalRanking" case _pointsEarned = "pointsEarned" + case _uniqueRandomIndex = "uniqueRandomIndex" } required init(from decoder: Decoder) throws { @@ -131,6 +135,7 @@ public class BaseTeamRegistration: SyncedModelObject, SyncedStorable { self.qualified = try container.decodeIfPresent(Bool.self, forKey: ._qualified) ?? false self.finalRanking = try container.decodeIfPresent(Int.self, forKey: ._finalRanking) ?? nil self.pointsEarned = try container.decodeIfPresent(Int.self, forKey: ._pointsEarned) ?? nil + self.uniqueRandomIndex = try container.decodeIfPresent(Int.self, forKey: ._uniqueRandomIndex) ?? 0 try super.init(from: decoder) } @@ -157,6 +162,7 @@ public class BaseTeamRegistration: SyncedModelObject, SyncedStorable { try container.encode(self.qualified, forKey: ._qualified) try container.encode(self.finalRanking, forKey: ._finalRanking) try container.encode(self.pointsEarned, forKey: ._pointsEarned) + try container.encode(self.uniqueRandomIndex, forKey: ._uniqueRandomIndex) try super.encode(to: encoder) } @@ -188,6 +194,7 @@ public class BaseTeamRegistration: SyncedModelObject, SyncedStorable { self.qualified = teamregistration.qualified self.finalRanking = teamregistration.finalRanking self.pointsEarned = teamregistration.pointsEarned + self.uniqueRandomIndex = teamregistration.uniqueRandomIndex } public static func relationships() -> [Relationship] { diff --git a/PadelClubData/Data/Gen/MatchScheduler.json b/PadelClubData/Data/Gen/MatchScheduler.json index 132bb36..960b39e 100644 --- a/PadelClubData/Data/Gen/MatchScheduler.json +++ b/PadelClubData/Data/Gen/MatchScheduler.json @@ -84,6 +84,16 @@ "name": "simultaneousStart", "type": "Bool", "defaultValue": "true" + }, + { + "name": "groupStageRotationDifference", + "type": "Int", + "defaultValue": "0" + }, + { + "name": "accountGroupStageBreakTime", + "type": "Bool", + "defaultValue": "false" } ] } diff --git a/PadelClubData/Data/Gen/TeamRegistration.json b/PadelClubData/Data/Gen/TeamRegistration.json index 995d08e..bb9cebc 100644 --- a/PadelClubData/Data/Gen/TeamRegistration.json +++ b/PadelClubData/Data/Gen/TeamRegistration.json @@ -111,6 +111,12 @@ "name": "pointsEarned", "type": "Int", "optional": true + }, + { + "name": "uniqueRandomIndex", + "type": "Int", + "defaultValue": "0", + "optional": false } ] } diff --git a/PadelClubData/Data/MatchScheduler.swift b/PadelClubData/Data/MatchScheduler.swift index e2b2466..a29f2ee 100644 --- a/PadelClubData/Data/MatchScheduler.swift +++ b/PadelClubData/Data/MatchScheduler.swift @@ -105,8 +105,15 @@ final public class MatchScheduler: BaseMatchScheduler, SideStorable { dispatch.timedMatches.forEach { matchSchedule in if let match = matches.first(where: { $0.id == matchSchedule.matchID }) { - let estimatedDuration = match.matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration) - let timeIntervalToAdd = (Double(matchSchedule.rotationIndex)) * Double(estimatedDuration) * 60 + var estimatedDuration = match.matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration) + if accountGroupStageBreakTime { + estimatedDuration += match.matchFormat.breakTime.breakTime * 60 + } + + var timeIntervalToAdd = (Double(matchSchedule.rotationIndex)) * Double(estimatedDuration) * 60 + + timeIntervalToAdd += Double(matchSchedule.rotationIndex) * Double(self.groupStageRotationDifference) * Double(match.matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration)) * 60 + if let startDate = match.groupStageObject?.startDate { let matchStartDate = startDate.addingTimeInterval(timeIntervalToAdd) match.startDate = matchStartDate @@ -129,8 +136,16 @@ final public class MatchScheduler: BaseMatchScheduler, SideStorable { dispatch.timedMatches.forEach { matchSchedule in if let match = matches.first(where: { $0.id == matchSchedule.matchID }) { - let estimatedDuration = match.matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration) - let timeIntervalToAdd = (Double(matchSchedule.rotationIndex)) * Double(estimatedDuration) * 60 + var estimatedDuration = match.matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration) + + if accountGroupStageBreakTime { + estimatedDuration += match.matchFormat.breakTime.breakTime * 60 + } + + var timeIntervalToAdd = (Double(matchSchedule.rotationIndex)) * Double(estimatedDuration) * 60 + + timeIntervalToAdd += Double(matchSchedule.rotationIndex) * Double(self.groupStageRotationDifference) * Double(match.matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration)) * 60 + if let startDate = match.groupStageObject?.startDate { let matchStartDate = startDate.addingTimeInterval(timeIntervalToAdd) match.startDate = matchStartDate @@ -207,8 +222,16 @@ final public class MatchScheduler: BaseMatchScheduler, SideStorable { courtsAvailable.forEach { courtIndex in print("Checking availability for court \(courtIndex) in rotation \(rotationIndex)") if let first = rotationMatches.first(where: { match in - let estimatedDuration = match.matchFormat.getEstimatedDuration(additionalEstimationDuration) - let timeIntervalToAdd = Double(rotationIndex) * Double(estimatedDuration) * 60 + var estimatedDuration = match.matchFormat.getEstimatedDuration(additionalEstimationDuration) + + if accountGroupStageBreakTime { + estimatedDuration += match.matchFormat.breakTime.breakTime * 60 + } + + var timeIntervalToAdd = (Double(rotationIndex)) * Double(estimatedDuration) * 60 + + timeIntervalToAdd += Double(rotationIndex) * Double(self.groupStageRotationDifference) * Double(match.matchFormat.getEstimatedDuration(additionalEstimationDuration)) * 60 + let rotationStartDate: Date = startingDate.addingTimeInterval(timeIntervalToAdd) let courtsUnavailable = courtsUnavailable(startDate: rotationStartDate, duration: match.matchFormat.getEstimatedDuration(additionalEstimationDuration), courtsUnavailability: courtsUnavailability) @@ -822,7 +845,31 @@ final public class MatchScheduler: BaseMatchScheduler, SideStorable { if tournament.groupStages(atStep: 1).isEmpty == false { lastDate = updateGroupStageSchedule(tournament: tournament, atStep: 1, startDate: lastDate) } - return updateBracketSchedule(tournament: tournament, fromRoundId: nil, fromMatchId: nil, startDate: lastDate) + let allMatches = tournament.groupStagesMatches().filter({ $0.hasEnded() == false && $0.hasStarted() == false }) + + let groupMatchesByDay = tournament.groupMatchesByDay(matches: allMatches) + + let countedSet = tournament.matchCountPerDay(matchesByDay: groupMatchesByDay) + + var bracketStartDate = lastDate + print("lastDate", lastDate) + var errorFormat = false + let dates = countedSet.keys + if let first = dates.first, let matchesInLastDate = countedSet[first] { + let totalMatches = matchesInLastDate.totalCount() + let count = matchesInLastDate.count(for: tournament.groupStageMatchFormat) + let totalForThisFormat = tournament.groupStageMatchFormat.maximumMatchPerDay(for: totalMatches) + print(totalMatches, count, totalForThisFormat) + errorFormat = count >= totalForThisFormat + print("bracketStartDate", bracketStartDate) + } + + + if tournament.dayDuration > 1 && (lastDate.timeOfDay == .evening || errorFormat) { + bracketStartDate = lastDate.tomorrowAtNine + } + + return updateBracketSchedule(tournament: tournament, fromRoundId: nil, fromMatchId: nil, startDate: bracketStartDate) } } @@ -886,3 +933,14 @@ extension Match { return groupStageObject._matchUp(for: index).map { groupStageObject.id + "_\($0)" } } } + +// Extension to compute the total count in an NSCountedSet +extension NSCountedSet { + func totalCount() -> Int { + var total = 0 + for element in self { + total += self.count(for: element) + } + return total + } +} diff --git a/PadelClubData/Data/Tournament.swift b/PadelClubData/Data/Tournament.swift index 967c99b..9bb4fed 100644 --- a/PadelClubData/Data/Tournament.swift +++ b/PadelClubData/Data/Tournament.swift @@ -636,7 +636,7 @@ defer { let defaultSorting : [MySortDescriptor] = _defaultSorting() - let _completeTeams = _teams.sorted(using: defaultSorting, order: .ascending).filter { $0.isWildCard() == false }.prefix(teamCount).sorted(using: [.keyPath(\.initialWeight), .keyPath(\.id)], order: .ascending) + let _completeTeams = _teams.sorted(using: defaultSorting, order: .ascending).filter { $0.isWildCard() == false }.prefix(teamCount).sorted(using: [.keyPath(\.initialWeight), .keyPath(\.uniqueRandomIndex), .keyPath(\.id)], order: .ascending) let wcGroupStage = _teams.filter { $0.wildCardGroupStage }.sorted(using: _currentSelectionSorting, order: .ascending) @@ -1777,9 +1777,9 @@ defer { private func _defaultSorting() -> [MySortDescriptor] { switch teamSorting { case .rank: - [.keyPath(\TeamRegistration.initialWeight), .keyPath(\TeamRegistration.id)] + [.keyPath(\TeamRegistration.initialWeight), .keyPath(\TeamRegistration.uniqueRandomIndex), .keyPath(\TeamRegistration.id)] case .inscriptionDate: - [.keyPath(\TeamRegistration.registrationDate!), .keyPath(\TeamRegistration.initialWeight), .keyPath(\TeamRegistration.id)] + [.keyPath(\TeamRegistration.registrationDate!), .keyPath(\TeamRegistration.initialWeight), .keyPath(\TeamRegistration.uniqueRandomIndex), .keyPath(\TeamRegistration.id)] } } @@ -1789,7 +1789,7 @@ defer { && federalTournamentAge == build.age } - private let _currentSelectionSorting : [MySortDescriptor] = [.keyPath(\.weight), .keyPath(\.id)] + private let _currentSelectionSorting : [MySortDescriptor] = [.keyPath(\.weight), .keyPath(\.uniqueRandomIndex), .keyPath(\.id)] private func _matchSchedulers() -> [MatchScheduler] { guard let tournamentStore = self.tournamentStore else { return [] } @@ -2227,6 +2227,87 @@ defer { return Array(leftInterval...maxSize) } + public func groupMatchesByDay(matches: [Match]) -> [Date: [Match]] { + var matchesByDay = [Date: [Match]]() + let calendar = Calendar.current + + for match in matches { + // Extract day/month/year and create a date with only these components + let components = calendar.dateComponents([.year, .month, .day], from: match.computedStartDateForSorting) + let strippedDate = calendar.date(from: components)! + + // Group matches by the strippedDate (only day/month/year) + if matchesByDay[strippedDate] == nil { + matchesByDay[strippedDate] = [] + } + + let shouldIncludeMatch: Bool + switch match.matchType { + case .groupStage: + shouldIncludeMatch = !matchesByDay[strippedDate]!.filter { $0.groupStage != nil }.compactMap { $0.groupStage }.contains(match.groupStage!) + case .bracket: + shouldIncludeMatch = !matchesByDay[strippedDate]!.filter { $0.round != nil }.compactMap { $0.round }.contains(match.round!) + case .loserBracket: + shouldIncludeMatch = true + } + + if shouldIncludeMatch { + matchesByDay[strippedDate]!.append(match) + } + } + + return matchesByDay + } + + public func matchCountPerDay(matchesByDay: [Date: [Match]]) -> [Date: NSCountedSet] { + let days = matchesByDay.keys + var matchCountPerDay = [Date: NSCountedSet]() + + for day in days { + if let matches = matchesByDay[day] { + var groupStageCount = 0 + let countedSet = NSCountedSet() + + for match in matches { + switch match.matchType { + case .groupStage: + if let groupStage = match.groupStageObject { + if groupStageCount < groupStage.size - 1 { + groupStageCount = groupStage.size - 1 + } + } + case .bracket: + countedSet.add(match.matchFormat) + case .loserBracket: + break + } + } + + if groupStageCount > 0 { + for _ in 0.. Date: Fri, 16 May 2025 00:04:44 +0200 Subject: [PATCH 02/15] add csv helper fix refresh table round view --- PadelClubData/Data/TeamRegistration.swift | 6 ++++++ PadelClubData/Data/Tournament.swift | 7 +++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/PadelClubData/Data/TeamRegistration.swift b/PadelClubData/Data/TeamRegistration.swift index 8e38721..3ed6fd0 100644 --- a/PadelClubData/Data/TeamRegistration.swift +++ b/PadelClubData/Data/TeamRegistration.swift @@ -400,6 +400,12 @@ final public class TeamRegistration: BaseTeamRegistration, SideStorable { ].joined(separator: exportFormat.separator()) } } + + public func teamLastNames() -> [String] { + players().map { p in + p.lastName + } + } public var computedRegistrationDate: Date { return registrationDate ?? .distantFuture diff --git a/PadelClubData/Data/Tournament.swift b/PadelClubData/Data/Tournament.swift index 9bb4fed..92dd988 100644 --- a/PadelClubData/Data/Tournament.swift +++ b/PadelClubData/Data/Tournament.swift @@ -243,10 +243,13 @@ defer { case .rawText: return (selectedSortedTeams.compactMap { $0.pasteData(exportFormat) } + ["Liste d'attente"] + waitingListTeams(in: selectedSortedTeams, includingWalkOuts: true).compactMap { $0.pasteData(exportFormat) }).joined(separator: exportFormat.newLineSeparator(2)) case .csv: - let headers = ["N°", "Nom Prénom", "rang", "Nom Prénom", "rang", "poids"].joined(separator: exportFormat.separator()) + let headers = ["N°", "Nom Prénom", "rang", "Nom Prénom", "rang", "poids", "Paire"].joined(separator: exportFormat.separator()) var teamPaste = [headers] for (index, team) in selectedSortedTeams.enumerated() { - teamPaste.append(team.pasteData(exportFormat, index + 1)) + var teamData = team.pasteData(exportFormat, index + 1) + teamData.append(exportFormat.separator()) + teamData.append(team.teamLastNames().joined(separator: " / ")) + teamPaste.append(teamData) } return teamPaste.joined(separator: exportFormat.newLineSeparator()) } From 7f0a53212645fa3ba0a26b7418a97a9d6c217f4e Mon Sep 17 00:00:00 2001 From: Raz Date: Fri, 16 May 2025 08:09:07 +0200 Subject: [PATCH 03/15] fix issue with loser round view --- PadelClubData/Data/Tournament.swift | 30 +++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/PadelClubData/Data/Tournament.swift b/PadelClubData/Data/Tournament.swift index 92dd988..47aa3ac 100644 --- a/PadelClubData/Data/Tournament.swift +++ b/PadelClubData/Data/Tournament.swift @@ -1037,22 +1037,24 @@ defer { } } } - - do { - try self.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: unsortedTeams()) - } catch { - Logger.error(error) - } - - if self.publishRankings == false { - self.publishRankings = true - do { - try DataStore.shared.tournaments.addOrUpdate(instance: self) - } catch { - Logger.error(error) + if rankings.isEmpty == false { + let teams = unsortedTeams() + Task { + await MainActor.run { + self.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams) + } + } + + if self.publishRankings == false { + self.publishRankings = true + } + Task { + await MainActor.run { + DataStore.shared.tournaments.addOrUpdate(instance: self) + } } } - + return rankings } From 5806e4b0218cc610864c2a1f0cb6f9bf899b4a5d Mon Sep 17 00:00:00 2001 From: Laurent Date: Fri, 16 May 2025 11:19:00 +0200 Subject: [PATCH 04/15] avoid club assign at all times --- PadelClubData/Data/DataStore.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/PadelClubData/Data/DataStore.swift b/PadelClubData/Data/DataStore.swift index fd68b54..45ed01e 100644 --- a/PadelClubData/Data/DataStore.swift +++ b/PadelClubData/Data/DataStore.swift @@ -111,6 +111,9 @@ public class DataStore: ObservableObject { } fileprivate func _fixMissingClubCreatorIfNecessary(_ clubsCollection: SyncedCollection) { + + if self.user.clubs.count > 0 { return } + for club in clubsCollection { if let userId = StoreCenter.main.userId, club.creator == nil { club.creator = userId From be1a8e16871c14252c025ede1bef2e1e83ea1d95 Mon Sep 17 00:00:00 2001 From: Raz Date: Sun, 18 May 2025 19:55:00 +0200 Subject: [PATCH 05/15] fix live scoring fix p1500 points repartition fix planning view stuff --- PadelClubData/Data/Tournament.swift | 12 +---- PadelClubData/ViewModel/MatchDescriptor.swift | 4 +- PadelClubData/ViewModel/PadelRule.swift | 46 ++++++++++++------- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/PadelClubData/Data/Tournament.swift b/PadelClubData/Data/Tournament.swift index 47aa3ac..50f9528 100644 --- a/PadelClubData/Data/Tournament.swift +++ b/PadelClubData/Data/Tournament.swift @@ -1039,20 +1039,12 @@ defer { } if rankings.isEmpty == false { let teams = unsortedTeams() - Task { - await MainActor.run { - self.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams) - } - } + self.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams) if self.publishRankings == false { self.publishRankings = true } - Task { - await MainActor.run { - DataStore.shared.tournaments.addOrUpdate(instance: self) - } - } + DataStore.shared.tournaments.addOrUpdate(instance: self) } return rankings diff --git a/PadelClubData/ViewModel/MatchDescriptor.swift b/PadelClubData/ViewModel/MatchDescriptor.swift index a93de28..41865df 100644 --- a/PadelClubData/ViewModel/MatchDescriptor.swift +++ b/PadelClubData/ViewModel/MatchDescriptor.swift @@ -102,8 +102,8 @@ public class MatchDescriptor: ObservableObject { setDescriptors.compactMap { $0.getValue(teamPosition: .two) } } - public var scoreTeamOne: Int { setDescriptors.compactMap { $0.winner }.filter { $0 == .one }.count } - public var scoreTeamTwo: Int { setDescriptors.compactMap { $0.winner }.filter { $0 == .two }.count } + public var scoreTeamOne: Int { setDescriptors.filter({ $0.hasEnded }).compactMap { $0.winner }.filter { $0 == .one }.count } + public var scoreTeamTwo: Int { setDescriptors.filter({ $0.hasEnded }).compactMap { $0.winner }.filter { $0 == .two }.count } public var hasEnded: Bool { return matchFormat.hasEnded(scoreTeamOne: scoreTeamOne, scoreTeamTwo: scoreTeamTwo) diff --git a/PadelClubData/ViewModel/PadelRule.swift b/PadelClubData/ViewModel/PadelRule.swift index 0dd3e1a..e6b077c 100644 --- a/PadelClubData/ViewModel/PadelRule.swift +++ b/PadelClubData/ViewModel/PadelRule.swift @@ -647,7 +647,7 @@ public enum TournamentLevel: Int, Hashable, Codable, CaseIterable, Identifiable case _ where count > 28: return [80,75,72,70,65,63,60,58,55,53,50,48,45,43,40,38,35,33,30,28,25,23,20,18,15,12,10,8,5,3, 1] default: - return [60,50,40,25,10,5] + return [60,50,40,25,10,5, 1] } case .p250: switch count { @@ -698,33 +698,47 @@ public enum TournamentLevel: Int, Hashable, Codable, CaseIterable, Identifiable case _ where count > 28: return [800,750,720,700,650,630,600,580,550,530,500,480,450,430,400,380,350,330,300,280,250,230,200,180,150,120,100,80,50,30, 10] default: - return [600,500,400,250,100,50] + return [600,500,400,250,100,50, 10] } case .p1500: switch count { + case 9...12: + return [975, 825, 750, 525, 375, 300, 225, 150, 75, 45, 15] + case 13...16: + return [1050, 900, 825, 675, 600, 525, 450, 375, 315, 270, 225, 150, 75, 45, 15] + case 17...20: + return [1125, 975, 900, 825, 750, 675, 600, 525, 450, 375, 345, 300, 270, 225, 180, 150, 75, 45, 15] case 21...24: - return [1125,1050,975,900,825,750,705,645,600,555,495,450,420,375,345,300,270,225,180,150,75,45,15] + return [1125, 1050, 975, 900, 825, 750, 705, 645, 600, 555, 495, 450, 420, 375, 345, 300, 270, 225, 180, 150, 75, 45, 15] case 25...28: - return [1200,1125,1050,975,900,825,795,750,720,675,645,600,570,525,495,450,420,375,345,300,270,225,180,150,75,45,15] + return [1200, 1125, 1050, 975, 900, 825, 795, 750, 720, 675, 645, 600, 570, 525, 495, 450, 420, 375, 345, 300, 270, 225, 180, 150, 75, 45, 15] case _ where count > 28: - return [1200,1125,1080,1050,975,945,900,870,825,795,750,720,675,645,600,570,525,495,450,420,375,345,300,270,225,180,150,120,75,45,15] + return [1200, 1125, 1080, 1050, 975, 945, 900, 870, 825, 795, 750, 720, 675, 645, 600, 570, 525, 495, 450, 420, 375, 345, 300, 270, 225, 180, 150, 120, 75, 45, 15] default: - return [1125,975,900,825,750,675,600,525,450,375,345,300,270,225,180,150,75,45,15] - } + return [900, 750, 600, 375, 150, 75, 15] + } case .p2000: - return [1600,1440,1300,1180,1120,1060,1000,880,840,800,760,720,680,640,600,540,520,500,480,460,440,420,400,260,200,40] + switch count { + case 9...12: + return [1300, 1100, 1000, 700, 500, 400, 300, 200, 100, 60, 20] + case 13...16: + return [1400, 1200, 1100, 900, 800, 700, 600, 500, 420, 360, 300, 200, 100, 60, 20] + case 17...20: + return [1500, 1300, 1200, 1100, 1000, 900, 800, 700, 600, 500, 460, 400, 360, 300, 240, 200, 100, 60, 20] + case 21...24: + return [1600, 1400, 1300, 1200, 1100, 1000, 940, 860, 800, 740, 660, 600, 560, 500, 460, 400, 360, 300, 240, 200, 100, 60, 20] + case 25...28: + return [1600, 1500, 1440, 1400, 1300, 1200, 1160, 1100, 1060, 1000, 960, 900, 860, 800, 760, 700, 660, 600, 560, 500, 460, 400, 360, 300, 240, 200, 60] + case _ where count > 28: + return [1600, 1500, 1440, 1400, 1300, 1260, 1200, 1160, 1100, 1060, 1000, 960, 900, 860, 800, 760, 700, 660, 600, 560, 500, 460, 400, 360, 300, 240, 200, 160, 100, 60, 20] + default: + return [1200, 1000, 800, 500, 200, 100, 20] + } } } public var ranges: [PlayersCountRange] { - switch self { - case .p1500: - return [.N16, .N24, .N28, .N32] - case .p2000: - return [.N32] - default: - return PlayersCountRange.allCases - } + return PlayersCountRange.allCases } // enum NewBallSystem { From 290370a5ea45f8acbf6145d6dc6694358c21cd38 Mon Sep 17 00:00:00 2001 From: Raz Date: Mon, 19 May 2025 18:13:45 +0200 Subject: [PATCH 06/15] fix registration import issue fix p2000 stuff --- .../Data/Gen/BasePlayerRegistration.swift | 16 ++++++- PadelClubData/Data/Gen/BaseRound.swift | 25 +++++------ PadelClubData/Data/Gen/BaseTournament.swift | 16 ++++++- .../Data/Gen/PlayerRegistration.json | 10 +++++ PadelClubData/Data/Gen/Tournament.json | 10 +++++ PadelClubData/Data/Match.swift | 3 ++ PadelClubData/Data/PlayerRegistration.swift | 42 ++++++++++++++++++- PadelClubData/Data/Round.swift | 6 ++- PadelClubData/Data/Tournament.swift | 13 ++---- 9 files changed, 115 insertions(+), 26 deletions(-) diff --git a/PadelClubData/Data/Gen/BasePlayerRegistration.swift b/PadelClubData/Data/Gen/BasePlayerRegistration.swift index 9cdfcef..83dbdf6 100644 --- a/PadelClubData/Data/Gen/BasePlayerRegistration.swift +++ b/PadelClubData/Data/Gen/BasePlayerRegistration.swift @@ -37,6 +37,8 @@ public class BasePlayerRegistration: SyncedModelObject, SyncedStorable { public var timeToConfirm: Date? = nil public var registrationStatus: PlayerRegistration.RegistrationStatus = PlayerRegistration.RegistrationStatus.waiting public var paymentId: String? = nil + public var clubCode: String? = nil + public var clubMember: Bool = false public init( id: String = Store.randomId(), @@ -63,7 +65,9 @@ public class BasePlayerRegistration: SyncedModelObject, SyncedStorable { registeredOnline: Bool = false, timeToConfirm: Date? = nil, registrationStatus: PlayerRegistration.RegistrationStatus = PlayerRegistration.RegistrationStatus.waiting, - paymentId: String? = nil + paymentId: String? = nil, + clubCode: String? = nil, + clubMember: Bool = false ) { super.init() self.id = id @@ -91,6 +95,8 @@ public class BasePlayerRegistration: SyncedModelObject, SyncedStorable { self.timeToConfirm = timeToConfirm self.registrationStatus = registrationStatus self.paymentId = paymentId + self.clubCode = clubCode + self.clubMember = clubMember } required public override init() { super.init() @@ -122,6 +128,8 @@ public class BasePlayerRegistration: SyncedModelObject, SyncedStorable { case _timeToConfirm = "timeToConfirm" case _registrationStatus = "registrationStatus" case _paymentId = "paymentId" + case _clubCode = "clubCode" + case _clubMember = "clubMember" } required init(from decoder: Decoder) throws { @@ -151,6 +159,8 @@ public class BasePlayerRegistration: SyncedModelObject, SyncedStorable { self.timeToConfirm = try container.decodeIfPresent(Date.self, forKey: ._timeToConfirm) ?? nil self.registrationStatus = try container.decodeIfPresent(PlayerRegistration.RegistrationStatus.self, forKey: ._registrationStatus) ?? PlayerRegistration.RegistrationStatus.waiting self.paymentId = try container.decodeIfPresent(String.self, forKey: ._paymentId) ?? nil + self.clubCode = try container.decodeIfPresent(String.self, forKey: ._clubCode) ?? nil + self.clubMember = try container.decodeIfPresent(Bool.self, forKey: ._clubMember) ?? false try super.init(from: decoder) } @@ -181,6 +191,8 @@ public class BasePlayerRegistration: SyncedModelObject, SyncedStorable { try container.encode(self.timeToConfirm, forKey: ._timeToConfirm) try container.encode(self.registrationStatus, forKey: ._registrationStatus) try container.encode(self.paymentId, forKey: ._paymentId) + try container.encode(self.clubCode, forKey: ._clubCode) + try container.encode(self.clubMember, forKey: ._clubMember) try super.encode(to: encoder) } @@ -216,6 +228,8 @@ public class BasePlayerRegistration: SyncedModelObject, SyncedStorable { self.timeToConfirm = playerregistration.timeToConfirm self.registrationStatus = playerregistration.registrationStatus self.paymentId = playerregistration.paymentId + self.clubCode = playerregistration.clubCode + self.clubMember = playerregistration.clubMember } public static func relationships() -> [Relationship] { diff --git a/PadelClubData/Data/Gen/BaseRound.swift b/PadelClubData/Data/Gen/BaseRound.swift index 0c055f2..4ca7cc2 100644 --- a/PadelClubData/Data/Gen/BaseRound.swift +++ b/PadelClubData/Data/Gen/BaseRound.swift @@ -7,11 +7,11 @@ import SwiftUI @Observable public class BaseRound: SyncedModelObject, SyncedStorable { - + public static func resourceName() -> String { return "rounds" } public static func tokenExemptedMethods() -> [HTTPMethod] { return [] } public static var copyServerResponse: Bool = false - + public var id: String = Store.randomId() public var tournament: String = "" public var index: Int = 0 @@ -19,13 +19,13 @@ public class BaseRound: SyncedModelObject, SyncedStorable { public var format: MatchFormat? = nil public var startDate: Date? = nil { didSet { - self.didSetStartDate() + self.didSetStartDate() } } public var groupStageLoserBracket: Bool = false public var loserBracketMode: LoserBracketMode = .automatic public var plannedStartDate: Date? = nil - + public init( id: String = Store.randomId(), tournament: String = "", @@ -51,9 +51,9 @@ public class BaseRound: SyncedModelObject, SyncedStorable { required public override init() { super.init() } - + public func didSetStartDate() {} - + public enum CodingKeys: String, CodingKey { case _id = "id" case _tournament = "tournament" @@ -65,7 +65,7 @@ public class BaseRound: SyncedModelObject, SyncedStorable { case _loserBracketMode = "loserBracketMode" case _plannedStartDate = "plannedStartDate" } - + required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() @@ -79,7 +79,7 @@ public class BaseRound: SyncedModelObject, SyncedStorable { self.plannedStartDate = try container.decodeIfPresent(Date.self, forKey: ._plannedStartDate) ?? nil try super.init(from: decoder) } - + public override func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.id, forKey: ._id) @@ -93,11 +93,11 @@ public class BaseRound: SyncedModelObject, SyncedStorable { try container.encode(self.plannedStartDate, forKey: ._plannedStartDate) try super.encode(to: encoder) } - + func tournamentValue() -> Tournament? { return Store.main.findById(tournament) } - + public func copy(from other: any Storable) { guard let round = other as? BaseRound else { return } self.id = round.id @@ -110,10 +110,11 @@ public class BaseRound: SyncedModelObject, SyncedStorable { self.loserBracketMode = round.loserBracketMode self.plannedStartDate = round.plannedStartDate } - + public static func relationships() -> [Relationship] { return [ Relationship(type: Tournament.self, keyPath: \BaseRound.tournament), ] } -} + +} \ No newline at end of file diff --git a/PadelClubData/Data/Gen/BaseTournament.swift b/PadelClubData/Data/Gen/BaseTournament.swift index d837d1e..cbc2bf0 100644 --- a/PadelClubData/Data/Gen/BaseTournament.swift +++ b/PadelClubData/Data/Gen/BaseTournament.swift @@ -81,6 +81,8 @@ public class BaseTournament: SyncedModelObject, SyncedStorable { public var isTemplate: Bool = false public var publishProg: Bool = false public var showTeamsInProg: Bool = false + public var clubMemberFeeDeduction: Double? = nil + public var unregisterDeltaInHours: Int = 24 public init( id: String = Store.randomId(), @@ -151,7 +153,9 @@ public class BaseTournament: SyncedModelObject, SyncedStorable { isCorporateTournament: Bool = false, isTemplate: Bool = false, publishProg: Bool = false, - showTeamsInProg: Bool = false + showTeamsInProg: Bool = false, + clubMemberFeeDeduction: Double? = nil, + unregisterDeltaInHours: Int = 24 ) { super.init() self.id = id @@ -223,6 +227,8 @@ public class BaseTournament: SyncedModelObject, SyncedStorable { self.isTemplate = isTemplate self.publishProg = publishProg self.showTeamsInProg = showTeamsInProg + self.clubMemberFeeDeduction = clubMemberFeeDeduction + self.unregisterDeltaInHours = unregisterDeltaInHours } required public override init() { super.init() @@ -300,6 +306,8 @@ public class BaseTournament: SyncedModelObject, SyncedStorable { case _isTemplate = "isTemplate" case _publishProg = "publishProg" case _showTeamsInProg = "showTeamsInProg" + case _clubMemberFeeDeduction = "clubMemberFeeDeduction" + case _unregisterDeltaInHours = "unregisterDeltaInHours" } private static func _decodePayment(container: KeyedDecodingContainer) throws -> TournamentPayment? { @@ -440,6 +448,8 @@ public class BaseTournament: SyncedModelObject, SyncedStorable { self.isTemplate = try container.decodeIfPresent(Bool.self, forKey: ._isTemplate) ?? false self.publishProg = try container.decodeIfPresent(Bool.self, forKey: ._publishProg) ?? false self.showTeamsInProg = try container.decodeIfPresent(Bool.self, forKey: ._showTeamsInProg) ?? false + self.clubMemberFeeDeduction = try container.decodeIfPresent(Double.self, forKey: ._clubMemberFeeDeduction) ?? nil + self.unregisterDeltaInHours = try container.decodeIfPresent(Int.self, forKey: ._unregisterDeltaInHours) ?? 24 try super.init(from: decoder) } @@ -514,6 +524,8 @@ public class BaseTournament: SyncedModelObject, SyncedStorable { try container.encode(self.isTemplate, forKey: ._isTemplate) try container.encode(self.publishProg, forKey: ._publishProg) try container.encode(self.showTeamsInProg, forKey: ._showTeamsInProg) + try container.encode(self.clubMemberFeeDeduction, forKey: ._clubMemberFeeDeduction) + try container.encode(self.unregisterDeltaInHours, forKey: ._unregisterDeltaInHours) try super.encode(to: encoder) } @@ -593,6 +605,8 @@ public class BaseTournament: SyncedModelObject, SyncedStorable { self.isTemplate = tournament.isTemplate self.publishProg = tournament.publishProg self.showTeamsInProg = tournament.showTeamsInProg + self.clubMemberFeeDeduction = tournament.clubMemberFeeDeduction + self.unregisterDeltaInHours = tournament.unregisterDeltaInHours } public static func relationships() -> [Relationship] { diff --git a/PadelClubData/Data/Gen/PlayerRegistration.json b/PadelClubData/Data/Gen/PlayerRegistration.json index 72dc484..5e723f0 100644 --- a/PadelClubData/Data/Gen/PlayerRegistration.json +++ b/PadelClubData/Data/Gen/PlayerRegistration.json @@ -131,6 +131,16 @@ "name": "paymentId", "type": "String", "optional": true + }, + { + "name": "clubCode", + "type": "String", + "optional": true + }, + { + "name": "clubMember", + "type": "Bool", + "defaultValue": "false" } ] } diff --git a/PadelClubData/Data/Gen/Tournament.json b/PadelClubData/Data/Gen/Tournament.json index 6851964..e058cc5 100644 --- a/PadelClubData/Data/Gen/Tournament.json +++ b/PadelClubData/Data/Gen/Tournament.json @@ -358,6 +358,16 @@ "name": "showTeamsInProg", "type": "Bool", "defaultValue": "false" + }, + { + "name": "clubMemberFeeDeduction", + "type": "Double", + "optional": true + }, + { + "name": "unregisterDeltaInHours", + "type": "Int", + "defaultValue": "24" } ] } diff --git a/PadelClubData/Data/Match.swift b/PadelClubData/Data/Match.swift index 96eb38d..4cdb3ec 100644 --- a/PadelClubData/Data/Match.swift +++ b/PadelClubData/Data/Match.swift @@ -39,6 +39,9 @@ final public class Match: BaseMatch, SideStorable { // MARK: - DidSet public override func didSetStartDate() { + if hasStarted() { + return + } if self.roundValue()?.tournamentObject()?.hasStarted() == false { plannedStartDate = startDate } else if self.groupStageValue()?.tournamentObject()?.hasStarted() == false { diff --git a/PadelClubData/Data/PlayerRegistration.swift b/PadelClubData/Data/PlayerRegistration.swift index 5451fef..3c59cfb 100644 --- a/PadelClubData/Data/PlayerRegistration.swift +++ b/PadelClubData/Data/PlayerRegistration.swift @@ -26,7 +26,7 @@ final public class PlayerRegistration: BasePlayerRegistration, SideStorable { } } - public init(teamRegistration: String? = nil, firstName: String, lastName: String, licenceId: String? = nil, rank: Int? = nil, paymentType: PlayerPaymentType? = nil, sex: PlayerSexType? = nil, tournamentPlayed: Int? = nil, points: Double? = nil, clubName: String? = nil, ligueName: String? = nil, assimilation: String? = nil, phoneNumber: String? = nil, email: String? = nil, birthdate: String? = nil, computedRank: Int = 0, source: PlayerRegistration.PlayerDataSource? = nil, hasArrived: Bool = false, coach: Bool = false, captain: Bool = false, registeredOnline: Bool = false, timeToConfirm: Date? = nil, registrationStatus: PlayerRegistration.RegistrationStatus = PlayerRegistration.RegistrationStatus.waiting, paymentId: String? = nil) { + public init(teamRegistration: String? = nil, firstName: String, lastName: String, licenceId: String? = nil, rank: Int? = nil, paymentType: PlayerPaymentType? = nil, sex: PlayerSexType? = nil, tournamentPlayed: Int? = nil, points: Double? = nil, clubName: String? = nil, ligueName: String? = nil, assimilation: String? = nil, phoneNumber: String? = nil, email: String? = nil, birthdate: String? = nil, computedRank: Int = 0, source: PlayerRegistration.PlayerDataSource? = nil, hasArrived: Bool = false, coach: Bool = false, captain: Bool = false, registeredOnline: Bool = false, timeToConfirm: Date? = nil, registrationStatus: PlayerRegistration.RegistrationStatus = PlayerRegistration.RegistrationStatus.waiting, paymentId: String? = nil, clubCode: String? = nil, clubMember: Bool = false) { super.init() self.teamRegistration = teamRegistration self.firstName = firstName @@ -52,6 +52,8 @@ final public class PlayerRegistration: BasePlayerRegistration, SideStorable { self.timeToConfirm = timeToConfirm self.registrationStatus = registrationStatus self.paymentId = paymentId + self.clubCode = clubCode + self.clubMember = clubMember } required init(from decoder: any Decoder) throws { @@ -204,6 +206,13 @@ final public class PlayerRegistration: BasePlayerRegistration, SideStorable { } } + public func setClubMember(for tournament: Tournament) { + guard let clubCode, clubCode.isEmpty == false, let code = tournament.eventObject()?.clubObject()?.code, code.isEmpty == false else { + return + } + self.clubMember = clubCode.replaceCharactersFromSet(characterSet: .whitespaces).caseInsensitiveCompare(code.replaceCharactersFromSet(characterSet: .whitespaces)) == .orderedSame + } + public func isMalePlayer() -> Bool { sex == .male } @@ -230,6 +239,37 @@ final public class PlayerRegistration: BasePlayerRegistration, SideStorable { registrationStatus = .confirmed } + public func paidAmount(_ tournament: Tournament, accountForGiftOrForfeit: Bool = false) -> Double { + if accountForGiftOrForfeit == false, paymentType == .gift { + return 0.0 + } + if accountForGiftOrForfeit == false, paymentType == .forfeit { + return 0.0 + } + if hasPaid(), let entryFee = tournament.entryFee { + if clubMember, let clubMemberFeeDeduction = tournament.clubMemberFeeDeduction { + return entryFee - clubMemberFeeDeduction + } else { + return entryFee + } + } else { + return 0.0 + } + } + + public func remainingAmount(_ tournament: Tournament) -> Double { + if let entryFee = tournament.entryFee { + if clubMember, let clubMemberFeeDeduction = tournament.clubMemberFeeDeduction { + return entryFee - clubMemberFeeDeduction - paidAmount(tournament, accountForGiftOrForfeit: true) + } else { + return entryFee - paidAmount(tournament, accountForGiftOrForfeit: true) + } + } else { + return 0.0 + } + } + + public enum PlayerDataSource: Int, Codable { case frenchFederation = 0 case beachPadel = 1 diff --git a/PadelClubData/Data/Round.swift b/PadelClubData/Data/Round.swift index 053cbbd..2f57d41 100644 --- a/PadelClubData/Data/Round.swift +++ b/PadelClubData/Data/Round.swift @@ -818,6 +818,7 @@ defer { public func disableUnplayedLoserBracketMatches() { let m = self._matches() + let roundTitle = roundTitle() if let previousRound = self.previousRound() { m.forEach { match in let prmc = previousRound.previousMatches(previousRound: previousRound, match: match).filter({ @@ -839,6 +840,7 @@ defer { match.disabled = false } } + match.setMatchName(roundTitle) } } else if let upperRound = self.parentRound { m.forEach { match in @@ -851,10 +853,12 @@ defer { } else { match.disabled = false } + match.setMatchName(roundTitle) } } + tournamentStore?.matches.addOrUpdate(contentOfs: m) - + loserRounds().forEach { loserRound in loserRound.disableUnplayedLoserBracketMatches() } diff --git a/PadelClubData/Data/Tournament.swift b/PadelClubData/Data/Tournament.swift index 50f9528..41371c8 100644 --- a/PadelClubData/Data/Tournament.swift +++ b/PadelClubData/Data/Tournament.swift @@ -1228,19 +1228,11 @@ defer { } public func earnings() -> Double { - if let entryFee { - return Double(selectedPlayers().filter { $0.hasPaid() }.count) * entryFee - } else { - return 0.0 - } + return selectedPlayers().compactMap { $0.paidAmount(self) }.reduce(0.0, +) } public func remainingAmount() -> Double { - if let entryFee { - return Double(selectedPlayers().filter { $0.hasPaid() == false }.count) * entryFee - } else { - return 0.0 - } + return selectedPlayers().compactMap { $0.remainingAmount(self) }.reduce(0.0, +) } public func paidCompletion() -> Double { @@ -1736,6 +1728,7 @@ defer { public func setupRegistrationSettings(templateTournament: Tournament) { self.enableOnlineRegistration = templateTournament.enableOnlineRegistration + self.unregisterDeltaInHours = templateTournament.unregisterDeltaInHours self.accountIsRequired = templateTournament.accountIsRequired self.licenseIsRequired = templateTournament.licenseIsRequired self.minimumPlayerPerTeam = templateTournament.minimumPlayerPerTeam From 85442dc822b91dd8058283ba097b1dbf56b5ccf1 Mon Sep 17 00:00:00 2001 From: Raz Date: Mon, 19 May 2025 19:18:31 +0200 Subject: [PATCH 07/15] piste au lieu de terrains --- PadelClubData/Data/Court.swift | 2 +- PadelClubData/Data/DrawLog.swift | 2 +- PadelClubData/Data/Tournament.swift | 2 +- PadelClubData/ViewModel/PadelRule.swift | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/PadelClubData/Data/Court.swift b/PadelClubData/Data/Court.swift index db5f774..645fed8 100644 --- a/PadelClubData/Data/Court.swift +++ b/PadelClubData/Data/Court.swift @@ -45,7 +45,7 @@ final public class Court: BaseCourt { } public static func courtIndexedTitle(atIndex index: Int) -> String { - ("Terrain #" + (index + 1).formatted()) + ("Piste #" + (index + 1).formatted()) } public func clubObject() -> Club? { diff --git a/PadelClubData/Data/DrawLog.swift b/PadelClubData/Data/DrawLog.swift index 0671d4b..e9ce078 100644 --- a/PadelClubData/Data/DrawLog.swift +++ b/PadelClubData/Data/DrawLog.swift @@ -91,7 +91,7 @@ public enum DrawType: Int, Codable { case .groupStage: return "Poule" case .court: - return "Terrain" + return "Piste" } } } diff --git a/PadelClubData/Data/Tournament.swift b/PadelClubData/Data/Tournament.swift index 41371c8..515a012 100644 --- a/PadelClubData/Data/Tournament.swift +++ b/PadelClubData/Data/Tournament.swift @@ -1347,7 +1347,7 @@ defer { } public func settingsDescriptionLocalizedLabel() -> String { - [courtCount.formatted() + " terrain\(courtCount.pluralSuffix)", entryFeeMessage].joined(separator: ", ") + [courtCount.formatted() + " piste\(courtCount.pluralSuffix)", entryFeeMessage].joined(separator: ", ") } public func structureDescriptionLocalizedLabel() -> String { diff --git a/PadelClubData/ViewModel/PadelRule.swift b/PadelClubData/ViewModel/PadelRule.swift index e6b077c..152d23d 100644 --- a/PadelClubData/ViewModel/PadelRule.swift +++ b/PadelClubData/ViewModel/PadelRule.swift @@ -1860,7 +1860,7 @@ public enum AnimationType: Int, CaseIterable, Hashable, Identifiable { case .playerAnimation: return "Chaque joueur joue avec quelqu'un de différent à chaque rotation (8 à 12 joueurs)" case .upAndDown: - return "Les gagnants montent sur le terrain d'à côté, les perdants descendent" + return "Les gagnants montent sur la piste d'à côté, les perdants descendent" case .brawl: return "A chaque rotation, les gagnants de la rotation précédente se jouent entre eux" } From 0aa515ea9fde6f82856adbe32328767b3e4c5f71 Mon Sep 17 00:00:00 2001 From: Raz Date: Tue, 20 May 2025 11:36:21 +0200 Subject: [PATCH 08/15] add loser bracket in pdf --- PadelClubData/Data/Match.swift | 17 +++++++++++++++-- PadelClubData/Data/Round.swift | 4 ++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/PadelClubData/Data/Match.swift b/PadelClubData/Data/Match.swift index 4cdb3ec..4c27dd1 100644 --- a/PadelClubData/Data/Match.swift +++ b/PadelClubData/Data/Match.swift @@ -191,13 +191,22 @@ defer { } } - public func scoreLabel() -> String { + public func scoreLabel(winnerFirst: Bool = false) -> String { if hasWalkoutTeam() == true { return "WO" } + let scoreOne = teamScore(.one)?.score?.components(separatedBy: ",") ?? [] let scoreTwo = teamScore(.two)?.score?.components(separatedBy: ",") ?? [] - let tuples = zip(scoreOne, scoreTwo).map { ($0, $1) } + var tuples = zip(scoreOne, scoreTwo).map { ($0, $1) } + + if winnerFirst { + if teamWon(atPosition: .one) { + + } else { + tuples = zip(scoreTwo, scoreOne).map { ($0, $1) } + } + } let scores = tuples.map { $0 + "/" + $1 }.joined(separator: " ") return scores } @@ -1066,6 +1075,10 @@ defer { [MatchSpot(match: self, teamPosition: .one), MatchSpot(match: self, teamPosition: .two)] } + public func initialStartDate() -> Date? { + plannedStartDate ?? startDate + } + func insertOnServer() { self.tournamentStore?.matches.writeChangeAndInsertOnServer(instance: self) for teamScore in self.teamScores { diff --git a/PadelClubData/Data/Round.swift b/PadelClubData/Data/Round.swift index 2f57d41..b77df39 100644 --- a/PadelClubData/Data/Round.swift +++ b/PadelClubData/Data/Round.swift @@ -907,6 +907,10 @@ defer { _cachedLoserRoundsAndChildren = nil } + public func initialStartDate() -> Date? { + plannedStartDate ?? startDate ?? playedMatches().first?.initialStartDate() + } + public override func deleteDependencies(store: Store, shouldBeSynchronized: Bool) { store.deleteDependencies(type: Match.self, shouldBeSynchronized: shouldBeSynchronized) { $0.round == self.id } From 12055d9e6f1ecfa85f0621d9b272f86d9ceafade Mon Sep 17 00:00:00 2001 From: Raz Date: Thu, 22 May 2025 09:08:09 +0200 Subject: [PATCH 09/15] add event link sharing --- PadelClubData/Data/Event.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/PadelClubData/Data/Event.swift b/PadelClubData/Data/Event.swift index 855710a..ad560f0 100644 --- a/PadelClubData/Data/Event.swift +++ b/PadelClubData/Data/Event.swift @@ -100,6 +100,10 @@ final public class Event: BaseEvent { tournaments.filter({ $0.isFree() == false && $0.isCanceled == false && $0.isDeleted == false }) } + public func shareURL() -> URL? { + return URL(string: URLs.main.url.appending(path: "event/\(id)").absoluteString.removingPercentEncoding!) + } + func insertOnServer() throws { DataStore.shared.events.writeChangeAndInsertOnServer(instance: self) for tournament in self.tournaments { From 95c95d1a87b18cb8844ec8b4114f71ad13e0ff92 Mon Sep 17 00:00:00 2001 From: Raz Date: Thu, 22 May 2025 12:24:11 +0200 Subject: [PATCH 10/15] clean up init --- PadelClubData/Data/Court.swift | 20 ------ PadelClubData/Data/CustomUser.swift | 22 ------ PadelClubData/Data/DateInterval.swift | 29 -------- .../Data/Gen/BaseTeamRegistration.swift | 6 +- PadelClubData/Data/Gen/TeamRegistration.json | 1 + PadelClubData/Data/Match.swift | 18 +---- PadelClubData/Data/MatchScheduler.swift | 39 +--------- PadelClubData/Data/PlayerRegistration.swift | 40 +---------- PadelClubData/Data/Round.swift | 38 +++------- PadelClubData/Data/TeamRegistration.swift | 72 +++++++++---------- PadelClubData/Data/TeamScore.swift | 25 +------ PadelClubData/Data/Tournament.swift | 6 +- 12 files changed, 56 insertions(+), 260 deletions(-) diff --git a/PadelClubData/Data/Court.swift b/PadelClubData/Data/Court.swift index 645fed8..4ff3cab 100644 --- a/PadelClubData/Data/Court.swift +++ b/PadelClubData/Data/Court.swift @@ -16,26 +16,6 @@ final public class Court: BaseCourt { lhs.id == rhs.id } - public init(index: Int, club: String, name: String? = nil, exitAllowed: Bool = false, indoor: Bool = false) { - - super.init() - - self.index = index - self.lastUpdate = Date() - self.club = club - self.name = name - self.exitAllowed = exitAllowed - self.indoor = indoor - } - - required init(from decoder: Decoder) throws { - try super.init(from: decoder) - } - - required public init() { - super.init() - } - public func courtTitle() -> String { self.name ?? courtIndexTitle() } diff --git a/PadelClubData/Data/CustomUser.swift b/PadelClubData/Data/CustomUser.swift index 30f3274..0275ea8 100644 --- a/PadelClubData/Data/CustomUser.swift +++ b/PadelClubData/Data/CustomUser.swift @@ -97,28 +97,6 @@ public class CustomUser: BaseCustomUser, UserBase { // // var deviceId: String? - public init(username: String, email: String, firstName: String, lastName: String, phone: String?, country: String?, loserBracketMode: LoserBracketMode = .automatic) { - super.init(username: username, email: email, firstName: firstName, lastName: lastName, phone: phone, country: country, loserBracketMode: loserBracketMode) - - -// self.lastUpdate = Date() -// self.username = username -// self.firstName = firstName -// self.lastName = lastName -// self.email = email -// self.phone = phone -// self.country = country -// self.loserBracketMode = loserBracketMode - } - - required init(from decoder: Decoder) throws { - try super.init(from: decoder) - } - - required public init() { - super.init() - } - public func uuid() throws -> UUID { if let uuid = UUID(uuidString: self.id) { return uuid diff --git a/PadelClubData/Data/DateInterval.swift b/PadelClubData/Data/DateInterval.swift index c141a1a..295e864 100644 --- a/PadelClubData/Data/DateInterval.swift +++ b/PadelClubData/Data/DateInterval.swift @@ -12,35 +12,6 @@ import LeStorage @Observable final public class DateInterval: BaseDateInterval { -// static func resourceName() -> String { return "date-intervals" } -// static func tokenExemptedMethods() -> [HTTPMethod] { return [] } -// static func filterByStoreIdentifier() -> Bool { return false } -// static var relationshipNames: [String] = [] -// -// var id: String = Store.randomId() -// var lastUpdate: Date -// var event: String -// var courtIndex: Int -// var startDate: Date -// var endDate: Date - - public init(event: String, courtIndex: Int, startDate: Date, endDate: Date) { - super.init(event: event, courtIndex: courtIndex, startDate: startDate, endDate: endDate) -// self.lastUpdate = Date() -// self.event = event -// self.courtIndex = courtIndex -// self.startDate = startDate -// self.endDate = endDate - } - - required init(from decoder: any Decoder) throws { - try super.init(from: decoder) - } - - required public init() { - super.init() - } - var range: Range { startDate.. = Set(), -// simultaneousStart: Bool = true) { -//>>>>>>> main -// self.tournament = tournament -// self.timeDifferenceLimit = timeDifferenceLimit -// self.loserBracketRotationDifference = loserBracketRotationDifference -// self.upperBracketRotationDifference = upperBracketRotationDifference -// self.accountUpperBracketBreakTime = accountUpperBracketBreakTime -// self.accountLoserBracketBreakTime = accountLoserBracketBreakTime -// self.randomizeCourts = randomizeCourts -// self.rotationDifferenceIsImportant = rotationDifferenceIsImportant -// self.shouldHandleUpperRoundSlice = shouldHandleUpperRoundSlice -// self.shouldEndRoundBeforeStartingNext = shouldEndRoundBeforeStartingNext -// self.groupStageChunkCount = groupStageChunkCount -// self.overrideCourtsUnavailability = overrideCourtsUnavailability -// self.shouldTryToFillUpCourtsAvailable = shouldTryToFillUpCourtsAvailable -// self.courtsAvailable = courtsAvailable -// self.simultaneousStart = simultaneousStart -// } - + var courtsUnavailability: [DateInterval]? { guard let event = tournamentObject()?.eventObject() else { return nil } return event.courtsUnavailability + (overrideCourtsUnavailability ? [] : event.tournamentsCourtsUsed(exluding: tournament)) diff --git a/PadelClubData/Data/PlayerRegistration.swift b/PadelClubData/Data/PlayerRegistration.swift index 3c59cfb..773934b 100644 --- a/PadelClubData/Data/PlayerRegistration.swift +++ b/PadelClubData/Data/PlayerRegistration.swift @@ -24,45 +24,7 @@ final public class PlayerRegistration: BasePlayerRegistration, SideStorable { return "créé par vous-même" } } - } - - public init(teamRegistration: String? = nil, firstName: String, lastName: String, licenceId: String? = nil, rank: Int? = nil, paymentType: PlayerPaymentType? = nil, sex: PlayerSexType? = nil, tournamentPlayed: Int? = nil, points: Double? = nil, clubName: String? = nil, ligueName: String? = nil, assimilation: String? = nil, phoneNumber: String? = nil, email: String? = nil, birthdate: String? = nil, computedRank: Int = 0, source: PlayerRegistration.PlayerDataSource? = nil, hasArrived: Bool = false, coach: Bool = false, captain: Bool = false, registeredOnline: Bool = false, timeToConfirm: Date? = nil, registrationStatus: PlayerRegistration.RegistrationStatus = PlayerRegistration.RegistrationStatus.waiting, paymentId: String? = nil, clubCode: String? = nil, clubMember: Bool = false) { - super.init() - self.teamRegistration = teamRegistration - self.firstName = firstName - self.lastName = lastName - self.licenceId = licenceId - self.rank = rank - self.paymentType = paymentType - self.sex = sex - self.tournamentPlayed = tournamentPlayed - self.points = points - self.clubName = clubName - self.ligueName = ligueName - self.assimilation = assimilation - self.phoneNumber = phoneNumber - self.email = email - self.birthdate = birthdate - self.computedRank = computedRank - self.source = source - self.hasArrived = hasArrived - self.coach = coach - self.captain = captain - self.registeredOnline = registeredOnline - self.timeToConfirm = timeToConfirm - self.registrationStatus = registrationStatus - self.paymentId = paymentId - self.clubCode = clubCode - self.clubMember = clubMember - } - - required init(from decoder: any Decoder) throws { - try super.init(from: decoder) - } - - required public init() { - super.init() - } + } public var tournamentStore: TournamentStore? { guard let storeId else { diff --git a/PadelClubData/Data/Round.swift b/PadelClubData/Data/Round.swift index b77df39..e00dfed 100644 --- a/PadelClubData/Data/Round.swift +++ b/PadelClubData/Data/Round.swift @@ -21,28 +21,6 @@ final public class Round: BaseRound, SideStorable { @ObservationIgnored private var _cachedLoserRoundsAndChildren: [Round]? - public init(tournament: String, index: Int, parent: String? = nil, matchFormat: MatchFormat? = nil, startDate: Date? = nil, groupStageLoserBracket: Bool = false, loserBracketMode: LoserBracketMode = .automatic) { - - super.init(tournament: tournament, index: index, parent: parent, format: matchFormat, startDate: startDate, groupStageLoserBracket: groupStageLoserBracket, loserBracketMode: loserBracketMode) - -// self.lastUpdate = Date() -// self.tournament = tournament -// self.index = index -// self.parent = parent -// self.format = matchFormat -// self.startDate = startDate -// self.groupStageLoserBracket = groupStageLoserBracket -// self.loserBracketMode = loserBracketMode - } - - required init(from decoder: any Decoder) throws { - try super.init(from: decoder) - } - - required public init() { - super.init() - } - // MARK: - DidSet public override func didSetStartDate() { @@ -265,7 +243,7 @@ defer { } #endif let parentRound = parentRound - if let parentRound, parentRound.parent == nil, groupStageLoserBracket == false, parentRound.loserBracketMode != .automatic { + if let parentRound, parentRound.parent == nil, isGroupStageLoserBracket() == false, parentRound.loserBracketMode != .automatic { return nil } @@ -286,7 +264,7 @@ defer { #endif let parentRound = parentRound - if let parentRound, parentRound.parent == nil, groupStageLoserBracket == false, parentRound.loserBracketMode != .automatic { + if let parentRound, parentRound.parent == nil, isGroupStageLoserBracket() == false, parentRound.loserBracketMode != .automatic { return nil } @@ -647,7 +625,7 @@ defer { } public func roundTitle(_ displayStyle: DisplayStyle = .wide, initialMode: Bool = false) -> String { - if groupStageLoserBracket { + if isGroupStageLoserBracket() { return "Classement Poules" } @@ -752,11 +730,11 @@ defer { } public func isUpperBracket() -> Bool { - return parent == nil && groupStageLoserBracket == false + return parent == nil && isGroupStageLoserBracket() == false } public func isLoserBracket() -> Bool { - return parent != nil || groupStageLoserBracket + return parent != nil || isGroupStageLoserBracket() } public func deleteLoserBracket() { @@ -793,7 +771,7 @@ defer { var titles = [String: String]() let rounds = (0.. Bool { + groupStageLoserBracket == true + } + public override func deleteDependencies(store: Store, shouldBeSynchronized: Bool) { store.deleteDependencies(type: Match.self, shouldBeSynchronized: shouldBeSynchronized) { $0.round == self.id } diff --git a/PadelClubData/Data/TeamRegistration.swift b/PadelClubData/Data/TeamRegistration.swift index 3ed6fd0..f07fcb4 100644 --- a/PadelClubData/Data/TeamRegistration.swift +++ b/PadelClubData/Data/TeamRegistration.swift @@ -14,38 +14,38 @@ final public class TeamRegistration: BaseTeamRegistration, SideStorable { @ObservationIgnored var _cachedRestingTime: (Bool, Date?)? - public init( - tournament: String, groupStage: String? = nil, registrationDate: Date? = nil, - callDate: Date? = nil, bracketPosition: Int? = nil, groupStagePosition: Int? = nil, - comment: String? = nil, source: String? = nil, sourceValue: String? = nil, - logo: String? = nil, name: String? = nil, walkOut: Bool = false, - wildCardBracket: Bool = false, wildCardGroupStage: Bool = false, weight: Int = 0, - lockedWeight: Int? = nil, confirmationDate: Date? = nil, qualified: Bool = false - ) { - - super.init() - - // self.storeId = tournament - self.tournament = tournament - self.groupStage = groupStage - self.registrationDate = registrationDate ?? Date() - self.callDate = callDate - self.bracketPosition = bracketPosition - self.groupStagePosition = groupStagePosition - self.comment = comment - self.source = source - self.sourceValue = sourceValue - self.logo = logo - self.name = name - self.walkOut = walkOut - self.wildCardBracket = wildCardBracket - self.wildCardGroupStage = wildCardGroupStage - self.weight = weight - self.lockedWeight = lockedWeight - self.confirmationDate = confirmationDate - self.qualified = qualified - } - +// public init( +// tournament: String, groupStage: String? = nil, registrationDate: Date? = nil, +// callDate: Date? = nil, bracketPosition: Int? = nil, groupStagePosition: Int? = nil, +// comment: String? = nil, source: String? = nil, sourceValue: String? = nil, +// logo: String? = nil, name: String? = nil, walkOut: Bool = false, +// wildCardBracket: Bool = false, wildCardGroupStage: Bool = false, weight: Int = 0, +// lockedWeight: Int? = nil, confirmationDate: Date? = nil, qualified: Bool = false +// ) { +// +// super.init() +// +// // self.storeId = tournament +// self.tournament = tournament +// self.groupStage = groupStage +// self.registrationDate = registrationDate ?? Date() +// self.callDate = callDate +// self.bracketPosition = bracketPosition +// self.groupStagePosition = groupStagePosition +// self.comment = comment +// self.source = source +// self.sourceValue = sourceValue +// self.logo = logo +// self.name = name +// self.walkOut = walkOut +// self.wildCardBracket = wildCardBracket +// self.wildCardGroupStage = wildCardGroupStage +// self.weight = weight +// self.lockedWeight = lockedWeight +// self.confirmationDate = confirmationDate +// self.qualified = qualified +// } +// public func hasRegisteredOnline() -> Bool { players().anySatisfy({ $0.registeredOnline }) } @@ -72,14 +72,6 @@ final public class TeamRegistration: BaseTeamRegistration, SideStorable { walkOut } - required init(from decoder: any Decoder) throws { - try super.init(from: decoder) - } - - required public init() { - super.init() - } - public var tournamentStore: TournamentStore? { return TournamentLibrary.shared.store(tournamentId: self.tournament) } diff --git a/PadelClubData/Data/TeamScore.swift b/PadelClubData/Data/TeamScore.swift index c5fb7c7..1c21ae4 100644 --- a/PadelClubData/Data/TeamScore.swift +++ b/PadelClubData/Data/TeamScore.swift @@ -27,33 +27,14 @@ final public class TeamScore: BaseTeamScore, SideStorable { // var luckyLoser: Int? // // var storeId: String? = nil - - init(match: String, teamRegistration: String? = nil, score: String? = nil, walkOut: Int? = nil, luckyLoser: Int? = nil) { - super.init(match: match, teamRegistration: teamRegistration, score: score, walkOut: walkOut, luckyLoser: luckyLoser) - -// self.match = match -// self.teamRegistration = teamRegistration -//// self.playerRegistrations = playerRegistrations -// self.score = score -// self.walkOut = walkOut -// self.luckyLoser = luckyLoser - } - init(match: String, team: TeamRegistration?) { - super.init(match: match) + convenience init(match: String, team: TeamRegistration?) { + self.init(match: match) if let team { self.teamRegistration = team.id } } - - required init(from decoder: Decoder) throws { - try super.init(from: decoder) - } - - required public init() { - super.init() - } - + var tournamentStore: TournamentStore? { guard let storeId else { fatalError("missing store id for \(String(describing: type(of: self)))") diff --git a/PadelClubData/Data/Tournament.swift b/PadelClubData/Data/Tournament.swift index 515a012..3f6529a 100644 --- a/PadelClubData/Data/Tournament.swift +++ b/PadelClubData/Data/Tournament.swift @@ -1415,7 +1415,7 @@ defer { let matchCount = RoundRule.numberOfMatches(forTeams: minimalBracketTeamCount ?? bracketTeamCount()) let rounds = (0.. Round? { - self.tournamentStore?.rounds.first(where: { $0.groupStageLoserBracket }) + self.tournamentStore?.rounds.first(where: { $0.isGroupStageLoserBracket() }) } public func groupStageLoserBracketsInitialPlace() -> Int { @@ -2026,7 +2026,7 @@ defer { public func addNewRound(_ roundIndex: Int) async { await MainActor.run { - let round = Round(tournament: id, index: roundIndex, matchFormat: matchFormat) + let round = Round(tournament: id, index: roundIndex, format: matchFormat) let matchCount = RoundRule.numberOfMatches(forRoundIndex: roundIndex) let matchStartIndex = RoundRule.matchIndex(fromRoundIndex: roundIndex) let nextRound = round.nextRound() From 888e02581a4e60eef33949b2880bcee9a8abae01 Mon Sep 17 00:00:00 2001 From: Raz Date: Thu, 22 May 2025 15:36:47 +0200 Subject: [PATCH 11/15] v1.2.30 --- PadelClubData/Data/MatchScheduler.swift | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/PadelClubData/Data/MatchScheduler.swift b/PadelClubData/Data/MatchScheduler.swift index 242a6a2..a5e5ee7 100644 --- a/PadelClubData/Data/MatchScheduler.swift +++ b/PadelClubData/Data/MatchScheduler.swift @@ -30,6 +30,14 @@ final public class MatchScheduler: BaseMatchScheduler, SideStorable { return Store.main.findById(tournament) } + func getGroupStageRotationDifference() -> Int { + if rotationDifferenceIsImportant { + return groupStageRotationDifference + } else { + return 0 + } + } + @discardableResult public func updateGroupStageSchedule(tournament: Tournament, specificGroupStage: GroupStage? = nil, atStep step: Int = 0, startDate: Date? = nil) -> Date { let computedGroupStageChunkCount = groupStageChunkCount ?? tournament.getGroupStageChunkValue() @@ -75,7 +83,7 @@ final public class MatchScheduler: BaseMatchScheduler, SideStorable { var timeIntervalToAdd = (Double(matchSchedule.rotationIndex)) * Double(estimatedDuration) * 60 - timeIntervalToAdd += Double(matchSchedule.rotationIndex) * Double(self.groupStageRotationDifference) * Double(match.matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration)) * 60 + timeIntervalToAdd += Double(matchSchedule.rotationIndex) * Double(self.getGroupStageRotationDifference()) * Double(match.matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration)) * 60 if let startDate = match.groupStageObject?.startDate { let matchStartDate = startDate.addingTimeInterval(timeIntervalToAdd) @@ -107,7 +115,7 @@ final public class MatchScheduler: BaseMatchScheduler, SideStorable { var timeIntervalToAdd = (Double(matchSchedule.rotationIndex)) * Double(estimatedDuration) * 60 - timeIntervalToAdd += Double(matchSchedule.rotationIndex) * Double(self.groupStageRotationDifference) * Double(match.matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration)) * 60 + timeIntervalToAdd += Double(matchSchedule.rotationIndex) * Double(self.getGroupStageRotationDifference()) * Double(match.matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration)) * 60 if let startDate = match.groupStageObject?.startDate { let matchStartDate = startDate.addingTimeInterval(timeIntervalToAdd) @@ -193,7 +201,7 @@ final public class MatchScheduler: BaseMatchScheduler, SideStorable { var timeIntervalToAdd = (Double(rotationIndex)) * Double(estimatedDuration) * 60 - timeIntervalToAdd += Double(rotationIndex) * Double(self.groupStageRotationDifference) * Double(match.matchFormat.getEstimatedDuration(additionalEstimationDuration)) * 60 + timeIntervalToAdd += Double(rotationIndex) * Double(self.getGroupStageRotationDifference()) * Double(match.matchFormat.getEstimatedDuration(additionalEstimationDuration)) * 60 let rotationStartDate: Date = startingDate.addingTimeInterval(timeIntervalToAdd) From eb112375fd1f8fb7b7e7314c28e8e9d218dcfd1d Mon Sep 17 00:00:00 2001 From: Raz Date: Thu, 22 May 2025 18:36:49 +0200 Subject: [PATCH 12/15] fix event sharing stuff --- PadelClubData/Data/Event.swift | 51 +++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/PadelClubData/Data/Event.swift b/PadelClubData/Data/Event.swift index ad560f0..15eb2ab 100644 --- a/PadelClubData/Data/Event.swift +++ b/PadelClubData/Data/Event.swift @@ -48,7 +48,7 @@ final public class Event: BaseEvent { // MARK: - Computed dependencies public var tournaments: [Tournament] { - DataStore.shared.tournaments.filter { $0.event == self.id && $0.isDeleted == false } + DataStore.shared.tournaments.filter { $0.event == self.id && $0.isDeleted == false }.sorted(by: \.startDate) } public func clubObject() -> Club? { @@ -103,6 +103,55 @@ final public class Event: BaseEvent { public func shareURL() -> URL? { return URL(string: URLs.main.url.appending(path: "event/\(id)").absoluteString.removingPercentEncoding!) } + + public func formattedDateInterval() -> String { + let tournaments = self.tournaments + guard !tournaments.isEmpty else { + return "" // Handle empty tournament list + } + + let firstTournament = tournaments.first! + let lastTournament = tournaments.last! + + // Helper to check if two dates are on the same day + func isSameDay(date1: Date, date2: Date) -> Bool { + return Calendar.current.isDate(date1, inSameDayAs: date2) + } + + // Scenario 1: Only one tournament + if tournaments.count == 1 { + return firstTournament.startDate.formattedAsDate() + } + + // Scenario 2: Multiple tournaments, but all start and end on the same day (single-day event overall) + let overallEventIsSingleDay = isSameDay(date1: firstTournament.startDate, date2: lastTournament.startDate) + + if overallEventIsSingleDay { + return firstTournament.startDate.formattedAsDate() + } + + // Scenario 3: Multiple tournaments, spanning multiple days (requires "Du ... au ...") + let startDay = firstTournament.startDate.localizedDay() + let endDay = lastTournament.startDate.localizedDay() + + let startMonthYear = firstTournament.startDate.monthYearFormatted + let endMonthYear = lastTournament.startDate.monthYearFormatted + + let calendar = Calendar.current + let firstStartComponents = calendar.dateComponents([.year, .month], from: firstTournament.startDate) + let lastEndComponents = calendar.dateComponents([.year, .month], from: lastTournament.startDate) + + if firstStartComponents.year == lastEndComponents.year && firstStartComponents.month == lastEndComponents.month { + // Same month and year: "Du 13 au 15 Juin 2025" + return "Du \(startDay) au \(endDay) \(startMonthYear)" + } else if firstStartComponents.year == lastEndComponents.year { + // Different months, same year: "Du 13 Juin au 15 Juillet 2025" + return "Du \(startDay) \(startMonthYear) au \(endDay) \(endMonthYear)" + } else { + // Different years: "Du 13 Juin 2025 au 15 Juin 2026" + return "Du \(startDay) \(startMonthYear) au \(endDay) \(endMonthYear)" + } + } func insertOnServer() throws { DataStore.shared.events.writeChangeAndInsertOnServer(instance: self) From 004405638f717592b3a205fe892d2514627a42e8 Mon Sep 17 00:00:00 2001 From: Raz Date: Thu, 22 May 2025 19:09:59 +0200 Subject: [PATCH 13/15] fix crash --- PadelClubData/Data/Tournament.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PadelClubData/Data/Tournament.swift b/PadelClubData/Data/Tournament.swift index 3f6529a..d2b3331 100644 --- a/PadelClubData/Data/Tournament.swift +++ b/PadelClubData/Data/Tournament.swift @@ -1377,7 +1377,7 @@ defer { guard let tournamentStore = self.tournamentStore else { return } let teams = (0.. Date: Mon, 26 May 2025 17:02:51 +0200 Subject: [PATCH 14/15] fix seed random pick missing for more than 32 players fix missing parameters taken from template --- PadelClubData/Data/Tournament.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/PadelClubData/Data/Tournament.swift b/PadelClubData/Data/Tournament.swift index d2b3331..6304f72 100644 --- a/PadelClubData/Data/Tournament.swift +++ b/PadelClubData/Data/Tournament.swift @@ -386,7 +386,8 @@ defer { case 24...31: return SeedInterval(first: 25, last: 32) default: - return nil + let pow = Int(pow(2.0, ceil(log2(Double(alreadySetupSeeds))))) + return SeedInterval(first: pow + 1, last: pow * 2) } } @@ -1735,13 +1736,15 @@ defer { self.maximumPlayerPerTeam = templateTournament.maximumPlayerPerTeam self.waitingListLimit = templateTournament.waitingListLimit self.teamCountLimit = templateTournament.teamCountLimit + self.teamCount = templateTournament.teamCount self.enableOnlinePayment = templateTournament.enableOnlinePayment self.onlinePaymentIsMandatory = templateTournament.onlinePaymentIsMandatory self.enableOnlinePaymentRefund = templateTournament.enableOnlinePaymentRefund self.stripeAccountId = templateTournament.stripeAccountId self.enableTimeToConfirm = templateTournament.enableTimeToConfirm self.isCorporateTournament = templateTournament.isCorporateTournament - + self.clubMemberFeeDeduction = templateTournament.clubMemberFeeDeduction + self.unregisterDeltaInHours = templateTournament.unregisterDeltaInHours if self.registrationDateLimit == nil, templateTournament.registrationDateLimit != nil { self.registrationDateLimit = startDate.truncateMinutesAndSeconds() } From 0bb502094a654709b923ec39698a75cd388fb75a Mon Sep 17 00:00:00 2001 From: Razmig Sarkissian Date: Thu, 29 May 2025 07:13:10 +0200 Subject: [PATCH 15/15] fix issue with stripe account onboarding --- .../xcshareddata/swiftpm/Package.resolved | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 PadelClubData.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/PadelClubData.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/PadelClubData.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..8232c62 --- /dev/null +++ b/PadelClubData.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,24 @@ +{ + "originHash" : "86635982bfc3fdf16c7186cd33e77b4dc10a24ff9127aaee5f4f0a3676ae2966", + "pins" : [ + { + "identity" : "swift-algorithms", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-algorithms.git", + "state" : { + "revision" : "87e50f483c54e6efd60e885f7f5aa946cee68023", + "version" : "1.2.1" + } + }, + { + "identity" : "swift-numerics", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-numerics.git", + "state" : { + "revision" : "e0ec0f5f3af6f3e4d5e7a19d2af26b481acb6ba8", + "version" : "1.0.3" + } + } + ], + "version" : 3 +}