diff --git a/PadelClub.xcodeproj/project.pbxproj b/PadelClub.xcodeproj/project.pbxproj index ad789ed..cc109c7 100644 --- a/PadelClub.xcodeproj/project.pbxproj +++ b/PadelClub.xcodeproj/project.pbxproj @@ -958,7 +958,6 @@ FF025AE62BD1111000A86CF8 /* GlobalSettingsView.swift */, FF025AEE2BD1AE9400A86CF8 /* DurationSettingsView.swift */, FF025AF02BD1AEBD00A86CF8 /* MatchFormatStorageView.swift */, - FFD783FE2B91BA42000F62A6 /* PadelClubView.swift */, ); path = Toolbox; sourceTree = ""; @@ -967,6 +966,7 @@ isa = PBXGroup; children = ( FF3F74F52B919E45004CFE0E /* UmpireView.swift */, + FFD783FE2B91BA42000F62A6 /* PadelClubView.swift */, ); path = Umpire; sourceTree = ""; diff --git a/PadelClub/Data/Federal/FederalPlayer.swift b/PadelClub/Data/Federal/FederalPlayer.swift index 1963c02..4742b83 100644 --- a/PadelClub/Data/Federal/FederalPlayer.swift +++ b/PadelClub/Data/Federal/FederalPlayer.swift @@ -169,7 +169,19 @@ class FederalPlayer: Decodable { club = result[10] } - static func lastRank(mostRecentDateAvailable: Date?, man: Bool) async -> Int? { + static func anonymousCount(mostRecentDateAvailable: Date?) async -> Int? { + let context = PersistenceController.shared.localContainer.newBackgroundContext() + let importedPlayerFetchRequest = ImportedPlayer.fetchRequest() + var predicate = NSPredicate(format: "lastName == %@ && firstName == %@", "", "") + if let mostRecentDateAvailable { + predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [predicate, NSPredicate(format: "importDate == %@", mostRecentDateAvailable as CVarArg)]) + } + importedPlayerFetchRequest.predicate = predicate + let count = try? context.count(for: importedPlayerFetchRequest) + return count + } + + static func lastRank(mostRecentDateAvailable: Date?, man: Bool) async -> (Int, Int?)? { let context = PersistenceController.shared.localContainer.newBackgroundContext() let lastPlayerFetch = ImportedPlayer.fetchRequest() lastPlayerFetch.sortDescriptors = [NSSortDescriptor(keyPath: \ImportedPlayer.rank, ascending: false)] @@ -178,7 +190,7 @@ class FederalPlayer: Decodable { predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [predicate, NSPredicate(format: "importDate == %@", mostRecentDateAvailable as CVarArg)]) } lastPlayerFetch.predicate = predicate - + let count = try? context.count(for: lastPlayerFetch) do { if let lr = try context.fetch(lastPlayerFetch).first?.rank { let fetch = ImportedPlayer.fetchRequest() @@ -189,7 +201,7 @@ class FederalPlayer: Decodable { fetch.predicate = rankPredicate let lastPlayersCount = try context.count(for: fetch) - return Int(lr) + Int(lastPlayersCount) - 1 + return (Int(lr) + Int(lastPlayersCount) - 1, count) } } catch { print("ImportedPlayer.fetchRequest", error) diff --git a/PadelClub/Data/MonthData.swift b/PadelClub/Data/MonthData.swift index 4820548..c586b9c 100644 --- a/PadelClub/Data/MonthData.swift +++ b/PadelClub/Data/MonthData.swift @@ -21,21 +21,32 @@ class MonthData : ModelObject, Storable { var maleUnrankedValue: Int? = nil var femaleUnrankedValue: Int? = nil + var maleCount: Int? = nil + var femaleCount: Int? = nil + var anonymousCount: Int? = nil + init(monthKey: String) { self.monthKey = monthKey self.creationDate = Date() } + func total() -> Int { + (maleCount ?? 0) + (femaleCount ?? 0) + } + static func calculateCurrentUnrankedValues(mostRecentDateAvailable: Date) async { let lastDataSourceMaleUnranked = await FederalPlayer.lastRank(mostRecentDateAvailable: mostRecentDateAvailable, man: true) let lastDataSourceFemaleUnranked = await FederalPlayer.lastRank(mostRecentDateAvailable: mostRecentDateAvailable, man: false) - + let anonymousCount = await FederalPlayer.anonymousCount(mostRecentDateAvailable: mostRecentDateAvailable) await MainActor.run { if let lastDataSource = DataStore.shared.appSettings.lastDataSource { let currentMonthData : MonthData = Store.main.filter(isIncluded: { $0.monthKey == lastDataSource }).first ?? MonthData(monthKey: lastDataSource) - currentMonthData.maleUnrankedValue = lastDataSourceMaleUnranked - currentMonthData.femaleUnrankedValue = lastDataSourceFemaleUnranked + currentMonthData.maleUnrankedValue = lastDataSourceMaleUnranked?.0 + currentMonthData.maleCount = lastDataSourceMaleUnranked?.1 + currentMonthData.femaleUnrankedValue = lastDataSourceFemaleUnranked?.0 + currentMonthData.femaleCount = lastDataSourceFemaleUnranked?.1 + currentMonthData.anonymousCount = anonymousCount try? DataStore.shared.monthData.addOrUpdate(instance: currentMonthData) } } @@ -50,5 +61,8 @@ class MonthData : ModelObject, Storable { case _creationDate = "creationDate" case _maleUnrankedValue = "maleUnrankedValue" case _femaleUnrankedValue = "femaleUnrankedValue" + case _maleCount = "maleCount" + case _femaleCount = "femaleCount" + case _anonymousCount = "anonymousCount" } } diff --git a/PadelClub/Data/Tournament.swift b/PadelClub/Data/Tournament.swift index b9a5a2a..0447a27 100644 --- a/PadelClub/Data/Tournament.swift +++ b/PadelClub/Data/Tournament.swift @@ -562,7 +562,7 @@ class Tournament : ModelObject, Storable { let defaultSorting : [MySortDescriptor] = _defaultSorting() - let _completeTeams = _teams.sorted(using: defaultSorting, order: .ascending).filter { $0.isWildCard() == false} + let _completeTeams = _teams.sorted(using: defaultSorting, order: .ascending).filter { $0.isWildCard() == false }.prefix(teamCount).sorted(by: \.initialWeight) let wcGroupStage = _teams.filter { $0.wildCardGroupStage }.sorted(using: _currentSelectionSorting, order: .ascending) diff --git a/PadelClub/Utils/FileImportManager.swift b/PadelClub/Utils/FileImportManager.swift index c3a153d..bde0d70 100644 --- a/PadelClub/Utils/FileImportManager.swift +++ b/PadelClub/Utils/FileImportManager.swift @@ -106,7 +106,9 @@ class FileImportManager { } } let significantPlayerCount = 2 - self.weight = (players.prefix(significantPlayerCount).map { $0.computedRank } + missing.map { tournament.unrankValue(for: $0 == 1 ? true : false ) ?? 0 }).prefix(significantPlayerCount).reduce(0,+) + let pl = players.prefix(significantPlayerCount).map { $0.computedRank } + let missingPl = (missing.map { tournament.unrankValue(for: $0 == 1 ? true : false ) ?? ($0 == 1 ? 100_000 : 10_000) }).prefix(significantPlayerCount) + self.weight = pl.reduce(0,+) + missingPl.reduce(0,+) } else { self.weight = players.map { $0.computedRank }.reduce(0,+) } @@ -307,6 +309,13 @@ class FileImportManager { } private func _getPadelClubTeams(from fileContent: String, tournament: Tournament) async -> [TeamHolder] { + let dateFormatter = DateFormatter() + dateFormatter.locale = Locale(identifier: "fr_FR") + + // Set the date format to match the input string + dateFormatter.dateFormat = "EEE dd MMM yyyy 'à' HH:mm" + + var lines = fileContent.components(separatedBy: "\n\n") var results: [TeamHolder] = [] let fetchRequest = ImportedPlayer.fetchRequest() @@ -324,8 +333,8 @@ class FileImportManager { }) if let registeredPlayers, registeredPlayers.isEmpty == false { var registrationDate: Date? { - if let registrationDateData = data[safe:2]?.replacingOccurrences(of: "inscrit le ", with: "") { - return try? Date(registrationDateData, strategy: .dateTime.weekday().day().month().hour().minute()) + if let registrationDateData = data[safe:2]?.replacingOccurrences(of: "Inscrit le ", with: "") { + return dateFormatter.date(from: registrationDateData) } return nil } diff --git a/PadelClub/Views/Navigation/MainView.swift b/PadelClub/Views/Navigation/MainView.swift index 52479a1..08653e2 100644 --- a/PadelClub/Views/Navigation/MainView.swift +++ b/PadelClub/Views/Navigation/MainView.swift @@ -59,6 +59,11 @@ struct MainView: View { // PadelClubView() // .tabItem(for: .padelClub) } + .id(Store.main.currentUserUUID) + .onChange(of: Store.main.currentUserUUID) { + navigation.tournament = nil + navigation.path.removeLast(navigation.path.count) + } .environmentObject(dataStore) .task { await self._checkSourceFileAvailability() diff --git a/PadelClub/Views/Navigation/Organizer/TournamentOrganizerView.swift b/PadelClub/Views/Navigation/Organizer/TournamentOrganizerView.swift index ceeaa53..6c8f037 100644 --- a/PadelClub/Views/Navigation/Organizer/TournamentOrganizerView.swift +++ b/PadelClub/Views/Navigation/Organizer/TournamentOrganizerView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import LeStorage struct TournamentOrganizerView: View { @EnvironmentObject var dataStore: DataStore @@ -46,6 +47,9 @@ struct TournamentOrganizerView: View { } } } + .onChange(of: Store.main.currentUserUUID) { + selectedTournamentId = nil + } } } diff --git a/PadelClub/Views/Navigation/Toolbox/PadelClubView.swift b/PadelClub/Views/Navigation/Umpire/PadelClubView.swift similarity index 87% rename from PadelClub/Views/Navigation/Toolbox/PadelClubView.swift rename to PadelClub/Views/Navigation/Umpire/PadelClubView.swift index 2a5f834..cb3f9e9 100644 --- a/PadelClub/Views/Navigation/Toolbox/PadelClubView.swift +++ b/PadelClub/Views/Navigation/Umpire/PadelClubView.swift @@ -26,12 +26,6 @@ struct PadelClubView: View { animation: .default) private var players: FetchedResults - @FetchRequest( - sortDescriptors: [], - predicate: NSPredicate(format: "lastName == %@ && firstName == %@", "", ""), - animation: .default) - private var anonymousPlayers: FetchedResults - var _mostRecentDateAvailable: Date? { SourceFileManager.shared.mostRecentDateAvailable } @@ -96,27 +90,6 @@ struct PadelClubView: View { } } } - - Section { - LabeledContent { - Text(players.filter{ $0.male }.count.formatted()) - } label: { - Text("Messieurs") - } - LabeledContent { - Text(players.filter{ $0.male == false }.count.formatted()) - } label: { - Text("Dames") - } - - LabeledContent { - Text(anonymousPlayers.count.formatted()) - } label: { - Text("Joueurs anonymes") - } - } header: { - Text(players.count.formatted() + " joueurs") - } } if importingFiles { @@ -136,6 +109,29 @@ struct PadelClubView: View { let monthData = dataStore.monthData.sorted(by: \.creationDate).reversed() ForEach(monthData) { monthData in Section { + LabeledContent { + if let maleCount = monthData.maleCount { + Text(maleCount.formatted()) + } + } label: { + Text("Messieurs") + } + LabeledContent { + if let femaleCount = monthData.femaleCount { + Text(femaleCount.formatted()) + } + } label: { + Text("Dames") + } + + LabeledContent { + if let anonymousCount = monthData.anonymousCount { + Text(anonymousCount.formatted()) + } + } label: { + Text("Joueurs anonymes") + } + LabeledContent { if let maleUnrankedValue = monthData.maleUnrankedValue { Text(maleUnrankedValue.formatted()) @@ -153,7 +149,22 @@ struct PadelClubView: View { Text("Dames") } } header: { - Text(monthData.monthKey) + HStack { + Text(monthData.monthKey) + Spacer() + Text(monthData.total().formatted() + " joueurs") + } + } footer: { + HStack { + Spacer() + FooterButtonView("recalculer") { + Task { + if let monthKeyDate = URL.importDateFormatter.date(from: monthData.monthKey) { + await MonthData.calculateCurrentUnrankedValues(mostRecentDateAvailable: monthKeyDate) + } + } + } + } } } } diff --git a/PadelClub/Views/Team/Components/TeamHeaderView.swift b/PadelClub/Views/Team/Components/TeamHeaderView.swift index 76818a6..700ec47 100644 --- a/PadelClub/Views/Team/Components/TeamHeaderView.swift +++ b/PadelClub/Views/Team/Components/TeamHeaderView.swift @@ -19,20 +19,31 @@ struct TeamHeaderView: View { private func _teamHeaderView(_ team: TeamRegistration, teamIndex: Int?) -> some View { HStack { if let teamIndex { - Text("#" + (teamIndex + 1).formatted()) + VStack(alignment: .leading, spacing: 0) { + Text("rang").font(.caption) + Text("#" + (teamIndex + 1).formatted()) + } } if team.unsortedPlayers().isEmpty == false { - Text(team.weight.formatted()) - } - if team.isWildCard() { - Text("wildcard").italic().font(.caption) + VStack(alignment: .leading, spacing: 0) { + Text("poids").font(.caption) + Text(team.weight.formatted()) + } } Spacer() - if team.walkOut { - Text("WO") - } else if let teamIndex, let tournament { - Text(tournament.cutLabel(index: teamIndex)) + VStack(alignment: .trailing, spacing: 0) { + if team.walkOut { + Text("").font(.caption) + Text("WO") + } else if let teamIndex, let tournament { + if team.isWildCard() { + Text("wildcard").font(.caption).italic() + } else { + Text("").font(.caption) + } + Text(tournament.cutLabel(index: teamIndex)) + } } } } diff --git a/PadelClub/Views/Tournament/FileImportView.swift b/PadelClub/Views/Tournament/FileImportView.swift index 8b47214..6b781b8 100644 --- a/PadelClub/Views/Tournament/FileImportView.swift +++ b/PadelClub/Views/Tournament/FileImportView.swift @@ -246,6 +246,10 @@ struct FileImportView: View { errorMessage = nil teams.removeAll() } + if let rankSourceDate = tournament.rankSourceDate, tournament.unrankValue(for: false) == nil || tournament.unrankValue(for: true) == nil { + await MonthData.calculateCurrentUnrankedValues(mostRecentDateAvailable: rankSourceDate) + } + self.teams = await FileImportManager.shared.createTeams(from: fileContent, tournament: tournament, fileProvider: fileProvider) await MainActor.run { convertingFile = false diff --git a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift index 991d737..73c09f6 100644 --- a/PadelClub/Views/Tournament/Shared/TournamentCellView.swift +++ b/PadelClub/Views/Tournament/Shared/TournamentCellView.swift @@ -69,7 +69,9 @@ struct TournamentCellView: View { } Spacer() if let tournament = tournament as? Tournament, displayStyle == .wide { - Text(tournament.sortedTeams().count.formatted()) + let hasStarted = tournament.inscriptionClosed() || tournament.hasStarted() + let count = hasStarted ? tournament.selectedSortedTeams().count : tournament.unsortedTeams().count + Text(count.formatted()) } else if let federalTournament = tournament as? FederalTournament { Button { _createOrShow(federalTournament: federalTournament, existingTournament: existingTournament, build: build) @@ -89,7 +91,9 @@ struct TournamentCellView: View { Text(tournament.durationLabel()) Spacer() if let tournament = tournament as? Tournament { - Text("équipes") + let hasStarted = tournament.inscriptionClosed() || tournament.hasStarted() + let word = hasStarted ? "équipes" : "inscriptions" + Text(word) } } Text(tournament.subtitleLabel())