Compare commits
2 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
5f6c01387a | 1 year ago |
|
|
9897685192 | 1 year ago |
@ -0,0 +1,86 @@ |
|||||||
|
// |
||||||
|
// Club+Extensions.swift |
||||||
|
// PadelClub |
||||||
|
// |
||||||
|
// Created by Laurent Morvillier on 27/08/2024. |
||||||
|
// |
||||||
|
|
||||||
|
import Foundation |
||||||
|
import PadelClubData |
||||||
|
|
||||||
|
extension Club { |
||||||
|
var isValid: Bool { |
||||||
|
name.isEmpty == false && name.count > 3 |
||||||
|
} |
||||||
|
|
||||||
|
func automaticShortName() -> String { |
||||||
|
name.acronym() |
||||||
|
} |
||||||
|
|
||||||
|
enum AcronymMode: String, CaseIterable { |
||||||
|
case automatic = "Automatique" |
||||||
|
case custom = "Personalisée" |
||||||
|
} |
||||||
|
|
||||||
|
func shortNameMode() -> AcronymMode { |
||||||
|
(acronym.isEmpty || acronym == automaticShortName()) ? .automatic : .custom |
||||||
|
} |
||||||
|
|
||||||
|
func hasTenupId() -> Bool { |
||||||
|
code != nil |
||||||
|
} |
||||||
|
|
||||||
|
func federalLink() -> URL? { |
||||||
|
guard let code else { return nil } |
||||||
|
return URL(string: "https://tenup.fft.fr/club/\(code)") |
||||||
|
} |
||||||
|
|
||||||
|
func courtName(atIndex courtIndex: Int) -> String { |
||||||
|
courtNameIfAvailable(atIndex: courtIndex) ?? Court.courtIndexedTitle(atIndex: courtIndex) |
||||||
|
} |
||||||
|
|
||||||
|
func courtNameIfAvailable(atIndex courtIndex: Int) -> String? { |
||||||
|
customizedCourts.first(where: { $0.index == courtIndex })?.name |
||||||
|
} |
||||||
|
|
||||||
|
func update(fromClub club: Club) { |
||||||
|
self.acronym = club.acronym |
||||||
|
self.name = club.name |
||||||
|
self.phone = club.phone |
||||||
|
self.code = club.code |
||||||
|
self.address = club.address |
||||||
|
self.city = club.city |
||||||
|
self.zipCode = club.zipCode |
||||||
|
self.latitude = club.latitude |
||||||
|
self.longitude = club.longitude |
||||||
|
} |
||||||
|
|
||||||
|
func hasBeenCreated(by creatorId: String?) -> Bool { |
||||||
|
return creatorId == creator || creator == nil |
||||||
|
} |
||||||
|
|
||||||
|
func isFavorite() -> Bool { |
||||||
|
return DataStore.shared.user.clubs.contains(where: { $0 == self.id }) |
||||||
|
} |
||||||
|
|
||||||
|
static func findOrCreate(name: String, code: String?, city: String? = nil, zipCode: String? = nil) -> Club { |
||||||
|
|
||||||
|
/* |
||||||
|
|
||||||
|
identify a club : code, name, ?? |
||||||
|
|
||||||
|
*/ |
||||||
|
let club: Club? = DataStore.shared.clubs.first(where: { (code == nil && $0.name == name && $0.city == city && $0.zipCode == zipCode) || code != nil && $0.code == code }) |
||||||
|
|
||||||
|
if let club { |
||||||
|
return club |
||||||
|
} else { |
||||||
|
return Club(creator: StoreCenter.main.userId, name: name, code: code, city: city, zipCode: zipCode) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func shareURL() -> URL? { |
||||||
|
return URL(string: URLs.main.url.appending(path: "?club=\(id)").absoluteString.removingPercentEncoding!) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -1,248 +0,0 @@ |
|||||||
// |
|
||||||
// Club.swift |
|
||||||
// PadelClub |
|
||||||
// |
|
||||||
// Created by Laurent Morvillier on 02/02/2024. |
|
||||||
// |
|
||||||
|
|
||||||
import Foundation |
|
||||||
import SwiftUI |
|
||||||
import LeStorage |
|
||||||
|
|
||||||
@Observable |
|
||||||
final class Club : ModelObject, Storable, Hashable { |
|
||||||
|
|
||||||
static func resourceName() -> String { return "clubs" } |
|
||||||
static func tokenExemptedMethods() -> [HTTPMethod] { return [.get] } |
|
||||||
static func filterByStoreIdentifier() -> Bool { return false } |
|
||||||
static var relationshipNames: [String] = [] |
|
||||||
|
|
||||||
static func == (lhs: Club, rhs: Club) -> Bool { |
|
||||||
lhs.id == rhs.id |
|
||||||
} |
|
||||||
|
|
||||||
func hash(into hasher: inout Hasher) { |
|
||||||
return hasher.combine(id) |
|
||||||
} |
|
||||||
|
|
||||||
var id: String = Store.randomId() |
|
||||||
var creator: String? |
|
||||||
var name: String |
|
||||||
var acronym: String |
|
||||||
var phone: String? |
|
||||||
var code: String? |
|
||||||
//var federalClubData: Data? |
|
||||||
var address: String? |
|
||||||
var city: String? |
|
||||||
var zipCode: String? |
|
||||||
var latitude: Double? |
|
||||||
var longitude: Double? |
|
||||||
var courtCount: Int = 2 |
|
||||||
var broadcastCode: String? |
|
||||||
// var alphabeticalName: Bool = false |
|
||||||
|
|
||||||
internal init(creator: String? = nil, name: String, acronym: String? = nil, phone: String? = nil, code: String? = nil, address: String? = nil, city: String? = nil, zipCode: String? = nil, latitude: Double? = nil, longitude: Double? = nil, courtCount: Int = 2, broadcastCode: String? = nil) { |
|
||||||
self.name = name |
|
||||||
self.creator = creator |
|
||||||
self.acronym = acronym ?? name.acronym() |
|
||||||
self.phone = phone |
|
||||||
self.code = code |
|
||||||
self.address = address |
|
||||||
self.city = city |
|
||||||
self.zipCode = zipCode |
|
||||||
self.latitude = latitude |
|
||||||
self.longitude = longitude |
|
||||||
self.courtCount = courtCount |
|
||||||
self.broadcastCode = broadcastCode |
|
||||||
} |
|
||||||
|
|
||||||
override func copyFromServerInstance(_ instance: any Storable) -> Bool { |
|
||||||
guard let copy = instance as? Club else { return false } |
|
||||||
self.broadcastCode = copy.broadcastCode |
|
||||||
// Logger.log("write code: \(self.broadcastCode)") |
|
||||||
return true |
|
||||||
} |
|
||||||
|
|
||||||
func clubTitle(_ displayStyle: DisplayStyle = .wide) -> String { |
|
||||||
switch displayStyle { |
|
||||||
case .wide, .title: |
|
||||||
return name |
|
||||||
case .short: |
|
||||||
return acronym |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func shareURL() -> URL? { |
|
||||||
return URL(string: URLs.main.url.appending(path: "?club=\(id)").absoluteString.removingPercentEncoding!) |
|
||||||
} |
|
||||||
|
|
||||||
var customizedCourts: [Court] { |
|
||||||
DataStore.shared.courts.filter { $0.club == self.id }.sorted(by: \.index) |
|
||||||
} |
|
||||||
|
|
||||||
override func deleteDependencies() throws { |
|
||||||
let customizedCourts = self.customizedCourts |
|
||||||
for customizedCourt in customizedCourts { |
|
||||||
try customizedCourt.deleteDependencies() |
|
||||||
} |
|
||||||
DataStore.shared.courts.deleteDependencies(customizedCourts) |
|
||||||
} |
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey { |
|
||||||
case _id = "id" |
|
||||||
case _creator = "creator" |
|
||||||
case _name = "name" |
|
||||||
case _acronym = "acronym" |
|
||||||
case _phone = "phone" |
|
||||||
case _code = "code" |
|
||||||
case _address = "address" |
|
||||||
case _city = "city" |
|
||||||
case _zipCode = "zipCode" |
|
||||||
case _latitude = "latitude" |
|
||||||
case _longitude = "longitude" |
|
||||||
case _courtCount = "courtCount" |
|
||||||
case _broadcastCode = "broadcastCode" |
|
||||||
// case _alphabeticalName = "alphabeticalName" |
|
||||||
} |
|
||||||
|
|
||||||
func encode(to encoder: Encoder) throws { |
|
||||||
var container = encoder.container(keyedBy: CodingKeys.self) |
|
||||||
|
|
||||||
try container.encode(id, forKey: ._id) |
|
||||||
|
|
||||||
if let creator = creator { |
|
||||||
try container.encode(creator, forKey: ._creator) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._creator) |
|
||||||
} |
|
||||||
|
|
||||||
try container.encode(name, forKey: ._name) |
|
||||||
try container.encode(acronym, forKey: ._acronym) |
|
||||||
|
|
||||||
if let phone = phone { |
|
||||||
try container.encode(phone, forKey: ._phone) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._phone) |
|
||||||
} |
|
||||||
|
|
||||||
if let code = code { |
|
||||||
try container.encode(code, forKey: ._code) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._code) |
|
||||||
} |
|
||||||
|
|
||||||
if let address = address { |
|
||||||
try container.encode(address, forKey: ._address) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._address) |
|
||||||
} |
|
||||||
|
|
||||||
if let city = city { |
|
||||||
try container.encode(city, forKey: ._city) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._city) |
|
||||||
} |
|
||||||
|
|
||||||
if let zipCode = zipCode { |
|
||||||
try container.encode(zipCode, forKey: ._zipCode) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._zipCode) |
|
||||||
} |
|
||||||
|
|
||||||
if let latitude = latitude { |
|
||||||
try container.encode(latitude, forKey: ._latitude) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._latitude) |
|
||||||
} |
|
||||||
|
|
||||||
if let longitude = longitude { |
|
||||||
try container.encode(longitude, forKey: ._longitude) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._longitude) |
|
||||||
} |
|
||||||
|
|
||||||
try container.encode(courtCount, forKey: ._courtCount) |
|
||||||
|
|
||||||
if let broadcastCode { |
|
||||||
try container.encode(broadcastCode, forKey: ._broadcastCode) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._broadcastCode) |
|
||||||
} |
|
||||||
|
|
||||||
// try container.encode(alphabeticalName, forKey: ._alphabeticalName) |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
extension Club { |
|
||||||
var isValid: Bool { |
|
||||||
name.isEmpty == false && name.count > 3 |
|
||||||
} |
|
||||||
|
|
||||||
func automaticShortName() -> String { |
|
||||||
name.acronym() |
|
||||||
} |
|
||||||
|
|
||||||
enum AcronymMode: String, CaseIterable { |
|
||||||
case automatic = "Automatique" |
|
||||||
case custom = "Personalisée" |
|
||||||
} |
|
||||||
|
|
||||||
func shortNameMode() -> AcronymMode { |
|
||||||
(acronym.isEmpty || acronym == automaticShortName()) ? .automatic : .custom |
|
||||||
} |
|
||||||
|
|
||||||
func hasTenupId() -> Bool { |
|
||||||
code != nil |
|
||||||
} |
|
||||||
|
|
||||||
func federalLink() -> URL? { |
|
||||||
guard let code else { return nil } |
|
||||||
return URL(string: "https://tenup.fft.fr/club/\(code)") |
|
||||||
} |
|
||||||
|
|
||||||
func courtName(atIndex courtIndex: Int) -> String { |
|
||||||
courtNameIfAvailable(atIndex: courtIndex) ?? Court.courtIndexedTitle(atIndex: courtIndex) |
|
||||||
} |
|
||||||
|
|
||||||
func courtNameIfAvailable(atIndex courtIndex: Int) -> String? { |
|
||||||
customizedCourts.first(where: { $0.index == courtIndex })?.name |
|
||||||
} |
|
||||||
|
|
||||||
func update(fromClub club: Club) { |
|
||||||
self.acronym = club.acronym |
|
||||||
self.name = club.name |
|
||||||
self.phone = club.phone |
|
||||||
self.code = club.code |
|
||||||
self.address = club.address |
|
||||||
self.city = club.city |
|
||||||
self.zipCode = club.zipCode |
|
||||||
self.latitude = club.latitude |
|
||||||
self.longitude = club.longitude |
|
||||||
} |
|
||||||
|
|
||||||
func hasBeenCreated(by creatorId: String?) -> Bool { |
|
||||||
return creatorId == creator || creator == nil |
|
||||||
} |
|
||||||
|
|
||||||
func isFavorite() -> Bool { |
|
||||||
return DataStore.shared.user.clubs.contains(where: { $0 == self.id }) |
|
||||||
} |
|
||||||
|
|
||||||
static func findOrCreate(name: String, code: String?, city: String? = nil, zipCode: String? = nil) -> Club { |
|
||||||
|
|
||||||
/* |
|
||||||
|
|
||||||
identify a club : code, name, ?? |
|
||||||
|
|
||||||
*/ |
|
||||||
let club: Club? = DataStore.shared.clubs.first(where: { (code == nil && $0.name == name && $0.city == city && $0.zipCode == zipCode) || code != nil && $0.code == code }) |
|
||||||
|
|
||||||
if let club { |
|
||||||
return club |
|
||||||
} else { |
|
||||||
return Club(creator: StoreCenter.main.userId, name: name, code: code, city: city, zipCode: zipCode) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -0,0 +1,73 @@ |
|||||||
|
// |
||||||
|
// TournamentCategory+Extensions.swift |
||||||
|
// PadelClub |
||||||
|
// |
||||||
|
// Created by Laurent Morvillier on 27/08/2024. |
||||||
|
// |
||||||
|
|
||||||
|
import Foundation |
||||||
|
import PadelClubData |
||||||
|
|
||||||
|
extension TournamentCategory { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
extension TournamentType { |
||||||
|
|
||||||
|
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { |
||||||
|
switch self { |
||||||
|
case .classic: |
||||||
|
return "Classique" |
||||||
|
case .doubleBrackets: |
||||||
|
return "Double Poules" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
extension TournamentBuild { |
||||||
|
|
||||||
|
var computedLabel: String { |
||||||
|
if age == .senior { return localizedLabel() } |
||||||
|
return localizedLabel() + " " + localizedAge |
||||||
|
} |
||||||
|
|
||||||
|
var localizedTitle: String { |
||||||
|
level.localizedLabel() + " " + category.localizedLabel() |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
extension TeamPosition { |
||||||
|
|
||||||
|
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { |
||||||
|
var shortName: String { |
||||||
|
switch self { |
||||||
|
case .one: |
||||||
|
return "#1" |
||||||
|
case .two: |
||||||
|
return "#2" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
switch displayStyle { |
||||||
|
case .wide, .title: |
||||||
|
return "Équipe " + shortName |
||||||
|
case .short: |
||||||
|
return shortName |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
extension MatchFormat { |
||||||
|
|
||||||
|
func formattedEstimatedBreakDuration() -> String { |
||||||
|
var label = Duration.seconds(breakTime.breakTime * 60).formatted(.units(allowed: [.minutes])) |
||||||
|
if breakTime.matchCount > 1 { |
||||||
|
label += " après \(breakTime.matchCount) match" |
||||||
|
label += breakTime.matchCount.pluralSuffix |
||||||
|
} |
||||||
|
return label |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,55 @@ |
|||||||
|
// |
||||||
|
// GroupStage+Extensions.swift |
||||||
|
// PadelClub |
||||||
|
// |
||||||
|
// Created by Laurent Morvillier on 27/08/2024. |
||||||
|
// |
||||||
|
|
||||||
|
import Foundation |
||||||
|
import PadelClubData |
||||||
|
|
||||||
|
extension GroupStage { |
||||||
|
|
||||||
|
func groupStageTitle(_ displayStyle: DisplayStyle = .wide) -> String { |
||||||
|
if let name { return name } |
||||||
|
switch displayStyle { |
||||||
|
case .wide, .title: |
||||||
|
return "Poule \(index + 1)" |
||||||
|
case .short: |
||||||
|
return "#\(index + 1)" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func pasteData() -> String { |
||||||
|
var data: [String] = [] |
||||||
|
data.append(self.groupStageTitle()) |
||||||
|
teams().forEach { team in |
||||||
|
data.append(team.teamLabelRanked(displayRank: true, displayTeamName: true)) |
||||||
|
} |
||||||
|
|
||||||
|
return data.joined(separator: "\n") |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
extension GroupStage: Selectable { |
||||||
|
func selectionLabel(index: Int) -> String { |
||||||
|
groupStageTitle() |
||||||
|
} |
||||||
|
|
||||||
|
func badgeValue() -> Int? { |
||||||
|
return runningMatches(playedMatches: _matches()).count |
||||||
|
} |
||||||
|
|
||||||
|
func badgeValueColor() -> Color? { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func badgeImage() -> Badge? { |
||||||
|
if teams().count < size { |
||||||
|
return .xmark |
||||||
|
} else { |
||||||
|
return hasEnded() ? .checkmark : nil |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,89 @@ |
|||||||
|
// |
||||||
|
// Match+Extensions.swift |
||||||
|
// PadelClub |
||||||
|
// |
||||||
|
// Created by Laurent Morvillier on 27/08/2024. |
||||||
|
// |
||||||
|
|
||||||
|
import Foundation |
||||||
|
import PadelClubData |
||||||
|
|
||||||
|
extension Match { |
||||||
|
|
||||||
|
func matchWarningSubject() -> String { |
||||||
|
[roundTitle(), matchTitle(.short)].compacted().joined(separator: " ") |
||||||
|
} |
||||||
|
|
||||||
|
func matchWarningMessage() -> String { |
||||||
|
[roundTitle(), matchTitle(.short), startDate?.localizedDate(), courtName()].compacted().joined(separator: "\n") |
||||||
|
} |
||||||
|
|
||||||
|
func matchTitle(_ displayStyle: DisplayStyle = .wide, inMatches matches: [Match]? = nil) -> String { |
||||||
|
#if _DEBUG_TIME //DEBUGING TIME |
||||||
|
let start = Date() |
||||||
|
defer { |
||||||
|
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) |
||||||
|
print("func matchTitle", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) |
||||||
|
} |
||||||
|
#endif |
||||||
|
if let groupStageObject { |
||||||
|
return groupStageObject.localizedMatchUpLabel(for: index) |
||||||
|
} |
||||||
|
|
||||||
|
switch displayStyle { |
||||||
|
case .wide, .title: |
||||||
|
return "Match \(indexInRound(in: matches) + 1)" |
||||||
|
case .short: |
||||||
|
return "#\(indexInRound(in: matches) + 1)" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func roundTitle() -> String? { |
||||||
|
if groupStage != nil { return groupStageObject?.groupStageTitle() } |
||||||
|
else if let roundObject { return roundObject.roundTitle() } |
||||||
|
else { return nil } |
||||||
|
} |
||||||
|
|
||||||
|
func teamNames(_ team: TeamRegistration?) -> [String]? { |
||||||
|
return team?.players().map { $0.playerLabel() } |
||||||
|
} |
||||||
|
|
||||||
|
func updateScore(fromMatchDescriptor matchDescriptor: MatchDescriptor) { |
||||||
|
let teamScoreOne = teamScore(.one) ?? TeamScore(match: id, team: team(.one)) |
||||||
|
teamScoreOne.score = matchDescriptor.teamOneScores.joined(separator: ",") |
||||||
|
let teamScoreTwo = teamScore(.two) ?? TeamScore(match: id, team: team(.two)) |
||||||
|
teamScoreTwo.score = matchDescriptor.teamTwoScores.joined(separator: ",") |
||||||
|
do { |
||||||
|
try self.tournamentStore.teamScores.addOrUpdate(contentOfs: [teamScoreOne, teamScoreTwo]) |
||||||
|
} catch { |
||||||
|
Logger.error(error) |
||||||
|
} |
||||||
|
matchFormat = matchDescriptor.matchFormat |
||||||
|
} |
||||||
|
|
||||||
|
func setScore(fromMatchDescriptor matchDescriptor: MatchDescriptor) { |
||||||
|
updateScore(fromMatchDescriptor: matchDescriptor) |
||||||
|
if endDate == nil { |
||||||
|
endDate = Date() |
||||||
|
} |
||||||
|
if startDate == nil { |
||||||
|
startDate = endDate?.addingTimeInterval(Double(-getDuration()*60)) |
||||||
|
} |
||||||
|
|
||||||
|
let teamOne = team(matchDescriptor.winner) |
||||||
|
let teamTwo = team(matchDescriptor.winner.otherTeam) |
||||||
|
|
||||||
|
teamOne?.hasArrived() |
||||||
|
teamTwo?.hasArrived() |
||||||
|
|
||||||
|
winningTeamId = teamOne?.id |
||||||
|
losingTeamId = teamTwo?.id |
||||||
|
|
||||||
|
confirmed = true |
||||||
|
|
||||||
|
groupStageObject?.updateGroupStageState() |
||||||
|
roundObject?.updateTournamentState() |
||||||
|
updateFollowingMatchTeamScore() |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,13 @@ |
|||||||
|
// |
||||||
|
// MatchScheduler+Extensions.swift |
||||||
|
// PadelClub |
||||||
|
// |
||||||
|
// Created by Laurent Morvillier on 27/08/2024. |
||||||
|
// |
||||||
|
|
||||||
|
import Foundation |
||||||
|
import PadelClubData |
||||||
|
|
||||||
|
extension MatchScheduler { |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,43 @@ |
|||||||
|
// |
||||||
|
// MonthData+Extensions.swift |
||||||
|
// PadelClub |
||||||
|
// |
||||||
|
// Created by Laurent Morvillier on 27/08/2024. |
||||||
|
// |
||||||
|
|
||||||
|
import Foundation |
||||||
|
import PadelClubData |
||||||
|
|
||||||
|
extension MonthData { |
||||||
|
|
||||||
|
static func calculateCurrentUnrankedValues(fromDate: Date) async { |
||||||
|
|
||||||
|
let fileURL = SourceFileManager.shared.allFiles(true).first(where: { $0.dateFromPath == fromDate && $0.index == 0 }) |
||||||
|
print("calculateCurrentUnrankedValues", fromDate.monthYearFormatted, fileURL?.path()) |
||||||
|
let fftImportingUncomplete = fileURL?.fftImportingUncomplete() |
||||||
|
let fftImportingMaleUnrankValue = fileURL?.fftImportingMaleUnrankValue() |
||||||
|
|
||||||
|
let incompleteMode = fftImportingUncomplete != nil |
||||||
|
|
||||||
|
let lastDataSourceMaleUnranked = await FederalPlayer.lastRank(mostRecentDateAvailable: fromDate, man: true) |
||||||
|
let lastDataSourceFemaleUnranked = await FederalPlayer.lastRank(mostRecentDateAvailable: fromDate, man: false) |
||||||
|
let anonymousCount = await FederalPlayer.anonymousCount(mostRecentDateAvailable: fromDate) |
||||||
|
await MainActor.run { |
||||||
|
let lastDataSource = URL.importDateFormatter.string(from: fromDate) |
||||||
|
let currentMonthData : MonthData = DataStore.shared.monthData.first(where: { $0.monthKey == lastDataSource }) ?? MonthData(monthKey: lastDataSource) |
||||||
|
currentMonthData._updateCreationDate() |
||||||
|
currentMonthData.maleUnrankedValue = incompleteMode ? fftImportingMaleUnrankValue : lastDataSourceMaleUnranked?.0 |
||||||
|
currentMonthData.incompleteMode = incompleteMode |
||||||
|
currentMonthData.maleCount = incompleteMode ? fftImportingUncomplete : lastDataSourceMaleUnranked?.1 |
||||||
|
currentMonthData.femaleUnrankedValue = lastDataSourceFemaleUnranked?.0 |
||||||
|
currentMonthData.femaleCount = lastDataSourceFemaleUnranked?.1 |
||||||
|
currentMonthData.anonymousCount = anonymousCount |
||||||
|
do { |
||||||
|
try DataStore.shared.monthData.addOrUpdate(instance: currentMonthData) |
||||||
|
} catch { |
||||||
|
Logger.error(error) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -1,101 +0,0 @@ |
|||||||
// |
|
||||||
// MonthData.swift |
|
||||||
// PadelClub |
|
||||||
// |
|
||||||
// Created by Razmig Sarkissian on 18/04/2024. |
|
||||||
// |
|
||||||
|
|
||||||
import Foundation |
|
||||||
import SwiftUI |
|
||||||
import LeStorage |
|
||||||
|
|
||||||
@Observable |
|
||||||
final class MonthData : ModelObject, Storable { |
|
||||||
|
|
||||||
static func resourceName() -> String { return "month-data" } |
|
||||||
static func tokenExemptedMethods() -> [HTTPMethod] { return [] } |
|
||||||
static func filterByStoreIdentifier() -> Bool { return false } |
|
||||||
static var relationshipNames: [String] = [] |
|
||||||
|
|
||||||
private(set) var id: String = Store.randomId() |
|
||||||
private(set) var monthKey: String |
|
||||||
private(set) var creationDate: Date |
|
||||||
var maleUnrankedValue: Int? = nil |
|
||||||
var femaleUnrankedValue: Int? = nil |
|
||||||
var maleCount: Int? = nil |
|
||||||
var femaleCount: Int? = nil |
|
||||||
var anonymousCount: Int? = nil |
|
||||||
var incompleteMode: Bool = false |
|
||||||
|
|
||||||
init(monthKey: String) { |
|
||||||
self.monthKey = monthKey |
|
||||||
self.creationDate = Date() |
|
||||||
} |
|
||||||
|
|
||||||
fileprivate func _updateCreationDate() { |
|
||||||
self.creationDate = Date() |
|
||||||
} |
|
||||||
|
|
||||||
required init(from decoder: Decoder) throws { |
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self) |
|
||||||
id = try container.decode(String.self, forKey: ._id) |
|
||||||
monthKey = try container.decode(String.self, forKey: ._monthKey) |
|
||||||
creationDate = try container.decode(Date.self, forKey: ._creationDate) |
|
||||||
maleUnrankedValue = try container.decodeIfPresent(Int.self, forKey: ._maleUnrankedValue) |
|
||||||
femaleUnrankedValue = try container.decodeIfPresent(Int.self, forKey: ._femaleUnrankedValue) |
|
||||||
maleCount = try container.decodeIfPresent(Int.self, forKey: ._maleCount) |
|
||||||
femaleCount = try container.decodeIfPresent(Int.self, forKey: ._femaleCount) |
|
||||||
anonymousCount = try container.decodeIfPresent(Int.self, forKey: ._anonymousCount) |
|
||||||
incompleteMode = try container.decodeIfPresent(Bool.self, forKey: ._incompleteMode) ?? false |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
func total() -> Int { |
|
||||||
return (maleCount ?? 0) + (femaleCount ?? 0) |
|
||||||
} |
|
||||||
|
|
||||||
static func calculateCurrentUnrankedValues(fromDate: Date) async { |
|
||||||
|
|
||||||
let fileURL = SourceFileManager.shared.allFiles(true).first(where: { $0.dateFromPath == fromDate && $0.index == 0 }) |
|
||||||
print("calculateCurrentUnrankedValues", fromDate.monthYearFormatted, fileURL?.path()) |
|
||||||
let fftImportingUncomplete = fileURL?.fftImportingUncomplete() |
|
||||||
let fftImportingMaleUnrankValue = fileURL?.fftImportingMaleUnrankValue() |
|
||||||
|
|
||||||
let incompleteMode = fftImportingUncomplete != nil |
|
||||||
|
|
||||||
let lastDataSourceMaleUnranked = await FederalPlayer.lastRank(mostRecentDateAvailable: fromDate, man: true) |
|
||||||
let lastDataSourceFemaleUnranked = await FederalPlayer.lastRank(mostRecentDateAvailable: fromDate, man: false) |
|
||||||
let anonymousCount = await FederalPlayer.anonymousCount(mostRecentDateAvailable: fromDate) |
|
||||||
await MainActor.run { |
|
||||||
let lastDataSource = URL.importDateFormatter.string(from: fromDate) |
|
||||||
let currentMonthData : MonthData = DataStore.shared.monthData.first(where: { $0.monthKey == lastDataSource }) ?? MonthData(monthKey: lastDataSource) |
|
||||||
currentMonthData._updateCreationDate() |
|
||||||
currentMonthData.maleUnrankedValue = incompleteMode ? fftImportingMaleUnrankValue : lastDataSourceMaleUnranked?.0 |
|
||||||
currentMonthData.incompleteMode = incompleteMode |
|
||||||
currentMonthData.maleCount = incompleteMode ? fftImportingUncomplete : lastDataSourceMaleUnranked?.1 |
|
||||||
currentMonthData.femaleUnrankedValue = lastDataSourceFemaleUnranked?.0 |
|
||||||
currentMonthData.femaleCount = lastDataSourceFemaleUnranked?.1 |
|
||||||
currentMonthData.anonymousCount = anonymousCount |
|
||||||
do { |
|
||||||
try DataStore.shared.monthData.addOrUpdate(instance: currentMonthData) |
|
||||||
} catch { |
|
||||||
Logger.error(error) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
override func deleteDependencies() throws { |
|
||||||
} |
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey { |
|
||||||
case _id = "id" |
|
||||||
case _monthKey = "monthKey" |
|
||||||
case _creationDate = "creationDate" |
|
||||||
case _maleUnrankedValue = "maleUnrankedValue" |
|
||||||
case _femaleUnrankedValue = "femaleUnrankedValue" |
|
||||||
case _maleCount = "maleCount" |
|
||||||
case _femaleCount = "femaleCount" |
|
||||||
case _anonymousCount = "anonymousCount" |
|
||||||
case _incompleteMode = "incompleteMode" |
|
||||||
} |
|
||||||
} |
|
||||||
@ -0,0 +1,230 @@ |
|||||||
|
// |
||||||
|
// PlayerRegistration+Extensions.swift |
||||||
|
// PadelClub |
||||||
|
// |
||||||
|
// Created by Laurent Morvillier on 27/08/2024. |
||||||
|
// |
||||||
|
|
||||||
|
import Foundation |
||||||
|
import PadelClubData |
||||||
|
|
||||||
|
extension PlayerRegistration { |
||||||
|
|
||||||
|
internal init(importedPlayer: ImportedPlayer) { |
||||||
|
self.teamRegistration = "" |
||||||
|
self.firstName = (importedPlayer.firstName ?? "").trimmed.capitalized |
||||||
|
self.lastName = (importedPlayer.lastName ?? "").trimmed.uppercased() |
||||||
|
self.licenceId = importedPlayer.license ?? nil |
||||||
|
self.rank = Int(importedPlayer.rank) |
||||||
|
self.sex = importedPlayer.male ? .male : .female |
||||||
|
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) { |
||||||
|
let _lastName = federalData[0].trimmed.uppercased() |
||||||
|
let _firstName = federalData[1].trimmed.capitalized |
||||||
|
if _lastName.isEmpty && _firstName.isEmpty { return nil } |
||||||
|
lastName = _lastName |
||||||
|
firstName = _firstName |
||||||
|
birthdate = federalData[2] |
||||||
|
licenceId = federalData[3] |
||||||
|
clubName = federalData[4] |
||||||
|
let stringRank = federalData[5] |
||||||
|
if stringRank.isEmpty { |
||||||
|
rank = nil |
||||||
|
} else { |
||||||
|
rank = Int(stringRank) |
||||||
|
} |
||||||
|
let _email = federalData[6] |
||||||
|
if _email.isEmpty == false { |
||||||
|
self.email = _email |
||||||
|
} |
||||||
|
let _phoneNumber = federalData[7] |
||||||
|
if _phoneNumber.isEmpty == false { |
||||||
|
self.phoneNumber = _phoneNumber |
||||||
|
} |
||||||
|
|
||||||
|
source = .beachPadel |
||||||
|
if sexUnknown { |
||||||
|
if sex == 1 && FileImportManager.shared.foundInWomenData(license: federalData[3]) { |
||||||
|
self.sex = .female |
||||||
|
} else if FileImportManager.shared.foundInMenData(license: federalData[3]) { |
||||||
|
self.sex = .male |
||||||
|
} else { |
||||||
|
self.sex = nil |
||||||
|
} |
||||||
|
} else { |
||||||
|
self.sex = PlayerSexType(rawValue: sex) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func playerLabel(_ displayStyle: DisplayStyle = .wide) -> String { |
||||||
|
switch displayStyle { |
||||||
|
case .wide, .title: |
||||||
|
return lastName.trimmed.capitalized + " " + firstName.trimmed.capitalized |
||||||
|
case .short: |
||||||
|
let names = lastName.components(separatedBy: .whitespaces) |
||||||
|
if lastName.components(separatedBy: .whitespaces).count > 1 { |
||||||
|
if let firstLongWord = names.first(where: { $0.count > 3 }) { |
||||||
|
return firstLongWord.trimmed.capitalized.trunc(length: 10) + " " + firstName.trimmed.prefix(1).capitalized + "." |
||||||
|
} |
||||||
|
} |
||||||
|
return lastName.trimmed.capitalized.trunc(length: 10) + " " + firstName.trimmed.prefix(1).capitalized + "." |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func pasteData(_ exportFormat: ExportFormat = .rawText) -> String { |
||||||
|
switch exportFormat { |
||||||
|
case .rawText: |
||||||
|
return [firstName.capitalized, lastName.capitalized, licenceId].compactMap({ $0 }).joined(separator: exportFormat.separator()) |
||||||
|
case .csv: |
||||||
|
return [lastName.uppercased() + " " + firstName.capitalized].joined(separator: exportFormat.separator()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@objc |
||||||
|
var canonicalName: String { |
||||||
|
playerLabel().folding(options: .diacriticInsensitive, locale: .current).lowercased() |
||||||
|
} |
||||||
|
|
||||||
|
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 |
||||||
|
} |
||||||
|
|
||||||
|
@MainActor |
||||||
|
func updateRank(from sources: [CSVParser], lastRank: Int) async throws { |
||||||
|
if let dataFound = try await history(from: sources) { |
||||||
|
rank = dataFound.rankValue?.toInt() |
||||||
|
points = dataFound.points |
||||||
|
tournamentPlayed = dataFound.tournamentCountValue?.toInt() |
||||||
|
} else { |
||||||
|
rank = lastRank |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
extension PlayerRegistration: PlayerHolder { |
||||||
|
func getAssimilatedAsMaleRank() -> Int? { |
||||||
|
nil |
||||||
|
} |
||||||
|
|
||||||
|
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() |
||||||
|
} |
||||||
|
|
||||||
|
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 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))" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func hasHomonym() -> Bool { |
||||||
|
let federalContext = PersistenceController.shared.localContainer.viewContext |
||||||
|
let fetchRequest = ImportedPlayer.fetchRequest() |
||||||
|
let predicate = NSPredicate(format: "firstName == %@ && lastName == %@", firstName, lastName) |
||||||
|
fetchRequest.predicate = predicate |
||||||
|
|
||||||
|
do { |
||||||
|
let count = try federalContext.count(for: fetchRequest) |
||||||
|
return count > 1 |
||||||
|
} catch { |
||||||
|
|
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
func hasInvalidLicence() -> Bool { |
||||||
|
return (self.isImported() && self.isValidLicenseNumber(year: licenseYearValidity) == false) || |
||||||
|
(self.isImported() == false && |
||||||
|
(self.licenceId == nil || self.formattedLicense().isLicenseNumber == false || self.licenceId?.isEmpty == true)) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -1,574 +0,0 @@ |
|||||||
// |
|
||||||
// PlayerRegistration.swift |
|
||||||
// Padel Tournament |
|
||||||
// |
|
||||||
// Created by razmig on 10/03/2024. |
|
||||||
// |
|
||||||
|
|
||||||
import Foundation |
|
||||||
import LeStorage |
|
||||||
|
|
||||||
@Observable |
|
||||||
final class PlayerRegistration: ModelObject, Storable { |
|
||||||
static func resourceName() -> String { "player-registrations" } |
|
||||||
static func tokenExemptedMethods() -> [HTTPMethod] { return [] } |
|
||||||
static func filterByStoreIdentifier() -> Bool { return true } |
|
||||||
static var relationshipNames: [String] = ["teamRegistration"] |
|
||||||
|
|
||||||
var id: String = Store.randomId() |
|
||||||
var teamRegistration: String? |
|
||||||
var firstName: String |
|
||||||
var lastName: String |
|
||||||
var licenceId: String? |
|
||||||
var rank: Int? |
|
||||||
var paymentType: PlayerPaymentType? |
|
||||||
var sex: PlayerSexType? |
|
||||||
|
|
||||||
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 computedRank: Int = 0 |
|
||||||
var source: PlayerDataSource? |
|
||||||
|
|
||||||
var hasArrived: Bool = false |
|
||||||
|
|
||||||
init(teamRegistration: String? = nil, firstName: String, lastName: String, licenceId: String? = nil, rank: Int? = nil, paymentType: PlayerPaymentType? = nil, sex: PlayerSexType? = nil, tournamentPlayed: Int? = nil, points: Double? = nil, clubName: String? = nil, ligueName: String? = nil, assimilation: String? = nil, phoneNumber: String? = nil, email: String? = nil, birthdate: String? = nil, computedRank: Int = 0, source: PlayerDataSource? = nil, hasArrived: Bool = false) { |
|
||||||
self.teamRegistration = teamRegistration |
|
||||||
self.firstName = firstName |
|
||||||
self.lastName = lastName |
|
||||||
self.licenceId = licenceId |
|
||||||
self.rank = rank |
|
||||||
self.paymentType = paymentType |
|
||||||
self.sex = sex |
|
||||||
self.tournamentPlayed = tournamentPlayed |
|
||||||
self.points = points |
|
||||||
self.clubName = clubName |
|
||||||
self.ligueName = ligueName |
|
||||||
self.assimilation = assimilation |
|
||||||
self.phoneNumber = phoneNumber |
|
||||||
self.email = email |
|
||||||
self.birthdate = birthdate |
|
||||||
self.computedRank = computedRank |
|
||||||
self.source = source |
|
||||||
self.hasArrived = hasArrived |
|
||||||
} |
|
||||||
|
|
||||||
internal init(importedPlayer: ImportedPlayer) { |
|
||||||
self.teamRegistration = "" |
|
||||||
self.firstName = (importedPlayer.firstName ?? "").trimmed.capitalized |
|
||||||
self.lastName = (importedPlayer.lastName ?? "").trimmed.uppercased() |
|
||||||
self.licenceId = importedPlayer.license ?? nil |
|
||||||
self.rank = Int(importedPlayer.rank) |
|
||||||
self.sex = importedPlayer.male ? .male : .female |
|
||||||
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) { |
|
||||||
let _lastName = federalData[0].trimmed.uppercased() |
|
||||||
let _firstName = federalData[1].trimmed.capitalized |
|
||||||
if _lastName.isEmpty && _firstName.isEmpty { return nil } |
|
||||||
lastName = _lastName |
|
||||||
firstName = _firstName |
|
||||||
birthdate = federalData[2] |
|
||||||
licenceId = federalData[3] |
|
||||||
clubName = federalData[4] |
|
||||||
let stringRank = federalData[5] |
|
||||||
if stringRank.isEmpty { |
|
||||||
rank = nil |
|
||||||
} else { |
|
||||||
rank = Int(stringRank) |
|
||||||
} |
|
||||||
let _email = federalData[6] |
|
||||||
if _email.isEmpty == false { |
|
||||||
self.email = _email |
|
||||||
} |
|
||||||
let _phoneNumber = federalData[7] |
|
||||||
if _phoneNumber.isEmpty == false { |
|
||||||
self.phoneNumber = _phoneNumber |
|
||||||
} |
|
||||||
|
|
||||||
source = .beachPadel |
|
||||||
if sexUnknown { |
|
||||||
if sex == 1 && FileImportManager.shared.foundInWomenData(license: federalData[3]) { |
|
||||||
self.sex = .female |
|
||||||
} else if FileImportManager.shared.foundInMenData(license: federalData[3]) { |
|
||||||
self.sex = .male |
|
||||||
} else { |
|
||||||
self.sex = nil |
|
||||||
} |
|
||||||
} else { |
|
||||||
self.sex = PlayerSexType(rawValue: sex) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
var tournamentStore: TournamentStore { |
|
||||||
if let store = self.store as? TournamentStore { |
|
||||||
return store |
|
||||||
} |
|
||||||
fatalError("missing store for \(String(describing: type(of: self)))") |
|
||||||
} |
|
||||||
|
|
||||||
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(_ exportFormat: ExportFormat = .rawText) -> String { |
|
||||||
switch exportFormat { |
|
||||||
case .rawText: |
|
||||||
return [firstName.capitalized, lastName.capitalized, licenceId].compactMap({ $0 }).joined(separator: exportFormat.separator()) |
|
||||||
case .csv: |
|
||||||
return [lastName.uppercased() + " " + firstName.capitalized].joined(separator: exportFormat.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 self.tournamentStore.teamRegistrations.findById(teamRegistration) |
|
||||||
} |
|
||||||
|
|
||||||
func hasPaid() -> Bool { |
|
||||||
paymentType != nil |
|
||||||
} |
|
||||||
|
|
||||||
func playerLabel(_ displayStyle: DisplayStyle = .wide) -> String { |
|
||||||
switch displayStyle { |
|
||||||
case .wide, .title: |
|
||||||
return lastName.trimmed.capitalized + " " + firstName.trimmed.capitalized |
|
||||||
case .short: |
|
||||||
let names = lastName.components(separatedBy: .whitespaces) |
|
||||||
if lastName.components(separatedBy: .whitespaces).count > 1 { |
|
||||||
if let firstLongWord = names.first(where: { $0.count > 3 }) { |
|
||||||
return firstLongWord.trimmed.capitalized.trunc(length: 10) + " " + firstName.trimmed.prefix(1).capitalized + "." |
|
||||||
} |
|
||||||
} |
|
||||||
return lastName.trimmed.capitalized.trunc(length: 10) + " " + 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 { |
|
||||||
if rank != computedRank { |
|
||||||
return computedRank.formatted() + " (" + rank.formatted() + ")" |
|
||||||
} else { |
|
||||||
return rank.formatted() |
|
||||||
} |
|
||||||
} else { |
|
||||||
return "non classé" + (isMalePlayer() ? "" : "e") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func getRank() -> Int { |
|
||||||
computedRank |
|
||||||
} |
|
||||||
|
|
||||||
@MainActor |
|
||||||
func updateRank(from sources: [CSVParser], lastRank: Int) async throws { |
|
||||||
if let dataFound = try await history(from: sources) { |
|
||||||
rank = dataFound.rankValue?.toInt() |
|
||||||
points = dataFound.points |
|
||||||
tournamentPlayed = dataFound.tournamentCountValue?.toInt() |
|
||||||
} 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 setComputedRank(in tournament: Tournament) { |
|
||||||
let currentRank = rank ?? tournament.unrankValue(for: isMalePlayer()) ?? 70_000 |
|
||||||
switch tournament.tournamentCategory { |
|
||||||
case .men: |
|
||||||
computedRank = isMalePlayer() ? currentRank : currentRank + PlayerRegistration.addon(for: currentRank, manMax: tournament.maleUnrankedValue ?? 0, womanMax: tournament.femaleUnrankedValue ?? 0) |
|
||||||
default: |
|
||||||
computedRank = currentRank |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func isMalePlayer() -> Bool { |
|
||||||
sex == .male |
|
||||||
} |
|
||||||
|
|
||||||
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))" |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func hasHomonym() -> Bool { |
|
||||||
let federalContext = PersistenceController.shared.localContainer.viewContext |
|
||||||
let fetchRequest = ImportedPlayer.fetchRequest() |
|
||||||
let predicate = NSPredicate(format: "firstName == %@ && lastName == %@", firstName, lastName) |
|
||||||
fetchRequest.predicate = predicate |
|
||||||
|
|
||||||
do { |
|
||||||
let count = try federalContext.count(for: fetchRequest) |
|
||||||
return count > 1 |
|
||||||
} catch { |
|
||||||
|
|
||||||
} |
|
||||||
return false |
|
||||||
} |
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey { |
|
||||||
case _id = "id" |
|
||||||
case _teamRegistration = "teamRegistration" |
|
||||||
case _firstName = "firstName" |
|
||||||
case _lastName = "lastName" |
|
||||||
case _licenceId = "licenceId" |
|
||||||
case _rank = "rank" |
|
||||||
case _paymentType = "paymentType" |
|
||||||
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 _computedRank = "computedRank" |
|
||||||
case _source = "source" |
|
||||||
case _hasArrived = "hasArrived" |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
func encode(to encoder: Encoder) throws { |
|
||||||
var container = encoder.container(keyedBy: CodingKeys.self) |
|
||||||
|
|
||||||
try container.encode(id, forKey: ._id) |
|
||||||
|
|
||||||
if let teamRegistration = teamRegistration { |
|
||||||
try container.encode(teamRegistration, forKey: ._teamRegistration) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._teamRegistration) |
|
||||||
} |
|
||||||
|
|
||||||
try container.encode(firstName, forKey: ._firstName) |
|
||||||
try container.encode(lastName, forKey: ._lastName) |
|
||||||
|
|
||||||
if let licenceId = licenceId { |
|
||||||
try container.encode(licenceId, forKey: ._licenceId) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._licenceId) |
|
||||||
} |
|
||||||
|
|
||||||
if let rank = rank { |
|
||||||
try container.encode(rank, forKey: ._rank) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._rank) |
|
||||||
} |
|
||||||
|
|
||||||
if let paymentType = paymentType { |
|
||||||
try container.encode(paymentType, forKey: ._paymentType) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._paymentType) |
|
||||||
} |
|
||||||
|
|
||||||
if let sex = sex { |
|
||||||
try container.encode(sex, forKey: ._sex) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._sex) |
|
||||||
} |
|
||||||
|
|
||||||
if let tournamentPlayed = tournamentPlayed { |
|
||||||
try container.encode(tournamentPlayed, forKey: ._tournamentPlayed) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._tournamentPlayed) |
|
||||||
} |
|
||||||
|
|
||||||
if let points = points { |
|
||||||
try container.encode(points, forKey: ._points) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._points) |
|
||||||
} |
|
||||||
|
|
||||||
if let clubName = clubName { |
|
||||||
try container.encode(clubName, forKey: ._clubName) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._clubName) |
|
||||||
} |
|
||||||
|
|
||||||
if let ligueName = ligueName { |
|
||||||
try container.encode(ligueName, forKey: ._ligueName) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._ligueName) |
|
||||||
} |
|
||||||
|
|
||||||
if let assimilation = assimilation { |
|
||||||
try container.encode(assimilation, forKey: ._assimilation) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._assimilation) |
|
||||||
} |
|
||||||
|
|
||||||
if let phoneNumber = phoneNumber { |
|
||||||
try container.encode(phoneNumber, forKey: ._phoneNumber) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._phoneNumber) |
|
||||||
} |
|
||||||
|
|
||||||
if let email = email { |
|
||||||
try container.encode(email, forKey: ._email) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._email) |
|
||||||
} |
|
||||||
|
|
||||||
if let birthdate = birthdate { |
|
||||||
try container.encode(birthdate, forKey: ._birthdate) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._birthdate) |
|
||||||
} |
|
||||||
|
|
||||||
try container.encode(computedRank, forKey: ._computedRank) |
|
||||||
|
|
||||||
if let source = source { |
|
||||||
try container.encode(source, forKey: ._source) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._source) |
|
||||||
} |
|
||||||
|
|
||||||
try container.encode(hasArrived, forKey: ._hasArrived) |
|
||||||
} |
|
||||||
|
|
||||||
enum PlayerDataSource: Int, Codable { |
|
||||||
case frenchFederation = 0 |
|
||||||
case beachPadel = 1 |
|
||||||
} |
|
||||||
|
|
||||||
enum PlayerSexType: Int, Hashable, CaseIterable, Identifiable, Codable { |
|
||||||
init?(rawValue: Int?) { |
|
||||||
guard let value = rawValue else { return nil } |
|
||||||
self.init(rawValue: value) |
|
||||||
} |
|
||||||
|
|
||||||
var id: Self { |
|
||||||
self |
|
||||||
} |
|
||||||
|
|
||||||
case female = 0 |
|
||||||
case male = 1 |
|
||||||
} |
|
||||||
|
|
||||||
enum PlayerPaymentType: 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 |
|
||||||
case forfeit = 8 |
|
||||||
|
|
||||||
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 .forfeit: |
|
||||||
return "Forfait" |
|
||||||
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 |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func insertOnServer() { |
|
||||||
self.tournamentStore.playerRegistrations.writeChangeAndInsertOnServer(instance: self) |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
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 getAssimilatedAsMaleRank() -> Int? { |
|
||||||
nil |
|
||||||
} |
|
||||||
|
|
||||||
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() |
|
||||||
} |
|
||||||
} |
|
||||||
@ -0,0 +1,155 @@ |
|||||||
|
// |
||||||
|
// Round+Extensions.swift |
||||||
|
// PadelClub |
||||||
|
// |
||||||
|
// Created by Laurent Morvillier on 27/08/2024. |
||||||
|
// |
||||||
|
|
||||||
|
import Foundation |
||||||
|
import PadelClubData |
||||||
|
|
||||||
|
extension Round { |
||||||
|
|
||||||
|
static func setServerTitle(upperRound: Round, matchIndex: Int) -> String { |
||||||
|
if upperRound.index == 0 { return upperRound.roundTitle() } |
||||||
|
return upperRound.roundTitle() + " #" + (matchIndex + 1).formatted() |
||||||
|
} |
||||||
|
|
||||||
|
func roundTitle(_ displayStyle: DisplayStyle = .wide, initialMode: Bool = false) -> String { |
||||||
|
if parent != nil { |
||||||
|
if let seedInterval = seedInterval(initialMode: initialMode) { |
||||||
|
return seedInterval.localizedLabel(displayStyle) |
||||||
|
} |
||||||
|
print("Round pas trouvé", id, parent, index) |
||||||
|
return "Match de classement" |
||||||
|
} |
||||||
|
return RoundRule.roundName(fromRoundIndex: index, displayStyle: displayStyle) |
||||||
|
} |
||||||
|
|
||||||
|
func pasteData() -> String { |
||||||
|
var data: [String] = [] |
||||||
|
data.append(self.roundTitle()) |
||||||
|
|
||||||
|
playedMatches().forEach { match in |
||||||
|
data.append(match.matchTitle()) |
||||||
|
data.append(match.team(.one)?.teamLabelRanked(displayRank: true, displayTeamName: true) ?? "-----") |
||||||
|
data.append(match.team(.two)?.teamLabelRanked(displayRank: true, displayTeamName: true) ?? "-----") |
||||||
|
} |
||||||
|
|
||||||
|
return data.joined(separator: "\n") |
||||||
|
} |
||||||
|
|
||||||
|
func correspondingLoserRoundTitle(_ displayStyle: DisplayStyle = .wide) -> String { |
||||||
|
#if _DEBUG_TIME //DEBUGING TIME |
||||||
|
let start = Date() |
||||||
|
defer { |
||||||
|
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) |
||||||
|
print("func correspondingLoserRoundTitle()", duration.formatted(.units(allowed: [.seconds, .milliseconds]))) |
||||||
|
} |
||||||
|
#endif |
||||||
|
let initialMatchIndexFromRoundIndex = RoundRule.matchIndex(fromRoundIndex: index) |
||||||
|
let seedsAfterThisRound: [TeamRegistration] = self.tournamentStore.teamRegistrations.filter { |
||||||
|
$0.bracketPosition != nil |
||||||
|
&& ($0.bracketPosition! / 2) < initialMatchIndexFromRoundIndex |
||||||
|
} |
||||||
|
|
||||||
|
// let seedsAfterThisRound : [TeamRegistration] = Store.main.filter(isIncluded: { |
||||||
|
// $0.tournament == tournament |
||||||
|
// && $0.bracketPosition != nil |
||||||
|
// && ($0.bracketPosition! / 2) < initialMatchIndexFromRoundIndex |
||||||
|
// }) |
||||||
|
let playedMatches = playedMatches() |
||||||
|
let seedInterval = SeedInterval(first: playedMatches.count + seedsAfterThisRound.count + 1, last: playedMatches.count * 2 + seedsAfterThisRound.count) |
||||||
|
return seedInterval.localizedLabel(displayStyle) |
||||||
|
} |
||||||
|
|
||||||
|
func buildLoserBracket() { |
||||||
|
guard loserRounds().isEmpty else { return } |
||||||
|
let currentRoundMatchCount = RoundRule.numberOfMatches(forRoundIndex: index) |
||||||
|
guard currentRoundMatchCount > 1 else { return } |
||||||
|
let roundCount = RoundRule.numberOfRounds(forTeams: currentRoundMatchCount) |
||||||
|
|
||||||
|
var loserBracketMatchFormat = tournamentObject()?.loserBracketMatchFormat |
||||||
|
if let parentRound { |
||||||
|
loserBracketMatchFormat = tournamentObject()?.loserBracketSmartMatchFormat(parentRound.index) |
||||||
|
} |
||||||
|
|
||||||
|
let rounds = (0..<roundCount).map { //index 0 is the final |
||||||
|
let round = Round(tournament: tournament, index: $0, matchFormat: loserBracketMatchFormat) |
||||||
|
round.parent = id //parent |
||||||
|
return round |
||||||
|
} |
||||||
|
|
||||||
|
do { |
||||||
|
try self.tournamentStore.rounds.addOrUpdate(contentOfs: rounds) |
||||||
|
} catch { |
||||||
|
Logger.error(error) |
||||||
|
} |
||||||
|
let matchCount = RoundRule.numberOfMatches(forTeams: currentRoundMatchCount) |
||||||
|
|
||||||
|
let matches = (0..<matchCount).map { //0 is final match |
||||||
|
let roundIndex = RoundRule.roundIndex(fromMatchIndex: $0) |
||||||
|
let round = rounds[roundIndex] |
||||||
|
return Match(round: round.id, index: $0, matchFormat: loserBracketMatchFormat, name: round.roundTitle(initialMode: true)) |
||||||
|
//initial mode let the roundTitle give a name without considering the playable match |
||||||
|
} |
||||||
|
|
||||||
|
do { |
||||||
|
try self.tournamentStore.matches.addOrUpdate(contentOfs: matches) |
||||||
|
} catch { |
||||||
|
Logger.error(error) |
||||||
|
} |
||||||
|
|
||||||
|
loserRounds().forEach { round in |
||||||
|
round.buildLoserBracket() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
extension Round: Selectable, Equatable { |
||||||
|
static func == (lhs: Round, rhs: Round) -> Bool { |
||||||
|
lhs.id == rhs.id |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
func selectionLabel(index: Int) -> String { |
||||||
|
if let parentRound { |
||||||
|
return "Tour #\(parentRound.loserRounds().count - index)" |
||||||
|
} else { |
||||||
|
return roundTitle(.short) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func badgeValue() -> Int? { |
||||||
|
#if _DEBUG_TIME //DEBUGING TIME |
||||||
|
let start = Date() |
||||||
|
defer { |
||||||
|
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) |
||||||
|
print("func badgeValue round of: ", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
|
||||||
|
if let parentRound { |
||||||
|
return parentRound.loserRounds(forRoundIndex: index).flatMap { $0.playedMatches() }.filter({ $0.isRunning() }).count |
||||||
|
} else { |
||||||
|
return playedMatches().filter({ $0.isRunning() }).count |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func badgeValueColor() -> Color? { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func badgeImage() -> Badge? { |
||||||
|
#if _DEBUG_TIME //DEBUGING TIME |
||||||
|
let start = Date() |
||||||
|
defer { |
||||||
|
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000) |
||||||
|
print("func badgeImage of round: ", id, duration.formatted(.units(allowed: [.seconds, .milliseconds]))) |
||||||
|
} |
||||||
|
#endif |
||||||
|
return hasEnded() ? .checkmark : nil |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,20 @@ |
|||||||
|
// |
||||||
|
// SeedInterval+Extensions.swift |
||||||
|
// PadelClub |
||||||
|
// |
||||||
|
// Created by Laurent Morvillier on 27/08/2024. |
||||||
|
// |
||||||
|
|
||||||
|
import Foundation |
||||||
|
import PadelClubData |
||||||
|
|
||||||
|
extension SeedInterval { |
||||||
|
|
||||||
|
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String { |
||||||
|
if dimension < 3 { |
||||||
|
return "\(first)\(first.ordinalFormattedSuffix()) place" |
||||||
|
} else { |
||||||
|
return "Place \(first) à \(last)" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,109 @@ |
|||||||
|
// |
||||||
|
// TeamRegistration+Extensions.swift |
||||||
|
// PadelClub |
||||||
|
// |
||||||
|
// Created by Laurent Morvillier on 27/08/2024. |
||||||
|
// |
||||||
|
|
||||||
|
import Foundation |
||||||
|
import PadelClubData |
||||||
|
|
||||||
|
extension TeamRegistration { |
||||||
|
|
||||||
|
func getPhoneNumbers() -> [String] { |
||||||
|
return players().compactMap { $0.phoneNumber }.filter({ $0.isMobileNumber() }) |
||||||
|
} |
||||||
|
|
||||||
|
func playersPasteData(_ exportFormat: ExportFormat = .rawText) -> String { |
||||||
|
switch exportFormat { |
||||||
|
case .rawText: |
||||||
|
return players().map { $0.pasteData(exportFormat) }.joined(separator: exportFormat.newLineSeparator()) |
||||||
|
case .csv: |
||||||
|
return players().map { [$0.pasteData(exportFormat), isWildCard() ? "WC" : $0.computedRank.formatted() ].joined(separator: exportFormat.separator()) }.joined(separator: exportFormat.separator()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func formattedInscriptionDate(_ exportFormat: ExportFormat = .rawText) -> String? { |
||||||
|
switch exportFormat { |
||||||
|
case .rawText: |
||||||
|
if let registrationDate { |
||||||
|
return "Inscrit le " + registrationDate.formatted(.dateTime.weekday().day().month().hour().minute()) |
||||||
|
} else { |
||||||
|
return nil |
||||||
|
} |
||||||
|
case .csv: |
||||||
|
if let registrationDate { |
||||||
|
return registrationDate.formatted(.dateTime.weekday().day().month().hour().minute()) |
||||||
|
} else { |
||||||
|
return nil |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func formattedSummonDate(_ exportFormat: ExportFormat = .rawText) -> String? { |
||||||
|
|
||||||
|
switch exportFormat { |
||||||
|
case .rawText: |
||||||
|
if let callDate { |
||||||
|
return "Convoqué le " + callDate.formatted(.dateTime.weekday().day().month().hour().minute()) |
||||||
|
} else { |
||||||
|
return nil |
||||||
|
} |
||||||
|
case .csv: |
||||||
|
if let callDate { |
||||||
|
return callDate.formatted(.dateTime.weekday().day().month().hour().minute()) |
||||||
|
} else { |
||||||
|
return nil |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func pasteData(_ exportFormat: ExportFormat = .rawText, _ index: Int = 0) -> String { |
||||||
|
switch exportFormat { |
||||||
|
case .rawText: |
||||||
|
return [playersPasteData(exportFormat), formattedInscriptionDate(exportFormat), name].compactMap({ $0 }).joined(separator: exportFormat.newLineSeparator()) |
||||||
|
case .csv: |
||||||
|
return [index.formatted(), playersPasteData(exportFormat), isWildCard() ? "WC" : weight.formatted()].joined(separator: exportFormat.separator()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func positionLabel() -> String? { |
||||||
|
if groupStagePosition != nil { return "Poule" } |
||||||
|
if let initialRound = initialRound() { |
||||||
|
return initialRound.roundTitle() |
||||||
|
} else { |
||||||
|
return nil |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func initialRoundColor() -> Color? { |
||||||
|
if walkOut { return Color.logoRed } |
||||||
|
if groupStagePosition != nil { return Color.blue } |
||||||
|
if let initialRound = initialRound(), let colorHex = RoundRule.colors[safe: initialRound.index] { |
||||||
|
return Color(uiColor: .init(fromHex: colorHex)) |
||||||
|
} else { |
||||||
|
return nil |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func containsExactlyPlayerLicenses(_ playerLicenses: [String?]) -> Bool { |
||||||
|
let arrayOfIds : [String] = unsortedPlayers().compactMap({ $0.licenceId?.strippedLicense?.canonicalVersion }) |
||||||
|
let ids : Set<String> = Set<String>(arrayOfIds.sorted()) |
||||||
|
let searchedIds = Set<String>(playerLicenses.compactMap({ $0?.strippedLicense?.canonicalVersion }).sorted()) |
||||||
|
return ids.hashValue == searchedIds.hashValue |
||||||
|
} |
||||||
|
|
||||||
|
@objc |
||||||
|
var canonicalName: String { |
||||||
|
players().map { $0.canonicalName }.joined(separator: " ") |
||||||
|
} |
||||||
|
|
||||||
|
func teamLabel(_ displayStyle: DisplayStyle = .wide, twoLines: Bool = false) -> String { |
||||||
|
return players().map { $0.playerLabel(displayStyle) }.joined(separator: twoLines ? "\n" : " & ") |
||||||
|
} |
||||||
|
|
||||||
|
func teamLabelRanked(displayRank: Bool, displayTeamName: Bool) -> String { |
||||||
|
[displayTeamName ? name : nil, displayRank ? seedIndex() : nil, displayTeamName ? (name == nil ? teamLabel() : name) : teamLabel()].compactMap({ $0 }).joined(separator: " ") |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,395 @@ |
|||||||
|
// |
||||||
|
// Tournament+Extensions.swift |
||||||
|
// PadelClub |
||||||
|
// |
||||||
|
// Created by Laurent Morvillier on 27/08/2024. |
||||||
|
// |
||||||
|
|
||||||
|
import Foundation |
||||||
|
import PadelClubData |
||||||
|
|
||||||
|
extension Tournament { |
||||||
|
|
||||||
|
func shareURL(_ pageLink: PageLink = .matches) -> URL? { |
||||||
|
if pageLink == .clubBroadcast { |
||||||
|
let club = club() |
||||||
|
print("club", club) |
||||||
|
print("club broadcast code", club?.broadcastCode) |
||||||
|
if let club, let broadcastCode = club.broadcastCode { |
||||||
|
return URLs.main.url.appending(path: "c/\(broadcastCode)") |
||||||
|
} else { |
||||||
|
return nil |
||||||
|
} |
||||||
|
} |
||||||
|
return URLs.main.url.appending(path: "tournament/\(id)").appending(path: pageLink.path) |
||||||
|
} |
||||||
|
|
||||||
|
func pasteDataForImporting(_ exportFormat: ExportFormat = .rawText) -> String { |
||||||
|
let selectedSortedTeams = selectedSortedTeams() |
||||||
|
switch exportFormat { |
||||||
|
case .rawText: |
||||||
|
return (selectedSortedTeams.compactMap { $0.pasteData(exportFormat) } + ["Liste d'attente"] + waitingListTeams(in: selectedSortedTeams, includingWalkOuts: true).compactMap { $0.pasteData(exportFormat) }).joined(separator: exportFormat.newLineSeparator(2)) |
||||||
|
case .csv: |
||||||
|
let headers = ["N°", "Nom Prénom", "rang", "Nom Prénom", "rang", "poids"].joined(separator: exportFormat.separator()) |
||||||
|
var teamPaste = [headers] |
||||||
|
for (index, team) in selectedSortedTeams.enumerated() { |
||||||
|
teamPaste.append(team.pasteData(exportFormat, index + 1)) |
||||||
|
} |
||||||
|
return teamPaste.joined(separator: exportFormat.newLineSeparator()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func importTeams(_ teams: [FileImportManager.TeamHolder]) { |
||||||
|
var teamsToImport = [TeamRegistration]() |
||||||
|
let players = players().filter { $0.licenceId != nil } |
||||||
|
teams.forEach { team in |
||||||
|
if let previousTeam = team.previousTeam { |
||||||
|
previousTeam.updatePlayers(team.players, inTournamentCategory: team.tournamentCategory) |
||||||
|
teamsToImport.append(previousTeam) |
||||||
|
} else { |
||||||
|
var registrationDate = team.registrationDate |
||||||
|
if let previousPlayer = players.first(where: { player in |
||||||
|
let ids = team.players.compactMap({ $0.licenceId }) |
||||||
|
return ids.contains(player.licenceId!) |
||||||
|
}), let previousTeamRegistrationDate = previousPlayer.team()?.registrationDate { |
||||||
|
registrationDate = previousTeamRegistrationDate |
||||||
|
} |
||||||
|
let newTeam = addTeam(team.players, registrationDate: registrationDate, name: team.name) |
||||||
|
teamsToImport.append(newTeam) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
do { |
||||||
|
try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teamsToImport) |
||||||
|
} catch { |
||||||
|
Logger.error(error) |
||||||
|
} |
||||||
|
do { |
||||||
|
try self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: teams.flatMap { $0.players }) |
||||||
|
} catch { |
||||||
|
Logger.error(error) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
if state() == .build && groupStageCount > 0 && groupStageTeams().isEmpty { |
||||||
|
setGroupStage(randomize: groupStageSortMode == .random) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func homonyms(in players: [PlayerRegistration]) -> [PlayerRegistration] { |
||||||
|
players.filter({ $0.hasHomonym() }) |
||||||
|
} |
||||||
|
|
||||||
|
func finalRanking() async -> [Int: [String]] { |
||||||
|
var teams: [Int: [String]] = [:] |
||||||
|
var ids: Set<String> = Set<String>() |
||||||
|
let rounds = rounds() |
||||||
|
let final = rounds.last?.playedMatches().last |
||||||
|
if let winner = final?.winningTeamId { |
||||||
|
teams[1] = [winner] |
||||||
|
ids.insert(winner) |
||||||
|
} |
||||||
|
if let finalist = final?.losingTeamId { |
||||||
|
teams[2] = [finalist] |
||||||
|
ids.insert(finalist) |
||||||
|
} |
||||||
|
|
||||||
|
let others: [Round] = rounds.flatMap { round in |
||||||
|
let losers = round.losers() |
||||||
|
let minimumFinalPosition = round.seedInterval()?.last ?? teamCount |
||||||
|
if teams[minimumFinalPosition] == nil { |
||||||
|
teams[minimumFinalPosition] = losers.map { $0.id } |
||||||
|
} else { |
||||||
|
teams[minimumFinalPosition]?.append(contentsOf: losers.map { $0.id }) |
||||||
|
} |
||||||
|
|
||||||
|
print("round", round.roundTitle()) |
||||||
|
let rounds = round.loserRoundsAndChildren().filter { $0.isRankDisabled() == false && $0.hasNextRound() == false } |
||||||
|
print(rounds.count, rounds.map { $0.roundTitle() }) |
||||||
|
return rounds |
||||||
|
}.compactMap({ $0 }) |
||||||
|
|
||||||
|
others.forEach { round in |
||||||
|
print("round", round.roundTitle()) |
||||||
|
if let interval = round.seedInterval() { |
||||||
|
print("interval", interval.localizedLabel()) |
||||||
|
let playedMatches = round.playedMatches().filter { $0.disabled == false || $0.isReady() } |
||||||
|
print("playedMatches", playedMatches.count) |
||||||
|
let winners = playedMatches.compactMap({ $0.winningTeamId }).filter({ ids.contains($0) == false }) |
||||||
|
print("winners", winners.count) |
||||||
|
let losers = playedMatches.compactMap({ $0.losingTeamId }).filter({ ids.contains($0) == false }) |
||||||
|
print("losers", losers.count) |
||||||
|
if winners.isEmpty { |
||||||
|
let disabledIds = playedMatches.flatMap({ $0.teamScores.compactMap({ $0.teamRegistration }) }).filter({ ids.contains($0) == false }) |
||||||
|
if disabledIds.isEmpty == false { |
||||||
|
_removeStrings(from: &teams, stringsToRemove: disabledIds) |
||||||
|
teams[interval.last] = disabledIds |
||||||
|
let teamNames : [String] = disabledIds.compactMap { |
||||||
|
let t : TeamRegistration? = Store.main.findById($0) |
||||||
|
return t |
||||||
|
}.map { $0.canonicalName } |
||||||
|
print("winners.isEmpty", "\(interval.last) : ", teamNames) |
||||||
|
disabledIds.forEach { |
||||||
|
ids.insert($0) |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
if winners.isEmpty == false { |
||||||
|
_removeStrings(from: &teams, stringsToRemove: winners) |
||||||
|
teams[interval.first + winners.count - 1] = winners |
||||||
|
let teamNames : [String] = winners.compactMap { |
||||||
|
let t: TeamRegistration? = Store.main.findById($0) |
||||||
|
return t |
||||||
|
}.map { $0.canonicalName } |
||||||
|
print("winners", "\(interval.last + winners.count - 1) : ", teamNames) |
||||||
|
winners.forEach { ids.insert($0) } |
||||||
|
} |
||||||
|
|
||||||
|
if losers.isEmpty == false { |
||||||
|
_removeStrings(from: &teams, stringsToRemove: losers) |
||||||
|
teams[interval.last] = losers |
||||||
|
let loserTeamNames : [String] = losers.compactMap { |
||||||
|
let t: TeamRegistration? = Store.main.findById($0) |
||||||
|
return t |
||||||
|
}.map { $0.canonicalName } |
||||||
|
print("losers", "\(interval.last) : ", loserTeamNames) |
||||||
|
losers.forEach { ids.insert($0) } |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
let groupStages = groupStages() |
||||||
|
let baseRank = teamCount - groupStageSpots() + qualifiedPerGroupStage * groupStageCount + groupStageAdditionalQualified |
||||||
|
|
||||||
|
groupStages.forEach { groupStage in |
||||||
|
let groupStageTeams = groupStage.teams(true) |
||||||
|
for (index, team) in groupStageTeams.enumerated() { |
||||||
|
if team.qualified == false { |
||||||
|
let groupStageWidth = max(((index == qualifiedPerGroupStage) ? groupStageCount - groupStageAdditionalQualified : groupStageCount) * (index - qualifiedPerGroupStage), 0) |
||||||
|
|
||||||
|
let _index = baseRank + groupStageWidth + 1 |
||||||
|
if let existingTeams = teams[_index] { |
||||||
|
teams[_index] = existingTeams + [team.id] |
||||||
|
} else { |
||||||
|
teams[_index] = [team.id] |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return teams |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
extension Tournament: FederalTournamentHolder { |
||||||
|
var holderId: String { id } |
||||||
|
|
||||||
|
func clubLabel() -> String { |
||||||
|
locationLabel() |
||||||
|
} |
||||||
|
|
||||||
|
func subtitleLabel() -> String { |
||||||
|
subtitle() |
||||||
|
} |
||||||
|
|
||||||
|
var tournaments: [any TournamentBuildHolder] { |
||||||
|
[ |
||||||
|
self |
||||||
|
] |
||||||
|
} |
||||||
|
|
||||||
|
func bracketStatus() async -> (status: String, description: String?, cut: TeamRegistration.TeamRange?) { |
||||||
|
let availableSeeds = availableSeeds() |
||||||
|
var description: String? = nil |
||||||
|
if availableSeeds.isEmpty == false { |
||||||
|
description = "placer \(availableSeeds.count) équipe\(availableSeeds.count.pluralSuffix)" |
||||||
|
} |
||||||
|
if description == nil { |
||||||
|
let availableQualifiedTeams = availableQualifiedTeams() |
||||||
|
if availableQualifiedTeams.isEmpty == false { |
||||||
|
description = "placer \(availableQualifiedTeams.count) qualifié" + availableQualifiedTeams.count.pluralSuffix |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
var cut: TeamRegistration.TeamRange? = nil |
||||||
|
if description == nil && isAnimation() == false { |
||||||
|
cut = TeamRegistration.TeamRange(availableSeeds.first, availableSeeds.last) |
||||||
|
} |
||||||
|
|
||||||
|
if let round = getActiveRound() { |
||||||
|
return ([round.roundTitle(.short), round.roundStatus()].joined(separator: " ").lowercased(), description, cut) |
||||||
|
} else { |
||||||
|
return ("", description, nil) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func groupStageStatus() async -> (status: String, cut: TeamRegistration.TeamRange?) { |
||||||
|
let groupStageTeams = groupStageTeams() |
||||||
|
let groupStageTeamsCount = groupStageTeams.count |
||||||
|
if groupStageTeamsCount == 0 || groupStageTeamsCount != groupStageSpots() { |
||||||
|
return ("à compléter", nil) |
||||||
|
} |
||||||
|
|
||||||
|
let cut : TeamRegistration.TeamRange? = isAnimation() ? nil : TeamRegistration.TeamRange(groupStageTeams.first, groupStageTeams.last) |
||||||
|
|
||||||
|
let runningGroupStages = groupStages().filter({ $0.isRunning() }) |
||||||
|
if groupStagesAreOver() { return ("terminées", cut) } |
||||||
|
if runningGroupStages.isEmpty { |
||||||
|
|
||||||
|
let ongoingGroupStages = runningGroupStages.filter({ $0.hasStarted() && $0.hasEnded() == false }) |
||||||
|
if ongoingGroupStages.isEmpty == false { |
||||||
|
return ("Poule" + ongoingGroupStages.count.pluralSuffix + " " + ongoingGroupStages.map { ($0.index + 1).formatted() }.joined(separator: ", ") + " en cours", cut) |
||||||
|
} |
||||||
|
return (groupStages().count.formatted() + " poule" + groupStages().count.pluralSuffix, cut) |
||||||
|
} else { |
||||||
|
return ("Poule" + runningGroupStages.count.pluralSuffix + " " + runningGroupStages.map { ($0.index + 1).formatted() }.joined(separator: ", ") + " en cours", cut) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func settingsDescriptionLocalizedLabel() -> String { |
||||||
|
[courtCount.formatted() + " terrain\(courtCount.pluralSuffix)", entryFeeMessage].joined(separator: ", ") |
||||||
|
} |
||||||
|
|
||||||
|
func structureDescriptionLocalizedLabel() -> String { |
||||||
|
let groupStageLabel: String? = groupStageCount > 0 ? groupStageCount.formatted() + " poule\(groupStageCount.pluralSuffix)" : nil |
||||||
|
return [teamCount.formatted() + " équipes", groupStageLabel].compactMap({ $0 }).joined(separator: ", ") |
||||||
|
} |
||||||
|
|
||||||
|
fileprivate func _paymentMethodMessage() -> String? { |
||||||
|
return DataStore.shared.user.summonsAvailablePaymentMethods ?? ContactType.defaultAvailablePaymentMethods |
||||||
|
} |
||||||
|
|
||||||
|
func updateRank(to newDate: Date?) async throws { |
||||||
|
guard let newDate else { return } |
||||||
|
rankSourceDate = newDate |
||||||
|
|
||||||
|
if currentMonthData() == nil { |
||||||
|
let lastRankWoman = SourceFileManager.shared.getUnrankValue(forMale: false, rankSourceDate: rankSourceDate) |
||||||
|
let lastRankMan = SourceFileManager.shared.getUnrankValue(forMale: true, rankSourceDate: rankSourceDate) |
||||||
|
await MainActor.run { |
||||||
|
let formatted: String = URL.importDateFormatter.string(from: newDate) |
||||||
|
let monthData: MonthData = MonthData(monthKey: formatted) |
||||||
|
monthData.maleUnrankedValue = lastRankMan |
||||||
|
monthData.femaleUnrankedValue = lastRankWoman |
||||||
|
do { |
||||||
|
try DataStore.shared.monthData.addOrUpdate(instance: monthData) |
||||||
|
} catch { |
||||||
|
Logger.error(error) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
let lastRankMan = currentMonthData()?.maleUnrankedValue |
||||||
|
let lastRankWoman = currentMonthData()?.femaleUnrankedValue |
||||||
|
let dataURLs = SourceFileManager.shared.allFiles.filter { $0.dateFromPath == newDate } |
||||||
|
let sources = dataURLs.map { CSVParser(url: $0) } |
||||||
|
|
||||||
|
try await unsortedPlayers().concurrentForEach { player in |
||||||
|
try await player.updateRank(from: sources, lastRank: (player.sex == .female ? lastRankWoman : lastRankMan) ?? 0) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
var entryFeeMessage: String { |
||||||
|
if let entryFee { |
||||||
|
let message: String = "Inscription: \(entryFee.formatted(.currency(code: "EUR"))) par joueur." |
||||||
|
return [message, self._paymentMethodMessage()].compactMap { $0 }.joined(separator: "\n") |
||||||
|
} else { |
||||||
|
return "Inscription: gratuite." |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func deleteAndBuildEverything() { |
||||||
|
resetBracketPosition() |
||||||
|
deleteStructure() |
||||||
|
deleteGroupStages() |
||||||
|
buildGroupStages() |
||||||
|
buildBracket() |
||||||
|
} |
||||||
|
|
||||||
|
func buildBracket() { |
||||||
|
guard rounds().isEmpty else { return } |
||||||
|
let roundCount = RoundRule.numberOfRounds(forTeams: bracketTeamCount()) |
||||||
|
|
||||||
|
let rounds = (0..<roundCount).map { //index 0 is the final |
||||||
|
return Round(tournament: id, index: $0, matchFormat: roundSmartMatchFormat($0)) |
||||||
|
} |
||||||
|
|
||||||
|
do { |
||||||
|
try self.tournamentStore.rounds.addOrUpdate(contentOfs: rounds) |
||||||
|
} catch { |
||||||
|
Logger.error(error) |
||||||
|
} |
||||||
|
let matchCount = RoundRule.numberOfMatches(forTeams: bracketTeamCount()) |
||||||
|
|
||||||
|
let matches = (0..<matchCount).map { //0 is final match |
||||||
|
let roundIndex = RoundRule.roundIndex(fromMatchIndex: $0) |
||||||
|
let round = rounds[roundIndex] |
||||||
|
return Match(round: round.id, index: $0, matchFormat: round.matchFormat, name: Match.setServerTitle(upperRound: round, matchIndex: RoundRule.matchIndexWithinRound(fromMatchIndex: $0))) |
||||||
|
} |
||||||
|
|
||||||
|
print(matches.map { |
||||||
|
(RoundRule.roundName(fromMatchIndex: $0.index), RoundRule.matchIndexWithinRound(fromMatchIndex: $0.index)) |
||||||
|
}) |
||||||
|
|
||||||
|
do { |
||||||
|
try self.tournamentStore.matches.addOrUpdate(contentOfs: matches) |
||||||
|
} catch { |
||||||
|
Logger.error(error) |
||||||
|
} |
||||||
|
|
||||||
|
rounds.forEach { round in |
||||||
|
round.buildLoserBracket() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func registrationIssues() -> Int { |
||||||
|
let players : [PlayerRegistration] = unsortedPlayers() |
||||||
|
let selectedTeams : [TeamRegistration] = selectedSortedTeams() |
||||||
|
let callDateIssue : [TeamRegistration] = selectedTeams.filter { $0.callDate != nil && isStartDateIsDifferentThanCallDate($0) } |
||||||
|
let duplicates : [PlayerRegistration] = duplicates(in: players) |
||||||
|
let problematicPlayers : [PlayerRegistration] = players.filter({ $0.sex == nil }) |
||||||
|
let inadequatePlayers : [PlayerRegistration] = inadequatePlayers(in: players) |
||||||
|
let playersWithoutValidLicense : [PlayerRegistration] = playersWithoutValidLicense(in: players) |
||||||
|
let playersMissing : [TeamRegistration] = selectedTeams.filter({ $0.unsortedPlayers().count < 2 }) |
||||||
|
let waitingList : [TeamRegistration] = waitingListTeams(in: selectedTeams, includingWalkOuts: true) |
||||||
|
let waitingListInBracket = waitingList.filter({ $0.bracketPosition != nil }) |
||||||
|
let waitingListInGroupStage = waitingList.filter({ $0.groupStage != nil }) |
||||||
|
|
||||||
|
return callDateIssue.count + duplicates.count + problematicPlayers.count + inadequatePlayers.count + playersWithoutValidLicense.count + playersMissing.count + waitingListInBracket.count + waitingListInGroupStage.count |
||||||
|
} |
||||||
|
|
||||||
|
func playersWithoutValidLicense(in players: [PlayerRegistration]) -> [PlayerRegistration] { |
||||||
|
let licenseYearValidity = self.licenseYearValidity() |
||||||
|
return players.filter { $0.hasInvalidLicence() } |
||||||
|
} |
||||||
|
|
||||||
|
func missingUnrankedValue() -> Bool { |
||||||
|
return maleUnrankedValue == nil || femaleUnrankedValue == nil |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
extension Tournament { |
||||||
|
static func newEmptyInstance() -> Tournament { |
||||||
|
let lastDataSource: String? = DataStore.shared.appSettings.lastDataSource |
||||||
|
var _mostRecentDateAvailable: Date? { |
||||||
|
guard let lastDataSource else { return nil } |
||||||
|
return URL.importDateFormatter.date(from: lastDataSource) |
||||||
|
} |
||||||
|
|
||||||
|
let rankSourceDate = _mostRecentDateAvailable |
||||||
|
let tournaments : [Tournament] = DataStore.shared.tournaments.filter { $0.endDate != nil && $0.isDeleted == false } |
||||||
|
let tournamentLevel = TournamentLevel.mostUsed(inTournaments: tournaments) |
||||||
|
let tournamentCategory = TournamentCategory.mostUsed(inTournaments: tournaments) |
||||||
|
let federalTournamentAge = FederalTournamentAge.mostUsed(inTournaments: tournaments) |
||||||
|
//creator: DataStore.shared.user?.id |
||||||
|
return Tournament(groupStageSortMode: .snake, rankSourceDate: rankSourceDate, teamSorting: tournamentLevel.defaultTeamSortingType, federalCategory: tournamentCategory, federalLevelCategory: tournamentLevel, federalAgeCategory: federalTournamentAge) |
||||||
|
} |
||||||
|
|
||||||
|
static func fake() -> Tournament { |
||||||
|
return Tournament(event: "Roland Garros", name: "Magic P100", startDate: Date(), endDate: Date(), creationDate: Date(), isPrivate: false, groupStageFormat: .nineGames, roundFormat: nil, loserRoundFormat: nil, groupStageSortMode: .snake, groupStageCount: 4, rankSourceDate: nil, dayDuration: 2, teamCount: 24, teamSorting: .rank, federalCategory: .men, federalLevelCategory: .p100, federalAgeCategory: .a45, closedRegistrationDate: nil, groupStageAdditionalQualified: 0, courtCount: 4, prioritizeClubMembers: false, qualifiedPerGroupStage: 2, teamsPerGroupStage: 4, entryFee: nil) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,21 @@ |
|||||||
|
// |
||||||
|
// User+Extensions.swift |
||||||
|
// PadelClub |
||||||
|
// |
||||||
|
// Created by Laurent Morvillier on 28/08/2024. |
||||||
|
// |
||||||
|
|
||||||
|
import Foundation |
||||||
|
|
||||||
|
extension User { |
||||||
|
|
||||||
|
func currentPlayerData() -> ImportedPlayer? { |
||||||
|
guard let licenceId else { return nil } |
||||||
|
let federalContext = PersistenceController.shared.localContainer.viewContext |
||||||
|
let fetchRequest = ImportedPlayer.fetchRequest() |
||||||
|
let predicate = NSPredicate(format: "license == %@", licenceId) |
||||||
|
fetchRequest.predicate = predicate |
||||||
|
return try? federalContext.fetch(fetchRequest).first |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -1,266 +0,0 @@ |
|||||||
// |
|
||||||
// User.swift |
|
||||||
// PadelClub |
|
||||||
// |
|
||||||
// Created by Laurent Morvillier on 21/02/2024. |
|
||||||
// |
|
||||||
|
|
||||||
import Foundation |
|
||||||
import LeStorage |
|
||||||
|
|
||||||
enum UserRight: Int, Codable { |
|
||||||
case none = 0 |
|
||||||
case edition = 1 |
|
||||||
case creation = 2 |
|
||||||
} |
|
||||||
|
|
||||||
@Observable |
|
||||||
class User: ModelObject, UserBase, Storable { |
|
||||||
|
|
||||||
static func resourceName() -> String { "users" } |
|
||||||
static func tokenExemptedMethods() -> [HTTPMethod] { return [.post] } |
|
||||||
static func filterByStoreIdentifier() -> Bool { return false } |
|
||||||
static var relationshipNames: [String] = [] |
|
||||||
|
|
||||||
public var id: String = Store.randomId() |
|
||||||
public var username: String |
|
||||||
public var email: String |
|
||||||
var clubs: [String] = [] |
|
||||||
var umpireCode: String? |
|
||||||
var licenceId: String? |
|
||||||
var firstName: String |
|
||||||
var lastName: String |
|
||||||
var phone: String? |
|
||||||
var country: String? |
|
||||||
|
|
||||||
var summonsMessageBody : String? = nil |
|
||||||
var summonsMessageSignature: String? = nil |
|
||||||
var summonsAvailablePaymentMethods: String? = nil |
|
||||||
var summonsDisplayFormat: Bool = false |
|
||||||
var summonsDisplayEntryFee: Bool = false |
|
||||||
var summonsUseFullCustomMessage: Bool = false |
|
||||||
var matchFormatsDefaultDuration: [MatchFormat: Int]? = nil |
|
||||||
var bracketMatchFormatPreference: MatchFormat? |
|
||||||
var groupStageMatchFormatPreference: MatchFormat? |
|
||||||
var loserBracketMatchFormatPreference: MatchFormat? |
|
||||||
|
|
||||||
var deviceId: String? |
|
||||||
|
|
||||||
init(username: String, email: String, firstName: String, lastName: String, phone: String?, country: String?) { |
|
||||||
self.username = username |
|
||||||
self.firstName = firstName |
|
||||||
self.lastName = lastName |
|
||||||
self.email = email |
|
||||||
self.phone = phone |
|
||||||
self.country = country |
|
||||||
} |
|
||||||
|
|
||||||
public func uuid() throws -> UUID { |
|
||||||
if let uuid = UUID(uuidString: self.id) { |
|
||||||
return uuid |
|
||||||
} |
|
||||||
throw UUIDError.cantConvertString(string: self.id) |
|
||||||
} |
|
||||||
|
|
||||||
func currentPlayerData() -> ImportedPlayer? { |
|
||||||
guard let licenceId else { return nil } |
|
||||||
let federalContext = PersistenceController.shared.localContainer.viewContext |
|
||||||
let fetchRequest = ImportedPlayer.fetchRequest() |
|
||||||
let predicate = NSPredicate(format: "license == %@", licenceId) |
|
||||||
fetchRequest.predicate = predicate |
|
||||||
return try? federalContext.fetch(fetchRequest).first |
|
||||||
} |
|
||||||
|
|
||||||
func defaultSignature() -> String { |
|
||||||
return "Sportivement,\n\(firstName) \(lastName), votre JAP." |
|
||||||
} |
|
||||||
|
|
||||||
func hasTenupClubs() -> Bool { |
|
||||||
self.clubsObjects().filter({ $0.code != nil }).isEmpty == false |
|
||||||
} |
|
||||||
|
|
||||||
func hasFavoriteClubsAndCreatedClubs() -> Bool { |
|
||||||
clubsObjects(includeCreated: true).isEmpty == false |
|
||||||
} |
|
||||||
|
|
||||||
func setUserClub(_ userClub: Club) { |
|
||||||
self.clubs.insert(userClub.id, at: 0) |
|
||||||
} |
|
||||||
|
|
||||||
func clubsObjects(includeCreated: Bool = false) -> [Club] { |
|
||||||
return DataStore.shared.clubs.filter({ (includeCreated && $0.creator == id) || clubs.contains($0.id) }) |
|
||||||
} |
|
||||||
|
|
||||||
func createdClubsObjectsNotFavorite() -> [Club] { |
|
||||||
return DataStore.shared.clubs.filter({ ($0.creator == id) && clubs.contains($0.id) == false }) |
|
||||||
} |
|
||||||
|
|
||||||
func saveMatchFormatsDefaultDuration(_ matchFormat: MatchFormat, estimatedDuration: Int) { |
|
||||||
if estimatedDuration == matchFormat.defaultEstimatedDuration { |
|
||||||
matchFormatsDefaultDuration?.removeValue(forKey: matchFormat) |
|
||||||
} else { |
|
||||||
matchFormatsDefaultDuration = matchFormatsDefaultDuration ?? [MatchFormat: Int]() |
|
||||||
matchFormatsDefaultDuration?[matchFormat] = estimatedDuration |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func addClub(_ club: Club) { |
|
||||||
if !self.clubs.contains(where: { $0.id == club.id }) { |
|
||||||
self.clubs.append(club.id) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey { |
|
||||||
case _id = "id" |
|
||||||
case _username = "username" |
|
||||||
case _email = "email" |
|
||||||
case _clubs = "clubs" |
|
||||||
case _umpireCode = "umpireCode" |
|
||||||
case _licenceId = "licenceId" |
|
||||||
case _firstName = "firstName" |
|
||||||
case _lastName = "lastName" |
|
||||||
case _phone = "phone" |
|
||||||
case _country = "country" |
|
||||||
case _summonsMessageBody = "summonsMessageBody" |
|
||||||
case _summonsMessageSignature = "summonsMessageSignature" |
|
||||||
case _summonsAvailablePaymentMethods = "summonsAvailablePaymentMethods" |
|
||||||
case _summonsDisplayFormat = "summonsDisplayFormat" |
|
||||||
case _summonsDisplayEntryFee = "summonsDisplayEntryFee" |
|
||||||
case _summonsUseFullCustomMessage = "summonsUseFullCustomMessage" |
|
||||||
case _matchFormatsDefaultDuration = "matchFormatsDefaultDuration" |
|
||||||
case _bracketMatchFormatPreference = "bracketMatchFormatPreference" |
|
||||||
case _groupStageMatchFormatPreference = "groupStageMatchFormatPreference" |
|
||||||
case _loserBracketMatchFormatPreference = "loserBracketMatchFormatPreference" |
|
||||||
case _deviceId = "deviceId" |
|
||||||
} |
|
||||||
|
|
||||||
func encode(to encoder: Encoder) throws { |
|
||||||
var container = encoder.container(keyedBy: CodingKeys.self) |
|
||||||
|
|
||||||
try container.encode(id, forKey: ._id) |
|
||||||
try container.encode(username, forKey: ._username) |
|
||||||
try container.encode(email, forKey: ._email) |
|
||||||
try container.encode(clubs, forKey: ._clubs) |
|
||||||
|
|
||||||
if let umpireCode = umpireCode { |
|
||||||
try container.encode(umpireCode, forKey: ._umpireCode) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._umpireCode) |
|
||||||
} |
|
||||||
|
|
||||||
if let licenceId = licenceId { |
|
||||||
try container.encode(licenceId, forKey: ._licenceId) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._licenceId) |
|
||||||
} |
|
||||||
|
|
||||||
try container.encode(firstName, forKey: ._firstName) |
|
||||||
try container.encode(lastName, forKey: ._lastName) |
|
||||||
|
|
||||||
if let phone = phone { |
|
||||||
try container.encode(phone, forKey: ._phone) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._phone) |
|
||||||
} |
|
||||||
|
|
||||||
if let country = country { |
|
||||||
try container.encode(country, forKey: ._country) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._country) |
|
||||||
} |
|
||||||
|
|
||||||
if let summonsMessageBody = summonsMessageBody { |
|
||||||
try container.encode(summonsMessageBody, forKey: ._summonsMessageBody) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._summonsMessageBody) |
|
||||||
} |
|
||||||
|
|
||||||
if let summonsMessageSignature = summonsMessageSignature { |
|
||||||
try container.encode(summonsMessageSignature, forKey: ._summonsMessageSignature) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._summonsMessageSignature) |
|
||||||
} |
|
||||||
|
|
||||||
if let summonsAvailablePaymentMethods = summonsAvailablePaymentMethods { |
|
||||||
try container.encode(summonsAvailablePaymentMethods, forKey: ._summonsAvailablePaymentMethods) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._summonsAvailablePaymentMethods) |
|
||||||
} |
|
||||||
|
|
||||||
try container.encode(summonsDisplayFormat, forKey: ._summonsDisplayFormat) |
|
||||||
try container.encode(summonsDisplayEntryFee, forKey: ._summonsDisplayEntryFee) |
|
||||||
try container.encode(summonsUseFullCustomMessage, forKey: ._summonsUseFullCustomMessage) |
|
||||||
|
|
||||||
if let matchFormatsDefaultDuration = matchFormatsDefaultDuration { |
|
||||||
try container.encode(matchFormatsDefaultDuration, forKey: ._matchFormatsDefaultDuration) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._matchFormatsDefaultDuration) |
|
||||||
} |
|
||||||
|
|
||||||
if let bracketMatchFormatPreference = bracketMatchFormatPreference { |
|
||||||
try container.encode(bracketMatchFormatPreference, forKey: ._bracketMatchFormatPreference) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._bracketMatchFormatPreference) |
|
||||||
} |
|
||||||
|
|
||||||
if let groupStageMatchFormatPreference = groupStageMatchFormatPreference { |
|
||||||
try container.encode(groupStageMatchFormatPreference, forKey: ._groupStageMatchFormatPreference) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._groupStageMatchFormatPreference) |
|
||||||
} |
|
||||||
|
|
||||||
if let loserBracketMatchFormatPreference = loserBracketMatchFormatPreference { |
|
||||||
try container.encode(loserBracketMatchFormatPreference, forKey: ._loserBracketMatchFormatPreference) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._loserBracketMatchFormatPreference) |
|
||||||
} |
|
||||||
|
|
||||||
if let deviceId { |
|
||||||
try container.encode(deviceId, forKey: ._deviceId) |
|
||||||
} else { |
|
||||||
try container.encodeNil(forKey: ._deviceId) |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
static func placeHolder() -> User { |
|
||||||
return User(username: "", email: "", firstName: "", lastName: "", phone: nil, country: nil) |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
class UserCreationForm: User, UserPasswordBase { |
|
||||||
|
|
||||||
init(user: User, username: String, password: String, firstName: String, lastName: String, email: String, phone: String?, country: String?) { |
|
||||||
self.password = password |
|
||||||
super.init(username: username, email: email, firstName: firstName, lastName: lastName, phone: phone, country: country) |
|
||||||
|
|
||||||
self.summonsMessageBody = user.summonsMessageBody |
|
||||||
self.summonsMessageSignature = user.summonsMessageSignature |
|
||||||
self.summonsAvailablePaymentMethods = user.summonsAvailablePaymentMethods |
|
||||||
self.summonsDisplayFormat = user.summonsDisplayFormat |
|
||||||
self.summonsDisplayEntryFee = user.summonsDisplayEntryFee |
|
||||||
self.summonsUseFullCustomMessage = user.summonsUseFullCustomMessage |
|
||||||
self.matchFormatsDefaultDuration = user.matchFormatsDefaultDuration |
|
||||||
self.bracketMatchFormatPreference = user.bracketMatchFormatPreference |
|
||||||
self.groupStageMatchFormatPreference = user.groupStageMatchFormatPreference |
|
||||||
self.loserBracketMatchFormatPreference = user.loserBracketMatchFormatPreference |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
required init(from decoder: Decoder) throws { |
|
||||||
fatalError("init(from:) has not been implemented") |
|
||||||
} |
|
||||||
|
|
||||||
public var password: String |
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey { |
|
||||||
case password |
|
||||||
} |
|
||||||
|
|
||||||
override func encode(to encoder: Encoder) throws { |
|
||||||
try super.encode(to: encoder) |
|
||||||
var container = encoder.container(keyedBy: CodingKeys.self) |
|
||||||
try container.encode(self.password, forKey: .password) |
|
||||||
} |
|
||||||
} |
|
||||||
@ -0,0 +1,695 @@ |
|||||||
|
// !$*UTF8*$! |
||||||
|
{ |
||||||
|
archiveVersion = 1; |
||||||
|
classes = { |
||||||
|
}; |
||||||
|
objectVersion = 56; |
||||||
|
objects = { |
||||||
|
|
||||||
|
/* Begin PBXBuildFile section */ |
||||||
|
C42330612C7E118B001DABF5 /* PadelClubData.docc in Sources */ = {isa = PBXBuildFile; fileRef = C42330602C7E118B001DABF5 /* PadelClubData.docc */; }; |
||||||
|
C42330672C7E118B001DABF5 /* PadelClubData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C423305C2C7E118B001DABF5 /* PadelClubData.framework */; }; |
||||||
|
C423306C2C7E118B001DABF5 /* PadelClubDataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C423306B2C7E118B001DABF5 /* PadelClubDataTests.swift */; }; |
||||||
|
C423306D2C7E118B001DABF5 /* PadelClubData.h in Headers */ = {isa = PBXBuildFile; fileRef = C423305F2C7E118B001DABF5 /* PadelClubData.h */; settings = {ATTRIBUTES = (Public, ); }; }; |
||||||
|
C42330782C7E126B001DABF5 /* LeStorage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C42330772C7E126B001DABF5 /* LeStorage.framework */; }; |
||||||
|
C42330792C7E126B001DABF5 /* LeStorage.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C42330772C7E126B001DABF5 /* LeStorage.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; |
||||||
|
C42330802C7E130A001DABF5 /* Club.swift in Sources */ = {isa = PBXBuildFile; fileRef = C423307F2C7E130A001DABF5 /* Club.swift */; }; |
||||||
|
C42330822C7E1330001DABF5 /* Court.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330812C7E1330001DABF5 /* Court.swift */; }; |
||||||
|
C42330942C7E1345001DABF5 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330932C7E1345001DABF5 /* User.swift */; }; |
||||||
|
C42330952C7E1345001DABF5 /* MockData.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330912C7E1345001DABF5 /* MockData.swift */; }; |
||||||
|
C42330962C7E1345001DABF5 /* Match.swift in Sources */ = {isa = PBXBuildFile; fileRef = C423308B2C7E1344001DABF5 /* Match.swift */; }; |
||||||
|
C42330982C7E1345001DABF5 /* Round.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330872C7E1344001DABF5 /* Round.swift */; }; |
||||||
|
C42330992C7E1345001DABF5 /* Tournament.swift in Sources */ = {isa = PBXBuildFile; fileRef = C423308C2C7E1344001DABF5 /* Tournament.swift */; }; |
||||||
|
C423309B2C7E1345001DABF5 /* GroupStage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C423308E2C7E1344001DABF5 /* GroupStage.swift */; }; |
||||||
|
C423309C2C7E1345001DABF5 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = C423308F2C7E1345001DABF5 /* Event.swift */; }; |
||||||
|
C423309D2C7E1345001DABF5 /* Purchase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330842C7E1344001DABF5 /* Purchase.swift */; }; |
||||||
|
C423309E2C7E1345001DABF5 /* TeamRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C423308A2C7E1344001DABF5 /* TeamRegistration.swift */; }; |
||||||
|
C423309F2C7E1345001DABF5 /* MonthData.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330852C7E1344001DABF5 /* MonthData.swift */; }; |
||||||
|
C42330A02C7E1345001DABF5 /* TeamScore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330832C7E1344001DABF5 /* TeamScore.swift */; }; |
||||||
|
C42330A22C7E1345001DABF5 /* MatchScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330902C7E1345001DABF5 /* MatchScheduler.swift */; }; |
||||||
|
C42330A32C7E1345001DABF5 /* DateInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330922C7E1345001DABF5 /* DateInterval.swift */; }; |
||||||
|
C42330A42C7E1345001DABF5 /* PlayerRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330882C7E1344001DABF5 /* PlayerRegistration.swift */; }; |
||||||
|
C42330A82C7E1433001DABF5 /* PadelRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330A72C7E1432001DABF5 /* PadelRule.swift */; }; |
||||||
|
C42330AB2C7E151A001DABF5 /* PlayerFilterOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330AA2C7E151A001DABF5 /* PlayerFilterOption.swift */; }; |
||||||
|
C42330B72C7E17ED001DABF5 /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330B62C7E17ED001DABF5 /* String+Extensions.swift */; }; |
||||||
|
C42330BB2C7E1D1D001DABF5 /* DisplayStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330BA2C7E1D1D001DABF5 /* DisplayStyle.swift */; }; |
||||||
|
C42330BF2C7E20AA001DABF5 /* Sequence+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330BE2C7E20AA001DABF5 /* Sequence+Extensions.swift */; }; |
||||||
|
C42330C12C7E2172001DABF5 /* SeedInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330C02C7E2172001DABF5 /* SeedInterval.swift */; }; |
||||||
|
C42330CA2C7E2589001DABF5 /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330C92C7E2589001DABF5 /* Date+Extensions.swift */; }; |
||||||
|
C42330CC2C7E2643001DABF5 /* Screen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330CB2C7E2643001DABF5 /* Screen.swift */; }; |
||||||
|
C42330CE2C7E2669001DABF5 /* MySortDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330CD2C7E2669001DABF5 /* MySortDescriptor.swift */; }; |
||||||
|
C42330DA2C7E2B3D001DABF5 /* TournamentStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330D92C7E2B3D001DABF5 /* TournamentStore.swift */; }; |
||||||
|
C42330DC2C7E2B86001DABF5 /* Algorithms in Frameworks */ = {isa = PBXBuildFile; productRef = C42330DB2C7E2B86001DABF5 /* Algorithms */; }; |
||||||
|
C42330DF2C7F1098001DABF5 /* DataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330DE2C7F1098001DABF5 /* DataStore.swift */; }; |
||||||
|
C42330E02C7F1098001DABF5 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330DD2C7F1098001DABF5 /* AppSettings.swift */; }; |
||||||
|
C42330E22C7F12FA001DABF5 /* PListReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330E12C7F12FA001DABF5 /* PListReader.swift */; }; |
||||||
|
C42330E92C7F179D001DABF5 /* Guard.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330E32C7F1347001DABF5 /* Guard.swift */; }; |
||||||
|
C42330EA2C7F179D001DABF5 /* StoreManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330E42C7F1347001DABF5 /* StoreManager.swift */; }; |
||||||
|
C42330EB2C7F179D001DABF5 /* StoreItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330E52C7F1347001DABF5 /* StoreItem.swift */; }; |
||||||
|
C42330ED2C7F17B9001DABF5 /* Patcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330EC2C7F17B8001DABF5 /* Patcher.swift */; }; |
||||||
|
C42330EF2C80610D001DABF5 /* String+Crypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330EE2C80610D001DABF5 /* String+Crypto.swift */; }; |
||||||
|
C42330F12C8061D9001DABF5 /* Key.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330F02C8061D9001DABF5 /* Key.swift */; }; |
||||||
|
C42330F32C8068D9001DABF5 /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42330F22C8068D9001DABF5 /* URL+Extensions.swift */; }; |
||||||
|
/* End PBXBuildFile section */ |
||||||
|
|
||||||
|
/* Begin PBXContainerItemProxy section */ |
||||||
|
C42330682C7E118B001DABF5 /* PBXContainerItemProxy */ = { |
||||||
|
isa = PBXContainerItemProxy; |
||||||
|
containerPortal = C42330532C7E118B001DABF5 /* Project object */; |
||||||
|
proxyType = 1; |
||||||
|
remoteGlobalIDString = C423305B2C7E118B001DABF5; |
||||||
|
remoteInfo = PadelClubData; |
||||||
|
}; |
||||||
|
/* End PBXContainerItemProxy section */ |
||||||
|
|
||||||
|
/* Begin PBXCopyFilesBuildPhase section */ |
||||||
|
C423307A2C7E126B001DABF5 /* Embed Frameworks */ = { |
||||||
|
isa = PBXCopyFilesBuildPhase; |
||||||
|
buildActionMask = 2147483647; |
||||||
|
dstPath = ""; |
||||||
|
dstSubfolderSpec = 10; |
||||||
|
files = ( |
||||||
|
C42330792C7E126B001DABF5 /* LeStorage.framework in Embed Frameworks */, |
||||||
|
); |
||||||
|
name = "Embed Frameworks"; |
||||||
|
runOnlyForDeploymentPostprocessing = 0; |
||||||
|
}; |
||||||
|
/* End PBXCopyFilesBuildPhase section */ |
||||||
|
|
||||||
|
/* Begin PBXFileReference section */ |
||||||
|
C423305C2C7E118B001DABF5 /* PadelClubData.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PadelClubData.framework; sourceTree = BUILT_PRODUCTS_DIR; }; |
||||||
|
C423305F2C7E118B001DABF5 /* PadelClubData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PadelClubData.h; sourceTree = "<group>"; }; |
||||||
|
C42330602C7E118B001DABF5 /* PadelClubData.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = PadelClubData.docc; sourceTree = "<group>"; }; |
||||||
|
C42330662C7E118B001DABF5 /* PadelClubDataTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PadelClubDataTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; |
||||||
|
C423306B2C7E118B001DABF5 /* PadelClubDataTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PadelClubDataTests.swift; sourceTree = "<group>"; }; |
||||||
|
C42330772C7E126B001DABF5 /* LeStorage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = LeStorage.framework; sourceTree = BUILT_PRODUCTS_DIR; }; |
||||||
|
C423307F2C7E130A001DABF5 /* Club.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Club.swift; sourceTree = "<group>"; }; |
||||||
|
C42330812C7E1330001DABF5 /* Court.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Court.swift; sourceTree = "<group>"; }; |
||||||
|
C42330832C7E1344001DABF5 /* TeamScore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TeamScore.swift; sourceTree = "<group>"; }; |
||||||
|
C42330842C7E1344001DABF5 /* Purchase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Purchase.swift; sourceTree = "<group>"; }; |
||||||
|
C42330852C7E1344001DABF5 /* MonthData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthData.swift; sourceTree = "<group>"; }; |
||||||
|
C42330872C7E1344001DABF5 /* Round.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Round.swift; sourceTree = "<group>"; }; |
||||||
|
C42330882C7E1344001DABF5 /* PlayerRegistration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerRegistration.swift; sourceTree = "<group>"; }; |
||||||
|
C423308A2C7E1344001DABF5 /* TeamRegistration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TeamRegistration.swift; sourceTree = "<group>"; }; |
||||||
|
C423308B2C7E1344001DABF5 /* Match.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Match.swift; sourceTree = "<group>"; }; |
||||||
|
C423308C2C7E1344001DABF5 /* Tournament.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tournament.swift; sourceTree = "<group>"; }; |
||||||
|
C423308E2C7E1344001DABF5 /* GroupStage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupStage.swift; sourceTree = "<group>"; }; |
||||||
|
C423308F2C7E1345001DABF5 /* Event.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Event.swift; sourceTree = "<group>"; }; |
||||||
|
C42330902C7E1345001DABF5 /* MatchScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchScheduler.swift; sourceTree = "<group>"; }; |
||||||
|
C42330912C7E1345001DABF5 /* MockData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockData.swift; sourceTree = "<group>"; }; |
||||||
|
C42330922C7E1345001DABF5 /* DateInterval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateInterval.swift; sourceTree = "<group>"; }; |
||||||
|
C42330932C7E1345001DABF5 /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = "<group>"; }; |
||||||
|
C42330A72C7E1432001DABF5 /* PadelRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PadelRule.swift; sourceTree = "<group>"; }; |
||||||
|
C42330AA2C7E151A001DABF5 /* PlayerFilterOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerFilterOption.swift; sourceTree = "<group>"; }; |
||||||
|
C42330B62C7E17ED001DABF5 /* String+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extensions.swift"; sourceTree = "<group>"; }; |
||||||
|
C42330BA2C7E1D1D001DABF5 /* DisplayStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayStyle.swift; sourceTree = "<group>"; }; |
||||||
|
C42330BE2C7E20AA001DABF5 /* Sequence+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Sequence+Extensions.swift"; sourceTree = "<group>"; }; |
||||||
|
C42330C02C7E2172001DABF5 /* SeedInterval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedInterval.swift; sourceTree = "<group>"; }; |
||||||
|
C42330C92C7E2589001DABF5 /* Date+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extensions.swift"; sourceTree = "<group>"; }; |
||||||
|
C42330CB2C7E2643001DABF5 /* Screen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Screen.swift; sourceTree = "<group>"; }; |
||||||
|
C42330CD2C7E2669001DABF5 /* MySortDescriptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MySortDescriptor.swift; sourceTree = "<group>"; }; |
||||||
|
C42330D92C7E2B3D001DABF5 /* TournamentStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentStore.swift; sourceTree = "<group>"; }; |
||||||
|
C42330DD2C7F1098001DABF5 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = "<group>"; }; |
||||||
|
C42330DE2C7F1098001DABF5 /* DataStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataStore.swift; sourceTree = "<group>"; }; |
||||||
|
C42330E12C7F12FA001DABF5 /* PListReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PListReader.swift; sourceTree = "<group>"; }; |
||||||
|
C42330E32C7F1347001DABF5 /* Guard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Guard.swift; sourceTree = "<group>"; }; |
||||||
|
C42330E42C7F1347001DABF5 /* StoreManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoreManager.swift; sourceTree = "<group>"; }; |
||||||
|
C42330E52C7F1347001DABF5 /* StoreItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoreItem.swift; sourceTree = "<group>"; }; |
||||||
|
C42330EC2C7F17B8001DABF5 /* Patcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Patcher.swift; sourceTree = "<group>"; }; |
||||||
|
C42330EE2C80610D001DABF5 /* String+Crypto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Crypto.swift"; sourceTree = "<group>"; }; |
||||||
|
C42330F02C8061D9001DABF5 /* Key.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Key.swift; sourceTree = "<group>"; }; |
||||||
|
C42330F22C8068D9001DABF5 /* URL+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Extensions.swift"; sourceTree = "<group>"; }; |
||||||
|
/* End PBXFileReference section */ |
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */ |
||||||
|
C42330592C7E118B001DABF5 /* Frameworks */ = { |
||||||
|
isa = PBXFrameworksBuildPhase; |
||||||
|
buildActionMask = 2147483647; |
||||||
|
files = ( |
||||||
|
C42330DC2C7E2B86001DABF5 /* Algorithms in Frameworks */, |
||||||
|
C42330782C7E126B001DABF5 /* LeStorage.framework in Frameworks */, |
||||||
|
); |
||||||
|
runOnlyForDeploymentPostprocessing = 0; |
||||||
|
}; |
||||||
|
C42330632C7E118B001DABF5 /* Frameworks */ = { |
||||||
|
isa = PBXFrameworksBuildPhase; |
||||||
|
buildActionMask = 2147483647; |
||||||
|
files = ( |
||||||
|
C42330672C7E118B001DABF5 /* PadelClubData.framework in Frameworks */, |
||||||
|
); |
||||||
|
runOnlyForDeploymentPostprocessing = 0; |
||||||
|
}; |
||||||
|
/* End PBXFrameworksBuildPhase section */ |
||||||
|
|
||||||
|
/* Begin PBXGroup section */ |
||||||
|
C42330522C7E118B001DABF5 = { |
||||||
|
isa = PBXGroup; |
||||||
|
children = ( |
||||||
|
C423305E2C7E118B001DABF5 /* PadelClubData */, |
||||||
|
C423306A2C7E118B001DABF5 /* PadelClubDataTests */, |
||||||
|
C423305D2C7E118B001DABF5 /* Products */, |
||||||
|
C42330762C7E126B001DABF5 /* Frameworks */, |
||||||
|
); |
||||||
|
sourceTree = "<group>"; |
||||||
|
}; |
||||||
|
C423305D2C7E118B001DABF5 /* Products */ = { |
||||||
|
isa = PBXGroup; |
||||||
|
children = ( |
||||||
|
C423305C2C7E118B001DABF5 /* PadelClubData.framework */, |
||||||
|
C42330662C7E118B001DABF5 /* PadelClubDataTests.xctest */, |
||||||
|
); |
||||||
|
name = Products; |
||||||
|
sourceTree = "<group>"; |
||||||
|
}; |
||||||
|
C423305E2C7E118B001DABF5 /* PadelClubData */ = { |
||||||
|
isa = PBXGroup; |
||||||
|
children = ( |
||||||
|
C42330CB2C7E2643001DABF5 /* Screen.swift */, |
||||||
|
C42330BA2C7E1D1D001DABF5 /* DisplayStyle.swift */, |
||||||
|
C42330A72C7E1432001DABF5 /* PadelRule.swift */, |
||||||
|
C42330AA2C7E151A001DABF5 /* PlayerFilterOption.swift */, |
||||||
|
C42330C02C7E2172001DABF5 /* SeedInterval.swift */, |
||||||
|
C42330DD2C7F1098001DABF5 /* AppSettings.swift */, |
||||||
|
C42330DE2C7F1098001DABF5 /* DataStore.swift */, |
||||||
|
C42330D92C7E2B3D001DABF5 /* TournamentStore.swift */, |
||||||
|
C42330A92C7E143C001DABF5 /* Model */, |
||||||
|
C42330E62C7F1347001DABF5 /* Subscription */, |
||||||
|
C423305F2C7E118B001DABF5 /* PadelClubData.h */, |
||||||
|
C42330602C7E118B001DABF5 /* PadelClubData.docc */, |
||||||
|
C42330B62C7E17ED001DABF5 /* String+Extensions.swift */, |
||||||
|
C42330C92C7E2589001DABF5 /* Date+Extensions.swift */, |
||||||
|
C42330BE2C7E20AA001DABF5 /* Sequence+Extensions.swift */, |
||||||
|
C42330EE2C80610D001DABF5 /* String+Crypto.swift */, |
||||||
|
C42330F22C8068D9001DABF5 /* URL+Extensions.swift */, |
||||||
|
C42330F02C8061D9001DABF5 /* Key.swift */, |
||||||
|
C42330CD2C7E2669001DABF5 /* MySortDescriptor.swift */, |
||||||
|
C42330EC2C7F17B8001DABF5 /* Patcher.swift */, |
||||||
|
C42330E12C7F12FA001DABF5 /* PListReader.swift */, |
||||||
|
); |
||||||
|
path = PadelClubData; |
||||||
|
sourceTree = "<group>"; |
||||||
|
}; |
||||||
|
C423306A2C7E118B001DABF5 /* PadelClubDataTests */ = { |
||||||
|
isa = PBXGroup; |
||||||
|
children = ( |
||||||
|
C423306B2C7E118B001DABF5 /* PadelClubDataTests.swift */, |
||||||
|
); |
||||||
|
path = PadelClubDataTests; |
||||||
|
sourceTree = "<group>"; |
||||||
|
}; |
||||||
|
C42330762C7E126B001DABF5 /* Frameworks */ = { |
||||||
|
isa = PBXGroup; |
||||||
|
children = ( |
||||||
|
C42330772C7E126B001DABF5 /* LeStorage.framework */, |
||||||
|
); |
||||||
|
name = Frameworks; |
||||||
|
sourceTree = "<group>"; |
||||||
|
}; |
||||||
|
C42330A92C7E143C001DABF5 /* Model */ = { |
||||||
|
isa = PBXGroup; |
||||||
|
children = ( |
||||||
|
C423307F2C7E130A001DABF5 /* Club.swift */, |
||||||
|
C42330812C7E1330001DABF5 /* Court.swift */, |
||||||
|
C42330922C7E1345001DABF5 /* DateInterval.swift */, |
||||||
|
C423308F2C7E1345001DABF5 /* Event.swift */, |
||||||
|
C423308E2C7E1344001DABF5 /* GroupStage.swift */, |
||||||
|
C423308B2C7E1344001DABF5 /* Match.swift */, |
||||||
|
C42330902C7E1345001DABF5 /* MatchScheduler.swift */, |
||||||
|
C42330912C7E1345001DABF5 /* MockData.swift */, |
||||||
|
C42330852C7E1344001DABF5 /* MonthData.swift */, |
||||||
|
C42330882C7E1344001DABF5 /* PlayerRegistration.swift */, |
||||||
|
C42330842C7E1344001DABF5 /* Purchase.swift */, |
||||||
|
C42330872C7E1344001DABF5 /* Round.swift */, |
||||||
|
C423308A2C7E1344001DABF5 /* TeamRegistration.swift */, |
||||||
|
C42330832C7E1344001DABF5 /* TeamScore.swift */, |
||||||
|
C423308C2C7E1344001DABF5 /* Tournament.swift */, |
||||||
|
C42330932C7E1345001DABF5 /* User.swift */, |
||||||
|
); |
||||||
|
path = Model; |
||||||
|
sourceTree = "<group>"; |
||||||
|
}; |
||||||
|
C42330E62C7F1347001DABF5 /* Subscription */ = { |
||||||
|
isa = PBXGroup; |
||||||
|
children = ( |
||||||
|
C42330E32C7F1347001DABF5 /* Guard.swift */, |
||||||
|
C42330E42C7F1347001DABF5 /* StoreManager.swift */, |
||||||
|
C42330E52C7F1347001DABF5 /* StoreItem.swift */, |
||||||
|
); |
||||||
|
path = Subscription; |
||||||
|
sourceTree = "<group>"; |
||||||
|
}; |
||||||
|
/* End PBXGroup section */ |
||||||
|
|
||||||
|
/* Begin PBXHeadersBuildPhase section */ |
||||||
|
C42330572C7E118B001DABF5 /* Headers */ = { |
||||||
|
isa = PBXHeadersBuildPhase; |
||||||
|
buildActionMask = 2147483647; |
||||||
|
files = ( |
||||||
|
C423306D2C7E118B001DABF5 /* PadelClubData.h in Headers */, |
||||||
|
); |
||||||
|
runOnlyForDeploymentPostprocessing = 0; |
||||||
|
}; |
||||||
|
/* End PBXHeadersBuildPhase section */ |
||||||
|
|
||||||
|
/* Begin PBXNativeTarget section */ |
||||||
|
C423305B2C7E118B001DABF5 /* PadelClubData */ = { |
||||||
|
isa = PBXNativeTarget; |
||||||
|
buildConfigurationList = C42330702C7E118B001DABF5 /* Build configuration list for PBXNativeTarget "PadelClubData" */; |
||||||
|
buildPhases = ( |
||||||
|
C42330572C7E118B001DABF5 /* Headers */, |
||||||
|
C42330582C7E118B001DABF5 /* Sources */, |
||||||
|
C42330592C7E118B001DABF5 /* Frameworks */, |
||||||
|
C423305A2C7E118B001DABF5 /* Resources */, |
||||||
|
C423307A2C7E126B001DABF5 /* Embed Frameworks */, |
||||||
|
); |
||||||
|
buildRules = ( |
||||||
|
); |
||||||
|
dependencies = ( |
||||||
|
); |
||||||
|
name = PadelClubData; |
||||||
|
packageProductDependencies = ( |
||||||
|
C42330DB2C7E2B86001DABF5 /* Algorithms */, |
||||||
|
); |
||||||
|
productName = PadelClubData; |
||||||
|
productReference = C423305C2C7E118B001DABF5 /* PadelClubData.framework */; |
||||||
|
productType = "com.apple.product-type.framework"; |
||||||
|
}; |
||||||
|
C42330652C7E118B001DABF5 /* PadelClubDataTests */ = { |
||||||
|
isa = PBXNativeTarget; |
||||||
|
buildConfigurationList = C42330732C7E118B001DABF5 /* Build configuration list for PBXNativeTarget "PadelClubDataTests" */; |
||||||
|
buildPhases = ( |
||||||
|
C42330622C7E118B001DABF5 /* Sources */, |
||||||
|
C42330632C7E118B001DABF5 /* Frameworks */, |
||||||
|
C42330642C7E118B001DABF5 /* Resources */, |
||||||
|
); |
||||||
|
buildRules = ( |
||||||
|
); |
||||||
|
dependencies = ( |
||||||
|
C42330692C7E118B001DABF5 /* PBXTargetDependency */, |
||||||
|
); |
||||||
|
name = PadelClubDataTests; |
||||||
|
productName = PadelClubDataTests; |
||||||
|
productReference = C42330662C7E118B001DABF5 /* PadelClubDataTests.xctest */; |
||||||
|
productType = "com.apple.product-type.bundle.unit-test"; |
||||||
|
}; |
||||||
|
/* End PBXNativeTarget section */ |
||||||
|
|
||||||
|
/* Begin PBXProject section */ |
||||||
|
C42330532C7E118B001DABF5 /* Project object */ = { |
||||||
|
isa = PBXProject; |
||||||
|
attributes = { |
||||||
|
BuildIndependentTargetsInParallel = 1; |
||||||
|
LastSwiftUpdateCheck = 1540; |
||||||
|
LastUpgradeCheck = 1540; |
||||||
|
TargetAttributes = { |
||||||
|
C423305B2C7E118B001DABF5 = { |
||||||
|
CreatedOnToolsVersion = 15.4; |
||||||
|
}; |
||||||
|
C42330652C7E118B001DABF5 = { |
||||||
|
CreatedOnToolsVersion = 15.4; |
||||||
|
}; |
||||||
|
}; |
||||||
|
}; |
||||||
|
buildConfigurationList = C42330562C7E118B001DABF5 /* Build configuration list for PBXProject "PadelClubData" */; |
||||||
|
compatibilityVersion = "Xcode 14.0"; |
||||||
|
developmentRegion = en; |
||||||
|
hasScannedForEncodings = 0; |
||||||
|
knownRegions = ( |
||||||
|
en, |
||||||
|
Base, |
||||||
|
); |
||||||
|
mainGroup = C42330522C7E118B001DABF5; |
||||||
|
packageReferences = ( |
||||||
|
C42330C42C7E23F8001DABF5 /* XCRemoteSwiftPackageReference "swift-algorithms" */, |
||||||
|
); |
||||||
|
productRefGroup = C423305D2C7E118B001DABF5 /* Products */; |
||||||
|
projectDirPath = ""; |
||||||
|
projectRoot = ""; |
||||||
|
targets = ( |
||||||
|
C423305B2C7E118B001DABF5 /* PadelClubData */, |
||||||
|
C42330652C7E118B001DABF5 /* PadelClubDataTests */, |
||||||
|
); |
||||||
|
}; |
||||||
|
/* End PBXProject section */ |
||||||
|
|
||||||
|
/* Begin PBXResourcesBuildPhase section */ |
||||||
|
C423305A2C7E118B001DABF5 /* Resources */ = { |
||||||
|
isa = PBXResourcesBuildPhase; |
||||||
|
buildActionMask = 2147483647; |
||||||
|
files = ( |
||||||
|
); |
||||||
|
runOnlyForDeploymentPostprocessing = 0; |
||||||
|
}; |
||||||
|
C42330642C7E118B001DABF5 /* Resources */ = { |
||||||
|
isa = PBXResourcesBuildPhase; |
||||||
|
buildActionMask = 2147483647; |
||||||
|
files = ( |
||||||
|
); |
||||||
|
runOnlyForDeploymentPostprocessing = 0; |
||||||
|
}; |
||||||
|
/* End PBXResourcesBuildPhase section */ |
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */ |
||||||
|
C42330582C7E118B001DABF5 /* Sources */ = { |
||||||
|
isa = PBXSourcesBuildPhase; |
||||||
|
buildActionMask = 2147483647; |
||||||
|
files = ( |
||||||
|
C42330F32C8068D9001DABF5 /* URL+Extensions.swift in Sources */, |
||||||
|
C42330E92C7F179D001DABF5 /* Guard.swift in Sources */, |
||||||
|
C42330EA2C7F179D001DABF5 /* StoreManager.swift in Sources */, |
||||||
|
C42330EB2C7F179D001DABF5 /* StoreItem.swift in Sources */, |
||||||
|
C42330BB2C7E1D1D001DABF5 /* DisplayStyle.swift in Sources */, |
||||||
|
C42330942C7E1345001DABF5 /* User.swift in Sources */, |
||||||
|
C42330A22C7E1345001DABF5 /* MatchScheduler.swift in Sources */, |
||||||
|
C423309E2C7E1345001DABF5 /* TeamRegistration.swift in Sources */, |
||||||
|
C42330612C7E118B001DABF5 /* PadelClubData.docc in Sources */, |
||||||
|
C423309C2C7E1345001DABF5 /* Event.swift in Sources */, |
||||||
|
C42330CE2C7E2669001DABF5 /* MySortDescriptor.swift in Sources */, |
||||||
|
C42330E22C7F12FA001DABF5 /* PListReader.swift in Sources */, |
||||||
|
C42330CC2C7E2643001DABF5 /* Screen.swift in Sources */, |
||||||
|
C42330A42C7E1345001DABF5 /* PlayerRegistration.swift in Sources */, |
||||||
|
C42330992C7E1345001DABF5 /* Tournament.swift in Sources */, |
||||||
|
C42330E02C7F1098001DABF5 /* AppSettings.swift in Sources */, |
||||||
|
C423309B2C7E1345001DABF5 /* GroupStage.swift in Sources */, |
||||||
|
C42330802C7E130A001DABF5 /* Club.swift in Sources */, |
||||||
|
C42330CA2C7E2589001DABF5 /* Date+Extensions.swift in Sources */, |
||||||
|
C423309F2C7E1345001DABF5 /* MonthData.swift in Sources */, |
||||||
|
C42330B72C7E17ED001DABF5 /* String+Extensions.swift in Sources */, |
||||||
|
C42330C12C7E2172001DABF5 /* SeedInterval.swift in Sources */, |
||||||
|
C42330822C7E1330001DABF5 /* Court.swift in Sources */, |
||||||
|
C42330952C7E1345001DABF5 /* MockData.swift in Sources */, |
||||||
|
C42330982C7E1345001DABF5 /* Round.swift in Sources */, |
||||||
|
C42330A02C7E1345001DABF5 /* TeamScore.swift in Sources */, |
||||||
|
C42330AB2C7E151A001DABF5 /* PlayerFilterOption.swift in Sources */, |
||||||
|
C42330DF2C7F1098001DABF5 /* DataStore.swift in Sources */, |
||||||
|
C42330A82C7E1433001DABF5 /* PadelRule.swift in Sources */, |
||||||
|
C423309D2C7E1345001DABF5 /* Purchase.swift in Sources */, |
||||||
|
C42330F12C8061D9001DABF5 /* Key.swift in Sources */, |
||||||
|
C42330BF2C7E20AA001DABF5 /* Sequence+Extensions.swift in Sources */, |
||||||
|
C42330EF2C80610D001DABF5 /* String+Crypto.swift in Sources */, |
||||||
|
C42330962C7E1345001DABF5 /* Match.swift in Sources */, |
||||||
|
C42330ED2C7F17B9001DABF5 /* Patcher.swift in Sources */, |
||||||
|
C42330DA2C7E2B3D001DABF5 /* TournamentStore.swift in Sources */, |
||||||
|
C42330A32C7E1345001DABF5 /* DateInterval.swift in Sources */, |
||||||
|
); |
||||||
|
runOnlyForDeploymentPostprocessing = 0; |
||||||
|
}; |
||||||
|
C42330622C7E118B001DABF5 /* Sources */ = { |
||||||
|
isa = PBXSourcesBuildPhase; |
||||||
|
buildActionMask = 2147483647; |
||||||
|
files = ( |
||||||
|
C423306C2C7E118B001DABF5 /* PadelClubDataTests.swift in Sources */, |
||||||
|
); |
||||||
|
runOnlyForDeploymentPostprocessing = 0; |
||||||
|
}; |
||||||
|
/* End PBXSourcesBuildPhase section */ |
||||||
|
|
||||||
|
/* Begin PBXTargetDependency section */ |
||||||
|
C42330692C7E118B001DABF5 /* PBXTargetDependency */ = { |
||||||
|
isa = PBXTargetDependency; |
||||||
|
target = C423305B2C7E118B001DABF5 /* PadelClubData */; |
||||||
|
targetProxy = C42330682C7E118B001DABF5 /* PBXContainerItemProxy */; |
||||||
|
}; |
||||||
|
/* End PBXTargetDependency section */ |
||||||
|
|
||||||
|
/* Begin XCBuildConfiguration section */ |
||||||
|
C423306E2C7E118B001DABF5 /* Debug */ = { |
||||||
|
isa = XCBuildConfiguration; |
||||||
|
buildSettings = { |
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO; |
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; |
||||||
|
CLANG_ANALYZER_NONNULL = YES; |
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; |
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; |
||||||
|
CLANG_ENABLE_MODULES = YES; |
||||||
|
CLANG_ENABLE_OBJC_ARC = YES; |
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES; |
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; |
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES; |
||||||
|
CLANG_WARN_COMMA = YES; |
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES; |
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; |
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; |
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES; |
||||||
|
CLANG_WARN_EMPTY_BODY = YES; |
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES; |
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES; |
||||||
|
CLANG_WARN_INT_CONVERSION = YES; |
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; |
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; |
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; |
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; |
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; |
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; |
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES; |
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES; |
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; |
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES; |
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; |
||||||
|
COPY_PHASE_STRIP = NO; |
||||||
|
CURRENT_PROJECT_VERSION = 1; |
||||||
|
DEBUG_INFORMATION_FORMAT = dwarf; |
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES; |
||||||
|
ENABLE_TESTABILITY = YES; |
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES; |
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu17; |
||||||
|
GCC_DYNAMIC_NO_PIC = NO; |
||||||
|
GCC_NO_COMMON_BLOCKS = YES; |
||||||
|
GCC_OPTIMIZATION_LEVEL = 0; |
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = ( |
||||||
|
"DEBUG=1", |
||||||
|
"$(inherited)", |
||||||
|
); |
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; |
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; |
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES; |
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; |
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES; |
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES; |
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 17.5; |
||||||
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES; |
||||||
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; |
||||||
|
MTL_FAST_MATH = YES; |
||||||
|
ONLY_ACTIVE_ARCH = YES; |
||||||
|
SDKROOT = iphoneos; |
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; |
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; |
||||||
|
VERSIONING_SYSTEM = "apple-generic"; |
||||||
|
VERSION_INFO_PREFIX = ""; |
||||||
|
}; |
||||||
|
name = Debug; |
||||||
|
}; |
||||||
|
C423306F2C7E118B001DABF5 /* Release */ = { |
||||||
|
isa = XCBuildConfiguration; |
||||||
|
buildSettings = { |
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO; |
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; |
||||||
|
CLANG_ANALYZER_NONNULL = YES; |
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; |
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; |
||||||
|
CLANG_ENABLE_MODULES = YES; |
||||||
|
CLANG_ENABLE_OBJC_ARC = YES; |
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES; |
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; |
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES; |
||||||
|
CLANG_WARN_COMMA = YES; |
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES; |
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; |
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; |
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES; |
||||||
|
CLANG_WARN_EMPTY_BODY = YES; |
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES; |
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES; |
||||||
|
CLANG_WARN_INT_CONVERSION = YES; |
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; |
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; |
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; |
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; |
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; |
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; |
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES; |
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES; |
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; |
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES; |
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; |
||||||
|
COPY_PHASE_STRIP = NO; |
||||||
|
CURRENT_PROJECT_VERSION = 1; |
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; |
||||||
|
ENABLE_NS_ASSERTIONS = NO; |
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES; |
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES; |
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu17; |
||||||
|
GCC_NO_COMMON_BLOCKS = YES; |
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; |
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; |
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES; |
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; |
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES; |
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES; |
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 17.5; |
||||||
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES; |
||||||
|
MTL_ENABLE_DEBUG_INFO = NO; |
||||||
|
MTL_FAST_MATH = YES; |
||||||
|
SDKROOT = iphoneos; |
||||||
|
SWIFT_COMPILATION_MODE = wholemodule; |
||||||
|
VALIDATE_PRODUCT = YES; |
||||||
|
VERSIONING_SYSTEM = "apple-generic"; |
||||||
|
VERSION_INFO_PREFIX = ""; |
||||||
|
}; |
||||||
|
name = Release; |
||||||
|
}; |
||||||
|
C42330712C7E118B001DABF5 /* Debug */ = { |
||||||
|
isa = XCBuildConfiguration; |
||||||
|
buildSettings = { |
||||||
|
BUILD_LIBRARY_FOR_DISTRIBUTION = NO; |
||||||
|
CODE_SIGN_STYLE = Automatic; |
||||||
|
CURRENT_PROJECT_VERSION = 1; |
||||||
|
DEFINES_MODULE = YES; |
||||||
|
DEVELOPMENT_TEAM = 526E96RFNP; |
||||||
|
DYLIB_COMPATIBILITY_VERSION = 1; |
||||||
|
DYLIB_CURRENT_VERSION = 1; |
||||||
|
DYLIB_INSTALL_NAME_BASE = "@rpath"; |
||||||
|
ENABLE_MODULE_VERIFIER = YES; |
||||||
|
GENERATE_INFOPLIST_FILE = YES; |
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = ""; |
||||||
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; |
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 17.2; |
||||||
|
LD_RUNPATH_SEARCH_PATHS = ( |
||||||
|
"$(inherited)", |
||||||
|
"@executable_path/Frameworks", |
||||||
|
"@loader_path/Frameworks", |
||||||
|
); |
||||||
|
MARKETING_VERSION = 1.0; |
||||||
|
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; |
||||||
|
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; |
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.staxriver.PadelClubData; |
||||||
|
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; |
||||||
|
SKIP_INSTALL = YES; |
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES; |
||||||
|
SWIFT_INSTALL_OBJC_HEADER = NO; |
||||||
|
SWIFT_VERSION = 5.0; |
||||||
|
TARGETED_DEVICE_FAMILY = "1,2"; |
||||||
|
}; |
||||||
|
name = Debug; |
||||||
|
}; |
||||||
|
C42330722C7E118B001DABF5 /* Release */ = { |
||||||
|
isa = XCBuildConfiguration; |
||||||
|
buildSettings = { |
||||||
|
BUILD_LIBRARY_FOR_DISTRIBUTION = NO; |
||||||
|
CODE_SIGN_STYLE = Automatic; |
||||||
|
CURRENT_PROJECT_VERSION = 1; |
||||||
|
DEFINES_MODULE = YES; |
||||||
|
DEVELOPMENT_TEAM = 526E96RFNP; |
||||||
|
DYLIB_COMPATIBILITY_VERSION = 1; |
||||||
|
DYLIB_CURRENT_VERSION = 1; |
||||||
|
DYLIB_INSTALL_NAME_BASE = "@rpath"; |
||||||
|
ENABLE_MODULE_VERIFIER = YES; |
||||||
|
GENERATE_INFOPLIST_FILE = YES; |
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = ""; |
||||||
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; |
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 17.2; |
||||||
|
LD_RUNPATH_SEARCH_PATHS = ( |
||||||
|
"$(inherited)", |
||||||
|
"@executable_path/Frameworks", |
||||||
|
"@loader_path/Frameworks", |
||||||
|
); |
||||||
|
MARKETING_VERSION = 1.0; |
||||||
|
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; |
||||||
|
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; |
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.staxriver.PadelClubData; |
||||||
|
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; |
||||||
|
SKIP_INSTALL = YES; |
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES; |
||||||
|
SWIFT_INSTALL_OBJC_HEADER = NO; |
||||||
|
SWIFT_VERSION = 5.0; |
||||||
|
TARGETED_DEVICE_FAMILY = "1,2"; |
||||||
|
}; |
||||||
|
name = Release; |
||||||
|
}; |
||||||
|
C42330742C7E118B001DABF5 /* Debug */ = { |
||||||
|
isa = XCBuildConfiguration; |
||||||
|
buildSettings = { |
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; |
||||||
|
CODE_SIGN_STYLE = Automatic; |
||||||
|
CURRENT_PROJECT_VERSION = 1; |
||||||
|
DEVELOPMENT_TEAM = 526E96RFNP; |
||||||
|
GENERATE_INFOPLIST_FILE = YES; |
||||||
|
MARKETING_VERSION = 1.0; |
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.staxriver.PadelClubDataTests; |
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)"; |
||||||
|
SWIFT_EMIT_LOC_STRINGS = NO; |
||||||
|
SWIFT_VERSION = 5.0; |
||||||
|
TARGETED_DEVICE_FAMILY = "1,2"; |
||||||
|
}; |
||||||
|
name = Debug; |
||||||
|
}; |
||||||
|
C42330752C7E118B001DABF5 /* Release */ = { |
||||||
|
isa = XCBuildConfiguration; |
||||||
|
buildSettings = { |
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; |
||||||
|
CODE_SIGN_STYLE = Automatic; |
||||||
|
CURRENT_PROJECT_VERSION = 1; |
||||||
|
DEVELOPMENT_TEAM = 526E96RFNP; |
||||||
|
GENERATE_INFOPLIST_FILE = YES; |
||||||
|
MARKETING_VERSION = 1.0; |
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.staxriver.PadelClubDataTests; |
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)"; |
||||||
|
SWIFT_EMIT_LOC_STRINGS = NO; |
||||||
|
SWIFT_VERSION = 5.0; |
||||||
|
TARGETED_DEVICE_FAMILY = "1,2"; |
||||||
|
}; |
||||||
|
name = Release; |
||||||
|
}; |
||||||
|
/* End XCBuildConfiguration section */ |
||||||
|
|
||||||
|
/* Begin XCConfigurationList section */ |
||||||
|
C42330562C7E118B001DABF5 /* Build configuration list for PBXProject "PadelClubData" */ = { |
||||||
|
isa = XCConfigurationList; |
||||||
|
buildConfigurations = ( |
||||||
|
C423306E2C7E118B001DABF5 /* Debug */, |
||||||
|
C423306F2C7E118B001DABF5 /* Release */, |
||||||
|
); |
||||||
|
defaultConfigurationIsVisible = 0; |
||||||
|
defaultConfigurationName = Release; |
||||||
|
}; |
||||||
|
C42330702C7E118B001DABF5 /* Build configuration list for PBXNativeTarget "PadelClubData" */ = { |
||||||
|
isa = XCConfigurationList; |
||||||
|
buildConfigurations = ( |
||||||
|
C42330712C7E118B001DABF5 /* Debug */, |
||||||
|
C42330722C7E118B001DABF5 /* Release */, |
||||||
|
); |
||||||
|
defaultConfigurationIsVisible = 0; |
||||||
|
defaultConfigurationName = Release; |
||||||
|
}; |
||||||
|
C42330732C7E118B001DABF5 /* Build configuration list for PBXNativeTarget "PadelClubDataTests" */ = { |
||||||
|
isa = XCConfigurationList; |
||||||
|
buildConfigurations = ( |
||||||
|
C42330742C7E118B001DABF5 /* Debug */, |
||||||
|
C42330752C7E118B001DABF5 /* Release */, |
||||||
|
); |
||||||
|
defaultConfigurationIsVisible = 0; |
||||||
|
defaultConfigurationName = Release; |
||||||
|
}; |
||||||
|
/* End XCConfigurationList section */ |
||||||
|
|
||||||
|
/* Begin XCRemoteSwiftPackageReference section */ |
||||||
|
C42330C42C7E23F8001DABF5 /* XCRemoteSwiftPackageReference "swift-algorithms" */ = { |
||||||
|
isa = XCRemoteSwiftPackageReference; |
||||||
|
repositoryURL = "https://github.com/apple/swift-algorithms.git"; |
||||||
|
requirement = { |
||||||
|
kind = upToNextMajorVersion; |
||||||
|
minimumVersion = 1.2.0; |
||||||
|
}; |
||||||
|
}; |
||||||
|
/* End XCRemoteSwiftPackageReference section */ |
||||||
|
|
||||||
|
/* Begin XCSwiftPackageProductDependency section */ |
||||||
|
C42330DB2C7E2B86001DABF5 /* Algorithms */ = { |
||||||
|
isa = XCSwiftPackageProductDependency; |
||||||
|
package = C42330C42C7E23F8001DABF5 /* XCRemoteSwiftPackageReference "swift-algorithms" */; |
||||||
|
productName = Algorithms; |
||||||
|
}; |
||||||
|
/* End XCSwiftPackageProductDependency section */ |
||||||
|
}; |
||||||
|
rootObject = C42330532C7E118B001DABF5 /* Project object */; |
||||||
|
} |
||||||
@ -0,0 +1,7 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<Workspace |
||||||
|
version = "1.0"> |
||||||
|
<FileRef |
||||||
|
location = "self:"> |
||||||
|
</FileRef> |
||||||
|
</Workspace> |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
||||||
|
<plist version="1.0"> |
||||||
|
<dict> |
||||||
|
<key>IDEDidComputeMac32BitWarning</key> |
||||||
|
<true/> |
||||||
|
</dict> |
||||||
|
</plist> |
||||||
Binary file not shown.
@ -0,0 +1,80 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<Scheme |
||||||
|
LastUpgradeVersion = "1540" |
||||||
|
version = "1.7"> |
||||||
|
<BuildAction |
||||||
|
parallelizeBuildables = "YES" |
||||||
|
buildImplicitDependencies = "YES" |
||||||
|
buildArchitectures = "Automatic"> |
||||||
|
<BuildActionEntries> |
||||||
|
<BuildActionEntry |
||||||
|
buildForTesting = "YES" |
||||||
|
buildForRunning = "YES" |
||||||
|
buildForProfiling = "YES" |
||||||
|
buildForArchiving = "YES" |
||||||
|
buildForAnalyzing = "YES"> |
||||||
|
<BuildableReference |
||||||
|
BuildableIdentifier = "primary" |
||||||
|
BlueprintIdentifier = "C423305B2C7E118B001DABF5" |
||||||
|
BuildableName = "PadelClubData.framework" |
||||||
|
BlueprintName = "PadelClubData" |
||||||
|
ReferencedContainer = "container:PadelClubData.xcodeproj"> |
||||||
|
</BuildableReference> |
||||||
|
</BuildActionEntry> |
||||||
|
</BuildActionEntries> |
||||||
|
</BuildAction> |
||||||
|
<TestAction |
||||||
|
buildConfiguration = "Debug" |
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" |
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" |
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES" |
||||||
|
shouldAutocreateTestPlan = "YES"> |
||||||
|
<Testables> |
||||||
|
<TestableReference |
||||||
|
skipped = "NO" |
||||||
|
parallelizable = "YES"> |
||||||
|
<BuildableReference |
||||||
|
BuildableIdentifier = "primary" |
||||||
|
BlueprintIdentifier = "C42330652C7E118B001DABF5" |
||||||
|
BuildableName = "PadelClubDataTests.xctest" |
||||||
|
BlueprintName = "PadelClubDataTests" |
||||||
|
ReferencedContainer = "container:PadelClubData.xcodeproj"> |
||||||
|
</BuildableReference> |
||||||
|
</TestableReference> |
||||||
|
</Testables> |
||||||
|
</TestAction> |
||||||
|
<LaunchAction |
||||||
|
buildConfiguration = "Debug" |
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" |
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" |
||||||
|
launchStyle = "0" |
||||||
|
useCustomWorkingDirectory = "NO" |
||||||
|
ignoresPersistentStateOnLaunch = "NO" |
||||||
|
debugDocumentVersioning = "YES" |
||||||
|
debugServiceExtension = "internal" |
||||||
|
allowLocationSimulation = "YES"> |
||||||
|
</LaunchAction> |
||||||
|
<ProfileAction |
||||||
|
buildConfiguration = "Release" |
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES" |
||||||
|
savedToolIdentifier = "" |
||||||
|
useCustomWorkingDirectory = "NO" |
||||||
|
debugDocumentVersioning = "YES"> |
||||||
|
<MacroExpansion> |
||||||
|
<BuildableReference |
||||||
|
BuildableIdentifier = "primary" |
||||||
|
BlueprintIdentifier = "C423305B2C7E118B001DABF5" |
||||||
|
BuildableName = "PadelClubData.framework" |
||||||
|
BlueprintName = "PadelClubData" |
||||||
|
ReferencedContainer = "container:PadelClubData.xcodeproj"> |
||||||
|
</BuildableReference> |
||||||
|
</MacroExpansion> |
||||||
|
</ProfileAction> |
||||||
|
<AnalyzeAction |
||||||
|
buildConfiguration = "Debug"> |
||||||
|
</AnalyzeAction> |
||||||
|
<ArchiveAction |
||||||
|
buildConfiguration = "Release" |
||||||
|
revealArchiveInOrganizer = "YES"> |
||||||
|
</ArchiveAction> |
||||||
|
</Scheme> |
||||||
@ -0,0 +1,27 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
||||||
|
<plist version="1.0"> |
||||||
|
<dict> |
||||||
|
<key>SchemeUserState</key> |
||||||
|
<dict> |
||||||
|
<key>PadelClubData.xcscheme_^#shared#^_</key> |
||||||
|
<dict> |
||||||
|
<key>orderHint</key> |
||||||
|
<integer>3</integer> |
||||||
|
</dict> |
||||||
|
</dict> |
||||||
|
<key>SuppressBuildableAutocreation</key> |
||||||
|
<dict> |
||||||
|
<key>C423305B2C7E118B001DABF5</key> |
||||||
|
<dict> |
||||||
|
<key>primary</key> |
||||||
|
<true/> |
||||||
|
</dict> |
||||||
|
<key>C42330652C7E118B001DABF5</key> |
||||||
|
<dict> |
||||||
|
<key>primary</key> |
||||||
|
<true/> |
||||||
|
</dict> |
||||||
|
</dict> |
||||||
|
</dict> |
||||||
|
</plist> |
||||||
@ -0,0 +1,65 @@ |
|||||||
|
// |
||||||
|
// Date+Extensions.swift |
||||||
|
// PadelClubData |
||||||
|
// |
||||||
|
// Created by Laurent Morvillier on 27/08/2024. |
||||||
|
// |
||||||
|
|
||||||
|
import Foundation |
||||||
|
|
||||||
|
extension Date { |
||||||
|
|
||||||
|
var dayInt: Int { |
||||||
|
Calendar.current.component(.day, from: self) |
||||||
|
} |
||||||
|
|
||||||
|
func isEarlierThan(_ date: Date) -> Bool { |
||||||
|
Calendar.current.compare(self, to: date, toGranularity: .minute) == .orderedAscending |
||||||
|
} |
||||||
|
|
||||||
|
func atNine() -> Date { |
||||||
|
let calendar = Calendar.current |
||||||
|
return calendar.date(bySettingHour: 9, minute: 0, second: 0, of: self)! |
||||||
|
} |
||||||
|
|
||||||
|
func atEightAM() -> Date { |
||||||
|
let calendar = Calendar.current |
||||||
|
return calendar.date(bySettingHour: 8, minute: 0, second: 0, of: self)! |
||||||
|
} |
||||||
|
|
||||||
|
var tomorrowAtNine: Date { |
||||||
|
let currentHour = Calendar.current.component(.hour, from: self) |
||||||
|
let startOfDay = Calendar.current.startOfDay(for: self) |
||||||
|
if currentHour < 8 { |
||||||
|
return Calendar.current.date(byAdding: .hour, value: 9, to: startOfDay)! |
||||||
|
} else { |
||||||
|
let date = Calendar.current.date(byAdding: .day, value: 1, to: startOfDay) |
||||||
|
return Calendar.current.date(byAdding: .hour, value: 9, to: date!)! |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func isInCurrentYear() -> Bool { |
||||||
|
let calendar = Calendar.current |
||||||
|
let currentYear = calendar.component(.year, from: Date()) |
||||||
|
let yearOfDate = calendar.component(.year, from: self) |
||||||
|
|
||||||
|
return currentYear == yearOfDate |
||||||
|
} |
||||||
|
|
||||||
|
func get(_ components: Calendar.Component..., calendar: Calendar = Calendar.current) -> DateComponents { |
||||||
|
return calendar.dateComponents(Set(components), from: self) |
||||||
|
} |
||||||
|
|
||||||
|
func get(_ component: Calendar.Component, calendar: Calendar = Calendar.current) -> Int { |
||||||
|
return calendar.component(component, from: self) |
||||||
|
} |
||||||
|
|
||||||
|
func atBeginningOfDay(hourInt: Int = 9) -> Date { |
||||||
|
Calendar.current.date(byAdding: .hour, value: hourInt, to: self.startOfDay)! |
||||||
|
} |
||||||
|
|
||||||
|
var startOfDay: Date { |
||||||
|
Calendar.current.startOfDay(for: self) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,14 @@ |
|||||||
|
// |
||||||
|
// DisplayStyle.swift |
||||||
|
// PadelClubData |
||||||
|
// |
||||||
|
// Created by Laurent Morvillier on 27/08/2024. |
||||||
|
// |
||||||
|
|
||||||
|
import Foundation |
||||||
|
|
||||||
|
enum DisplayStyle { |
||||||
|
case title |
||||||
|
case wide |
||||||
|
case short |
||||||
|
} |
||||||
@ -0,0 +1,171 @@ |
|||||||
|
// |
||||||
|
// Club.swift |
||||||
|
// PadelClub |
||||||
|
// |
||||||
|
// Created by Laurent Morvillier on 02/02/2024. |
||||||
|
// |
||||||
|
|
||||||
|
import Foundation |
||||||
|
import SwiftUI |
||||||
|
import LeStorage |
||||||
|
|
||||||
|
final class Club : ModelObject, Storable, Hashable { |
||||||
|
|
||||||
|
static func resourceName() -> String { return "clubs" } |
||||||
|
static func tokenExemptedMethods() -> [HTTPMethod] { return [.get] } |
||||||
|
static func filterByStoreIdentifier() -> Bool { return false } |
||||||
|
static var relationshipNames: [String] = [] |
||||||
|
|
||||||
|
static func == (lhs: Club, rhs: Club) -> Bool { |
||||||
|
lhs.id == rhs.id |
||||||
|
} |
||||||
|
|
||||||
|
func hash(into hasher: inout Hasher) { |
||||||
|
return hasher.combine(id) |
||||||
|
} |
||||||
|
|
||||||
|
var id: String = Store.randomId() |
||||||
|
var creator: String? |
||||||
|
var name: String |
||||||
|
var acronym: String |
||||||
|
var phone: String? |
||||||
|
var code: String? |
||||||
|
//var federalClubData: Data? |
||||||
|
var address: String? |
||||||
|
var city: String? |
||||||
|
var zipCode: String? |
||||||
|
var latitude: Double? |
||||||
|
var longitude: Double? |
||||||
|
var courtCount: Int = 2 |
||||||
|
var broadcastCode: String? |
||||||
|
// var alphabeticalName: Bool = false |
||||||
|
|
||||||
|
internal init(creator: String? = nil, name: String, acronym: String? = nil, phone: String? = nil, code: String? = nil, address: String? = nil, city: String? = nil, zipCode: String? = nil, latitude: Double? = nil, longitude: Double? = nil, courtCount: Int = 2, broadcastCode: String? = nil) { |
||||||
|
self.name = name |
||||||
|
self.creator = creator |
||||||
|
self.acronym = acronym ?? name.acronym() |
||||||
|
self.phone = phone |
||||||
|
self.code = code |
||||||
|
self.address = address |
||||||
|
self.city = city |
||||||
|
self.zipCode = zipCode |
||||||
|
self.latitude = latitude |
||||||
|
self.longitude = longitude |
||||||
|
self.courtCount = courtCount |
||||||
|
self.broadcastCode = broadcastCode |
||||||
|
} |
||||||
|
|
||||||
|
override func copyFromServerInstance(_ instance: any Storable) -> Bool { |
||||||
|
guard let copy = instance as? Club else { return false } |
||||||
|
self.broadcastCode = copy.broadcastCode |
||||||
|
// Logger.log("write code: \(self.broadcastCode)") |
||||||
|
return true |
||||||
|
} |
||||||
|
|
||||||
|
func clubTitle(_ displayStyle: DisplayStyle = .wide) -> String { |
||||||
|
switch displayStyle { |
||||||
|
case .wide, .title: |
||||||
|
return name |
||||||
|
case .short: |
||||||
|
return acronym |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
var customizedCourts: [Court] { |
||||||
|
let courts: [Court] = DataStore.shared.courts.filter { $0.club == self.id } |
||||||
|
return courts.sorted(by: \Court.index) |
||||||
|
} |
||||||
|
|
||||||
|
override func deleteDependencies() throws { |
||||||
|
let customizedCourts = self.customizedCourts |
||||||
|
for customizedCourt in customizedCourts { |
||||||
|
try customizedCourt.deleteDependencies() |
||||||
|
} |
||||||
|
DataStore.shared.courts.deleteDependencies(customizedCourts) |
||||||
|
} |
||||||
|
|
||||||
|
// enum CodingKeys: String, CodingKey { |
||||||
|
// case _id = "id" |
||||||
|
// case _creator = "creator" |
||||||
|
// case _name = "name" |
||||||
|
// case _acronym = "acronym" |
||||||
|
// case _phone = "phone" |
||||||
|
// case _code = "code" |
||||||
|
// case _address = "address" |
||||||
|
// case _city = "city" |
||||||
|
// case _zipCode = "zipCode" |
||||||
|
// case _latitude = "latitude" |
||||||
|
// case _longitude = "longitude" |
||||||
|
// case _courtCount = "courtCount" |
||||||
|
// case _broadcastCode = "broadcastCode" |
||||||
|
//// case _alphabeticalName = "alphabeticalName" |
||||||
|
// } |
||||||
|
|
||||||
|
// func encode(to encoder: Encoder) throws { |
||||||
|
// var container = encoder.container(keyedBy: CodingKeys.self) |
||||||
|
// |
||||||
|
// try container.encode(id, forKey: ._id) |
||||||
|
// |
||||||
|
// if let creator = creator { |
||||||
|
// try container.encode(creator, forKey: ._creator) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._creator) |
||||||
|
// } |
||||||
|
// |
||||||
|
// try container.encode(name, forKey: ._name) |
||||||
|
// try container.encode(acronym, forKey: ._acronym) |
||||||
|
// |
||||||
|
// if let phone = phone { |
||||||
|
// try container.encode(phone, forKey: ._phone) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._phone) |
||||||
|
// } |
||||||
|
// |
||||||
|
// if let code = code { |
||||||
|
// try container.encode(code, forKey: ._code) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._code) |
||||||
|
// } |
||||||
|
// |
||||||
|
// if let address = address { |
||||||
|
// try container.encode(address, forKey: ._address) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._address) |
||||||
|
// } |
||||||
|
// |
||||||
|
// if let city = city { |
||||||
|
// try container.encode(city, forKey: ._city) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._city) |
||||||
|
// } |
||||||
|
// |
||||||
|
// if let zipCode = zipCode { |
||||||
|
// try container.encode(zipCode, forKey: ._zipCode) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._zipCode) |
||||||
|
// } |
||||||
|
// |
||||||
|
// if let latitude = latitude { |
||||||
|
// try container.encode(latitude, forKey: ._latitude) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._latitude) |
||||||
|
// } |
||||||
|
// |
||||||
|
// if let longitude = longitude { |
||||||
|
// try container.encode(longitude, forKey: ._longitude) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._longitude) |
||||||
|
// } |
||||||
|
// |
||||||
|
// try container.encode(courtCount, forKey: ._courtCount) |
||||||
|
// |
||||||
|
// if let broadcastCode { |
||||||
|
// try container.encode(broadcastCode, forKey: ._broadcastCode) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._broadcastCode) |
||||||
|
// } |
||||||
|
// |
||||||
|
// // try container.encode(alphabeticalName, forKey: ._alphabeticalName) |
||||||
|
// } |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,70 @@ |
|||||||
|
// |
||||||
|
// MonthData.swift |
||||||
|
// PadelClub |
||||||
|
// |
||||||
|
// Created by Razmig Sarkissian on 18/04/2024. |
||||||
|
// |
||||||
|
|
||||||
|
import Foundation |
||||||
|
import SwiftUI |
||||||
|
import LeStorage |
||||||
|
|
||||||
|
final class MonthData : ModelObject, Storable { |
||||||
|
|
||||||
|
static func resourceName() -> String { return "month-data" } |
||||||
|
static func tokenExemptedMethods() -> [HTTPMethod] { return [] } |
||||||
|
static func filterByStoreIdentifier() -> Bool { return false } |
||||||
|
static var relationshipNames: [String] = [] |
||||||
|
|
||||||
|
private(set) var id: String = Store.randomId() |
||||||
|
private(set) var monthKey: String |
||||||
|
private(set) var creationDate: Date |
||||||
|
var maleUnrankedValue: Int? = nil |
||||||
|
var femaleUnrankedValue: Int? = nil |
||||||
|
var maleCount: Int? = nil |
||||||
|
var femaleCount: Int? = nil |
||||||
|
var anonymousCount: Int? = nil |
||||||
|
var incompleteMode: Bool = false |
||||||
|
|
||||||
|
init(monthKey: String) { |
||||||
|
self.monthKey = monthKey |
||||||
|
self.creationDate = Date() |
||||||
|
} |
||||||
|
|
||||||
|
fileprivate func _updateCreationDate() { |
||||||
|
self.creationDate = Date() |
||||||
|
} |
||||||
|
|
||||||
|
// required init(from decoder: Decoder) throws { |
||||||
|
// let container = try decoder.container(keyedBy: CodingKeys.self) |
||||||
|
// id = try container.decode(String.self, forKey: ._id) |
||||||
|
// monthKey = try container.decode(String.self, forKey: ._monthKey) |
||||||
|
// creationDate = try container.decode(Date.self, forKey: ._creationDate) |
||||||
|
// maleUnrankedValue = try container.decodeIfPresent(Int.self, forKey: ._maleUnrankedValue) |
||||||
|
// femaleUnrankedValue = try container.decodeIfPresent(Int.self, forKey: ._femaleUnrankedValue) |
||||||
|
// maleCount = try container.decodeIfPresent(Int.self, forKey: ._maleCount) |
||||||
|
// femaleCount = try container.decodeIfPresent(Int.self, forKey: ._femaleCount) |
||||||
|
// anonymousCount = try container.decodeIfPresent(Int.self, forKey: ._anonymousCount) |
||||||
|
// incompleteMode = try container.decodeIfPresent(Bool.self, forKey: ._incompleteMode) ?? false |
||||||
|
|
||||||
|
// } |
||||||
|
|
||||||
|
func total() -> Int { |
||||||
|
return (maleCount ?? 0) + (femaleCount ?? 0) |
||||||
|
} |
||||||
|
|
||||||
|
override func deleteDependencies() throws { |
||||||
|
} |
||||||
|
|
||||||
|
// enum CodingKeys: String, CodingKey { |
||||||
|
// case _id = "id" |
||||||
|
// case _monthKey = "monthKey" |
||||||
|
// case _creationDate = "creationDate" |
||||||
|
// case _maleUnrankedValue = "maleUnrankedValue" |
||||||
|
// case _femaleUnrankedValue = "femaleUnrankedValue" |
||||||
|
// case _maleCount = "maleCount" |
||||||
|
// case _femaleCount = "femaleCount" |
||||||
|
// case _anonymousCount = "anonymousCount" |
||||||
|
// case _incompleteMode = "incompleteMode" |
||||||
|
// } |
||||||
|
} |
||||||
@ -0,0 +1,363 @@ |
|||||||
|
// |
||||||
|
// PlayerRegistration.swift |
||||||
|
// Padel Tournament |
||||||
|
// |
||||||
|
// Created by razmig on 10/03/2024. |
||||||
|
// |
||||||
|
|
||||||
|
import Foundation |
||||||
|
import LeStorage |
||||||
|
|
||||||
|
final class PlayerRegistration: ModelObject, Storable { |
||||||
|
static func resourceName() -> String { "player-registrations" } |
||||||
|
static func tokenExemptedMethods() -> [HTTPMethod] { return [] } |
||||||
|
static func filterByStoreIdentifier() -> Bool { return true } |
||||||
|
static var relationshipNames: [String] = ["teamRegistration"] |
||||||
|
|
||||||
|
var id: String = Store.randomId() |
||||||
|
var teamRegistration: String? |
||||||
|
var firstName: String |
||||||
|
var lastName: String |
||||||
|
var licenceId: String? |
||||||
|
var rank: Int? |
||||||
|
var paymentType: PlayerPaymentType? |
||||||
|
var sex: PlayerSexType? |
||||||
|
|
||||||
|
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 computedRank: Int = 0 |
||||||
|
var source: PlayerDataSource? |
||||||
|
|
||||||
|
var hasArrived: Bool = false |
||||||
|
|
||||||
|
init(teamRegistration: String? = nil, firstName: String, lastName: String, licenceId: String? = nil, rank: Int? = nil, paymentType: PlayerPaymentType? = nil, sex: PlayerSexType? = nil, tournamentPlayed: Int? = nil, points: Double? = nil, clubName: String? = nil, ligueName: String? = nil, assimilation: String? = nil, phoneNumber: String? = nil, email: String? = nil, birthdate: String? = nil, computedRank: Int = 0, source: PlayerDataSource? = nil, hasArrived: Bool = false) { |
||||||
|
self.teamRegistration = teamRegistration |
||||||
|
self.firstName = firstName |
||||||
|
self.lastName = lastName |
||||||
|
self.licenceId = licenceId |
||||||
|
self.rank = rank |
||||||
|
self.paymentType = paymentType |
||||||
|
self.sex = sex |
||||||
|
self.tournamentPlayed = tournamentPlayed |
||||||
|
self.points = points |
||||||
|
self.clubName = clubName |
||||||
|
self.ligueName = ligueName |
||||||
|
self.assimilation = assimilation |
||||||
|
self.phoneNumber = phoneNumber |
||||||
|
self.email = email |
||||||
|
self.birthdate = birthdate |
||||||
|
self.computedRank = computedRank |
||||||
|
self.source = source |
||||||
|
self.hasArrived = hasArrived |
||||||
|
} |
||||||
|
|
||||||
|
var tournamentStore: TournamentStore { |
||||||
|
if let store = self.store as? TournamentStore { |
||||||
|
return store |
||||||
|
} |
||||||
|
fatalError("missing store for \(String(describing: type(of: self)))") |
||||||
|
} |
||||||
|
|
||||||
|
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 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 self.tournamentStore.teamRegistrations.findById(teamRegistration) |
||||||
|
} |
||||||
|
|
||||||
|
func hasPaid() -> Bool { |
||||||
|
paymentType != nil |
||||||
|
} |
||||||
|
|
||||||
|
func isImported() -> Bool { |
||||||
|
source == .beachPadel |
||||||
|
} |
||||||
|
|
||||||
|
func rankLabel(_ displayStyle: DisplayStyle = .wide) -> String { |
||||||
|
if let rank, rank > 0 { |
||||||
|
if rank != computedRank { |
||||||
|
return computedRank.formatted() + " (" + rank.formatted() + ")" |
||||||
|
} else { |
||||||
|
return rank.formatted() |
||||||
|
} |
||||||
|
} else { |
||||||
|
return "non classé" + (isMalePlayer() ? "" : "e") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func getRank() -> Int { |
||||||
|
computedRank |
||||||
|
} |
||||||
|
|
||||||
|
func isMalePlayer() -> Bool { |
||||||
|
sex == .male |
||||||
|
} |
||||||
|
|
||||||
|
func setComputedRank(in tournament: Tournament) { |
||||||
|
let currentRank = rank ?? tournament.unrankValue(for: isMalePlayer()) ?? 70_000 |
||||||
|
switch tournament.tournamentCategory { |
||||||
|
case .men: |
||||||
|
computedRank = isMalePlayer() ? currentRank : currentRank + PlayerRegistration.addon(for: currentRank, manMax: tournament.maleUnrankedValue ?? 0, womanMax: tournament.femaleUnrankedValue ?? 0) |
||||||
|
default: |
||||||
|
computedRank = currentRank |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// enum CodingKeys: String, CodingKey { |
||||||
|
// case _id = "id" |
||||||
|
// case _teamRegistration = "teamRegistration" |
||||||
|
// case _firstName = "firstName" |
||||||
|
// case _lastName = "lastName" |
||||||
|
// case _licenceId = "licenceId" |
||||||
|
// case _rank = "rank" |
||||||
|
// case _paymentType = "paymentType" |
||||||
|
// 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 _computedRank = "computedRank" |
||||||
|
// case _source = "source" |
||||||
|
// case _hasArrived = "hasArrived" |
||||||
|
// |
||||||
|
// } |
||||||
|
|
||||||
|
// func encode(to encoder: Encoder) throws { |
||||||
|
// var container = encoder.container(keyedBy: CodingKeys.self) |
||||||
|
// |
||||||
|
// try container.encode(id, forKey: ._id) |
||||||
|
// |
||||||
|
// if let teamRegistration = teamRegistration { |
||||||
|
// try container.encode(teamRegistration, forKey: ._teamRegistration) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._teamRegistration) |
||||||
|
// } |
||||||
|
// |
||||||
|
// try container.encode(firstName, forKey: ._firstName) |
||||||
|
// try container.encode(lastName, forKey: ._lastName) |
||||||
|
// |
||||||
|
// if let licenceId = licenceId { |
||||||
|
// try container.encode(licenceId, forKey: ._licenceId) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._licenceId) |
||||||
|
// } |
||||||
|
// |
||||||
|
// if let rank = rank { |
||||||
|
// try container.encode(rank, forKey: ._rank) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._rank) |
||||||
|
// } |
||||||
|
// |
||||||
|
// if let paymentType = paymentType { |
||||||
|
// try container.encode(paymentType, forKey: ._paymentType) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._paymentType) |
||||||
|
// } |
||||||
|
// |
||||||
|
// if let sex = sex { |
||||||
|
// try container.encode(sex, forKey: ._sex) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._sex) |
||||||
|
// } |
||||||
|
// |
||||||
|
// if let tournamentPlayed = tournamentPlayed { |
||||||
|
// try container.encode(tournamentPlayed, forKey: ._tournamentPlayed) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._tournamentPlayed) |
||||||
|
// } |
||||||
|
// |
||||||
|
// if let points = points { |
||||||
|
// try container.encode(points, forKey: ._points) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._points) |
||||||
|
// } |
||||||
|
// |
||||||
|
// if let clubName = clubName { |
||||||
|
// try container.encode(clubName, forKey: ._clubName) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._clubName) |
||||||
|
// } |
||||||
|
// |
||||||
|
// if let ligueName = ligueName { |
||||||
|
// try container.encode(ligueName, forKey: ._ligueName) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._ligueName) |
||||||
|
// } |
||||||
|
// |
||||||
|
// if let assimilation = assimilation { |
||||||
|
// try container.encode(assimilation, forKey: ._assimilation) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._assimilation) |
||||||
|
// } |
||||||
|
// |
||||||
|
// if let phoneNumber = phoneNumber { |
||||||
|
// try container.encode(phoneNumber, forKey: ._phoneNumber) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._phoneNumber) |
||||||
|
// } |
||||||
|
// |
||||||
|
// if let email = email { |
||||||
|
// try container.encode(email, forKey: ._email) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._email) |
||||||
|
// } |
||||||
|
// |
||||||
|
// if let birthdate = birthdate { |
||||||
|
// try container.encode(birthdate, forKey: ._birthdate) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._birthdate) |
||||||
|
// } |
||||||
|
// |
||||||
|
// try container.encode(computedRank, forKey: ._computedRank) |
||||||
|
// |
||||||
|
// if let source = source { |
||||||
|
// try container.encode(source, forKey: ._source) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._source) |
||||||
|
// } |
||||||
|
// |
||||||
|
// try container.encode(hasArrived, forKey: ._hasArrived) |
||||||
|
// } |
||||||
|
|
||||||
|
enum PlayerDataSource: Int, Codable { |
||||||
|
case frenchFederation = 0 |
||||||
|
case beachPadel = 1 |
||||||
|
} |
||||||
|
|
||||||
|
enum PlayerSexType: Int, Hashable, CaseIterable, Identifiable, Codable { |
||||||
|
init?(rawValue: Int?) { |
||||||
|
guard let value = rawValue else { return nil } |
||||||
|
self.init(rawValue: value) |
||||||
|
} |
||||||
|
|
||||||
|
var id: Self { |
||||||
|
self |
||||||
|
} |
||||||
|
|
||||||
|
case female = 0 |
||||||
|
case male = 1 |
||||||
|
} |
||||||
|
|
||||||
|
enum PlayerPaymentType: 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 |
||||||
|
case forfeit = 8 |
||||||
|
|
||||||
|
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 .forfeit: |
||||||
|
return "Forfait" |
||||||
|
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 |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func insertOnServer() { |
||||||
|
self.tournamentStore.playerRegistrations.writeChangeAndInsertOnServer(instance: self) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
extension PlayerRegistration: Hashable { |
||||||
|
static func == (lhs: PlayerRegistration, rhs: PlayerRegistration) -> Bool { |
||||||
|
lhs.id == rhs.id |
||||||
|
} |
||||||
|
|
||||||
|
func hash(into hasher: inout Hasher) { |
||||||
|
hasher.combine(id) |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,256 @@ |
|||||||
|
// |
||||||
|
// User.swift |
||||||
|
// PadelClub |
||||||
|
// |
||||||
|
// Created by Laurent Morvillier on 21/02/2024. |
||||||
|
// |
||||||
|
|
||||||
|
import Foundation |
||||||
|
import LeStorage |
||||||
|
|
||||||
|
enum UserRight: Int, Codable { |
||||||
|
case none = 0 |
||||||
|
case edition = 1 |
||||||
|
case creation = 2 |
||||||
|
} |
||||||
|
|
||||||
|
class User: ModelObject, UserBase, Storable { |
||||||
|
|
||||||
|
static func resourceName() -> String { "users" } |
||||||
|
static func tokenExemptedMethods() -> [HTTPMethod] { return [.post] } |
||||||
|
static func filterByStoreIdentifier() -> Bool { return false } |
||||||
|
static var relationshipNames: [String] = [] |
||||||
|
|
||||||
|
public var id: String = Store.randomId() |
||||||
|
public var username: String |
||||||
|
public var email: String |
||||||
|
var clubs: [String] = [] |
||||||
|
var umpireCode: String? |
||||||
|
var licenceId: String? |
||||||
|
var firstName: String |
||||||
|
var lastName: String |
||||||
|
var phone: String? |
||||||
|
var country: String? |
||||||
|
|
||||||
|
var summonsMessageBody : String? = nil |
||||||
|
var summonsMessageSignature: String? = nil |
||||||
|
var summonsAvailablePaymentMethods: String? = nil |
||||||
|
var summonsDisplayFormat: Bool = false |
||||||
|
var summonsDisplayEntryFee: Bool = false |
||||||
|
var summonsUseFullCustomMessage: Bool = false |
||||||
|
var matchFormatsDefaultDuration: [MatchFormat: Int]? = nil |
||||||
|
var bracketMatchFormatPreference: MatchFormat? |
||||||
|
var groupStageMatchFormatPreference: MatchFormat? |
||||||
|
var loserBracketMatchFormatPreference: MatchFormat? |
||||||
|
|
||||||
|
var deviceId: String? |
||||||
|
|
||||||
|
init(username: String, email: String, firstName: String, lastName: String, phone: String?, country: String?) { |
||||||
|
self.username = username |
||||||
|
self.firstName = firstName |
||||||
|
self.lastName = lastName |
||||||
|
self.email = email |
||||||
|
self.phone = phone |
||||||
|
self.country = country |
||||||
|
} |
||||||
|
|
||||||
|
public func uuid() throws -> UUID { |
||||||
|
if let uuid = UUID(uuidString: self.id) { |
||||||
|
return uuid |
||||||
|
} |
||||||
|
throw UUIDError.cantConvertString(string: self.id) |
||||||
|
} |
||||||
|
|
||||||
|
func defaultSignature() -> String { |
||||||
|
return "Sportivement,\n\(firstName) \(lastName), votre JAP." |
||||||
|
} |
||||||
|
|
||||||
|
func hasTenupClubs() -> Bool { |
||||||
|
self.clubsObjects().filter({ $0.code != nil }).isEmpty == false |
||||||
|
} |
||||||
|
|
||||||
|
func hasFavoriteClubsAndCreatedClubs() -> Bool { |
||||||
|
clubsObjects(includeCreated: true).isEmpty == false |
||||||
|
} |
||||||
|
|
||||||
|
func setUserClub(_ userClub: Club) { |
||||||
|
self.clubs.insert(userClub.id, at: 0) |
||||||
|
} |
||||||
|
|
||||||
|
func clubsObjects(includeCreated: Bool = false) -> [Club] { |
||||||
|
return DataStore.shared.clubs.filter({ (includeCreated && $0.creator == id) || clubs.contains($0.id) }) |
||||||
|
} |
||||||
|
|
||||||
|
func createdClubsObjectsNotFavorite() -> [Club] { |
||||||
|
return DataStore.shared.clubs.filter({ ($0.creator == id) && clubs.contains($0.id) == false }) |
||||||
|
} |
||||||
|
|
||||||
|
func saveMatchFormatsDefaultDuration(_ matchFormat: MatchFormat, estimatedDuration: Int) { |
||||||
|
if estimatedDuration == matchFormat.defaultEstimatedDuration { |
||||||
|
matchFormatsDefaultDuration?.removeValue(forKey: matchFormat) |
||||||
|
} else { |
||||||
|
matchFormatsDefaultDuration = matchFormatsDefaultDuration ?? [MatchFormat: Int]() |
||||||
|
matchFormatsDefaultDuration?[matchFormat] = estimatedDuration |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func addClub(_ club: Club) { |
||||||
|
if !self.clubs.contains(where: { $0 == club.id }) { |
||||||
|
self.clubs.append(club.id) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// enum CodingKeys: String, CodingKey { |
||||||
|
// case _id = "id" |
||||||
|
// case _username = "username" |
||||||
|
// case _email = "email" |
||||||
|
// case _clubs = "clubs" |
||||||
|
// case _umpireCode = "umpireCode" |
||||||
|
// case _licenceId = "licenceId" |
||||||
|
// case _firstName = "firstName" |
||||||
|
// case _lastName = "lastName" |
||||||
|
// case _phone = "phone" |
||||||
|
// case _country = "country" |
||||||
|
// case _summonsMessageBody = "summonsMessageBody" |
||||||
|
// case _summonsMessageSignature = "summonsMessageSignature" |
||||||
|
// case _summonsAvailablePaymentMethods = "summonsAvailablePaymentMethods" |
||||||
|
// case _summonsDisplayFormat = "summonsDisplayFormat" |
||||||
|
// case _summonsDisplayEntryFee = "summonsDisplayEntryFee" |
||||||
|
// case _summonsUseFullCustomMessage = "summonsUseFullCustomMessage" |
||||||
|
// case _matchFormatsDefaultDuration = "matchFormatsDefaultDuration" |
||||||
|
// case _bracketMatchFormatPreference = "bracketMatchFormatPreference" |
||||||
|
// case _groupStageMatchFormatPreference = "groupStageMatchFormatPreference" |
||||||
|
// case _loserBracketMatchFormatPreference = "loserBracketMatchFormatPreference" |
||||||
|
// case _deviceId = "deviceId" |
||||||
|
// } |
||||||
|
|
||||||
|
// func encode(to encoder: Encoder) throws { |
||||||
|
// var container = encoder.container(keyedBy: CodingKeys.self) |
||||||
|
// |
||||||
|
// try container.encode(id, forKey: ._id) |
||||||
|
// try container.encode(username, forKey: ._username) |
||||||
|
// try container.encode(email, forKey: ._email) |
||||||
|
// try container.encode(clubs, forKey: ._clubs) |
||||||
|
// |
||||||
|
// if let umpireCode = umpireCode { |
||||||
|
// try container.encode(umpireCode, forKey: ._umpireCode) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._umpireCode) |
||||||
|
// } |
||||||
|
// |
||||||
|
// if let licenceId = licenceId { |
||||||
|
// try container.encode(licenceId, forKey: ._licenceId) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._licenceId) |
||||||
|
// } |
||||||
|
// |
||||||
|
// try container.encode(firstName, forKey: ._firstName) |
||||||
|
// try container.encode(lastName, forKey: ._lastName) |
||||||
|
// |
||||||
|
// if let phone = phone { |
||||||
|
// try container.encode(phone, forKey: ._phone) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._phone) |
||||||
|
// } |
||||||
|
// |
||||||
|
// if let country = country { |
||||||
|
// try container.encode(country, forKey: ._country) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._country) |
||||||
|
// } |
||||||
|
// |
||||||
|
// if let summonsMessageBody = summonsMessageBody { |
||||||
|
// try container.encode(summonsMessageBody, forKey: ._summonsMessageBody) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._summonsMessageBody) |
||||||
|
// } |
||||||
|
// |
||||||
|
// if let summonsMessageSignature = summonsMessageSignature { |
||||||
|
// try container.encode(summonsMessageSignature, forKey: ._summonsMessageSignature) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._summonsMessageSignature) |
||||||
|
// } |
||||||
|
// |
||||||
|
// if let summonsAvailablePaymentMethods = summonsAvailablePaymentMethods { |
||||||
|
// try container.encode(summonsAvailablePaymentMethods, forKey: ._summonsAvailablePaymentMethods) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._summonsAvailablePaymentMethods) |
||||||
|
// } |
||||||
|
// |
||||||
|
// try container.encode(summonsDisplayFormat, forKey: ._summonsDisplayFormat) |
||||||
|
// try container.encode(summonsDisplayEntryFee, forKey: ._summonsDisplayEntryFee) |
||||||
|
// try container.encode(summonsUseFullCustomMessage, forKey: ._summonsUseFullCustomMessage) |
||||||
|
// |
||||||
|
// if let matchFormatsDefaultDuration = matchFormatsDefaultDuration { |
||||||
|
// try container.encode(matchFormatsDefaultDuration, forKey: ._matchFormatsDefaultDuration) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._matchFormatsDefaultDuration) |
||||||
|
// } |
||||||
|
// |
||||||
|
// if let bracketMatchFormatPreference = bracketMatchFormatPreference { |
||||||
|
// try container.encode(bracketMatchFormatPreference, forKey: ._bracketMatchFormatPreference) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._bracketMatchFormatPreference) |
||||||
|
// } |
||||||
|
// |
||||||
|
// if let groupStageMatchFormatPreference = groupStageMatchFormatPreference { |
||||||
|
// try container.encode(groupStageMatchFormatPreference, forKey: ._groupStageMatchFormatPreference) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._groupStageMatchFormatPreference) |
||||||
|
// } |
||||||
|
// |
||||||
|
// if let loserBracketMatchFormatPreference = loserBracketMatchFormatPreference { |
||||||
|
// try container.encode(loserBracketMatchFormatPreference, forKey: ._loserBracketMatchFormatPreference) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._loserBracketMatchFormatPreference) |
||||||
|
// } |
||||||
|
// |
||||||
|
// if let deviceId { |
||||||
|
// try container.encode(deviceId, forKey: ._deviceId) |
||||||
|
// } else { |
||||||
|
// try container.encodeNil(forKey: ._deviceId) |
||||||
|
// } |
||||||
|
// |
||||||
|
// } |
||||||
|
|
||||||
|
static func placeHolder() -> User { |
||||||
|
return User(username: "", email: "", firstName: "", lastName: "", phone: nil, country: nil) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
class UserCreationForm: User, UserPasswordBase { |
||||||
|
|
||||||
|
init(user: User, username: String, password: String, firstName: String, lastName: String, email: String, phone: String?, country: String?) { |
||||||
|
self.password = password |
||||||
|
super.init(username: username, email: email, firstName: firstName, lastName: lastName, phone: phone, country: country) |
||||||
|
|
||||||
|
self.summonsMessageBody = user.summonsMessageBody |
||||||
|
self.summonsMessageSignature = user.summonsMessageSignature |
||||||
|
self.summonsAvailablePaymentMethods = user.summonsAvailablePaymentMethods |
||||||
|
self.summonsDisplayFormat = user.summonsDisplayFormat |
||||||
|
self.summonsDisplayEntryFee = user.summonsDisplayEntryFee |
||||||
|
self.summonsUseFullCustomMessage = user.summonsUseFullCustomMessage |
||||||
|
self.matchFormatsDefaultDuration = user.matchFormatsDefaultDuration |
||||||
|
self.bracketMatchFormatPreference = user.bracketMatchFormatPreference |
||||||
|
self.groupStageMatchFormatPreference = user.groupStageMatchFormatPreference |
||||||
|
self.loserBracketMatchFormatPreference = user.loserBracketMatchFormatPreference |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
required init(from decoder: Decoder) throws { |
||||||
|
fatalError("init(from:) has not been implemented") |
||||||
|
} |
||||||
|
|
||||||
|
public var password: String |
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey { |
||||||
|
case password |
||||||
|
} |
||||||
|
|
||||||
|
override func encode(to encoder: Encoder) throws { |
||||||
|
try super.encode(to: encoder) |
||||||
|
var container = encoder.container(keyedBy: CodingKeys.self) |
||||||
|
try container.encode(self.password, forKey: .password) |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,13 @@ |
|||||||
|
# ``PadelClubData`` |
||||||
|
|
||||||
|
<!--@START_MENU_TOKEN@-->Summary<!--@END_MENU_TOKEN@--> |
||||||
|
|
||||||
|
## Overview |
||||||
|
|
||||||
|
<!--@START_MENU_TOKEN@-->Text<!--@END_MENU_TOKEN@--> |
||||||
|
|
||||||
|
## Topics |
||||||
|
|
||||||
|
### <!--@START_MENU_TOKEN@-->Group<!--@END_MENU_TOKEN@--> |
||||||
|
|
||||||
|
- <!--@START_MENU_TOKEN@-->``Symbol``<!--@END_MENU_TOKEN@--> |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
//
|
||||||
|
// PadelClubData.h
|
||||||
|
// PadelClubData
|
||||||
|
//
|
||||||
|
// Created by Laurent Morvillier on 27/08/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h> |
||||||
|
|
||||||
|
//! Project version number for PadelClubData.
|
||||||
|
FOUNDATION_EXPORT double PadelClubDataVersionNumber; |
||||||
|
|
||||||
|
//! Project version string for PadelClubData.
|
||||||
|
FOUNDATION_EXPORT const unsigned char PadelClubDataVersionString[]; |
||||||
|
|
||||||
|
// In this header, you should import all the public headers of your framework using statements like #import <PadelClubData/PublicHeader.h>
|
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,37 @@ |
|||||||
|
// |
||||||
|
// PlayerFilterOption.swift |
||||||
|
// PadelClubData |
||||||
|
// |
||||||
|
// Created by Laurent Morvillier on 27/08/2024. |
||||||
|
// |
||||||
|
|
||||||
|
import Foundation |
||||||
|
|
||||||
|
enum PlayerFilterOption: Int, Hashable, CaseIterable, Identifiable { |
||||||
|
case all = -1 |
||||||
|
case male = 1 |
||||||
|
case female = 0 |
||||||
|
|
||||||
|
var id: Int { rawValue } |
||||||
|
|
||||||
|
func icon() -> String { |
||||||
|
switch self { |
||||||
|
case .all: |
||||||
|
return "Tous" |
||||||
|
case .male: |
||||||
|
return "Homme" |
||||||
|
case .female: |
||||||
|
return "Femme" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
var localizedPlayerLabel: String { |
||||||
|
switch self { |
||||||
|
case .female: |
||||||
|
return "joueuse" |
||||||
|
default: |
||||||
|
return "joueur" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,81 @@ |
|||||||
|
// |
||||||
|
// Sequence+Extensions.swift |
||||||
|
// PadelClubData |
||||||
|
// |
||||||
|
// Created by Laurent Morvillier on 27/08/2024. |
||||||
|
// |
||||||
|
|
||||||
|
import Foundation |
||||||
|
|
||||||
|
enum SortOrder { |
||||||
|
case ascending |
||||||
|
case descending |
||||||
|
} |
||||||
|
|
||||||
|
extension Sequence { |
||||||
|
func sorted<T: Comparable>(by keyPath: KeyPath<Element, T>) -> [Element] { |
||||||
|
return sorted { a, b in |
||||||
|
return a[keyPath: keyPath] < b[keyPath: keyPath] |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func sorted(using descriptors: [MySortDescriptor<Element>], |
||||||
|
order: SortOrder) -> [Element] { |
||||||
|
sorted { valueA, valueB in |
||||||
|
for descriptor in descriptors { |
||||||
|
let result = descriptor.comparator(valueA, valueB) |
||||||
|
|
||||||
|
switch result { |
||||||
|
case .orderedSame: |
||||||
|
// Keep iterating if the two elements are equal, |
||||||
|
// since that'll let the next descriptor determine |
||||||
|
// the sort order: |
||||||
|
break |
||||||
|
case .orderedAscending: |
||||||
|
return order == .ascending |
||||||
|
case .orderedDescending: |
||||||
|
return order == .descending |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// If no descriptor was able to determine the sort |
||||||
|
// order, we'll default to false (similar to when |
||||||
|
// using the '<' operator with the built-in API): |
||||||
|
return false |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func sorted(using descriptors: MySortDescriptor<Element>...) -> [Element] { |
||||||
|
sorted(using: descriptors, order: .ascending) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
extension Array { |
||||||
|
func chunked(into size: Int) -> [[Element]] { |
||||||
|
return stride(from: 0, to: count, by: size).map { |
||||||
|
Array(self[$0 ..< Swift.min($0 + size, count)]) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func anySatisfy(_ p: (Element) -> Bool) -> Bool { |
||||||
|
return first(where: { p($0) }) != nil |
||||||
|
//return !self.allSatisfy { !p($0) } |
||||||
|
} |
||||||
|
|
||||||
|
func sorted<T: Comparable>(by keyPath: KeyPath<Element, T>, order: SortOrder) -> [Element] { |
||||||
|
switch order { |
||||||
|
case .ascending: |
||||||
|
return self.sorted { $0[keyPath: keyPath] < $1[keyPath: keyPath] } |
||||||
|
case .descending: |
||||||
|
return self.sorted { $0[keyPath: keyPath] > $1[keyPath: keyPath] } |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
extension Collection { |
||||||
|
/// Returns the element at the specified index if it is within bounds, otherwise nil. |
||||||
|
subscript (safe index: Index) -> Element? { |
||||||
|
return indices.contains(index) ? self[index] : nil |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,63 @@ |
|||||||
|
// |
||||||
|
// String+Extensions.swift |
||||||
|
// PadelClubData |
||||||
|
// |
||||||
|
// Created by Laurent Morvillier on 27/08/2024. |
||||||
|
// |
||||||
|
|
||||||
|
import Foundation |
||||||
|
|
||||||
|
extension String { |
||||||
|
|
||||||
|
func replaceCharactersFromSet(characterSet: CharacterSet, replacementString: String = "") -> String { |
||||||
|
components(separatedBy: characterSet).joined(separator:replacementString) |
||||||
|
} |
||||||
|
|
||||||
|
var removingFirstCharacter: String { |
||||||
|
String(dropFirst()) |
||||||
|
} |
||||||
|
|
||||||
|
var trimmed: String { |
||||||
|
trimmingCharacters(in: .whitespacesAndNewlines) |
||||||
|
} |
||||||
|
|
||||||
|
var canonicalVersion: String { |
||||||
|
self.trimmed.replaceCharactersFromSet(characterSet: .punctuationCharacters, replacementString: " ").folding(options: .diacriticInsensitive, locale: .current).lowercased() |
||||||
|
} |
||||||
|
|
||||||
|
var canonicalVersionWithPunctuation: String { |
||||||
|
trimmed.folding(options: .diacriticInsensitive, locale: .current).lowercased() |
||||||
|
} |
||||||
|
|
||||||
|
func acronym() -> String { |
||||||
|
let acronym = canonicalVersion.replaceCharactersFromSet(characterSet: .whitespacesAndNewlines) |
||||||
|
if acronym.count > 10 { |
||||||
|
return concatenateFirstLetters().uppercased() |
||||||
|
} else { |
||||||
|
return acronym.uppercased() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func concatenateFirstLetters() -> String { |
||||||
|
// Split the input into sentences |
||||||
|
let sentences = self.components(separatedBy: .whitespacesAndNewlines) |
||||||
|
if sentences.count == 1 { |
||||||
|
return String(self.prefix(10)) |
||||||
|
} |
||||||
|
// Extract the first character of each sentence |
||||||
|
let firstLetters = sentences.compactMap { sentence -> Character? in |
||||||
|
let trimmedSentence = sentence.trimmingCharacters(in: .whitespacesAndNewlines) |
||||||
|
if trimmedSentence.count > 2 { |
||||||
|
if let firstCharacter = trimmedSentence.first { |
||||||
|
return firstCharacter |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// Join the first letters together into a string |
||||||
|
let result = String(firstLetters) |
||||||
|
return result |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,36 @@ |
|||||||
|
// |
||||||
|
// PadelClubDataTests.swift |
||||||
|
// PadelClubDataTests |
||||||
|
// |
||||||
|
// Created by Laurent Morvillier on 27/08/2024. |
||||||
|
// |
||||||
|
|
||||||
|
import XCTest |
||||||
|
@testable import PadelClubData |
||||||
|
|
||||||
|
final class PadelClubDataTests: XCTestCase { |
||||||
|
|
||||||
|
override func setUpWithError() throws { |
||||||
|
// Put setup code here. This method is called before the invocation of each test method in the class. |
||||||
|
} |
||||||
|
|
||||||
|
override func tearDownWithError() throws { |
||||||
|
// Put teardown code here. This method is called after the invocation of each test method in the class. |
||||||
|
} |
||||||
|
|
||||||
|
func testExample() throws { |
||||||
|
// This is an example of a functional test case. |
||||||
|
// Use XCTAssert and related functions to verify your tests produce the correct results. |
||||||
|
// Any test you write for XCTest can be annotated as throws and async. |
||||||
|
// Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. |
||||||
|
// Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. |
||||||
|
} |
||||||
|
|
||||||
|
func testPerformanceExample() throws { |
||||||
|
// This is an example of a performance test case. |
||||||
|
self.measure { |
||||||
|
// Put the code you want to measure the time of here. |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue