fix wip scheduler

multistore
Razmig Sarkissian 1 year ago
parent 5ca9f8f462
commit 039110785a
  1. 4
      PadelClub.xcodeproj/project.pbxproj
  2. 43
      PadelClub/Data/MatchScheduler.swift
  3. 1
      PadelClub/Views/Club/ClubRowView.swift
  4. 19
      PadelClub/Views/Planning/PlanningSettingsView.swift
  5. 2
      PadelClub/Views/Planning/PlanningView.swift

@ -1859,7 +1859,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 47; CURRENT_PROJECT_VERSION = 48;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
DEVELOPMENT_TEAM = BQ3Y44M3Q6; DEVELOPMENT_TEAM = BQ3Y44M3Q6;
@ -1896,7 +1896,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 47; CURRENT_PROJECT_VERSION = 48;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"PadelClub/Preview Content\"";
DEVELOPMENT_TEAM = BQ3Y44M3Q6; DEVELOPMENT_TEAM = BQ3Y44M3Q6;

@ -363,6 +363,7 @@ class MatchScheduler : ModelObject, Storable {
var rotationIndex = 0 var rotationIndex = 0
var availableMatchs = flattenedMatches.filter({ $0.startDate == nil }) var availableMatchs = flattenedMatches.filter({ $0.startDate == nil })
let courtsUnavailability = courtsUnavailability let courtsUnavailability = courtsUnavailability
var issueFound: Bool = false
flattenedMatches.filter { $0.startDate != nil }.sorted(by: \.startDate!).forEach { match in flattenedMatches.filter { $0.startDate != nil }.sorted(by: \.startDate!).forEach { match in
if _startDate == nil { if _startDate == nil {
@ -388,7 +389,7 @@ class MatchScheduler : ModelObject, Storable {
var shouldStartAtDispatcherDate = rotationIndex > 0 var shouldStartAtDispatcherDate = rotationIndex > 0
while availableMatchs.count > 0 { while availableMatchs.count > 0 && issueFound == false {
freeCourtPerRotation[rotationIndex] = [] freeCourtPerRotation[rotationIndex] = []
let previousRotationSlots = slots.filter({ $0.rotationIndex == rotationIndex - 1 }) let previousRotationSlots = slots.filter({ $0.rotationIndex == rotationIndex - 1 })
var rotationStartDate: Date = getNextStartDate(fromPreviousRotationSlots: previousRotationSlots, includeBreakTime: false) ?? dispatcherStartDate var rotationStartDate: Date = getNextStartDate(fromPreviousRotationSlots: previousRotationSlots, includeBreakTime: false) ?? dispatcherStartDate
@ -431,6 +432,13 @@ class MatchScheduler : ModelObject, Storable {
rotationStartDate = rotationStartDate.addingTimeInterval(-difference) rotationStartDate = rotationStartDate.addingTimeInterval(-difference)
} }
} }
} else if let first = availableMatchs.first {
let duration = first.matchFormat.getEstimatedDuration(additionalEstimationDuration)
let courtsUnavailable = courtsUnavailable(startDate: rotationStartDate, duration: duration, courtsUnavailability: courtsUnavailability)
if courtsUnavailable == numberOfCourtsAvailablePerRotation {
print("issue")
issueFound = true
}
} }
dispatchCourts(availableCourts: numberOfCourtsAvailablePerRotation, courts: courts, availableMatchs: &availableMatchs, slots: &slots, rotationIndex: rotationIndex, rotationStartDate: rotationStartDate, freeCourtPerRotation: &freeCourtPerRotation, courtsUnavailability: courtsUnavailability) dispatchCourts(availableCourts: numberOfCourtsAvailablePerRotation, courts: courts, availableMatchs: &availableMatchs, slots: &slots, rotationIndex: rotationIndex, rotationStartDate: rotationStartDate, freeCourtPerRotation: &freeCourtPerRotation, courtsUnavailability: courtsUnavailability)
@ -450,7 +458,7 @@ class MatchScheduler : ModelObject, Storable {
} }
return MatchDispatcher(timedMatches: slots, freeCourtPerRotation: freeCourtPerRotation, rotationCount: rotationIndex) return MatchDispatcher(timedMatches: slots, freeCourtPerRotation: freeCourtPerRotation, rotationCount: rotationIndex, issueFound: issueFound)
} }
func dispatchCourts(availableCourts: Int, courts: [Int], availableMatchs: inout [Match], slots: inout [TimeMatch], rotationIndex: Int, rotationStartDate: Date, freeCourtPerRotation: inout [Int: [Int]], courtsUnavailability: [DateInterval]?) { func dispatchCourts(availableCourts: Int, courts: [Int], availableMatchs: inout [Match], slots: inout [TimeMatch], rotationIndex: Int, rotationStartDate: Date, freeCourtPerRotation: inout [Int: [Int]], courtsUnavailability: [DateInterval]?) {
@ -542,16 +550,18 @@ class MatchScheduler : ModelObject, Storable {
}.sorted(by: \.1).map { $0.0 } }.sorted(by: \.1).map { $0.0 }
} }
let courtsUnavailable = courtsUnavailable(at: minimumTargetedEndDate, courtsUnavailability: courtsUnavailability)
if let courtsUnavailable, courtsUnavailable == availableCourts { if let first = availableMatchs.first {
minimumTargetedEndDate.addTimeInterval(3600) let duration = first.matchFormat.getEstimatedDuration(additionalEstimationDuration)
} let courtsUnavailable = courtsUnavailable(startDate: minimumTargetedEndDate, duration: duration, courtsUnavailability: courtsUnavailability)
if courtsUnavailable < availableCourts {
dispatchCourts(availableCourts: availableCourts, courts: freeCourts, availableMatchs: &availableMatchs, slots: &slots, rotationIndex: rotationIndex, rotationStartDate: minimumTargetedEndDate, freeCourtPerRotation: &freeCourtPerRotation, courtsUnavailability: courtsUnavailability) dispatchCourts(availableCourts: availableCourts, courts: freeCourts, availableMatchs: &availableMatchs, slots: &slots, rotationIndex: rotationIndex, rotationStartDate: minimumTargetedEndDate, freeCourtPerRotation: &freeCourtPerRotation, courtsUnavailability: courtsUnavailability)
} }
} }
}
}
func updateBracketSchedule(tournament: Tournament, fromRoundId roundId: String?, fromMatchId matchId: String?, startDate: Date) { @discardableResult func updateBracketSchedule(tournament: Tournament, fromRoundId roundId: String?, fromMatchId matchId: String?, startDate: Date) -> Bool {
let upperRounds: [Round] = tournament.rounds() let upperRounds: [Round] = tournament.rounds()
let allMatches: [Match] = tournament.allMatches() let allMatches: [Match] = tournament.allMatches()
@ -643,6 +653,8 @@ class MatchScheduler : ModelObject, Storable {
} catch { } catch {
Logger.error(error) Logger.error(error)
} }
return roundDispatch.issueFound
} }
@ -656,18 +668,6 @@ class MatchScheduler : ModelObject, Storable {
}.count }.count
} }
func courtsUnavailable(at startDate: Date, courtsUnavailability: [DateInterval]?) -> Int? {
guard let courtsUnavailability else { return nil }
let groupedBy = Dictionary(grouping: courtsUnavailability, by: { $0.courtIndex })
let courts = groupedBy.keys
return courts.filter { courtIndex in
let courtLockedSchedule = courtsUnavailability.filter({ $0.courtIndex == courtIndex })
return courtLockedSchedule.anySatisfy({ dateInterval in
dateInterval.range.contains(startDate)
})
}.count
}
func courtUnavailable(courtIndex: Int, from startDate: Date, to endDate: Date, source: [DateInterval]) -> Bool { func courtUnavailable(courtIndex: Int, from startDate: Date, to endDate: Date, source: [DateInterval]) -> Bool {
let courtLockedSchedule = source.filter({ $0.courtIndex == courtIndex }) let courtLockedSchedule = source.filter({ $0.courtIndex == courtIndex })
return courtLockedSchedule.anySatisfy({ dateInterval in return courtLockedSchedule.anySatisfy({ dateInterval in
@ -676,9 +676,9 @@ class MatchScheduler : ModelObject, Storable {
}) })
} }
func updateSchedule(tournament: Tournament) { func updateSchedule(tournament: Tournament) -> Bool {
let lastDate = updateGroupStageSchedule(tournament: tournament) let lastDate = updateGroupStageSchedule(tournament: tournament)
updateBracketSchedule(tournament: tournament, fromRoundId: nil, fromMatchId: nil, startDate: lastDate) return updateBracketSchedule(tournament: tournament, fromRoundId: nil, fromMatchId: nil, startDate: lastDate)
} }
} }
@ -714,6 +714,7 @@ struct MatchDispatcher {
let timedMatches: [TimeMatch] let timedMatches: [TimeMatch]
let freeCourtPerRotation: [Int: [Int]] let freeCourtPerRotation: [Int: [Int]]
let rotationCount: Int let rotationCount: Int
let issueFound: Bool
} }
extension Match { extension Match {

@ -19,7 +19,6 @@ struct ClubRowView: View {
// .foregroundStyle(club.isFavorite() ? .green : .logoRed) // .foregroundStyle(club.isFavorite() ? .green : .logoRed)
// } // }
} label: { } label: {
Text("Club")
Text(club.name) Text(club.name)
} }
} }

@ -17,6 +17,7 @@ struct PlanningSettingsView: View {
@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 issueFound: Bool = false
init(tournament: Tournament) { init(tournament: Tournament) {
self.tournament = tournament self.tournament = tournament
@ -87,6 +88,11 @@ struct PlanningSettingsView: View {
} }
} }
if issueFound {
Text("Padel Club n'a pas réussi à définir un horaire pour tous les matchs de ce tournoi, à cause de la programmation d'autres épreuves ou de l'indisponibilité des terrains.")
.foregroundStyle(.logoRed)
}
NavigationLink { NavigationLink {
List { List {
_optionsView() _optionsView()
@ -151,9 +157,10 @@ struct PlanningSettingsView: View {
} }
RowButtonView("Horaire intelligent", role: .destructive) { RowButtonView("Horaire intelligent", role: .destructive) {
await MainActor.run { await MainActor.run {
issueFound = false
schedulingDone = false schedulingDone = false
} }
await _setupSchedule() self.issueFound = await _setupSchedule()
await MainActor.run { await MainActor.run {
_save() _save()
schedulingDone = true schedulingDone = true
@ -173,11 +180,17 @@ struct PlanningSettingsView: View {
} }
.overlay(alignment: .bottom) { .overlay(alignment: .bottom) {
if schedulingDone { if schedulingDone {
if issueFound {
Label("Horaires mis à jour", systemImage: "xmark.circle.fill")
.toastFormatted()
.deferredRendering(for: .seconds(2))
} else {
Label("Horaires mis à jour", systemImage: "checkmark.circle.fill") Label("Horaires mis à jour", systemImage: "checkmark.circle.fill")
.toastFormatted() .toastFormatted()
.deferredRendering(for: .seconds(2)) .deferredRendering(for: .seconds(2))
} }
} }
}
.onChange(of: tournament.startDate) { .onChange(of: tournament.startDate) {
_save() _save()
} }
@ -262,8 +275,8 @@ struct PlanningSettingsView: View {
} }
} }
private func _setupSchedule() async { private func _setupSchedule() async -> Bool {
matchScheduler.updateSchedule(tournament: tournament) return matchScheduler.updateSchedule(tournament: tournament)
} }
private func _save() { private func _save() {

@ -90,7 +90,7 @@ struct PlanningView: View {
private func _timeSlotView(key: Date, matches: [Match]) -> some View { private func _timeSlotView(key: Date, matches: [Match]) -> some View {
LabeledContent { LabeledContent {
Text(self._formattedMatchCount(self.matches.count)) Text(self._formattedMatchCount(matches.count))
} label: { } label: {
Text(key.formatted(date: .omitted, time: .shortened)).font(.title).fontWeight(.semibold) Text(key.formatted(date: .omitted, time: .shortened)).font(.title).fontWeight(.semibold)
Text(Set(matches.compactMap { $0.roundTitle() }).joined(separator: ", ")) Text(Set(matches.compactMap { $0.roundTitle() }).joined(separator: ", "))

Loading…
Cancel
Save