@ -131,13 +131,15 @@ class FileImportManager {
let previousTeam : TeamRegistration ?
var registrationDate : Date ? = nil
var name : String ? = nil
var teamChampionship : TeamChampionship ?
init ( players : [ PlayerRegistration ] , tournamentCategory : TournamentCategory , tournamentAgeCategory : FederalTournamentAge , previousTeam : TeamRegistration ? , registrationDate : Date ? = nil , name : String ? = nil , tournament : Tournament ) {
init ( players : [ PlayerRegistration ] , tournamentCategory : TournamentCategory , tournamentAgeCategory : FederalTournamentAge , previousTeam : TeamRegistration ? , registrationDate : Date ? = nil , name : String ? = nil , teamChampionship : TeamChampionship ? = nil , t ournament : Tournament ) {
self . players = Set ( players )
self . tournamentCategory = tournamentCategory
self . tournamentAgeCategory = tournamentAgeCategory
self . name = name
self . previousTeam = previousTeam
self . teamChampionship = teamChampionship
if players . count < 2 {
let s = players . compactMap { $0 . sex ? . rawValue }
var missing = tournamentCategory . mandatoryPlayerType ( )
@ -146,7 +148,7 @@ class FileImportManager {
missing . remove ( at : index )
}
}
let significantPlayerCount = 2
let significantPlayerCount = tournament . significantPlayerCount ( )
let pl = players . prefix ( significantPlayerCount ) . map { $0 . computedRank }
let missingPl = ( missing . map { tournament . unrankValue ( for : $0 = = 1 ? true : false ) ? ? ( $0 = = 1 ? 70_000 : 10_000 ) } ) . prefix ( significantPlayerCount )
self . weight = pl . reduce ( 0 , + ) + missingPl . reduce ( 0 , + )
@ -179,7 +181,7 @@ class FileImportManager {
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 , checkingCategoryDisabled : Bool , chunkByParameter : Bool ) async throws -> [ TeamHolder ] {
func createTeams ( from fileContent : String , tournament : Tournament , fileProvider : FileProvider = . frenchFederation , checkingCategoryDisabled : Bool , chunkMode : ChunkMode ) async throws -> [ TeamHolder ] {
switch fileProvider {
case . frenchFederation :
@ -187,9 +189,9 @@ class FileImportManager {
case . padelClub :
return await _getPadelClubTeams ( from : fileContent , tournament : tournament )
case . custom :
return await _getPadelBusinessLeagueTeams ( from : fileContent , chunkByParameter : chunkByParameter , autoSearch : false , tournament : tournament )
return await _getPadelBusinessLeagueTeams ( from : fileContent , chunkMode : chunkMode , autoSearch : false , tournament : tournament )
case . customAutoSearch :
return await _getPadelBusinessLeagueTeams ( from : fileContent , chunkByParameter : chunkByParameter , autoSearch : true , tournament : tournament )
return await _getPadelBusinessLeagueTeams ( from : fileContent , chunkMode : chunkMode , autoSearch : true , tournament : tournament )
}
}
@ -425,7 +427,7 @@ class FileImportManager {
return results
}
private func _getPadelBusinessLeagueTeams ( from fileContent : String , chunkByParameter : Bool , autoSearch : Bool , tournament : Tournament ) async -> [ TeamHolder ] {
private func _getPadelBusinessLeagueTeams ( from fileContent : String , chunkMode : ChunkMode , autoSearch : Bool , tournament : Tournament ) async -> [ TeamHolder ] {
let lines = fileContent . replacingOccurrences ( of : " \" " , with : " " ) . components ( separatedBy : " \n " )
guard let firstLine = lines . first else { return [ ] }
var separator = " , "
@ -436,13 +438,27 @@ class FileImportManager {
let federalContext = PersistenceController . shared . localContainer . viewContext
var chunks : [ [ String ] ] = [ ]
if chunkByParameter {
switch chunkMode {
case . byParameter :
chunks = lines . chunked ( byParameterAt : 1 )
} else {
case . byCoupleOfLines :
chunks = lines . chunked ( into : 2 )
case . byColumn :
chunks = lines . extractPlayers ( filterKey : tournament . tournamentCategory . importingRawValue . capitalized , separator : separator )
}
let results = chunks . map { team in
let results = chunks . map { teamSource in
var teamName : String ? = nil
var teamChampionship : TeamChampionship ? = nil
var team = teamSource
if chunkMode = = . byColumn {
if let first = teamSource . first ? . components ( separatedBy : separator ) {
team = Array ( teamSource . dropFirst ( ) )
teamChampionship = TeamChampionship ( registrationDate : first [ 0 ] , registrationMail : first [ 1 ] , clubCode : first [ 2 ] , teamIndex : first [ 3 ] , clubName : first [ 4 ] )
}
}
let players = team . map { player in
let data = player . components ( separatedBy : separator )
let lastName : String = data [ safe : 2 ] ? . prefixTrimmed ( 50 ) ? ? " "
@ -456,27 +472,54 @@ class FileImportManager {
let rank : Int ? = data [ safe : 6 ] ? . trimmed . toInt ( )
let licenceId : String ? = data [ safe : 7 ] ? . prefixTrimmed ( 50 )
let club : String ? = data [ safe : 8 ] ? . prefixTrimmed ( 200 )
let predicate = NSPredicate ( format : " firstName like[cd] %@ && lastName like[cd] %@ " , firstName , lastName )
fetchRequest . predicate = predicate
let found = try ? federalContext . fetch ( fetchRequest ) . first
if let found , autoSearch {
let player = PlayerRegistration ( importedPlayer : found )
player . setComputedRank ( in : tournament )
player . email = email
player . phoneNumber = phoneNumber
return player
let status : String ? = data [ safe : 9 ]
if chunkMode = = . byColumn {
let predicate = NSPredicate ( format : " license == %@ " , licenceId ! . strippedLicense ! )
fetchRequest . predicate = predicate
let found = try ? federalContext . fetch ( fetchRequest ) . first
if let found {
let player = PlayerRegistration ( importedPlayer : found )
player . setComputedRank ( in : tournament )
player . sourceName = lastName
player . isNVEQ = status = = " NVEQ "
player . clubCode = found . clubCode
return player
} else {
let player = PlayerRegistration ( firstName : firstName , lastName : lastName , licenceId : licenceId , rank : rank , sex : sex , clubName : club , phoneNumber : phoneNumber , email : email )
player . sourceName = lastName
player . isNVEQ = status = = " NVEQ "
if rank = = nil {
player . setComputedRank ( in : tournament )
} else {
player . computedRank = rank ? ? 0
}
return player
}
} else {
let player = PlayerRegistration ( firstName : firstName , lastName : lastName , licenceId : licenceId , rank : rank , sex : sex , clubName : club , phoneNumber : phoneNumber , email : email )
if rank = = nil , autoSearch {
let predicate = NSPredicate ( format : " firstName like[cd] %@ && lastName like[cd] %@ " , firstName , lastName )
fetchRequest . predicate = predicate
let found = try ? federalContext . fetch ( fetchRequest ) . first
if let found , autoSearch {
let player = PlayerRegistration ( importedPlayer : found )
player . setComputedRank ( in : tournament )
player . email = email
player . phoneNumber = phoneNumber
return player
} else {
player . computedRank = rank ? ? 0
let player = PlayerRegistration ( firstName : firstName , lastName : lastName , licenceId : licenceId , rank : rank , sex : sex , clubName : club , phoneNumber : phoneNumber , email : email )
if rank = = nil , autoSearch {
player . setComputedRank ( in : tournament )
} else {
player . computedRank = rank ? ? 0
}
return player
}
return player
}
}
return TeamHolder ( players : players , tournamentCategory : tournament . tournamentCategory , tournamentAgeCategory : tournament . federalTournamentAge , previousTeam : nil , name : teamName , tournament : tournament )
return TeamHolder ( players : players , tournamentCategory : tournament . tournamentCategory , tournamentAgeCategory : tournament . federalTournamentAge , previousTeam : nil , teamChampionship : teamChampionship , tournament : tournament )
}
return results
}
@ -513,5 +556,262 @@ extension Array where Element == String {
return groups . map { $0 . value }
}
}
func extractPlayers ( filterKey : String = " Messieurs " , separator : String = " ; " ) -> [ [ String ] ] {
return self . dropFirst ( ) . compactMap { line in
let components = line . components ( separatedBy : separator )
guard components . count >= 62 else { return nil }
guard components . contains ( filterKey ) else { return nil }
var players : [ PlayerChampionship ] = [ ]
let teamChampionship = TeamChampionship ( registrationDate : components [ 0 ] , registrationMail : components [ 1 ] , clubCode : components [ 3 ] , teamIndex : components [ 5 ] , clubName : components [ 2 ] )
// A d d c a p t a i n a n d c o a c h f i r s t
// p l a y e r s . a p p e n d ( P l a y e r C h a m p i o n s h i p . c a p t a i n ( c o m p o n e n t s ) )
// p l a y e r s . a p p e n d ( P l a y e r C h a m p i o n s h i p . c o a c h ( c o m p o n e n t s ) )
// E x t r a c t t e a m i n f o r m a t i o n
let teamType = components [ 4 ]
let sex = teamType . lowercased ( ) . contains ( " dames " ) ? " f " : " m "
// P r o c e s s u p t o 1 0 p l a y e r s
for i in 0. . < 10 {
let lastNameIndex = 12 + ( i * 5 )
let firstNameIndex = 13 + ( i * 5 )
let licenseIndex = 14 + ( i * 5 )
let rankingIndex = 15 + ( i * 5 )
let statusIndex = 16 + ( i * 5 )
guard lastNameIndex < components . count ,
! components [ lastNameIndex ] . isEmpty else {
continue
}
let statusString = components [ statusIndex ]
let status : PlayerChampionship . Status = statusString . hasPrefix ( " NVEQ " ) ? . nveq : . eq
var licenseNumber = components [ licenseIndex ]
// v a r r a n k i n g = c o m p o n e n t s [ r a n k i n g I n d e x ]
let strippedLicense = components [ licenseIndex ] . strippedLicense
let strippedLicenseRank = components [ rankingIndex ] . strippedLicense
if strippedLicense = = nil && strippedLicenseRank != nil {
licenseNumber = components [ rankingIndex ]
// r a n k i n g = c o m p o n e n t s [ l i c e n s e I n d e x ]
}
let player = PlayerChampionship (
lastName : components [ lastNameIndex ] ,
firstName : components [ firstNameIndex ] ,
licenseNumber : licenseNumber ,
ranking : nil ,
status : status ,
email : nil ,
mobileNumber : nil
)
players . append ( player )
}
return [ teamChampionship . rawValue ( separator : separator ) ] + players . map { player in
player . rawValue ( separator : separator , sex : sex )
}
}
}
}
struct TeamChampionship {
let registrationDate : String
let registrationMail : String
let clubCode : String
let teamIndex : String
let clubName : String
func getRegistrationDate ( ) -> Date ? {
let dateFormatter = DateFormatter ( )
dateFormatter . dateFormat = " yyyy-MM-dd HH:mm:ss.SSS "
if let date = dateFormatter . date ( from : registrationDate ) {
return date
} else {
return nil
}
}
func rawValue ( separator : String = " ; " ) -> String {
let components = [
registrationDate ,
registrationMail ,
clubCode . replaceCharactersFromSet ( characterSet : . whitespacesAndNewlines ) . trimmed ,
teamIndex . replaceCharactersFromSet ( characterSet : . whitespacesAndNewlines ) . trimmed ,
clubName . trimmed
]
return components . joined ( separator : separator )
}
}
struct PlayerChampionship {
enum Status : String {
case eq = " EQ " // é t a i t l i c e n c i é a u c l u b l ' a n d e r n i e r
case nveq = " NVEQ " // N ' a p a s j o u é a v e c l e c l u b l ' a n d e r n i e r
case captain = " CAPTAIN "
case coach = " COACH "
}
let lastName : String
let firstName : String
let licenseNumber : String
let ranking : Int ?
let status : Status
let email : String ?
let mobileNumber : String ?
static func captain ( _ components : [ String ] ) -> PlayerChampionship {
let fullName = components [ 6 ] . components ( separatedBy : " " )
let lastName = fullName . count > 1 ? fullName [ 0 ] : components [ 6 ]
let firstName = fullName . count > 1 ? fullName [ 1 ] : " "
return PlayerChampionship (
lastName : lastName ,
firstName : firstName ,
licenseNumber : " " ,
ranking : 0 ,
status : . captain ,
email : components [ 8 ] ,
mobileNumber : components [ 7 ]
)
}
static func coach ( _ components : [ String ] ) -> PlayerChampionship {
let fullName = components [ 9 ] . components ( separatedBy : " " )
let lastName = fullName . count > 1 ? fullName [ 0 ] : components [ 9 ]
let firstName = fullName . count > 1 ? fullName [ 1 ] : " "
return PlayerChampionship (
lastName : lastName ,
firstName : firstName ,
licenseNumber : " " ,
ranking : 0 ,
status : . coach ,
email : components [ 11 ] ,
mobileNumber : components [ 10 ]
)
}
func rawValue ( separator : String = " ; " , sex : String = " m " ) -> String {
let components = [
sex ,
" " ,
lastName . trimmed ,
firstName . trimmed ,
" " ,
" " ,
" " ,
licenseNumber . replaceCharactersFromSet ( characterSet : . whitespacesAndNewlines ) ,
" " ,
status . rawValue
]
return components . joined ( separator : separator )
}
}
enum ChunkMode : Int , Identifiable , CaseIterable {
var id : Int { self . rawValue }
case byParameter
case byCoupleOfLines
case byColumn
func localizedChunkModeLabel ( ) -> String {
switch self {
case . byParameter :
return " Nom d'équipe "
case . byCoupleOfLines :
return " Groupe de 2 lignes "
case . byColumn :
return " Par colonne "
}
}
}
enum ChampionshipAlert : LocalizedError {
case clubCodeInvalid ( TeamRegistration )
case tooManyNVEQ ( TeamRegistration )
case tooManyPlayers ( TeamRegistration )
case playerClubInvalid ( PlayerRegistration )
case playerLicenseInvalid ( PlayerRegistration )
case playerNameInvalid ( PlayerRegistration )
case unranked ( PlayerRegistration )
case playerAgeInvalid ( PlayerRegistration )
case playerSexInvalid ( PlayerRegistration )
var errorDescription : String ? {
switch self {
case . clubCodeInvalid ( let teamRegistration ) :
if let clubCode = teamRegistration . clubCode {
return " CODE NOK : \( clubCode ) "
} else {
return " aucun code club "
}
case . tooManyNVEQ ( let teamRegistration ) :
return " TOO MANY NVEQ "
case . tooManyPlayers ( let teamRegistration ) :
return " TOO MANY PLAYERS "
case . playerClubInvalid ( let playerRegistration ) :
return playerRegistration . errorDescription ( championshipAlert : self )
case . playerLicenseInvalid ( let playerRegistration ) :
return playerRegistration . errorDescription ( championshipAlert : self )
case . playerNameInvalid ( let playerRegistration ) :
return playerRegistration . errorDescription ( championshipAlert : self )
case . unranked ( let playerRegistration ) :
return playerRegistration . errorDescription ( championshipAlert : self )
case . playerAgeInvalid ( let playerRegistration ) :
return playerRegistration . errorDescription ( championshipAlert : self )
case . playerSexInvalid ( let playerRegistration ) :
return playerRegistration . errorDescription ( championshipAlert : self )
}
}
}
extension PlayerRegistration {
func errorDescription ( championshipAlert : ChampionshipAlert ) -> String ? {
var message = lastName + " -> "
switch championshipAlert {
case . clubCodeInvalid , . tooManyNVEQ , . tooManyPlayers :
return nil
case . unranked :
message += " NC NOT FOUND "
case . playerClubInvalid :
if let clubCode {
message += " CLUB NOK : " + clubCode
} else {
message += " aucun club "
}
case . playerLicenseInvalid :
if let licenceId {
message += " LICENCE NOK : " + licenceId
} else {
message += " aucune licence "
}
case . playerNameInvalid :
if let sourceName {
message += " NOM NOK : " + sourceName
} else {
message += " aucun nom source "
}
case . playerAgeInvalid :
if let computedAge {
message += " AGE NOK : \( computedAge ) " + " ans "
} else {
message += " aucun âge "
}
case . playerSexInvalid :
message += " SEXE NOK : " + ( isMalePlayer ( ) ? " H " : " F " )
}
return message
}
}