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.
389 lines
12 KiB
389 lines
12 KiB
//
|
|
// PlayerRegistration.swift
|
|
// Padel Tournament
|
|
//
|
|
// Created by razmig on 10/03/2024.
|
|
//
|
|
|
|
import Foundation
|
|
import LeStorage
|
|
|
|
@Observable
|
|
class PlayerRegistration: ModelObject, Storable {
|
|
static func resourceName() -> String { "player-registrations" }
|
|
|
|
var id: String = Store.randomId()
|
|
var teamRegistration: String?
|
|
var firstName: String
|
|
var lastName: String
|
|
var licenceId: String?
|
|
var rank: Int?
|
|
var registrationType: PaymentType?
|
|
var registrationDate: Date?
|
|
var sex: Int
|
|
|
|
var tournamentPlayed: Int?
|
|
var points: Double?
|
|
var clubName: String?
|
|
var ligueName: String?
|
|
var assimilation: String?
|
|
|
|
var phoneNumber: String?
|
|
var email: String?
|
|
var birthdate: String?
|
|
|
|
var weight: Int = 0
|
|
var source: PlayerDataSource?
|
|
|
|
var hasArrived: Bool = false
|
|
|
|
internal init(teamRegistration: String? = nil, firstName: String, lastName: String, licenceId: String? = nil, rank: Int? = nil, registrationType: PaymentType? = nil, registrationDate: Date? = nil, sex: Int, source: PlayerDataSource? = nil) {
|
|
self.teamRegistration = teamRegistration
|
|
self.firstName = firstName
|
|
self.lastName = lastName
|
|
self.licenceId = licenceId
|
|
self.rank = rank
|
|
self.registrationType = registrationType
|
|
self.registrationDate = registrationDate
|
|
self.sex = sex
|
|
self.source = source
|
|
}
|
|
|
|
internal init(importedPlayer: ImportedPlayer) {
|
|
self.teamRegistration = ""
|
|
self.firstName = importedPlayer.firstName ?? ""
|
|
self.lastName = importedPlayer.lastName ?? ""
|
|
self.licenceId = importedPlayer.license ?? nil
|
|
self.rank = Int(importedPlayer.rank)
|
|
self.sex = importedPlayer.male ? 1 : 0
|
|
self.tournamentPlayed = importedPlayer.tournamentPlayed
|
|
self.points = importedPlayer.getPoints()
|
|
self.clubName = importedPlayer.clubName
|
|
self.ligueName = importedPlayer.ligueName
|
|
self.assimilation = importedPlayer.assimilation
|
|
self.source = .frenchFederation
|
|
}
|
|
|
|
internal init(federalData: [String], sex: Int, sexUnknown: Bool) {
|
|
lastName = federalData[0]
|
|
firstName = federalData[1]
|
|
birthdate = federalData[2]
|
|
licenceId = federalData[3]
|
|
clubName = federalData[4]
|
|
rank = Int(federalData[5])
|
|
email = federalData[6]
|
|
phoneNumber = federalData[7]
|
|
source = .beachPadel
|
|
if sexUnknown {
|
|
if sex == 1 && FileImportManager.shared.foundInWomenData(license: federalData[3]) {
|
|
self.sex = 0
|
|
} else if FileImportManager.shared.foundInMenData(license: federalData[3]) {
|
|
self.sex = 1
|
|
} else {
|
|
self.sex = -1
|
|
}
|
|
} else {
|
|
self.sex = sex
|
|
}
|
|
}
|
|
|
|
var computedAge: Int? {
|
|
if let birthdate {
|
|
let components = birthdate.components(separatedBy: "/")
|
|
if components.count == 3 {
|
|
if let year = Calendar.current.dateComponents([.year], from: Date()).year, let age = components.last, let ageInt = Int(age) {
|
|
if age.count == 2 { //si l'année est sur 2 chiffres dans le fichier
|
|
if ageInt < 23 {
|
|
return year - 2000 - ageInt
|
|
} else {
|
|
return year - 2000 + 100 - ageInt
|
|
}
|
|
} else { //si l'année est représenté sur 4 chiffres
|
|
return year - ageInt
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func pasteData() -> String {
|
|
[firstName.capitalized, lastName.capitalized, licenceId].compactMap({ $0 }).joined(separator: " ")
|
|
}
|
|
|
|
func isPlaying() -> Bool {
|
|
team()?.isPlaying() == true
|
|
}
|
|
|
|
func contains(_ searchField: String) -> Bool {
|
|
firstName.localizedCaseInsensitiveContains(searchField) || lastName.localizedCaseInsensitiveContains(searchField)
|
|
}
|
|
|
|
func isSameAs(_ player: PlayerRegistration) -> Bool {
|
|
firstName.localizedCaseInsensitiveCompare(player.firstName) == .orderedSame &&
|
|
lastName.localizedCaseInsensitiveCompare(player.lastName) == .orderedSame
|
|
}
|
|
|
|
func tournament() -> Tournament? {
|
|
guard let tournament = team()?.tournament else { return nil }
|
|
return Store.main.findById(tournament)
|
|
}
|
|
|
|
func team() -> TeamRegistration? {
|
|
guard let teamRegistration else { return nil }
|
|
return Store.main.findById(teamRegistration)
|
|
}
|
|
|
|
func hasPaid() -> Bool {
|
|
registrationType != nil
|
|
}
|
|
|
|
func playerLabel(_ displayStyle: DisplayStyle = .wide) -> String {
|
|
switch displayStyle {
|
|
case .wide:
|
|
lastName.trimmed.capitalized + " " + firstName.trimmed.capitalized
|
|
case .short:
|
|
lastName.trimmed.capitalized + " " + firstName.trimmed.prefix(1).capitalized + "."
|
|
}
|
|
}
|
|
|
|
func isImported() -> Bool {
|
|
source == .beachPadel
|
|
}
|
|
|
|
func isValidLicenseNumber(year: Int) -> Bool {
|
|
guard let licenceId else { return false }
|
|
guard licenceId.isLicenseNumber else { return false }
|
|
guard licenceId.suffix(6) == "(\(year))" else { return false }
|
|
return true
|
|
}
|
|
|
|
@objc
|
|
var canonicalName: String {
|
|
playerLabel().folding(options: .diacriticInsensitive, locale: .current).lowercased()
|
|
}
|
|
|
|
func rankLabel(_ displayStyle: DisplayStyle = .wide) -> String {
|
|
if let rank, rank > 0 {
|
|
return rank.formatted()
|
|
} else {
|
|
return "non classé" + (isMalePlayer() ? "" : "e")
|
|
}
|
|
}
|
|
|
|
func getRank() -> Int {
|
|
weight
|
|
}
|
|
|
|
@MainActor
|
|
func updateRank(from sources: [CSVParser], lastRank: Int) async throws {
|
|
if let value = try await history(from: sources)?.rank {
|
|
rank = value
|
|
} else {
|
|
rank = lastRank
|
|
}
|
|
}
|
|
|
|
func history(from sources: [CSVParser]) async throws -> Line? {
|
|
guard let license = licenceId?.strippedLicense else {
|
|
return try await historyFromName(from: sources)
|
|
}
|
|
|
|
return await withTaskGroup(of: Line?.self) { group in
|
|
for source in sources.filter({ $0.maleData == isMalePlayer() }) {
|
|
group.addTask {
|
|
guard !Task.isCancelled else { print("Cancelled"); return nil }
|
|
|
|
return try? await source.first(where: { line in
|
|
line.rawValue.contains(";\(license);")
|
|
})
|
|
}
|
|
}
|
|
|
|
if let first = await group.first(where: { $0 != nil }) {
|
|
group.cancelAll()
|
|
return first
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
func historyFromName(from sources: [CSVParser]) async throws -> Line? {
|
|
return await withTaskGroup(of: Line?.self) { group in
|
|
for source in sources.filter({ $0.maleData == isMalePlayer() }) {
|
|
group.addTask { [lastName, firstName] in
|
|
guard !Task.isCancelled else { print("Cancelled"); return nil }
|
|
|
|
return try? await source.first(where: { line in
|
|
line.rawValue.canonicalVersionWithPunctuation.contains(";\(lastName.canonicalVersionWithPunctuation);\(firstName.canonicalVersionWithPunctuation);")
|
|
})
|
|
}
|
|
}
|
|
|
|
if let first = await group.first(where: { $0 != nil }) {
|
|
group.cancelAll()
|
|
return first
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
func setWeight(in tournament: Tournament) {
|
|
let currentRank = rank ?? tournament.unrankValue(for: isMalePlayer()) ?? 100_000
|
|
switch tournament.tournamentCategory {
|
|
case .men:
|
|
weight = isMalePlayer() ? currentRank : currentRank + PlayerRegistration.addon(for: currentRank, manMax: tournament.maleUnrankedValue ?? 0, womanMax: tournament.femaleUnrankedValue ?? 0)
|
|
default:
|
|
weight = currentRank
|
|
}
|
|
}
|
|
|
|
func isMalePlayer() -> Bool {
|
|
sex == 1
|
|
}
|
|
|
|
func validateLicenceId(_ year: Int) {
|
|
if let currentLicenceId = licenceId {
|
|
if currentLicenceId.trimmed.hasSuffix("(\(year-1))") {
|
|
self.licenceId = currentLicenceId.replacingOccurrences(of: "\(year-1)", with: "\(year)")
|
|
} else if let computedLicense = currentLicenceId.strippedLicense {
|
|
self.licenceId = computedLicense + " (\(year))"
|
|
}
|
|
}
|
|
}
|
|
|
|
enum CodingKeys: String, CodingKey {
|
|
case _id = "id"
|
|
case _teamRegistration = "teamRegistration"
|
|
case _firstName = "firstName"
|
|
case _lastName = "lastName"
|
|
case _licenceId = "licenceId"
|
|
case _rank = "rank"
|
|
case _registrationType = "registrationType"
|
|
case _registrationDate = "registrationDate"
|
|
case _sex = "sex"
|
|
case _tournamentPlayed = "tournamentPlayed"
|
|
case _points = "points"
|
|
case _clubName = "clubName"
|
|
case _ligueName = "ligueName"
|
|
case _assimilation = "assimilation"
|
|
case _birthdate = "birthdate"
|
|
case _phoneNumber = "phoneNumber"
|
|
case _email = "email"
|
|
case _weight = "weight"
|
|
case _source = "source"
|
|
case _hasArrived = "hasArrived"
|
|
|
|
}
|
|
|
|
enum PlayerDataSource: Int, Codable {
|
|
case frenchFederation
|
|
case beachPadel
|
|
}
|
|
|
|
enum PaymentType: Int, CaseIterable, Identifiable, Codable {
|
|
init?(rawValue: Int?) {
|
|
guard let value = rawValue else { return nil }
|
|
self.init(rawValue: value)
|
|
}
|
|
|
|
var id: Self {
|
|
self
|
|
}
|
|
|
|
case cash = 0
|
|
case lydia = 1
|
|
case gift = 2
|
|
case check = 3
|
|
case paylib = 4
|
|
case bankTransfer = 5
|
|
case clubHouse = 6
|
|
case creditCard = 7
|
|
|
|
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
|
|
switch self {
|
|
case .check:
|
|
return "Chèque"
|
|
case .cash:
|
|
return "Cash"
|
|
case .lydia:
|
|
return "Lydia"
|
|
case .paylib:
|
|
return "Paylib"
|
|
case .bankTransfer:
|
|
return "Virement"
|
|
case .clubHouse:
|
|
return "Clubhouse"
|
|
case .creditCard:
|
|
return "CB"
|
|
case .gift:
|
|
return "Offert"
|
|
}
|
|
}
|
|
}
|
|
|
|
static func addon(for playerRank: Int, manMax: Int, womanMax: Int) -> Int {
|
|
switch playerRank {
|
|
case 0: return 0
|
|
case womanMax: return manMax - womanMax
|
|
case manMax: return 0
|
|
case 1...10: return 400
|
|
case 11...30: return 1000
|
|
case 31...60: return 2000
|
|
case 61...100: return 3000
|
|
case 101...200: return 8000
|
|
case 201...500: return 12000
|
|
default:
|
|
return 15000
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
extension PlayerRegistration: Hashable {
|
|
static func == (lhs: PlayerRegistration, rhs: PlayerRegistration) -> Bool {
|
|
lhs.id == rhs.id
|
|
}
|
|
|
|
func hash(into hasher: inout Hasher) {
|
|
hasher.combine(id)
|
|
}
|
|
}
|
|
|
|
extension PlayerRegistration: PlayerHolder {
|
|
|
|
func getFirstName() -> String {
|
|
firstName
|
|
}
|
|
|
|
func getLastName() -> String {
|
|
lastName
|
|
}
|
|
|
|
func getPoints() -> Double? {
|
|
self.points
|
|
}
|
|
|
|
func getRank() -> Int? {
|
|
rank
|
|
}
|
|
|
|
func isUnranked() -> Bool {
|
|
rank == nil
|
|
}
|
|
|
|
func formattedRank() -> String {
|
|
self.rankLabel()
|
|
}
|
|
|
|
func formattedLicense() -> String {
|
|
if let licenceId { return licenceId.computedLicense }
|
|
return "aucune licence"
|
|
}
|
|
|
|
var male: Bool {
|
|
isMalePlayer()
|
|
}
|
|
}
|
|
|