// // FileImportManager.swift // PadelClub // // Created by Razmig Sarkissian on 01/03/2024. // import Foundation class FileImportManager { static let shared = FileImportManager() func foundInWomenData(license: String?) -> Bool { guard let license = license?.strippedLicense else { return false } do { return try SourceFileManager.shared.allFilesSortedByDate(false).first(where: { let fileContent = try String(contentsOf: $0) return fileContent.contains(";\(license);") }) != nil } catch { print("history", error) return false } } func foundInMenData(license: String?) -> Bool { guard let license = license?.strippedLicense else { return false } do { return try SourceFileManager.shared.allFilesSortedByDate(true).first(where: { let fileContent = try String(contentsOf: $0) return fileContent.contains(";\(license);") }) != nil } catch { print("history", error) return false } } enum FileProvider: CaseIterable, Identifiable { var id: Self { self } case frenchFederation case unknown var localizedLabel: String { switch self { case .frenchFederation: return "FFT" case .unknown: return "Inconnu" } } } struct TeamHolder: Identifiable { let id: UUID = UUID() let playerOne: PlayerRegistration let playerTwo: PlayerRegistration let weight: Int let tournamentCategory: TournamentCategory let previousTeam: TeamRegistration? init(playerOne: PlayerRegistration, playerTwo: PlayerRegistration, tournamentCategory: TournamentCategory, previousTeam: TeamRegistration?) { self.playerOne = playerOne self.playerTwo = playerTwo self.tournamentCategory = tournamentCategory self.previousTeam = previousTeam self.weight = playerOne.weight + playerTwo.weight } var players: Set { Set([playerOne, playerTwo]) } func index(in teams: [TeamHolder]) -> Int? { teams.firstIndex(where: { $0.id == id }) } func formattedSeedIndex(index: Int?) -> String { if let index { return "#\(index + 1)" } else { return "###" } } func formattedSeed(in teams: [TeamHolder]) -> String { if let index = index(in: teams) { return "#\(index + 1)" } else { return "###" } } } static let FFT_ASSIMILATION_WOMAN_IN_MAN = "A calculer selon la pondération en vigueur" func createTeams(from fileContent: String, tournament: Tournament, fileProvider: FileProvider = .frenchFederation) async -> [TeamHolder] { let lines = fileContent.components(separatedBy: "\n") guard let firstLine = lines.first else { return [] } var separator = "," if firstLine.contains(";") { separator = ";" } let headerCount = firstLine.components(separatedBy: separator).count var results: [TeamHolder] = [] if headerCount == 23 && fileProvider == .unknown { //PBL let fetchRequest = ImportedPlayer.fetchRequest() let federalContext = PersistenceController.shared.localContainer.viewContext lines.dropFirst().forEach { line in let data = line.components(separatedBy: separator) if data.count == 23 { // let team = Team(context: context) // let brand = Brand(context: context) // brand.title = data[2].trimmed // brand.qualifier = data[0].trimmed // brand.country = data[1].trimmed // brand.lineOfBusiness = data[3].trimmed // if brand.lineOfBusiness == "Bâtiment / Immo" { //quick fix // brand.lineOfBusiness = "Bâtiment / Immo / Transport" // } // brand.name = data[4].trimmed // team.brand = brand // // for i in 0...5 { // let sex = data[i*3+5] // let lastName = data[i*3+6].trimmed // let firstName = data[i*3+7].trimmed // if lastName.isEmpty == false { // let playerOne = Player(context: context) // let predicate = NSPredicate(format: "(canonicalLastName matches[cd] %@ OR canonicalLastName matches[cd] %@) AND (canonicalFirstName matches[cd] %@ OR canonicalFirstName matches[cd] %@)", lastName, lastName.removePunctuationAndHyphens, firstName, firstName.removePunctuationAndHyphens) // fetchRequest.predicate = predicate // if let playerFound = try? federalContext.fetch(fetchRequest).first { // playerOne.updateWithImportedPlayer(playerFound) // } else { // playerOne.lastName = lastName // playerOne.firstName = firstName // playerOne.sex = sex == "H" ? 1 : sex == "F" ? 0 : -1 // playerOne.currentRank = tournament?.lastRankMan ?? 0 // } // team.addToPlayers(playerOne) // } // } // team.category = TournamentCategory.men.importingRawValue // // if let players = team.players, players.count > 0 { // results.append(team) // } else { // context.delete(team) // } } } return results } else if headerCount <= 18 && fileProvider == .frenchFederation { Array(lines.dropFirst()).chunked(into: 2).forEach { teamLines in if teamLines.count == 2 { let dataOne = teamLines[0].replacingOccurrences(of: "\"", with: "").components(separatedBy: separator) var dataTwo = teamLines[1].replacingOccurrences(of: "\"", with: "").components(separatedBy: separator) if dataOne[11] != dataTwo[3] || dataOne[12] != dataTwo[4] { if let found = lines.map({ $0.replacingOccurrences(of: "\"", with: "").components(separatedBy: separator) }).first(where: { components in return dataOne[11] == components[3] && dataOne[12] == components[4] }) { dataTwo = found } } if dataOne.count == dataTwo.count { let category = dataOne[0] var tournamentCategory: TournamentCategory { switch category { case "Double Messieurs": return .men case "Double Dames": return .women case "Double Mixte": return .mix default: return .men } } let resultOne = Array(dataOne.dropFirst(3).dropLast()) let resultTwo = Array(dataTwo.dropFirst(3).dropLast()) let sexUnknown: Bool = (resultOne.last?.hasPrefix(FileImportManager.FFT_ASSIMILATION_WOMAN_IN_MAN) == true) || (resultTwo.last?.hasPrefix(FileImportManager.FFT_ASSIMILATION_WOMAN_IN_MAN) == true) var sexPlayerOne : Int { switch tournamentCategory { case .men: return 1 case .women: return 0 case .mix: return 0 } } var sexPlayerTwo : Int { switch tournamentCategory { case .men: return 1 case .women: return 0 case .mix: return 1 } } let playerOne = PlayerRegistration(federalData: Array(resultOne[0...7]), sex: sexPlayerOne, sexUnknown: sexUnknown) playerOne.setWeight(in: tournament) let playerTwo = PlayerRegistration(federalData: Array(resultTwo[0...7]), sex: sexPlayerTwo, sexUnknown: sexUnknown) playerTwo.setWeight(in: tournament) let team = TeamHolder(playerOne: playerOne, playerTwo: playerTwo, tournamentCategory: tournamentCategory, previousTeam: tournament.findTeam([playerOne, playerTwo])) results.append(team) } } } return results } else if headerCount > 18 && fileProvider == .frenchFederation { lines.dropFirst().forEach { line in let data = line.components(separatedBy: separator) if data.count > 18 { let category = data[0] let sexUnknown: Bool = (data.last?.hasPrefix(FileImportManager.FFT_ASSIMILATION_WOMAN_IN_MAN) == true) var tournamentCategory: TournamentCategory { switch category { case "Double Messieurs": return .men case "Double Dames": return .women case "Double Mixte": return .mix default: return .men } } let result = Array(data.dropFirst(3).dropLast()) var sexPlayerOne : Int { switch tournamentCategory { case .men: return 1 case .women: return 0 case .mix: return 1 } } var sexPlayerTwo : Int { switch tournamentCategory { case .men: return 1 case .women: return 0 case .mix: return 0 } } let playerOne = PlayerRegistration(federalData: Array(result[0...7]), sex: sexPlayerOne, sexUnknown: sexUnknown) playerOne.setWeight(in: tournament) let playerTwo = PlayerRegistration(federalData: Array(result[8...]), sex: sexPlayerTwo, sexUnknown: sexUnknown) playerTwo.setWeight(in: tournament) let team = TeamHolder(playerOne: playerOne, playerTwo: playerTwo, tournamentCategory: tournamentCategory, previousTeam: tournament.findTeam([playerOne, playerTwo])) results.append(team) } } return results } else { return [] } } func importDataFromFFT() async -> String? { if let importingDate = SourceFileManager.shared.mostRecentDateAvailable { for source in SourceFile.allCases { for fileURL in source.currentURLs { let p = readCSV(inputFile: fileURL) await importingChunkOfPlayers(p, importingDate: importingDate) } } return URL.importDateFormatter.string(from: importingDate) } return nil } func readCSV(inputFile: URL) -> [FederalPlayer] { do { let fileContent = try String(contentsOf: inputFile) return loadFromCSV(fileContent: fileContent, isMale: inputFile.manData) } catch { print("error: \(error)") // to do deal with errors } return [] } func loadFromCSV(fileContent: String, isMale: Bool) -> [FederalPlayer] { let lines = fileContent.components(separatedBy: "\n") return lines.compactMap { line in if line.components(separatedBy: ";").count < 10 { } else { let data = line.components(separatedBy: ";").joined(separator: "\n") return FederalPlayer(data, isMale: isMale) } return nil } } func importingChunkOfPlayers(_ players: [FederalPlayer], importingDate: Date) async { for chunk in players.chunked(into: 1000) { await PersistenceController.shared.batchInsertPlayers(chunk, importingDate: importingDate) } } }