You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
PadelClub/PadelClub/Utils/FileImportManager.swift

367 lines
16 KiB

//
// FileImportManager.swift
// PadelClub
//
// Created by Razmig Sarkissian on 01/03/2024.
//
import Foundation
import LeStorage
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 padelClub
case unknown
var localizedLabel: String {
switch self {
case .padelClub:
return "Padel Club"
case .frenchFederation:
return "FFT"
case .unknown:
return "Inconnu"
}
}
}
struct TeamHolder: Identifiable {
let id: UUID = UUID()
let players: Set<PlayerRegistration>
let weight: Int
let tournamentCategory: TournamentCategory
let previousTeam: TeamRegistration?
var registrationDate: Date? = nil
init(players: [PlayerRegistration], tournamentCategory: TournamentCategory, previousTeam: TeamRegistration?, registrationDate: Date? = nil) {
self.players = Set(players)
self.tournamentCategory = tournamentCategory
self.previousTeam = previousTeam
self.weight = players.map { $0.computedRank }.reduce(0,+)
self.registrationDate = registrationDate
}
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] {
switch fileProvider {
case .frenchFederation:
return await _getFederalTeams(from: fileContent, tournament: tournament)
case .padelClub:
return await _getPadelClubTeams(from: fileContent, tournament: tournament)
case .unknown:
return await _getPadelBusinessLeagueTeams(from: fileContent, tournament: tournament)
}
}
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: 2000) {
await PersistenceController.shared.batchInsertPlayers(chunk, importingDate: importingDate)
}
}
private func _getFederalTeams(from fileContent: String, tournament: Tournament) 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 <= 18 {
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.setComputedRank(in: tournament)
let playerTwo = PlayerRegistration(federalData: Array(resultTwo[0...7]), sex: sexPlayerTwo, sexUnknown: sexUnknown)
playerTwo.setComputedRank(in: tournament)
let team = TeamHolder(players: [playerOne, playerTwo], tournamentCategory: tournamentCategory, previousTeam: tournament.findTeam([playerOne, playerTwo]))
results.append(team)
}
}
}
return results
} else {
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.setComputedRank(in: tournament)
let playerTwo = PlayerRegistration(federalData: Array(result[8...]), sex: sexPlayerTwo, sexUnknown: sexUnknown)
playerTwo.setComputedRank(in: tournament)
let team = TeamHolder(players: [playerOne, playerTwo], tournamentCategory: tournamentCategory, previousTeam: tournament.findTeam([playerOne, playerTwo]))
results.append(team)
}
}
return results
}
}
private func _getPadelClubTeams(from fileContent: String, tournament: Tournament) async -> [TeamHolder] {
let lines = fileContent.components(separatedBy: "\n\n")
var results: [TeamHolder] = []
let fetchRequest = ImportedPlayer.fetchRequest()
let federalContext = PersistenceController.shared.localContainer.viewContext
lines.forEach { team in
let data = team.components(separatedBy: "\n")
let players = team.licencesFound()
fetchRequest.predicate = NSPredicate(format: "license IN %@", players)
let found = try? federalContext.fetch(fetchRequest)
let registeredPlayers = found?.map({ importedPlayer in
let player = PlayerRegistration(importedPlayer: importedPlayer)
player.setComputedRank(in: tournament)
return player
})
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())
}
return nil
}
let team = TeamHolder(players: registeredPlayers, tournamentCategory: tournament.tournamentCategory, previousTeam: tournament.findTeam(registeredPlayers), registrationDate: registrationDate)
results.append(team)
}
}
return results
}
private func _getPadelBusinessLeagueTeams(from fileContent: String, tournament: Tournament) 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 {
//todo
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
}
return []
}
}