paca_championship
Raz 10 months ago
parent 472a456465
commit 7dc2619543
  1. 4
      PadelClub.xcodeproj/project.pbxproj
  2. 42
      PadelClub/Data/PlayerRegistration.swift
  3. 12
      PadelClub/Data/TeamRegistration.swift
  4. 2
      PadelClub/Data/Tournament.swift
  5. 24
      PadelClub/Utils/FileImportManager.swift
  6. 1
      PadelClub/Views/Navigation/MainView.swift
  7. 3
      PadelClub/Views/Navigation/Umpire/PadelClubView.swift
  8. 71
      PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift

@ -3306,7 +3306,7 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.42;
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub;
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub.dec;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
@ -3353,7 +3353,7 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.42;
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub;
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub.dec;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";

@ -45,10 +45,12 @@ final class PlayerRegistration: ModelObject, Storable {
var sourceName: String?
var isNveq: Bool = false
var isEQConfirmed: Bool?
var duplicatePlayers: Int?
var isYearValid: Bool?
func localizedSourceLabel() -> String {
switch source {
case .frenchFederation, .frenchFederationVerified:
case .frenchFederation, .frenchFederationVerified, .frenchFederationEQVerified:
return "Via la base fédérale"
case .beachPadel:
return "Via le fichier beach-padel"
@ -192,21 +194,29 @@ final class PlayerRegistration: ModelObject, Storable {
}
func isVerified() -> String {
source == .frenchFederationVerified ? "ok" : ""
switch source {
case .frenchFederationVerified:
return "ok"
case .frenchFederationEQVerified:
return "ok2"
default:
return ""
}
}
var duplicatePlayers: Int?
func championshipAlerts(tournament: Tournament, allPlayers: [(String, String)], forRegional: Bool) -> [ChampionshipAlert] {
var alerts = [ChampionshipAlert]()
if forRegional {
if forRegional, source != .frenchFederationEQVerified {
if isNveq == false && (isEQConfirmed == false || isEQConfirmed == nil) {
alerts.append(.notEQ(isEQConfirmed, self))
}
}
if isYearValid == nil || isYearValid == false {
alerts.append(.isYearValid(isYearValid, self))
}
if duplicatePlayers == nil {
let teamClubCode = self.team()?.clubCode
let strippedLicense = self.licenceId?.strippedLicense
@ -223,7 +233,7 @@ final class PlayerRegistration: ModelObject, Storable {
if isUnranked() && source == nil {
alerts.append(.unranked(self))
} else if source != .frenchFederationVerified {
} else if source != .frenchFederationVerified && source != .frenchFederationEQVerified {
if tournament.tournamentCategory == .men && isMalePlayer() == false {
alerts.append(.playerSexInvalid(self))
}
@ -242,6 +252,8 @@ final class PlayerRegistration: ModelObject, Storable {
if isLicenceOK() == false {
alerts.append(.playerLicenseInvalid(self))
}
}
return alerts
@ -523,6 +535,10 @@ defer {
case _clubCode = "clubCode"
case _sourceName = "sourceName"
case _isNveq = "isNveq"
case _duplicatePlayers = "duplicatePlayers"
case _isYearValid = "isYearValid"
case _isEQConfirmed = "isEQConfirmed"
}
init(from decoder: Decoder) throws {
@ -555,6 +571,10 @@ defer {
clubCode = try container.decodeIfPresent(String.self, forKey: ._clubCode)
isNveq = try container.decodeIfPresent(Bool.self, forKey: ._isNveq) ?? false
sourceName = try container.decodeIfPresent(String.self, forKey: ._sourceName)
isEQConfirmed = try container.decodeIfPresent(Bool.self, forKey: ._isEQConfirmed)
duplicatePlayers = try container.decodeIfPresent(Int.self, forKey: ._duplicatePlayers)
isYearValid = try container.decodeIfPresent(Bool.self, forKey: ._isYearValid)
sourceName = try container.decodeIfPresent(String.self, forKey: ._sourceName)
}
func encode(to encoder: Encoder) throws {
@ -585,6 +605,9 @@ defer {
try container.encode(clubCode, forKey: ._clubCode)
try container.encode(sourceName, forKey: ._sourceName)
try container.encode(isNveq, forKey: ._isNveq)
try container.encode(isEQConfirmed, forKey: ._isEQConfirmed)
try container.encode(duplicatePlayers, forKey: ._duplicatePlayers)
try container.encode(isYearValid, forKey: ._isYearValid)
}
enum PlayerDataSource: Int, Codable {
@ -592,6 +615,7 @@ defer {
case beachPadel = 1
case onlineRegistration = 2
case frenchFederationVerified = 3
case frenchFederationEQVerified = 4
}
enum PlayerSexType: Int, Hashable, CaseIterable, Identifiable, Codable {

@ -449,18 +449,18 @@ final class TeamRegistration: ModelObject, Storable {
let players = players()
if teamIndex <= 16, players.filter({ $0.isNveq }).count > 2 {
if teamIndex <= 24, players.filter({ $0.isNveq }).count > 2 {
alerts.append(.tooManyNVEQ(self))
}
if teamIndex <= 16, players.count > 8 {
alerts.append(.tooManyPlayers(self))
}
// if teamIndex <= 24, players.count > 8 {
// alerts.append(.tooManyPlayers(self))
//
// }
players.forEach { pr in
alerts.append(contentsOf: pr.championshipAlerts(tournament: tournament, allPlayers: allPlayers, forRegional: teamIndex <= 16))
alerts.append(contentsOf: pr.championshipAlerts(tournament: tournament, allPlayers: allPlayers, forRegional: teamIndex <= 24))
}
return alerts

@ -1692,7 +1692,7 @@ defer {
guard let newDate = URL.importDateFormatter.date(from: "08-2024") else { return }
let dataURLs = SourceFileManager.shared.allFiles.filter { $0.dateFromPath == newDate }
let sources = dataURLs.map { CSVParser(url: $0) }
let teams = selectedSortedTeams().prefix(16)
let teams = selectedSortedTeams().prefix(24)
let players = teams.flatMap({ $0.unsortedPlayers() })
try await players.concurrentForEach { player in

@ -475,6 +475,7 @@ class FileImportManager {
let status : String? = data[safe: 9]
let verified : String? = data[safe: 10]
let isVerified = verified == "ok"
let isEQVerified = verified == "ok2"
if chunkMode == .byColumn {
if let licenceId = licenceId?.strippedLicense {
@ -487,7 +488,9 @@ class FileImportManager {
player.sourceName = lastName
player.isNveq = status == "NVEQ"
player.clubCode = found.clubCode
if isVerified {
if isEQVerified {
player.source = .frenchFederationEQVerified
} else if isVerified {
player.source = .frenchFederationVerified
}
return player
@ -495,7 +498,9 @@ class FileImportManager {
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 isVerified {
if isEQVerified {
player.source = .frenchFederationEQVerified
} else if isVerified {
player.source = .frenchFederationVerified
}
if rank == nil {
@ -509,7 +514,9 @@ class FileImportManager {
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 isVerified {
if isEQVerified {
player.source = .frenchFederationEQVerified
} else if isVerified {
player.source = .frenchFederationVerified
}
if rank == nil {
@ -779,9 +786,12 @@ enum ChampionshipAlert: LocalizedError {
case playerSexInvalid(PlayerRegistration)
case duplicate(Int, PlayerRegistration)
case notEQ(Bool?, PlayerRegistration)
case isYearValid(Bool?, PlayerRegistration)
var errorDescription: String? {
switch self {
case .isYearValid(_, let playerRegistration):
return playerRegistration.errorDescription(championshipAlert: self)
case .notEQ(_, let playerRegistration):
return playerRegistration.errorDescription(championshipAlert: self)
case .duplicate(_, let playerRegistration):
@ -816,6 +826,12 @@ extension PlayerRegistration {
func errorDescription(championshipAlert: ChampionshipAlert) -> String? {
var message = self.playerLabel() + " - " + self.formattedLicense() + " -> "
switch championshipAlert {
case .isYearValid(let bool, _):
if bool == nil {
message += "MILLESIME INVERIFIABLE"
} else {
message += "MILLESIME INVALIDE"
}
case .notEQ(let bool, _):
if bool == nil {
message += "EQ INVERIFIABLE"

@ -176,6 +176,7 @@ struct MainView: View {
}
private func _checkSourceFileAvailability() async {
return
print("dataStore.appSettings.lastDataSource :", dataStore.appSettings.lastDataSource ?? "none")
print("check internet")
print("check files on internet")

@ -269,7 +269,7 @@ func fetchPlayerData(for licenseID: String) async throws -> [Player]? {
request.setValue("XMLHttpRequest", forHTTPHeaderField: "X-Requested-With")
// Add cookies if needed (example cookie header value shown, replace with valid cookies)
request.setValue("JSESSIONID=C4B647EB27A036891E29D27FD83CE29B; AWSALB=1btRrmQjbf1VjZH74hBI3+OcfTO/HfJWbf+wm0kpBEVz9jUgvXb+UO4FkcIimuferhUjEfWmENKUS1SYmHCkndtiaR5x89W9uHYi/JTO3zn1ZO2JRCjvEgY7BNJm; AWSALBCORS=1btRrmQjbf1VjZH74hBI3+OcfTO/HfJWbf+wm0kpBEVz9jUgvXb+UO4FkcIimuferhUjEfWmENKUS1SYmHCkndtiaR5x89W9uHYi/JTO3zn1ZO2JRCjvEgY7BNJm; datadome=thpudGMDaIXBt6W8GjURPii9mF0ovaPnNozziskvWhA0uZ7bt~ZX3CICjEZEV5J4GXL151J19_NsLGL5wNT2BZ9NJUbJciEo9YJdFp_OKwlM13_dYqIrJ5rlKW65SPOg; _pcid=%7B%22browserId%22%3A%22m42mi4kbtfuyj367%22%2C%22_t%22%3A%22mjr1fm32%7Cm42mi4r2%22%7D; _pctx=%7Bu%7DN4IgrgzgpgThIC4B2YA2qA05owMoBcBDfSREQpAeyRCwgEt8oBJAE0RXSwH18yBbAFYwAjADN%2BAZgCsAH34AWAEz96CmNJABfIA; _pprv=eyJjb25zZW50Ijp7IjAiOnsibW9kZSI6ImVzc2VudGlhbCJ9LCI3Ijp7Im1vZGUiOiJvcHQtaW4ifX0sInB1cnBvc2VzIjpudWxsLCJfdCI6Im1qcjFmbHdofG00Mm1pNGtoIn0%3D; tc_cj_v2=%5Ecl_%5Dny%5B%5D%5D_mmZZZZZZKQMMORPMMMQNNZZZ%5D777m_iZZZ%22**%22%27%20ZZZKQMMQMPMPMKOPZZZ%5D777%5Ecl_%5Dny%5B%5D%5D_mmZZZZZZKQMPLNJNKJQQJZZZ%5D777m_iZZZ%22**%22%27%20ZZZKQMPLNJNLQOJKZZZ%5D777%5Ecl_%5Dny%5B%5D%5D_mmZZZZZZKQMPLONOQNSNSZZZ%5D; tc_cj_v2_cmp=; tc_cj_v2_med=; SSESS7ba44afc36c80c3faa2b8fa87e7742c5=EFTV9aCrNCaJM7Soo-1OemOQ0qdJXpp7cqiYrMSoQRQ; xtan=-; xtant=1; TCID=124122155494907703483; TCPID=124115115191501043230; xtvrn=$548419$; visid_incap_2712217=PSfJngzoSuiowsuXXhvOu5K+7mUAAAAAQUIPAAAAAAAleL9ldvN/FC1VykkU9ret; SessionStatId=10.91.140.42.1662124965429001", forHTTPHeaderField: "Cookie")
request.setValue("JSESSIONID=6F1BB57625577350AFC8812090D1427C; AWSALB=TJD45ilgPon0e9eo5ZqVQ7BHh5V1rPmc8NrYGnzC+WP3KtCcc07bxAT7DvYmKIsukqkVdTpmw/NM1+0MTrwuP+DlYFXpnpHM6n5AGEIyRzjEIMDXKnl2IwwKI49N; AWSALBCORS=TJD45ilgPon0e9eo5ZqVQ7BHh5V1rPmc8NrYGnzC+WP3KtCcc07bxAT7DvYmKIsukqkVdTpmw/NM1+0MTrwuP+DlYFXpnpHM6n5AGEIyRzjEIMDXKnl2IwwKI49N; datadome=plkXf6gRKVGRESutB228m4fq9z~jOuLzJpYixiEGDAnr2OlZC1Woq4Gg_YtVR0BVC_n1fn~~rEsBk5fwZlfnT~Z9Gn004VInlh5grj7ve6RLBAebWaYGVOLQnKwy88TH; _pcid=%7B%22browserId%22%3A%22m42mi4kbtfuyj367%22%2C%22_t%22%3A%22mjr1fm32%7Cm42mi4r2%22%7D; _pctx=%7Bu%7DN4IgrgzgpgThIC4B2YA2qA05owMoBcBDfSREQpAeyRCwgEt8oBJAE0RXSwH18yBbAFYwAjADN%2BAZgCsAH34AWAEz96CmNJABfIA; _pprv=eyJjb25zZW50Ijp7IjAiOnsibW9kZSI6ImVzc2VudGlhbCJ9LCI3Ijp7Im1vZGUiOiJvcHQtaW4ifX0sInB1cnBvc2VzIjpudWxsLCJfdCI6Im1qcjFmbHdofG00Mm1pNGtoIn0%3D; tc_cj_v2=%5Ecl_%5Dny%5B%5D%5D_mmZZZZZZKQMMORPMMMQNNZZZ%5D777m_iZZZ%22**%22%27%20ZZZKQMMQMPMPMKOPZZZ%5D777%5Ecl_%5Dny%5B%5D%5D_mmZZZZZZKQMPLNJNKJQQJZZZ%5D777m_iZZZ%22**%22%27%20ZZZKQMPLNJNLQOJKZZZ%5D777%5Ecl_%5Dny%5B%5D%5D_mmZZZZZZKQMPNSONSOPQOZZZ%5D; tc_cj_v2_cmp=; tc_cj_v2_med=; SSESS7ba44afc36c80c3faa2b8fa87e7742c5=EFTV9aCrNCaJM7Soo-1OemOQ0qdJXpp7cqiYrMSoQRQ; xtan=-; xtant=1; TCID=124122155494907703483; TCPID=124115115191501043230; xtvrn=$548419$; visid_incap_2712217=PSfJngzoSuiowsuXXhvOu5K+7mUAAAAAQUIPAAAAAAAleL9ldvN/FC1VykkU9ret; SessionStatId=10.91.140.42.1662124965429001", forHTTPHeaderField: "Cookie")
let (data, _) = try await URLSession.shared.data(for: request)
let decoder = JSONDecoder()
@ -311,6 +311,7 @@ struct Player: Codable {
let sexe: String
let codeClub: String
let dateNaissanceFr: String
let millesimeLicence: Int
func birthYear() -> Int? {
return Int(dateNaissanceFr.suffix(4))

@ -873,7 +873,6 @@ struct InscriptionManagerView: View {
}
}
}
Button {
Task {
DispatchQueue.main.async {
@ -898,6 +897,7 @@ struct InscriptionManagerView: View {
player.lastName = playerData.nom
player.birthdate = playerData.dateNaissanceFr
player.clubCode = playerData.codeClub
player.isYearValid = playerData.millesimeLicence == 2025
player.source = .frenchFederation
playersToSave.append(player)
}
@ -943,6 +943,75 @@ struct InscriptionManagerView: View {
}
}
Button {
Task {
DispatchQueue.main.async {
gatheringInProgress = true
gathered = 0
gatheringDone = false
}
let unrankedUnsourcedChunk = tournament.players().filter { $0.isYearValid == nil }.chunked(into: 100)
for unrankedUnsourced in unrankedUnsourcedChunk {
await withTaskGroup(of: Void.self) { group in
var playersToSave = [PlayerRegistration]()
for player in unrankedUnsourced {
group.addTask {
do {
if let playerData = try await player.fetchUnrankPlayerData() {
player.lastName = playerData.nom
player.birthdate = playerData.dateNaissanceFr
player.clubCode = playerData.codeClub
player.isYearValid = playerData.millesimeLicence == 2025
//player.source = .frenchFederation
playersToSave.append(player)
}
DispatchQueue.main.async {
gathered += 1
print("gathered", gathered)
}
} catch {
print("Error fetching data for player \(player):", error)
}
}
do {
try tournamentStore.playerRegistrations.addOrUpdate(contentOfs: playersToSave)
} catch {
Logger.error(error)
}
}
// Wait for all tasks to complete
await group.waitForAll()
}
}
gatheringDone = true
gatheringInProgress = false
}
} label: {
if gatheringInProgress {
LabeledContent {
Text("\(gathered.formatted()) / \(totalUnrankedUnsourced.formatted())")
} label: {
ProgressView("Récupérés", value: gathered, total: totalUnrankedUnsourced)
}
} else {
LabeledContent {
if gatheringDone {
Image(systemName: "checkmark").foregroundStyle(.green)
}
} label: {
Text("Check millesime")
}
}
}
Button("Recalculer les poids") {
tournament.updateWeights()
_setHash()

Loading…
Cancel
Save