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