fix stuff scheduler

fix min more qualified team
fix min qualified
paca_championship
Raz 1 year ago
parent 996220fe6f
commit 34ad82f177
  1. 18
      PadelClub/Data/MatchScheduler.swift
  2. 2
      PadelClub/Data/PlayerRegistration.swift
  3. 16
      PadelClub/Data/Tournament.swift
  4. 2
      PadelClub/Views/Calling/TeamsCallingView.swift
  5. 1
      PadelClub/Views/Planning/PlanningSettingsView.swift
  6. 32
      PadelClub/Views/Planning/PlanningView.swift
  7. 7
      PadelClub/Views/Player/Components/EditablePlayerView.swift
  8. 21
      PadelClub/Views/Player/PlayerDetailView.swift
  9. 36
      PadelClub/Views/Tournament/Screen/Components/InscriptionInfoView.swift
  10. 7
      PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift
  11. 4
      PadelClub/Views/Tournament/Screen/TableStructureView.swift

@ -385,16 +385,10 @@ final class MatchScheduler : ModelObject, Storable {
print("Targeted start date is after the minimum possible end date, returning true.")
return true
}
} else {
if targetedStartDate == minimumTargetedEndDate {
print("Updating minimumTargetedEndDate to minimumPossibleEndDate: \(minimumPossibleEndDate)")
minimumTargetedEndDate = minimumPossibleEndDate
} else {
print("Setting minimumTargetedEndDate to the earlier of \(minimumPossibleEndDate) and \(minimumTargetedEndDate)")
minimumTargetedEndDate = min(minimumPossibleEndDate, minimumTargetedEndDate)
}
print("Targeted start date is before the minimum possible end date, returning false.")
return false
minimumTargetedEndDate = minimumPossibleEndDate
return true
}
}
@ -463,7 +457,7 @@ final class MatchScheduler : ModelObject, Storable {
var courts = initialCourts ?? Array(courtsAvailable)
var shouldStartAtDispatcherDate = rotationIndex > 0
while !availableMatchs.isEmpty && !issueFound && rotationIndex < 100 {
while !availableMatchs.isEmpty && !issueFound && rotationIndex < 50 {
freeCourtPerRotation[rotationIndex] = []
let previousRotationSlots = slots.filter({ $0.rotationIndex == rotationIndex - 1 })
var rotationStartDate: Date = getNextStartDate(fromPreviousRotationSlots: previousRotationSlots, includeBreakTime: false) ?? dispatcherStartDate
@ -604,7 +598,9 @@ final class MatchScheduler : ModelObject, Storable {
if shouldTryToFillUpCourtsAvailable == false {
if roundObject.parent == nil && roundObject.index > 1 && indexInRound == 0, let nextMatch = match.next() {
if courtPosition < courts.count - 1 && canBePlayed && roundMatchCanBePlayed(nextMatch, roundObject: roundObject, slots: slots, rotationIndex: rotationIndex, targetedStartDate: rotationStartDate, minimumTargetedEndDate: &minimumTargetedEndDate) {
var nextMinimumTargetedEndDate = minimumTargetedEndDate
if courtPosition < courts.count - 1 && canBePlayed && roundMatchCanBePlayed(nextMatch, roundObject: roundObject, slots: slots, rotationIndex: rotationIndex, targetedStartDate: rotationStartDate, minimumTargetedEndDate: &nextMinimumTargetedEndDate) {
print("Returning true: Both current \(match.index) and next match \(nextMatch.index) can be played in rotation \(rotationIndex).")
return true
} else {
@ -625,7 +621,7 @@ final class MatchScheduler : ModelObject, Storable {
matchID: firstMatch.id,
rotationIndex: rotationIndex,
courtIndex: courtIndex,
startDate: rotationStartDate,
startDate: minimumTargetedEndDate,
durationLeft: firstMatch.matchFormat.getEstimatedDuration(additionalEstimationDuration),
minimumBreakTime: firstMatch.matchFormat.breakTime.breakTime
)

@ -300,7 +300,7 @@ final class PlayerRegistration: ModelObject, Storable {
if let currentLicenceId = licenceId {
if currentLicenceId.trimmed.hasSuffix("(\(year-1))") {
self.licenceId = currentLicenceId.replacingOccurrences(of: "\(year-1)", with: "\(year)")
} else if let computedLicense = currentLicenceId.strippedLicense {
} else if let computedLicense = currentLicenceId.strippedLicense?.computedLicense {
self.licenceId = computedLicense + " (\(year))"
}
}

@ -1021,8 +1021,20 @@ defer {
func playersWithoutValidLicense(in players: [PlayerRegistration], isImported: Bool) -> [PlayerRegistration] {
let licenseYearValidity = self.licenseYearValidity()
return players.filter({
($0.isImported() && $0.isValidLicenseNumber(year: licenseYearValidity) == false) || ($0.isImported() == false && ($0.licenceId == nil || $0.formattedLicense().isLicenseNumber == false || $0.licenceId?.isEmpty == true) || ($0.isImported() == false && isImported))
return players.filter({ player in
if player.isImported() {
// Player is marked as imported: check if the license is valid
return !player.isValidLicenseNumber(year: licenseYearValidity)
} else {
// Player is not imported: validate license and handle `isImported` flag for non-imported players
let noLicenseId = player.licenceId == nil || player.licenceId?.isEmpty == true
let invalidFormattedLicense = player.formattedLicense().isLicenseNumber == false
// If global `isImported` is true, check license number as well
let invalidLicenseForImportedFlag = isImported && !player.isValidLicenseNumber(year: licenseYearValidity)
return noLicenseId || invalidFormattedLicense || invalidLicenseForImportedFlag
}
})
}

@ -14,6 +14,8 @@ struct TeamsCallingView: View {
var body: some View {
List {
PlayersWithoutContactView(players: teams.flatMap({ $0.unsortedPlayers() }).sorted(by: \.computedRank))
Section {
ForEach(teams) { team in
Menu {

@ -257,6 +257,7 @@ struct PlanningSettingsView: View {
_save()
}
.onChange(of: tournament.courtCount) {
matchScheduler.courtsAvailable = Set(tournament.courtsAvailable())
_save()
}
.onChange(of: tournament.dayDuration) {

@ -10,6 +10,7 @@ import SwiftUI
struct PlanningView: View {
@EnvironmentObject var dataStore: DataStore
@Environment(Tournament.self) var tournament: Tournament
@State private var selectedDay: Date?
let matches: [Match]
@Binding var selectedScheduleDestination: ScheduleDestination?
@ -39,7 +40,7 @@ struct PlanningView: View {
case .byCourt:
return "Par terrain"
case .byDefault:
return "Par défaut"
return "Par ordre des matchs"
}
}
}
@ -49,11 +50,38 @@ struct PlanningView: View {
_selectedScheduleDestination = selectedScheduleDestination
}
private func _computedTitle() -> String {
if let selectedDay {
return selectedDay.formatted(.dateTime.day().weekday().month())
} else {
if days.count > 1 {
return "Tous les jours"
} else {
return "Horaires"
}
}
}
var body: some View {
List {
_bySlotView()
}
.navigationTitle(Text(_computedTitle()))
.toolbar(content: {
if days.count > 1 {
ToolbarTitleMenu {
Picker(selection: $selectedDay) {
Text("Tous les jours").tag(nil as Date?)
ForEach(days, id: \.self) { day in
Text(day.formatted(.dateTime.day().weekday().month())).tag(day as Date?)
}
} label: {
Text("Jour")
}
.pickerStyle(.automatic)
}
}
ToolbarItem(placement: .topBarTrailing) {
Menu {
Picker(selection: $filterOption) {
@ -89,7 +117,7 @@ struct PlanningView: View {
@ViewBuilder
func _bySlotView() -> some View {
if matches.allSatisfy({ $0.startDate == nil }) == false {
ForEach(days, id: \.self) { day in
ForEach(days.filter({ selectedDay == nil || selectedDay == $0 }), id: \.self) { day in
Section {
ForEach(keys.filter({ $0.dayInt == day.dayInt }), id: \.self) { key in
if let _matches = timeSlots[key] {

@ -78,6 +78,13 @@ struct EditablePlayerView: View {
Logger.error(error)
}
}
.onChange(of: player.licenceId) {
do {
try self.tournamentStore.playerRegistrations.addOrUpdate(instance: player)
} catch {
Logger.error(error)
}
}
.onChange(of: player.hasArrived) {
do {
try self.tournamentStore.playerRegistrations.addOrUpdate(instance: player)

@ -186,6 +186,27 @@ struct PlayerDetailView: View {
}
}
}
Section {
if let number = player.phoneNumber?.replacingOccurrences(of: " ", with: "") {
if let url = URL(string: "tel:\(number)") {
Link(destination: url) {
Label("Appeler", systemImage: "phone")
}
}
if let url = URL(string: "sms:\(number)") {
Link(destination: url) {
Label("Message", systemImage: "message")
}
}
}
if let mail = player.email, let mailURL = URL(string: "mail:\(mail)") {
Link(destination: mailURL) {
Label("Mail", systemImage: "mail")
}
}
}
}
.toolbar {
ToolbarItem(placement: .topBarTrailing) {

@ -126,9 +126,7 @@ struct InscriptionInfoView: View {
}
}
.listRowView(color: .logoRed)
}
Section {
DisclosureGroup {
ForEach(homonyms) { player in
ImportedPlayerView(player: player)
@ -141,8 +139,20 @@ struct InscriptionInfoView: View {
}
}
.listRowView(color: .logoRed)
}
DisclosureGroup {
ForEach(playersMissing) {
TeamDetailView(team: $0)
}
} label: {
LabeledContent {
Text(playersMissing.count.formatted())
} label: {
Text("Paires incomplètes")
}
}
.listRowView(color: .pink)
}
Section {
DisclosureGroup {
@ -200,6 +210,11 @@ struct InscriptionInfoView: View {
ForEach(playersWithoutValidLicense) {
EditablePlayerView(player: $0, editingOptions: [.licenceId])
.environmentObject(tournament.tournamentStore)
.onChange(of: $0.licenceId) {
players = tournament.unsortedPlayers()
let isImported = players.anySatisfy({ $0.isImported() })
playersWithoutValidLicense = tournament.playersWithoutValidLicense(in: players, isImported: isImported)
}
}
} label: {
LabeledContent {
@ -212,21 +227,6 @@ struct InscriptionInfoView: View {
} footer: {
Text("importé du fichier beach-padel sans licence valide ou créé sans licence")
}
Section {
DisclosureGroup {
ForEach(playersMissing) {
TeamDetailView(team: $0)
}
} label: {
LabeledContent {
Text(playersMissing.count.formatted())
} label: {
Text("Paires incomplètes")
}
}
.listRowView(color: .pink)
}
}
.task {
await _getIssues()

@ -165,6 +165,7 @@ struct InscriptionManagerView: View {
if self.teamsHash == nil, selectedSortedTeams.isEmpty == false {
self.teamsHash = _simpleHash(ids: selectedSortedTeams.map { $0.id })
}
self.registrationIssues = nil
Task {
self.registrationIssues = await tournament.registrationIssues()
}
@ -708,6 +709,12 @@ struct InscriptionManagerView: View {
NavigationLink {
InscriptionInfoView()
.environment(tournament)
.onDisappear {
self.registrationIssues = nil
Task {
self.registrationIssues = await tournament.registrationIssues()
}
}
} label: {
LabeledContent {
if let registrationIssues {

@ -106,14 +106,14 @@ struct TableStructureView: View {
if structurePreset != .doubleGroupStage {
LabeledContent {
StepperView(count: $qualifiedPerGroupStage, minimum: 0, maximum: (teamsPerGroupStage-1))
StepperView(count: $qualifiedPerGroupStage, minimum: 1, maximum: (teamsPerGroupStage-1))
} label: {
Text("Qualifié\(qualifiedPerGroupStage.pluralSuffix) par poule")
}
if qualifiedPerGroupStage < teamsPerGroupStage - 1 {
LabeledContent {
StepperView(count: $groupStageAdditionalQualified, minimum: 1, maximum: maxMoreQualified)
StepperView(count: $groupStageAdditionalQualified, minimum: 0, maximum: maxMoreQualified)
} label: {
Text("Qualifié\(groupStageAdditionalQualified.pluralSuffix) supplémentaires")
Text(moreQualifiedLabel)

Loading…
Cancel
Save