first commit

sync2
Laurent 1 year ago
parent 31ee3cef62
commit 671edd3412
  1. 20
      PadelClub.xcodeproj/project.pbxproj
  2. 28
      PadelClub/Data/Club.swift
  3. 21
      PadelClub/Data/Court.swift
  4. 17
      PadelClub/Data/CustomUser.swift
  5. 62
      PadelClub/Data/DataStore.swift
  6. 10
      PadelClub/Data/DateInterval.swift
  7. 16
      PadelClub/Data/Event.swift
  8. 60
      PadelClub/Data/GroupStage.swift
  9. 90
      PadelClub/Data/Match.swift
  10. 4
      PadelClub/Data/MonthData.swift
  11. 22
      PadelClub/Data/PlayerRegistration.swift
  12. 1
      PadelClub/Data/README.md
  13. 63
      PadelClub/Data/Round.swift
  14. 58
      PadelClub/Data/TeamRegistration.swift
  15. 11
      PadelClub/Data/TeamScore.swift
  16. 132
      PadelClub/Data/Tournament.swift
  17. 17
      PadelClub/Data/TournamentStore.swift
  18. 4
      PadelClub/Utils/SourceFileManager.swift
  19. 6
      PadelClub/Views/Club/ClubDetailView.swift
  20. 24
      PadelClub/Views/GroupStage/LoserBracketFromGroupStageView.swift
  21. 4
      PadelClub/Views/Navigation/Agenda/TournamentSubscriptionView.swift
  22. 53
      PadelClub/Views/Round/RoundSettingsView.swift
  23. 400
      PadelClub/Views/Tournament/Screen/AddTeamView.swift
  24. 1
      PadelClub/Views/Tournament/Subscription/Guard.swift
  25. 10
      PadelClub/Views/Tournament/Subscription/Purchase.swift
  26. 2
      PadelClub/Views/User/AccountView.swift
  27. 4
      PadelClub/Views/User/LoginView.swift
  28. 2
      PadelClub/Views/User/UserCreationView.swift
  29. 32
      PadelClubTests/ServerDataTests.swift
  30. 33
      PadelClubTests/SynchronizationTests.swift
  31. 8
      PadelClubTests/UserDataTests.swift

@ -42,12 +42,13 @@
C4A47D9F2B7D0BCE00ADC637 /* StepperView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D9E2B7D0BCE00ADC637 /* StepperView.swift */; }; C4A47D9F2B7D0BCE00ADC637 /* StepperView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D9E2B7D0BCE00ADC637 /* StepperView.swift */; };
C4A47DA62B83948E00ADC637 /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DA52B83948E00ADC637 /* LoginView.swift */; }; C4A47DA62B83948E00ADC637 /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DA52B83948E00ADC637 /* LoginView.swift */; };
C4A47DA92B85F82100ADC637 /* ChangePasswordView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DA82B85F82100ADC637 /* ChangePasswordView.swift */; }; C4A47DA92B85F82100ADC637 /* ChangePasswordView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DA82B85F82100ADC637 /* ChangePasswordView.swift */; };
C4A47DAD2B85FCCD00ADC637 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DAC2B85FCCD00ADC637 /* User.swift */; }; C4A47DAD2B85FCCD00ADC637 /* CustomUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DAC2B85FCCD00ADC637 /* CustomUser.swift */; };
C4A47DB32B86387500ADC637 /* AccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DB22B86387500ADC637 /* AccountView.swift */; }; C4A47DB32B86387500ADC637 /* AccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DB22B86387500ADC637 /* AccountView.swift */; };
C4B3A1552C2581DA0078EAA8 /* Patcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B3A1542C2581DA0078EAA8 /* Patcher.swift */; }; C4B3A1552C2581DA0078EAA8 /* Patcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B3A1542C2581DA0078EAA8 /* Patcher.swift */; };
C4C01D982C481C0C0059087C /* CapsuleViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C01D972C481C0C0059087C /* CapsuleViewModifier.swift */; }; C4C01D982C481C0C0059087C /* CapsuleViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C01D972C481C0C0059087C /* CapsuleViewModifier.swift */; };
C4C33F762C9B1ED4006316DE /* CodingContainer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C33F752C9B1EC5006316DE /* CodingContainer+Extensions.swift */; }; C4C33F762C9B1ED4006316DE /* CodingContainer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C33F752C9B1EC5006316DE /* CodingContainer+Extensions.swift */; };
C4C33F772C9B1ED4006316DE /* CodingContainer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C33F752C9B1EC5006316DE /* CodingContainer+Extensions.swift */; }; C4C33F772C9B1ED4006316DE /* CodingContainer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C33F752C9B1EC5006316DE /* CodingContainer+Extensions.swift */; };
C4D477992CB6704C0077713D /* SynchronizationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D477982CB6704C0077713D /* SynchronizationTests.swift */; };
C4EC6F572BE92CAC000CEAB4 /* local.plist in Resources */ = {isa = PBXBuildFile; fileRef = C4EC6F562BE92CAC000CEAB4 /* local.plist */; }; C4EC6F572BE92CAC000CEAB4 /* local.plist in Resources */ = {isa = PBXBuildFile; fileRef = C4EC6F562BE92CAC000CEAB4 /* local.plist */; };
C4EC6F592BE92D88000CEAB4 /* PListReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC6F582BE92D88000CEAB4 /* PListReader.swift */; }; C4EC6F592BE92D88000CEAB4 /* PListReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC6F582BE92D88000CEAB4 /* PListReader.swift */; };
C4FC2E272C2AABC90021F3BF /* PasswordField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FC2E262C2AABC90021F3BF /* PasswordField.swift */; }; C4FC2E272C2AABC90021F3BF /* PasswordField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FC2E262C2AABC90021F3BF /* PasswordField.swift */; };
@ -308,7 +309,7 @@
FF4CBFFA2C996C0600151637 /* TournamentScheduleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF0E0B6C2BC254C6005F00A9 /* TournamentScheduleView.swift */; }; FF4CBFFA2C996C0600151637 /* TournamentScheduleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF0E0B6C2BC254C6005F00A9 /* TournamentScheduleView.swift */; };
FF4CBFFB2C996C0600151637 /* MatchFormatStorageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AF02BD1AEBD00A86CF8 /* MatchFormatStorageView.swift */; }; FF4CBFFB2C996C0600151637 /* MatchFormatStorageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AF02BD1AEBD00A86CF8 /* MatchFormatStorageView.swift */; };
FF4CBFFC2C996C0600151637 /* UmpireView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3F74F52B919E45004CFE0E /* UmpireView.swift */; }; FF4CBFFC2C996C0600151637 /* UmpireView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3F74F52B919E45004CFE0E /* UmpireView.swift */; };
FF4CBFFD2C996C0600151637 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DAC2B85FCCD00ADC637 /* User.swift */; }; FF4CBFFD2C996C0600151637 /* CustomUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DAC2B85FCCD00ADC637 /* CustomUser.swift */; };
FF4CBFFE2C996C0600151637 /* MatchSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D002BAEF0B400A9A3BD /* MatchSummaryView.swift */; }; FF4CBFFE2C996C0600151637 /* MatchSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D002BAEF0B400A9A3BD /* MatchSummaryView.swift */; };
FF4CBFFF2C996C0600151637 /* TournamentDurationManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26442BAE0A3400650388 /* TournamentDurationManagerView.swift */; }; FF4CBFFF2C996C0600151637 /* TournamentDurationManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26442BAE0A3400650388 /* TournamentDurationManagerView.swift */; };
FF4CC0002C996C0600151637 /* MockData.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5522BAB354A00FD8220 /* MockData.swift */; }; FF4CC0002C996C0600151637 /* MockData.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5522BAB354A00FD8220 /* MockData.swift */; };
@ -611,7 +612,7 @@
FF70FB792C90584900129CC2 /* TournamentScheduleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF0E0B6C2BC254C6005F00A9 /* TournamentScheduleView.swift */; }; FF70FB792C90584900129CC2 /* TournamentScheduleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF0E0B6C2BC254C6005F00A9 /* TournamentScheduleView.swift */; };
FF70FB7A2C90584900129CC2 /* MatchFormatStorageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AF02BD1AEBD00A86CF8 /* MatchFormatStorageView.swift */; }; FF70FB7A2C90584900129CC2 /* MatchFormatStorageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AF02BD1AEBD00A86CF8 /* MatchFormatStorageView.swift */; };
FF70FB7B2C90584900129CC2 /* UmpireView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3F74F52B919E45004CFE0E /* UmpireView.swift */; }; FF70FB7B2C90584900129CC2 /* UmpireView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3F74F52B919E45004CFE0E /* UmpireView.swift */; };
FF70FB7C2C90584900129CC2 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DAC2B85FCCD00ADC637 /* User.swift */; }; FF70FB7C2C90584900129CC2 /* CustomUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DAC2B85FCCD00ADC637 /* CustomUser.swift */; };
FF70FB7D2C90584900129CC2 /* MatchSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D002BAEF0B400A9A3BD /* MatchSummaryView.swift */; }; FF70FB7D2C90584900129CC2 /* MatchSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF967D002BAEF0B400A9A3BD /* MatchSummaryView.swift */; };
FF70FB7E2C90584900129CC2 /* TournamentDurationManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26442BAE0A3400650388 /* TournamentDurationManagerView.swift */; }; FF70FB7E2C90584900129CC2 /* TournamentDurationManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26442BAE0A3400650388 /* TournamentDurationManagerView.swift */; };
FF70FB7F2C90584900129CC2 /* MockData.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5522BAB354A00FD8220 /* MockData.swift */; }; FF70FB7F2C90584900129CC2 /* MockData.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5522BAB354A00FD8220 /* MockData.swift */; };
@ -897,11 +898,12 @@
C4A47D9E2B7D0BCE00ADC637 /* StepperView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepperView.swift; sourceTree = "<group>"; }; C4A47D9E2B7D0BCE00ADC637 /* StepperView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepperView.swift; sourceTree = "<group>"; };
C4A47DA52B83948E00ADC637 /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = "<group>"; }; C4A47DA52B83948E00ADC637 /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = "<group>"; };
C4A47DA82B85F82100ADC637 /* ChangePasswordView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangePasswordView.swift; sourceTree = "<group>"; }; C4A47DA82B85F82100ADC637 /* ChangePasswordView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangePasswordView.swift; sourceTree = "<group>"; };
C4A47DAC2B85FCCD00ADC637 /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = "<group>"; }; C4A47DAC2B85FCCD00ADC637 /* CustomUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomUser.swift; sourceTree = "<group>"; };
C4A47DB22B86387500ADC637 /* AccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountView.swift; sourceTree = "<group>"; }; C4A47DB22B86387500ADC637 /* AccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountView.swift; sourceTree = "<group>"; };
C4B3A1542C2581DA0078EAA8 /* Patcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Patcher.swift; sourceTree = "<group>"; }; C4B3A1542C2581DA0078EAA8 /* Patcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Patcher.swift; sourceTree = "<group>"; };
C4C01D972C481C0C0059087C /* CapsuleViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CapsuleViewModifier.swift; sourceTree = "<group>"; }; C4C01D972C481C0C0059087C /* CapsuleViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CapsuleViewModifier.swift; sourceTree = "<group>"; };
C4C33F752C9B1EC5006316DE /* CodingContainer+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CodingContainer+Extensions.swift"; sourceTree = "<group>"; }; C4C33F752C9B1EC5006316DE /* CodingContainer+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CodingContainer+Extensions.swift"; sourceTree = "<group>"; };
C4D477982CB6704C0077713D /* SynchronizationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SynchronizationTests.swift; sourceTree = "<group>"; };
C4EC6F562BE92CAC000CEAB4 /* local.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = local.plist; sourceTree = "<group>"; }; C4EC6F562BE92CAC000CEAB4 /* local.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = local.plist; sourceTree = "<group>"; };
C4EC6F582BE92D88000CEAB4 /* PListReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PListReader.swift; sourceTree = "<group>"; }; C4EC6F582BE92D88000CEAB4 /* PListReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PListReader.swift; sourceTree = "<group>"; };
C4FC2E262C2AABC90021F3BF /* PasswordField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordField.swift; sourceTree = "<group>"; }; C4FC2E262C2AABC90021F3BF /* PasswordField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordField.swift; sourceTree = "<group>"; };
@ -1293,6 +1295,7 @@
C411C9C22BEBA453003017AD /* ServerDataTests.swift */, C411C9C22BEBA453003017AD /* ServerDataTests.swift */,
C411C9C82BF219CB003017AD /* UserDataTests.swift */, C411C9C82BF219CB003017AD /* UserDataTests.swift */,
C411C9CF2BF38F41003017AD /* TokenExemptionTests.swift */, C411C9CF2BF38F41003017AD /* TokenExemptionTests.swift */,
C4D477982CB6704C0077713D /* SynchronizationTests.swift */,
); );
path = PadelClubTests; path = PadelClubTests;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1320,7 +1323,7 @@
C411C9CC2BF21DAF003017AD /* README.md */, C411C9CC2BF21DAF003017AD /* README.md */,
C4A47D5D2B6D38EC00ADC637 /* DataStore.swift */, C4A47D5D2B6D38EC00ADC637 /* DataStore.swift */,
C4FC2E2A2C2C0E4D0021F3BF /* TournamentStore.swift */, C4FC2E2A2C2C0E4D0021F3BF /* TournamentStore.swift */,
C4A47DAC2B85FCCD00ADC637 /* User.swift */, C4A47DAC2B85FCCD00ADC637 /* CustomUser.swift */,
C4A47D592B6D383C00ADC637 /* Tournament.swift */, C4A47D592B6D383C00ADC637 /* Tournament.swift */,
FF967CE72BAEC70100A9A3BD /* GroupStage.swift */, FF967CE72BAEC70100A9A3BD /* GroupStage.swift */,
FF967CED2BAECBD700A9A3BD /* Round.swift */, FF967CED2BAECBD700A9A3BD /* Round.swift */,
@ -2405,7 +2408,7 @@
FF0E0B6D2BC254C6005F00A9 /* TournamentScheduleView.swift in Sources */, FF0E0B6D2BC254C6005F00A9 /* TournamentScheduleView.swift in Sources */,
FF025AF12BD1AEBD00A86CF8 /* MatchFormatStorageView.swift in Sources */, FF025AF12BD1AEBD00A86CF8 /* MatchFormatStorageView.swift in Sources */,
FF3F74F62B919E45004CFE0E /* UmpireView.swift in Sources */, FF3F74F62B919E45004CFE0E /* UmpireView.swift in Sources */,
C4A47DAD2B85FCCD00ADC637 /* User.swift in Sources */, C4A47DAD2B85FCCD00ADC637 /* CustomUser.swift in Sources */,
C4C33F762C9B1ED4006316DE /* CodingContainer+Extensions.swift in Sources */, C4C33F762C9B1ED4006316DE /* CodingContainer+Extensions.swift in Sources */,
FF967D012BAEF0B400A9A3BD /* MatchSummaryView.swift in Sources */, FF967D012BAEF0B400A9A3BD /* MatchSummaryView.swift in Sources */,
FF8F26452BAE0A3400650388 /* TournamentDurationManagerView.swift in Sources */, FF8F26452BAE0A3400650388 /* TournamentDurationManagerView.swift in Sources */,
@ -2470,6 +2473,7 @@
files = ( files = (
C411C9D02BF38F41003017AD /* TokenExemptionTests.swift in Sources */, C411C9D02BF38F41003017AD /* TokenExemptionTests.swift in Sources */,
C49EF0422BE23BF50077B5AA /* PaymentTests.swift in Sources */, C49EF0422BE23BF50077B5AA /* PaymentTests.swift in Sources */,
C4D477992CB6704C0077713D /* SynchronizationTests.swift in Sources */,
C425D4122B6D249E002A7B48 /* PadelClubTests.swift in Sources */, C425D4122B6D249E002A7B48 /* PadelClubTests.swift in Sources */,
C411C9C92BF219CB003017AD /* UserDataTests.swift in Sources */, C411C9C92BF219CB003017AD /* UserDataTests.swift in Sources */,
C411C9C32BEBA453003017AD /* ServerDataTests.swift in Sources */, C411C9C32BEBA453003017AD /* ServerDataTests.swift in Sources */,
@ -2675,7 +2679,7 @@
FFBA2D2D2CA2CE9E00D5BBDD /* CodingContainer+Extensions.swift in Sources */, FFBA2D2D2CA2CE9E00D5BBDD /* CodingContainer+Extensions.swift in Sources */,
FF4CBFFB2C996C0600151637 /* MatchFormatStorageView.swift in Sources */, FF4CBFFB2C996C0600151637 /* MatchFormatStorageView.swift in Sources */,
FF4CBFFC2C996C0600151637 /* UmpireView.swift in Sources */, FF4CBFFC2C996C0600151637 /* UmpireView.swift in Sources */,
FF4CBFFD2C996C0600151637 /* User.swift in Sources */, FF4CBFFD2C996C0600151637 /* CustomUser.swift in Sources */,
FF4CBFFE2C996C0600151637 /* MatchSummaryView.swift in Sources */, FF4CBFFE2C996C0600151637 /* MatchSummaryView.swift in Sources */,
FF4CBFFF2C996C0600151637 /* TournamentDurationManagerView.swift in Sources */, FF4CBFFF2C996C0600151637 /* TournamentDurationManagerView.swift in Sources */,
FF4CC0002C996C0600151637 /* MockData.swift in Sources */, FF4CC0002C996C0600151637 /* MockData.swift in Sources */,
@ -2922,7 +2926,7 @@
FF70FB792C90584900129CC2 /* TournamentScheduleView.swift in Sources */, FF70FB792C90584900129CC2 /* TournamentScheduleView.swift in Sources */,
FF70FB7A2C90584900129CC2 /* MatchFormatStorageView.swift in Sources */, FF70FB7A2C90584900129CC2 /* MatchFormatStorageView.swift in Sources */,
FF70FB7B2C90584900129CC2 /* UmpireView.swift in Sources */, FF70FB7B2C90584900129CC2 /* UmpireView.swift in Sources */,
FF70FB7C2C90584900129CC2 /* User.swift in Sources */, FF70FB7C2C90584900129CC2 /* CustomUser.swift in Sources */,
C4C33F772C9B1ED4006316DE /* CodingContainer+Extensions.swift in Sources */, C4C33F772C9B1ED4006316DE /* CodingContainer+Extensions.swift in Sources */,
FF70FB7D2C90584900129CC2 /* MatchSummaryView.swift in Sources */, FF70FB7D2C90584900129CC2 /* MatchSummaryView.swift in Sources */,
FF70FB7E2C90584900129CC2 /* TournamentDurationManagerView.swift in Sources */, FF70FB7E2C90584900129CC2 /* TournamentDurationManagerView.swift in Sources */,

@ -10,22 +10,15 @@ import SwiftUI
import LeStorage import LeStorage
@Observable @Observable
final class Club : ModelObject, Storable, Hashable { final class Club: ModelObject, SyncedStorable {
static func resourceName() -> String { return "clubs" } static func resourceName() -> String { return "clubs" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [.get] } static func tokenExemptedMethods() -> [HTTPMethod] { return [.get] }
static func filterByStoreIdentifier() -> Bool { return false } static func filterByStoreIdentifier() -> Bool { return false }
static var relationshipNames: [String] = [] 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 id: String = Store.randomId()
var lastUpdate: Date
var creator: String? var creator: String?
var name: String var name: String
var acronym: String var acronym: String
@ -41,7 +34,11 @@ final class Club : ModelObject, Storable, Hashable {
var broadcastCode: String? var broadcastCode: String?
// var alphabeticalName: Bool = false // var alphabeticalName: Bool = false
var storeId: String? { return nil }
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) { 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.lastUpdate = Date()
self.name = name self.name = name
self.creator = creator self.creator = creator
self.acronym = acronym ?? name.acronym() self.acronym = acronym ?? name.acronym()
@ -54,8 +51,14 @@ final class Club : ModelObject, Storable, Hashable {
self.longitude = longitude self.longitude = longitude
self.courtCount = courtCount self.courtCount = courtCount
self.broadcastCode = broadcastCode self.broadcastCode = broadcastCode
super.init()
} }
// required init(from decoder: any Decoder) throws {
// fatalError("init(from:) has not been implemented")
// }
override func copyFromServerInstance(_ instance: any Storable) -> Bool { override func copyFromServerInstance(_ instance: any Storable) -> Bool {
guard let copy = instance as? Club else { return false } guard let copy = instance as? Club else { return false }
self.broadcastCode = copy.broadcastCode self.broadcastCode = copy.broadcastCode
@ -80,16 +83,17 @@ final class Club : ModelObject, Storable, Hashable {
DataStore.shared.courts.filter { $0.club == self.id }.sorted(by: \.index) DataStore.shared.courts.filter { $0.club == self.id }.sorted(by: \.index)
} }
override func deleteDependencies() throws { override func deleteDependencies() {
let customizedCourts = self.customizedCourts let customizedCourts = self.customizedCourts
for customizedCourt in customizedCourts { for customizedCourt in customizedCourts {
try customizedCourt.deleteDependencies() customizedCourt.deleteDependencies()
} }
DataStore.shared.courts.deleteDependencies(customizedCourts) DataStore.shared.courts.deleteDependencies(customizedCourts)
} }
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case _id = "id" case _id = "id"
case _lastUpdate = "lastUpdate"
case _creator = "creator" case _creator = "creator"
case _name = "name" case _name = "name"
case _acronym = "acronym" case _acronym = "acronym"
@ -106,9 +110,11 @@ final class Club : ModelObject, Storable, Hashable {
} }
func encode(to encoder: Encoder) throws { func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: ._id) try container.encode(id, forKey: ._id)
try container.encode(lastUpdate, forKey: ._lastUpdate)
try container.encode(creator, forKey: ._creator) try container.encode(creator, forKey: ._creator)
try container.encode(name, forKey: ._name) try container.encode(name, forKey: ._name)
try container.encode(acronym, forKey: ._acronym) try container.encode(acronym, forKey: ._acronym)

@ -10,7 +10,8 @@ import SwiftUI
import LeStorage import LeStorage
@Observable @Observable
final class Court : ModelObject, Storable, Hashable { final class Court : ModelObject, SyncedStorable {
static func resourceName() -> String { return "courts" } static func resourceName() -> String { return "courts" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
static func filterByStoreIdentifier() -> Bool { return false } static func filterByStoreIdentifier() -> Bool { return false }
@ -20,31 +21,25 @@ final class Court : ModelObject, Storable, Hashable {
lhs.id == rhs.id lhs.id == rhs.id
} }
func hash(into hasher: inout Hasher) {
return hasher.combine(id)
}
var id: String = Store.randomId() var id: String = Store.randomId()
var lastUpdate: Date
var index: Int var index: Int
var club: String var club: String
var name: String? var name: String?
var exitAllowed: Bool = false var exitAllowed: Bool = false
var indoor: Bool = false var indoor: Bool = false
var storeId: String? { return nil }
init(index: Int, club: String, name: String? = nil, exitAllowed: Bool = false, indoor: Bool = false) { init(index: Int, club: String, name: String? = nil, exitAllowed: Bool = false, indoor: Bool = false) {
self.index = index self.index = index
self.lastUpdate = Date()
self.club = club self.club = club
self.name = name self.name = name
self.exitAllowed = exitAllowed self.exitAllowed = exitAllowed
self.indoor = indoor self.indoor = indoor
} }
// internal init(club: String, name: String? = nil, index: Int) {
// self.club = club
// self.name = name
// self.index = index
// }
func courtTitle() -> String { func courtTitle() -> String {
self.name ?? courtIndexTitle() self.name ?? courtIndexTitle()
} }
@ -61,11 +56,12 @@ final class Court : ModelObject, Storable, Hashable {
Store.main.findById(club) Store.main.findById(club)
} }
override func deleteDependencies() throws { override func deleteDependencies() {
} }
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case _id = "id" case _id = "id"
case _lastUpdate = "lastUpdate"
case _index = "index" case _index = "index"
case _club = "club" case _club = "club"
case _name = "name" case _name = "name"
@ -77,6 +73,7 @@ final class Court : ModelObject, Storable, Hashable {
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: ._id) try container.encode(id, forKey: ._id)
try container.encode(lastUpdate, forKey: ._lastUpdate)
try container.encode(index, forKey: ._index) try container.encode(index, forKey: ._index)
try container.encode(club, forKey: ._club) try container.encode(club, forKey: ._club)
try container.encode(name, forKey: ._name) try container.encode(name, forKey: ._name)

@ -15,7 +15,7 @@ enum UserRight: Int, Codable {
} }
@Observable @Observable
class User: ModelObject, UserBase, Storable { class CustomUser: ModelObject, UserBase, SyncedStorable {
static func resourceName() -> String { "users" } static func resourceName() -> String { "users" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [.post] } static func tokenExemptedMethods() -> [HTTPMethod] { return [.post] }
@ -23,6 +23,7 @@ class User: ModelObject, UserBase, Storable {
static var relationshipNames: [String] = [] static var relationshipNames: [String] = []
public var id: String = Store.randomId() public var id: String = Store.randomId()
var lastUpdate: Date
public var username: String public var username: String
public var email: String public var email: String
var clubs: [String] = [] var clubs: [String] = []
@ -47,7 +48,10 @@ class User: ModelObject, UserBase, Storable {
var deviceId: String? var deviceId: String?
var storeId: String? { return nil }
init(username: String, email: String, firstName: String, lastName: String, phone: String?, country: String?, loserBracketMode: LoserBracketMode = .automatic) { init(username: String, email: String, firstName: String, lastName: String, phone: String?, country: String?, loserBracketMode: LoserBracketMode = .automatic) {
self.lastUpdate = Date()
self.username = username self.username = username
self.firstName = firstName self.firstName = firstName
self.lastName = lastName self.lastName = lastName
@ -121,6 +125,7 @@ class User: ModelObject, UserBase, Storable {
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case _id = "id" case _id = "id"
case _lastUpdate = "lastUpdate"
case _username = "username" case _username = "username"
case _email = "email" case _email = "email"
case _clubs = "clubs" case _clubs = "clubs"
@ -149,6 +154,7 @@ class User: ModelObject, UserBase, Storable {
// Required properties // Required properties
id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId() id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId()
lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date()
username = try container.decode(String.self, forKey: ._username) username = try container.decode(String.self, forKey: ._username)
email = try container.decode(String.self, forKey: ._email) email = try container.decode(String.self, forKey: ._email)
firstName = try container.decode(String.self, forKey: ._firstName) firstName = try container.decode(String.self, forKey: ._firstName)
@ -181,6 +187,7 @@ class User: ModelObject, UserBase, Storable {
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: ._id) try container.encode(id, forKey: ._id)
try container.encode(lastUpdate, forKey: ._lastUpdate)
try container.encode(username, forKey: ._username) try container.encode(username, forKey: ._username)
try container.encode(email, forKey: ._email) try container.encode(email, forKey: ._email)
try container.encode(clubs, forKey: ._clubs) try container.encode(clubs, forKey: ._clubs)
@ -207,15 +214,15 @@ class User: ModelObject, UserBase, Storable {
try container.encode(loserBracketMode, forKey: ._loserBracketMode) try container.encode(loserBracketMode, forKey: ._loserBracketMode)
} }
static func placeHolder() -> User { static func placeHolder() -> CustomUser {
return User(username: "", email: "", firstName: "", lastName: "", phone: nil, country: nil) return CustomUser(username: "", email: "", firstName: "", lastName: "", phone: nil, country: nil)
} }
} }
class UserCreationForm: User, UserPasswordBase { class UserCreationForm: CustomUser, UserPasswordBase {
init(user: User, username: String, password: String, firstName: String, lastName: String, email: String, phone: String?, country: String?) { init(user: CustomUser, username: String, password: String, firstName: String, lastName: String, email: String, phone: String?, country: String?) {
self.password = password self.password = password
super.init(username: username, email: email, firstName: firstName, lastName: lastName, phone: phone, country: country) super.init(username: username, email: email, firstName: firstName, lastName: lastName, phone: phone, country: country)

@ -13,7 +13,7 @@ class DataStore: ObservableObject {
static let shared = DataStore() static let shared = DataStore()
@Published var user: User = User.placeHolder() { @Published var user: CustomUser = CustomUser.placeHolder() {
didSet { didSet {
let loggedUser = StoreCenter.main.userId != nil let loggedUser = StoreCenter.main.userId != nil
StoreCenter.main.collectionsCanSynchronize = loggedUser StoreCenter.main.collectionsCanSynchronize = loggedUser
@ -22,7 +22,7 @@ class DataStore: ObservableObject {
if self.user.id != self.userStorage.item()?.id { if self.user.id != self.userStorage.item()?.id {
self.userStorage.setItemNoSync(self.user) self.userStorage.setItemNoSync(self.user)
if StoreCenter.main.collectionsCanSynchronize { if StoreCenter.main.collectionsCanSynchronize {
Store.main.loadCollectionsFromServer() StoreCenter.main.initialSynchronization()
self._fixMissingClubCreatorIfNecessary(self.clubs) self._fixMissingClubCreatorIfNecessary(self.clubs)
self._fixMissingEventCreatorIfNecessary(self.events) self._fixMissingEventCreatorIfNecessary(self.events)
} }
@ -41,9 +41,9 @@ class DataStore: ObservableObject {
fileprivate(set) var dateIntervals: StoredCollection<DateInterval> fileprivate(set) var dateIntervals: StoredCollection<DateInterval>
fileprivate(set) var purchases: StoredCollection<Purchase> fileprivate(set) var purchases: StoredCollection<Purchase>
fileprivate var userStorage: StoredSingleton<User> fileprivate var userStorage: StoredSingleton<CustomUser>
fileprivate var _temporaryLocalUser: OptionalStorage<User> = OptionalStorage(fileName: "tmp_local_user.json") fileprivate var _temporaryLocalUser: OptionalStorage<CustomUser> = OptionalStorage(fileName: "tmp_local_user.json")
fileprivate(set) var appSettingsStorage: MicroStorage<AppSettings> = MicroStorage(fileName: "appsettings.json") fileprivate(set) var appSettingsStorage: MicroStorage<AppSettings> = MicroStorage(fileName: "appsettings.json")
var appSettings: AppSettings { var appSettings: AppSettings {
@ -75,18 +75,20 @@ class DataStore: ObservableObject {
} }
#endif #endif
StoreCenter.main.forceNoSynchronization = !synchronized
Logger.log("Sync URL: \(StoreCenter.main.synchronizationApiURL ?? "none"), sync: \(synchronized) ") Logger.log("Sync URL: \(StoreCenter.main.synchronizationApiURL ?? "none"), sync: \(synchronized) ")
let indexed: Bool = true let indexed: Bool = true
self.clubs = store.registerCollection(synchronized: synchronized, indexed: indexed) self.clubs = store.registerSynchronizedCollection(indexed: indexed)
self.courts = store.registerCollection(synchronized: synchronized, indexed: indexed) self.courts = store.registerSynchronizedCollection(indexed: indexed)
self.tournaments = store.registerCollection(synchronized: synchronized, indexed: indexed) self.tournaments = store.registerSynchronizedCollection(indexed: indexed)
self.events = store.registerCollection(synchronized: synchronized, indexed: indexed) self.events = store.registerSynchronizedCollection(indexed: indexed)
self.monthData = store.registerCollection(synchronized: false, indexed: indexed) self.dateIntervals = store.registerSynchronizedCollection(indexed: indexed)
self.dateIntervals = store.registerCollection(synchronized: synchronized, indexed: indexed)
self.userStorage = store.registerObject(synchronized: synchronized) self.userStorage = store.registerObject(synchronized: synchronized)
self.purchases = Store.main.registerCollection(synchronized: true, inMemory: true) self.purchases = Store.main.registerSynchronizedCollection(inMemory: true)
self.monthData = store.registerCollection(indexed: indexed)
// Load ApiCallCollection, making them restart at launch and deletable on disconnect // Load ApiCallCollection, making them restart at launch and deletable on disconnect
StoreCenter.main.loadApiCallCollection(type: GroupStage.self) StoreCenter.main.loadApiCallCollection(type: GroupStage.self)
@ -102,14 +104,10 @@ class DataStore: ObservableObject {
} }
func saveUser() { func saveUser() {
do { if user.username.count > 0 {
if user.username.count > 0 { self.userStorage.update()
try self.userStorage.update() } else {
} else { self._temporaryLocalUser.item = self.user
self._temporaryLocalUser.item = self.user
}
} catch {
Logger.error(error)
} }
} }
@ -119,8 +117,8 @@ class DataStore: ObservableObject {
self.objectWillChange.send() self.objectWillChange.send()
} }
if let userSingleton: StoredSingleton<User> = notification.object as? StoredSingleton<User> { if let userSingleton: StoredSingleton<CustomUser> = notification.object as? StoredSingleton<CustomUser> {
self.user = userSingleton.item() ?? self._temporaryLocalUser.item ?? User.placeHolder() self.user = userSingleton.item() ?? self._temporaryLocalUser.item ?? CustomUser.placeHolder()
} else if let clubsCollection: StoredCollection<Club> = notification.object as? StoredCollection<Club> { } else if let clubsCollection: StoredCollection<Club> = notification.object as? StoredCollection<Club> {
self._fixMissingClubCreatorIfNecessary(clubsCollection) self._fixMissingClubCreatorIfNecessary(clubsCollection)
} else if let eventsCollection: StoredCollection<Event> = notification.object as? StoredCollection<Event> { } else if let eventsCollection: StoredCollection<Event> = notification.object as? StoredCollection<Event> {
@ -134,17 +132,13 @@ class DataStore: ObservableObject {
} }
fileprivate func _fixMissingClubCreatorIfNecessary(_ clubsCollection: StoredCollection<Club>) { fileprivate func _fixMissingClubCreatorIfNecessary(_ clubsCollection: StoredCollection<Club>) {
do { for club in clubsCollection {
for club in clubsCollection { if let userId = StoreCenter.main.userId, club.creator == nil {
if let userId = StoreCenter.main.userId, club.creator == nil { club.creator = userId
club.creator = userId self.userStorage.item()?.addClub(club)
self.userStorage.item()?.addClub(club) self.userStorage.update()
try self.userStorage.update() clubsCollection.writeChangeAndInsertOnServer(instance: club)
clubsCollection.writeChangeAndInsertOnServer(instance: club)
}
} }
} catch {
Logger.error(error)
} }
} }
@ -221,7 +215,7 @@ class DataStore: ObservableObject {
Guard.main.disconnect() Guard.main.disconnect()
StoreCenter.main.disconnect() StoreCenter.main.disconnect()
self.user = self._temporaryLocalUser.item ?? User.placeHolder() self.user = self._temporaryLocalUser.item ?? CustomUser.placeHolder()
self.user.clubs.removeAll() self.user.clubs.removeAll()
// done after because otherwise folders remain // done after because otherwise folders remain
@ -240,7 +234,7 @@ class DataStore: ObservableObject {
let login = PListReader.readString(plist: "local", key: "username"), let login = PListReader.readString(plist: "local", key: "username"),
let pass = PListReader.readString(plist: "local", key: "password") { let pass = PListReader.readString(plist: "local", key: "password") {
let service = Services(url: url) let service = Services(url: url)
let _: User = try await service.login(username: login, password: pass) let _: CustomUser = try await service.login(username: login, password: pass)
tournament.event = nil tournament.event = nil
_ = try await service.post(tournament) _ = try await service.post(tournament)

@ -10,19 +10,22 @@ import SwiftUI
import LeStorage import LeStorage
@Observable @Observable
final class DateInterval: ModelObject, Storable { final class DateInterval: ModelObject, SyncedStorable {
static func resourceName() -> String { return "date-intervals" } static func resourceName() -> String { return "date-intervals" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
static func filterByStoreIdentifier() -> Bool { return false } static func filterByStoreIdentifier() -> Bool { return false }
static var relationshipNames: [String] = [] static var relationshipNames: [String] = []
var id: String = Store.randomId() var id: String = Store.randomId()
var lastUpdate: Date
var event: String var event: String
var courtIndex: Int var courtIndex: Int
var startDate: Date var startDate: Date
var endDate: Date var endDate: Date
internal init(event: String, courtIndex: Int, startDate: Date, endDate: Date) { internal init(event: String, courtIndex: Int, startDate: Date, endDate: Date) {
self.lastUpdate = Date()
self.event = event self.event = event
self.courtIndex = courtIndex self.courtIndex = courtIndex
self.startDate = startDate self.startDate = startDate
@ -45,11 +48,12 @@ final class DateInterval: ModelObject, Storable {
date <= startDate && date <= endDate && date >= startDate && date >= endDate date <= startDate && date <= endDate && date >= startDate && date >= endDate
} }
override func deleteDependencies() throws { override func deleteDependencies() {
} }
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case _id = "id" case _id = "id"
case _lastUpdate = "lastUpdate"
case _event = "event" case _event = "event"
case _courtIndex = "courtIndex" case _courtIndex = "courtIndex"
case _startDate = "startDate" case _startDate = "startDate"
@ -57,7 +61,7 @@ final class DateInterval: ModelObject, Storable {
} }
func insertOnServer() throws { func insertOnServer() throws {
try DataStore.shared.dateIntervals.writeChangeAndInsertOnServer(instance: self) DataStore.shared.dateIntervals.writeChangeAndInsertOnServer(instance: self)
} }
} }

@ -10,7 +10,7 @@ import LeStorage
import SwiftUI import SwiftUI
@Observable @Observable
final class Event: ModelObject, Storable { final class Event: ModelObject, SyncedStorable {
static func resourceName() -> String { return "events" } static func resourceName() -> String { return "events" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
@ -18,30 +18,34 @@ final class Event: ModelObject, Storable {
static var relationshipNames: [String] = [] static var relationshipNames: [String] = []
var id: String = Store.randomId() var id: String = Store.randomId()
var lastUpdate: Date
var creator: String? var creator: String?
var club: String? var club: String?
var creationDate: Date = Date() var creationDate: Date = Date()
var name: String? var name: String?
var tenupId: String? var tenupId: String?
var storeId: String? { return nil }
internal init(creator: String? = nil, club: String? = nil, name: String? = nil, tenupId: String? = nil) { internal init(creator: String? = nil, club: String? = nil, name: String? = nil, tenupId: String? = nil) {
self.lastUpdate = Date()
self.creator = creator self.creator = creator
self.club = club self.club = club
self.name = name self.name = name
self.tenupId = tenupId self.tenupId = tenupId
} }
override func deleteDependencies() throws { override func deleteDependencies() {
let tournaments = self.tournaments let tournaments = self.tournaments
for tournament in tournaments { for tournament in tournaments {
try tournament.deleteDependencies() tournament.deleteDependencies()
} }
DataStore.shared.tournaments.deleteDependencies(tournaments) DataStore.shared.tournaments.deleteDependencies(tournaments)
let courtsUnavailabilities = self.courtsUnavailability let courtsUnavailabilities = self.courtsUnavailability
for courtsUnavailability in courtsUnavailabilities { for courtsUnavailability in courtsUnavailabilities {
try courtsUnavailability.deleteDependencies() courtsUnavailability.deleteDependencies()
} }
DataStore.shared.dateIntervals.deleteDependencies(courtsUnavailabilities) DataStore.shared.dateIntervals.deleteDependencies(courtsUnavailabilities)
} }
@ -94,7 +98,7 @@ final class Event: ModelObject, Storable {
} }
func insertOnServer() throws { func insertOnServer() throws {
try DataStore.shared.events.writeChangeAndInsertOnServer(instance: self) DataStore.shared.events.writeChangeAndInsertOnServer(instance: self)
for tournament in self.tournaments { for tournament in self.tournaments {
try tournament.insertOnServer() try tournament.insertOnServer()
} }
@ -109,6 +113,7 @@ final class Event: ModelObject, Storable {
extension Event { extension Event {
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case _id = "id" case _id = "id"
case _lastUpdate = "lastUpdate"
case _creator = "creator" case _creator = "creator"
case _club = "club" case _club = "club"
case _creationDate = "creationDate" case _creationDate = "creationDate"
@ -120,6 +125,7 @@ extension Event {
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: ._id) try container.encode(id, forKey: ._id)
try container.encode(lastUpdate, forKey: ._lastUpdate)
try container.encode(creator, forKey: ._creator) try container.encode(creator, forKey: ._creator)
try container.encode(club, forKey: ._club) try container.encode(club, forKey: ._club)
try container.encode(creationDate, forKey: ._creationDate) try container.encode(creationDate, forKey: ._creationDate)

@ -11,13 +11,15 @@ import Algorithms
import SwiftUI import SwiftUI
@Observable @Observable
final class GroupStage: ModelObject, Storable { final class GroupStage: ModelObject, SyncedStorable, SideStorable {
static func resourceName() -> String { "group-stages" } static func resourceName() -> String { "group-stages" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
static func filterByStoreIdentifier() -> Bool { return true } static func filterByStoreIdentifier() -> Bool { return true }
static var relationshipNames: [String] = [] static var relationshipNames: [String] = []
var id: String = Store.randomId() var id: String = Store.randomId()
var lastUpdate: Date
var tournament: String var tournament: String
var index: Int var index: Int
var size: Int var size: Int
@ -35,7 +37,10 @@ final class GroupStage: ModelObject, Storable {
} }
} }
var storeId: String? = nil
internal init(tournament: String, index: Int, size: Int, matchFormat: MatchFormat? = nil, startDate: Date? = nil, name: String? = nil, step: Int = 0) { internal init(tournament: String, index: Int, size: Int, matchFormat: MatchFormat? = nil, startDate: Date? = nil, name: String? = nil, step: Int = 0) {
self.lastUpdate = Date()
self.tournament = tournament self.tournament = tournament
self.index = index self.index = index
self.size = size self.size = size
@ -128,12 +133,8 @@ final class GroupStage: ModelObject, Storable {
matches.append(newMatch) matches.append(newMatch)
} }
do { self.tournamentStore.matches.addOrUpdate(contentOfs: matches)
try self.tournamentStore.matches.addOrUpdate(contentOfs: matches) self.tournamentStore.teamScores.addOrUpdate(contentOfs: teamScores)
try self.tournamentStore.teamScores.addOrUpdate(contentOfs: teamScores)
} catch {
Logger.error(error)
}
} }
func playedMatches() -> [Match] { func playedMatches() -> [Match] {
@ -151,19 +152,15 @@ final class GroupStage: ModelObject, Storable {
clearScoreCache() clearScoreCache()
if hasEnded(), let tournament = tournamentObject() { if hasEnded(), let tournament = tournamentObject() {
do { let teams = teams(true)
let teams = teams(true) for (index, team) in teams.enumerated() {
for (index, team) in teams.enumerated() { team.qualified = index < tournament.qualifiedPerGroupStage
team.qualified = index < tournament.qualifiedPerGroupStage if team.bracketPosition != nil && team.qualified == false {
if team.bracketPosition != nil && team.qualified == false { tournamentObject()?.resetTeamScores(in: team.bracketPosition)
tournamentObject()?.resetTeamScores(in: team.bracketPosition) team.bracketPosition = nil
team.bracketPosition = nil
}
} }
try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams)
} catch {
Logger.error(error)
} }
self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams)
let groupStagesAreOverAtFirstStep = tournament.groupStagesAreOver(atStep: 0) let groupStagesAreOverAtFirstStep = tournament.groupStagesAreOver(atStep: 0)
let nextStepGroupStages = tournament.groupStages(atStep: 1) let nextStepGroupStages = tournament.groupStages(atStep: 1)
@ -171,11 +168,7 @@ final class GroupStage: ModelObject, Storable {
if groupStagesAreOverAtFirstStep, nextStepGroupStages.isEmpty || groupStagesAreOverAtSecondStep == true, tournament.groupStageLoserBracketAreOver(), tournament.rounds().isEmpty { if groupStagesAreOverAtFirstStep, nextStepGroupStages.isEmpty || groupStagesAreOverAtSecondStep == true, tournament.groupStageLoserBracketAreOver(), tournament.rounds().isEmpty {
tournament.endDate = Date() tournament.endDate = Date()
do { DataStore.shared.tournaments.addOrUpdate(instance: tournament)
try DataStore.shared.tournaments.addOrUpdate(instance: tournament)
} catch {
Logger.error(error)
}
} }
} }
} }
@ -332,11 +325,7 @@ final class GroupStage: ModelObject, Storable {
} }
private func _removeMatches() { private func _removeMatches() {
do { self.tournamentStore.matches.delete(contentOfs: _matches())
try self.tournamentStore.matches.delete(contentOfs: _matches())
} catch {
Logger.error(error)
}
} }
private func _numberOfMatchesToBuild() -> Int { private func _numberOfMatchesToBuild() -> Int {
@ -485,11 +474,7 @@ final class GroupStage: ModelObject, Storable {
playedMatches.forEach { match in playedMatches.forEach { match in
match.matchFormat = matchFormat match.matchFormat = matchFormat
} }
do { self.tournamentStore.matches.addOrUpdate(contentOfs: playedMatches)
try self.tournamentStore.matches.addOrUpdate(contentOfs: playedMatches)
} catch {
Logger.error(error)
}
} }
func pasteData() -> String { func pasteData() -> String {
@ -507,10 +492,10 @@ final class GroupStage: ModelObject, Storable {
return teams(true).firstIndex(of: team) return teams(true).firstIndex(of: team)
} }
override func deleteDependencies() throws { override func deleteDependencies() {
let matches = self._matches() let matches = self._matches()
for match in matches { for match in matches {
try match.deleteDependencies() match.deleteDependencies()
} }
self.tournamentStore.matches.deleteDependencies(matches) self.tournamentStore.matches.deleteDependencies(matches)
} }
@ -519,6 +504,7 @@ final class GroupStage: ModelObject, Storable {
let container = try decoder.container(keyedBy: CodingKeys.self) let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: ._id) id = try container.decode(String.self, forKey: ._id)
lastUpdate = try container.decode(Date.self, forKey: ._lastUpdate)
tournament = try container.decode(String.self, forKey: ._tournament) tournament = try container.decode(String.self, forKey: ._tournament)
index = try container.decode(Int.self, forKey: ._index) index = try container.decode(Int.self, forKey: ._index)
size = try container.decode(Int.self, forKey: ._size) size = try container.decode(Int.self, forKey: ._size)
@ -532,6 +518,8 @@ final class GroupStage: ModelObject, Storable {
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: ._id) try container.encode(id, forKey: ._id)
try container.encode(storeId, forKey: ._storeId)
try container.encode(lastUpdate, forKey: ._lastUpdate)
try container.encode(tournament, forKey: ._tournament) try container.encode(tournament, forKey: ._tournament)
try container.encode(index, forKey: ._index) try container.encode(index, forKey: ._index)
try container.encode(size, forKey: ._size) try container.encode(size, forKey: ._size)
@ -553,6 +541,8 @@ final class GroupStage: ModelObject, Storable {
extension GroupStage { extension GroupStage {
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case _id = "id" case _id = "id"
case _storeId = "storeId"
case _lastUpdate = "lastUpdate"
case _tournament = "tournament" case _tournament = "tournament"
case _index = "index" case _index = "index"
case _size = "size" case _size = "size"

@ -9,7 +9,7 @@ import Foundation
import LeStorage import LeStorage
@Observable @Observable
final class Match: ModelObject, Storable { final class Match: ModelObject, SyncedStorable, SideStorable {
static func resourceName() -> String { "matches" } static func resourceName() -> String { "matches" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
static func filterByStoreIdentifier() -> Bool { return true } static func filterByStoreIdentifier() -> Bool { return true }
@ -23,6 +23,7 @@ final class Match: ModelObject, Storable {
var byeState: Bool = false var byeState: Bool = false
var id: String = Store.randomId() var id: String = Store.randomId()
var lastUpdate: Date
var round: String? var round: String?
var groupStage: String? var groupStage: String?
var startDate: Date? var startDate: Date?
@ -40,7 +41,10 @@ final class Match: ModelObject, Storable {
private(set) var courtIndex: Int? private(set) var courtIndex: Int?
var confirmed: Bool = false var confirmed: Bool = false
var storeId: String? = nil
init(round: String? = nil, groupStage: String? = nil, startDate: Date? = nil, endDate: Date? = nil, index: Int, matchFormat: MatchFormat? = nil, servingTeamId: String? = nil, winningTeamId: String? = nil, losingTeamId: String? = nil, name: String? = nil, disabled: Bool = false, courtIndex: Int? = nil, confirmed: Bool = false) { init(round: String? = nil, groupStage: String? = nil, startDate: Date? = nil, endDate: Date? = nil, index: Int, matchFormat: MatchFormat? = nil, servingTeamId: String? = nil, winningTeamId: String? = nil, losingTeamId: String? = nil, name: String? = nil, disabled: Bool = false, courtIndex: Int? = nil, confirmed: Bool = false) {
self.lastUpdate = Date()
self.round = round self.round = round
self.groupStage = groupStage self.groupStage = groupStage
self.startDate = startDate self.startDate = startDate
@ -78,13 +82,13 @@ final class Match: ModelObject, Storable {
// MARK: - // MARK: -
override func deleteDependencies() throws { override func deleteDependencies() {
guard let tournament = self.currentTournament() else { guard let tournament = self.currentTournament() else {
return return
} }
let teamScores = self.teamScores let teamScores = self.teamScores
for teamScore in teamScores { for teamScore in teamScores {
try teamScore.deleteDependencies() teamScore.deleteDependencies()
} }
tournament.tournamentStore.teamScores.deleteDependencies(teamScores) tournament.tournamentStore.teamScores.deleteDependencies(teamScores)
} }
@ -207,11 +211,7 @@ defer {
endDate = nil endDate = nil
followingMatch()?.cleanScheduleAndSave(nil) followingMatch()?.cleanScheduleAndSave(nil)
_loserMatch()?.cleanScheduleAndSave(nil) _loserMatch()?.cleanScheduleAndSave(nil)
do { self.tournamentStore.matches.addOrUpdate(instance: self)
try self.tournamentStore.matches.addOrUpdate(instance: self)
} catch {
Logger.error(error)
}
} }
func resetMatch() { func resetMatch() {
@ -227,22 +227,14 @@ defer {
func resetScores() { func resetScores() {
teamScores.forEach({ $0.score = nil }) teamScores.forEach({ $0.score = nil })
do { self.tournamentStore.teamScores.addOrUpdate(contentOfs: teamScores)
try self.tournamentStore.teamScores.addOrUpdate(contentOfs: teamScores)
} catch {
Logger.error(error)
}
} }
func teamWillBeWalkOut(_ team: TeamRegistration) { func teamWillBeWalkOut(_ team: TeamRegistration) {
resetMatch() resetMatch()
let existingTeamScore = teamScore(ofTeam: team) ?? TeamScore(match: id, team: team) let existingTeamScore = teamScore(ofTeam: team) ?? TeamScore(match: id, team: team)
existingTeamScore.walkOut = 1 existingTeamScore.walkOut = 1
do { self.tournamentStore.teamScores.addOrUpdate(instance: existingTeamScore)
try self.tournamentStore.teamScores.addOrUpdate(instance: existingTeamScore)
} catch {
Logger.error(error)
}
} }
func luckyLosers() -> [TeamRegistration] { func luckyLosers() -> [TeamRegistration] {
@ -260,19 +252,11 @@ defer {
let position = matchIndex * 2 + teamPosition.rawValue let position = matchIndex * 2 + teamPosition.rawValue
let previousScores = teamScores.filter({ $0.luckyLoser == position }) let previousScores = teamScores.filter({ $0.luckyLoser == position })
do { self.tournamentStore.teamScores.delete(contentOfs: previousScores)
try self.tournamentStore.teamScores.delete(contentOfs: previousScores)
} catch {
Logger.error(error)
}
let teamScoreLuckyLoser = teamScore(ofTeam: team) ?? TeamScore(match: id, team: team) let teamScoreLuckyLoser = teamScore(ofTeam: team) ?? TeamScore(match: id, team: team)
teamScoreLuckyLoser.luckyLoser = position teamScoreLuckyLoser.luckyLoser = position
do { self.tournamentStore.teamScores.addOrUpdate(instance: teamScoreLuckyLoser)
try self.tournamentStore.teamScores.addOrUpdate(instance: teamScoreLuckyLoser)
} catch {
Logger.error(error)
}
} }
func disableMatch() { func disableMatch() {
@ -373,32 +357,19 @@ defer {
disabled = state disabled = state
if disabled { if disabled {
do { self.tournamentStore.teamScores.delete(contentOfs: teamScores)
try self.tournamentStore.teamScores.delete(contentOfs: teamScores)
} catch {
Logger.error(error)
}
} }
if state == true { if state == true {
let teams = teams() let teams = teams()
for team in teams { for team in teams {
if isSeededBy(team: team) { if isSeededBy(team: team) {
team.bracketPosition = nil team.bracketPosition = nil
do { self.tournamentStore.teamRegistrations.addOrUpdate(instance: team)
try self.tournamentStore.teamRegistrations.addOrUpdate(instance: team)
} catch {
Logger.error(error)
}
} }
} }
} }
//byeState = false //byeState = false
do { self.tournamentStore.matches.addOrUpdate(instance: self)
try self.tournamentStore.matches.addOrUpdate(instance: self)
} catch {
Logger.error(error)
}
if single == false { if single == false {
_toggleLoserMatchDisableState(state) _toggleLoserMatchDisableState(state)
if forward { if forward {
@ -506,11 +477,7 @@ defer {
teamScoreWalkout.walkOut = 0 teamScoreWalkout.walkOut = 0
let teamScoreWinning = teamScore(teamPosition.otherTeam) ?? TeamScore(match: id, team: team(teamPosition.otherTeam)) let teamScoreWinning = teamScore(teamPosition.otherTeam) ?? TeamScore(match: id, team: team(teamPosition.otherTeam))
teamScoreWinning.walkOut = nil teamScoreWinning.walkOut = nil
do { self.tournamentStore.teamScores.addOrUpdate(contentOfs: [teamScoreWalkout, teamScoreWinning])
try self.tournamentStore.teamScores.addOrUpdate(contentOfs: [teamScoreWalkout, teamScoreWinning])
} catch {
Logger.error(error)
}
if endDate == nil { if endDate == nil {
endDate = Date() endDate = Date()
@ -555,11 +522,7 @@ defer {
teamScoreOne.score = matchDescriptor.teamOneScores.joined(separator: ",") teamScoreOne.score = matchDescriptor.teamOneScores.joined(separator: ",")
let teamScoreTwo = teamScore(.two) ?? TeamScore(match: id, team: team(.two)) let teamScoreTwo = teamScore(.two) ?? TeamScore(match: id, team: team(.two))
teamScoreTwo.score = matchDescriptor.teamTwoScores.joined(separator: ",") teamScoreTwo.score = matchDescriptor.teamTwoScores.joined(separator: ",")
do { self.tournamentStore.teamScores.addOrUpdate(contentOfs: [teamScoreOne, teamScoreTwo])
try self.tournamentStore.teamScores.addOrUpdate(contentOfs: [teamScoreOne, teamScoreTwo])
} catch {
Logger.error(error)
}
matchFormat = matchDescriptor.matchFormat matchFormat = matchDescriptor.matchFormat
} }
@ -572,11 +535,7 @@ defer {
let ids = newTeamScores.map { $0.id } let ids = newTeamScores.map { $0.id }
let teamScores = teamScores.filter({ ids.contains($0.id) == false }) let teamScores = teamScores.filter({ ids.contains($0.id) == false })
if teamScores.isEmpty == false { if teamScores.isEmpty == false {
do { self.tournamentStore.teamScores.delete(contentOfs: teamScores)
try self.tournamentStore.teamScores.delete(contentOfs: teamScores)
} catch {
Logger.error(error)
}
followingMatch()?.resetTeamScores(outsideOf: []) followingMatch()?.resetTeamScores(outsideOf: [])
_loserMatch()?.resetTeamScores(outsideOf: []) _loserMatch()?.resetTeamScores(outsideOf: [])
} }
@ -598,11 +557,8 @@ defer {
func updateTeamScores() { func updateTeamScores() {
let teams = getOrCreateTeamScores() let teams = getOrCreateTeamScores()
do {
try self.tournamentStore.teamScores.addOrUpdate(contentOfs: teams) self.tournamentStore.teamScores.addOrUpdate(contentOfs: teams)
} catch {
Logger.error(error)
}
resetTeamScores(outsideOf: teams) resetTeamScores(outsideOf: teams)
if teams.isEmpty == false { if teams.isEmpty == false {
updateFollowingMatchTeamScore() updateFollowingMatchTeamScore()
@ -883,6 +839,8 @@ defer {
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case _id = "id" case _id = "id"
case _storeId = "storeId"
case _lastUpdate = "lastUpdate"
case _round = "round" case _round = "round"
case _groupStage = "groupStage" case _groupStage = "groupStage"
case _startDate = "startDate" case _startDate = "startDate"
@ -905,6 +863,8 @@ defer {
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: ._id) try container.encode(id, forKey: ._id)
try container.encode(storeId, forKey: ._storeId)
try container.encode(lastUpdate, forKey: ._lastUpdate)
try container.encode(round, forKey: ._round) try container.encode(round, forKey: ._round)
try container.encode(groupStage, forKey: ._groupStage) try container.encode(groupStage, forKey: ._groupStage)
try container.encode(startDate, forKey: ._startDate) try container.encode(startDate, forKey: ._startDate)
@ -923,7 +883,7 @@ defer {
func insertOnServer() { func insertOnServer() {
self.tournamentStore.matches.writeChangeAndInsertOnServer(instance: self) self.tournamentStore.matches.writeChangeAndInsertOnServer(instance: self)
for teamScore in self.teamScores { for teamScore in self.teamScores {
try teamScore.insertOnServer() teamScore.insertOnServer()
} }
} }

@ -10,7 +10,7 @@ import SwiftUI
import LeStorage import LeStorage
@Observable @Observable
final class MonthData : ModelObject, Storable { final class MonthData: ModelObject, Storable {
static func resourceName() -> String { return "month-data" } static func resourceName() -> String { return "month-data" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
@ -85,7 +85,7 @@ final class MonthData : ModelObject, Storable {
} }
} }
override func deleteDependencies() throws { override func deleteDependencies() {
} }
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {

@ -9,13 +9,14 @@ import Foundation
import LeStorage import LeStorage
@Observable @Observable
final class PlayerRegistration: ModelObject, Storable { final class PlayerRegistration: ModelObject, SyncedStorable, SideStorable {
static func resourceName() -> String { "player-registrations" } static func resourceName() -> String { "player-registrations" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
static func filterByStoreIdentifier() -> Bool { return true } static func filterByStoreIdentifier() -> Bool { return true }
static var relationshipNames: [String] = ["teamRegistration"] static var relationshipNames: [String] = ["teamRegistration"]
var id: String = Store.randomId() var id: String = Store.randomId()
var lastUpdate: Date
var teamRegistration: String? var teamRegistration: String?
var firstName: String var firstName: String
var lastName: String var lastName: String
@ -39,7 +40,10 @@ final class PlayerRegistration: ModelObject, Storable {
var hasArrived: Bool = false var hasArrived: Bool = false
var storeId: String? = nil
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) { 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.lastUpdate = Date()
self.teamRegistration = teamRegistration self.teamRegistration = teamRegistration
self.firstName = firstName self.firstName = firstName
self.lastName = lastName self.lastName = lastName
@ -61,6 +65,7 @@ final class PlayerRegistration: ModelObject, Storable {
} }
internal init(importedPlayer: ImportedPlayer) { internal init(importedPlayer: ImportedPlayer) {
self.lastUpdate = Date()
self.teamRegistration = "" self.teamRegistration = ""
self.firstName = (importedPlayer.firstName ?? "").trimmed.capitalized self.firstName = (importedPlayer.firstName ?? "").trimmed.capitalized
self.lastName = (importedPlayer.lastName ?? "").trimmed.uppercased() self.lastName = (importedPlayer.lastName ?? "").trimmed.uppercased()
@ -77,6 +82,7 @@ final class PlayerRegistration: ModelObject, Storable {
} }
internal init?(federalData: [String], sex: Int, sexUnknown: Bool) { internal init?(federalData: [String], sex: Int, sexUnknown: Bool) {
self.lastUpdate = Date()
let _lastName = federalData[0].trimmed.uppercased() let _lastName = federalData[0].trimmed.uppercased()
let _firstName = federalData[1].trimmed.capitalized let _firstName = federalData[1].trimmed.capitalized
if _lastName.isEmpty && _firstName.isEmpty { return nil } if _lastName.isEmpty && _firstName.isEmpty { return nil }
@ -323,6 +329,8 @@ final class PlayerRegistration: ModelObject, Storable {
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case _id = "id" case _id = "id"
case _storeId = "storeId"
case _lastUpdate = "lastUpdate"
case _teamRegistration = "teamRegistration" case _teamRegistration = "teamRegistration"
case _firstName = "firstName" case _firstName = "firstName"
case _lastName = "lastName" case _lastName = "lastName"
@ -348,6 +356,8 @@ final class PlayerRegistration: ModelObject, Storable {
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: ._id) try container.encode(id, forKey: ._id)
try container.encode(storeId, forKey: ._storeId)
try container.encode(lastUpdate, forKey: ._lastUpdate)
try container.encode(teamRegistration, forKey: ._teamRegistration) try container.encode(teamRegistration, forKey: ._teamRegistration)
try container.encode(firstName, forKey: ._firstName) try container.encode(firstName, forKey: ._firstName)
@ -454,16 +464,6 @@ final class PlayerRegistration: ModelObject, Storable {
} }
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 { extension PlayerRegistration: PlayerHolder {
func getAssimilatedAsMaleRank() -> Int? { func getAssimilatedAsMaleRank() -> Int? {
nil nil

@ -18,4 +18,3 @@ Dans Django:
Enfin, revenir dans Xcode, ouvrir ServerDataTests et lancer le test mis à jour Enfin, revenir dans Xcode, ouvrir ServerDataTests et lancer le test mis à jour

@ -10,13 +10,15 @@ import LeStorage
import SwiftUI import SwiftUI
@Observable @Observable
final class Round: ModelObject, Storable { final class Round: ModelObject, SyncedStorable, SideStorable {
static func resourceName() -> String { "rounds" } static func resourceName() -> String { "rounds" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
static func filterByStoreIdentifier() -> Bool { return true } static func filterByStoreIdentifier() -> Bool { return true }
static var relationshipNames: [String] = [] static var relationshipNames: [String] = []
var id: String = Store.randomId() var id: String = Store.randomId()
var lastUpdate: Date
var tournament: String var tournament: String
var index: Int var index: Int
var parent: String? var parent: String?
@ -25,7 +27,10 @@ final class Round: ModelObject, Storable {
var groupStageLoserBracket: Bool = false var groupStageLoserBracket: Bool = false
var loserBracketMode: LoserBracketMode = .automatic var loserBracketMode: LoserBracketMode = .automatic
var storeId: String? = nil
internal init(tournament: String, index: Int, parent: String? = nil, matchFormat: MatchFormat? = nil, startDate: Date? = nil, groupStageLoserBracket: Bool = false, loserBracketMode: LoserBracketMode = .automatic) { internal init(tournament: String, index: Int, parent: String? = nil, matchFormat: MatchFormat? = nil, startDate: Date? = nil, groupStageLoserBracket: Bool = false, loserBracketMode: LoserBracketMode = .automatic) {
self.lastUpdate = Date()
self.tournament = tournament self.tournament = tournament
self.index = index self.index = index
self.parent = parent self.parent = parent
@ -397,11 +402,7 @@ defer {
// Logger.error(error) // Logger.error(error)
// } // }
} }
do { self.tournamentStore.matches.addOrUpdate(contentOfs: _matches)
try self.tournamentStore.matches.addOrUpdate(contentOfs: _matches)
} catch {
Logger.error(error)
}
} }
var cumulativeMatchCount: Int { var cumulativeMatchCount: Int {
@ -550,11 +551,7 @@ defer {
func updateTournamentState() { func updateTournamentState() {
if let tournamentObject = tournamentObject(), index == 0, isUpperBracket(), hasEnded() { if let tournamentObject = tournamentObject(), index == 0, isUpperBracket(), hasEnded() {
tournamentObject.endDate = Date() tournamentObject.endDate = Date()
do { DataStore.shared.tournaments.addOrUpdate(instance: tournamentObject)
try DataStore.shared.tournaments.addOrUpdate(instance: tournamentObject)
} catch {
Logger.error(error)
}
} }
} }
@ -596,12 +593,8 @@ defer {
} }
func deleteLoserBracket() { func deleteLoserBracket() {
do { let loserRounds = loserRounds()
let loserRounds = loserRounds() self.tournamentStore.rounds.delete(contentOfs: loserRounds)
try self.tournamentStore.rounds.delete(contentOfs: loserRounds)
} catch {
Logger.error(error)
}
} }
func buildLoserBracket() { func buildLoserBracket() {
@ -620,12 +613,7 @@ defer {
round.parent = id //parent round.parent = id //parent
return round return round
} }
self.tournamentStore.rounds.addOrUpdate(contentOfs: rounds)
do {
try self.tournamentStore.rounds.addOrUpdate(contentOfs: rounds)
} catch {
Logger.error(error)
}
let matchCount = RoundRule.numberOfMatches(forTeams: currentRoundMatchCount) let matchCount = RoundRule.numberOfMatches(forTeams: currentRoundMatchCount)
let matches = (0..<matchCount).map { //0 is final match let matches = (0..<matchCount).map { //0 is final match
@ -635,11 +623,7 @@ defer {
//initial mode let the roundTitle give a name without considering the playable match //initial mode let the roundTitle give a name without considering the playable match
} }
do { self.tournamentStore.matches.addOrUpdate(contentOfs: matches)
try self.tournamentStore.matches.addOrUpdate(contentOfs: matches)
} catch {
Logger.error(error)
}
loserRounds().forEach { round in loserRounds().forEach { round in
round.buildLoserBracket() round.buildLoserBracket()
@ -672,24 +656,20 @@ defer {
playedMatches.forEach { match in playedMatches.forEach { match in
match.matchFormat = updatedMatchFormat match.matchFormat = updatedMatchFormat
} }
do { self.tournamentStore.matches.addOrUpdate(contentOfs: playedMatches)
try self.tournamentStore.matches.addOrUpdate(contentOfs: playedMatches)
} catch {
Logger.error(error)
}
} }
override func deleteDependencies() throws { override func deleteDependencies() {
let matches = self._matches() let matches = self._matches()
for match in matches { for match in matches {
try match.deleteDependencies() match.deleteDependencies()
} }
self.tournamentStore.matches.deleteDependencies(matches) self.tournamentStore.matches.deleteDependencies(matches)
let loserRounds = self.loserRounds() let loserRounds = self.loserRounds()
for round in loserRounds { for round in loserRounds {
try round.deleteDependencies() round.deleteDependencies()
} }
self.tournamentStore.rounds.deleteDependencies(loserRounds) self.tournamentStore.rounds.deleteDependencies(loserRounds)
@ -697,6 +677,8 @@ defer {
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case _id = "id" case _id = "id"
case _storeId = "storeId"
case _lastUpdate = "lastUpdate"
case _tournament = "tournament" case _tournament = "tournament"
case _index = "index" case _index = "index"
case _parent = "parent" case _parent = "parent"
@ -709,6 +691,7 @@ defer {
required init(from decoder: Decoder) throws { required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self) let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: ._id) id = try container.decode(String.self, forKey: ._id)
lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date()
tournament = try container.decode(String.self, forKey: ._tournament) tournament = try container.decode(String.self, forKey: ._tournament)
index = try container.decode(Int.self, forKey: ._index) index = try container.decode(Int.self, forKey: ._index)
parent = try container.decodeIfPresent(String.self, forKey: ._parent) parent = try container.decodeIfPresent(String.self, forKey: ._parent)
@ -722,6 +705,8 @@ defer {
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: ._id) try container.encode(id, forKey: ._id)
try container.encode(lastUpdate, forKey: ._lastUpdate)
try container.encode(storeId, forKey: ._storeId)
try container.encode(tournament, forKey: ._tournament) try container.encode(tournament, forKey: ._tournament)
try container.encode(index, forKey: ._index) try container.encode(index, forKey: ._index)
try container.encode(groupStageLoserBracket, forKey: ._groupStageLoserBracket) try container.encode(groupStageLoserBracket, forKey: ._groupStageLoserBracket)
@ -742,11 +727,7 @@ defer {
} }
extension Round: Selectable, Equatable { extension Round: Selectable {
static func == (lhs: Round, rhs: Round) -> Bool {
lhs.id == rhs.id
}
func selectionLabel(index: Int) -> String { func selectionLabel(index: Int) -> String {
if let parentRound { if let parentRound {

@ -10,13 +10,14 @@ import LeStorage
import SwiftUI import SwiftUI
@Observable @Observable
final class TeamRegistration: ModelObject, Storable { final class TeamRegistration: ModelObject, SyncedStorable, SideStorable {
static func resourceName() -> String { "team-registrations" } static func resourceName() -> String { "team-registrations" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
static func filterByStoreIdentifier() -> Bool { return true } static func filterByStoreIdentifier() -> Bool { return true }
static var relationshipNames: [String] = [] static var relationshipNames: [String] = []
var id: String = Store.randomId() var id: String = Store.randomId()
var lastUpdate: Date
var tournament: String var tournament: String
var groupStage: String? var groupStage: String?
var registrationDate: Date? var registrationDate: Date?
@ -39,7 +40,12 @@ final class TeamRegistration: ModelObject, Storable {
var finalRanking: Int? var finalRanking: Int?
var pointsEarned: Int? var pointsEarned: Int?
var storeId: String? = nil
init(tournament: String, groupStage: String? = nil, registrationDate: Date? = nil, callDate: Date? = nil, bracketPosition: Int? = nil, groupStagePosition: Int? = nil, comment: String? = nil, source: String? = nil, sourceValue: String? = nil, logo: String? = nil, name: String? = nil, walkOut: Bool = false, wildCardBracket: Bool = false, wildCardGroupStage: Bool = false, weight: Int = 0, lockedWeight: Int? = nil, confirmationDate: Date? = nil, qualified: Bool = false) { init(tournament: String, groupStage: String? = nil, registrationDate: Date? = nil, callDate: Date? = nil, bracketPosition: Int? = nil, groupStagePosition: Int? = nil, comment: String? = nil, source: String? = nil, sourceValue: String? = nil, logo: String? = nil, name: String? = nil, walkOut: Bool = false, wildCardBracket: Bool = false, wildCardGroupStage: Bool = false, weight: Int = 0, lockedWeight: Int? = nil, confirmationDate: Date? = nil, qualified: Bool = false) {
self.storeId = tournament
self.lastUpdate = Date()
self.tournament = tournament self.tournament = tournament
self.groupStage = groupStage self.groupStage = groupStage
self.registrationDate = registrationDate self.registrationDate = registrationDate
@ -74,23 +80,19 @@ final class TeamRegistration: ModelObject, Storable {
func deleteTeamScores() { func deleteTeamScores() {
let ts = self.tournamentStore.teamScores.filter({ $0.teamRegistration == id }) let ts = self.tournamentStore.teamScores.filter({ $0.teamRegistration == id })
do { self.tournamentStore.teamScores.delete(contentOfs: ts)
try self.tournamentStore.teamScores.delete(contentOfs: ts)
} catch {
Logger.error(error)
}
} }
override func deleteDependencies() throws { override func deleteDependencies() {
let unsortedPlayers = unsortedPlayers() let unsortedPlayers = unsortedPlayers()
for player in unsortedPlayers { for player in unsortedPlayers {
try player.deleteDependencies() player.deleteDependencies()
} }
self.tournamentStore.playerRegistrations.deleteDependencies(unsortedPlayers) self.tournamentStore.playerRegistrations.deleteDependencies(unsortedPlayers)
let teamScores = teamScores() let teamScores = teamScores()
for teamScore in teamScores { for teamScore in teamScores {
try teamScore.deleteDependencies() teamScore.deleteDependencies()
} }
self.tournamentStore.teamScores.deleteDependencies(teamScores) self.tournamentStore.teamScores.deleteDependencies(teamScores)
} }
@ -98,11 +100,7 @@ final class TeamRegistration: ModelObject, Storable {
func hasArrived(isHere: Bool = false) { func hasArrived(isHere: Bool = false) {
let unsortedPlayers = unsortedPlayers() let unsortedPlayers = unsortedPlayers()
unsortedPlayers.forEach({ $0.hasArrived = !isHere }) unsortedPlayers.forEach({ $0.hasArrived = !isHere })
do { self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: unsortedPlayers)
try self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: unsortedPlayers)
} catch {
Logger.error(error)
}
} }
func isHere() -> Bool { func isHere() -> Bool {
@ -301,11 +299,7 @@ final class TeamRegistration: ModelObject, Storable {
if let groupStage { if let groupStage {
let matches = self.tournamentStore.matches.filter({ $0.groupStage == groupStage }).map { $0.id } let matches = self.tournamentStore.matches.filter({ $0.groupStage == groupStage }).map { $0.id }
let teamScores = self.tournamentStore.teamScores.filter({ $0.teamRegistration == id && matches.contains($0.match) }) let teamScores = self.tournamentStore.teamScores.filter({ $0.teamRegistration == id && matches.contains($0.match) })
do { self.tournamentStore.teamScores.delete(contentOfs: teamScores)
try tournamentStore.teamScores.delete(contentOfs: teamScores)
} catch {
Logger.error(error)
}
} }
//groupStageObject()?._matches().forEach({ $0.updateTeamScores() }) //groupStageObject()?._matches().forEach({ $0.updateTeamScores() })
groupStage = nil groupStage = nil
@ -315,11 +309,7 @@ final class TeamRegistration: ModelObject, Storable {
func resetBracketPosition() { func resetBracketPosition() {
let matches = self.tournamentStore.matches.filter({ $0.groupStage == nil }).map { $0.id } let matches = self.tournamentStore.matches.filter({ $0.groupStage == nil }).map { $0.id }
let teamScores = self.tournamentStore.teamScores.filter({ $0.teamRegistration == id && matches.contains($0.match) }) let teamScores = self.tournamentStore.teamScores.filter({ $0.teamRegistration == id && matches.contains($0.match) })
do { self.tournamentStore.teamScores.delete(contentOfs: teamScores)
try tournamentStore.teamScores.delete(contentOfs: teamScores)
} catch {
Logger.error(error)
}
self.bracketPosition = nil self.bracketPosition = nil
} }
@ -389,11 +379,7 @@ final class TeamRegistration: ModelObject, Storable {
func updatePlayers(_ players: Set<PlayerRegistration>, inTournamentCategory tournamentCategory: TournamentCategory) { func updatePlayers(_ players: Set<PlayerRegistration>, inTournamentCategory tournamentCategory: TournamentCategory) {
let previousPlayers = Set(unsortedPlayers()) let previousPlayers = Set(unsortedPlayers())
let playersToRemove = previousPlayers.subtracting(players) let playersToRemove = previousPlayers.subtracting(players)
do { self.tournamentStore.playerRegistrations.delete(contentOfs: playersToRemove)
try self.tournamentStore.playerRegistrations.delete(contentOfs: playersToRemove)
} catch {
Logger.error(error)
}
setWeight(from: Array(players), inTournamentCategory: tournamentCategory) setWeight(from: Array(players), inTournamentCategory: tournamentCategory)
players.forEach { player in players.forEach { player in
@ -525,6 +511,8 @@ final class TeamRegistration: ModelObject, Storable {
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case _id = "id" case _id = "id"
case _lastUpdate = "lastUpdate"
case _storeId = "storeId"
case _tournament = "tournament" case _tournament = "tournament"
case _groupStage = "groupStage" case _groupStage = "groupStage"
case _registrationDate = "registrationDate" case _registrationDate = "registrationDate"
@ -551,6 +539,8 @@ final class TeamRegistration: ModelObject, Storable {
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: ._id) try container.encode(id, forKey: ._id)
try container.encode(storeId, forKey: ._storeId)
try container.encode(lastUpdate, forKey: ._lastUpdate)
try container.encode(tournament, forKey: ._tournament) try container.encode(tournament, forKey: ._tournament)
try container.encode(groupStage, forKey: ._groupStage) try container.encode(groupStage, forKey: ._groupStage)
try container.encode(registrationDate, forKey: ._registrationDate) try container.encode(registrationDate, forKey: ._registrationDate)
@ -582,16 +572,6 @@ final class TeamRegistration: ModelObject, Storable {
} }
extension TeamRegistration: Hashable {
static func == (lhs: TeamRegistration, rhs: TeamRegistration) -> Bool {
lhs.id == rhs.id
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}
enum TeamDataSource: Int, Codable { enum TeamDataSource: Int, Codable {
case beachPadel case beachPadel
} }

@ -9,7 +9,7 @@ import Foundation
import LeStorage import LeStorage
@Observable @Observable
final class TeamScore: ModelObject, Storable { final class TeamScore: ModelObject, SyncedStorable, SideStorable {
static func resourceName() -> String { "team-scores" } static func resourceName() -> String { "team-scores" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
@ -17,6 +17,7 @@ final class TeamScore: ModelObject, Storable {
static var relationshipNames: [String] = ["match"] static var relationshipNames: [String] = ["match"]
var id: String = Store.randomId() var id: String = Store.randomId()
var lastUpdate: Date
var match: String var match: String
var teamRegistration: String? var teamRegistration: String?
//var playerRegistrations: [String] = [] //var playerRegistrations: [String] = []
@ -24,7 +25,10 @@ final class TeamScore: ModelObject, Storable {
var walkOut: Int? var walkOut: Int?
var luckyLoser: Int? var luckyLoser: Int?
var storeId: String? = nil
init(match: String, teamRegistration: String? = nil, score: String? = nil, walkOut: Int? = nil, luckyLoser: Int? = nil) { init(match: String, teamRegistration: String? = nil, score: String? = nil, walkOut: Int? = nil, luckyLoser: Int? = nil) {
self.lastUpdate = Date()
self.match = match self.match = match
self.teamRegistration = teamRegistration self.teamRegistration = teamRegistration
// self.playerRegistrations = playerRegistrations // self.playerRegistrations = playerRegistrations
@ -34,6 +38,7 @@ final class TeamScore: ModelObject, Storable {
} }
init(match: String, team: TeamRegistration?) { init(match: String, team: TeamRegistration?) {
self.lastUpdate = Date()
self.match = match self.match = match
if let team { if let team {
self.teamRegistration = team.id self.teamRegistration = team.id
@ -72,6 +77,8 @@ final class TeamScore: ModelObject, Storable {
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case _id = "id" case _id = "id"
case _storeId = "storeId"
case _lastUpdate = "lastUpdate"
case _match = "match" case _match = "match"
case _teamRegistration = "teamRegistration" case _teamRegistration = "teamRegistration"
//case _playerRegistrations = "playerRegistrations" //case _playerRegistrations = "playerRegistrations"
@ -84,6 +91,8 @@ final class TeamScore: ModelObject, Storable {
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: ._id) try container.encode(id, forKey: ._id)
try container.encode(storeId, forKey: ._storeId)
try container.encode(lastUpdate, forKey: ._lastUpdate)
try container.encode(match, forKey: ._match) try container.encode(match, forKey: ._match)
try container.encode(teamRegistration, forKey: ._teamRegistration) try container.encode(teamRegistration, forKey: ._teamRegistration)
try container.encode(score, forKey: ._score) try container.encode(score, forKey: ._score)

@ -10,13 +10,15 @@ import LeStorage
import SwiftUI import SwiftUI
@Observable @Observable
final class Tournament : ModelObject, Storable { final class Tournament: ModelObject, SyncedStorable {
static func resourceName() -> String { "tournaments" } static func resourceName() -> String { "tournaments" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
static func filterByStoreIdentifier() -> Bool { return false } static func filterByStoreIdentifier() -> Bool { return false }
static var relationshipNames: [String] = [] static var relationshipNames: [String] = []
var id: String = Store.randomId() var id: String = Store.randomId()
var lastUpdate: Date
var event: String? var event: String?
var name: String? var name: String?
var startDate: Date var startDate: Date
@ -59,11 +61,14 @@ final class Tournament : ModelObject, Storable {
var publishRankings: Bool = false var publishRankings: Bool = false
var loserBracketMode: LoserBracketMode = .automatic var loserBracketMode: LoserBracketMode = .automatic
var storeId: String? { return nil }
@ObservationIgnored @ObservationIgnored
var navigationPath: [Screen] = [] var navigationPath: [Screen] = []
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case _id = "id" case _id = "id"
case _lastUpdate = "lastUpdate"
case _event = "event" case _event = "event"
case _creator = "creator" case _creator = "creator"
case _name = "name" case _name = "name"
@ -110,6 +115,7 @@ final class Tournament : ModelObject, Storable {
} }
internal init(event: String? = nil, name: String? = nil, startDate: Date = Date(), endDate: Date? = nil, creationDate: Date = Date(), isPrivate: Bool = false, groupStageFormat: MatchFormat? = nil, roundFormat: MatchFormat? = nil, loserRoundFormat: MatchFormat? = nil, groupStageSortMode: GroupStageOrderingMode, groupStageCount: Int = 4, rankSourceDate: Date? = nil, dayDuration: Int = 1, teamCount: Int = 24, teamSorting: TeamSortingType? = nil, federalCategory: TournamentCategory, federalLevelCategory: TournamentLevel, federalAgeCategory: FederalTournamentAge, closedRegistrationDate: Date? = nil, groupStageAdditionalQualified: Int = 0, courtCount: Int = 2, prioritizeClubMembers: Bool = false, qualifiedPerGroupStage: Int = 1, teamsPerGroupStage: Int = 4, entryFee: Double? = nil, additionalEstimationDuration: Int = 0, isDeleted: Bool = false, publishTeams: Bool = false, publishSummons: Bool = false, publishGroupStages: Bool = false, publishBrackets: Bool = false, shouldVerifyBracket: Bool = false, shouldVerifyGroupStage: Bool = false, hideTeamsWeight: Bool = false, publishTournament: Bool = false, hidePointsEarned: Bool = false, publishRankings: Bool = false, loserBracketMode: LoserBracketMode = .automatic) { internal init(event: String? = nil, name: String? = nil, startDate: Date = Date(), endDate: Date? = nil, creationDate: Date = Date(), isPrivate: Bool = false, groupStageFormat: MatchFormat? = nil, roundFormat: MatchFormat? = nil, loserRoundFormat: MatchFormat? = nil, groupStageSortMode: GroupStageOrderingMode, groupStageCount: Int = 4, rankSourceDate: Date? = nil, dayDuration: Int = 1, teamCount: Int = 24, teamSorting: TeamSortingType? = nil, federalCategory: TournamentCategory, federalLevelCategory: TournamentLevel, federalAgeCategory: FederalTournamentAge, closedRegistrationDate: Date? = nil, groupStageAdditionalQualified: Int = 0, courtCount: Int = 2, prioritizeClubMembers: Bool = false, qualifiedPerGroupStage: Int = 1, teamsPerGroupStage: Int = 4, entryFee: Double? = nil, additionalEstimationDuration: Int = 0, isDeleted: Bool = false, publishTeams: Bool = false, publishSummons: Bool = false, publishGroupStages: Bool = false, publishBrackets: Bool = false, shouldVerifyBracket: Bool = false, shouldVerifyGroupStage: Bool = false, hideTeamsWeight: Bool = false, publishTournament: Bool = false, hidePointsEarned: Bool = false, publishRankings: Bool = false, loserBracketMode: LoserBracketMode = .automatic) {
self.lastUpdate = Date()
self.event = event self.event = event
self.name = name self.name = name
self.startDate = startDate self.startDate = startDate
@ -153,6 +159,7 @@ final class Tournament : ModelObject, Storable {
required init(from decoder: Decoder) throws { required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self) let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: ._id) id = try container.decode(String.self, forKey: ._id)
lastUpdate = try container.decode(Date.self, forKey: ._lastUpdate)
event = try container.decodeIfPresent(String.self, forKey: ._event) event = try container.decodeIfPresent(String.self, forKey: ._event)
name = try container.decodeIfPresent(String.self, forKey: ._name) name = try container.decodeIfPresent(String.self, forKey: ._name)
startDate = try container.decode(Date.self, forKey: ._startDate) startDate = try container.decode(Date.self, forKey: ._startDate)
@ -230,6 +237,7 @@ final class Tournament : ModelObject, Storable {
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: ._id) try container.encode(id, forKey: ._id)
try container.encode(lastUpdate, forKey: ._lastUpdate)
try container.encode(event, forKey: ._event) try container.encode(event, forKey: ._event)
try container.encode(name, forKey: ._name) try container.encode(name, forKey: ._name)
@ -321,23 +329,23 @@ final class Tournament : ModelObject, Storable {
return TournamentStore.instance(tournamentId: self.id) return TournamentStore.instance(tournamentId: self.id)
} }
override func deleteDependencies() throws { override func deleteDependencies() {
let store = self.tournamentStore let store = self.tournamentStore
let teams = self.tournamentStore.teamRegistrations let teams = self.tournamentStore.teamRegistrations
for team in teams { for team in Array(teams) {
try team.deleteDependencies() team.deleteDependencies()
} }
store.teamRegistrations.deleteDependencies(teams) store.teamRegistrations.deleteDependencies(teams)
let groups = self.tournamentStore.groupStages let groups = self.tournamentStore.groupStages
for group in groups { for group in groups {
try group.deleteDependencies() group.deleteDependencies()
} }
store.groupStages.deleteDependencies(groups) store.groupStages.deleteDependencies(groups)
let rounds = self.tournamentStore.rounds let rounds = self.tournamentStore.rounds
for round in rounds { for round in rounds {
try round.deleteDependencies() round.deleteDependencies()
} }
store.rounds.deleteDependencies(rounds) store.rounds.deleteDependencies(rounds)
@ -1056,17 +1064,8 @@ defer {
} }
} }
do { self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teamsToImport)
try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teamsToImport) self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: teams.flatMap { $0.players })
} 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 { if state() == .build && groupStageCount > 0 && groupStageTeams().isEmpty {
setGroupStage(randomize: groupStageSortMode == .random) setGroupStage(randomize: groupStageSortMode == .random)
@ -1316,12 +1315,7 @@ defer {
} }
} }
do { self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: unsortedTeams())
try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: unsortedTeams())
} catch {
Logger.error(error)
}
return rankings return rankings
} }
@ -1336,11 +1330,7 @@ defer {
teams.forEach { team in teams.forEach { team in
team.lockedWeight = team.weight team.lockedWeight = team.weight
} }
do { self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams)
try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams)
} catch {
Logger.error(error)
}
} }
func unlockRegistration() { func unlockRegistration() {
@ -1349,31 +1339,18 @@ defer {
teams.forEach { team in teams.forEach { team in
team.lockedWeight = nil team.lockedWeight = nil
} }
do { self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams)
try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams)
} catch {
Logger.error(error)
}
} }
func updateWeights() { func updateWeights() {
let teams = self.unsortedTeams() let teams = self.unsortedTeams()
teams.forEach { team in teams.forEach { team in
let players = team.unsortedPlayers() let players = team.unsortedPlayers()
players.forEach { $0.setComputedRank(in: self) } players.forEach { $0.setComputedRank(in: self) }
team.setWeight(from: players, inTournamentCategory: tournamentCategory) team.setWeight(from: players, inTournamentCategory: tournamentCategory)
do { self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players)
try self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players)
} catch {
Logger.error(error)
}
}
do {
try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams)
} catch {
Logger.error(error)
} }
self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams)
} }
func updateRank(to newDate: Date?) async throws { func updateRank(to newDate: Date?) async throws {
@ -1388,11 +1365,7 @@ defer {
let monthData: MonthData = MonthData(monthKey: formatted) let monthData: MonthData = MonthData(monthKey: formatted)
monthData.maleUnrankedValue = lastRankMan monthData.maleUnrankedValue = lastRankMan
monthData.femaleUnrankedValue = lastRankWoman monthData.femaleUnrankedValue = lastRankWoman
do { DataStore.shared.monthData.addOrUpdate(instance: monthData)
try DataStore.shared.monthData.addOrUpdate(instance: monthData)
} catch {
Logger.error(error)
}
} }
} }
@ -1685,12 +1658,7 @@ defer {
_groupStages.append(groupStage) _groupStages.append(groupStage)
} }
do { self.tournamentStore.groupStages.addOrUpdate(contentOfs: _groupStages)
try self.tournamentStore.groupStages.addOrUpdate(contentOfs: _groupStages)
} catch {
Logger.error(error)
}
refreshGroupStages() refreshGroupStages()
} }
@ -1707,11 +1675,7 @@ defer {
return Round(tournament: id, index: $0, matchFormat: roundSmartMatchFormat($0), loserBracketMode: loserBracketMode) return Round(tournament: id, index: $0, matchFormat: roundSmartMatchFormat($0), loserBracketMode: loserBracketMode)
} }
do { self.tournamentStore.rounds.addOrUpdate(contentOfs: rounds)
try self.tournamentStore.rounds.addOrUpdate(contentOfs: rounds)
} catch {
Logger.error(error)
}
let matchCount = RoundRule.numberOfMatches(forTeams: bracketTeamCount()) let matchCount = RoundRule.numberOfMatches(forTeams: bracketTeamCount())
let matches = (0..<matchCount).map { //0 is final match let matches = (0..<matchCount).map { //0 is final match
@ -1724,11 +1688,7 @@ defer {
(RoundRule.roundName(fromMatchIndex: $0.index), RoundRule.matchIndexWithinRound(fromMatchIndex: $0.index)) (RoundRule.roundName(fromMatchIndex: $0.index), RoundRule.matchIndexWithinRound(fromMatchIndex: $0.index))
}) })
do { self.tournamentStore.matches.addOrUpdate(contentOfs: matches)
try self.tournamentStore.matches.addOrUpdate(contentOfs: matches)
} catch {
Logger.error(error)
}
rounds.forEach { round in rounds.forEach { round in
round.buildLoserBracket() round.buildLoserBracket()
@ -1758,11 +1718,7 @@ defer {
} }
func deleteStructure() { func deleteStructure() {
do { self.tournamentStore.rounds.delete(contentOfs: rounds())
try self.tournamentStore.rounds.delete(contentOfs: rounds())
} catch {
Logger.error(error)
}
} }
func resetBracketPosition() { func resetBracketPosition() {
@ -1770,11 +1726,7 @@ defer {
} }
func deleteGroupStages() { func deleteGroupStages() {
do { self.tournamentStore.groupStages.delete(contentOfs: allGroupStages())
try self.tournamentStore.groupStages.delete(contentOfs: allGroupStages())
} catch {
Logger.error(error)
}
} }
func refreshGroupStages() { func refreshGroupStages() {
@ -1827,11 +1779,7 @@ defer {
} }
} }
do { self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: unsortedTeams())
try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: unsortedTeams())
} catch {
Logger.error(error)
}
} }
func isFree() -> Bool { func isFree() -> Bool {
@ -2097,11 +2045,7 @@ defer {
let lastStep = lastStep() + 1 let lastStep = lastStep() + 1
for i in 0..<teamsPerGroupStage { for i in 0..<teamsPerGroupStage {
let gs = GroupStage(tournament: id, index: i, size: groupStageCount, step: lastStep) let gs = GroupStage(tournament: id, index: i, size: groupStageCount, step: lastStep)
do { tournamentStore.groupStages.addOrUpdate(instance: gs)
try tournamentStore.groupStages.addOrUpdate(instance: gs)
} catch {
Logger.error(error)
}
} }
groupStages(atStep: 1).forEach { $0.buildMatches() } groupStages(atStep: 1).forEach { $0.buildMatches() }
@ -2118,11 +2062,7 @@ defer {
let placeCount = i * 2 + 1 let placeCount = i * 2 + 1
let match = Match(round: groupStageLoserBracket.id, index: placeCount, matchFormat: groupStageLoserBracket.matchFormat) let match = Match(round: groupStageLoserBracket.id, index: placeCount, matchFormat: groupStageLoserBracket.matchFormat)
match.name = "\(placeCount)\(placeCount.ordinalFormattedSuffix(feminine: true)) place" match.name = "\(placeCount)\(placeCount.ordinalFormattedSuffix(feminine: true)) place"
do { tournamentStore.matches.addOrUpdate(instance: match)
try tournamentStore.matches.addOrUpdate(instance: match)
} catch {
Logger.error(error)
}
if let gs1 = gss.first, let gs2 = gss.last, let score1 = gs1.teams(true)[safe: i], let score2 = gs2.teams(true)[safe: i] { if let gs1 = gss.first, let gs2 = gss.last, let score1 = gs1.teams(true)[safe: i], let score2 = gs2.teams(true)[safe: i] {
print("rang \(i)") print("rang \(i)")
@ -2177,7 +2117,7 @@ defer {
if self.payment != nil { return } if self.payment != nil { return }
if let payment = Guard.main.paymentForNewTournament() { if let payment = Guard.main.paymentForNewTournament() {
self.payment = payment self.payment = payment
try DataStore.shared.tournaments.addOrUpdate(instance: self) DataStore.shared.tournaments.addOrUpdate(instance: self)
return return
} }
throw PaymentError.cantPayTournament throw PaymentError.cantPayTournament
@ -2240,16 +2180,6 @@ fileprivate extension Bool {
// } // }
//} //}
extension Tournament: Hashable {
static func == (lhs: Tournament, rhs: Tournament) -> Bool {
lhs.id == rhs.id
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}
extension Tournament: FederalTournamentHolder { extension Tournament: FederalTournamentHolder {
func tournamentTitle(_ displayStyle: DisplayStyle, forBuild build: any TournamentBuildHolder) -> String { func tournamentTitle(_ displayStyle: DisplayStyle, forBuild build: any TournamentBuildHolder) -> String {

@ -12,9 +12,6 @@ import SwiftUI
class TournamentStore: Store, ObservableObject { class TournamentStore: Store, ObservableObject {
static func instance(tournamentId: String) -> TournamentStore { static func instance(tournamentId: String) -> TournamentStore {
// if StoreCenter.main.userId == nil {
// fatalError("cant request store without id")
// }
return StoreCenter.main.store(identifier: tournamentId, parameter: "tournament") return StoreCenter.main.store(identifier: tournamentId, parameter: "tournament")
} }
@ -44,13 +41,13 @@ class TournamentStore: Store, ObservableObject {
} }
#endif #endif
self.groupStages = self.registerCollection(synchronized: synchronized, indexed: indexed) self.groupStages = self.registerSynchronizedCollection(indexed: indexed)
self.rounds = self.registerCollection(synchronized: synchronized, indexed: indexed) self.rounds = self.registerSynchronizedCollection(indexed: indexed)
self.teamRegistrations = self.registerCollection(synchronized: synchronized, indexed: indexed) self.teamRegistrations = self.registerSynchronizedCollection(indexed: indexed)
self.playerRegistrations = self.registerCollection(synchronized: synchronized, indexed: indexed) self.playerRegistrations = self.registerSynchronizedCollection(indexed: indexed)
self.matches = self.registerCollection(synchronized: synchronized, indexed: indexed) self.matches = self.registerSynchronizedCollection(indexed: indexed)
self.teamScores = self.registerCollection(synchronized: synchronized, indexed: indexed) self.teamScores = self.registerSynchronizedCollection(indexed: indexed)
self.matchSchedulers = self.registerCollection(synchronized: false, indexed: indexed) self.matchSchedulers = self.registerCollection(indexed: indexed)
self.loadCollectionsFromServerIfNoFile() self.loadCollectionsFromServerIfNoFile()

@ -26,9 +26,9 @@ class SourceFileManager {
if !fileManager.fileExists(atPath: directoryURL.path) { if !fileManager.fileExists(atPath: directoryURL.path) {
// Directory does not exist, create it // Directory does not exist, create it
try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil) try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
print("Directory created at: \(directoryURL)") // print("Directory created at: \(directoryURL)")
} else { } else {
print("Directory already exists at: \(directoryURL)") // print("Directory already exists at: \(directoryURL)")
} }
} catch { } catch {
print("Error: \(error)") print("Error: \(error)")

@ -242,11 +242,7 @@ struct ClubDetailView: View {
} }
.onDisappear { .onDisappear {
if displayContext == .edition && clubDeleted == false { if displayContext == .edition && clubDeleted == false {
do { dataStore.clubs.addOrUpdate(instance: club)
try dataStore.clubs.addOrUpdate(instance: club)
} catch {
Logger.error(error)
}
} }
} }
.onAppear { .onAppear {

@ -108,21 +108,13 @@ struct LoserBracketFromGroupStageView: View {
let placeCount = displayableMatches.isEmpty ? currentGroupStageLoserBracketsInitialPlace : max(currentGroupStageLoserBracketsInitialPlace, displayableMatches.map({ $0.index }).max()! + 2) let placeCount = displayableMatches.isEmpty ? currentGroupStageLoserBracketsInitialPlace : max(currentGroupStageLoserBracketsInitialPlace, displayableMatches.map({ $0.index }).max()! + 2)
let match = Match(round: loserBracket.id, index: placeCount, matchFormat: loserBracket.matchFormat) let match = Match(round: loserBracket.id, index: placeCount, matchFormat: loserBracket.matchFormat)
match.name = "\(placeCount)\(placeCount.ordinalFormattedSuffix()) place" match.name = "\(placeCount)\(placeCount.ordinalFormattedSuffix()) place"
do { tournamentStore.matches.addOrUpdate(instance: match)
try tournamentStore.matches.addOrUpdate(instance: match)
} catch {
Logger.error(error)
}
} }
private func _deleteAllMatches() { private func _deleteAllMatches() {
let displayableMatches = loserBracket.playedMatches().sorted(by: \.index) let displayableMatches = loserBracket.playedMatches().sorted(by: \.index)
do { tournamentStore.matches.delete(contentOfs: displayableMatches)
try tournamentStore.matches.delete(contentOfs: displayableMatches)
} catch {
Logger.error(error)
}
} }
@ -205,15 +197,7 @@ struct GroupStageLoserBracketMatchFooterView: View {
match.name = "\(newIndexValidated)\(newIndexValidated.ordinalFormattedSuffix()) place" match.name = "\(newIndexValidated)\(newIndexValidated.ordinalFormattedSuffix()) place"
do { match.tournamentStore.teamScores.addOrUpdate(contentOfs: teamScores)
try match.tournamentStore.teamScores.addOrUpdate(contentOfs: teamScores) match.tournamentStore.matches.addOrUpdate(instance: match)
} catch {
Logger.error(error)
}
do {
try match.tournamentStore.matches.addOrUpdate(instance: match)
} catch {
Logger.error(error)
}
} }
} }

@ -13,7 +13,7 @@ struct TournamentSubscriptionView: View {
let federalTournament: FederalTournament let federalTournament: FederalTournament
let build: any TournamentBuildHolder let build: any TournamentBuildHolder
let user: User let user: CustomUser
@State private var selectedPlayers: [ImportedPlayer] @State private var selectedPlayers: [ImportedPlayer]
@State private var contactType: ContactType? = nil @State private var contactType: ContactType? = nil
@ -21,7 +21,7 @@ struct TournamentSubscriptionView: View {
@State private var didSendMessage: Bool = false @State private var didSendMessage: Bool = false
@State private var didSaveInCalendar: Bool = false @State private var didSaveInCalendar: Bool = false
init(federalTournament: FederalTournament, build: any TournamentBuildHolder, user: User) { init(federalTournament: FederalTournament, build: any TournamentBuildHolder, user: CustomUser) {
self.federalTournament = federalTournament self.federalTournament = federalTournament
self.build = build self.build = build
self.user = user self.user = user

@ -42,11 +42,7 @@ struct RoundSettingsView: View {
Menu { Menu {
Button("Retirer du tableau") { Button("Retirer du tableau") {
team.resetBracketPosition() team.resetBracketPosition()
do { self.tournamentStore.teamRegistrations.addOrUpdate(instance: team)
try self.tournamentStore.teamRegistrations.addOrUpdate(instance: team)
} catch {
Logger.error(error)
}
} }
} label: { } label: {
TeamRowView(team: team) TeamRowView(team: team)
@ -63,11 +59,7 @@ struct RoundSettingsView: View {
RowButtonView("Valider l'état du tableau", role: .destructive) { RowButtonView("Valider l'état du tableau", role: .destructive) {
tournament.shouldVerifyBracket = false tournament.shouldVerifyBracket = false
do { dataStore.tournaments.addOrUpdate(instance: tournament)
try dataStore.tournaments.addOrUpdate(instance: tournament)
} catch {
Logger.error(error)
}
} }
} footer: { } footer: {
Text("Suite à un changement dans votre liste d'inscrits, veuillez vérifier l'intégrité de votre tableau et valider que tout est ok.") Text("Suite à un changement dans votre liste d'inscrits, veuillez vérifier l'intégrité de votre tableau et valider que tout est ok.")
@ -121,16 +113,9 @@ struct RoundSettingsView: View {
return match return match
} }
do { tournamentStore.rounds.addOrUpdate(instance: round)
try tournamentStore.rounds.addOrUpdate(instance: round) tournamentStore.matches.addOrUpdate(contentOfs: matches)
} catch {
Logger.error(error)
}
do {
try tournamentStore.matches.addOrUpdate(contentOfs: matches)
} catch {
Logger.error(error)
}
round.buildLoserBracket() round.buildLoserBracket()
matches.filter { $0.disabled }.forEach { matches.filter { $0.disabled }.forEach {
$0._toggleLoserMatchDisableState(true) $0._toggleLoserMatchDisableState(true)
@ -141,12 +126,12 @@ struct RoundSettingsView: View {
Section { Section {
if let lastRound = tournament.rounds().first { // first is final, last round if let lastRound = tournament.rounds().first { // first is final, last round
RowButtonView("Supprimer " + lastRound.roundTitle(), role: .destructive) { RowButtonView("Supprimer " + lastRound.roundTitle(), role: .destructive) {
let teams = lastRound.seeds()
teams.forEach { team in
team.resetBracketPosition()
}
tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams)
do { do {
let teams = lastRound.seeds()
teams.forEach { team in
team.resetBracketPosition()
}
try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams)
try tournamentStore.rounds.delete(instance: lastRound) try tournamentStore.rounds.delete(instance: lastRound)
} catch { } catch {
Logger.error(error) Logger.error(error)
@ -159,11 +144,7 @@ struct RoundSettingsView: View {
RowButtonView("Synchroniser les noms des matchs") { RowButtonView("Synchroniser les noms des matchs") {
let allRoundMatches = tournament.allRoundMatches() let allRoundMatches = tournament.allRoundMatches()
allRoundMatches.forEach({ $0.name = $0.roundTitle() }) allRoundMatches.forEach({ $0.name = $0.roundTitle() })
do { self.tournament.tournamentStore.matches.addOrUpdate(contentOfs: allRoundMatches)
try self.tournament.tournamentStore.matches.addOrUpdate(contentOfs: allRoundMatches)
} catch {
Logger.error(error)
}
} }
} }
} }
@ -182,16 +163,8 @@ struct RoundSettingsView: View {
match.teamScores match.teamScores
} }
do { tournamentStore.teamScores.delete(contentOfs: ts)
try tournamentStore.teamScores.delete(contentOfs: ts) tournamentStore.teamRegistrations.addOrUpdate(contentOfs: tournament.unsortedTeams())
} catch {
Logger.error(error)
}
do {
try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: tournament.unsortedTeams())
} catch {
Logger.error(error)
}
tournament.allRounds().forEach({ round in tournament.allRounds().forEach({ round in
round.enableRound() round.enableRound()
}) })

@ -344,7 +344,7 @@ struct AddTeamView: View {
return false return false
} }
private func _createTeam(checkDuplicates: Bool, checkHomonym: Bool) { private func _createTeam(checkDuplicates: Bool, checkHomonym: Bool) {
if checkDuplicates && _isDuplicate() { if checkDuplicates && _isDuplicate() {
confirmDuplicate = true confirmDuplicate = true
return return
@ -361,27 +361,19 @@ struct AddTeamView: View {
} }
let team = tournament.addTeam(players) let team = tournament.addTeam(players)
do { self.tournamentStore.teamRegistrations.addOrUpdate(instance: team)
try self.tournamentStore.teamRegistrations.addOrUpdate(instance: team) self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players)
} catch {
Logger.error(error) pasteString = nil
} editableTextField = ""
do {
try self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players) if team.players().count > 1 {
} catch { createdPlayers.removeAll()
Logger.error(error) createdPlayerIds.removeAll()
} dismiss()
} else {
pasteString = nil editedTeam = team
editableTextField = "" }
if team.players().count > 1 {
createdPlayers.removeAll()
createdPlayerIds.removeAll()
dismiss()
} else {
editedTeam = team
}
} }
private func _updateTeam(checkDuplicates: Bool) { private func _updateTeam(checkDuplicates: Bool) {
@ -393,16 +385,8 @@ struct AddTeamView: View {
let players = _currentSelection() let players = _currentSelection()
editedTeam.updatePlayers(players, inTournamentCategory: tournament.tournamentCategory) editedTeam.updatePlayers(players, inTournamentCategory: tournament.tournamentCategory)
do { self.tournamentStore.teamRegistrations.addOrUpdate(instance: editedTeam)
try self.tournamentStore.teamRegistrations.addOrUpdate(instance: editedTeam) self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players)
} catch {
Logger.error(error)
}
do {
try self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players)
} catch {
Logger.error(error)
}
pasteString = nil pasteString = nil
editableTextField = "" editableTextField = ""
@ -425,14 +409,22 @@ struct AddTeamView: View {
return max(boundingRect.height + 20, 40) // Add some padding and set a minimum height return max(boundingRect.height + 20, 40) // Add some padding and set a minimum height
} }
@ViewBuilder struct PasteStringSection: View {
private func _buildingTeamView() -> some View {
let pasteString: String?
@Binding var editableTextField: String
@Binding var textHeight: CGFloat
@FocusState var focusedField: AddTeamView.FocusField?
var handlePasteString: (String) -> Void
@Binding var displayWarningNotEnoughCharacter: Bool
var body: some View {
if let pasteString { if let pasteString {
Section { Section {
TextEditor(text: $editableTextField) TextEditor(text: $editableTextField)
.frame(height: textHeight) .frame(height: textHeight)
.onChange(of: editableTextField) { .onChange(of: editableTextField) {
textHeight = Self._calculateHeight(text: pasteString) textHeight = AddTeamView._calculateHeight(text: pasteString)
} }
.focused($focusedField, equals: .pasteField) .focused($focusedField, equals: .pasteField)
.toolbar { .toolbar {
@ -464,121 +456,104 @@ struct AddTeamView: View {
FooterButtonView("effacer le texte") { FooterButtonView("effacer le texte") {
self.focusedField = nil self.focusedField = nil
self.editableTextField = "" self.editableTextField = ""
self.pasteString = nil self.handlePasteString("")
} }
} }
} }
} }
}
}
Section {
ForEach(createdPlayerIds.sorted(), id: \.self) { id in
if let p = createdPlayers.first(where: { $0.id == id }) {
VStack(alignment: .leading, spacing: 0) {
if let player = unsortedPlayers.first(where: { ($0.licenceId == p.licenceId && $0.licenceId != nil) }), editedTeam?.includes(player: player) == false {
Text("Déjà inscrit !").foregroundStyle(.logoRed).bold()
}
if tournament.isPlayerAgeInadequate(player: p) {
Text("Âge invalide !").foregroundStyle(.logoRed).bold()
}
if tournament.isPlayerRankInadequate(player: p) {
Text("Trop bien classé !").foregroundStyle(.logoRed).bold()
}
PlayerView(player: p).tag(p.id)
.environment(tournament)
}
}
if let p = fetchPlayers.first(where: { $0.license == id }) {
VStack(alignment: .leading, spacing: 0) {
if let pasteString, pasteString.isEmpty == false, unsortedPlayers.first(where: { $0.licenceId == p.license }) != nil {
Text("Déjà inscrit !").foregroundStyle(.logoRed).bold()
}
if tournament.isPlayerAgeInadequate(player: p) {
Text("Âge invalide !").foregroundStyle(.logoRed).bold()
}
if tournament.isPlayerRankInadequate(player: p) {
Text("Trop bien classé !").foregroundStyle(.logoRed).bold()
}
ImportedPlayerView(player: p).tag(p.license!)
}
}
}
if editedTeam == nil {
if createdPlayerIds.isEmpty {
RowButtonView("Bloquer une place") {
_createTeam(checkDuplicates: false, checkHomonym: false)
}
} else {
RowButtonView("Ajouter l'équipe") {
_createTeam(checkDuplicates: true, checkHomonym: true)
}
}
} else {
RowButtonView("Confirmer") {
_updateTeam(checkDuplicates: false)
dismiss()
}
}
} header: {
let _currentSelection = _currentSelection()
let selectedSortedTeams = tournament.selectedSortedTeams()
let rank = _currentSelection.map {
$0.computedRank
}.reduce(0, +)
let teamIndex = selectedSortedTeams.firstIndex(where: { $0.weight >= rank }) ?? selectedSortedTeams.count
if _currentSelection.isEmpty == false, tournament.hideWeight() == false, rank > 0 {
HStack(spacing: 16.0) {
VStack(alignment: .leading, spacing: 0) {
Text("Rang").font(.caption)
Text("#" + (teamIndex + 1).formatted())
}
VStack(alignment: .leading, spacing: 0) { @ViewBuilder
Text("Poids").font(.caption) private func _buildingTeamView() -> some View {
Text(rank.formatted())
}
Spacer()
VStack(alignment: .trailing, spacing: 0) {
Text("").font(.caption)
Text(tournament.cutLabel(index: teamIndex, teamCount: selectedSortedTeams.count))
}
}
// } else {
// Text("Préparation de l'équipe")
}
}
PasteStringSection(
pasteString: pasteString,
editableTextField: $editableTextField,
textHeight: $textHeight,
focusedField: _focusedField,
handlePasteString: handlePasteString,
displayWarningNotEnoughCharacter: $displayWarningNotEnoughCharacter
)
if let pasteString, pasteString.isEmpty == false { TeamSelectionSection(
let sortedPlayers = _searchFilteredPlayers() createdPlayerIds: createdPlayerIds,
createdPlayers: createdPlayers,
if sortedPlayers.isEmpty { unsortedPlayers: unsortedPlayers,
ContentUnavailableView { fetchPlayers: fetchPlayers,
Label("Aucun résultat", systemImage: "person.2.slash") editedTeam: editedTeam,
} description: { pasteString: pasteString,
Text("Aucun joueur classé n'a été trouvé dans ce message. Attention, si un joueur n'a pas joué de tournoi dans les 12 derniers, Padel Club ne pourra pas le trouver.") tournament: tournament,
} actions: { _createTeam: _createTeam,
RowButtonView("Créer un joueur non classé") { _updateTeam: _updateTeam,
selectionSearchField = pasteString dismiss: dismiss,
} _currentSelection: _currentSelection
)
RowButtonView("Chercher dans la base") { if let pasteString, pasteString.isEmpty == false {
presentPlayerSearch = true let sortedPlayers = _searchFilteredPlayers()
}
if sortedPlayers.isEmpty {
ContentUnavailableView {
Label("Aucun résultat", systemImage: "person.2.slash")
} description: {
Text("Aucun joueur classé n'a été trouvé dans ce message. Attention, si un joueur n'a pas joué de tournoi dans les 12 derniers, Padel Club ne pourra pas le trouver.")
} actions: {
RowButtonView("Créer un joueur non classé") {
selectionSearchField = pasteString
}
RowButtonView("Effacer cette recherche") { RowButtonView("Chercher dans la base") {
self.pasteString = nil presentPlayerSearch = true
self.editableTextField = ""
}
} }
} else { RowButtonView("Effacer cette recherche") {
_listOfPlayers(searchFilteredPlayers: sortedPlayers, pasteString: pasteString) self.pasteString = nil
self.editableTextField = ""
}
} }
} else { } else {
_managementView() _listOfPlayers(searchFilteredPlayers: sortedPlayers, pasteString: pasteString)
} }
} else {
_managementView()
}
} }
//
// if let pasteString, pasteString.isEmpty == false {
// let sortedPlayers = _searchFilteredPlayers()
//
// if sortedPlayers.isEmpty {
// ContentUnavailableView {
// Label("Aucun résultat", systemImage: "person.2.slash")
// } description: {
// Text("Aucun joueur classé n'a été trouvé dans ce message. Attention, si un joueur n'a pas joué de tournoi dans les 12 derniers, Padel Club ne pourra pas le trouver.")
// } actions: {
// RowButtonView("Créer un joueur non classé") {
// selectionSearchField = pasteString
// }
//
// RowButtonView("Chercher dans la base") {
// presentPlayerSearch = true
// }
//
// RowButtonView("Effacer cette recherche") {
// self.pasteString = nil
// self.editableTextField = ""
// }
// }
//
// } else {
// _listOfPlayers(searchFilteredPlayers: sortedPlayers, pasteString: pasteString)
// }
// } else {
// _managementView()
// }
// }
@MainActor @MainActor
func hitForSearch(_ ip: ImportedPlayer, _ pasteString: String?) -> Int { func hitForSearch(_ ip: ImportedPlayer, _ pasteString: String?) -> Int {
guard let pasteString else { return 0 } guard let pasteString else { return 0 }
@ -658,6 +633,165 @@ struct AddTeamView: View {
} }
} }
struct TeamSelectionSection: View {
let createdPlayerIds: Set<String>
let createdPlayers: Set<PlayerRegistration>
let unsortedPlayers: [PlayerRegistration]
let fetchPlayers: FetchedResults<ImportedPlayer>
let editedTeam: TeamRegistration?
let pasteString: String?
let tournament: Tournament
let _createTeam: (Bool, Bool) -> Void
let _updateTeam: (Bool) -> Void
let dismiss: DismissAction
let _currentSelection: () -> Set<PlayerRegistration>
var body: some View {
Section {
PlayerList(createdPlayerIds: createdPlayerIds,
createdPlayers: createdPlayers,
unsortedPlayers: unsortedPlayers,
fetchPlayers: fetchPlayers,
editedTeam: editedTeam,
pasteString: pasteString,
tournament: tournament)
ActionButton(editedTeam: editedTeam,
createdPlayerIds: createdPlayerIds,
_createTeam: _createTeam,
_updateTeam: _updateTeam,
dismiss: dismiss)
} header: {
TeamHeader(tournament: tournament,
_currentSelection: _currentSelection)
}
}
}
struct PlayerList: View {
let createdPlayerIds: Set<String>
let createdPlayers: Set<PlayerRegistration>
let unsortedPlayers: [PlayerRegistration]
let fetchPlayers: FetchedResults<ImportedPlayer>
let editedTeam: TeamRegistration?
let pasteString: String?
let tournament: Tournament
var body: some View {
ForEach(createdPlayerIds.sorted(), id: \.self) { id in
if let p = createdPlayers.first(where: { $0.id == id }) {
CreatedPlayerView(player: p, unsortedPlayers: unsortedPlayers, editedTeam: editedTeam, tournament: tournament)
}
if let p = fetchPlayers.first(where: { $0.license == id }) {
FetchedPlayerView(player: p, unsortedPlayers: unsortedPlayers, pasteString: pasteString, tournament: tournament)
}
}
}
}
struct CreatedPlayerView: View {
let player: PlayerRegistration
let unsortedPlayers: [PlayerRegistration]
let editedTeam: TeamRegistration?
let tournament: Tournament
var body: some View {
VStack(alignment: .leading, spacing: 0) {
if let existingPlayer = unsortedPlayers.first(where: { ($0.licenceId == player.licenceId && $0.licenceId != nil) }), editedTeam?.includes(player: existingPlayer) == false {
Text("Déjà inscrit !").foregroundStyle(.logoRed).bold()
}
if tournament.isPlayerAgeInadequate(player: player) {
Text("Âge invalide !").foregroundStyle(.logoRed).bold()
}
if tournament.isPlayerRankInadequate(player: player) {
Text("Trop bien classé !").foregroundStyle(.logoRed).bold()
}
PlayerView(player: player).tag(player.id)
.environment(tournament)
}
}
}
struct FetchedPlayerView: View {
let player: ImportedPlayer
let unsortedPlayers: [PlayerRegistration]
let pasteString: String?
let tournament: Tournament
var body: some View {
VStack(alignment: .leading, spacing: 0) {
if let pasteString, pasteString.isEmpty == false, unsortedPlayers.first(where: { $0.licenceId == player.license }) != nil {
Text("Déjà inscrit !").foregroundStyle(.logoRed).bold()
}
if tournament.isPlayerAgeInadequate(player: player) {
Text("Âge invalide !").foregroundStyle(.logoRed).bold()
}
if tournament.isPlayerRankInadequate(player: player) {
Text("Trop bien classé !").foregroundStyle(.logoRed).bold()
}
ImportedPlayerView(player: player).tag(player.license!)
}
}
}
struct ActionButton: View {
let editedTeam: TeamRegistration?
let createdPlayerIds: Set<String>
let _createTeam: (Bool, Bool) -> Void
let _updateTeam: (Bool) -> Void
let dismiss: DismissAction
var body: some View {
if editedTeam == nil {
if createdPlayerIds.isEmpty {
RowButtonView("Bloquer une place") {
_createTeam(false, false)
}
} else {
RowButtonView("Ajouter l'équipe") {
_createTeam(true, true)
}
}
} else {
RowButtonView("Confirmer") {
_updateTeam(false)
dismiss()
}
}
}
}
struct TeamHeader: View {
let tournament: Tournament
let _currentSelection: () -> Set<PlayerRegistration>
var body: some View {
let currentSelection = _currentSelection()
let selectedSortedTeams = tournament.selectedSortedTeams()
let rank = currentSelection.map { $0.computedRank }.reduce(0, +)
let teamIndex = selectedSortedTeams.firstIndex(where: { $0.weight >= rank }) ?? selectedSortedTeams.count
if !currentSelection.isEmpty, !tournament.hideWeight(), rank > 0 {
HStack(spacing: 16.0) {
VStack(alignment: .leading, spacing: 0) {
Text("Rang").font(.caption)
Text("#" + (teamIndex + 1).formatted())
}
VStack(alignment: .leading, spacing: 0) {
Text("Poids").font(.caption)
Text(rank.formatted())
}
Spacer()
VStack(alignment: .trailing, spacing: 0) {
Text("").font(.caption)
Text(tournament.cutLabel(index: teamIndex, teamCount: selectedSortedTeams.count))
}
}
}
}
}
let testMessages = [ let testMessages = [
"Anthony dovetta ( 3620578 K )et christophe capeau ( 4666443v)", "Anthony dovetta ( 3620578 K )et christophe capeau ( 4666443v)",
""" """

@ -20,7 +20,6 @@ import LeStorage
var updateListenerTask: Task<Void, Never>? = nil var updateListenerTask: Task<Void, Never>? = nil
override init() { override init() {
super.init() super.init()

@ -8,13 +8,15 @@
import Foundation import Foundation
import LeStorage import LeStorage
class Purchase: ModelObject, Storable { class Purchase: ModelObject, SyncedStorable {
static func resourceName() -> String { return "purchases" } static func resourceName() -> String { return "purchases" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] } static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
static func filterByStoreIdentifier() -> Bool { return false } static func filterByStoreIdentifier() -> Bool { return false }
static var relationshipNames: [String] = [] static var relationshipNames: [String] = []
var id: UInt64 var id: UInt64
var lastUpdate: Date
var user: String var user: String
var purchaseDate: Date var purchaseDate: Date
var productId: String var productId: String
@ -22,8 +24,11 @@ class Purchase: ModelObject, Storable {
var revocationDate: Date? = nil var revocationDate: Date? = nil
var expirationDate: Date? = nil var expirationDate: Date? = nil
var storeId: String? { return nil }
init(user: String, transactionId: UInt64, purchaseDate: Date, productId: String, quantity: Int? = nil, revocationDate: Date? = nil, expirationDate: Date? = nil) { init(user: String, transactionId: UInt64, purchaseDate: Date, productId: String, quantity: Int? = nil, revocationDate: Date? = nil, expirationDate: Date? = nil) {
self.id = transactionId self.id = transactionId
self.lastUpdate = Date()
self.user = user self.user = user
self.purchaseDate = purchaseDate self.purchaseDate = purchaseDate
self.productId = productId self.productId = productId
@ -34,6 +39,7 @@ class Purchase: ModelObject, Storable {
enum CodingKeys: String, CodingKey, CaseIterable { enum CodingKeys: String, CodingKey, CaseIterable {
case id case id
case lastUpdate
case user case user
case purchaseDate case purchaseDate
case productId case productId
@ -56,6 +62,7 @@ class Purchase: ModelObject, Storable {
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.id, forKey: .id) try container.encode(self.id, forKey: .id)
try container.encode(self.lastUpdate, forKey: .lastUpdate)
try container.encodeAndEncryptIfPresent(self.user.data(using: .utf8), forKey: .user) try container.encodeAndEncryptIfPresent(self.user.data(using: .utf8), forKey: .user)
try container.encode(self.purchaseDate, forKey: .purchaseDate) try container.encode(self.purchaseDate, forKey: .purchaseDate)
try container.encode(self.productId, forKey: .productId) try container.encode(self.productId, forKey: .productId)
@ -68,6 +75,7 @@ class Purchase: ModelObject, Storable {
let container = try decoder.container(keyedBy: CodingKeys.self) let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(UInt64.self, forKey: .id) self.id = try container.decode(UInt64.self, forKey: .id)
self.lastUpdate = try container.decodeIfPresent(Date.self, forKey: .lastUpdate) ?? Date()
self.user = try container.decodeEncrypted(key: .user) self.user = try container.decodeEncrypted(key: .user)
self.purchaseDate = try container.decode(Date.self, forKey: .purchaseDate) self.purchaseDate = try container.decode(Date.self, forKey: .purchaseDate)
self.productId = try container.decode(String.self, forKey: .productId) self.productId = try container.decode(String.self, forKey: .productId)

@ -10,7 +10,7 @@ import LeStorage
struct AccountView: View { struct AccountView: View {
var user: User var user: CustomUser
var handler: () -> () var handler: () -> ()
var body: some View { var body: some View {

@ -74,7 +74,7 @@ struct LoginView: View {
} }
} }
var handler: (User) -> () var handler: (CustomUser) -> ()
var body: some View { var body: some View {
@ -195,7 +195,7 @@ struct LoginView: View {
self.isLoading = true self.isLoading = true
do { do {
let service = try StoreCenter.main.service() let service = try StoreCenter.main.service()
let user: User = try await service.login( let user: CustomUser = try await service.login(
username: self.username, username: self.username,
password: self.password) password: self.password)
self.dataStore.user = user self.dataStore.user = user

@ -232,7 +232,7 @@ struct UserCreationFormView: View {
country: country) country: country)
let service: Services = try StoreCenter.main.service() let service: Services = try StoreCenter.main.service()
let _: User = try await service.createAccount(user: userCreationForm) let _: CustomUser = try await service.createAccount(user: userCreationForm)
DispatchQueue.main.async { DispatchQueue.main.async {
self.isLoading = false self.isLoading = false

@ -31,7 +31,7 @@ final class ServerDataTests: XCTestCase {
func login() async throws { func login() async throws {
// print("LOGIN!") // print("LOGIN!")
let _: User = try await StoreCenter.main.service().login(username: self.username, password: self.password) let _: CustomUser = try await StoreCenter.main.service().login(username: self.username, password: self.password)
} }
func testClub() async throws { func testClub() async throws {
@ -48,6 +48,7 @@ final class ServerDataTests: XCTestCase {
club.courtCount = 3 club.courtCount = 3
let inserted_club: Club = try await StoreCenter.main.service().post(club) let inserted_club: Club = try await StoreCenter.main.service().post(club)
assert(inserted_club.lastUpdate == club.lastUpdate)
assert(inserted_club.name == club.name) assert(inserted_club.name == club.name)
assert(inserted_club.acronym == club.acronym) assert(inserted_club.acronym == club.acronym)
assert(inserted_club.zipCode == club.zipCode) assert(inserted_club.zipCode == club.zipCode)
@ -59,6 +60,7 @@ final class ServerDataTests: XCTestCase {
assert(inserted_club.broadcastCode != nil) assert(inserted_club.broadcastCode != nil)
inserted_club.phone = "123456" inserted_club.phone = "123456"
inserted_club.lastUpdate = Date()
let updated_club: Club = try await StoreCenter.main.service().put(inserted_club) let updated_club: Club = try await StoreCenter.main.service().put(inserted_club)
assert(updated_club.phone == inserted_club.phone) assert(updated_club.phone == inserted_club.phone)
@ -66,7 +68,7 @@ final class ServerDataTests: XCTestCase {
} }
func testLogin() async throws { func testLogin() async throws {
let user: User = try await StoreCenter.main.service().login(username: self.username, password: self.password) let user: CustomUser = try await StoreCenter.main.service().login(username: self.username, password: self.password)
assert(user.username == "test") assert(user.username == "test")
} }
@ -87,6 +89,7 @@ final class ServerDataTests: XCTestCase {
let e = try await StoreCenter.main.service().post(event) let e = try await StoreCenter.main.service().post(event)
assert(e.name == event.name) assert(e.name == event.name)
assert(e.lastUpdate == event.lastUpdate)
assert(e.tenupId == event.tenupId) assert(e.tenupId == event.tenupId)
} }
@ -102,6 +105,7 @@ final class ServerDataTests: XCTestCase {
let tournament = Tournament(event: eventId, name: "RG Homme", startDate: Date(), endDate: nil, creationDate: Date(), isPrivate: false, groupStageFormat: MatchFormat.megaTie, roundFormat: MatchFormat.nineGames, loserRoundFormat: MatchFormat.nineGamesDecisivePoint, groupStageSortMode: GroupStageOrderingMode.snake, groupStageCount: 2, rankSourceDate: Date(), dayDuration: 5, teamCount: 3, teamSorting: TeamSortingType.rank, federalCategory: TournamentCategory.mix, federalLevelCategory: TournamentLevel.p1000, federalAgeCategory: FederalTournamentAge.a45, closedRegistrationDate: Date(), groupStageAdditionalQualified: 4, courtCount: 9, prioritizeClubMembers: true, qualifiedPerGroupStage: 1, teamsPerGroupStage: 2, entryFee: 30.0, additionalEstimationDuration: 5, isDeleted: true, publishTeams: true, publishSummons: true, publishGroupStages: true, publishBrackets: true, shouldVerifyBracket: true, shouldVerifyGroupStage: true, hideTeamsWeight: true, publishTournament: true, hidePointsEarned: true, publishRankings: true, loserBracketMode: .manual) let tournament = Tournament(event: eventId, name: "RG Homme", startDate: Date(), endDate: nil, creationDate: Date(), isPrivate: false, groupStageFormat: MatchFormat.megaTie, roundFormat: MatchFormat.nineGames, loserRoundFormat: MatchFormat.nineGamesDecisivePoint, groupStageSortMode: GroupStageOrderingMode.snake, groupStageCount: 2, rankSourceDate: Date(), dayDuration: 5, teamCount: 3, teamSorting: TeamSortingType.rank, federalCategory: TournamentCategory.mix, federalLevelCategory: TournamentLevel.p1000, federalAgeCategory: FederalTournamentAge.a45, closedRegistrationDate: Date(), groupStageAdditionalQualified: 4, courtCount: 9, prioritizeClubMembers: true, qualifiedPerGroupStage: 1, teamsPerGroupStage: 2, entryFee: 30.0, additionalEstimationDuration: 5, isDeleted: true, publishTeams: true, publishSummons: true, publishGroupStages: true, publishBrackets: true, shouldVerifyBracket: true, shouldVerifyGroupStage: true, hideTeamsWeight: true, publishTournament: true, hidePointsEarned: true, publishRankings: true, loserBracketMode: .manual)
let t = try await StoreCenter.main.service().post(tournament) let t = try await StoreCenter.main.service().post(tournament)
assert(t.lastUpdate.formatted() == tournament.lastUpdate.formatted())
assert(t.event == tournament.event) assert(t.event == tournament.event)
assert(t.name == tournament.name) assert(t.name == tournament.name)
assert(t.startDate.formatted() == tournament.startDate.formatted()) assert(t.startDate.formatted() == tournament.startDate.formatted())
@ -151,9 +155,12 @@ final class ServerDataTests: XCTestCase {
} }
let groupStage = GroupStage(tournament: tournamentId, index: 2, size: 3, matchFormat: MatchFormat.nineGames, startDate: Date(), name: "Yeah!", step: 1) let groupStage = GroupStage(tournament: tournamentId, index: 2, size: 3, matchFormat: MatchFormat.nineGames, startDate: Date(), name: "Yeah!", step: 1)
groupStage.storeId = "123"
let gs: GroupStage = try await StoreCenter.main.service().post(groupStage) let gs: GroupStage = try await StoreCenter.main.service().post(groupStage)
assert(gs.tournament == groupStage.tournament) assert(gs.tournament == groupStage.tournament)
assert(gs.storeId == groupStage.storeId)
assert(gs.lastUpdate == groupStage.lastUpdate)
assert(gs.name == groupStage.name) assert(gs.name == groupStage.name)
assert(gs.index == groupStage.index) assert(gs.index == groupStage.index)
assert(gs.size == groupStage.size) assert(gs.size == groupStage.size)
@ -175,9 +182,12 @@ final class ServerDataTests: XCTestCase {
let parentRoundId = rounds.first?.id let parentRoundId = rounds.first?.id
let round = Round(tournament: tournamentId, index: 1, parent: parentRoundId, matchFormat: MatchFormat.nineGames, startDate: Date(), groupStageLoserBracket: false, loserBracketMode: .manual) let round = Round(tournament: tournamentId, index: 1, parent: parentRoundId, matchFormat: MatchFormat.nineGames, startDate: Date(), groupStageLoserBracket: false, loserBracketMode: .manual)
round.storeId = "abc"
let r: Round = try await StoreCenter.main.service().post(round) let r: Round = try await StoreCenter.main.service().post(round)
assert(r.storeId == round.storeId)
assert(r.tournament == round.tournament) assert(r.tournament == round.tournament)
assert(r.lastUpdate == round.lastUpdate)
assert(r.index == round.index) assert(r.index == round.index)
assert(r.parent == round.parent) assert(r.parent == round.parent)
assert(r.matchFormat == round.matchFormat) assert(r.matchFormat == round.matchFormat)
@ -201,10 +211,13 @@ final class ServerDataTests: XCTestCase {
} }
let teamRegistration = TeamRegistration(tournament: tournamentId, groupStage: groupStageId, registrationDate: Date(), callDate: Date(), bracketPosition: 1, groupStagePosition: 2, comment: "comment", source: "source", sourceValue: "source V", logo: "logo", name: "Stax", walkOut: true, wildCardBracket: true, wildCardGroupStage: true, weight: 1, lockedWeight: 11, confirmationDate: Date(), qualified: true) let teamRegistration = TeamRegistration(tournament: tournamentId, groupStage: groupStageId, registrationDate: Date(), callDate: Date(), bracketPosition: 1, groupStagePosition: 2, comment: "comment", source: "source", sourceValue: "source V", logo: "logo", name: "Stax", walkOut: true, wildCardBracket: true, wildCardGroupStage: true, weight: 1, lockedWeight: 11, confirmationDate: Date(), qualified: true)
teamRegistration.storeId = "123"
let tr: TeamRegistration = try await StoreCenter.main.service().post(teamRegistration) let tr: TeamRegistration = try await StoreCenter.main.service().post(teamRegistration)
assert(tr.storeId == teamRegistration.storeId)
assert(tr.tournament == teamRegistration.tournament) assert(tr.tournament == teamRegistration.tournament)
assert(tr.lastUpdate == teamRegistration.lastUpdate)
assert(tr.groupStage == teamRegistration.groupStage) assert(tr.groupStage == teamRegistration.groupStage)
assert(tr.registrationDate != nil) assert(tr.registrationDate != nil)
assert(tr.callDate != nil) assert(tr.callDate != nil)
@ -234,8 +247,11 @@ final class ServerDataTests: XCTestCase {
} }
let playerRegistration = PlayerRegistration(teamRegistration: teamRegistrationId, firstName: "juan", lastName: "lebron", licenceId: "123", rank: 11, paymentType: PlayerRegistration.PlayerPaymentType.cash, sex: PlayerRegistration.PlayerSexType.male, tournamentPlayed: 2, points: 33, clubName: "le club", ligueName: "la league", assimilation: "ass", phoneNumber: "123123", email: "email@email.com", birthdate: nil, computedRank: 222, source: PlayerRegistration.PlayerDataSource.frenchFederation, hasArrived: true) let playerRegistration = PlayerRegistration(teamRegistration: teamRegistrationId, firstName: "juan", lastName: "lebron", licenceId: "123", rank: 11, paymentType: PlayerRegistration.PlayerPaymentType.cash, sex: PlayerRegistration.PlayerSexType.male, tournamentPlayed: 2, points: 33, clubName: "le club", ligueName: "la league", assimilation: "ass", phoneNumber: "123123", email: "email@email.com", birthdate: nil, computedRank: 222, source: PlayerRegistration.PlayerDataSource.frenchFederation, hasArrived: true)
playerRegistration.storeId = "123"
let pr: PlayerRegistration = try await StoreCenter.main.service().post(playerRegistration) let pr: PlayerRegistration = try await StoreCenter.main.service().post(playerRegistration)
assert(pr.storeId == playerRegistration.storeId)
assert(pr.lastName == playerRegistration.lastName)
assert(pr.teamRegistration == playerRegistration.teamRegistration) assert(pr.teamRegistration == playerRegistration.teamRegistration)
assert(pr.firstName == playerRegistration.firstName) assert(pr.firstName == playerRegistration.firstName)
assert(pr.lastName == playerRegistration.lastName) assert(pr.lastName == playerRegistration.lastName)
@ -267,8 +283,11 @@ final class ServerDataTests: XCTestCase {
let parentRoundId = rounds.first?.id let parentRoundId = rounds.first?.id
let match: Match = Match(round: parentRoundId, groupStage: nil, startDate: Date(), endDate: Date(), index: 2, matchFormat: MatchFormat.twoSets, servingTeamId: teamRegistrationId, winningTeamId: teamRegistrationId, losingTeamId: teamRegistrationId, disabled: true, courtIndex: 1, confirmed: true) let match: Match = Match(round: parentRoundId, groupStage: nil, startDate: Date(), endDate: Date(), index: 2, matchFormat: MatchFormat.twoSets, servingTeamId: teamRegistrationId, winningTeamId: teamRegistrationId, losingTeamId: teamRegistrationId, disabled: true, courtIndex: 1, confirmed: true)
match.storeId = "123"
let m: Match = try await StoreCenter.main.service().post(match) let m: Match = try await StoreCenter.main.service().post(match)
assert(m.storeId == match.storeId)
assert(m.lastUpdate == match.lastUpdate)
assert(m.round == match.round) assert(m.round == match.round)
assert(m.groupStage == match.groupStage) assert(m.groupStage == match.groupStage)
assert(m.startDate != nil) assert(m.startDate != nil)
@ -297,8 +316,11 @@ final class ServerDataTests: XCTestCase {
return return
} }
let teamScore = TeamScore(match: matchId, teamRegistration: teamRegistrationId, score: "6/6", walkOut: 1, luckyLoser: 1) let teamScore = TeamScore(match: matchId, teamRegistration: teamRegistrationId, score: "6/6", walkOut: 1, luckyLoser: 1)
teamScore.storeId = "!23"
let ts: TeamScore = try await StoreCenter.main.service().post(teamScore) let ts: TeamScore = try await StoreCenter.main.service().post(teamScore)
assert(ts.storeId == teamScore.storeId)
assert(ts.lastUpdate == teamScore.lastUpdate)
assert(ts.match == teamScore.match) assert(ts.match == teamScore.match)
assert(ts.teamRegistration == teamScore.teamRegistration) assert(ts.teamRegistration == teamScore.teamRegistration)
assert(ts.score == teamScore.score) assert(ts.score == teamScore.score)
@ -318,6 +340,7 @@ final class ServerDataTests: XCTestCase {
let court = Court(index: 1, club: clubId, name: "Philippe Chatrier", exitAllowed: true, indoor: true) let court = Court(index: 1, club: clubId, name: "Philippe Chatrier", exitAllowed: true, indoor: true)
let c: Court = try await StoreCenter.main.service().post(court) let c: Court = try await StoreCenter.main.service().post(court)
assert(c.lastUpdate == court.lastUpdate)
assert(c.club == court.club) assert(c.club == court.club)
assert(c.name == court.name) assert(c.name == court.name)
assert(c.index == court.index) assert(c.index == court.index)
@ -337,6 +360,7 @@ final class ServerDataTests: XCTestCase {
let dateInterval = DateInterval(event: eventId, courtIndex: 1, startDate: Date(), endDate: Date()) let dateInterval = DateInterval(event: eventId, courtIndex: 1, startDate: Date(), endDate: Date())
let di: PadelClub.DateInterval = try await StoreCenter.main.service().post(dateInterval) let di: PadelClub.DateInterval = try await StoreCenter.main.service().post(dateInterval)
assert(di.lastUpdate == dateInterval.lastUpdate)
assert(di.event == dateInterval.event) assert(di.event == dateInterval.event)
assert(di.courtIndex == dateInterval.courtIndex) assert(di.courtIndex == dateInterval.courtIndex)
assert(di.startDate.formatted() == dateInterval.startDate.formatted()) assert(di.startDate.formatted() == dateInterval.startDate.formatted())
@ -354,10 +378,12 @@ final class ServerDataTests: XCTestCase {
let transactionId = UInt64.random(in: 0...100000) let transactionId = UInt64.random(in: 0...100000)
let quantity = Int.random(in: 0...10) let quantity = Int.random(in: 0...10)
let purchase: Purchase = Purchase(user: userId, transactionId: transactionId, purchaseDate: Date(), productId: "app.padelclub.productId", quantity: quantity, revocationDate: Date(), expirationDate: Date()) let purchase: Purchase = Purchase(transactionId: transactionId, purchaseDate: Date(), productId: "app.padelclub.productId", quantity: quantity, revocationDate: Date(), expirationDate: Date())
let p: Purchase = try await StoreCenter.main.service().post(purchase) let p: Purchase = try await StoreCenter.main.service().post(purchase)
assert(p.id == purchase.id) assert(p.id == purchase.id)
assert(p.lastUpdate == purchase.lastUpdate)
assert(p.user == purchase.user) assert(p.user == purchase.user)
assert(p.productId == purchase.productId) assert(p.productId == purchase.productId)
assert(p.purchaseDate.formatted() == purchase.purchaseDate.formatted()) assert(p.purchaseDate.formatted() == purchase.purchaseDate.formatted())

@ -0,0 +1,33 @@
//
// SynchronizationTests.swift
// PadelClubTests
//
// Created by Laurent Morvillier on 09/10/2024.
//
import Testing
import LeStorage
@testable import PadelClub
struct SynchronizationTests {
let username: String = "laurent"
let password: String = "StaxKikoo12"
init() {
StoreCenter.main.synchronizationApiURL = "http://127.0.0.1:8000/roads/"
}
@Test func synchronizationTest() async throws {
_ = try await self.login()
try await StoreCenter.main.synchronizeLastUpdates()
}
func login() async throws -> CustomUser {
let user: CustomUser = try await StoreCenter.main.service().login(username: self.username, password: self.password)
return user
}
}

@ -24,8 +24,8 @@ final class UserDataTests: XCTestCase {
func testUserCreation() async throws { func testUserCreation() async throws {
let userCreationForm = UserCreationForm(user: User.placeHolder(), username: self.username, password: self.password, firstName: "jean", lastName: "coco", email: "test@lolomo.com", phone: "0123", country: "France") let userCreationForm = UserCreationForm(user: CustomUser.placeHolder(), username: self.username, password: self.password, firstName: "jean", lastName: "coco", email: "test@lolomo.com", phone: "0123", country: "France")
let user: User = try await StoreCenter.main.service().createAccount(user: userCreationForm) let user: CustomUser = try await StoreCenter.main.service().createAccount(user: userCreationForm)
assert(user.username == userCreationForm.username) assert(user.username == userCreationForm.username)
assert(user.firstName == userCreationForm.firstName) assert(user.firstName == userCreationForm.firstName)
@ -36,8 +36,8 @@ final class UserDataTests: XCTestCase {
} }
func login() async throws -> User { func login() async throws -> CustomUser {
let user: User = try await StoreCenter.main.service().login(username: self.username, password: self.password) let user: CustomUser = try await StoreCenter.main.service().login(username: self.username, password: self.password)
return user return user
} }

Loading…
Cancel
Save