sync3
Laurent 5 months ago
commit c3d3c701bb
  1. 24
      PadelClubData.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
  2. 22
      PadelClubData/Data/Court.swift
  3. 22
      PadelClubData/Data/CustomUser.swift
  4. 3
      PadelClubData/Data/DataStore.swift
  5. 29
      PadelClubData/Data/DateInterval.swift
  6. 2
      PadelClubData/Data/DrawLog.swift
  7. 55
      PadelClubData/Data/Event.swift
  8. 16
      PadelClubData/Data/Gen/BaseMatchScheduler.swift
  9. 16
      PadelClubData/Data/Gen/BasePlayerRegistration.swift
  10. 15
      PadelClubData/Data/Gen/BaseTeamRegistration.swift
  11. 16
      PadelClubData/Data/Gen/BaseTournament.swift
  12. 10
      PadelClubData/Data/Gen/MatchScheduler.json
  13. 10
      PadelClubData/Data/Gen/PlayerRegistration.json
  14. 7
      PadelClubData/Data/Gen/TeamRegistration.json
  15. 10
      PadelClubData/Data/Gen/Tournament.json
  16. 59
      PadelClubData/Data/Match.swift
  17. 117
      PadelClubData/Data/MatchScheduler.swift
  18. 74
      PadelClubData/Data/PlayerRegistration.swift
  19. 46
      PadelClubData/Data/Round.swift
  20. 78
      PadelClubData/Data/TeamRegistration.swift
  21. 23
      PadelClubData/Data/TeamScore.swift
  22. 140
      PadelClubData/Data/Tournament.swift
  23. 4
      PadelClubData/ViewModel/MatchDescriptor.swift
  24. 38
      PadelClubData/ViewModel/PadelRule.swift

@ -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
}

@ -16,26 +16,6 @@ final public class Court: BaseCourt {
lhs.id == rhs.id 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 { public func courtTitle() -> String {
self.name ?? courtIndexTitle() self.name ?? courtIndexTitle()
} }
@ -45,7 +25,7 @@ final public class Court: BaseCourt {
} }
public static func courtIndexedTitle(atIndex index: Int) -> String { public static func courtIndexedTitle(atIndex index: Int) -> String {
("Terrain #" + (index + 1).formatted()) ("Piste #" + (index + 1).formatted())
} }
public func clubObject() -> Club? { public func clubObject() -> Club? {

@ -97,28 +97,6 @@ public class CustomUser: BaseCustomUser, UserBase {
// //
// var deviceId: String? // 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 { public func uuid() throws -> UUID {
if let uuid = UUID(uuidString: self.id) { if let uuid = UUID(uuidString: self.id) {
return uuid return uuid

@ -111,6 +111,9 @@ public class DataStore: ObservableObject {
} }
fileprivate func _fixMissingClubCreatorIfNecessary(_ clubsCollection: SyncedCollection<Club>) { fileprivate func _fixMissingClubCreatorIfNecessary(_ clubsCollection: SyncedCollection<Club>) {
if self.user.clubs.count > 0 { return }
for club in clubsCollection { for club in clubsCollection {
if let userId = StoreCenter.main.userId, club.creator == nil { if let userId = StoreCenter.main.userId, club.creator == nil {
club.creator = userId club.creator = userId

@ -12,35 +12,6 @@ import LeStorage
@Observable @Observable
final public class DateInterval: BaseDateInterval { 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<Date> { var range: Range<Date> {
startDate..<endDate startDate..<endDate
} }

@ -91,7 +91,7 @@ public enum DrawType: Int, Codable {
case .groupStage: case .groupStage:
return "Poule" return "Poule"
case .court: case .court:
return "Terrain" return "Piste"
} }
} }
} }

@ -40,7 +40,7 @@ final public class Event: BaseEvent {
// MARK: - Computed dependencies // MARK: - Computed dependencies
public var tournaments: [Tournament] { 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? { public func clubObject() -> Club? {
@ -92,6 +92,59 @@ final public class Event: BaseEvent {
tournaments.filter({ $0.isFree() == false && $0.isCanceled == false && $0.isDeleted == false }) 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!)
}
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 { func insertOnServer() throws {
DataStore.shared.events.writeChangeAndInsertOnServer(instance: self) DataStore.shared.events.writeChangeAndInsertOnServer(instance: self)
for tournament in self.tournaments { for tournament in self.tournaments {

@ -29,6 +29,8 @@ public class BaseMatchScheduler: BaseModelObject, Storable {
public var shouldTryToFillUpCourtsAvailable: Bool = false public var shouldTryToFillUpCourtsAvailable: Bool = false
public var courtsAvailable: Set<Int> = Set<Int>() public var courtsAvailable: Set<Int> = Set<Int>()
public var simultaneousStart: Bool = true public var simultaneousStart: Bool = true
public var groupStageRotationDifference: Int = 0
public var accountGroupStageBreakTime: Bool = false
public init( public init(
id: String = Store.randomId(), id: String = Store.randomId(),
@ -46,7 +48,9 @@ public class BaseMatchScheduler: BaseModelObject, Storable {
overrideCourtsUnavailability: Bool = false, overrideCourtsUnavailability: Bool = false,
shouldTryToFillUpCourtsAvailable: Bool = false, shouldTryToFillUpCourtsAvailable: Bool = false,
courtsAvailable: Set<Int> = Set<Int>(), courtsAvailable: Set<Int> = Set<Int>(),
simultaneousStart: Bool = true simultaneousStart: Bool = true,
groupStageRotationDifference: Int = 0,
accountGroupStageBreakTime: Bool = false
) { ) {
super.init() super.init()
self.id = id self.id = id
@ -65,6 +69,8 @@ public class BaseMatchScheduler: BaseModelObject, Storable {
self.shouldTryToFillUpCourtsAvailable = shouldTryToFillUpCourtsAvailable self.shouldTryToFillUpCourtsAvailable = shouldTryToFillUpCourtsAvailable
self.courtsAvailable = courtsAvailable self.courtsAvailable = courtsAvailable
self.simultaneousStart = simultaneousStart self.simultaneousStart = simultaneousStart
self.groupStageRotationDifference = groupStageRotationDifference
self.accountGroupStageBreakTime = accountGroupStageBreakTime
} }
required public override init() { required public override init() {
super.init() super.init()
@ -87,6 +93,8 @@ public class BaseMatchScheduler: BaseModelObject, Storable {
case _shouldTryToFillUpCourtsAvailable = "shouldTryToFillUpCourtsAvailable" case _shouldTryToFillUpCourtsAvailable = "shouldTryToFillUpCourtsAvailable"
case _courtsAvailable = "courtsAvailable" case _courtsAvailable = "courtsAvailable"
case _simultaneousStart = "simultaneousStart" case _simultaneousStart = "simultaneousStart"
case _groupStageRotationDifference = "groupStageRotationDifference"
case _accountGroupStageBreakTime = "accountGroupStageBreakTime"
} }
required init(from decoder: Decoder) throws { required init(from decoder: Decoder) throws {
@ -107,6 +115,8 @@ public class BaseMatchScheduler: BaseModelObject, Storable {
self.shouldTryToFillUpCourtsAvailable = try container.decodeIfPresent(Bool.self, forKey: ._shouldTryToFillUpCourtsAvailable) ?? false self.shouldTryToFillUpCourtsAvailable = try container.decodeIfPresent(Bool.self, forKey: ._shouldTryToFillUpCourtsAvailable) ?? false
self.courtsAvailable = try container.decodeIfPresent(Set<Int>.self, forKey: ._courtsAvailable) ?? Set<Int>() self.courtsAvailable = try container.decodeIfPresent(Set<Int>.self, forKey: ._courtsAvailable) ?? Set<Int>()
self.simultaneousStart = try container.decodeIfPresent(Bool.self, forKey: ._simultaneousStart) ?? true 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) try super.init(from: decoder)
} }
@ -128,6 +138,8 @@ public class BaseMatchScheduler: BaseModelObject, Storable {
try container.encode(self.shouldTryToFillUpCourtsAvailable, forKey: ._shouldTryToFillUpCourtsAvailable) try container.encode(self.shouldTryToFillUpCourtsAvailable, forKey: ._shouldTryToFillUpCourtsAvailable)
try container.encode(self.courtsAvailable, forKey: ._courtsAvailable) try container.encode(self.courtsAvailable, forKey: ._courtsAvailable)
try container.encode(self.simultaneousStart, forKey: ._simultaneousStart) 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) try super.encode(to: encoder)
} }
@ -153,6 +165,8 @@ public class BaseMatchScheduler: BaseModelObject, Storable {
self.shouldTryToFillUpCourtsAvailable = matchscheduler.shouldTryToFillUpCourtsAvailable self.shouldTryToFillUpCourtsAvailable = matchscheduler.shouldTryToFillUpCourtsAvailable
self.courtsAvailable = matchscheduler.courtsAvailable self.courtsAvailable = matchscheduler.courtsAvailable
self.simultaneousStart = matchscheduler.simultaneousStart self.simultaneousStart = matchscheduler.simultaneousStart
self.groupStageRotationDifference = matchscheduler.groupStageRotationDifference
self.accountGroupStageBreakTime = matchscheduler.accountGroupStageBreakTime
} }
public static func parentRelationships() -> [Relationship] { public static func parentRelationships() -> [Relationship] {

@ -38,6 +38,8 @@ public class BasePlayerRegistration: SyncedModelObject, SyncedStorable {
public var timeToConfirm: Date? = nil public var timeToConfirm: Date? = nil
public var registrationStatus: PlayerRegistration.RegistrationStatus = PlayerRegistration.RegistrationStatus.waiting public var registrationStatus: PlayerRegistration.RegistrationStatus = PlayerRegistration.RegistrationStatus.waiting
public var paymentId: String? = nil public var paymentId: String? = nil
public var clubCode: String? = nil
public var clubMember: Bool = false
public init( public init(
id: String = Store.randomId(), id: String = Store.randomId(),
@ -64,7 +66,9 @@ public class BasePlayerRegistration: SyncedModelObject, SyncedStorable {
registeredOnline: Bool = false, registeredOnline: Bool = false,
timeToConfirm: Date? = nil, timeToConfirm: Date? = nil,
registrationStatus: PlayerRegistration.RegistrationStatus = PlayerRegistration.RegistrationStatus.waiting, registrationStatus: PlayerRegistration.RegistrationStatus = PlayerRegistration.RegistrationStatus.waiting,
paymentId: String? = nil paymentId: String? = nil,
clubCode: String? = nil,
clubMember: Bool = false
) { ) {
super.init() super.init()
self.id = id self.id = id
@ -92,6 +96,8 @@ public class BasePlayerRegistration: SyncedModelObject, SyncedStorable {
self.timeToConfirm = timeToConfirm self.timeToConfirm = timeToConfirm
self.registrationStatus = registrationStatus self.registrationStatus = registrationStatus
self.paymentId = paymentId self.paymentId = paymentId
self.clubCode = clubCode
self.clubMember = clubMember
} }
required public override init() { required public override init() {
super.init() super.init()
@ -123,6 +129,8 @@ public class BasePlayerRegistration: SyncedModelObject, SyncedStorable {
case _timeToConfirm = "timeToConfirm" case _timeToConfirm = "timeToConfirm"
case _registrationStatus = "registrationStatus" case _registrationStatus = "registrationStatus"
case _paymentId = "paymentId" case _paymentId = "paymentId"
case _clubCode = "clubCode"
case _clubMember = "clubMember"
} }
required init(from decoder: Decoder) throws { required init(from decoder: Decoder) throws {
@ -152,6 +160,8 @@ public class BasePlayerRegistration: SyncedModelObject, SyncedStorable {
self.timeToConfirm = try container.decodeIfPresent(Date.self, forKey: ._timeToConfirm) ?? nil self.timeToConfirm = try container.decodeIfPresent(Date.self, forKey: ._timeToConfirm) ?? nil
self.registrationStatus = try container.decodeIfPresent(PlayerRegistration.RegistrationStatus.self, forKey: ._registrationStatus) ?? PlayerRegistration.RegistrationStatus.waiting self.registrationStatus = try container.decodeIfPresent(PlayerRegistration.RegistrationStatus.self, forKey: ._registrationStatus) ?? PlayerRegistration.RegistrationStatus.waiting
self.paymentId = try container.decodeIfPresent(String.self, forKey: ._paymentId) ?? nil 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) try super.init(from: decoder)
} }
@ -182,6 +192,8 @@ public class BasePlayerRegistration: SyncedModelObject, SyncedStorable {
try container.encode(self.timeToConfirm, forKey: ._timeToConfirm) try container.encode(self.timeToConfirm, forKey: ._timeToConfirm)
try container.encode(self.registrationStatus, forKey: ._registrationStatus) try container.encode(self.registrationStatus, forKey: ._registrationStatus)
try container.encode(self.paymentId, forKey: ._paymentId) 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) try super.encode(to: encoder)
} }
@ -217,6 +229,8 @@ public class BasePlayerRegistration: SyncedModelObject, SyncedStorable {
self.timeToConfirm = playerregistration.timeToConfirm self.timeToConfirm = playerregistration.timeToConfirm
self.registrationStatus = playerregistration.registrationStatus self.registrationStatus = playerregistration.registrationStatus
self.paymentId = playerregistration.paymentId self.paymentId = playerregistration.paymentId
self.clubCode = playerregistration.clubCode
self.clubMember = playerregistration.clubMember
} }
public static func parentRelationships() -> [Relationship] { public static func parentRelationships() -> [Relationship] {

@ -16,7 +16,7 @@ public class BaseTeamRegistration: SyncedModelObject, SyncedStorable {
public var id: String = Store.randomId() public var id: String = Store.randomId()
public var tournament: String = "" public var tournament: String = ""
public var groupStage: String? = nil public var groupStage: String? = nil
public var registrationDate: Date? = nil public var registrationDate: Date? = Date()
public var callDate: Date? = nil public var callDate: Date? = nil
public var bracketPosition: Int? = nil public var bracketPosition: Int? = nil
public var groupStagePosition: Int? = nil public var groupStagePosition: Int? = nil
@ -34,12 +34,13 @@ public class BaseTeamRegistration: SyncedModelObject, SyncedStorable {
public var qualified: Bool = false public var qualified: Bool = false
public var finalRanking: Int? = nil public var finalRanking: Int? = nil
public var pointsEarned: Int? = nil public var pointsEarned: Int? = nil
public var uniqueRandomIndex: Int = 0
public init( public init(
id: String = Store.randomId(), id: String = Store.randomId(),
tournament: String = "", tournament: String = "",
groupStage: String? = nil, groupStage: String? = nil,
registrationDate: Date? = nil, registrationDate: Date? = Date(),
callDate: Date? = nil, callDate: Date? = nil,
bracketPosition: Int? = nil, bracketPosition: Int? = nil,
groupStagePosition: Int? = nil, groupStagePosition: Int? = nil,
@ -56,7 +57,8 @@ public class BaseTeamRegistration: SyncedModelObject, SyncedStorable {
confirmationDate: Date? = nil, confirmationDate: Date? = nil,
qualified: Bool = false, qualified: Bool = false,
finalRanking: Int? = nil, finalRanking: Int? = nil,
pointsEarned: Int? = nil pointsEarned: Int? = nil,
uniqueRandomIndex: Int = 0
) { ) {
super.init() super.init()
self.id = id self.id = id
@ -80,6 +82,7 @@ public class BaseTeamRegistration: SyncedModelObject, SyncedStorable {
self.qualified = qualified self.qualified = qualified
self.finalRanking = finalRanking self.finalRanking = finalRanking
self.pointsEarned = pointsEarned self.pointsEarned = pointsEarned
self.uniqueRandomIndex = uniqueRandomIndex
} }
required public override init() { required public override init() {
super.init() super.init()
@ -107,6 +110,7 @@ public class BaseTeamRegistration: SyncedModelObject, SyncedStorable {
case _qualified = "qualified" case _qualified = "qualified"
case _finalRanking = "finalRanking" case _finalRanking = "finalRanking"
case _pointsEarned = "pointsEarned" case _pointsEarned = "pointsEarned"
case _uniqueRandomIndex = "uniqueRandomIndex"
} }
required init(from decoder: Decoder) throws { required init(from decoder: Decoder) throws {
@ -114,7 +118,7 @@ public class BaseTeamRegistration: SyncedModelObject, SyncedStorable {
self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId()
self.tournament = try container.decodeIfPresent(String.self, forKey: ._tournament) ?? "" self.tournament = try container.decodeIfPresent(String.self, forKey: ._tournament) ?? ""
self.groupStage = try container.decodeIfPresent(String.self, forKey: ._groupStage) ?? nil self.groupStage = try container.decodeIfPresent(String.self, forKey: ._groupStage) ?? nil
self.registrationDate = try container.decodeIfPresent(Date.self, forKey: ._registrationDate) ?? nil self.registrationDate = try container.decodeIfPresent(Date.self, forKey: ._registrationDate) ?? Date()
self.callDate = try container.decodeIfPresent(Date.self, forKey: ._callDate) ?? nil self.callDate = try container.decodeIfPresent(Date.self, forKey: ._callDate) ?? nil
self.bracketPosition = try container.decodeIfPresent(Int.self, forKey: ._bracketPosition) ?? nil self.bracketPosition = try container.decodeIfPresent(Int.self, forKey: ._bracketPosition) ?? nil
self.groupStagePosition = try container.decodeIfPresent(Int.self, forKey: ._groupStagePosition) ?? nil self.groupStagePosition = try container.decodeIfPresent(Int.self, forKey: ._groupStagePosition) ?? nil
@ -132,6 +136,7 @@ public class BaseTeamRegistration: SyncedModelObject, SyncedStorable {
self.qualified = try container.decodeIfPresent(Bool.self, forKey: ._qualified) ?? false self.qualified = try container.decodeIfPresent(Bool.self, forKey: ._qualified) ?? false
self.finalRanking = try container.decodeIfPresent(Int.self, forKey: ._finalRanking) ?? nil self.finalRanking = try container.decodeIfPresent(Int.self, forKey: ._finalRanking) ?? nil
self.pointsEarned = try container.decodeIfPresent(Int.self, forKey: ._pointsEarned) ?? 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) try super.init(from: decoder)
} }
@ -158,6 +163,7 @@ public class BaseTeamRegistration: SyncedModelObject, SyncedStorable {
try container.encode(self.qualified, forKey: ._qualified) try container.encode(self.qualified, forKey: ._qualified)
try container.encode(self.finalRanking, forKey: ._finalRanking) try container.encode(self.finalRanking, forKey: ._finalRanking)
try container.encode(self.pointsEarned, forKey: ._pointsEarned) try container.encode(self.pointsEarned, forKey: ._pointsEarned)
try container.encode(self.uniqueRandomIndex, forKey: ._uniqueRandomIndex)
try super.encode(to: encoder) try super.encode(to: encoder)
} }
@ -193,6 +199,7 @@ public class BaseTeamRegistration: SyncedModelObject, SyncedStorable {
self.qualified = teamregistration.qualified self.qualified = teamregistration.qualified
self.finalRanking = teamregistration.finalRanking self.finalRanking = teamregistration.finalRanking
self.pointsEarned = teamregistration.pointsEarned self.pointsEarned = teamregistration.pointsEarned
self.uniqueRandomIndex = teamregistration.uniqueRandomIndex
} }
public static func parentRelationships() -> [Relationship] { public static func parentRelationships() -> [Relationship] {

@ -82,6 +82,8 @@ public class BaseTournament: SyncedModelObject, SyncedStorable {
public var isTemplate: Bool = false public var isTemplate: Bool = false
public var publishProg: Bool = false public var publishProg: Bool = false
public var showTeamsInProg: Bool = false public var showTeamsInProg: Bool = false
public var clubMemberFeeDeduction: Double? = nil
public var unregisterDeltaInHours: Int = 24
public init( public init(
id: String = Store.randomId(), id: String = Store.randomId(),
@ -152,7 +154,9 @@ public class BaseTournament: SyncedModelObject, SyncedStorable {
isCorporateTournament: Bool = false, isCorporateTournament: Bool = false,
isTemplate: Bool = false, isTemplate: Bool = false,
publishProg: Bool = false, publishProg: Bool = false,
showTeamsInProg: Bool = false showTeamsInProg: Bool = false,
clubMemberFeeDeduction: Double? = nil,
unregisterDeltaInHours: Int = 24
) { ) {
super.init() super.init()
self.id = id self.id = id
@ -224,6 +228,8 @@ public class BaseTournament: SyncedModelObject, SyncedStorable {
self.isTemplate = isTemplate self.isTemplate = isTemplate
self.publishProg = publishProg self.publishProg = publishProg
self.showTeamsInProg = showTeamsInProg self.showTeamsInProg = showTeamsInProg
self.clubMemberFeeDeduction = clubMemberFeeDeduction
self.unregisterDeltaInHours = unregisterDeltaInHours
} }
required public override init() { required public override init() {
super.init() super.init()
@ -301,6 +307,8 @@ public class BaseTournament: SyncedModelObject, SyncedStorable {
case _isTemplate = "isTemplate" case _isTemplate = "isTemplate"
case _publishProg = "publishProg" case _publishProg = "publishProg"
case _showTeamsInProg = "showTeamsInProg" case _showTeamsInProg = "showTeamsInProg"
case _clubMemberFeeDeduction = "clubMemberFeeDeduction"
case _unregisterDeltaInHours = "unregisterDeltaInHours"
} }
private static func _decodePayment(container: KeyedDecodingContainer<CodingKeys>) throws -> TournamentPayment? { private static func _decodePayment(container: KeyedDecodingContainer<CodingKeys>) throws -> TournamentPayment? {
@ -441,6 +449,8 @@ public class BaseTournament: SyncedModelObject, SyncedStorable {
self.isTemplate = try container.decodeIfPresent(Bool.self, forKey: ._isTemplate) ?? false self.isTemplate = try container.decodeIfPresent(Bool.self, forKey: ._isTemplate) ?? false
self.publishProg = try container.decodeIfPresent(Bool.self, forKey: ._publishProg) ?? false self.publishProg = try container.decodeIfPresent(Bool.self, forKey: ._publishProg) ?? false
self.showTeamsInProg = try container.decodeIfPresent(Bool.self, forKey: ._showTeamsInProg) ?? 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) try super.init(from: decoder)
} }
@ -515,6 +525,8 @@ public class BaseTournament: SyncedModelObject, SyncedStorable {
try container.encode(self.isTemplate, forKey: ._isTemplate) try container.encode(self.isTemplate, forKey: ._isTemplate)
try container.encode(self.publishProg, forKey: ._publishProg) try container.encode(self.publishProg, forKey: ._publishProg)
try container.encode(self.showTeamsInProg, forKey: ._showTeamsInProg) 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) try super.encode(to: encoder)
} }
@ -594,6 +606,8 @@ public class BaseTournament: SyncedModelObject, SyncedStorable {
self.isTemplate = tournament.isTemplate self.isTemplate = tournament.isTemplate
self.publishProg = tournament.publishProg self.publishProg = tournament.publishProg
self.showTeamsInProg = tournament.showTeamsInProg self.showTeamsInProg = tournament.showTeamsInProg
self.clubMemberFeeDeduction = tournament.clubMemberFeeDeduction
self.unregisterDeltaInHours = tournament.unregisterDeltaInHours
} }
public static func parentRelationships() -> [Relationship] { public static func parentRelationships() -> [Relationship] {

@ -84,6 +84,16 @@
"name": "simultaneousStart", "name": "simultaneousStart",
"type": "Bool", "type": "Bool",
"defaultValue": "true" "defaultValue": "true"
},
{
"name": "groupStageRotationDifference",
"type": "Int",
"defaultValue": "0"
},
{
"name": "accountGroupStageBreakTime",
"type": "Bool",
"defaultValue": "false"
} }
] ]
} }

@ -130,6 +130,16 @@
"name": "paymentId", "name": "paymentId",
"type": "String", "type": "String",
"optional": true "optional": true
},
{
"name": "clubCode",
"type": "String",
"optional": true
},
{
"name": "clubMember",
"type": "Bool",
"defaultValue": "false"
} }
] ]
} }

@ -25,6 +25,7 @@
{ {
"name": "registrationDate", "name": "registrationDate",
"type": "Date", "type": "Date",
"defaultValue": "Date()",
"optional": true "optional": true
}, },
{ {
@ -111,6 +112,12 @@
"name": "pointsEarned", "name": "pointsEarned",
"type": "Int", "type": "Int",
"optional": true "optional": true
},
{
"name": "uniqueRandomIndex",
"type": "Int",
"defaultValue": "0",
"optional": false
} }
] ]
} }

@ -357,6 +357,16 @@
"name": "showTeamsInProg", "name": "showTeamsInProg",
"type": "Bool", "type": "Bool",
"defaultValue": "false" "defaultValue": "false"
},
{
"name": "clubMemberFeeDeduction",
"type": "Double",
"optional": true
},
{
"name": "unregisterDeltaInHours",
"type": "Int",
"defaultValue": "24"
} }
] ]
} }

@ -22,25 +22,31 @@ final public class Match: BaseMatch, SideStorable {
public var byeState: Bool = false public var byeState: Bool = false
public init(round: String? = nil, groupStage: String? = nil, startDate: Date? = nil, endDate: Date? = nil, index: Int, format: MatchFormat? = nil, servingTeamId: String? = nil, winningTeamId: String? = nil, losingTeamId: String? = nil, name: String? = nil, disabled: Bool = false, courtIndex: Int? = nil, confirmed: Bool = false) { //<<<<<<< HEAD
// public init(round: String? = nil, groupStage: String? = nil, startDate: Date? = nil, endDate: Date? = nil, index: Int, format: MatchFormat? = nil, servingTeamId: String? = nil, winningTeamId: String? = nil, losingTeamId: String? = nil, name: String? = nil, disabled: Bool = false, courtIndex: Int? = nil, confirmed: Bool = false) {
super.init(round: round, groupStage: groupStage, startDate: startDate, endDate: endDate, index: index, format: format, servingTeamId: servingTeamId, winningTeamId: winningTeamId, losingTeamId: losingTeamId, name: name, disabled: disabled, courtIndex: courtIndex, confirmed: confirmed) //
// super.init(round: round, groupStage: groupStage, startDate: startDate, endDate: endDate, index: index, format: format, servingTeamId: servingTeamId, winningTeamId: winningTeamId, losingTeamId: losingTeamId, name: name, disabled: disabled, courtIndex: courtIndex, confirmed: confirmed)
} //
// }
required init(from decoder: Decoder) throws { //
try super.init(from: decoder) // required init(from decoder: Decoder) throws {
} // try super.init(from: decoder)
// }
required public init() { //
super.init() // required public init() {
// super.init()
let drawLog = DrawLog() //
} // let drawLog = DrawLog()
// }
//
//=======
//>>>>>>> main
// MARK: - DidSet // MARK: - DidSet
public override func didSetStartDate() { public override func didSetStartDate() {
if hasStarted() {
return
}
if self.roundValue()?.tournamentObject()?.hasStarted() == false { if self.roundValue()?.tournamentObject()?.hasStarted() == false {
plannedStartDate = startDate plannedStartDate = startDate
} else if self.groupStageValue()?.tournamentObject()?.hasStarted() == false { } else if self.groupStageValue()?.tournamentObject()?.hasStarted() == false {
@ -127,7 +133,7 @@ defer {
} }
#endif #endif
if roundObject?.groupStageLoserBracket == true { if roundObject?.isGroupStageLoserBracket() == true {
return "\(index)\(index.ordinalFormattedSuffix()) place" return "\(index)\(index.ordinalFormattedSuffix()) place"
} }
if let groupStageObject { if let groupStageObject {
@ -187,13 +193,22 @@ defer {
} }
} }
public func scoreLabel() -> String { public func scoreLabel(winnerFirst: Bool = false) -> String {
if hasWalkoutTeam() == true { if hasWalkoutTeam() == true {
return "WO" return "WO"
} }
let scoreOne = teamScore(.one)?.score?.components(separatedBy: ",") ?? [] let scoreOne = teamScore(.one)?.score?.components(separatedBy: ",") ?? []
let scoreTwo = teamScore(.two)?.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: " ") let scores = tuples.map { $0 + "/" + $1 }.joined(separator: " ")
return scores return scores
} }
@ -935,7 +950,7 @@ defer {
public var isLoserBracket: Bool { public var isLoserBracket: Bool {
if let roundObject { if let roundObject {
if roundObject.parent != nil || roundObject.groupStageLoserBracket { if roundObject.parent != nil || roundObject.isGroupStageLoserBracket() {
return true return true
} }
} }
@ -1062,6 +1077,10 @@ defer {
[MatchSpot(match: self, teamPosition: .one), MatchSpot(match: self, teamPosition: .two)] [MatchSpot(match: self, teamPosition: .one), MatchSpot(match: self, teamPosition: .two)]
} }
public func initialStartDate() -> Date? {
plannedStartDate ?? startDate
}
func insertOnServer() { func insertOnServer() {
self.tournamentStore?.matches.writeChangeAndInsertOnServer(instance: self) self.tournamentStore?.matches.writeChangeAndInsertOnServer(instance: self)
for teamScore in self.teamScores { for teamScore in self.teamScores {

@ -11,43 +11,6 @@ import SwiftUI
@Observable @Observable
final public class MatchScheduler: BaseMatchScheduler, SideStorable { final public class MatchScheduler: BaseMatchScheduler, SideStorable {
//
// init(tournament: String,
// timeDifferenceLimit: Int = 5,
// loserBracketRotationDifference: Int = 0,
// upperBracketRotationDifference: Int = 1,
// accountUpperBracketBreakTime: Bool = true,
// accountLoserBracketBreakTime: Bool = false,
// randomizeCourts: Bool = true,
// rotationDifferenceIsImportant: Bool = false,
// shouldHandleUpperRoundSlice: Bool = false,
// shouldEndRoundBeforeStartingNext: Bool = true,
//<<<<<<< HEAD
// groupStageChunkCount: Int? = nil, overrideCourtsUnavailability: Bool = false, shouldTryToFillUpCourtsAvailable: Bool = false) {
// super.init()
//=======
// groupStageChunkCount: Int? = nil,
// overrideCourtsUnavailability: Bool = false,
// shouldTryToFillUpCourtsAvailable: Bool = true,
// courtsAvailable: Set<Int> = Set<Int>(),
// 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]? { var courtsUnavailability: [DateInterval]? {
guard let event = tournamentObject()?.eventObject() else { return nil } guard let event = tournamentObject()?.eventObject() else { return nil }
@ -67,6 +30,14 @@ final public class MatchScheduler: BaseMatchScheduler, SideStorable {
return Store.main.findById(tournament) return Store.main.findById(tournament)
} }
func getGroupStageRotationDifference() -> Int {
if rotationDifferenceIsImportant {
return groupStageRotationDifference
} else {
return 0
}
}
@discardableResult @discardableResult
public func updateGroupStageSchedule(tournament: Tournament, specificGroupStage: GroupStage? = nil, atStep step: Int = 0, startDate: Date? = nil) -> Date { public func updateGroupStageSchedule(tournament: Tournament, specificGroupStage: GroupStage? = nil, atStep step: Int = 0, startDate: Date? = nil) -> Date {
let computedGroupStageChunkCount = groupStageChunkCount ?? tournament.getGroupStageChunkValue() let computedGroupStageChunkCount = groupStageChunkCount ?? tournament.getGroupStageChunkValue()
@ -105,8 +76,15 @@ final public class MatchScheduler: BaseMatchScheduler, SideStorable {
dispatch.timedMatches.forEach { matchSchedule in dispatch.timedMatches.forEach { matchSchedule in
if let match = matches.first(where: { $0.id == matchSchedule.matchID }) { if let match = matches.first(where: { $0.id == matchSchedule.matchID }) {
let estimatedDuration = match.matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration) var estimatedDuration = match.matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration)
let timeIntervalToAdd = (Double(matchSchedule.rotationIndex)) * Double(estimatedDuration) * 60 if accountGroupStageBreakTime {
estimatedDuration += match.matchFormat.breakTime.breakTime * 60
}
var timeIntervalToAdd = (Double(matchSchedule.rotationIndex)) * Double(estimatedDuration) * 60
timeIntervalToAdd += Double(matchSchedule.rotationIndex) * Double(self.getGroupStageRotationDifference()) * Double(match.matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration)) * 60
if let startDate = match.groupStageObject?.startDate { if let startDate = match.groupStageObject?.startDate {
let matchStartDate = startDate.addingTimeInterval(timeIntervalToAdd) let matchStartDate = startDate.addingTimeInterval(timeIntervalToAdd)
match.startDate = matchStartDate match.startDate = matchStartDate
@ -129,8 +107,16 @@ final public class MatchScheduler: BaseMatchScheduler, SideStorable {
dispatch.timedMatches.forEach { matchSchedule in dispatch.timedMatches.forEach { matchSchedule in
if let match = matches.first(where: { $0.id == matchSchedule.matchID }) { if let match = matches.first(where: { $0.id == matchSchedule.matchID }) {
let estimatedDuration = match.matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration) var estimatedDuration = match.matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration)
let timeIntervalToAdd = (Double(matchSchedule.rotationIndex)) * Double(estimatedDuration) * 60
if accountGroupStageBreakTime {
estimatedDuration += match.matchFormat.breakTime.breakTime * 60
}
var timeIntervalToAdd = (Double(matchSchedule.rotationIndex)) * Double(estimatedDuration) * 60
timeIntervalToAdd += Double(matchSchedule.rotationIndex) * Double(self.getGroupStageRotationDifference()) * Double(match.matchFormat.getEstimatedDuration(tournament.additionalEstimationDuration)) * 60
if let startDate = match.groupStageObject?.startDate { if let startDate = match.groupStageObject?.startDate {
let matchStartDate = startDate.addingTimeInterval(timeIntervalToAdd) let matchStartDate = startDate.addingTimeInterval(timeIntervalToAdd)
match.startDate = matchStartDate match.startDate = matchStartDate
@ -207,8 +193,16 @@ final public class MatchScheduler: BaseMatchScheduler, SideStorable {
courtsAvailable.forEach { courtIndex in courtsAvailable.forEach { courtIndex in
print("Checking availability for court \(courtIndex) in rotation \(rotationIndex)") print("Checking availability for court \(courtIndex) in rotation \(rotationIndex)")
if let first = rotationMatches.first(where: { match in if let first = rotationMatches.first(where: { match in
let estimatedDuration = match.matchFormat.getEstimatedDuration(additionalEstimationDuration) var estimatedDuration = match.matchFormat.getEstimatedDuration(additionalEstimationDuration)
let timeIntervalToAdd = Double(rotationIndex) * Double(estimatedDuration) * 60
if accountGroupStageBreakTime {
estimatedDuration += match.matchFormat.breakTime.breakTime * 60
}
var timeIntervalToAdd = (Double(rotationIndex)) * Double(estimatedDuration) * 60
timeIntervalToAdd += Double(rotationIndex) * Double(self.getGroupStageRotationDifference()) * Double(match.matchFormat.getEstimatedDuration(additionalEstimationDuration)) * 60
let rotationStartDate: Date = startingDate.addingTimeInterval(timeIntervalToAdd) let rotationStartDate: Date = startingDate.addingTimeInterval(timeIntervalToAdd)
let courtsUnavailable = courtsUnavailable(startDate: rotationStartDate, duration: match.matchFormat.getEstimatedDuration(additionalEstimationDuration), courtsUnavailability: courtsUnavailability) let courtsUnavailable = courtsUnavailable(startDate: rotationStartDate, duration: match.matchFormat.getEstimatedDuration(additionalEstimationDuration), courtsUnavailability: courtsUnavailability)
@ -822,7 +816,31 @@ final public class MatchScheduler: BaseMatchScheduler, SideStorable {
if tournament.groupStages(atStep: 1).isEmpty == false { if tournament.groupStages(atStep: 1).isEmpty == false {
lastDate = updateGroupStageSchedule(tournament: tournament, atStep: 1, startDate: lastDate) 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 +904,14 @@ extension Match {
return groupStageObject._matchUp(for: index).map { groupStageObject.id + "_\($0)" } 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
}
}

@ -26,42 +26,6 @@ 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) {
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
}
required init(from decoder: any Decoder) throws {
try super.init(from: decoder)
}
required public init() {
super.init()
}
public var tournamentStore: TournamentStore? { public var tournamentStore: TournamentStore? {
guard let storeId else { guard let storeId else {
fatalError("missing store id for \(String(describing: type(of: self)))") fatalError("missing store id for \(String(describing: type(of: self)))")
@ -204,6 +168,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 { public func isMalePlayer() -> Bool {
sex == .male sex == .male
} }
@ -230,6 +201,37 @@ final public class PlayerRegistration: BasePlayerRegistration, SideStorable {
registrationStatus = .confirmed 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 { public enum PlayerDataSource: Int, Codable {
case frenchFederation = 0 case frenchFederation = 0
case beachPadel = 1 case beachPadel = 1

@ -21,28 +21,6 @@ final public class Round: BaseRound, SideStorable {
@ObservationIgnored @ObservationIgnored
private var _cachedLoserRoundsAndChildren: [Round]? 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 // MARK: - DidSet
public override func didSetStartDate() { public override func didSetStartDate() {
@ -265,7 +243,7 @@ defer {
} }
#endif #endif
let parentRound = parentRound 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 return nil
} }
@ -286,7 +264,7 @@ defer {
#endif #endif
let parentRound = parentRound 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 return nil
} }
@ -647,7 +625,7 @@ defer {
} }
public func roundTitle(_ displayStyle: DisplayStyle = .wide, initialMode: Bool = false) -> String { public func roundTitle(_ displayStyle: DisplayStyle = .wide, initialMode: Bool = false) -> String {
if groupStageLoserBracket { if isGroupStageLoserBracket() {
return "Classement Poules" return "Classement Poules"
} }
@ -752,11 +730,11 @@ defer {
} }
public func isUpperBracket() -> Bool { public func isUpperBracket() -> Bool {
return parent == nil && groupStageLoserBracket == false return parent == nil && isGroupStageLoserBracket() == false
} }
public func isLoserBracket() -> Bool { public func isLoserBracket() -> Bool {
return parent != nil || groupStageLoserBracket return parent != nil || isGroupStageLoserBracket()
} }
public func deleteLoserBracket() { public func deleteLoserBracket() {
@ -793,7 +771,7 @@ defer {
var titles = [String: String]() var titles = [String: String]()
let rounds = (0..<roundCount).map { //index 0 is the final let rounds = (0..<roundCount).map { //index 0 is the final
let round = Round(tournament: tournament, index: $0, matchFormat: loserBracketMatchFormat) let round = Round(tournament: tournament, index: $0, format: loserBracketMatchFormat)
round.parent = id //parent round.parent = id //parent
//titles[round.id] = round.roundTitle(initialMode: true) //titles[round.id] = round.roundTitle(initialMode: true)
return round return round
@ -818,6 +796,7 @@ defer {
public func disableUnplayedLoserBracketMatches() { public func disableUnplayedLoserBracketMatches() {
let m = self._matches() let m = self._matches()
let roundTitle = roundTitle()
if let previousRound = self.previousRound() { if let previousRound = self.previousRound() {
m.forEach { match in m.forEach { match in
let prmc = previousRound.previousMatches(previousRound: previousRound, match: match).filter({ let prmc = previousRound.previousMatches(previousRound: previousRound, match: match).filter({
@ -839,6 +818,7 @@ defer {
match.disabled = false match.disabled = false
} }
} }
match.setMatchName(roundTitle)
} }
} else if let upperRound = self.parentRound { } else if let upperRound = self.parentRound {
m.forEach { match in m.forEach { match in
@ -851,8 +831,10 @@ defer {
} else { } else {
match.disabled = false match.disabled = false
} }
match.setMatchName(roundTitle)
} }
} }
tournamentStore?.matches.addOrUpdate(contentOfs: m) tournamentStore?.matches.addOrUpdate(contentOfs: m)
loserRounds().forEach { loserRound in loserRounds().forEach { loserRound in
@ -903,6 +885,14 @@ defer {
_cachedLoserRoundsAndChildren = nil _cachedLoserRoundsAndChildren = nil
} }
public func initialStartDate() -> Date? {
plannedStartDate ?? startDate ?? playedMatches().first?.initialStartDate()
}
public func isGroupStageLoserBracket() -> Bool {
groupStageLoserBracket == true
}
public override func deleteDependencies(store: Store, actionOption: ActionOption) { public override func deleteDependencies(store: Store, actionOption: ActionOption) {
store.deleteDependencies(type: Match.self, actionOption: actionOption) { $0.round == self.id } store.deleteDependencies(type: Match.self, actionOption: actionOption) { $0.round == self.id }

@ -14,38 +14,38 @@ final public class TeamRegistration: BaseTeamRegistration, SideStorable {
@ObservationIgnored @ObservationIgnored
var _cachedRestingTime: (Bool, Date?)? var _cachedRestingTime: (Bool, Date?)?
public init( // public init(
tournament: String, groupStage: String? = nil, registrationDate: Date? = nil, // tournament: String, groupStage: String? = nil, registrationDate: Date? = nil,
callDate: Date? = nil, bracketPosition: Int? = nil, groupStagePosition: Int? = nil, // callDate: Date? = nil, bracketPosition: Int? = nil, groupStagePosition: Int? = nil,
comment: String? = nil, source: String? = nil, sourceValue: String? = nil, // comment: String? = nil, source: String? = nil, sourceValue: String? = nil,
logo: String? = nil, name: String? = nil, walkOut: Bool = false, // logo: String? = nil, name: String? = nil, walkOut: Bool = false,
wildCardBracket: Bool = false, wildCardGroupStage: Bool = false, weight: Int = 0, // wildCardBracket: Bool = false, wildCardGroupStage: Bool = false, weight: Int = 0,
lockedWeight: Int? = nil, confirmationDate: Date? = nil, qualified: Bool = false // lockedWeight: Int? = nil, confirmationDate: Date? = nil, qualified: Bool = false
) { // ) {
//
super.init() // super.init()
//
// self.storeId = tournament // // self.storeId = tournament
self.tournament = tournament // self.tournament = tournament
self.groupStage = groupStage // self.groupStage = groupStage
self.registrationDate = registrationDate ?? Date() // self.registrationDate = registrationDate ?? Date()
self.callDate = callDate // self.callDate = callDate
self.bracketPosition = bracketPosition // self.bracketPosition = bracketPosition
self.groupStagePosition = groupStagePosition // self.groupStagePosition = groupStagePosition
self.comment = comment // self.comment = comment
self.source = source // self.source = source
self.sourceValue = sourceValue // self.sourceValue = sourceValue
self.logo = logo // self.logo = logo
self.name = name // self.name = name
self.walkOut = walkOut // self.walkOut = walkOut
self.wildCardBracket = wildCardBracket // self.wildCardBracket = wildCardBracket
self.wildCardGroupStage = wildCardGroupStage // self.wildCardGroupStage = wildCardGroupStage
self.weight = weight // self.weight = weight
self.lockedWeight = lockedWeight // self.lockedWeight = lockedWeight
self.confirmationDate = confirmationDate // self.confirmationDate = confirmationDate
self.qualified = qualified // self.qualified = qualified
} // }
//
public func hasRegisteredOnline() -> Bool { public func hasRegisteredOnline() -> Bool {
players().anySatisfy({ $0.registeredOnline }) players().anySatisfy({ $0.registeredOnline })
} }
@ -72,14 +72,6 @@ final public class TeamRegistration: BaseTeamRegistration, SideStorable {
walkOut walkOut
} }
required init(from decoder: any Decoder) throws {
try super.init(from: decoder)
}
required public init() {
super.init()
}
public var tournamentStore: TournamentStore? { public var tournamentStore: TournamentStore? {
return TournamentLibrary.shared.store(tournamentId: self.tournament) return TournamentLibrary.shared.store(tournamentId: self.tournament)
} }
@ -395,6 +387,12 @@ final public class TeamRegistration: BaseTeamRegistration, SideStorable {
} }
} }
public func teamLastNames() -> [String] {
players().map { p in
p.lastName
}
}
public var computedRegistrationDate: Date { public var computedRegistrationDate: Date {
return registrationDate ?? .distantFuture return registrationDate ?? .distantFuture
} }

@ -28,32 +28,13 @@ final public class TeamScore: BaseTeamScore, SideStorable {
// //
// var storeId: String? = nil // var storeId: String? = nil
init(match: String, teamRegistration: String? = nil, score: String? = nil, walkOut: Int? = nil, luckyLoser: Int? = nil) { convenience init(match: String, team: TeamRegistration?) {
super.init(match: match, teamRegistration: teamRegistration, score: score, walkOut: walkOut, luckyLoser: luckyLoser) self.init(match: match)
// 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)
if let team { if let team {
self.teamRegistration = team.id self.teamRegistration = team.id
} }
} }
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
}
required public init() {
super.init()
}
var tournamentStore: TournamentStore? { var tournamentStore: TournamentStore? {
guard let storeId else { guard let storeId else {
fatalError("missing store id for \(String(describing: type(of: self)))") fatalError("missing store id for \(String(describing: type(of: self)))")

@ -259,10 +259,13 @@ defer {
case .rawText: 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)) 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: case .csv:
let headers = ["", "Nom Prénom", "rang", "Nom Prénom", "rang", "poids"].joined(separator: exportFormat.separator()) let headers = ["", "Nom Prénom", "rang", "Nom Prénom", "rang", "poids", "Paire"].joined(separator: exportFormat.separator())
var teamPaste = [headers] var teamPaste = [headers]
for (index, team) in selectedSortedTeams.enumerated() { 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()) return teamPaste.joined(separator: exportFormat.newLineSeparator())
} }
@ -399,7 +402,8 @@ defer {
case 24...31: case 24...31:
return SeedInterval(first: 25, last: 32) return SeedInterval(first: 25, last: 32)
default: default:
return nil let pow = Int(pow(2.0, ceil(log2(Double(alreadySetupSeeds)))))
return SeedInterval(first: pow + 1, last: pow * 2)
} }
} }
@ -652,7 +656,7 @@ defer {
let defaultSorting : [MySortDescriptor<TeamRegistration>] = _defaultSorting() let defaultSorting : [MySortDescriptor<TeamRegistration>] = _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) let wcGroupStage = _teams.filter { $0.wildCardGroupStage }.sorted(using: _currentSelectionSorting, order: .ascending)
@ -1050,20 +1054,14 @@ defer {
} }
} }
} }
if rankings.isEmpty == false {
do { let teams = unsortedTeams()
try self.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: unsortedTeams()) self.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams)
} catch {
Logger.error(error)
}
if self.publishRankings == false { if self.publishRankings == false {
self.publishRankings = true self.publishRankings = true
do {
try DataStore.shared.tournaments.addOrUpdate(instance: self)
} catch {
Logger.error(error)
} }
DataStore.shared.tournaments.addOrUpdate(instance: self)
} }
return rankings return rankings
@ -1247,19 +1245,11 @@ defer {
} }
public func earnings() -> Double { public func earnings() -> Double {
if let entryFee { return selectedPlayers().compactMap { $0.paidAmount(self) }.reduce(0.0, +)
return Double(selectedPlayers().filter { $0.hasPaid() }.count) * entryFee
} else {
return 0.0
}
} }
public func remainingAmount() -> Double { public func remainingAmount() -> Double {
if let entryFee { return selectedPlayers().compactMap { $0.remainingAmount(self) }.reduce(0.0, +)
return Double(selectedPlayers().filter { $0.hasPaid() == false }.count) * entryFee
} else {
return 0.0
}
} }
public func paidCompletion() -> Double { public func paidCompletion() -> Double {
@ -1374,7 +1364,7 @@ defer {
} }
public func settingsDescriptionLocalizedLabel() -> String { public func settingsDescriptionLocalizedLabel() -> String {
[courtCount.formatted() + " terrain\(courtCount.pluralSuffix)", entryFeeMessage].joined(separator: ", ") [courtCount.formatted() + " piste\(courtCount.pluralSuffix)", entryFeeMessage].joined(separator: ", ")
} }
public func structureDescriptionLocalizedLabel() -> String { public func structureDescriptionLocalizedLabel() -> String {
@ -1404,7 +1394,7 @@ defer {
guard let tournamentStore = self.tournamentStore else { return } guard let tournamentStore = self.tournamentStore else { return }
let teams = (0..<count).map { _ in let teams = (0..<count).map { _ in
let team = TeamRegistration(tournament: id) let team = TeamRegistration(tournament: id, registrationDate: Date())
team.setWeight(from: [], inTournamentCategory: self.tournamentCategory) team.setWeight(from: [], inTournamentCategory: self.tournamentCategory)
return team return team
} }
@ -1442,7 +1432,7 @@ defer {
let matchCount = RoundRule.numberOfMatches(forTeams: minimalBracketTeamCount ?? bracketTeamCount()) let matchCount = RoundRule.numberOfMatches(forTeams: minimalBracketTeamCount ?? bracketTeamCount())
let rounds = (0..<roundCount).map { //index 0 is the final let rounds = (0..<roundCount).map { //index 0 is the final
return Round(tournament: id, index: $0, matchFormat: matchFormat, loserBracketMode: loserBracketMode) return Round(tournament: id, index: $0, format: matchFormat, loserBracketMode: loserBracketMode)
} }
if rounds.isEmpty { if rounds.isEmpty {
@ -1755,19 +1745,22 @@ defer {
public func setupRegistrationSettings(templateTournament: Tournament) { public func setupRegistrationSettings(templateTournament: Tournament) {
self.enableOnlineRegistration = templateTournament.enableOnlineRegistration self.enableOnlineRegistration = templateTournament.enableOnlineRegistration
self.unregisterDeltaInHours = templateTournament.unregisterDeltaInHours
self.accountIsRequired = templateTournament.accountIsRequired self.accountIsRequired = templateTournament.accountIsRequired
self.licenseIsRequired = templateTournament.licenseIsRequired self.licenseIsRequired = templateTournament.licenseIsRequired
self.minimumPlayerPerTeam = templateTournament.minimumPlayerPerTeam self.minimumPlayerPerTeam = templateTournament.minimumPlayerPerTeam
self.maximumPlayerPerTeam = templateTournament.maximumPlayerPerTeam self.maximumPlayerPerTeam = templateTournament.maximumPlayerPerTeam
self.waitingListLimit = templateTournament.waitingListLimit self.waitingListLimit = templateTournament.waitingListLimit
self.teamCountLimit = templateTournament.teamCountLimit self.teamCountLimit = templateTournament.teamCountLimit
self.teamCount = templateTournament.teamCount
self.enableOnlinePayment = templateTournament.enableOnlinePayment self.enableOnlinePayment = templateTournament.enableOnlinePayment
self.onlinePaymentIsMandatory = templateTournament.onlinePaymentIsMandatory self.onlinePaymentIsMandatory = templateTournament.onlinePaymentIsMandatory
self.enableOnlinePaymentRefund = templateTournament.enableOnlinePaymentRefund self.enableOnlinePaymentRefund = templateTournament.enableOnlinePaymentRefund
self.stripeAccountId = templateTournament.stripeAccountId self.stripeAccountId = templateTournament.stripeAccountId
self.enableTimeToConfirm = templateTournament.enableTimeToConfirm self.enableTimeToConfirm = templateTournament.enableTimeToConfirm
self.isCorporateTournament = templateTournament.isCorporateTournament self.isCorporateTournament = templateTournament.isCorporateTournament
self.clubMemberFeeDeduction = templateTournament.clubMemberFeeDeduction
self.unregisterDeltaInHours = templateTournament.unregisterDeltaInHours
if self.registrationDateLimit == nil, templateTournament.registrationDateLimit != nil { if self.registrationDateLimit == nil, templateTournament.registrationDateLimit != nil {
self.registrationDateLimit = startDate.truncateMinutesAndSeconds() self.registrationDateLimit = startDate.truncateMinutesAndSeconds()
} }
@ -1793,9 +1786,9 @@ defer {
private func _defaultSorting() -> [MySortDescriptor<TeamRegistration>] { private func _defaultSorting() -> [MySortDescriptor<TeamRegistration>] {
switch teamSorting { switch teamSorting {
case .rank: case .rank:
[.keyPath(\TeamRegistration.initialWeight), .keyPath(\TeamRegistration.id)] [.keyPath(\TeamRegistration.initialWeight), .keyPath(\TeamRegistration.uniqueRandomIndex), .keyPath(\TeamRegistration.id)]
case .inscriptionDate: case .inscriptionDate:
[.keyPath(\TeamRegistration.registrationDate!), .keyPath(\TeamRegistration.initialWeight), .keyPath(\TeamRegistration.id)] [.keyPath(\TeamRegistration.registrationDate!), .keyPath(\TeamRegistration.initialWeight), .keyPath(\TeamRegistration.uniqueRandomIndex), .keyPath(\TeamRegistration.id)]
} }
} }
@ -1805,7 +1798,7 @@ defer {
&& federalTournamentAge == build.age && federalTournamentAge == build.age
} }
private let _currentSelectionSorting : [MySortDescriptor<TeamRegistration>] = [.keyPath(\.weight), .keyPath(\.id)] private let _currentSelectionSorting : [MySortDescriptor<TeamRegistration>] = [.keyPath(\.weight), .keyPath(\.uniqueRandomIndex), .keyPath(\.id)]
private func _matchSchedulers() -> [MatchScheduler] { private func _matchSchedulers() -> [MatchScheduler] {
guard let tournamentStore = self.tournamentStore else { return [] } guard let tournamentStore = self.tournamentStore else { return [] }
@ -1907,7 +1900,7 @@ defer {
} }
public func groupStageLoserBracket() -> Round? { public func groupStageLoserBracket() -> Round? {
self.tournamentStore?.rounds.first(where: { $0.groupStageLoserBracket }) self.tournamentStore?.rounds.first(where: { $0.isGroupStageLoserBracket() })
} }
public func groupStageLoserBracketsInitialPlace() -> Int { public func groupStageLoserBracketsInitialPlace() -> Int {
@ -2052,7 +2045,7 @@ defer {
public func addNewRound(_ roundIndex: Int) async { public func addNewRound(_ roundIndex: Int) async {
await MainActor.run { 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 matchCount = RoundRule.numberOfMatches(forRoundIndex: roundIndex)
let matchStartIndex = RoundRule.matchIndex(fromRoundIndex: roundIndex) let matchStartIndex = RoundRule.matchIndex(fromRoundIndex: roundIndex)
let nextRound = round.nextRound() let nextRound = round.nextRound()
@ -2243,6 +2236,87 @@ defer {
return Array(leftInterval...maxSize) 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..<groupStageCount {
countedSet.add(groupStageMatchFormat)
}
}
if let loserRounds = matches.filter({ $0.round != nil }).filter({ $0.roundObject?.parent == nil }).sorted(by: \.computedStartDateForSorting).last?.roundObject?.loserRounds() {
let ids = matches.map { $0.id }
for loserRound in loserRounds {
if let first = loserRound.playedMatches().first {
if ids.contains(first.id) {
countedSet.add(first.matchFormat)
}
}
}
}
matchCountPerDay[day] = countedSet
}
}
return matchCountPerDay
}
// MARK: - // MARK: -
func insertOnServer() throws { func insertOnServer() throws {

@ -102,8 +102,8 @@ public class MatchDescriptor: ObservableObject {
setDescriptors.compactMap { $0.getValue(teamPosition: .two) } setDescriptors.compactMap { $0.getValue(teamPosition: .two) }
} }
public var scoreTeamOne: Int { setDescriptors.compactMap { $0.winner }.filter { $0 == .one }.count } public var scoreTeamOne: Int { setDescriptors.filter({ $0.hasEnded }).compactMap { $0.winner }.filter { $0 == .one }.count }
public var scoreTeamTwo: Int { setDescriptors.compactMap { $0.winner }.filter { $0 == .two }.count } public var scoreTeamTwo: Int { setDescriptors.filter({ $0.hasEnded }).compactMap { $0.winner }.filter { $0 == .two }.count }
public var hasEnded: Bool { public var hasEnded: Bool {
return matchFormat.hasEnded(scoreTeamOne: scoreTeamOne, scoreTeamTwo: scoreTeamTwo) return matchFormat.hasEnded(scoreTeamOne: scoreTeamOne, scoreTeamTwo: scoreTeamTwo)

@ -647,7 +647,7 @@ public enum TournamentLevel: Int, Hashable, Codable, CaseIterable, Identifiable
case _ where count > 28: 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] 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: default:
return [60,50,40,25,10,5] return [60,50,40,25,10,5, 1]
} }
case .p250: case .p250:
switch count { switch count {
@ -698,10 +698,16 @@ public enum TournamentLevel: Int, Hashable, Codable, CaseIterable, Identifiable
case _ where count > 28: 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] 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: default:
return [600,500,400,250,100,50] return [600,500,400,250,100,50, 10]
} }
case .p1500: case .p1500:
switch count { 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: 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: case 25...28:
@ -709,23 +715,31 @@ public enum TournamentLevel: Int, Hashable, Codable, CaseIterable, Identifiable
case _ where count > 28: 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: 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: 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] { 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 { // enum NewBallSystem {
// case perField // case perField
@ -1846,7 +1860,7 @@ public enum AnimationType: Int, CaseIterable, Hashable, Identifiable {
case .playerAnimation: case .playerAnimation:
return "Chaque joueur joue avec quelqu'un de différent à chaque rotation (8 à 12 joueurs)" return "Chaque joueur joue avec quelqu'un de différent à chaque rotation (8 à 12 joueurs)"
case .upAndDown: 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: case .brawl:
return "A chaque rotation, les gagnants de la rotation précédente se jouent entre eux" return "A chaque rotation, les gagnants de la rotation précédente se jouent entre eux"
} }

Loading…
Cancel
Save