add matchscheduler storage object

multistore
Razmig Sarkissian 2 years ago
parent c2933805e9
commit ed0148694f
  1. 2
      PadelClub.xcodeproj/project.pbxproj
  2. 6
      PadelClub/Data/DataStore.swift
  3. 200
      PadelClub/Data/MatchScheduler.swift
  4. 9
      PadelClub/Data/Tournament.swift
  5. 2
      PadelClub/Views/Planning/LoserRoundScheduleEditorView.swift
  6. 2
      PadelClub/Views/Planning/LoserRoundStepScheduleEditorView.swift
  7. 2
      PadelClub/Views/Planning/MatchScheduleEditorView.swift
  8. 181
      PadelClub/Views/Planning/PlanningSettingsView.swift
  9. 2
      PadelClub/Views/Planning/RoundScheduleEditorView.swift
  10. 2
      PadelClub/Views/Planning/SchedulerView.swift

@ -690,6 +690,7 @@
FF8F263E2BAD7D5C00650388 /* Event.swift */, FF8F263E2BAD7D5C00650388 /* Event.swift */,
FF025AE82BD1307E00A86CF8 /* MonthData.swift */, FF025AE82BD1307E00A86CF8 /* MonthData.swift */,
FF1DC5522BAB354A00FD8220 /* MockData.swift */, FF1DC5522BAB354A00FD8220 /* MockData.swift */,
FF3B60A22BC49BBC008C2E66 /* MatchScheduler.swift */,
FFDB1C6C2BB2A02000F1E467 /* AppSettings.swift */, FFDB1C6C2BB2A02000F1E467 /* AppSettings.swift */,
FFC91B002BD85C2F00B29808 /* Court.swift */, FFC91B002BD85C2F00B29808 /* Court.swift */,
FFF116E02BD2A9B600A33B06 /* DateInterval.swift */, FFF116E02BD2A9B600A33B06 /* DateInterval.swift */,
@ -987,7 +988,6 @@
FFCFC0132BBC59FC00B82851 /* MatchDescriptor.swift */, FFCFC0132BBC59FC00B82851 /* MatchDescriptor.swift */,
FFCFC01B2BBC5AAA00B82851 /* SetDescriptor.swift */, FFCFC01B2BBC5AAA00B82851 /* SetDescriptor.swift */,
FFBF065F2BBD9F6D009D6715 /* NavigationViewModel.swift */, FFBF065F2BBD9F6D009D6715 /* NavigationViewModel.swift */,
FF3B60A22BC49BBC008C2E66 /* MatchScheduler.swift */,
FF5BAF6D2BE0B3C8008B4B7E /* FederalDataViewModel.swift */, FF5BAF6D2BE0B3C8008B4B7E /* FederalDataViewModel.swift */,
); );
path = ViewModel; path = ViewModel;

@ -42,7 +42,8 @@ class DataStore: ObservableObject {
fileprivate(set) var teamScores: StoredCollection<TeamScore> fileprivate(set) var teamScores: StoredCollection<TeamScore>
fileprivate(set) var monthData: StoredCollection<MonthData> fileprivate(set) var monthData: StoredCollection<MonthData>
fileprivate(set) var dateIntervals: StoredCollection<DateInterval> fileprivate(set) var dateIntervals: StoredCollection<DateInterval>
fileprivate(set) var matchSchedulers: StoredCollection<MatchScheduler>
fileprivate(set) var userStorage: StoredSingleton<User> fileprivate(set) var userStorage: StoredSingleton<User>
// fileprivate var _userStorage: OptionalStorage<User> = OptionalStorage<User>(fileName: "user.json") // fileprivate var _userStorage: OptionalStorage<User> = OptionalStorage<User>(fileName: "user.json")
@ -81,7 +82,8 @@ class DataStore: ObservableObject {
self.matches = store.registerCollection(synchronized: synchronized, indexed: indexed) self.matches = store.registerCollection(synchronized: synchronized, indexed: indexed)
self.monthData = store.registerCollection(synchronized: false, indexed: indexed) self.monthData = store.registerCollection(synchronized: false, indexed: indexed)
self.dateIntervals = store.registerCollection(synchronized: synchronized, indexed: indexed) self.dateIntervals = store.registerCollection(synchronized: synchronized, indexed: indexed)
self.matchSchedulers = store.registerCollection(synchronized: false, indexed: indexed)
self.userStorage = store.registerObject(synchronized: synchronized) self.userStorage = store.registerObject(synchronized: synchronized)
NotificationCenter.default.addObserver(self, selector: #selector(collectionDidLoad), name: NSNotification.Name.CollectionDidLoad, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(collectionDidLoad), name: NSNotification.Name.CollectionDidLoad, object: nil)

@ -7,91 +7,71 @@
import Foundation import Foundation
import LeStorage import LeStorage
import SwiftUI
struct GroupStageTimeMatch {
let matchID: String @Observable
let rotationIndex: Int class MatchScheduler : ModelObject, Storable {
var courtIndex: Int static func resourceName() -> String { return "match-scheduler" }
let groupIndex: Int static func requestsRequiresToken() -> Bool { true }
}
private(set) var id: String = Store.randomId()
struct TimeMatch { var tournament: String
let matchID: String var timeDifferenceLimit: Int
let rotationIndex: Int var loserBracketRotationDifference: Int
var courtIndex: Int var upperBracketRotationDifference: Int
var startDate: Date var accountUpperBracketBreakTime: Bool
var durationLeft: Int //in minutes var accountLoserBracketBreakTime: Bool
var minimumBreakTime: Int //in minutes var randomizeCourts: Bool
var rotationDifferenceIsImportant: Bool
func estimatedEndDate(includeBreakTime: Bool) -> Date { var shouldHandleUpperRoundSlice: Bool
let minutesToAdd = Double(durationLeft + (includeBreakTime ? minimumBreakTime : 0)) var shouldEndRoundBeforeStartingNext: Bool
return startDate.addingTimeInterval(minutesToAdd * 60.0)
} init(tournament: String,
} timeDifferenceLimit: Int = 5,
loserBracketRotationDifference: Int = 0,
struct GroupStageMatchDispatcher { upperBracketRotationDifference: Int = 1,
let timedMatches: [GroupStageTimeMatch] accountUpperBracketBreakTime: Bool = true,
let freeCourtPerRotation: [Int: [Int]] accountLoserBracketBreakTime: Bool = false,
let rotationCount: Int randomizeCourts: Bool = true,
let groupLastRotation: [Int: Int] rotationDifferenceIsImportant: Bool = false,
} shouldHandleUpperRoundSlice: Bool = true,
shouldEndRoundBeforeStartingNext: Bool = true) {
struct MatchDispatcher { self.tournament = tournament
let timedMatches: [TimeMatch] self.timeDifferenceLimit = timeDifferenceLimit
let freeCourtPerRotation: [Int: [Int]] self.loserBracketRotationDifference = loserBracketRotationDifference
let rotationCount: Int self.upperBracketRotationDifference = upperBracketRotationDifference
} self.accountUpperBracketBreakTime = accountUpperBracketBreakTime
self.accountLoserBracketBreakTime = accountLoserBracketBreakTime
extension Match { self.randomizeCourts = randomizeCourts
func teamIds() -> [String] { self.rotationDifferenceIsImportant = rotationDifferenceIsImportant
return teams().map { $0.id } self.shouldHandleUpperRoundSlice = shouldHandleUpperRoundSlice
self.shouldEndRoundBeforeStartingNext = shouldEndRoundBeforeStartingNext
} }
func containsTeamId(_ id: String) -> Bool { enum CodingKeys: String, CodingKey {
teamIds().contains(id) case _id = "id"
case _tournament = "tournament"
case _timeDifferenceLimit = "timeDifferenceLimit"
case _loserBracketRotationDifference = "loserBracketRotationDifference"
case _upperBracketRotationDifference = "upperBracketRotationDifference"
case _accountUpperBracketBreakTime = "accountUpperBracketBreakTime"
case _accountLoserBracketBreakTime = "accountLoserBracketBreakTime"
case _randomizeCourts = "randomizeCourts"
case _rotationDifferenceIsImportant = "rotationDifferenceIsImportant"
case _shouldHandleUpperRoundSlice = "shouldHandleUpperRoundSlice"
case _shouldEndRoundBeforeStartingNext = "shouldEndRoundBeforeStartingNext"
} }
}
enum MatchSchedulerOption: Hashable { var courtsUnavailability: [DateInterval]? {
case accountUpperBracketBreakTime tournamentObject()?.eventObject()?.courtsUnavailability
case accountLoserBracketBreakTime
case randomizeCourts
case rotationDifferenceIsImportant
case shouldHandleUpperRoundSlice
case shouldEndRoundBeforeStartingNext
}
class MatchScheduler {
static let shared = MatchScheduler()
var additionalEstimationDuration : Int = 0
var options: Set<MatchSchedulerOption> = Set(arrayLiteral: .accountUpperBracketBreakTime)
var timeDifferenceLimit: Double = 300.0
var loserBracketRotationDifference: Int = 0
var upperBracketRotationDifference: Int = 1
var courtsUnavailability: [DateInterval]? = nil
func shouldEndRoundBeforeStartingNext() -> Bool {
options.contains(.shouldEndRoundBeforeStartingNext)
} }
func shouldHandleUpperRoundSlice() -> Bool { var additionalEstimationDuration : Int {
options.contains(.shouldHandleUpperRoundSlice) tournamentObject()?.additionalEstimationDuration ?? 0
} }
func accountLoserBracketBreakTime() -> Bool { func tournamentObject() -> Tournament? {
options.contains(.accountLoserBracketBreakTime) Store.main.findById(tournament)
}
func accountUpperBracketBreakTime() -> Bool {
options.contains(.accountUpperBracketBreakTime)
}
func randomizeCourts() -> Bool {
options.contains(.randomizeCourts)
}
func rotationDifferenceIsImportant() -> Bool {
options.contains(.rotationDifferenceIsImportant)
} }
@discardableResult @discardableResult
@ -99,7 +79,6 @@ class MatchScheduler {
let groupStageCourtCount = tournament.groupStageCourtCount ?? 1 let groupStageCourtCount = tournament.groupStageCourtCount ?? 1
let groupStages = tournament.groupStages() let groupStages = tournament.groupStages()
let numberOfCourtsAvailablePerRotation: Int = tournament.courtCount let numberOfCourtsAvailablePerRotation: Int = tournament.courtCount
courtsUnavailability = tournament.eventObject()?.courtsUnavailability
let matches = groupStages.flatMap({ $0._matches() }) let matches = groupStages.flatMap({ $0._matches() })
matches.forEach({ matches.forEach({
@ -197,7 +176,7 @@ class MatchScheduler {
var organizedSlots = [GroupStageTimeMatch]() var organizedSlots = [GroupStageTimeMatch]()
for i in 0..<rotationIndex { for i in 0..<rotationIndex {
let courtsSorted = slots.filter({ $0.rotationIndex == i }).map { $0.courtIndex }.sorted() let courtsSorted = slots.filter({ $0.rotationIndex == i }).map { $0.courtIndex }.sorted()
let courts = randomizeCourts() ? courtsSorted.shuffled() : courtsSorted let courts = randomizeCourts ? courtsSorted.shuffled() : courtsSorted
var matches = slots.filter({ $0.rotationIndex == i }).sorted(using: .keyPath(\.groupIndex), .keyPath(\.courtIndex)) var matches = slots.filter({ $0.rotationIndex == i }).sorted(using: .keyPath(\.groupIndex), .keyPath(\.courtIndex))
for j in 0..<matches.count { for j in 0..<matches.count {
@ -254,11 +233,11 @@ class MatchScheduler {
var includeBreakTime = false var includeBreakTime = false
if accountLoserBracketBreakTime() && roundObject.isLoserBracket() { if accountLoserBracketBreakTime && roundObject.isLoserBracket() {
includeBreakTime = true includeBreakTime = true
} }
if accountUpperBracketBreakTime() && roundObject.isLoserBracket() == false { if accountUpperBracketBreakTime && roundObject.isLoserBracket() == false {
includeBreakTime = true includeBreakTime = true
} }
@ -269,7 +248,7 @@ class MatchScheduler {
} }
if targetedStartDate >= minimumPossibleEndDate { if targetedStartDate >= minimumPossibleEndDate {
if rotationDifferenceIsImportant() { if rotationDifferenceIsImportant {
return previousMatchIsInPreviousRotation return previousMatchIsInPreviousRotation
} else { } else {
return true return true
@ -375,14 +354,15 @@ class MatchScheduler {
let differenceWithoutBreak = rotationStartDate.timeIntervalSince(previousEndDateNoBreak) let differenceWithoutBreak = rotationStartDate.timeIntervalSince(previousEndDateNoBreak)
print("difference w break", differenceWithBreak) print("difference w break", differenceWithBreak)
print("difference w/o break", differenceWithoutBreak) print("difference w/o break", differenceWithoutBreak)
let timeDifferenceLimitInSeconds = Double(timeDifferenceLimit * 60)
var difference = differenceWithBreak var difference = differenceWithBreak
if differenceWithBreak <= 0 { if differenceWithBreak <= 0 {
difference = differenceWithoutBreak difference = differenceWithoutBreak
} else if differenceWithBreak > timeDifferenceLimit && differenceWithoutBreak > timeDifferenceLimit { } else if differenceWithBreak > timeDifferenceLimitInSeconds && differenceWithoutBreak > timeDifferenceLimitInSeconds {
difference = noBreakAlreadyTested ? differenceWithBreak : max(differenceWithBreak, differenceWithoutBreak) difference = noBreakAlreadyTested ? differenceWithBreak : max(differenceWithBreak, differenceWithoutBreak)
} }
if difference > timeDifferenceLimit { if difference > timeDifferenceLimitInSeconds {
courts.removeAll(where: { index in freeCourtPreviousRotation.contains(index) courts.removeAll(where: { index in freeCourtPreviousRotation.contains(index)
}) })
freeCourtPerRotation[rotationIndex] = courts freeCourtPerRotation[rotationIndex] = courts
@ -399,7 +379,7 @@ class MatchScheduler {
var organizedSlots = [TimeMatch]() var organizedSlots = [TimeMatch]()
for i in 0..<rotationIndex { for i in 0..<rotationIndex {
let courtsSorted = slots.filter({ $0.rotationIndex == i }).map { $0.courtIndex }.sorted() let courtsSorted = slots.filter({ $0.rotationIndex == i }).map { $0.courtIndex }.sorted()
let courts = randomizeCourts() ? courtsSorted.shuffled() : courtsSorted let courts = randomizeCourts ? courtsSorted.shuffled() : courtsSorted
var matches = slots.filter({ $0.rotationIndex == i }).sorted(using: .keyPath(\.courtIndex)) var matches = slots.filter({ $0.rotationIndex == i }).sorted(using: .keyPath(\.courtIndex))
for j in 0..<matches.count { for j in 0..<matches.count {
@ -430,7 +410,7 @@ class MatchScheduler {
let currentRotationSameRoundMatches = matchPerRound[roundObject.index] ?? 0 let currentRotationSameRoundMatches = matchPerRound[roundObject.index] ?? 0
if shouldHandleUpperRoundSlice() { if shouldHandleUpperRoundSlice {
let roundMatchesCount = roundObject.playedMatches().count let roundMatchesCount = roundObject.playedMatches().count
print("shouldHandleUpperRoundSlice \(roundMatchesCount)") print("shouldHandleUpperRoundSlice \(roundMatchesCount)")
if roundObject.parent == nil && roundMatchesCount > courts.count { if roundObject.parent == nil && roundMatchesCount > courts.count {
@ -502,14 +482,13 @@ class MatchScheduler {
} }
func updateBracketSchedule(tournament: Tournament, fromRoundId roundId: String?, fromMatchId matchId: String?, startDate: Date) { func updateBracketSchedule(tournament: Tournament, fromRoundId roundId: String?, fromMatchId matchId: String?, startDate: Date) {
courtsUnavailability = tournament.eventObject()?.courtsUnavailability
let upperRounds = tournament.rounds() let upperRounds = tournament.rounds()
let allMatches = tournament.allMatches() let allMatches = tournament.allMatches()
var rounds = [Round]() var rounds = [Round]()
if shouldEndRoundBeforeStartingNext() { if shouldEndRoundBeforeStartingNext {
rounds = upperRounds.flatMap { rounds = upperRounds.flatMap {
[$0] + $0.loserRoundsAndChildren() [$0] + $0.loserRoundsAndChildren()
} }
@ -607,8 +586,51 @@ class MatchScheduler {
} }
func updateSchedule(tournament: Tournament) { func updateSchedule(tournament: Tournament) {
courtsUnavailability = tournament.eventObject()?.courtsUnavailability
let lastDate = updateGroupStageSchedule(tournament: tournament) let lastDate = updateGroupStageSchedule(tournament: tournament)
updateBracketSchedule(tournament: tournament, fromRoundId: nil, fromMatchId: nil, startDate: lastDate) updateBracketSchedule(tournament: tournament, fromRoundId: nil, fromMatchId: nil, startDate: lastDate)
} }
} }
struct GroupStageTimeMatch {
let matchID: String
let rotationIndex: Int
var courtIndex: Int
let groupIndex: Int
}
struct TimeMatch {
let matchID: String
let rotationIndex: Int
var courtIndex: Int
var startDate: Date
var durationLeft: Int //in minutes
var minimumBreakTime: Int //in minutes
func estimatedEndDate(includeBreakTime: Bool) -> Date {
let minutesToAdd = Double(durationLeft + (includeBreakTime ? minimumBreakTime : 0))
return startDate.addingTimeInterval(minutesToAdd * 60.0)
}
}
struct GroupStageMatchDispatcher {
let timedMatches: [GroupStageTimeMatch]
let freeCourtPerRotation: [Int: [Int]]
let rotationCount: Int
let groupLastRotation: [Int: Int]
}
struct MatchDispatcher {
let timedMatches: [TimeMatch]
let freeCourtPerRotation: [Int: [Int]]
let rotationCount: Int
}
extension Match {
func teamIds() -> [String] {
return teams().map { $0.id }
}
func containsTeamId(_ id: String) -> Bool {
teamIds().contains(id)
}
}

@ -1302,8 +1302,17 @@ class Tournament : ModelObject, Storable {
try Store.main.deleteDependencies(items: self.unsortedTeams()) try Store.main.deleteDependencies(items: self.unsortedTeams())
try Store.main.deleteDependencies(items: self.groupStages()) try Store.main.deleteDependencies(items: self.groupStages())
try Store.main.deleteDependencies(items: self.rounds()) try Store.main.deleteDependencies(items: self.rounds())
try Store.main.deleteDependencies(items: self._matchSchedulers())
} }
private func _matchSchedulers() -> [MatchScheduler] {
Store.main.filter(isIncluded: { $0.id == self.id })
}
func matchScheduler() -> MatchScheduler? {
_matchSchedulers().first
}
func currentMonthData() -> MonthData? { func currentMonthData() -> MonthData? {
guard let rankSourceDate else { return nil } guard let rankSourceDate else { return nil }
let dateString = URL.importDateFormatter.string(from: rankSourceDate) let dateString = URL.importDateFormatter.string(from: rankSourceDate)

@ -66,7 +66,7 @@ struct LoserRoundScheduleEditorView: View {
// _save() // _save()
let loserRounds = upperRound.loserRounds().filter { $0.isDisabled() == false } let loserRounds = upperRound.loserRounds().filter { $0.isDisabled() == false }
MatchScheduler.shared.updateBracketSchedule(tournament: tournament, fromRoundId: loserRounds.first?.id, fromMatchId: nil, startDate: startDate) tournament.matchScheduler()?.updateBracketSchedule(tournament: tournament, fromRoundId: loserRounds.first?.id, fromMatchId: nil, startDate: startDate)
loserRounds.first?.startDate = startDate loserRounds.first?.startDate = startDate
_save() _save()
} }

@ -59,7 +59,7 @@ struct LoserRoundStepScheduleEditorView: View {
} }
private func _updateSchedule() async { private func _updateSchedule() async {
MatchScheduler.shared.updateBracketSchedule(tournament: tournament, fromRoundId: round.id, fromMatchId: nil, startDate: startDate) tournament.matchScheduler()?.updateBracketSchedule(tournament: tournament, fromRoundId: round.id, fromMatchId: nil, startDate: startDate)
upperRound.loserRounds(forRoundIndex: round.index).forEach({ round in upperRound.loserRounds(forRoundIndex: round.index).forEach({ round in
round.startDate = startDate round.startDate = startDate
}) })

@ -33,7 +33,7 @@ struct MatchScheduleEditorView: View {
} }
private func _updateSchedule() async { private func _updateSchedule() async {
MatchScheduler.shared.updateBracketSchedule(tournament: tournament, fromRoundId: match.round, fromMatchId: match.id, startDate: startDate) tournament.matchScheduler()?.updateBracketSchedule(tournament: tournament, fromRoundId: match.round, fromMatchId: match.id, startDate: startDate)
} }
} }

@ -10,36 +10,25 @@ import LeStorage
struct PlanningSettingsView: View { struct PlanningSettingsView: View {
@EnvironmentObject var dataStore: DataStore @EnvironmentObject var dataStore: DataStore
var tournament: Tournament @Bindable var tournament: Tournament
@State private var randomCourtDistribution: Bool @Bindable var matchScheduler: MatchScheduler
@State private var groupStageCourtCount: Int @State private var groupStageCourtCount: Int
@State private var upperBracketBreakTime: Bool
@State private var loserBracketBreakTime: Bool
@State private var rotationDifferenceIsImportant: Bool
@State private var loserBracketRotationDifference: Int
@State private var upperBracketRotationDifference: Int
@State private var timeDifferenceLimit: Double
@State private var shouldHandleUpperRoundSlice: Bool
@State private var isScheduling: Bool = false @State private var isScheduling: Bool = false
@State private var schedulingDone: Bool = false @State private var schedulingDone: Bool = false
@State private var showOptions: Bool = false @State private var showOptions: Bool = false
@State private var shouldEndBeforeStartNext: Bool = true
init(tournament: Tournament) { init(tournament: Tournament) {
self.tournament = tournament self.tournament = tournament
if let matchScheduler = tournament.matchScheduler() {
self.matchScheduler = matchScheduler
} else {
self.matchScheduler = MatchScheduler(tournament: tournament.id)
}
self._groupStageCourtCount = State(wrappedValue: tournament.groupStageCourtCount ?? 1) self._groupStageCourtCount = State(wrappedValue: tournament.groupStageCourtCount ?? 1)
self._loserBracketRotationDifference = State(wrappedValue: MatchScheduler.shared.loserBracketRotationDifference)
self._upperBracketRotationDifference = State(wrappedValue: MatchScheduler.shared.upperBracketRotationDifference)
self._timeDifferenceLimit = State(wrappedValue: MatchScheduler.shared.timeDifferenceLimit)
self._rotationDifferenceIsImportant = State(wrappedValue: MatchScheduler.shared.rotationDifferenceIsImportant())
self._randomCourtDistribution = State(wrappedValue: MatchScheduler.shared.randomizeCourts())
self._upperBracketBreakTime = State(wrappedValue: MatchScheduler.shared.accountUpperBracketBreakTime())
self._loserBracketBreakTime = State(wrappedValue: MatchScheduler.shared.accountLoserBracketBreakTime())
self._shouldHandleUpperRoundSlice = State(wrappedValue: MatchScheduler.shared.shouldHandleUpperRoundSlice())
} }
var body: some View { var body: some View {
@Bindable var tournament = tournament
List { List {
SubscriptionInfoView() SubscriptionInfoView()
@ -80,10 +69,6 @@ struct PlanningSettingsView: View {
await _setupSchedule() await _setupSchedule()
schedulingDone = true schedulingDone = true
} }
if showOptions {
_optionsView()
}
} footer: { } footer: {
Button { Button {
showOptions.toggle() showOptions.toggle()
@ -93,23 +78,38 @@ struct PlanningSettingsView: View {
} }
.buttonStyle(.borderless) .buttonStyle(.borderless)
} }
if showOptions {
_optionsView()
}
Section { Section {
RowButtonView("Supprimer tous les horaires", role: .destructive) { RowButtonView("Supprimer tous les horaires", role: .destructive) {
let allMatches = tournament.allMatches() do {
allMatches.forEach({ $0.startDate = nil }) let allMatches = tournament.allMatches()
try? dataStore.matches.addOrUpdate(contentOfs: allMatches) allMatches.forEach({ $0.startDate = nil })
try dataStore.matches.addOrUpdate(contentOfs: allMatches)
let allGroupStages = tournament.groupStages()
allGroupStages.forEach({ $0.startDate = nil }) let allGroupStages = tournament.groupStages()
try? dataStore.groupStages.addOrUpdate(contentOfs: allGroupStages) allGroupStages.forEach({ $0.startDate = nil })
try dataStore.groupStages.addOrUpdate(contentOfs: allGroupStages)
let allRounds = tournament.allRounds()
allRounds.forEach({ $0.startDate = nil }) let allRounds = tournament.allRounds()
try? dataStore.rounds.addOrUpdate(contentOfs: allRounds) allRounds.forEach({ $0.startDate = nil })
try dataStore.rounds.addOrUpdate(contentOfs: allRounds)
} catch {
Logger.error(error)
}
} }
} }
} }
.onAppear {
do {
try dataStore.matchSchedulers.addOrUpdate(instance: matchScheduler)
} catch {
Logger.error(error)
}
}
.overlay(alignment: .bottom) { .overlay(alignment: .bottom) {
if schedulingDone { if schedulingDone {
Label("Horaires mis à jour", systemImage: "checkmark.circle.fill") Label("Horaires mis à jour", systemImage: "checkmark.circle.fill")
@ -137,87 +137,70 @@ struct PlanningSettingsView: View {
@ViewBuilder @ViewBuilder
private func _optionsView() -> some View { private func _optionsView() -> some View {
Toggle(isOn: $randomCourtDistribution) {
Text("Distribuer les terrains au hasard")
}
Toggle(isOn: $shouldHandleUpperRoundSlice) { Section {
Text("Équilibrer les matchs d'une manche sur plusieurs tours") Toggle(isOn: $matchScheduler.randomizeCourts) {
} Text("Distribuer les terrains au hasard")
}
Toggle(isOn: $shouldEndBeforeStartNext) {
Text("Finir une manche et les matchs de classements avant de continuer") Toggle(isOn: $matchScheduler.shouldHandleUpperRoundSlice) {
} Text("Équilibrer les matchs d'une manche sur plusieurs tours")
}
Toggle(isOn: $upperBracketBreakTime) {
Text("Tableau : tenir compte des pauses") Toggle(isOn: $matchScheduler.shouldEndRoundBeforeStartingNext) {
Text("Finir une manche, classement inclus avant de continuer")
}
} }
Toggle(isOn: $loserBracketBreakTime) { Section {
Text("Classement : tenir compte des pauses") Toggle(isOn: $matchScheduler.accountUpperBracketBreakTime) {
Text("Tenir compte des pauses")
Text("Tableau")
}
Toggle(isOn: $matchScheduler.accountLoserBracketBreakTime) {
Text("Tenir compte des pauses")
Text("Classement")
}
} }
Toggle(isOn: $rotationDifferenceIsImportant) { Section {
Text("Forcer un créneau supplémentaire entre 2 phases") Toggle(isOn: $matchScheduler.rotationDifferenceIsImportant) {
Text("Forcer un créneau supplémentaire entre 2 phases")
}
LabeledContent {
StepperView(count: $matchScheduler.upperBracketRotationDifference, minimum: 0, maximum: 2)
} label: {
Text("Tableau")
}
.disabled(matchScheduler.rotationDifferenceIsImportant == false)
LabeledContent {
StepperView(count: $matchScheduler.loserBracketRotationDifference, minimum: 0, maximum: 2)
} label: {
Text("Classement")
}
.disabled(matchScheduler.rotationDifferenceIsImportant == false)
} }
LabeledContent { Section {
StepperView(count: $upperBracketRotationDifference, minimum: 0, maximum: 2) LabeledContent {
} label: { StepperView(count: $matchScheduler.timeDifferenceLimit, step: 5)
Text("Tableau") } label: {
} Text("Optimisation des créneaux")
.disabled(rotationDifferenceIsImportant == false) Text("Si libre plus de \(matchScheduler.timeDifferenceLimit) minutes")
}
LabeledContent {
StepperView(count: $loserBracketRotationDifference, minimum: 0, maximum: 2)
} label: {
Text("Classement")
} }
.disabled(rotationDifferenceIsImportant == false)
//timeDifferenceLimit
} }
private func _setupSchedule() async { private func _setupSchedule() async {
let matchScheduler = MatchScheduler.shared
matchScheduler.options.removeAll()
if shouldEndBeforeStartNext {
matchScheduler.options.insert(.shouldEndRoundBeforeStartingNext)
}
if randomCourtDistribution {
matchScheduler.options.insert(.randomizeCourts)
}
if shouldHandleUpperRoundSlice {
matchScheduler.options.insert(.shouldHandleUpperRoundSlice)
}
if upperBracketBreakTime {
matchScheduler.options.insert(.accountUpperBracketBreakTime)
}
if loserBracketBreakTime {
matchScheduler.options.insert(.accountLoserBracketBreakTime)
}
if rotationDifferenceIsImportant {
matchScheduler.options.insert(.rotationDifferenceIsImportant)
}
matchScheduler.loserBracketRotationDifference = loserBracketRotationDifference
matchScheduler.upperBracketRotationDifference = upperBracketRotationDifference
matchScheduler.timeDifferenceLimit = timeDifferenceLimit
matchScheduler.updateSchedule(tournament: tournament) matchScheduler.updateSchedule(tournament: tournament)
} }
private func _save() { private func _save() {
do { do {
try dataStore.matchSchedulers.addOrUpdate(instance: matchScheduler)
try dataStore.tournaments.addOrUpdate(instance: tournament) try dataStore.tournaments.addOrUpdate(instance: tournament)
} catch { } catch {
Logger.error(error) Logger.error(error)

@ -54,7 +54,7 @@ struct RoundScheduleEditorView: View {
} }
private func _updateSchedule() async { private func _updateSchedule() async {
MatchScheduler.shared.updateBracketSchedule(tournament: tournament, fromRoundId: round.id, fromMatchId: nil, startDate: startDate) tournament.matchScheduler()?.updateBracketSchedule(tournament: tournament, fromRoundId: round.id, fromMatchId: nil, startDate: startDate)
round.startDate = startDate round.startDate = startDate
_save() _save()
} }

@ -32,7 +32,7 @@ struct SchedulerView: View {
case .scheduleGroupStage: case .scheduleGroupStage:
MatchFormatPickingView(matchFormat: $tournament.groupStageMatchFormat) { MatchFormatPickingView(matchFormat: $tournament.groupStageMatchFormat) {
Task { Task {
MatchScheduler.shared.updateSchedule(tournament: tournament) tournament.matchScheduler()?.updateSchedule(tournament: tournament)
} }
} }
.onChange(of: tournament.groupStageMatchFormat) { .onChange(of: tournament.groupStageMatchFormat) {

Loading…
Cancel
Save