Merge remote-tracking branch 'refs/remotes/origin/main'

sync_v2
Raz 8 months ago
commit 4a378f8737
  1. 330
      PadelClub.xcodeproj/project.pbxproj
  2. 3
      PadelClub/AppDelegate.swift
  3. 104
      PadelClub/Data/Club.swift
  4. 49
      PadelClub/Data/Court.swift
  5. 262
      PadelClub/Data/CustomUser.swift
  6. 206
      PadelClub/Data/DataStore.swift
  7. 57
      PadelClub/Data/DateInterval.swift
  8. 71
      PadelClub/Data/DrawLog.swift
  9. 56
      PadelClub/Data/Event.swift
  10. 146
      PadelClub/Data/Gen/BaseClub.swift
  11. 89
      PadelClub/Data/Gen/BaseCourt.swift
  12. 202
      PadelClub/Data/Gen/BaseCustomUser.swift
  13. 76
      PadelClub/Data/Gen/BaseDateInterval.swift
  14. 96
      PadelClub/Data/Gen/BaseDrawLog.swift
  15. 96
      PadelClub/Data/Gen/BaseEvent.swift
  16. 103
      PadelClub/Data/Gen/BaseGroupStage.swift
  17. 152
      PadelClub/Data/Gen/BaseMatch.swift
  18. 159
      PadelClub/Data/Gen/BaseMatchScheduler.swift
  19. 118
      PadelClub/Data/Gen/BaseMonthData.swift
  20. 202
      PadelClub/Data/Gen/BasePlayerRegistration.swift
  21. 95
      PadelClub/Data/Gen/BasePurchase.swift
  22. 103
      PadelClub/Data/Gen/BaseRound.swift
  23. 195
      PadelClub/Data/Gen/BaseTeamRegistration.swift
  24. 95
      PadelClub/Data/Gen/BaseTeamScore.swift
  25. 473
      PadelClub/Data/Gen/BaseTournament.swift
  26. 92
      PadelClub/Data/Gen/Club.json
  27. 42
      PadelClub/Data/Gen/Court.json
  28. 136
      PadelClub/Data/Gen/CustomUser.json
  29. 33
      PadelClub/Data/Gen/DateInterval.json
  30. 47
      PadelClub/Data/Gen/Drawlog.json
  31. 48
      PadelClub/Data/Gen/Event.json
  32. 53
      PadelClub/Data/Gen/GroupStage.json
  33. 83
      PadelClub/Data/Gen/Match.json
  34. 84
      PadelClub/Data/Gen/MatchScheduler.json
  35. 71
      PadelClub/Data/Gen/MonthData.json
  36. 122
      PadelClub/Data/Gen/PlayerRegistration.json
  37. 51
      PadelClub/Data/Gen/Purchase.json
  38. 53
      PadelClub/Data/Gen/Round.json
  39. 118
      PadelClub/Data/Gen/TeamRegistration.json
  40. 44
      PadelClub/Data/Gen/TeamScore.json
  41. 271
      PadelClub/Data/Gen/Tournament.json
  42. 566
      PadelClub/Data/Gen/generator.py
  43. 145
      PadelClub/Data/GroupStage.swift
  44. 198
      PadelClub/Data/Match.swift
  45. 123
      PadelClub/Data/MatchScheduler.swift
  46. 57
      PadelClub/Data/MonthData.swift
  47. 53
      PadelClub/Data/PlayerPaymentType.swift
  48. 233
      PadelClub/Data/PlayerRegistration.swift
  49. 22
      PadelClub/Data/README.md
  50. 241
      PadelClub/Data/Round.swift
  51. 394
      PadelClub/Data/TeamRegistration.swift
  52. 115
      PadelClub/Data/TeamScore.swift
  53. 606
      PadelClub/Data/Tournament.swift
  54. 33
      PadelClub/Data/TournamentLibrary.swift
  55. 56
      PadelClub/Data/TournamentStore.swift
  56. 250
      PadelClub/Data/User.swift
  57. 8
      PadelClub/Extensions/CodingContainer+Extensions.swift
  58. 4
      PadelClub/Extensions/NumberFormatter+Extensions.swift
  59. 2
      PadelClub/Utils/FileImportManager.swift
  60. 152
      PadelClub/Utils/Patcher.swift
  61. 4
      PadelClub/Utils/SourceFileManager.swift
  62. 10
      PadelClub/Utils/URLs.swift
  63. 1
      PadelClub/ViewModel/Screen.swift
  64. 6
      PadelClub/Views/Calling/CallSettingsView.swift
  65. 4
      PadelClub/Views/Calling/CallView.swift
  66. 8
      PadelClub/Views/Calling/SendToAllView.swift
  67. 6
      PadelClub/Views/Calling/TeamsCallingView.swift
  68. 6
      PadelClub/Views/Cashier/CashierDetailView.swift
  69. 13
      PadelClub/Views/Cashier/CashierSettingsView.swift
  70. 12
      PadelClub/Views/Club/ClubDetailView.swift
  71. 2
      PadelClub/Views/Club/ClubSearchView.swift
  72. 6
      PadelClub/Views/Components/Labels.swift
  73. 2
      PadelClub/Views/Components/RowButtonView.swift
  74. 12
      PadelClub/Views/GroupStage/Components/GroupStageSettingsView.swift
  75. 4
      PadelClub/Views/GroupStage/Components/GroupStageTeamView.swift
  76. 10
      PadelClub/Views/GroupStage/GroupStageView.swift
  77. 22
      PadelClub/Views/GroupStage/GroupStagesSettingsView.swift
  78. 2
      PadelClub/Views/GroupStage/GroupStagesView.swift
  79. 31
      PadelClub/Views/GroupStage/LoserBracketFromGroupStageView.swift
  80. 2
      PadelClub/Views/Match/Components/MatchDateView.swift
  81. 18
      PadelClub/Views/Match/MatchDetailView.swift
  82. 2
      PadelClub/Views/Match/MatchRowView.swift
  83. 38
      PadelClub/Views/Match/MatchSetupView.swift
  84. 19
      PadelClub/Views/Navigation/Agenda/EventListView.swift
  85. 4
      PadelClub/Views/Navigation/Agenda/TournamentSubscriptionView.swift
  86. 12
      PadelClub/Views/Navigation/MainView.swift
  87. 198
      PadelClub/Views/Navigation/Toolbox/APICallsListView.swift
  88. 34
      PadelClub/Views/Navigation/Toolbox/DebugSettingsView.swift
  89. 209
      PadelClub/Views/Navigation/Toolbox/ToolboxView.swift
  90. 45
      PadelClub/Views/Navigation/Umpire/NetworkStatusView.swift
  91. 4
      PadelClub/Views/Navigation/Umpire/PadelClubView.swift
  92. 25
      PadelClub/Views/Navigation/Umpire/UmpireView.swift
  93. 4
      PadelClub/Views/Planning/GroupStageScheduleEditorView.swift
  94. 4
      PadelClub/Views/Planning/LoserRoundScheduleEditorView.swift
  95. 4
      PadelClub/Views/Planning/LoserRoundStepScheduleEditorView.swift
  96. 4
      PadelClub/Views/Planning/MatchScheduleEditorView.swift
  97. 24
      PadelClub/Views/Planning/PlanningSettingsView.swift
  98. 4
      PadelClub/Views/Planning/PlanningView.swift
  99. 4
      PadelClub/Views/Planning/RoundScheduleEditorView.swift
  100. 10
      PadelClub/Views/Planning/SchedulerView.swift
  101. Some files were not shown because too many files have changed in this diff Show More

@ -17,12 +17,119 @@
C425D4122B6D249E002A7B48 /* PadelClubTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C425D4112B6D249E002A7B48 /* PadelClubTests.swift */; };
C425D41C2B6D249E002A7B48 /* PadelClubUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C425D41B2B6D249E002A7B48 /* PadelClubUITests.swift */; };
C425D41E2B6D249E002A7B48 /* PadelClubUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C425D41D2B6D249E002A7B48 /* PadelClubUITestsLaunchTests.swift */; };
C4339BFB2CFF7D68004E5F09 /* ShareModelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4339BFA2CFF7D64004E5F09 /* ShareModelView.swift */; };
C4339BFC2CFF7D68004E5F09 /* ShareModelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4339BFA2CFF7D64004E5F09 /* ShareModelView.swift */; };
C4339BFD2CFF7D68004E5F09 /* ShareModelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4339BFA2CFF7D64004E5F09 /* ShareModelView.swift */; };
C4489BE22C05BF5000043F3D /* DebugSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4489BE12C05BF5000043F3D /* DebugSettingsView.swift */; };
C44B79112BBDA63A00906534 /* Locale+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44B79102BBDA63A00906534 /* Locale+Extensions.swift */; };
C45BAE3B2BC6DF10002EEC8A /* SyncedProducts.storekit in Resources */ = {isa = PBXBuildFile; fileRef = C45BAE3A2BC6DF10002EEC8A /* SyncedProducts.storekit */; };
C45BAE442BCA753E002EEC8A /* Purchase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45BAE432BCA753E002EEC8A /* Purchase.swift */; };
C4607A7D2C04DDE2004CB781 /* APICallsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4607A7C2C04DDE2004CB781 /* APICallsListView.swift */; };
C471D1542D0C8FED0068091F /* Drawlog.json in Resources */ = {isa = PBXBuildFile; fileRef = C471D1532D0C8FE80068091F /* Drawlog.json */; };
C471D1552D0C8FED0068091F /* Drawlog.json in Resources */ = {isa = PBXBuildFile; fileRef = C471D1532D0C8FE80068091F /* Drawlog.json */; };
C471D1562D0C8FED0068091F /* Drawlog.json in Resources */ = {isa = PBXBuildFile; fileRef = C471D1532D0C8FE80068091F /* Drawlog.json */; };
C471D1582D0C91FE0068091F /* BaseDrawLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = C471D1572D0C91FE0068091F /* BaseDrawLog.swift */; };
C471D1592D0C91FE0068091F /* BaseDrawLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = C471D1572D0C91FE0068091F /* BaseDrawLog.swift */; };
C471D15A2D0C91FF0068091F /* BaseDrawLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = C471D1572D0C91FE0068091F /* BaseDrawLog.swift */; };
C488C7E92CC7D16F0082001F /* generator.py in Resources */ = {isa = PBXBuildFile; fileRef = C488C7E52CC7D1660082001F /* generator.py */; };
C488C7EA2CC7D16F0082001F /* generator.py in Resources */ = {isa = PBXBuildFile; fileRef = C488C7E52CC7D1660082001F /* generator.py */; };
C488C7EB2CC7D16F0082001F /* generator.py in Resources */ = {isa = PBXBuildFile; fileRef = C488C7E52CC7D1660082001F /* generator.py */; };
C488C7F12CC7D22D0082001F /* Club.json in Sources */ = {isa = PBXBuildFile; fileRef = C488C7EC2CC7D2290082001F /* Club.json */; };
C488C7F22CC7D22D0082001F /* Club.json in Sources */ = {isa = PBXBuildFile; fileRef = C488C7EC2CC7D2290082001F /* Club.json */; };
C488C7FF2CC7DCB80082001F /* BaseClub.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C7FE2CC7DCB80082001F /* BaseClub.swift */; };
C488C8002CC7DCB80082001F /* BaseClub.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C7FE2CC7DCB80082001F /* BaseClub.swift */; };
C488C8012CC7DCB80082001F /* BaseClub.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C7FE2CC7DCB80082001F /* BaseClub.swift */; };
C488C8032CC7E1E40082001F /* BaseCourt.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8022CC7E1E40082001F /* BaseCourt.swift */; };
C488C8042CC7E1E40082001F /* BaseCourt.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8022CC7E1E40082001F /* BaseCourt.swift */; };
C488C8052CC7E1E40082001F /* BaseCourt.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8022CC7E1E40082001F /* BaseCourt.swift */; };
C488C8202CC7E4240082001F /* Event.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8162CC7E4240082001F /* Event.json */; };
C488C8212CC7E4240082001F /* Court.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8132CC7E4240082001F /* Court.json */; };
C488C8222CC7E4240082001F /* Tournament.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81F2CC7E4240082001F /* Tournament.json */; };
C488C8232CC7E4240082001F /* CustomUser.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8142CC7E4240082001F /* CustomUser.json */; };
C488C8242CC7E4240082001F /* Round.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81C2CC7E4240082001F /* Round.json */; };
C488C8252CC7E4240082001F /* MatchScheduler.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8192CC7E4240082001F /* MatchScheduler.json */; };
C488C8262CC7E4240082001F /* DateInterval.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8152CC7E4240082001F /* DateInterval.json */; };
C488C8272CC7E4240082001F /* TeamRegistration.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81D2CC7E4240082001F /* TeamRegistration.json */; };
C488C8282CC7E4240082001F /* GroupStage.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8172CC7E4240082001F /* GroupStage.json */; };
C488C8292CC7E4240082001F /* MonthData.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81A2CC7E4240082001F /* MonthData.json */; };
C488C82A2CC7E4240082001F /* TeamScore.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81E2CC7E4240082001F /* TeamScore.json */; };
C488C82B2CC7E4240082001F /* Match.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8182CC7E4240082001F /* Match.json */; };
C488C82C2CC7E4240082001F /* PlayerRegistration.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81B2CC7E4240082001F /* PlayerRegistration.json */; };
C488C82D2CC7E4240082001F /* BaseDateInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8072CC7E4240082001F /* BaseDateInterval.swift */; };
C488C82E2CC7E4240082001F /* BaseMonthData.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80C2CC7E4240082001F /* BaseMonthData.swift */; };
C488C82F2CC7E4240082001F /* BaseTeamRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80F2CC7E4240082001F /* BaseTeamRegistration.swift */; };
C488C8302CC7E4240082001F /* BaseGroupStage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8092CC7E4240082001F /* BaseGroupStage.swift */; };
C488C8312CC7E4240082001F /* BaseCustomUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8062CC7E4240082001F /* BaseCustomUser.swift */; };
C488C8322CC7E4240082001F /* BaseMatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80A2CC7E4240082001F /* BaseMatch.swift */; };
C488C8332CC7E4240082001F /* BaseEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8082CC7E4240082001F /* BaseEvent.swift */; };
C488C8342CC7E4240082001F /* BaseRound.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80E2CC7E4240082001F /* BaseRound.swift */; };
C488C8352CC7E4240082001F /* BaseMatchScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80B2CC7E4240082001F /* BaseMatchScheduler.swift */; };
C488C8372CC7E4240082001F /* BasePlayerRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80D2CC7E4240082001F /* BasePlayerRegistration.swift */; };
C488C8382CC7E4240082001F /* BaseTeamScore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8102CC7E4240082001F /* BaseTeamScore.swift */; };
C488C8392CC7E4240082001F /* BaseTournament.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8112CC7E4240082001F /* BaseTournament.swift */; };
C488C83A2CC7E4240082001F /* Event.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8162CC7E4240082001F /* Event.json */; };
C488C83B2CC7E4240082001F /* Court.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8132CC7E4240082001F /* Court.json */; };
C488C83C2CC7E4240082001F /* Tournament.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81F2CC7E4240082001F /* Tournament.json */; };
C488C83D2CC7E4240082001F /* CustomUser.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8142CC7E4240082001F /* CustomUser.json */; };
C488C83E2CC7E4240082001F /* Round.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81C2CC7E4240082001F /* Round.json */; };
C488C83F2CC7E4240082001F /* MatchScheduler.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8192CC7E4240082001F /* MatchScheduler.json */; };
C488C8402CC7E4240082001F /* DateInterval.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8152CC7E4240082001F /* DateInterval.json */; };
C488C8412CC7E4240082001F /* TeamRegistration.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81D2CC7E4240082001F /* TeamRegistration.json */; };
C488C8422CC7E4240082001F /* GroupStage.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8172CC7E4240082001F /* GroupStage.json */; };
C488C8432CC7E4240082001F /* MonthData.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81A2CC7E4240082001F /* MonthData.json */; };
C488C8442CC7E4240082001F /* TeamScore.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81E2CC7E4240082001F /* TeamScore.json */; };
C488C8452CC7E4240082001F /* Match.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8182CC7E4240082001F /* Match.json */; };
C488C8462CC7E4240082001F /* PlayerRegistration.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81B2CC7E4240082001F /* PlayerRegistration.json */; };
C488C8472CC7E4240082001F /* BaseDateInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8072CC7E4240082001F /* BaseDateInterval.swift */; };
C488C8482CC7E4240082001F /* BaseMonthData.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80C2CC7E4240082001F /* BaseMonthData.swift */; };
C488C8492CC7E4240082001F /* BaseTeamRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80F2CC7E4240082001F /* BaseTeamRegistration.swift */; };
C488C84A2CC7E4240082001F /* BaseGroupStage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8092CC7E4240082001F /* BaseGroupStage.swift */; };
C488C84B2CC7E4240082001F /* BaseCustomUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8062CC7E4240082001F /* BaseCustomUser.swift */; };
C488C84C2CC7E4240082001F /* BaseMatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80A2CC7E4240082001F /* BaseMatch.swift */; };
C488C84D2CC7E4240082001F /* BaseEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8082CC7E4240082001F /* BaseEvent.swift */; };
C488C84E2CC7E4240082001F /* BaseRound.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80E2CC7E4240082001F /* BaseRound.swift */; };
C488C84F2CC7E4240082001F /* BaseMatchScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80B2CC7E4240082001F /* BaseMatchScheduler.swift */; };
C488C8512CC7E4240082001F /* BasePlayerRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80D2CC7E4240082001F /* BasePlayerRegistration.swift */; };
C488C8522CC7E4240082001F /* BaseTeamScore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8102CC7E4240082001F /* BaseTeamScore.swift */; };
C488C8532CC7E4240082001F /* BaseTournament.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8112CC7E4240082001F /* BaseTournament.swift */; };
C488C8542CC7E4240082001F /* BaseDateInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8072CC7E4240082001F /* BaseDateInterval.swift */; };
C488C8552CC7E4240082001F /* BaseMonthData.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80C2CC7E4240082001F /* BaseMonthData.swift */; };
C488C8562CC7E4240082001F /* BaseTeamRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80F2CC7E4240082001F /* BaseTeamRegistration.swift */; };
C488C8572CC7E4240082001F /* BaseGroupStage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8092CC7E4240082001F /* BaseGroupStage.swift */; };
C488C8582CC7E4240082001F /* BaseCustomUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8062CC7E4240082001F /* BaseCustomUser.swift */; };
C488C8592CC7E4240082001F /* BaseMatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80A2CC7E4240082001F /* BaseMatch.swift */; };
C488C85A2CC7E4240082001F /* BaseEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8082CC7E4240082001F /* BaseEvent.swift */; };
C488C85B2CC7E4240082001F /* BaseRound.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80E2CC7E4240082001F /* BaseRound.swift */; };
C488C85C2CC7E4240082001F /* BaseMatchScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80B2CC7E4240082001F /* BaseMatchScheduler.swift */; };
C488C85E2CC7E4240082001F /* BasePlayerRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C80D2CC7E4240082001F /* BasePlayerRegistration.swift */; };
C488C85F2CC7E4240082001F /* BaseTeamScore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8102CC7E4240082001F /* BaseTeamScore.swift */; };
C488C8602CC7E4240082001F /* BaseTournament.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8112CC7E4240082001F /* BaseTournament.swift */; };
C488C8612CC7E4240082001F /* Event.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8162CC7E4240082001F /* Event.json */; };
C488C8622CC7E4240082001F /* Court.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8132CC7E4240082001F /* Court.json */; };
C488C8632CC7E4240082001F /* Tournament.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81F2CC7E4240082001F /* Tournament.json */; };
C488C8642CC7E4240082001F /* CustomUser.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8142CC7E4240082001F /* CustomUser.json */; };
C488C8652CC7E4240082001F /* Round.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81C2CC7E4240082001F /* Round.json */; };
C488C8662CC7E4240082001F /* MatchScheduler.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8192CC7E4240082001F /* MatchScheduler.json */; };
C488C8672CC7E4240082001F /* DateInterval.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8152CC7E4240082001F /* DateInterval.json */; };
C488C8682CC7E4240082001F /* TeamRegistration.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81D2CC7E4240082001F /* TeamRegistration.json */; };
C488C8692CC7E4240082001F /* GroupStage.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8172CC7E4240082001F /* GroupStage.json */; };
C488C86A2CC7E4240082001F /* MonthData.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81A2CC7E4240082001F /* MonthData.json */; };
C488C86B2CC7E4240082001F /* TeamScore.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81E2CC7E4240082001F /* TeamScore.json */; };
C488C86C2CC7E4240082001F /* Match.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8182CC7E4240082001F /* Match.json */; };
C488C86D2CC7E4240082001F /* PlayerRegistration.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C81B2CC7E4240082001F /* PlayerRegistration.json */; };
C488C8712CC816410082001F /* Purchase.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8702CC816410082001F /* Purchase.json */; };
C488C8722CC816410082001F /* BasePurchase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C86F2CC816410082001F /* BasePurchase.swift */; };
C488C8732CC816410082001F /* BasePurchase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C86F2CC816410082001F /* BasePurchase.swift */; };
C488C8742CC816410082001F /* Purchase.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8702CC816410082001F /* Purchase.json */; };
C488C8752CC816410082001F /* Purchase.json in Resources */ = {isa = PBXBuildFile; fileRef = C488C8702CC816410082001F /* Purchase.json */; };
C488C8762CC816410082001F /* BasePurchase.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C86F2CC816410082001F /* BasePurchase.swift */; };
C488C8822CCBE8FC0082001F /* NetworkStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8812CCBE8FC0082001F /* NetworkStatusView.swift */; };
C488C8832CCBE8FC0082001F /* NetworkStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8812CCBE8FC0082001F /* NetworkStatusView.swift */; };
C488C8842CCBE8FC0082001F /* NetworkStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C8812CCBE8FC0082001F /* NetworkStatusView.swift */; };
C493B37E2C10AD3600862481 /* LoadingViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C493B37D2C10AD3600862481 /* LoadingViewModifier.swift */; };
C49C73142D5B98D8008DD299 /* PlayerPaymentType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49C73132D5B98D7008DD299 /* PlayerPaymentType.swift */; };
C49C73152D5B98D8008DD299 /* PlayerPaymentType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49C73132D5B98D7008DD299 /* PlayerPaymentType.swift */; };
C49C73162D5B98D8008DD299 /* PlayerPaymentType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49C73132D5B98D7008DD299 /* PlayerPaymentType.swift */; };
C49C731E2D5E3BE8008DD299 /* VersionComparator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49C731D2D5E3BE4008DD299 /* VersionComparator.swift */; };
C49C731F2D5E3BE8008DD299 /* VersionComparator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49C731D2D5E3BE4008DD299 /* VersionComparator.swift */; };
C49C73202D5E3BE8008DD299 /* VersionComparator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49C731D2D5E3BE4008DD299 /* VersionComparator.swift */; };
@ -34,6 +141,9 @@
C49EF03C2BE15AF80077B5AA /* String+Crypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF03B2BE15AF80077B5AA /* String+Crypto.swift */; };
C49EF0422BE23BF50077B5AA /* PaymentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0412BE23BF50077B5AA /* PaymentTests.swift */; };
C49EF0442BE286780077B5AA /* CryptoKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0432BE286780077B5AA /* CryptoKey.swift */; };
C4A36F582CE2626A003738C6 /* TournamentLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A36F572CE2626A003738C6 /* TournamentLibrary.swift */; };
C4A36F592CE2626A003738C6 /* TournamentLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A36F572CE2626A003738C6 /* TournamentLibrary.swift */; };
C4A36F5A2CE2626A003738C6 /* TournamentLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A36F572CE2626A003738C6 /* TournamentLibrary.swift */; };
C4A47D5A2B6D383C00ADC637 /* Tournament.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D592B6D383C00ADC637 /* Tournament.swift */; };
C4A47D5E2B6D38EC00ADC637 /* DataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D5D2B6D38EC00ADC637 /* DataStore.swift */; };
C4A47D632B6D3D6500ADC637 /* Club.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D622B6D3D6500ADC637 /* Club.swift */; };
@ -45,12 +155,13 @@
C4A47D9F2B7D0BCE00ADC637 /* StepperView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D9E2B7D0BCE00ADC637 /* StepperView.swift */; };
C4A47DA62B83948E00ADC637 /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DA52B83948E00ADC637 /* LoginView.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 */; };
C4B3A1552C2581DA0078EAA8 /* Patcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B3A1542C2581DA0078EAA8 /* Patcher.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 */; };
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 */; };
C4EC6F592BE92D88000CEAB4 /* PListReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC6F582BE92D88000CEAB4 /* PListReader.swift */; };
C4FC2E272C2AABC90021F3BF /* PasswordField.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FC2E262C2AABC90021F3BF /* PasswordField.swift */; };
@ -332,7 +443,7 @@
FF4CBFFA2C996C0600151637 /* TournamentScheduleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF0E0B6C2BC254C6005F00A9 /* TournamentScheduleView.swift */; };
FF4CBFFB2C996C0600151637 /* MatchFormatStorageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AF02BD1AEBD00A86CF8 /* MatchFormatStorageView.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 */; };
FF4CBFFF2C996C0600151637 /* TournamentDurationManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26442BAE0A3400650388 /* TournamentDurationManagerView.swift */; };
FF4CC0002C996C0600151637 /* MockData.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5522BAB354A00FD8220 /* MockData.swift */; };
@ -644,7 +755,7 @@
FF70FB792C90584900129CC2 /* TournamentScheduleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF0E0B6C2BC254C6005F00A9 /* TournamentScheduleView.swift */; };
FF70FB7A2C90584900129CC2 /* MatchFormatStorageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF025AF02BD1AEBD00A86CF8 /* MatchFormatStorageView.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 */; };
FF70FB7E2C90584900129CC2 /* TournamentDurationManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF8F26442BAE0A3400650388 /* TournamentDurationManagerView.swift */; };
FF70FB7F2C90584900129CC2 /* MockData.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF1DC5522BAB354A00FD8220 /* MockData.swift */; };
@ -945,12 +1056,48 @@
C425D4172B6D249E002A7B48 /* PadelClubUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PadelClubUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
C425D41B2B6D249E002A7B48 /* PadelClubUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PadelClubUITests.swift; sourceTree = "<group>"; };
C425D41D2B6D249E002A7B48 /* PadelClubUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PadelClubUITestsLaunchTests.swift; sourceTree = "<group>"; };
C4339BFA2CFF7D64004E5F09 /* ShareModelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareModelView.swift; sourceTree = "<group>"; };
C4489BE12C05BF5000043F3D /* DebugSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugSettingsView.swift; sourceTree = "<group>"; };
C44B79102BBDA63A00906534 /* Locale+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Locale+Extensions.swift"; sourceTree = "<group>"; };
C45BAE3A2BC6DF10002EEC8A /* SyncedProducts.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = SyncedProducts.storekit; sourceTree = "<group>"; };
C45BAE432BCA753E002EEC8A /* Purchase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Purchase.swift; sourceTree = "<group>"; };
C4607A7C2C04DDE2004CB781 /* APICallsListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APICallsListView.swift; sourceTree = "<group>"; };
C471D1532D0C8FE80068091F /* Drawlog.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Drawlog.json; sourceTree = "<group>"; };
C471D1572D0C91FE0068091F /* BaseDrawLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseDrawLog.swift; sourceTree = "<group>"; };
C488C7E52CC7D1660082001F /* generator.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = generator.py; sourceTree = "<group>"; };
C488C7EC2CC7D2290082001F /* Club.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Club.json; sourceTree = "<group>"; };
C488C7FE2CC7DCB80082001F /* BaseClub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseClub.swift; sourceTree = "<group>"; };
C488C8022CC7E1E40082001F /* BaseCourt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseCourt.swift; sourceTree = "<group>"; };
C488C8062CC7E4240082001F /* BaseCustomUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseCustomUser.swift; sourceTree = "<group>"; };
C488C8072CC7E4240082001F /* BaseDateInterval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseDateInterval.swift; sourceTree = "<group>"; };
C488C8082CC7E4240082001F /* BaseEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseEvent.swift; sourceTree = "<group>"; };
C488C8092CC7E4240082001F /* BaseGroupStage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseGroupStage.swift; sourceTree = "<group>"; };
C488C80A2CC7E4240082001F /* BaseMatch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseMatch.swift; sourceTree = "<group>"; };
C488C80B2CC7E4240082001F /* BaseMatchScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseMatchScheduler.swift; sourceTree = "<group>"; };
C488C80C2CC7E4240082001F /* BaseMonthData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseMonthData.swift; sourceTree = "<group>"; };
C488C80D2CC7E4240082001F /* BasePlayerRegistration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasePlayerRegistration.swift; sourceTree = "<group>"; };
C488C80E2CC7E4240082001F /* BaseRound.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseRound.swift; sourceTree = "<group>"; };
C488C80F2CC7E4240082001F /* BaseTeamRegistration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseTeamRegistration.swift; sourceTree = "<group>"; };
C488C8102CC7E4240082001F /* BaseTeamScore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseTeamScore.swift; sourceTree = "<group>"; };
C488C8112CC7E4240082001F /* BaseTournament.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseTournament.swift; sourceTree = "<group>"; };
C488C8132CC7E4240082001F /* Court.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Court.json; sourceTree = "<group>"; };
C488C8142CC7E4240082001F /* CustomUser.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = CustomUser.json; sourceTree = "<group>"; };
C488C8152CC7E4240082001F /* DateInterval.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = DateInterval.json; sourceTree = "<group>"; };
C488C8162CC7E4240082001F /* Event.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Event.json; sourceTree = "<group>"; };
C488C8172CC7E4240082001F /* GroupStage.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = GroupStage.json; sourceTree = "<group>"; };
C488C8182CC7E4240082001F /* Match.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Match.json; sourceTree = "<group>"; };
C488C8192CC7E4240082001F /* MatchScheduler.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = MatchScheduler.json; sourceTree = "<group>"; };
C488C81A2CC7E4240082001F /* MonthData.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = MonthData.json; sourceTree = "<group>"; };
C488C81B2CC7E4240082001F /* PlayerRegistration.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = PlayerRegistration.json; sourceTree = "<group>"; };
C488C81C2CC7E4240082001F /* Round.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Round.json; sourceTree = "<group>"; };
C488C81D2CC7E4240082001F /* TeamRegistration.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = TeamRegistration.json; sourceTree = "<group>"; };
C488C81E2CC7E4240082001F /* TeamScore.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = TeamScore.json; sourceTree = "<group>"; };
C488C81F2CC7E4240082001F /* Tournament.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Tournament.json; sourceTree = "<group>"; };
C488C86F2CC816410082001F /* BasePurchase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasePurchase.swift; sourceTree = "<group>"; };
C488C8702CC816410082001F /* Purchase.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Purchase.json; sourceTree = "<group>"; };
C488C8812CCBE8FC0082001F /* NetworkStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkStatusView.swift; sourceTree = "<group>"; };
C493B37D2C10AD3600862481 /* LoadingViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingViewModifier.swift; sourceTree = "<group>"; };
C49C73132D5B98D7008DD299 /* PlayerPaymentType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerPaymentType.swift; sourceTree = "<group>"; };
C49C731D2D5E3BE4008DD299 /* VersionComparator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionComparator.swift; sourceTree = "<group>"; };
C49EF0182BD694290077B5AA /* PurchaseListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PurchaseListView.swift; sourceTree = "<group>"; };
C49EF01A2BD6A1E80077B5AA /* URLs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLs.swift; sourceTree = "<group>"; };
@ -959,6 +1106,7 @@
C49EF03B2BE15AF80077B5AA /* String+Crypto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Crypto.swift"; sourceTree = "<group>"; };
C49EF0412BE23BF50077B5AA /* PaymentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentTests.swift; sourceTree = "<group>"; };
C49EF0432BE286780077B5AA /* CryptoKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CryptoKey.swift; sourceTree = "<group>"; };
C4A36F572CE2626A003738C6 /* TournamentLibrary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentLibrary.swift; sourceTree = "<group>"; };
C4A47D592B6D383C00ADC637 /* Tournament.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tournament.swift; sourceTree = "<group>"; };
C4A47D5D2B6D38EC00ADC637 /* DataStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataStore.swift; sourceTree = "<group>"; };
C4A47D622B6D3D6500ADC637 /* Club.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Club.swift; sourceTree = "<group>"; };
@ -970,11 +1118,12 @@
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>"; };
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>"; };
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>"; };
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>"; };
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>"; };
@ -1391,6 +1540,7 @@
C411C9C22BEBA453003017AD /* ServerDataTests.swift */,
C411C9C82BF219CB003017AD /* UserDataTests.swift */,
C411C9CF2BF38F41003017AD /* TokenExemptionTests.swift */,
C4D477982CB6704C0077713D /* SynchronizationTests.swift */,
);
path = PadelClubTests;
sourceTree = "<group>";
@ -1412,18 +1562,61 @@
name = Frameworks;
sourceTree = "<group>";
};
C488C7FD2CC7D6CD0082001F /* Gen */ = {
isa = PBXGroup;
children = (
C488C7E52CC7D1660082001F /* generator.py */,
C488C7EC2CC7D2290082001F /* Club.json */,
C488C8132CC7E4240082001F /* Court.json */,
C488C8142CC7E4240082001F /* CustomUser.json */,
C488C8152CC7E4240082001F /* DateInterval.json */,
C471D1532D0C8FE80068091F /* Drawlog.json */,
C488C8162CC7E4240082001F /* Event.json */,
C488C8172CC7E4240082001F /* GroupStage.json */,
C488C8182CC7E4240082001F /* Match.json */,
C488C8192CC7E4240082001F /* MatchScheduler.json */,
C488C81A2CC7E4240082001F /* MonthData.json */,
C488C81B2CC7E4240082001F /* PlayerRegistration.json */,
C488C8702CC816410082001F /* Purchase.json */,
C488C81C2CC7E4240082001F /* Round.json */,
C488C81D2CC7E4240082001F /* TeamRegistration.json */,
C488C81E2CC7E4240082001F /* TeamScore.json */,
C488C81F2CC7E4240082001F /* Tournament.json */,
C488C7FE2CC7DCB80082001F /* BaseClub.swift */,
C488C8022CC7E1E40082001F /* BaseCourt.swift */,
C488C8062CC7E4240082001F /* BaseCustomUser.swift */,
C488C8072CC7E4240082001F /* BaseDateInterval.swift */,
C471D1572D0C91FE0068091F /* BaseDrawLog.swift */,
C488C8082CC7E4240082001F /* BaseEvent.swift */,
C488C8092CC7E4240082001F /* BaseGroupStage.swift */,
C488C80A2CC7E4240082001F /* BaseMatch.swift */,
C488C80B2CC7E4240082001F /* BaseMatchScheduler.swift */,
C488C80C2CC7E4240082001F /* BaseMonthData.swift */,
C488C80D2CC7E4240082001F /* BasePlayerRegistration.swift */,
C488C86F2CC816410082001F /* BasePurchase.swift */,
C488C80E2CC7E4240082001F /* BaseRound.swift */,
C488C80F2CC7E4240082001F /* BaseTeamRegistration.swift */,
C488C8102CC7E4240082001F /* BaseTeamScore.swift */,
C488C8112CC7E4240082001F /* BaseTournament.swift */,
);
path = Gen;
sourceTree = "<group>";
};
C4A47D5F2B6D3B2D00ADC637 /* Data */ = {
isa = PBXGroup;
children = (
C488C7FD2CC7D6CD0082001F /* Gen */,
C411C9CC2BF21DAF003017AD /* README.md */,
C4A47D5D2B6D38EC00ADC637 /* DataStore.swift */,
C4FC2E2A2C2C0E4D0021F3BF /* TournamentStore.swift */,
C4A47DAC2B85FCCD00ADC637 /* User.swift */,
C4A36F572CE2626A003738C6 /* TournamentLibrary.swift */,
C4A47DAC2B85FCCD00ADC637 /* CustomUser.swift */,
C4A47D592B6D383C00ADC637 /* Tournament.swift */,
FF967CE72BAEC70100A9A3BD /* GroupStage.swift */,
FF967CED2BAECBD700A9A3BD /* Round.swift */,
FF967CEB2BAECB9900A9A3BD /* Match.swift */,
FF967CF12BAECC0B00A9A3BD /* PlayerRegistration.swift */,
C49C73132D5B98D7008DD299 /* PlayerPaymentType.swift */,
FF967CF02BAECC0B00A9A3BD /* TeamRegistration.swift */,
FF967CEF2BAECC0A00A9A3BD /* TeamScore.swift */,
C4A47D622B6D3D6500ADC637 /* Club.swift */,
@ -1468,6 +1661,7 @@
C4A47D852B7BA33F00ADC637 /* User */ = {
isa = PBXGroup;
children = (
C4339BFA2CFF7D64004E5F09 /* ShareModelView.swift */,
C4A47DB22B86387500ADC637 /* AccountView.swift */,
C4A47DA82B85F82100ADC637 /* ChangePasswordView.swift */,
C4A47DA52B83948E00ADC637 /* LoginView.swift */,
@ -1751,6 +1945,7 @@
FF3F74F52B919E45004CFE0E /* UmpireView.swift */,
FFA252AC2CDB734A0074E63F /* UmpireStatisticView.swift */,
FFD783FE2B91BA42000F62A6 /* PadelClubView.swift */,
C488C8812CCBE8FC0082001F /* NetworkStatusView.swift */,
);
path = Umpire;
sourceTree = "<group>";
@ -2255,8 +2450,24 @@
FF1F4B842BFA02A4000B4573 /* groupstagescore-template.html in Resources */,
FF1F4B852BFA02A4000B4573 /* player-template.html in Resources */,
FF1F4B862BFA02A4000B4573 /* groupstagerow-template.html in Resources */,
C488C8712CC816410082001F /* Purchase.json in Resources */,
FF1F4B872BFA02A4000B4573 /* hiddenplayer-template.html in Resources */,
C488C7EB2CC7D16F0082001F /* generator.py in Resources */,
FF1F4B882BFA02A4000B4573 /* bracket-template.html in Resources */,
C488C8202CC7E4240082001F /* Event.json in Resources */,
C488C8212CC7E4240082001F /* Court.json in Resources */,
C488C8222CC7E4240082001F /* Tournament.json in Resources */,
C471D1552D0C8FED0068091F /* Drawlog.json in Resources */,
C488C8232CC7E4240082001F /* CustomUser.json in Resources */,
C488C8242CC7E4240082001F /* Round.json in Resources */,
C488C8252CC7E4240082001F /* MatchScheduler.json in Resources */,
C488C8262CC7E4240082001F /* DateInterval.json in Resources */,
C488C8272CC7E4240082001F /* TeamRegistration.json in Resources */,
C488C8282CC7E4240082001F /* GroupStage.json in Resources */,
C488C8292CC7E4240082001F /* MonthData.json in Resources */,
C488C82A2CC7E4240082001F /* TeamScore.json in Resources */,
C488C82B2CC7E4240082001F /* Match.json in Resources */,
C488C82C2CC7E4240082001F /* PlayerRegistration.json in Resources */,
FF1F4B892BFA02A4000B4573 /* groupstagecol-template.html in Resources */,
FF1F4B8A2BFA02A4000B4573 /* groupstage-template.html in Resources */,
FF1F4B8B2BFA02A4000B4573 /* groupstageentrant-template.html in Resources */,
@ -2294,8 +2505,24 @@
FF4CC03D2C996C0600151637 /* groupstagescore-template.html in Resources */,
FF4CC03E2C996C0600151637 /* player-template.html in Resources */,
FF4CC03F2C996C0600151637 /* groupstagerow-template.html in Resources */,
C488C8752CC816410082001F /* Purchase.json in Resources */,
FF4CC0402C996C0600151637 /* hiddenplayer-template.html in Resources */,
C488C7E92CC7D16F0082001F /* generator.py in Resources */,
FF4CC0412C996C0600151637 /* bracket-template.html in Resources */,
C488C83A2CC7E4240082001F /* Event.json in Resources */,
C488C83B2CC7E4240082001F /* Court.json in Resources */,
C488C83C2CC7E4240082001F /* Tournament.json in Resources */,
C471D1562D0C8FED0068091F /* Drawlog.json in Resources */,
C488C83D2CC7E4240082001F /* CustomUser.json in Resources */,
C488C83E2CC7E4240082001F /* Round.json in Resources */,
C488C83F2CC7E4240082001F /* MatchScheduler.json in Resources */,
C488C8402CC7E4240082001F /* DateInterval.json in Resources */,
C488C8412CC7E4240082001F /* TeamRegistration.json in Resources */,
C488C8422CC7E4240082001F /* GroupStage.json in Resources */,
C488C8432CC7E4240082001F /* MonthData.json in Resources */,
C488C8442CC7E4240082001F /* TeamScore.json in Resources */,
C488C8452CC7E4240082001F /* Match.json in Resources */,
C488C8462CC7E4240082001F /* PlayerRegistration.json in Resources */,
FF4CC0422C996C0600151637 /* groupstagecol-template.html in Resources */,
FF4CC0432C996C0600151637 /* groupstage-template.html in Resources */,
FF4CC0442C996C0600151637 /* groupstageentrant-template.html in Resources */,
@ -2319,8 +2546,24 @@
FF70FBBC2C90584900129CC2 /* groupstagescore-template.html in Resources */,
FF70FBBD2C90584900129CC2 /* player-template.html in Resources */,
FF70FBBE2C90584900129CC2 /* groupstagerow-template.html in Resources */,
C488C8742CC816410082001F /* Purchase.json in Resources */,
FF70FBBF2C90584900129CC2 /* hiddenplayer-template.html in Resources */,
C488C7EA2CC7D16F0082001F /* generator.py in Resources */,
FF70FBC02C90584900129CC2 /* bracket-template.html in Resources */,
C488C8612CC7E4240082001F /* Event.json in Resources */,
C488C8622CC7E4240082001F /* Court.json in Resources */,
C488C8632CC7E4240082001F /* Tournament.json in Resources */,
C471D1542D0C8FED0068091F /* Drawlog.json in Resources */,
C488C8642CC7E4240082001F /* CustomUser.json in Resources */,
C488C8652CC7E4240082001F /* Round.json in Resources */,
C488C8662CC7E4240082001F /* MatchScheduler.json in Resources */,
C488C8672CC7E4240082001F /* DateInterval.json in Resources */,
C488C8682CC7E4240082001F /* TeamRegistration.json in Resources */,
C488C8692CC7E4240082001F /* GroupStage.json in Resources */,
C488C86A2CC7E4240082001F /* MonthData.json in Resources */,
C488C86B2CC7E4240082001F /* TeamScore.json in Resources */,
C488C86C2CC7E4240082001F /* Match.json in Resources */,
C488C86D2CC7E4240082001F /* PlayerRegistration.json in Resources */,
FF70FBC12C90584900129CC2 /* groupstagecol-template.html in Resources */,
FF70FBC22C90584900129CC2 /* groupstage-template.html in Resources */,
FF70FBC32C90584900129CC2 /* groupstageentrant-template.html in Resources */,
@ -2353,6 +2596,7 @@
C4A47D9F2B7D0BCE00ADC637 /* StepperView.swift in Sources */,
FFC83D4F2BB807D100750834 /* RoundsView.swift in Sources */,
FF1CBC1B2BB53D1F0036DAAB /* FederalTournament.swift in Sources */,
C4339BFB2CFF7D68004E5F09 /* ShareModelView.swift in Sources */,
FF8F26412BADFC8700650388 /* TournamentInitView.swift in Sources */,
C4A47D8A2B7BBB6500ADC637 /* SubscriptionView.swift in Sources */,
FFD655D82C8DE27400E5B35E /* TournamentLookUpView.swift in Sources */,
@ -2377,6 +2621,7 @@
FF7091682B90F79F00AB08DA /* TournamentCellView.swift in Sources */,
FF6EC9042B9479F500EA7F5A /* Sequence+Extensions.swift in Sources */,
FF9267FA2BCE78EC0080F940 /* CashierDetailView.swift in Sources */,
C488C8722CC816410082001F /* BasePurchase.swift in Sources */,
FFE103082C353B7600684FC9 /* EventClubSettingsView.swift in Sources */,
C4A47DB32B86387500ADC637 /* AccountView.swift in Sources */,
FFCEDA4C2C2C08EA00F8C0F2 /* PlayersWithoutContactView.swift in Sources */,
@ -2399,6 +2644,19 @@
FF8F263B2BAD528600650388 /* EventCreationView.swift in Sources */,
FFC1E1082BAC29FC008D6F59 /* LocationManager.swift in Sources */,
C4C01D982C481C0C0059087C /* CapsuleViewModifier.swift in Sources */,
C488C82D2CC7E4240082001F /* BaseDateInterval.swift in Sources */,
C488C82E2CC7E4240082001F /* BaseMonthData.swift in Sources */,
C471D1592D0C91FE0068091F /* BaseDrawLog.swift in Sources */,
C488C82F2CC7E4240082001F /* BaseTeamRegistration.swift in Sources */,
C488C8302CC7E4240082001F /* BaseGroupStage.swift in Sources */,
C488C8312CC7E4240082001F /* BaseCustomUser.swift in Sources */,
C488C8322CC7E4240082001F /* BaseMatch.swift in Sources */,
C488C8332CC7E4240082001F /* BaseEvent.swift in Sources */,
C488C8342CC7E4240082001F /* BaseRound.swift in Sources */,
C488C8352CC7E4240082001F /* BaseMatchScheduler.swift in Sources */,
C488C8372CC7E4240082001F /* BasePlayerRegistration.swift in Sources */,
C488C8382CC7E4240082001F /* BaseTeamScore.swift in Sources */,
C488C8392CC7E4240082001F /* BaseTournament.swift in Sources */,
FF6087EC2BE26A2F004E1E47 /* BroadcastView.swift in Sources */,
FFF964552BC266CF00EEF017 /* SchedulerView.swift in Sources */,
FFA1B1292BB71773006CE248 /* PadelClubButtonView.swift in Sources */,
@ -2436,6 +2694,7 @@
FF4AB6BB2B9256D50002987F /* SearchViewModel.swift in Sources */,
FF967CF32BAECC0B00A9A3BD /* PlayerRegistration.swift in Sources */,
FF4AB6BF2B92577A0002987F /* ImportedPlayerView.swift in Sources */,
C4A36F5A2CE2626A003738C6 /* TournamentLibrary.swift in Sources */,
FF1162872BD004AD000C4809 /* EditingTeamView.swift in Sources */,
FF3A74332D37DCF2007E3032 /* InscriptionLegendView.swift in Sources */,
FF6EC9062B947A1000EA7F5A /* NetworkManagerError.swift in Sources */,
@ -2445,6 +2704,7 @@
FF3795662B9399AA004EA093 /* Persistence.swift in Sources */,
FFCF76072C3BE9BC006C8C3D /* CloseDatePicker.swift in Sources */,
FF1DF49B2BD8D23900822FA0 /* BarButtonView.swift in Sources */,
C488C8052CC7E1E40082001F /* BaseCourt.swift in Sources */,
FFF964502BC25E3700EEF017 /* PlanningView.swift in Sources */,
FF967CEC2BAECB9900A9A3BD /* Match.swift in Sources */,
FF8F264B2BAE0B4100650388 /* TournamentLevelPickerView.swift in Sources */,
@ -2531,6 +2791,7 @@
FF3A73F32D37C34D007E3032 /* RegistrationInfoSheetView.swift in Sources */,
C49EF01B2BD6A1E80077B5AA /* URLs.swift in Sources */,
FFCFC0142BBC59FC00B82851 /* MatchDescriptor.swift in Sources */,
C49C73142D5B98D8008DD299 /* PlayerPaymentType.swift in Sources */,
FF8F264C2BAE0B4100650388 /* TournamentFormatSelectionView.swift in Sources */,
FF17CA572CC02FEA003C7323 /* CoachListView.swift in Sources */,
FFBF065E2BBD8040009D6715 /* MatchListView.swift in Sources */,
@ -2543,13 +2804,14 @@
FF0E0B6D2BC254C6005F00A9 /* TournamentScheduleView.swift in Sources */,
FF025AF12BD1AEBD00A86CF8 /* MatchFormatStorageView.swift in Sources */,
FF3F74F62B919E45004CFE0E /* UmpireView.swift in Sources */,
C4A47DAD2B85FCCD00ADC637 /* User.swift in Sources */,
C4A47DAD2B85FCCD00ADC637 /* CustomUser.swift in Sources */,
C4C33F762C9B1ED4006316DE /* CodingContainer+Extensions.swift in Sources */,
FF967D012BAEF0B400A9A3BD /* MatchSummaryView.swift in Sources */,
FFA252B62CDD2C6C0074E63F /* OngoingDestination.swift in Sources */,
FF8F26452BAE0A3400650388 /* TournamentDurationManagerView.swift in Sources */,
FF1DC5532BAB354A00FD8220 /* MockData.swift in Sources */,
FF967D092BAF3D4000A9A3BD /* TeamDetailView.swift in Sources */,
C488C8822CCBE8FC0082001F /* NetworkStatusView.swift in Sources */,
FFA252A92CDB70520074E63F /* PlayerStatisticView.swift in Sources */,
FF5DA18F2BB9268800A33061 /* GroupStagesSettingsView.swift in Sources */,
FF663FBE2BE019EC0031AE83 /* TournamentFilterView.swift in Sources */,
@ -2571,6 +2833,7 @@
FF089EBD2BB0287D00F0AEC7 /* PlayerView.swift in Sources */,
FF967D032BAEF0C000A9A3BD /* MatchDetailView.swift in Sources */,
FFF1D2CB2C4A22B200C8D33D /* ExportFormat.swift in Sources */,
C488C8012CC7DCB80082001F /* BaseClub.swift in Sources */,
FF967D0F2BAF63B000A9A3BD /* PlayerBlockView.swift in Sources */,
C4A47D922B7BBBEC00ADC637 /* StoreItem.swift in Sources */,
FFB9C8712BBADDE200A0EF4F /* Selectable.swift in Sources */,
@ -2614,6 +2877,7 @@
files = (
C411C9D02BF38F41003017AD /* TokenExemptionTests.swift in Sources */,
C49EF0422BE23BF50077B5AA /* PaymentTests.swift in Sources */,
C4D477992CB6704C0077713D /* SynchronizationTests.swift in Sources */,
C425D4122B6D249E002A7B48 /* PadelClubTests.swift in Sources */,
C411C9C92BF219CB003017AD /* UserDataTests.swift in Sources */,
C411C9C32BEBA453003017AD /* ServerDataTests.swift in Sources */,
@ -2678,6 +2942,7 @@
FF4CBF6B2C996C0600151637 /* Calendar+Extensions.swift in Sources */,
FF4CBF6C2C996C0600151637 /* TeamScore.swift in Sources */,
FF4CBF6D2C996C0600151637 /* EditablePlayerView.swift in Sources */,
C488C7F12CC7D22D0082001F /* Club.json in Sources */,
FF4CBF6E2C996C0600151637 /* PlayerDetailView.swift in Sources */,
FFA252AF2CDB734A0074E63F /* UmpireStatisticView.swift in Sources */,
FF4CBF6F2C996C0600151637 /* ListRowViewModifier.swift in Sources */,
@ -2692,6 +2957,20 @@
FF4CBF782C996C0600151637 /* EventCreationView.swift in Sources */,
FF4CBF792C996C0600151637 /* LocationManager.swift in Sources */,
FF4CBF7A2C996C0600151637 /* CapsuleViewModifier.swift in Sources */,
C488C8472CC7E4240082001F /* BaseDateInterval.swift in Sources */,
C488C8832CCBE8FC0082001F /* NetworkStatusView.swift in Sources */,
C488C8482CC7E4240082001F /* BaseMonthData.swift in Sources */,
C488C8492CC7E4240082001F /* BaseTeamRegistration.swift in Sources */,
C488C84A2CC7E4240082001F /* BaseGroupStage.swift in Sources */,
C488C84B2CC7E4240082001F /* BaseCustomUser.swift in Sources */,
C488C84C2CC7E4240082001F /* BaseMatch.swift in Sources */,
C488C84D2CC7E4240082001F /* BaseEvent.swift in Sources */,
C488C84E2CC7E4240082001F /* BaseRound.swift in Sources */,
C488C84F2CC7E4240082001F /* BaseMatchScheduler.swift in Sources */,
C49C73152D5B98D8008DD299 /* PlayerPaymentType.swift in Sources */,
C488C8512CC7E4240082001F /* BasePlayerRegistration.swift in Sources */,
C488C8522CC7E4240082001F /* BaseTeamScore.swift in Sources */,
C488C8532CC7E4240082001F /* BaseTournament.swift in Sources */,
FF4CBF7B2C996C0600151637 /* BroadcastView.swift in Sources */,
FF4CBF7C2C996C0600151637 /* SchedulerView.swift in Sources */,
FF4CBF7D2C996C0600151637 /* PadelClubButtonView.swift in Sources */,
@ -2710,6 +2989,7 @@
FF4CBF882C996C0600151637 /* TeamRowView.swift in Sources */,
FF4CBF892C996C0600151637 /* ContactManager.swift in Sources */,
FF4CBF8A2C996C0600151637 /* AppDelegate.swift in Sources */,
C488C8762CC816410082001F /* BasePurchase.swift in Sources */,
FF4CBF8B2C996C0600151637 /* SubscriptionInfoView.swift in Sources */,
FF4CBF8C2C996C0600151637 /* EditScoreView.swift in Sources */,
FF4CBF8D2C996C0600151637 /* TournamentOrganizerView.swift in Sources */,
@ -2728,6 +3008,7 @@
FF4CBF992C996C0600151637 /* StoreManager.swift in Sources */,
FF4CBF9A2C996C0600151637 /* SearchViewModel.swift in Sources */,
FF4CBF9B2C996C0600151637 /* PlayerRegistration.swift in Sources */,
C4339BFD2CFF7D68004E5F09 /* ShareModelView.swift in Sources */,
FF4CBF9C2C996C0600151637 /* ImportedPlayerView.swift in Sources */,
FF4CBF9D2C996C0600151637 /* EditingTeamView.swift in Sources */,
FF3A74322D37DCF2007E3032 /* InscriptionLegendView.swift in Sources */,
@ -2738,6 +3019,7 @@
FF4CBFA22C996C0600151637 /* Persistence.swift in Sources */,
FF4CBFA32C996C0600151637 /* CloseDatePicker.swift in Sources */,
FF4CBFA42C996C0600151637 /* BarButtonView.swift in Sources */,
C488C8042CC7E1E40082001F /* BaseCourt.swift in Sources */,
FF4CBFA52C996C0600151637 /* PlanningView.swift in Sources */,
FF4CBFA62C996C0600151637 /* Match.swift in Sources */,
FF4CBFA72C996C0600151637 /* TournamentLevelPickerView.swift in Sources */,
@ -2766,6 +3048,7 @@
FF4CBFBD2C996C0600151637 /* AgendaDestination.swift in Sources */,
FF4CBFBE2C996C0600151637 /* PadelClubApp.xcdatamodeld in Sources */,
FF4CBFBF2C996C0600151637 /* SetInputView.swift in Sources */,
C471D15A2D0C91FF0068091F /* BaseDrawLog.swift in Sources */,
FF4CBFC02C996C0600151637 /* ButtonValidateView.swift in Sources */,
FF4CBFC12C996C0600151637 /* ClubRowView.swift in Sources */,
FF4CBFC22C996C0600151637 /* ClubDetailView.swift in Sources */,
@ -2829,6 +3112,7 @@
FF4CBFF32C996C0600151637 /* MatchListView.swift in Sources */,
FF4CBFF42C996C0600151637 /* PadelClubApp.swift in Sources */,
FF4CBFF52C996C0600151637 /* TournamentSettingsView.swift in Sources */,
C4A36F582CE2626A003738C6 /* TournamentLibrary.swift in Sources */,
FF4CBFF62C996C0600151637 /* String+Crypto.swift in Sources */,
FF4CBFF72C996C0600151637 /* GroupStageTeamReplacementView.swift in Sources */,
FF4CBFF82C996C0600151637 /* TabItemModifier.swift in Sources */,
@ -2837,7 +3121,7 @@
FFBA2D2D2CA2CE9E00D5BBDD /* CodingContainer+Extensions.swift in Sources */,
FF4CBFFB2C996C0600151637 /* MatchFormatStorageView.swift in Sources */,
FF4CBFFC2C996C0600151637 /* UmpireView.swift in Sources */,
FF4CBFFD2C996C0600151637 /* User.swift in Sources */,
FF4CBFFD2C996C0600151637 /* CustomUser.swift in Sources */,
FF4CBFFE2C996C0600151637 /* MatchSummaryView.swift in Sources */,
FFA252B52CDD2C6C0074E63F /* OngoingDestination.swift in Sources */,
FF4CBFFF2C996C0600151637 /* TournamentDurationManagerView.swift in Sources */,
@ -2864,6 +3148,7 @@
FF4CC0112C996C0600151637 /* PlayerView.swift in Sources */,
FF4CC0122C996C0600151637 /* MatchDetailView.swift in Sources */,
FF4CC0132C996C0600151637 /* ExportFormat.swift in Sources */,
C488C7FF2CC7DCB80082001F /* BaseClub.swift in Sources */,
FF4CC0142C996C0600151637 /* PlayerBlockView.swift in Sources */,
FF4CC0152C996C0600151637 /* StoreItem.swift in Sources */,
FF4CC0162C996C0600151637 /* Selectable.swift in Sources */,
@ -2950,6 +3235,7 @@
FF70FAEA2C90584900129CC2 /* Calendar+Extensions.swift in Sources */,
FF70FAEB2C90584900129CC2 /* TeamScore.swift in Sources */,
FF70FAEC2C90584900129CC2 /* EditablePlayerView.swift in Sources */,
C488C7F22CC7D22D0082001F /* Club.json in Sources */,
FF70FAED2C90584900129CC2 /* PlayerDetailView.swift in Sources */,
FFA252AD2CDB734A0074E63F /* UmpireStatisticView.swift in Sources */,
FF70FAEE2C90584900129CC2 /* ListRowViewModifier.swift in Sources */,
@ -2964,6 +3250,20 @@
FF70FAF72C90584900129CC2 /* EventCreationView.swift in Sources */,
FF70FAF82C90584900129CC2 /* LocationManager.swift in Sources */,
FF70FAF92C90584900129CC2 /* CapsuleViewModifier.swift in Sources */,
C488C8542CC7E4240082001F /* BaseDateInterval.swift in Sources */,
C488C8842CCBE8FC0082001F /* NetworkStatusView.swift in Sources */,
C488C8552CC7E4240082001F /* BaseMonthData.swift in Sources */,
C488C8562CC7E4240082001F /* BaseTeamRegistration.swift in Sources */,
C488C8572CC7E4240082001F /* BaseGroupStage.swift in Sources */,
C488C8582CC7E4240082001F /* BaseCustomUser.swift in Sources */,
C488C8592CC7E4240082001F /* BaseMatch.swift in Sources */,
C488C85A2CC7E4240082001F /* BaseEvent.swift in Sources */,
C488C85B2CC7E4240082001F /* BaseRound.swift in Sources */,
C488C85C2CC7E4240082001F /* BaseMatchScheduler.swift in Sources */,
C49C73162D5B98D8008DD299 /* PlayerPaymentType.swift in Sources */,
C488C85E2CC7E4240082001F /* BasePlayerRegistration.swift in Sources */,
C488C85F2CC7E4240082001F /* BaseTeamScore.swift in Sources */,
C488C8602CC7E4240082001F /* BaseTournament.swift in Sources */,
FF70FAFA2C90584900129CC2 /* BroadcastView.swift in Sources */,
FF70FAFB2C90584900129CC2 /* SchedulerView.swift in Sources */,
FF70FAFC2C90584900129CC2 /* PadelClubButtonView.swift in Sources */,
@ -2982,6 +3282,7 @@
FF70FB072C90584900129CC2 /* TeamRowView.swift in Sources */,
FF70FB082C90584900129CC2 /* ContactManager.swift in Sources */,
FF70FB092C90584900129CC2 /* AppDelegate.swift in Sources */,
C488C8732CC816410082001F /* BasePurchase.swift in Sources */,
FF70FB0A2C90584900129CC2 /* SubscriptionInfoView.swift in Sources */,
FF70FB0B2C90584900129CC2 /* EditScoreView.swift in Sources */,
FF70FB0C2C90584900129CC2 /* TournamentOrganizerView.swift in Sources */,
@ -3000,6 +3301,7 @@
FF70FB182C90584900129CC2 /* StoreManager.swift in Sources */,
FF70FB192C90584900129CC2 /* SearchViewModel.swift in Sources */,
FF70FB1A2C90584900129CC2 /* PlayerRegistration.swift in Sources */,
C4339BFC2CFF7D68004E5F09 /* ShareModelView.swift in Sources */,
FF70FB1B2C90584900129CC2 /* ImportedPlayerView.swift in Sources */,
FF70FB1C2C90584900129CC2 /* EditingTeamView.swift in Sources */,
FF3A74342D37DCF2007E3032 /* InscriptionLegendView.swift in Sources */,
@ -3010,6 +3312,7 @@
FF70FB212C90584900129CC2 /* Persistence.swift in Sources */,
FF70FB222C90584900129CC2 /* CloseDatePicker.swift in Sources */,
FF70FB232C90584900129CC2 /* BarButtonView.swift in Sources */,
C488C8032CC7E1E40082001F /* BaseCourt.swift in Sources */,
FF70FB242C90584900129CC2 /* PlanningView.swift in Sources */,
FF70FB252C90584900129CC2 /* Match.swift in Sources */,
FF70FB262C90584900129CC2 /* TournamentLevelPickerView.swift in Sources */,
@ -3038,6 +3341,7 @@
FF70FB3C2C90584900129CC2 /* AgendaDestination.swift in Sources */,
FF70FB3D2C90584900129CC2 /* PadelClubApp.xcdatamodeld in Sources */,
FF70FB3E2C90584900129CC2 /* SetInputView.swift in Sources */,
C471D1582D0C91FE0068091F /* BaseDrawLog.swift in Sources */,
FF70FB3F2C90584900129CC2 /* ButtonValidateView.swift in Sources */,
FF70FB402C90584900129CC2 /* ClubRowView.swift in Sources */,
FF70FB412C90584900129CC2 /* ClubDetailView.swift in Sources */,
@ -3101,6 +3405,7 @@
FF70FB722C90584900129CC2 /* MatchListView.swift in Sources */,
FF70FB732C90584900129CC2 /* PadelClubApp.swift in Sources */,
FF70FB742C90584900129CC2 /* TournamentSettingsView.swift in Sources */,
C4A36F592CE2626A003738C6 /* TournamentLibrary.swift in Sources */,
FF70FB752C90584900129CC2 /* String+Crypto.swift in Sources */,
FF70FB762C90584900129CC2 /* GroupStageTeamReplacementView.swift in Sources */,
FF70FB772C90584900129CC2 /* TabItemModifier.swift in Sources */,
@ -3108,7 +3413,7 @@
FF70FB792C90584900129CC2 /* TournamentScheduleView.swift in Sources */,
FF70FB7A2C90584900129CC2 /* MatchFormatStorageView.swift in Sources */,
FF70FB7B2C90584900129CC2 /* UmpireView.swift in Sources */,
FF70FB7C2C90584900129CC2 /* User.swift in Sources */,
FF70FB7C2C90584900129CC2 /* CustomUser.swift in Sources */,
C4C33F772C9B1ED4006316DE /* CodingContainer+Extensions.swift in Sources */,
FF70FB7D2C90584900129CC2 /* MatchSummaryView.swift in Sources */,
FFA252B72CDD2C6C0074E63F /* OngoingDestination.swift in Sources */,
@ -3136,6 +3441,7 @@
FF70FB902C90584900129CC2 /* PlayerView.swift in Sources */,
FF70FB912C90584900129CC2 /* MatchDetailView.swift in Sources */,
FF70FB922C90584900129CC2 /* ExportFormat.swift in Sources */,
C488C8002CC7DCB80082001F /* BaseClub.swift in Sources */,
FF70FB932C90584900129CC2 /* PlayerBlockView.swift in Sources */,
FF70FB942C90584900129CC2 /* StoreItem.swift in Sources */,
FF70FB952C90584900129CC2 /* Selectable.swift in Sources */,
@ -3352,7 +3658,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.1.26;
MARKETING_VERSION = 1.2;
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -3397,7 +3703,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.1.26;
MARKETING_VERSION = 1.2;
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -3604,7 +3910,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.1.1;
MARKETING_VERSION = 1.2;
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub.beta;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -3646,7 +3952,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.1.1;
MARKETING_VERSION = 1.2;
PRODUCT_BUNDLE_IDENTIFIER = app.padelclub.beta;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";

@ -20,7 +20,6 @@ class AppDelegate : NSObject, UIApplicationDelegate, UNUserNotificationCenterDel
UIApplication.shared.registerForRemoteNotifications()
UNUserNotificationCenter.current().delegate = self
Logger.log("didFinishLaunchingWithOptions")
return true
}
@ -28,7 +27,7 @@ class AppDelegate : NSObject, UIApplicationDelegate, UNUserNotificationCenterDel
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
if StoreCenter.main.hasToken() {
if StoreCenter.main.isAuthenticated {
Task {
do {
let services = try StoreCenter.main.service()

@ -10,60 +10,9 @@ import SwiftUI
import LeStorage
@Observable
final class Club : ModelObject, Storable, Hashable {
static func resourceName() -> String { return "clubs" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [.get] }
static func filterByStoreIdentifier() -> Bool { return false }
static var relationshipNames: [String] = []
static func == (lhs: Club, rhs: Club) -> Bool {
lhs.id == rhs.id
}
func hash(into hasher: inout Hasher) {
return hasher.combine(id)
}
var id: String = Store.randomId()
var creator: String?
var name: String
var acronym: String
var phone: String?
var code: String?
//var federalClubData: Data?
var address: String?
var city: String?
var zipCode: String?
var latitude: Double?
var longitude: Double?
var courtCount: Int = 2
var broadcastCode: String?
var timezone: String?
// var alphabeticalName: Bool = false
internal init(creator: String? = nil, name: String, acronym: String? = nil, phone: String? = nil, code: String? = nil, address: String? = nil, city: String? = nil, zipCode: String? = nil, latitude: Double? = nil, longitude: Double? = nil, courtCount: Int = 2, broadcastCode: String? = nil, timezone: String? = nil) {
self.name = name
self.creator = creator
self.acronym = acronym ?? name.acronym()
self.phone = phone
self.code = code
self.address = address
self.city = city
self.zipCode = zipCode
self.latitude = latitude
self.longitude = longitude
self.courtCount = courtCount
self.broadcastCode = broadcastCode
self.timezone = TimeZone.current.identifier
}
override func copyFromServerInstance(_ instance: any Storable) -> Bool {
guard let copy = instance as? Club else { return false }
self.broadcastCode = copy.broadcastCode
// Logger.log("write code: \(self.broadcastCode)")
return true
}
final class Club: BaseClub {
static var copyServerResponse: Bool { return true }
func clubTitle(_ displayStyle: DisplayStyle = .wide) -> String {
switch displayStyle {
@ -82,51 +31,14 @@ final class Club : ModelObject, Storable, Hashable {
DataStore.shared.courts.filter { $0.club == self.id }.sorted(by: \.index)
}
override func deleteDependencies() throws {
override func deleteDependencies() {
let customizedCourts = self.customizedCourts
for customizedCourt in customizedCourts {
try customizedCourt.deleteDependencies()
customizedCourt.deleteDependencies()
}
DataStore.shared.courts.deleteDependencies(customizedCourts)
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _creator = "creator"
case _name = "name"
case _acronym = "acronym"
case _phone = "phone"
case _code = "code"
case _address = "address"
case _city = "city"
case _zipCode = "zipCode"
case _latitude = "latitude"
case _longitude = "longitude"
case _courtCount = "courtCount"
case _broadcastCode = "broadcastCode"
case _timezone = "timezone"
// case _alphabeticalName = "alphabeticalName"
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: ._id)
try container.encode(creator, forKey: ._creator)
try container.encode(name, forKey: ._name)
try container.encode(acronym, forKey: ._acronym)
try container.encode(phone, forKey: ._phone)
try container.encode(code, forKey: ._code)
try container.encode(address, forKey: ._address)
try container.encode(city, forKey: ._city)
try container.encode(zipCode, forKey: ._zipCode)
try container.encode(latitude, forKey: ._latitude)
try container.encode(longitude, forKey: ._longitude)
try container.encode(courtCount, forKey: ._courtCount)
try container.encode(broadcastCode, forKey: ._broadcastCode)
try container.encode(timezone, forKey: ._timezone)
}
}
extension Club {
@ -177,7 +89,7 @@ extension Club {
}
func hasBeenCreated(by creatorId: String?) -> Bool {
return creatorId == creator || creator == nil
return creatorId == creator || creator == nil || self.relatedUser == creatorId
}
func isFavorite() -> Bool {
@ -196,7 +108,9 @@ extension Club {
if let club {
return club
} else {
return Club(creator: StoreCenter.main.userId, name: name, code: code, city: city, zipCode: zipCode)
let club = Club(creator: StoreCenter.main.userId, name: name, code: code, city: city, zipCode: zipCode)
club.relatedUser = StoreCenter.main.userId
return club
}
}

@ -10,40 +10,27 @@ import SwiftUI
import LeStorage
@Observable
final class Court : ModelObject, Storable, Hashable {
static func resourceName() -> String { return "courts" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
static func filterByStoreIdentifier() -> Bool { return false }
static var relationshipNames: [String] = []
final class Court: BaseCourt {
static func == (lhs: Court, rhs: Court) -> Bool {
lhs.id == rhs.id
}
func hash(into hasher: inout Hasher) {
return hasher.combine(id)
}
init(index: Int, club: String, name: String? = nil, exitAllowed: Bool = false, indoor: Bool = false) {
var id: String = Store.randomId()
var index: Int
var club: String
var name: String?
var exitAllowed: Bool = false
var indoor: Bool = false
super.init()
init(index: Int, club: String, name: String? = nil, exitAllowed: Bool = false, indoor: Bool = false) {
self.index = index
self.lastUpdate = Date()
self.club = club
self.name = name
self.exitAllowed = exitAllowed
self.indoor = indoor
}
// internal init(club: String, name: String? = nil, index: Int) {
// self.club = club
// self.name = name
// self.index = index
// }
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
}
func courtTitle() -> String {
self.name ?? courtIndexTitle()
@ -61,27 +48,7 @@ final class Court : ModelObject, Storable, Hashable {
Store.main.findById(club)
}
override func deleteDependencies() throws {
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _index = "index"
case _club = "club"
case _name = "name"
case _exitAllowed = "exitAllowed"
case _indoor = "indoor"
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: ._id)
try container.encode(index, forKey: ._index)
try container.encode(club, forKey: ._club)
try container.encode(name, forKey: ._name)
try container.encode(exitAllowed, forKey: ._exitAllowed)
try container.encode(indoor, forKey: ._indoor)
override func deleteDependencies() {
}
}

@ -0,0 +1,262 @@
//
// User.swift
// PadelClub
//
// Created by Laurent Morvillier on 21/02/2024.
//
import Foundation
import LeStorage
enum UserRight: Int, Codable {
case none = 0
case edition = 1
case creation = 2
}
@Observable
class CustomUser: BaseCustomUser, UserBase {
// static func resourceName() -> String { "users" }
// static func tokenExemptedMethods() -> [HTTPMethod] { return [.post] }
// static func filterByStoreIdentifier() -> Bool { return false }
// static var relationshipNames: [String] = []
//
// public var id: String = Store.randomId()
// var lastUpdate: Date
// public var username: String
// public var email: String
// var clubs: [String] = []
// var umpireCode: String?
// var licenceId: String?
// var firstName: String
// var lastName: String
// var phone: String?
// var country: String?
//
// var summonsMessageBody : String? = nil
// var summonsMessageSignature: String? = nil
// var summonsAvailablePaymentMethods: String? = nil
// var summonsDisplayFormat: Bool = false
// var summonsDisplayEntryFee: Bool = false
// var summonsUseFullCustomMessage: Bool = false
// var matchFormatsDefaultDuration: [MatchFormat: Int]? = nil
// var bracketMatchFormatPreference: MatchFormat?
// var groupStageMatchFormatPreference: MatchFormat?
// var loserBracketMatchFormatPreference: MatchFormat?
// var loserBracketMode: LoserBracketMode = .automatic
//
// var deviceId: String?
init(username: String, email: String, firstName: String, lastName: String, phone: String?, country: String?, loserBracketMode: LoserBracketMode = .automatic) {
super.init(username: username, email: email, firstName: firstName, lastName: lastName, phone: phone, country: country, loserBracketMode: loserBracketMode)
// self.lastUpdate = Date()
// self.username = username
// self.firstName = firstName
// self.lastName = lastName
// self.email = email
// self.phone = phone
// self.country = country
// self.loserBracketMode = loserBracketMode
}
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
}
public func uuid() throws -> UUID {
if let uuid = UUID(uuidString: self.id) {
return uuid
}
throw UUIDError.cantConvertString(string: self.id)
}
func currentPlayerData() -> ImportedPlayer? {
guard let licenceId else { return nil }
let federalContext = PersistenceController.shared.localContainer.viewContext
let fetchRequest = ImportedPlayer.fetchRequest()
let predicate = NSPredicate(format: "license == %@", licenceId)
fetchRequest.predicate = predicate
return try? federalContext.fetch(fetchRequest).first
}
func defaultSignature() -> String {
return "Sportivement,\n\(firstName) \(lastName), votre JAP."
}
func fullName() -> String? {
guard firstName.isEmpty == false && lastName.isEmpty == false else {
return nil
}
return "\(firstName) \(lastName)"
}
func hasTenupClubs() -> Bool {
self.clubsObjects().filter({ $0.code != nil }).isEmpty == false
}
func hasFavoriteClubsAndCreatedClubs() -> Bool {
clubsObjects(includeCreated: true).isEmpty == false
}
func setUserClub(_ userClub: Club) {
self.clubs.insert(userClub.id, at: 0)
}
func clubsObjects(includeCreated: Bool = false) -> [Club] {
return DataStore.shared.clubs.filter({ (includeCreated && $0.creator == id) || clubs.contains($0.id) })
}
func createdClubsObjectsNotFavorite() -> [Club] {
return DataStore.shared.clubs.filter({ ($0.creator == id) && clubs.contains($0.id) == false })
}
func saveMatchFormatsDefaultDuration(_ matchFormat: MatchFormat, estimatedDuration: Int) {
if estimatedDuration == matchFormat.defaultEstimatedDuration {
matchFormatsDefaultDuration?.removeValue(forKey: matchFormat)
} else {
matchFormatsDefaultDuration = matchFormatsDefaultDuration ?? [MatchFormat: Int]()
matchFormatsDefaultDuration?[matchFormat] = estimatedDuration
}
}
func addClub(_ club: Club) {
if !self.clubs.contains(where: { $0.id == club.id }) {
self.clubs.append(club.id)
}
}
// enum CodingKeys: String, CodingKey {
// case _id = "id"
// case _lastUpdate = "lastUpdate"
// case _username = "username"
// case _email = "email"
// case _clubs = "clubs"
// case _umpireCode = "umpireCode"
// case _licenceId = "licenceId"
// case _firstName = "firstName"
// case _lastName = "lastName"
// case _phone = "phone"
// case _country = "country"
// case _summonsMessageBody = "summonsMessageBody"
// case _summonsMessageSignature = "summonsMessageSignature"
// case _summonsAvailablePaymentMethods = "summonsAvailablePaymentMethods"
// case _summonsDisplayFormat = "summonsDisplayFormat"
// case _summonsDisplayEntryFee = "summonsDisplayEntryFee"
// case _summonsUseFullCustomMessage = "summonsUseFullCustomMessage"
// case _matchFormatsDefaultDuration = "matchFormatsDefaultDuration"
// case _bracketMatchFormatPreference = "bracketMatchFormatPreference"
// case _groupStageMatchFormatPreference = "groupStageMatchFormatPreference"
// case _loserBracketMatchFormatPreference = "loserBracketMatchFormatPreference"
// case _deviceId = "deviceId"
// case _loserBracketMode = "loserBracketMode"
// }
//
// public required init(from decoder: Decoder) throws {
// let container = try decoder.container(keyedBy: CodingKeys.self)
//
// // Required properties
// 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)
// email = try container.decode(String.self, forKey: ._email)
// firstName = try container.decode(String.self, forKey: ._firstName)
// lastName = try container.decode(String.self, forKey: ._lastName)
//
// // Optional properties
// clubs = try container.decodeIfPresent([String].self, forKey: ._clubs) ?? []
// umpireCode = try container.decodeIfPresent(String.self, forKey: ._umpireCode)
// licenceId = try container.decodeIfPresent(String.self, forKey: ._licenceId)
// phone = try container.decodeIfPresent(String.self, forKey: ._phone)
// country = try container.decodeIfPresent(String.self, forKey: ._country)
//
// // Summons-related properties
// summonsMessageBody = try container.decodeIfPresent(String.self, forKey: ._summonsMessageBody)
// summonsMessageSignature = try container.decodeIfPresent(String.self, forKey: ._summonsMessageSignature)
// summonsAvailablePaymentMethods = try container.decodeIfPresent(String.self, forKey: ._summonsAvailablePaymentMethods)
// summonsDisplayFormat = try container.decodeIfPresent(Bool.self, forKey: ._summonsDisplayFormat) ?? false
// summonsDisplayEntryFee = try container.decodeIfPresent(Bool.self, forKey: ._summonsDisplayEntryFee) ?? false
// summonsUseFullCustomMessage = try container.decodeIfPresent(Bool.self, forKey: ._summonsUseFullCustomMessage) ?? false
//
// // Match-related properties
// matchFormatsDefaultDuration = try container.decodeIfPresent([MatchFormat: Int].self, forKey: ._matchFormatsDefaultDuration)
// bracketMatchFormatPreference = try container.decodeIfPresent(MatchFormat.self, forKey: ._bracketMatchFormatPreference)
// groupStageMatchFormatPreference = try container.decodeIfPresent(MatchFormat.self, forKey: ._groupStageMatchFormatPreference)
// loserBracketMatchFormatPreference = try container.decodeIfPresent(MatchFormat.self, forKey: ._loserBracketMatchFormatPreference)
// loserBracketMode = try container.decodeIfPresent(LoserBracketMode.self, forKey: ._loserBracketMode) ?? .automatic
// }
//
// func encode(to encoder: Encoder) throws {
// var container = encoder.container(keyedBy: CodingKeys.self)
//
// try container.encode(id, forKey: ._id)
// try container.encode(lastUpdate, forKey: ._lastUpdate)
// try container.encode(username, forKey: ._username)
// try container.encode(email, forKey: ._email)
// try container.encode(clubs, forKey: ._clubs)
//
// try container.encode(umpireCode, forKey: ._umpireCode)
// try container.encode(licenceId, forKey: ._licenceId)
// try container.encode(firstName, forKey: ._firstName)
// try container.encode(lastName, forKey: ._lastName)
// try container.encode(phone, forKey: ._phone)
// try container.encode(country, forKey: ._country)
// try container.encode(summonsMessageBody, forKey: ._summonsMessageBody)
// try container.encode(summonsMessageSignature, forKey: ._summonsMessageSignature)
// try container.encode(summonsAvailablePaymentMethods, forKey: ._summonsAvailablePaymentMethods)
// try container.encode(summonsDisplayFormat, forKey: ._summonsDisplayFormat)
// try container.encode(summonsDisplayEntryFee, forKey: ._summonsDisplayEntryFee)
// try container.encode(summonsUseFullCustomMessage, forKey: ._summonsUseFullCustomMessage)
//
// try container.encode(matchFormatsDefaultDuration, forKey: ._matchFormatsDefaultDuration)
// try container.encode(bracketMatchFormatPreference, forKey: ._bracketMatchFormatPreference)
// try container.encode(groupStageMatchFormatPreference, forKey: ._groupStageMatchFormatPreference)
// try container.encode(loserBracketMatchFormatPreference, forKey: ._loserBracketMatchFormatPreference)
// try container.encode(deviceId, forKey: ._deviceId)
//
// try container.encode(loserBracketMode, forKey: ._loserBracketMode)
// }
static func placeHolder() -> CustomUser {
return CustomUser(username: "", email: "", firstName: "", lastName: "", phone: nil, country: nil)
}
}
class UserCreationForm: CustomUser, UserPasswordBase {
init(user: CustomUser, username: String, password: String, firstName: String, lastName: String, email: String, phone: String?, country: String?) {
self.password = password
super.init(username: username, email: email, firstName: firstName, lastName: lastName, phone: phone, country: country)
self.summonsMessageBody = user.summonsMessageBody
self.summonsMessageSignature = user.summonsMessageSignature
self.summonsAvailablePaymentMethods = user.summonsAvailablePaymentMethods
self.summonsDisplayFormat = user.summonsDisplayFormat
self.summonsDisplayEntryFee = user.summonsDisplayEntryFee
self.summonsUseFullCustomMessage = user.summonsUseFullCustomMessage
self.matchFormatsDefaultDuration = user.matchFormatsDefaultDuration
self.bracketMatchFormatPreference = user.bracketMatchFormatPreference
self.groupStageMatchFormatPreference = user.groupStageMatchFormatPreference
self.loserBracketMatchFormatPreference = user.loserBracketMatchFormatPreference
}
required init(from decoder: Decoder) throws {
fatalError("init(from:) has not been implemented")
}
public var password: String
private enum CodingKeys: String, CodingKey {
case password
}
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.password, forKey: .password)
}
}

@ -13,7 +13,7 @@ class DataStore: ObservableObject {
static let shared = DataStore()
@Published var user: User = User.placeHolder() {
@Published var user: CustomUser = CustomUser.placeHolder() {
didSet {
let loggedUser = StoreCenter.main.userId != nil
StoreCenter.main.collectionsCanSynchronize = loggedUser
@ -22,7 +22,7 @@ class DataStore: ObservableObject {
if self.user.id != self.userStorage.item()?.id {
self.userStorage.setItemNoSync(self.user)
if StoreCenter.main.collectionsCanSynchronize {
Store.main.loadCollectionsFromServer()
StoreCenter.main.initialSynchronization()
self._fixMissingClubCreatorIfNecessary(self.clubs)
self._fixMissingEventCreatorIfNecessary(self.events)
}
@ -41,9 +41,9 @@ class DataStore: ObservableObject {
fileprivate(set) var dateIntervals: StoredCollection<DateInterval>
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")
var appSettings: AppSettings {
@ -52,17 +52,20 @@ class DataStore: ObservableObject {
init() {
let store = Store.main
let serverURL: String = URLs.api.rawValue
StoreCenter.main.blackListUserName("apple-test")
// let secureScheme = true
let domain: String = URLs.activationHost.rawValue
#if DEBUG
if let server = PListReader.readString(plist: "local", key: "server") {
StoreCenter.main.synchronizationApiURL = server
if let secure = PListReader.readBool(plist: "local", key: "secure_server"),
let domain = PListReader.readString(plist: "local", key: "server_domain") {
StoreCenter.main.configureURLs(secureScheme: secure, domain: domain)
} else {
StoreCenter.main.synchronizationApiURL = serverURL
StoreCenter.main.configureURLs(secureScheme: true, domain: domain)
}
#else
StoreCenter.main.synchronizationApiURL = serverURL
StoreCenter.main.configureURLs(secureScheme: true, domain: domain)
#endif
StoreCenter.main.logsFailedAPICalls()
@ -75,18 +78,19 @@ class DataStore: ObservableObject {
}
#endif
Logger.log("Sync URL: \(StoreCenter.main.synchronizationApiURL ?? "none"), sync: \(synchronized) ")
StoreCenter.main.forceNoSynchronization = !synchronized
let indexed: Bool = true
self.clubs = store.registerCollection(synchronized: synchronized, indexed: indexed)
self.courts = store.registerCollection(synchronized: synchronized, indexed: indexed)
self.tournaments = store.registerCollection(synchronized: synchronized, indexed: indexed)
self.events = store.registerCollection(synchronized: synchronized, indexed: indexed)
self.monthData = store.registerCollection(synchronized: false, indexed: indexed)
self.dateIntervals = store.registerCollection(synchronized: synchronized, indexed: indexed)
let indexed: Bool = true
self.clubs = store.registerSynchronizedCollection(indexed: indexed)
self.courts = store.registerSynchronizedCollection(indexed: indexed)
self.tournaments = store.registerSynchronizedCollection(indexed: indexed)
self.events = store.registerSynchronizedCollection(indexed: indexed)
self.dateIntervals = store.registerSynchronizedCollection(indexed: indexed)
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
StoreCenter.main.loadApiCallCollection(type: GroupStage.self)
@ -95,6 +99,7 @@ class DataStore: ObservableObject {
StoreCenter.main.loadApiCallCollection(type: TeamRegistration.self)
StoreCenter.main.loadApiCallCollection(type: Match.self)
StoreCenter.main.loadApiCallCollection(type: TeamScore.self)
StoreCenter.main.loadApiCallCollection(type: DrawLog.self)
NotificationCenter.default.addObserver(self, selector: #selector(collectionDidLoad), name: NSNotification.Name.CollectionDidLoad, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(collectionDidUpdate), name: NSNotification.Name.CollectionDidChange, object: nil)
@ -111,46 +116,38 @@ class DataStore: ObservableObject {
}
func saveUser() {
do {
if user.username.count > 0 {
try self.userStorage.update()
self.userStorage.update()
} else {
self._temporaryLocalUser.item = self.user
}
} catch {
Logger.error(error)
}
}
@objc func collectionDidLoad(notification: Notification) {
if let userSingleton: StoredSingleton<User> = notification.object as? StoredSingleton<User> {
self.user = userSingleton.item() ?? self._temporaryLocalUser.item ?? User.placeHolder()
if let userSingleton: StoredSingleton<CustomUser> = notification.object as? StoredSingleton<CustomUser> {
self.user = userSingleton.item() ?? self._temporaryLocalUser.item ?? CustomUser.placeHolder()
} else if let clubsCollection: StoredCollection<Club> = notification.object as? StoredCollection<Club> {
self._fixMissingClubCreatorIfNecessary(clubsCollection)
} else if let eventsCollection: StoredCollection<Event> = notification.object as? StoredCollection<Event> {
self._fixMissingEventCreatorIfNecessary(eventsCollection)
}
if Store.main.collectionsAllLoaded() {
if Store.main.fileCollectionsAllLoaded() {
Patcher.applyAllWhenApplicable()
}
}
fileprivate func _fixMissingClubCreatorIfNecessary(_ clubsCollection: StoredCollection<Club>) {
do {
for club in clubsCollection {
if let userId = StoreCenter.main.userId, club.creator == nil {
club.creator = userId
self.userStorage.item()?.addClub(club)
try self.userStorage.update()
self.userStorage.update()
clubsCollection.writeChangeAndInsertOnServer(instance: club)
}
}
} catch {
Logger.error(error)
}
}
fileprivate func _fixMissingEventCreatorIfNecessary(_ eventsCollection: StoredCollection<Event>) {
@ -215,12 +212,24 @@ class DataStore: ObservableObject {
}
func deleteTournament(_ tournament: Tournament) {
let event = tournament.eventObject()
let isLastTournament = event?.tournaments.count == 1
self.tournaments.delete(instance: tournament)
if let event, isLastTournament {
self.events.delete(instance: event)
}
StoreCenter.main.destroyStore(identifier: tournament.id)
}
fileprivate func _localDisconnect() {
StoreCenter.main.collectionsCanSynchronize = false
let tournamendIds: [String] = self.tournaments.map { $0.id }
TournamentLibrary.shared.reset()
self.tournaments.reset()
self.clubs.reset()
self.courts.reset()
@ -232,75 +241,75 @@ class DataStore: ObservableObject {
Guard.main.disconnect()
StoreCenter.main.disconnect()
self.user = self._temporaryLocalUser.item ?? User.placeHolder()
self.user.clubs.removeAll()
// done after because otherwise folders remain
for tournament in tournamendIds {
StoreCenter.main.destroyStore(identifier: tournament.id)
}
}
func copyToLocalServer(tournament: Tournament) {
Task {
do {
if let url = PListReader.readString(plist: "local", key: "local_server"),
let login = PListReader.readString(plist: "local", key: "username"),
let pass = PListReader.readString(plist: "local", key: "password") {
let service = Services(url: url)
let _: User = try await service.login(username: login, password: pass)
tournament.event = nil
_ = try await service.post(tournament)
for groupStage in tournament.groupStages() {
_ = try await service.post(groupStage)
}
for round in tournament.rounds() {
try await self._insertRoundAndChildren(round: round, service: service)
}
for teamRegistration in tournament.unsortedTeams() {
_ = try await service.post(teamRegistration)
for playerRegistration in teamRegistration.unsortedPlayers() {
_ = try await service.post(playerRegistration)
}
}
for groupStage in tournament.groupStages() {
for match in groupStage._matches() {
try await self._insertMatch(match: match, service: service)
}
}
for round in tournament.allRounds() {
for match in round._matches() {
try await self._insertMatch(match: match, service: service)
}
}
}
} catch {
Logger.error(error)
}
}
}
fileprivate func _insertRoundAndChildren(round: Round, service: Services) async throws {
_ = try await service.post(round)
for loserRound in round.loserRounds() {
try await self._insertRoundAndChildren(round: loserRound, service: service)
}
}
self.user = self._temporaryLocalUser.item ?? CustomUser.placeHolder()
self.user.clubs.removeAll()
fileprivate func _insertMatch(match: Match, service: Services) async throws {
_ = try await service.post(match)
for teamScore in match.teamScores {
_ = try await service.post(teamScore)
}
}
// func copyToLocalServer(tournament: Tournament) {
//
// Task {
// do {
//
// if let url = PListReader.readString(plist: "local", key: "local_server"),
// let login = PListReader.readString(plist: "local", key: "username"),
// let pass = PListReader.readString(plist: "local", key: "password") {
// let service = Services(url: url)
// let _: CustomUser = try await service.login(username: login, password: pass)
//
// tournament.event = nil
// _ = try await service.post(tournament)
//
// for groupStage in tournament.groupStages() {
// _ = try await service.post(groupStage)
// }
// for round in tournament.rounds() {
// try await self._insertRoundAndChildren(round: round, service: service)
// }
// for teamRegistration in tournament.unsortedTeams() {
// _ = try await service.post(teamRegistration)
// for playerRegistration in teamRegistration.unsortedPlayers() {
// _ = try await service.post(playerRegistration)
// }
// }
// for groupStage in tournament.groupStages() {
// for match in groupStage._matches() {
// try await self._insertMatch(match: match, service: service)
// }
// }
// for round in tournament.allRounds() {
// for match in round._matches() {
// try await self._insertMatch(match: match, service: service)
// }
// }
//
// }
// } catch {
// Logger.error(error)
// }
// }
//
// }
//
// fileprivate func _insertRoundAndChildren(round: Round, service: Services) async throws {
// _ = try await service.post(round)
// for loserRound in round.loserRounds() {
// try await self._insertRoundAndChildren(round: loserRound, service: service)
// }
// }
//
// fileprivate func _insertMatch(match: Match, service: Services) async throws {
// _ = try await service.post(match)
// for teamScore in match.teamScores {
// _ = try await service.post(teamScore)
// }
//
// }
// MARK: - Convenience
@ -310,25 +319,28 @@ class DataStore: ObservableObject {
var runningMatches: [Match] = []
for tournament in lastTournaments {
let matches = tournament.tournamentStore.matches.filter { match in
if let store = tournament.tournamentStore {
let matches = store.matches.filter { match in
match.disabled == false && match.isRunning()
}
runningMatches.append(contentsOf: matches)
}
}
return runningMatches
}
func runningAndNextMatches() -> [Match] {
let dateNow : Date = Date()
let lastTournaments = self.tournaments.filter { $0.isDeleted == false && $0.startDate <= dateNow && $0.hasEnded() == false }.sorted(by: \Tournament.startDate, order: .descending).prefix(10)
var runningMatches: [Match] = []
for tournament in lastTournaments {
let matches = tournament.tournamentStore.matches.filter { match in
if let store = tournament.tournamentStore {
let matches = store.matches.filter { match in
match.disabled == false && match.startDate != nil && match.endDate == nil }
runningMatches.append(contentsOf: matches)
}
}
return runningMatches
}
@ -338,10 +350,12 @@ class DataStore: ObservableObject {
var runningMatches: [Match] = []
for tournament in lastTournaments {
let matches = tournament.tournamentStore.matches.filter { match in
if let store = tournament.tournamentStore {
let matches = store.matches.filter { match in
match.disabled == false && match.hasEnded() }
runningMatches.append(contentsOf: matches)
}
}
return runningMatches.sorted(by: \.endDate!, order: .descending)
}

@ -10,23 +10,31 @@ import SwiftUI
import LeStorage
@Observable
final class DateInterval: ModelObject, Storable {
static func resourceName() -> String { return "date-intervals" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
static func filterByStoreIdentifier() -> Bool { return false }
static var relationshipNames: [String] = []
var id: String = Store.randomId()
var event: String
var courtIndex: Int
var startDate: Date
var endDate: Date
final class DateInterval: BaseDateInterval {
// static func resourceName() -> String { return "date-intervals" }
// static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
// static func filterByStoreIdentifier() -> Bool { return false }
// static var relationshipNames: [String] = []
//
// var id: String = Store.randomId()
// var lastUpdate: Date
// var event: String
// var courtIndex: Int
// var startDate: Date
// var endDate: Date
internal init(event: String, courtIndex: Int, startDate: Date, endDate: Date) {
self.event = event
self.courtIndex = courtIndex
self.startDate = startDate
self.endDate = endDate
super.init(event: event, courtIndex: courtIndex, startDate: startDate, endDate: endDate)
// self.lastUpdate = Date()
// self.event = event
// self.courtIndex = courtIndex
// self.startDate = startDate
// self.endDate = endDate
}
required init(from decoder: any Decoder) throws {
try super.init(from: decoder)
}
var range: Range<Date> {
@ -45,19 +53,20 @@ final class DateInterval: ModelObject, Storable {
date <= startDate && date <= endDate && date >= startDate && date >= endDate
}
override func deleteDependencies() throws {
override func deleteDependencies() {
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _event = "event"
case _courtIndex = "courtIndex"
case _startDate = "startDate"
case _endDate = "endDate"
}
// enum CodingKeys: String, CodingKey {
// case _id = "id"
// case _lastUpdate = "lastUpdate"
// case _event = "event"
// case _courtIndex = "courtIndex"
// case _startDate = "startDate"
// case _endDate = "endDate"
// }
func insertOnServer() throws {
try DataStore.shared.dateIntervals.writeChangeAndInsertOnServer(instance: self)
DataStore.shared.dateIntervals.writeChangeAndInsertOnServer(instance: self)
}
}

@ -10,29 +10,7 @@ import SwiftUI
import LeStorage
@Observable
final class DrawLog: ModelObject, Storable {
static func resourceName() -> String { return "draw-logs" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
static func filterByStoreIdentifier() -> Bool { return true }
static var relationshipNames: [String] = []
var id: String = Store.randomId()
var tournament: String
var drawDate: Date = Date()
var drawSeed: Int
var drawMatchIndex: Int
var drawTeamPosition: TeamPosition
var drawType: DrawType
internal init(id: String = Store.randomId(), tournament: String, drawDate: Date = Date(), drawSeed: Int, drawMatchIndex: Int, drawTeamPosition: TeamPosition, drawType: DrawType) {
self.id = id
self.tournament = tournament
self.drawDate = drawDate
self.drawSeed = drawSeed
self.drawMatchIndex = drawMatchIndex
self.drawTeamPosition = drawTeamPosition
self.drawType = drawType
}
final class DrawLog: BaseDrawLog, SideStorable {
func tournamentObject() -> Tournament? {
Store.main.findById(self.tournament)
@ -74,7 +52,7 @@ final class DrawLog: ModelObject, Storable {
switch drawType {
case .seed:
let roundIndex = RoundRule.roundIndex(fromMatchIndex: drawMatchIndex)
return tournamentStore.rounds.first(where: { $0.parent == nil && $0.index == roundIndex })?._matches().first(where: { $0.index == drawMatchIndex })
return tournamentStore?.rounds.first(where: { $0.parent == nil && $0.index == roundIndex })?._matches().first(where: { $0.index == drawMatchIndex })
default:
return nil
}
@ -92,50 +70,11 @@ final class DrawLog: ModelObject, Storable {
return drawMatch()?.matchTitle() ?? ""
}
var tournamentStore: TournamentStore {
return TournamentStore.instance(tournamentId: self.tournament)
}
override func deleteDependencies() throws {
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _tournament = "tournament"
case _drawDate = "drawDate"
case _drawSeed = "drawSeed"
case _drawMatchIndex = "drawMatchIndex"
case _drawTeamPosition = "drawTeamPosition"
case _drawType = "drawType"
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: ._id)
tournament = try container.decode(String.self, forKey: ._tournament)
drawDate = try container.decode(Date.self, forKey: ._drawDate)
drawSeed = try container.decode(Int.self, forKey: ._drawSeed)
drawMatchIndex = try container.decode(Int.self, forKey: ._drawMatchIndex)
drawTeamPosition = try container.decode(TeamPosition.self, forKey: ._drawTeamPosition)
drawType = try container.decodeIfPresent(DrawType.self, forKey: ._drawType) ?? .seed
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: ._id)
try container.encode(tournament, forKey: ._tournament)
try container.encode(drawDate, forKey: ._drawDate)
try container.encode(drawSeed, forKey: ._drawSeed)
try container.encode(drawMatchIndex, forKey: ._drawMatchIndex)
try container.encode(drawTeamPosition, forKey: ._drawTeamPosition)
try container.encode(drawType, forKey: ._drawType)
var tournamentStore: TournamentStore? {
return TournamentLibrary.shared.store(tournamentId: self.tournament)
}
func insertOnServer() throws {
self.tournamentStore.drawLogs.writeChangeAndInsertOnServer(instance: self)
override func deleteDependencies() {
}
}

@ -10,38 +10,28 @@ import LeStorage
import SwiftUI
@Observable
final class Event: ModelObject, Storable {
static func resourceName() -> String { return "events" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
static func filterByStoreIdentifier() -> Bool { return false }
static var relationshipNames: [String] = []
var id: String = Store.randomId()
var creator: String?
var club: String?
var creationDate: Date = Date()
var name: String?
var tenupId: String?
final class Event: BaseEvent {
internal init(creator: String? = nil, club: String? = nil, name: String? = nil, tenupId: String? = nil) {
self.creator = creator
self.club = club
self.name = name
self.tenupId = tenupId
super.init(creator: creator, club: club, name: name, tenupId: tenupId)
self.relatedUser = creator
}
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
}
override func deleteDependencies() throws {
override func deleteDependencies() {
let tournaments = self.tournaments
for tournament in tournaments {
try tournament.deleteDependencies()
tournament.deleteDependencies()
}
DataStore.shared.tournaments.deleteDependencies(tournaments)
let courtsUnavailabilities = self.courtsUnavailability
for courtsUnavailability in courtsUnavailabilities {
try courtsUnavailability.deleteDependencies()
courtsUnavailability.deleteDependencies()
}
DataStore.shared.dateIntervals.deleteDependencies(courtsUnavailabilities)
}
@ -94,7 +84,7 @@ final class Event: ModelObject, Storable {
}
func insertOnServer() throws {
try DataStore.shared.events.writeChangeAndInsertOnServer(instance: self)
DataStore.shared.events.writeChangeAndInsertOnServer(instance: self)
for tournament in self.tournaments {
try tournament.insertOnServer()
}
@ -104,27 +94,3 @@ final class Event: ModelObject, Storable {
}
}
extension Event {
enum CodingKeys: String, CodingKey {
case _id = "id"
case _creator = "creator"
case _club = "club"
case _creationDate = "creationDate"
case _name = "name"
case _tenupId = "tenupId"
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: ._id)
try container.encode(creator, forKey: ._creator)
try container.encode(club, forKey: ._club)
try container.encode(creationDate, forKey: ._creationDate)
try container.encode(name, forKey: ._name)
try container.encode(tenupId, forKey: ._tenupId)
}
}

@ -0,0 +1,146 @@
// Generated by SwiftModelGenerator
// Do not modify this file manually
import Foundation
import LeStorage
import SwiftUI
@Observable
class BaseClub: SyncedModelObject, SyncedStorable {
static func resourceName() -> String { return "clubs" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
var id: String = Store.randomId()
var creator: String? = nil
var name: String = ""
var acronym: String = ""
var phone: String? = nil
var code: String? = nil
var address: String? = nil
var city: String? = nil
var zipCode: String? = nil
var latitude: Double? = nil
var longitude: Double? = nil
var courtCount: Int = 2
var broadcastCode: String? = nil
var timezone: String? = TimeZone.current.identifier
init(
id: String = Store.randomId(),
creator: String? = nil,
name: String = "",
acronym: String = "",
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,
timezone: String? = TimeZone.current.identifier
) {
super.init()
self.id = id
self.creator = creator
self.name = name
self.acronym = acronym
self.phone = phone
self.code = code
self.address = address
self.city = city
self.zipCode = zipCode
self.latitude = latitude
self.longitude = longitude
self.courtCount = courtCount
self.broadcastCode = broadcastCode
self.timezone = timezone
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _creator = "creator"
case _name = "name"
case _acronym = "acronym"
case _phone = "phone"
case _code = "code"
case _address = "address"
case _city = "city"
case _zipCode = "zipCode"
case _latitude = "latitude"
case _longitude = "longitude"
case _courtCount = "courtCount"
case _broadcastCode = "broadcastCode"
case _timezone = "timezone"
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId()
self.creator = try container.decodeIfPresent(String.self, forKey: ._creator) ?? nil
self.name = try container.decodeIfPresent(String.self, forKey: ._name) ?? ""
self.acronym = try container.decodeIfPresent(String.self, forKey: ._acronym) ?? ""
self.phone = try container.decodeIfPresent(String.self, forKey: ._phone) ?? nil
self.code = try container.decodeIfPresent(String.self, forKey: ._code) ?? nil
self.address = try container.decodeIfPresent(String.self, forKey: ._address) ?? nil
self.city = try container.decodeIfPresent(String.self, forKey: ._city) ?? nil
self.zipCode = try container.decodeIfPresent(String.self, forKey: ._zipCode) ?? nil
self.latitude = try container.decodeIfPresent(Double.self, forKey: ._latitude) ?? nil
self.longitude = try container.decodeIfPresent(Double.self, forKey: ._longitude) ?? nil
self.courtCount = try container.decodeIfPresent(Int.self, forKey: ._courtCount) ?? 2
self.broadcastCode = try container.decodeIfPresent(String.self, forKey: ._broadcastCode) ?? nil
self.timezone = try container.decodeIfPresent(String.self, forKey: ._timezone) ?? TimeZone.current.identifier
try super.init(from: decoder)
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.id, forKey: ._id)
try container.encode(self.creator, forKey: ._creator)
try container.encode(self.name, forKey: ._name)
try container.encode(self.acronym, forKey: ._acronym)
try container.encode(self.phone, forKey: ._phone)
try container.encode(self.code, forKey: ._code)
try container.encode(self.address, forKey: ._address)
try container.encode(self.city, forKey: ._city)
try container.encode(self.zipCode, forKey: ._zipCode)
try container.encode(self.latitude, forKey: ._latitude)
try container.encode(self.longitude, forKey: ._longitude)
try container.encode(self.courtCount, forKey: ._courtCount)
try container.encode(self.broadcastCode, forKey: ._broadcastCode)
try container.encode(self.timezone, forKey: ._timezone)
try super.encode(to: encoder)
}
func creatorValue() -> CustomUser? {
guard let creator = self.creator else { return nil }
return Store.main.findById(creator)
}
func copy(from other: any Storable) {
guard let club = other as? BaseClub else { return }
self.id = club.id
self.creator = club.creator
self.name = club.name
self.acronym = club.acronym
self.phone = club.phone
self.code = club.code
self.address = club.address
self.city = club.city
self.zipCode = club.zipCode
self.latitude = club.latitude
self.longitude = club.longitude
self.courtCount = club.courtCount
self.broadcastCode = club.broadcastCode
self.timezone = club.timezone
}
static func relationships() -> [Relationship] {
return [
Relationship(type: CustomUser.self, keyPath: \BaseClub.creator),
]
}
}

@ -0,0 +1,89 @@
// Generated by SwiftModelGenerator
// Do not modify this file manually
import Foundation
import LeStorage
import SwiftUI
@Observable
class BaseCourt: SyncedModelObject, SyncedStorable {
static func resourceName() -> String { return "courts" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
var id: String = Store.randomId()
var index: Int = 0
var club: String = ""
var name: String? = nil
var exitAllowed: Bool = false
var indoor: Bool = false
init(
id: String = Store.randomId(),
index: Int = 0,
club: String = "",
name: String? = nil,
exitAllowed: Bool = false,
indoor: Bool = false
) {
super.init()
self.id = id
self.index = index
self.club = club
self.name = name
self.exitAllowed = exitAllowed
self.indoor = indoor
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _index = "index"
case _club = "club"
case _name = "name"
case _exitAllowed = "exitAllowed"
case _indoor = "indoor"
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId()
self.index = try container.decodeIfPresent(Int.self, forKey: ._index) ?? 0
self.club = try container.decodeIfPresent(String.self, forKey: ._club) ?? ""
self.name = try container.decodeIfPresent(String.self, forKey: ._name) ?? nil
self.exitAllowed = try container.decodeIfPresent(Bool.self, forKey: ._exitAllowed) ?? false
self.indoor = try container.decodeIfPresent(Bool.self, forKey: ._indoor) ?? false
try super.init(from: decoder)
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.id, forKey: ._id)
try container.encode(self.index, forKey: ._index)
try container.encode(self.club, forKey: ._club)
try container.encode(self.name, forKey: ._name)
try container.encode(self.exitAllowed, forKey: ._exitAllowed)
try container.encode(self.indoor, forKey: ._indoor)
try super.encode(to: encoder)
}
func clubValue() -> Club? {
return Store.main.findById(club)
}
func copy(from other: any Storable) {
guard let court = other as? BaseCourt else { return }
self.id = court.id
self.index = court.index
self.club = court.club
self.name = court.name
self.exitAllowed = court.exitAllowed
self.indoor = court.indoor
}
static func relationships() -> [Relationship] {
return [
Relationship(type: Club.self, keyPath: \BaseCourt.club),
]
}
}

@ -0,0 +1,202 @@
// Generated by SwiftModelGenerator
// Do not modify this file manually
import Foundation
import LeStorage
import SwiftUI
@Observable
class BaseCustomUser: SyncedModelObject, SyncedStorable {
static func resourceName() -> String { return "users" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [.post] }
var id: String = Store.randomId()
var username: String = ""
var email: String = ""
var clubs: [String] = []
var umpireCode: String? = nil
var licenceId: String? = nil
var firstName: String = ""
var lastName: String = ""
var phone: String? = nil
var country: String? = nil
var summonsMessageBody: String? = nil
var summonsMessageSignature: String? = nil
var summonsAvailablePaymentMethods: String? = nil
var summonsDisplayFormat: Bool = false
var summonsDisplayEntryFee: Bool = false
var summonsUseFullCustomMessage: Bool = false
var matchFormatsDefaultDuration: [MatchFormat: Int]? = nil
var bracketMatchFormatPreference: MatchFormat? = nil
var groupStageMatchFormatPreference: MatchFormat? = nil
var loserBracketMatchFormatPreference: MatchFormat? = nil
var loserBracketMode: LoserBracketMode = .automatic
var deviceId: String? = nil
var agents: [String] = []
init(
id: String = Store.randomId(),
username: String = "",
email: String = "",
clubs: [String] = [],
umpireCode: String? = nil,
licenceId: String? = nil,
firstName: String = "",
lastName: String = "",
phone: String? = nil,
country: String? = nil,
summonsMessageBody: String? = nil,
summonsMessageSignature: String? = nil,
summonsAvailablePaymentMethods: String? = nil,
summonsDisplayFormat: Bool = false,
summonsDisplayEntryFee: Bool = false,
summonsUseFullCustomMessage: Bool = false,
matchFormatsDefaultDuration: [MatchFormat: Int]? = nil,
bracketMatchFormatPreference: MatchFormat? = nil,
groupStageMatchFormatPreference: MatchFormat? = nil,
loserBracketMatchFormatPreference: MatchFormat? = nil,
loserBracketMode: LoserBracketMode = .automatic,
deviceId: String? = nil,
agents: [String] = []
) {
super.init()
self.id = id
self.username = username
self.email = email
self.clubs = clubs
self.umpireCode = umpireCode
self.licenceId = licenceId
self.firstName = firstName
self.lastName = lastName
self.phone = phone
self.country = country
self.summonsMessageBody = summonsMessageBody
self.summonsMessageSignature = summonsMessageSignature
self.summonsAvailablePaymentMethods = summonsAvailablePaymentMethods
self.summonsDisplayFormat = summonsDisplayFormat
self.summonsDisplayEntryFee = summonsDisplayEntryFee
self.summonsUseFullCustomMessage = summonsUseFullCustomMessage
self.matchFormatsDefaultDuration = matchFormatsDefaultDuration
self.bracketMatchFormatPreference = bracketMatchFormatPreference
self.groupStageMatchFormatPreference = groupStageMatchFormatPreference
self.loserBracketMatchFormatPreference = loserBracketMatchFormatPreference
self.loserBracketMode = loserBracketMode
self.deviceId = deviceId
self.agents = agents
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _username = "username"
case _email = "email"
case _clubs = "clubs"
case _umpireCode = "umpireCode"
case _licenceId = "licenceId"
case _firstName = "firstName"
case _lastName = "lastName"
case _phone = "phone"
case _country = "country"
case _summonsMessageBody = "summonsMessageBody"
case _summonsMessageSignature = "summonsMessageSignature"
case _summonsAvailablePaymentMethods = "summonsAvailablePaymentMethods"
case _summonsDisplayFormat = "summonsDisplayFormat"
case _summonsDisplayEntryFee = "summonsDisplayEntryFee"
case _summonsUseFullCustomMessage = "summonsUseFullCustomMessage"
case _matchFormatsDefaultDuration = "matchFormatsDefaultDuration"
case _bracketMatchFormatPreference = "bracketMatchFormatPreference"
case _groupStageMatchFormatPreference = "groupStageMatchFormatPreference"
case _loserBracketMatchFormatPreference = "loserBracketMatchFormatPreference"
case _loserBracketMode = "loserBracketMode"
case _deviceId = "deviceId"
case _agents = "agents"
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId()
self.username = try container.decodeIfPresent(String.self, forKey: ._username) ?? ""
self.email = try container.decodeIfPresent(String.self, forKey: ._email) ?? ""
self.clubs = try container.decodeIfPresent([String].self, forKey: ._clubs) ?? []
self.umpireCode = try container.decodeIfPresent(String.self, forKey: ._umpireCode) ?? nil
self.licenceId = try container.decodeIfPresent(String.self, forKey: ._licenceId) ?? nil
self.firstName = try container.decodeIfPresent(String.self, forKey: ._firstName) ?? ""
self.lastName = try container.decodeIfPresent(String.self, forKey: ._lastName) ?? ""
self.phone = try container.decodeIfPresent(String.self, forKey: ._phone) ?? nil
self.country = try container.decodeIfPresent(String.self, forKey: ._country) ?? nil
self.summonsMessageBody = try container.decodeIfPresent(String.self, forKey: ._summonsMessageBody) ?? nil
self.summonsMessageSignature = try container.decodeIfPresent(String.self, forKey: ._summonsMessageSignature) ?? nil
self.summonsAvailablePaymentMethods = try container.decodeIfPresent(String.self, forKey: ._summonsAvailablePaymentMethods) ?? nil
self.summonsDisplayFormat = try container.decodeIfPresent(Bool.self, forKey: ._summonsDisplayFormat) ?? false
self.summonsDisplayEntryFee = try container.decodeIfPresent(Bool.self, forKey: ._summonsDisplayEntryFee) ?? false
self.summonsUseFullCustomMessage = try container.decodeIfPresent(Bool.self, forKey: ._summonsUseFullCustomMessage) ?? false
self.matchFormatsDefaultDuration = try container.decodeIfPresent([MatchFormat: Int].self, forKey: ._matchFormatsDefaultDuration) ?? nil
self.bracketMatchFormatPreference = try container.decodeIfPresent(MatchFormat.self, forKey: ._bracketMatchFormatPreference) ?? nil
self.groupStageMatchFormatPreference = try container.decodeIfPresent(MatchFormat.self, forKey: ._groupStageMatchFormatPreference) ?? nil
self.loserBracketMatchFormatPreference = try container.decodeIfPresent(MatchFormat.self, forKey: ._loserBracketMatchFormatPreference) ?? nil
self.loserBracketMode = try container.decodeIfPresent(LoserBracketMode.self, forKey: ._loserBracketMode) ?? .automatic
self.deviceId = try container.decodeIfPresent(String.self, forKey: ._deviceId) ?? nil
self.agents = try container.decodeIfPresent([String].self, forKey: ._agents) ?? []
try super.init(from: decoder)
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.id, forKey: ._id)
try container.encode(self.username, forKey: ._username)
try container.encode(self.email, forKey: ._email)
try container.encode(self.clubs, forKey: ._clubs)
try container.encode(self.umpireCode, forKey: ._umpireCode)
try container.encode(self.licenceId, forKey: ._licenceId)
try container.encode(self.firstName, forKey: ._firstName)
try container.encode(self.lastName, forKey: ._lastName)
try container.encode(self.phone, forKey: ._phone)
try container.encode(self.country, forKey: ._country)
try container.encode(self.summonsMessageBody, forKey: ._summonsMessageBody)
try container.encode(self.summonsMessageSignature, forKey: ._summonsMessageSignature)
try container.encode(self.summonsAvailablePaymentMethods, forKey: ._summonsAvailablePaymentMethods)
try container.encode(self.summonsDisplayFormat, forKey: ._summonsDisplayFormat)
try container.encode(self.summonsDisplayEntryFee, forKey: ._summonsDisplayEntryFee)
try container.encode(self.summonsUseFullCustomMessage, forKey: ._summonsUseFullCustomMessage)
try container.encode(self.matchFormatsDefaultDuration, forKey: ._matchFormatsDefaultDuration)
try container.encode(self.bracketMatchFormatPreference, forKey: ._bracketMatchFormatPreference)
try container.encode(self.groupStageMatchFormatPreference, forKey: ._groupStageMatchFormatPreference)
try container.encode(self.loserBracketMatchFormatPreference, forKey: ._loserBracketMatchFormatPreference)
try container.encode(self.loserBracketMode, forKey: ._loserBracketMode)
try container.encode(self.deviceId, forKey: ._deviceId)
try container.encode(self.agents, forKey: ._agents)
try super.encode(to: encoder)
}
func copy(from other: any Storable) {
guard let customuser = other as? BaseCustomUser else { return }
self.id = customuser.id
self.username = customuser.username
self.email = customuser.email
self.clubs = customuser.clubs
self.umpireCode = customuser.umpireCode
self.licenceId = customuser.licenceId
self.firstName = customuser.firstName
self.lastName = customuser.lastName
self.phone = customuser.phone
self.country = customuser.country
self.summonsMessageBody = customuser.summonsMessageBody
self.summonsMessageSignature = customuser.summonsMessageSignature
self.summonsAvailablePaymentMethods = customuser.summonsAvailablePaymentMethods
self.summonsDisplayFormat = customuser.summonsDisplayFormat
self.summonsDisplayEntryFee = customuser.summonsDisplayEntryFee
self.summonsUseFullCustomMessage = customuser.summonsUseFullCustomMessage
self.matchFormatsDefaultDuration = customuser.matchFormatsDefaultDuration
self.bracketMatchFormatPreference = customuser.bracketMatchFormatPreference
self.groupStageMatchFormatPreference = customuser.groupStageMatchFormatPreference
self.loserBracketMatchFormatPreference = customuser.loserBracketMatchFormatPreference
self.loserBracketMode = customuser.loserBracketMode
self.deviceId = customuser.deviceId
self.agents = customuser.agents
}
static func relationships() -> [Relationship] {
return []
}
}

@ -0,0 +1,76 @@
// Generated by SwiftModelGenerator
// Do not modify this file manually
import Foundation
import LeStorage
import SwiftUI
@Observable
class BaseDateInterval: SyncedModelObject, SyncedStorable {
static func resourceName() -> String { return "date-intervals" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
var id: String = Store.randomId()
var event: String = ""
var courtIndex: Int = 0
var startDate: Date = Date()
var endDate: Date = Date()
init(
id: String = Store.randomId(),
event: String = "",
courtIndex: Int = 0,
startDate: Date = Date(),
endDate: Date = Date()
) {
super.init()
self.id = id
self.event = event
self.courtIndex = courtIndex
self.startDate = startDate
self.endDate = endDate
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _event = "event"
case _courtIndex = "courtIndex"
case _startDate = "startDate"
case _endDate = "endDate"
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId()
self.event = try container.decodeIfPresent(String.self, forKey: ._event) ?? ""
self.courtIndex = try container.decodeIfPresent(Int.self, forKey: ._courtIndex) ?? 0
self.startDate = try container.decodeIfPresent(Date.self, forKey: ._startDate) ?? Date()
self.endDate = try container.decodeIfPresent(Date.self, forKey: ._endDate) ?? Date()
try super.init(from: decoder)
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.id, forKey: ._id)
try container.encode(self.event, forKey: ._event)
try container.encode(self.courtIndex, forKey: ._courtIndex)
try container.encode(self.startDate, forKey: ._startDate)
try container.encode(self.endDate, forKey: ._endDate)
try super.encode(to: encoder)
}
func copy(from other: any Storable) {
guard let dateinterval = other as? BaseDateInterval else { return }
self.id = dateinterval.id
self.event = dateinterval.event
self.courtIndex = dateinterval.courtIndex
self.startDate = dateinterval.startDate
self.endDate = dateinterval.endDate
}
static func relationships() -> [Relationship] {
return []
}
}

@ -0,0 +1,96 @@
// Generated by SwiftModelGenerator
// Do not modify this file manually
import Foundation
import LeStorage
import SwiftUI
@Observable
class BaseDrawLog: SyncedModelObject, SyncedStorable {
static func resourceName() -> String { return "draw-logs" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
var id: String = Store.randomId()
var tournament: String = ""
var drawDate: Date = Date()
var drawSeed: Int = 0
var drawMatchIndex: Int = 0
var drawTeamPosition: TeamPosition = TeamPosition.one
var drawType: DrawType = DrawType.seed
init(
id: String = Store.randomId(),
tournament: String = "",
drawDate: Date = Date(),
drawSeed: Int = 0,
drawMatchIndex: Int = 0,
drawTeamPosition: TeamPosition = TeamPosition.one,
drawType: DrawType = DrawType.seed
) {
super.init()
self.id = id
self.tournament = tournament
self.drawDate = drawDate
self.drawSeed = drawSeed
self.drawMatchIndex = drawMatchIndex
self.drawTeamPosition = drawTeamPosition
self.drawType = drawType
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _tournament = "tournament"
case _drawDate = "drawDate"
case _drawSeed = "drawSeed"
case _drawMatchIndex = "drawMatchIndex"
case _drawTeamPosition = "drawTeamPosition"
case _drawType = "drawType"
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId()
self.tournament = try container.decodeIfPresent(String.self, forKey: ._tournament) ?? ""
self.drawDate = try container.decodeIfPresent(Date.self, forKey: ._drawDate) ?? Date()
self.drawSeed = try container.decodeIfPresent(Int.self, forKey: ._drawSeed) ?? 0
self.drawMatchIndex = try container.decodeIfPresent(Int.self, forKey: ._drawMatchIndex) ?? 0
self.drawTeamPosition = try container.decodeIfPresent(TeamPosition.self, forKey: ._drawTeamPosition) ?? TeamPosition.one
self.drawType = try container.decodeIfPresent(DrawType.self, forKey: ._drawType) ?? DrawType.seed
try super.init(from: decoder)
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.id, forKey: ._id)
try container.encode(self.tournament, forKey: ._tournament)
try container.encode(self.drawDate, forKey: ._drawDate)
try container.encode(self.drawSeed, forKey: ._drawSeed)
try container.encode(self.drawMatchIndex, forKey: ._drawMatchIndex)
try container.encode(self.drawTeamPosition, forKey: ._drawTeamPosition)
try container.encode(self.drawType, forKey: ._drawType)
try super.encode(to: encoder)
}
func tournamentValue() -> Tournament? {
return Store.main.findById(tournament)
}
func copy(from other: any Storable) {
guard let drawlog = other as? BaseDrawLog else { return }
self.id = drawlog.id
self.tournament = drawlog.tournament
self.drawDate = drawlog.drawDate
self.drawSeed = drawlog.drawSeed
self.drawMatchIndex = drawlog.drawMatchIndex
self.drawTeamPosition = drawlog.drawTeamPosition
self.drawType = drawlog.drawType
}
static func relationships() -> [Relationship] {
return [
Relationship(type: Tournament.self, keyPath: \BaseDrawLog.tournament),
]
}
}

@ -0,0 +1,96 @@
// Generated by SwiftModelGenerator
// Do not modify this file manually
import Foundation
import LeStorage
import SwiftUI
@Observable
class BaseEvent: SyncedModelObject, SyncedStorable {
static func resourceName() -> String { return "events" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
var id: String = Store.randomId()
var creator: String? = nil
var club: String? = nil
var creationDate: Date = Date()
var name: String? = nil
var tenupId: String? = nil
init(
id: String = Store.randomId(),
creator: String? = nil,
club: String? = nil,
creationDate: Date = Date(),
name: String? = nil,
tenupId: String? = nil
) {
super.init()
self.id = id
self.creator = creator
self.club = club
self.creationDate = creationDate
self.name = name
self.tenupId = tenupId
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _creator = "creator"
case _club = "club"
case _creationDate = "creationDate"
case _name = "name"
case _tenupId = "tenupId"
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId()
self.creator = try container.decodeIfPresent(String.self, forKey: ._creator) ?? nil
self.club = try container.decodeIfPresent(String.self, forKey: ._club) ?? nil
self.creationDate = try container.decodeIfPresent(Date.self, forKey: ._creationDate) ?? Date()
self.name = try container.decodeIfPresent(String.self, forKey: ._name) ?? nil
self.tenupId = try container.decodeIfPresent(String.self, forKey: ._tenupId) ?? nil
try super.init(from: decoder)
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.id, forKey: ._id)
try container.encode(self.creator, forKey: ._creator)
try container.encode(self.club, forKey: ._club)
try container.encode(self.creationDate, forKey: ._creationDate)
try container.encode(self.name, forKey: ._name)
try container.encode(self.tenupId, forKey: ._tenupId)
try super.encode(to: encoder)
}
func creatorValue() -> CustomUser? {
guard let creator = self.creator else { return nil }
return Store.main.findById(creator)
}
func clubValue() -> Club? {
guard let club = self.club else { return nil }
return Store.main.findById(club)
}
func copy(from other: any Storable) {
guard let event = other as? BaseEvent else { return }
self.id = event.id
self.creator = event.creator
self.club = event.club
self.creationDate = event.creationDate
self.name = event.name
self.tenupId = event.tenupId
}
static func relationships() -> [Relationship] {
return [
Relationship(type: CustomUser.self, keyPath: \BaseEvent.creator),
Relationship(type: Club.self, keyPath: \BaseEvent.club),
]
}
}

@ -0,0 +1,103 @@
// Generated by SwiftModelGenerator
// Do not modify this file manually
import Foundation
import LeStorage
import SwiftUI
@Observable
class BaseGroupStage: SyncedModelObject, SyncedStorable {
static func resourceName() -> String { return "group-stages" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
var id: String = Store.randomId()
var tournament: String = ""
var index: Int = 0
var size: Int = 0
var format: MatchFormat? = nil
var startDate: Date? = nil
var name: String? = nil
var step: Int = 0
init(
id: String = Store.randomId(),
tournament: String = "",
index: Int = 0,
size: Int = 0,
format: MatchFormat? = nil,
startDate: Date? = nil,
name: String? = nil,
step: Int = 0
) {
super.init()
self.id = id
self.tournament = tournament
self.index = index
self.size = size
self.format = format
self.startDate = startDate
self.name = name
self.step = step
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _tournament = "tournament"
case _index = "index"
case _size = "size"
case _format = "format"
case _startDate = "startDate"
case _name = "name"
case _step = "step"
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId()
self.tournament = try container.decodeIfPresent(String.self, forKey: ._tournament) ?? ""
self.index = try container.decodeIfPresent(Int.self, forKey: ._index) ?? 0
self.size = try container.decodeIfPresent(Int.self, forKey: ._size) ?? 0
self.format = try container.decodeIfPresent(MatchFormat.self, forKey: ._format) ?? nil
self.startDate = try container.decodeIfPresent(Date.self, forKey: ._startDate) ?? nil
self.name = try container.decodeIfPresent(String.self, forKey: ._name) ?? nil
self.step = try container.decodeIfPresent(Int.self, forKey: ._step) ?? 0
try super.init(from: decoder)
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.id, forKey: ._id)
try container.encode(self.tournament, forKey: ._tournament)
try container.encode(self.index, forKey: ._index)
try container.encode(self.size, forKey: ._size)
try container.encode(self.format, forKey: ._format)
try container.encode(self.startDate, forKey: ._startDate)
try container.encode(self.name, forKey: ._name)
try container.encode(self.step, forKey: ._step)
try super.encode(to: encoder)
}
func tournamentValue() -> Tournament? {
return Store.main.findById(tournament)
}
func copy(from other: any Storable) {
guard let groupstage = other as? BaseGroupStage else { return }
self.id = groupstage.id
self.tournament = groupstage.tournament
self.index = groupstage.index
self.size = groupstage.size
self.format = groupstage.format
self.startDate = groupstage.startDate
self.name = groupstage.name
self.step = groupstage.step
}
static func relationships() -> [Relationship] {
return [
Relationship(type: Tournament.self, keyPath: \BaseGroupStage.tournament),
]
}
}

@ -0,0 +1,152 @@
// Generated by SwiftModelGenerator
// Do not modify this file manually
import Foundation
import LeStorage
import SwiftUI
@Observable
class BaseMatch: SyncedModelObject, SyncedStorable {
static func resourceName() -> String { return "matches" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
var id: String = Store.randomId()
var round: String? = nil
var groupStage: String? = nil
var startDate: Date? = nil
var endDate: Date? = nil
var index: Int = 0
var format: MatchFormat? = nil
var servingTeamId: String? = nil
var winningTeamId: String? = nil
var losingTeamId: String? = nil
var name: String? = nil
var disabled: Bool = false
var courtIndex: Int? = nil
var confirmed: Bool = false
init(
id: String = Store.randomId(),
round: String? = nil,
groupStage: String? = nil,
startDate: Date? = nil,
endDate: Date? = nil,
index: Int = 0,
format: MatchFormat? = nil,
servingTeamId: String? = nil,
winningTeamId: String? = nil,
losingTeamId: String? = nil,
name: String? = nil,
disabled: Bool = false,
courtIndex: Int? = nil,
confirmed: Bool = false
) {
super.init()
self.id = id
self.round = round
self.groupStage = groupStage
self.startDate = startDate
self.endDate = endDate
self.index = index
self.format = format
self.servingTeamId = servingTeamId
self.winningTeamId = winningTeamId
self.losingTeamId = losingTeamId
self.name = name
self.disabled = disabled
self.courtIndex = courtIndex
self.confirmed = confirmed
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _round = "round"
case _groupStage = "groupStage"
case _startDate = "startDate"
case _endDate = "endDate"
case _index = "index"
case _format = "format"
case _servingTeamId = "servingTeamId"
case _winningTeamId = "winningTeamId"
case _losingTeamId = "losingTeamId"
case _name = "name"
case _disabled = "disabled"
case _courtIndex = "courtIndex"
case _confirmed = "confirmed"
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId()
self.round = try container.decodeIfPresent(String.self, forKey: ._round) ?? nil
self.groupStage = try container.decodeIfPresent(String.self, forKey: ._groupStage) ?? nil
self.startDate = try container.decodeIfPresent(Date.self, forKey: ._startDate) ?? nil
self.endDate = try container.decodeIfPresent(Date.self, forKey: ._endDate) ?? nil
self.index = try container.decodeIfPresent(Int.self, forKey: ._index) ?? 0
self.format = try container.decodeIfPresent(MatchFormat.self, forKey: ._format) ?? nil
self.servingTeamId = try container.decodeIfPresent(String.self, forKey: ._servingTeamId) ?? nil
self.winningTeamId = try container.decodeIfPresent(String.self, forKey: ._winningTeamId) ?? nil
self.losingTeamId = try container.decodeIfPresent(String.self, forKey: ._losingTeamId) ?? nil
self.name = try container.decodeIfPresent(String.self, forKey: ._name) ?? nil
self.disabled = try container.decodeIfPresent(Bool.self, forKey: ._disabled) ?? false
self.courtIndex = try container.decodeIfPresent(Int.self, forKey: ._courtIndex) ?? nil
self.confirmed = try container.decodeIfPresent(Bool.self, forKey: ._confirmed) ?? false
try super.init(from: decoder)
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.id, forKey: ._id)
try container.encode(self.round, forKey: ._round)
try container.encode(self.groupStage, forKey: ._groupStage)
try container.encode(self.startDate, forKey: ._startDate)
try container.encode(self.endDate, forKey: ._endDate)
try container.encode(self.index, forKey: ._index)
try container.encode(self.format, forKey: ._format)
try container.encode(self.servingTeamId, forKey: ._servingTeamId)
try container.encode(self.winningTeamId, forKey: ._winningTeamId)
try container.encode(self.losingTeamId, forKey: ._losingTeamId)
try container.encode(self.name, forKey: ._name)
try container.encode(self.disabled, forKey: ._disabled)
try container.encode(self.courtIndex, forKey: ._courtIndex)
try container.encode(self.confirmed, forKey: ._confirmed)
try super.encode(to: encoder)
}
func roundValue() -> Round? {
guard let round = self.round else { return nil }
return self.store?.findById(round)
}
func groupStageValue() -> GroupStage? {
guard let groupStage = self.groupStage else { return nil }
return self.store?.findById(groupStage)
}
func copy(from other: any Storable) {
guard let match = other as? BaseMatch else { return }
self.id = match.id
self.round = match.round
self.groupStage = match.groupStage
self.startDate = match.startDate
self.endDate = match.endDate
self.index = match.index
self.format = match.format
self.servingTeamId = match.servingTeamId
self.winningTeamId = match.winningTeamId
self.losingTeamId = match.losingTeamId
self.name = match.name
self.disabled = match.disabled
self.courtIndex = match.courtIndex
self.confirmed = match.confirmed
}
static func relationships() -> [Relationship] {
return [
Relationship(type: Round.self, keyPath: \BaseMatch.round),
Relationship(type: GroupStage.self, keyPath: \BaseMatch.groupStage),
]
}
}

@ -0,0 +1,159 @@
// Generated by SwiftModelGenerator
// Do not modify this file manually
import Foundation
import LeStorage
import SwiftUI
@Observable
class BaseMatchScheduler: BaseModelObject, Storable {
static func resourceName() -> String { return "match-scheduler" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
var id: String = Store.randomId()
var tournament: String = ""
var timeDifferenceLimit: Int = 0
var loserBracketRotationDifference: Int = 0
var upperBracketRotationDifference: Int = 0
var accountUpperBracketBreakTime: Bool = false
var accountLoserBracketBreakTime: Bool = false
var randomizeCourts: Bool = false
var rotationDifferenceIsImportant: Bool = false
var shouldHandleUpperRoundSlice: Bool = false
var shouldEndRoundBeforeStartingNext: Bool = false
var groupStageChunkCount: Int? = nil
var overrideCourtsUnavailability: Bool = false
var shouldTryToFillUpCourtsAvailable: Bool = false
var courtsAvailable: Set<Int> = Set<Int>()
var simultaneousStart: Bool = true
init(
id: String = Store.randomId(),
tournament: String = "",
timeDifferenceLimit: Int = 0,
loserBracketRotationDifference: Int = 0,
upperBracketRotationDifference: Int = 0,
accountUpperBracketBreakTime: Bool = false,
accountLoserBracketBreakTime: Bool = false,
randomizeCourts: Bool = false,
rotationDifferenceIsImportant: Bool = false,
shouldHandleUpperRoundSlice: Bool = false,
shouldEndRoundBeforeStartingNext: Bool = false,
groupStageChunkCount: Int? = nil,
overrideCourtsUnavailability: Bool = false,
shouldTryToFillUpCourtsAvailable: Bool = false,
courtsAvailable: Set<Int> = Set<Int>(),
simultaneousStart: Bool = true
) {
super.init()
self.id = id
self.tournament = tournament
self.timeDifferenceLimit = timeDifferenceLimit
self.loserBracketRotationDifference = loserBracketRotationDifference
self.upperBracketRotationDifference = upperBracketRotationDifference
self.accountUpperBracketBreakTime = accountUpperBracketBreakTime
self.accountLoserBracketBreakTime = accountLoserBracketBreakTime
self.randomizeCourts = randomizeCourts
self.rotationDifferenceIsImportant = rotationDifferenceIsImportant
self.shouldHandleUpperRoundSlice = shouldHandleUpperRoundSlice
self.shouldEndRoundBeforeStartingNext = shouldEndRoundBeforeStartingNext
self.groupStageChunkCount = groupStageChunkCount
self.overrideCourtsUnavailability = overrideCourtsUnavailability
self.shouldTryToFillUpCourtsAvailable = shouldTryToFillUpCourtsAvailable
self.courtsAvailable = courtsAvailable
self.simultaneousStart = simultaneousStart
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _tournament = "tournament"
case _timeDifferenceLimit = "timeDifferenceLimit"
case _loserBracketRotationDifference = "loserBracketRotationDifference"
case _upperBracketRotationDifference = "upperBracketRotationDifference"
case _accountUpperBracketBreakTime = "accountUpperBracketBreakTime"
case _accountLoserBracketBreakTime = "accountLoserBracketBreakTime"
case _randomizeCourts = "randomizeCourts"
case _rotationDifferenceIsImportant = "rotationDifferenceIsImportant"
case _shouldHandleUpperRoundSlice = "shouldHandleUpperRoundSlice"
case _shouldEndRoundBeforeStartingNext = "shouldEndRoundBeforeStartingNext"
case _groupStageChunkCount = "groupStageChunkCount"
case _overrideCourtsUnavailability = "overrideCourtsUnavailability"
case _shouldTryToFillUpCourtsAvailable = "shouldTryToFillUpCourtsAvailable"
case _courtsAvailable = "courtsAvailable"
case _simultaneousStart = "simultaneousStart"
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId()
self.tournament = try container.decodeIfPresent(String.self, forKey: ._tournament) ?? ""
self.timeDifferenceLimit = try container.decodeIfPresent(Int.self, forKey: ._timeDifferenceLimit) ?? 0
self.loserBracketRotationDifference = try container.decodeIfPresent(Int.self, forKey: ._loserBracketRotationDifference) ?? 0
self.upperBracketRotationDifference = try container.decodeIfPresent(Int.self, forKey: ._upperBracketRotationDifference) ?? 0
self.accountUpperBracketBreakTime = try container.decodeIfPresent(Bool.self, forKey: ._accountUpperBracketBreakTime) ?? false
self.accountLoserBracketBreakTime = try container.decodeIfPresent(Bool.self, forKey: ._accountLoserBracketBreakTime) ?? false
self.randomizeCourts = try container.decodeIfPresent(Bool.self, forKey: ._randomizeCourts) ?? false
self.rotationDifferenceIsImportant = try container.decodeIfPresent(Bool.self, forKey: ._rotationDifferenceIsImportant) ?? false
self.shouldHandleUpperRoundSlice = try container.decodeIfPresent(Bool.self, forKey: ._shouldHandleUpperRoundSlice) ?? false
self.shouldEndRoundBeforeStartingNext = try container.decodeIfPresent(Bool.self, forKey: ._shouldEndRoundBeforeStartingNext) ?? false
self.groupStageChunkCount = try container.decodeIfPresent(Int.self, forKey: ._groupStageChunkCount) ?? nil
self.overrideCourtsUnavailability = try container.decodeIfPresent(Bool.self, forKey: ._overrideCourtsUnavailability) ?? false
self.shouldTryToFillUpCourtsAvailable = try container.decodeIfPresent(Bool.self, forKey: ._shouldTryToFillUpCourtsAvailable) ?? false
self.courtsAvailable = try container.decodeIfPresent(Set<Int>.self, forKey: ._courtsAvailable) ?? Set<Int>()
self.simultaneousStart = try container.decodeIfPresent(Bool.self, forKey: ._simultaneousStart) ?? true
try super.init(from: decoder)
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.id, forKey: ._id)
try container.encode(self.tournament, forKey: ._tournament)
try container.encode(self.timeDifferenceLimit, forKey: ._timeDifferenceLimit)
try container.encode(self.loserBracketRotationDifference, forKey: ._loserBracketRotationDifference)
try container.encode(self.upperBracketRotationDifference, forKey: ._upperBracketRotationDifference)
try container.encode(self.accountUpperBracketBreakTime, forKey: ._accountUpperBracketBreakTime)
try container.encode(self.accountLoserBracketBreakTime, forKey: ._accountLoserBracketBreakTime)
try container.encode(self.randomizeCourts, forKey: ._randomizeCourts)
try container.encode(self.rotationDifferenceIsImportant, forKey: ._rotationDifferenceIsImportant)
try container.encode(self.shouldHandleUpperRoundSlice, forKey: ._shouldHandleUpperRoundSlice)
try container.encode(self.shouldEndRoundBeforeStartingNext, forKey: ._shouldEndRoundBeforeStartingNext)
try container.encode(self.groupStageChunkCount, forKey: ._groupStageChunkCount)
try container.encode(self.overrideCourtsUnavailability, forKey: ._overrideCourtsUnavailability)
try container.encode(self.shouldTryToFillUpCourtsAvailable, forKey: ._shouldTryToFillUpCourtsAvailable)
try container.encode(self.courtsAvailable, forKey: ._courtsAvailable)
try container.encode(self.simultaneousStart, forKey: ._simultaneousStart)
try super.encode(to: encoder)
}
func tournamentValue() -> Tournament? {
return Store.main.findById(tournament)
}
func copy(from other: any Storable) {
guard let matchscheduler = other as? BaseMatchScheduler else { return }
self.id = matchscheduler.id
self.tournament = matchscheduler.tournament
self.timeDifferenceLimit = matchscheduler.timeDifferenceLimit
self.loserBracketRotationDifference = matchscheduler.loserBracketRotationDifference
self.upperBracketRotationDifference = matchscheduler.upperBracketRotationDifference
self.accountUpperBracketBreakTime = matchscheduler.accountUpperBracketBreakTime
self.accountLoserBracketBreakTime = matchscheduler.accountLoserBracketBreakTime
self.randomizeCourts = matchscheduler.randomizeCourts
self.rotationDifferenceIsImportant = matchscheduler.rotationDifferenceIsImportant
self.shouldHandleUpperRoundSlice = matchscheduler.shouldHandleUpperRoundSlice
self.shouldEndRoundBeforeStartingNext = matchscheduler.shouldEndRoundBeforeStartingNext
self.groupStageChunkCount = matchscheduler.groupStageChunkCount
self.overrideCourtsUnavailability = matchscheduler.overrideCourtsUnavailability
self.shouldTryToFillUpCourtsAvailable = matchscheduler.shouldTryToFillUpCourtsAvailable
self.courtsAvailable = matchscheduler.courtsAvailable
self.simultaneousStart = matchscheduler.simultaneousStart
}
static func relationships() -> [Relationship] {
return [
Relationship(type: Tournament.self, keyPath: \BaseMatchScheduler.tournament),
]
}
}

@ -0,0 +1,118 @@
// Generated by SwiftModelGenerator
// Do not modify this file manually
import Foundation
import LeStorage
import SwiftUI
@Observable
class BaseMonthData: BaseModelObject, Storable {
static func resourceName() -> String { return "month-data" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
var id: String = Store.randomId()
var monthKey: String = ""
var creationDate: Date = Date()
var maleUnrankedValue: Int? = nil
var femaleUnrankedValue: Int? = nil
var maleCount: Int? = nil
var femaleCount: Int? = nil
var anonymousCount: Int? = nil
var incompleteMode: Bool = false
var dataModelIdentifier: String? = nil
var fileModelIdentifier: String? = nil
init(
id: String = Store.randomId(),
monthKey: String = "",
creationDate: Date = Date(),
maleUnrankedValue: Int? = nil,
femaleUnrankedValue: Int? = nil,
maleCount: Int? = nil,
femaleCount: Int? = nil,
anonymousCount: Int? = nil,
incompleteMode: Bool = false,
dataModelIdentifier: String? = nil,
fileModelIdentifier: String? = nil
) {
super.init()
self.id = id
self.monthKey = monthKey
self.creationDate = creationDate
self.maleUnrankedValue = maleUnrankedValue
self.femaleUnrankedValue = femaleUnrankedValue
self.maleCount = maleCount
self.femaleCount = femaleCount
self.anonymousCount = anonymousCount
self.incompleteMode = incompleteMode
self.dataModelIdentifier = dataModelIdentifier
self.fileModelIdentifier = fileModelIdentifier
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _monthKey = "monthKey"
case _creationDate = "creationDate"
case _maleUnrankedValue = "maleUnrankedValue"
case _femaleUnrankedValue = "femaleUnrankedValue"
case _maleCount = "maleCount"
case _femaleCount = "femaleCount"
case _anonymousCount = "anonymousCount"
case _incompleteMode = "incompleteMode"
case _dataModelIdentifier = "dataModelIdentifier"
case _fileModelIdentifier = "fileModelIdentifier"
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId()
self.monthKey = try container.decodeIfPresent(String.self, forKey: ._monthKey) ?? ""
self.creationDate = try container.decodeIfPresent(Date.self, forKey: ._creationDate) ?? Date()
self.maleUnrankedValue = try container.decodeIfPresent(Int.self, forKey: ._maleUnrankedValue) ?? nil
self.femaleUnrankedValue = try container.decodeIfPresent(Int.self, forKey: ._femaleUnrankedValue) ?? nil
self.maleCount = try container.decodeIfPresent(Int.self, forKey: ._maleCount) ?? nil
self.femaleCount = try container.decodeIfPresent(Int.self, forKey: ._femaleCount) ?? nil
self.anonymousCount = try container.decodeIfPresent(Int.self, forKey: ._anonymousCount) ?? nil
self.incompleteMode = try container.decodeIfPresent(Bool.self, forKey: ._incompleteMode) ?? false
self.dataModelIdentifier = try container.decodeIfPresent(String.self, forKey: ._dataModelIdentifier) ?? nil
self.fileModelIdentifier = try container.decodeIfPresent(String.self, forKey: ._fileModelIdentifier) ?? nil
try super.init(from: decoder)
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.id, forKey: ._id)
try container.encode(self.monthKey, forKey: ._monthKey)
try container.encode(self.creationDate, forKey: ._creationDate)
try container.encode(self.maleUnrankedValue, forKey: ._maleUnrankedValue)
try container.encode(self.femaleUnrankedValue, forKey: ._femaleUnrankedValue)
try container.encode(self.maleCount, forKey: ._maleCount)
try container.encode(self.femaleCount, forKey: ._femaleCount)
try container.encode(self.anonymousCount, forKey: ._anonymousCount)
try container.encode(self.incompleteMode, forKey: ._incompleteMode)
try container.encode(self.dataModelIdentifier, forKey: ._dataModelIdentifier)
try container.encode(self.fileModelIdentifier, forKey: ._fileModelIdentifier)
try super.encode(to: encoder)
}
func copy(from other: any Storable) {
guard let monthdata = other as? BaseMonthData else { return }
self.id = monthdata.id
self.monthKey = monthdata.monthKey
self.creationDate = monthdata.creationDate
self.maleUnrankedValue = monthdata.maleUnrankedValue
self.femaleUnrankedValue = monthdata.femaleUnrankedValue
self.maleCount = monthdata.maleCount
self.femaleCount = monthdata.femaleCount
self.anonymousCount = monthdata.anonymousCount
self.incompleteMode = monthdata.incompleteMode
self.dataModelIdentifier = monthdata.dataModelIdentifier
self.fileModelIdentifier = monthdata.fileModelIdentifier
}
static func relationships() -> [Relationship] {
return []
}
}

@ -0,0 +1,202 @@
// Generated by SwiftModelGenerator
// Do not modify this file manually
import Foundation
import LeStorage
import SwiftUI
@Observable
class BasePlayerRegistration: SyncedModelObject, SyncedStorable {
static func resourceName() -> String { return "player-registrations" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
var id: String = Store.randomId()
var teamRegistration: String? = nil
var firstName: String = ""
var lastName: String = ""
var licenceId: String? = nil
var rank: Int? = nil
var paymentType: PlayerPaymentType? = nil
var sex: PlayerSexType? = nil
var tournamentPlayed: Int? = nil
var points: Double? = nil
var clubName: String? = nil
var ligueName: String? = nil
var assimilation: String? = nil
var phoneNumber: String? = nil
var email: String? = nil
var birthdate: String? = nil
var computedRank: Int = 0
var source: PlayerRegistration.PlayerDataSource? = nil
var hasArrived: Bool = false
var coach: Bool = false
var captain: Bool = false
var registeredOnline: Bool = false
init(
id: String = Store.randomId(),
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: PlayerRegistration.PlayerDataSource? = nil,
hasArrived: Bool = false,
coach: Bool = false,
captain: Bool = false,
registeredOnline: Bool = false
) {
super.init()
self.id = id
self.teamRegistration = teamRegistration
self.firstName = firstName
self.lastName = lastName
self.licenceId = licenceId
self.rank = rank
self.paymentType = paymentType
self.sex = sex
self.tournamentPlayed = tournamentPlayed
self.points = points
self.clubName = clubName
self.ligueName = ligueName
self.assimilation = assimilation
self.phoneNumber = phoneNumber
self.email = email
self.birthdate = birthdate
self.computedRank = computedRank
self.source = source
self.hasArrived = hasArrived
self.coach = coach
self.captain = captain
self.registeredOnline = registeredOnline
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _teamRegistration = "teamRegistration"
case _firstName = "firstName"
case _lastName = "lastName"
case _licenceId = "licenceId"
case _rank = "rank"
case _paymentType = "paymentType"
case _sex = "sex"
case _tournamentPlayed = "tournamentPlayed"
case _points = "points"
case _clubName = "clubName"
case _ligueName = "ligueName"
case _assimilation = "assimilation"
case _phoneNumber = "phoneNumber"
case _email = "email"
case _birthdate = "birthdate"
case _computedRank = "computedRank"
case _source = "source"
case _hasArrived = "hasArrived"
case _coach = "coach"
case _captain = "captain"
case _registeredOnline = "registeredOnline"
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId()
self.teamRegistration = try container.decodeIfPresent(String.self, forKey: ._teamRegistration) ?? nil
self.firstName = try container.decodeIfPresent(String.self, forKey: ._firstName) ?? ""
self.lastName = try container.decodeIfPresent(String.self, forKey: ._lastName) ?? ""
self.licenceId = try container.decodeIfPresent(String.self, forKey: ._licenceId) ?? nil
self.rank = try container.decodeIfPresent(Int.self, forKey: ._rank) ?? nil
self.paymentType = try container.decodeIfPresent(PlayerPaymentType.self, forKey: ._paymentType) ?? nil
self.sex = try container.decodeIfPresent(PlayerSexType.self, forKey: ._sex) ?? nil
self.tournamentPlayed = try container.decodeIfPresent(Int.self, forKey: ._tournamentPlayed) ?? nil
self.points = try container.decodeIfPresent(Double.self, forKey: ._points) ?? nil
self.clubName = try container.decodeIfPresent(String.self, forKey: ._clubName) ?? nil
self.ligueName = try container.decodeIfPresent(String.self, forKey: ._ligueName) ?? nil
self.assimilation = try container.decodeIfPresent(String.self, forKey: ._assimilation) ?? nil
self.phoneNumber = try container.decodeIfPresent(String.self, forKey: ._phoneNumber) ?? nil
self.email = try container.decodeIfPresent(String.self, forKey: ._email) ?? nil
self.birthdate = try container.decodeIfPresent(String.self, forKey: ._birthdate) ?? nil
self.computedRank = try container.decodeIfPresent(Int.self, forKey: ._computedRank) ?? 0
self.source = try container.decodeIfPresent(PlayerRegistration.PlayerDataSource.self, forKey: ._source) ?? nil
self.hasArrived = try container.decodeIfPresent(Bool.self, forKey: ._hasArrived) ?? false
self.coach = try container.decodeIfPresent(Bool.self, forKey: ._coach) ?? false
self.captain = try container.decodeIfPresent(Bool.self, forKey: ._captain) ?? false
self.registeredOnline = try container.decodeIfPresent(Bool.self, forKey: ._registeredOnline) ?? false
try super.init(from: decoder)
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.id, forKey: ._id)
try container.encode(self.teamRegistration, forKey: ._teamRegistration)
try container.encode(self.firstName, forKey: ._firstName)
try container.encode(self.lastName, forKey: ._lastName)
try container.encode(self.licenceId, forKey: ._licenceId)
try container.encode(self.rank, forKey: ._rank)
try container.encode(self.paymentType, forKey: ._paymentType)
try container.encode(self.sex, forKey: ._sex)
try container.encode(self.tournamentPlayed, forKey: ._tournamentPlayed)
try container.encode(self.points, forKey: ._points)
try container.encode(self.clubName, forKey: ._clubName)
try container.encode(self.ligueName, forKey: ._ligueName)
try container.encode(self.assimilation, forKey: ._assimilation)
try container.encode(self.phoneNumber, forKey: ._phoneNumber)
try container.encode(self.email, forKey: ._email)
try container.encode(self.birthdate, forKey: ._birthdate)
try container.encode(self.computedRank, forKey: ._computedRank)
try container.encode(self.source, forKey: ._source)
try container.encode(self.hasArrived, forKey: ._hasArrived)
try container.encode(self.coach, forKey: ._coach)
try container.encode(self.captain, forKey: ._captain)
try container.encode(self.registeredOnline, forKey: ._registeredOnline)
try super.encode(to: encoder)
}
func teamRegistrationValue() -> TeamRegistration? {
guard let teamRegistration = self.teamRegistration else { return nil }
return Store.main.findById(teamRegistration)
}
func copy(from other: any Storable) {
guard let playerregistration = other as? BasePlayerRegistration else { return }
self.id = playerregistration.id
self.teamRegistration = playerregistration.teamRegistration
self.firstName = playerregistration.firstName
self.lastName = playerregistration.lastName
self.licenceId = playerregistration.licenceId
self.rank = playerregistration.rank
self.paymentType = playerregistration.paymentType
self.sex = playerregistration.sex
self.tournamentPlayed = playerregistration.tournamentPlayed
self.points = playerregistration.points
self.clubName = playerregistration.clubName
self.ligueName = playerregistration.ligueName
self.assimilation = playerregistration.assimilation
self.phoneNumber = playerregistration.phoneNumber
self.email = playerregistration.email
self.birthdate = playerregistration.birthdate
self.computedRank = playerregistration.computedRank
self.source = playerregistration.source
self.hasArrived = playerregistration.hasArrived
self.coach = playerregistration.coach
self.captain = playerregistration.captain
self.registeredOnline = playerregistration.registeredOnline
}
static func relationships() -> [Relationship] {
return [
Relationship(type: TeamRegistration.self, keyPath: \BasePlayerRegistration.teamRegistration),
]
}
}

@ -0,0 +1,95 @@
// Generated by SwiftModelGenerator
// Do not modify this file manually
import Foundation
import LeStorage
class BasePurchase: SyncedModelObject, SyncedStorable {
static func resourceName() -> String { return "purchases" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
var id: UInt64 = 0
var user: String = ""
var purchaseDate: Date = Date()
var productId: String = ""
var quantity: Int? = nil
var revocationDate: Date? = nil
var expirationDate: Date? = nil
init(
id: UInt64 = 0,
user: String = "",
purchaseDate: Date = Date(),
productId: String = "",
quantity: Int? = nil,
revocationDate: Date? = nil,
expirationDate: Date? = nil
) {
super.init()
self.id = id
self.user = user
self.purchaseDate = purchaseDate
self.productId = productId
self.quantity = quantity
self.revocationDate = revocationDate
self.expirationDate = expirationDate
}
enum CodingKeys: String, CodingKey {
case id = "id"
case user = "user"
case purchaseDate = "purchaseDate"
case productId = "productId"
case quantity = "quantity"
case revocationDate = "revocationDate"
case expirationDate = "expirationDate"
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decodeIfPresent(UInt64.self, forKey: .id) ?? 0
self.user = try container.decodeEncrypted(key: .user)
self.purchaseDate = try container.decodeIfPresent(Date.self, forKey: .purchaseDate) ?? Date()
self.productId = try container.decodeIfPresent(String.self, forKey: .productId) ?? ""
self.quantity = try container.decodeIfPresent(Int.self, forKey: .quantity) ?? nil
self.revocationDate = try container.decodeIfPresent(Date.self, forKey: .revocationDate) ?? nil
self.expirationDate = try container.decodeIfPresent(Date.self, forKey: .expirationDate) ?? nil
try super.init(from: decoder)
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.id, forKey: .id)
try container.encodeAndEncryptIfPresent(self.user.data(using: .utf8), forKey: .user)
try container.encode(self.purchaseDate, forKey: .purchaseDate)
try container.encode(self.productId, forKey: .productId)
try container.encode(self.quantity, forKey: .quantity)
try container.encode(self.revocationDate, forKey: .revocationDate)
try container.encode(self.expirationDate, forKey: .expirationDate)
try super.encode(to: encoder)
}
func userValue() -> CustomUser? {
return Store.main.findById(user)
}
func copy(from other: any Storable) {
guard let purchase = other as? BasePurchase else { return }
self.id = purchase.id
self.user = purchase.user
self.purchaseDate = purchase.purchaseDate
self.productId = purchase.productId
self.quantity = purchase.quantity
self.revocationDate = purchase.revocationDate
self.expirationDate = purchase.expirationDate
}
static func relationships() -> [Relationship] {
return [
Relationship(type: CustomUser.self, keyPath: \BasePurchase.user),
]
}
}

@ -0,0 +1,103 @@
// Generated by SwiftModelGenerator
// Do not modify this file manually
import Foundation
import LeStorage
import SwiftUI
@Observable
class BaseRound: SyncedModelObject, SyncedStorable {
static func resourceName() -> String { return "rounds" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
var id: String = Store.randomId()
var tournament: String = ""
var index: Int = 0
var parent: String? = nil
var format: MatchFormat? = nil
var startDate: Date? = nil
var groupStageLoserBracket: Bool = false
var loserBracketMode: LoserBracketMode = .automatic
init(
id: String = Store.randomId(),
tournament: String = "",
index: Int = 0,
parent: String? = nil,
format: MatchFormat? = nil,
startDate: Date? = nil,
groupStageLoserBracket: Bool = false,
loserBracketMode: LoserBracketMode = .automatic
) {
super.init()
self.id = id
self.tournament = tournament
self.index = index
self.parent = parent
self.format = format
self.startDate = startDate
self.groupStageLoserBracket = groupStageLoserBracket
self.loserBracketMode = loserBracketMode
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _tournament = "tournament"
case _index = "index"
case _parent = "parent"
case _format = "format"
case _startDate = "startDate"
case _groupStageLoserBracket = "groupStageLoserBracket"
case _loserBracketMode = "loserBracketMode"
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId()
self.tournament = try container.decodeIfPresent(String.self, forKey: ._tournament) ?? ""
self.index = try container.decodeIfPresent(Int.self, forKey: ._index) ?? 0
self.parent = try container.decodeIfPresent(String.self, forKey: ._parent) ?? nil
self.format = try container.decodeIfPresent(MatchFormat.self, forKey: ._format) ?? nil
self.startDate = try container.decodeIfPresent(Date.self, forKey: ._startDate) ?? nil
self.groupStageLoserBracket = try container.decodeIfPresent(Bool.self, forKey: ._groupStageLoserBracket) ?? false
self.loserBracketMode = try container.decodeIfPresent(LoserBracketMode.self, forKey: ._loserBracketMode) ?? .automatic
try super.init(from: decoder)
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.id, forKey: ._id)
try container.encode(self.tournament, forKey: ._tournament)
try container.encode(self.index, forKey: ._index)
try container.encode(self.parent, forKey: ._parent)
try container.encode(self.format, forKey: ._format)
try container.encode(self.startDate, forKey: ._startDate)
try container.encode(self.groupStageLoserBracket, forKey: ._groupStageLoserBracket)
try container.encode(self.loserBracketMode, forKey: ._loserBracketMode)
try super.encode(to: encoder)
}
func tournamentValue() -> Tournament? {
return Store.main.findById(tournament)
}
func copy(from other: any Storable) {
guard let round = other as? BaseRound else { return }
self.id = round.id
self.tournament = round.tournament
self.index = round.index
self.parent = round.parent
self.format = round.format
self.startDate = round.startDate
self.groupStageLoserBracket = round.groupStageLoserBracket
self.loserBracketMode = round.loserBracketMode
}
static func relationships() -> [Relationship] {
return [
Relationship(type: Tournament.self, keyPath: \BaseRound.tournament),
]
}
}

@ -0,0 +1,195 @@
// Generated by SwiftModelGenerator
// Do not modify this file manually
import Foundation
import LeStorage
import SwiftUI
@Observable
class BaseTeamRegistration: SyncedModelObject, SyncedStorable {
static func resourceName() -> String { return "team-registrations" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
var id: String = Store.randomId()
var tournament: String = ""
var groupStage: String? = nil
var registrationDate: Date? = nil
var callDate: Date? = nil
var bracketPosition: Int? = nil
var groupStagePosition: Int? = nil
var comment: String? = nil
var source: String? = nil
var sourceValue: String? = nil
var logo: String? = nil
var name: String? = nil
var walkOut: Bool = false
var wildCardBracket: Bool = false
var wildCardGroupStage: Bool = false
var weight: Int = 0
var lockedWeight: Int? = nil
var confirmationDate: Date? = nil
var qualified: Bool = false
var finalRanking: Int? = nil
var pointsEarned: Int? = nil
init(
id: String = Store.randomId(),
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,
finalRanking: Int? = nil,
pointsEarned: Int? = nil
) {
super.init()
self.id = id
self.tournament = tournament
self.groupStage = groupStage
self.registrationDate = registrationDate
self.callDate = callDate
self.bracketPosition = bracketPosition
self.groupStagePosition = groupStagePosition
self.comment = comment
self.source = source
self.sourceValue = sourceValue
self.logo = logo
self.name = name
self.walkOut = walkOut
self.wildCardBracket = wildCardBracket
self.wildCardGroupStage = wildCardGroupStage
self.weight = weight
self.lockedWeight = lockedWeight
self.confirmationDate = confirmationDate
self.qualified = qualified
self.finalRanking = finalRanking
self.pointsEarned = pointsEarned
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _tournament = "tournament"
case _groupStage = "groupStage"
case _registrationDate = "registrationDate"
case _callDate = "callDate"
case _bracketPosition = "bracketPosition"
case _groupStagePosition = "groupStagePosition"
case _comment = "comment"
case _source = "source"
case _sourceValue = "sourceValue"
case _logo = "logo"
case _name = "name"
case _walkOut = "walkOut"
case _wildCardBracket = "wildCardBracket"
case _wildCardGroupStage = "wildCardGroupStage"
case _weight = "weight"
case _lockedWeight = "lockedWeight"
case _confirmationDate = "confirmationDate"
case _qualified = "qualified"
case _finalRanking = "finalRanking"
case _pointsEarned = "pointsEarned"
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId()
self.tournament = try container.decodeIfPresent(String.self, forKey: ._tournament) ?? ""
self.groupStage = try container.decodeIfPresent(String.self, forKey: ._groupStage) ?? nil
self.registrationDate = try container.decodeIfPresent(Date.self, forKey: ._registrationDate) ?? nil
self.callDate = try container.decodeIfPresent(Date.self, forKey: ._callDate) ?? nil
self.bracketPosition = try container.decodeIfPresent(Int.self, forKey: ._bracketPosition) ?? nil
self.groupStagePosition = try container.decodeIfPresent(Int.self, forKey: ._groupStagePosition) ?? nil
self.comment = try container.decodeIfPresent(String.self, forKey: ._comment) ?? nil
self.source = try container.decodeIfPresent(String.self, forKey: ._source) ?? nil
self.sourceValue = try container.decodeIfPresent(String.self, forKey: ._sourceValue) ?? nil
self.logo = try container.decodeIfPresent(String.self, forKey: ._logo) ?? nil
self.name = try container.decodeIfPresent(String.self, forKey: ._name) ?? nil
self.walkOut = try container.decodeIfPresent(Bool.self, forKey: ._walkOut) ?? false
self.wildCardBracket = try container.decodeIfPresent(Bool.self, forKey: ._wildCardBracket) ?? false
self.wildCardGroupStage = try container.decodeIfPresent(Bool.self, forKey: ._wildCardGroupStage) ?? false
self.weight = try container.decodeIfPresent(Int.self, forKey: ._weight) ?? 0
self.lockedWeight = try container.decodeIfPresent(Int.self, forKey: ._lockedWeight) ?? nil
self.confirmationDate = try container.decodeIfPresent(Date.self, forKey: ._confirmationDate) ?? nil
self.qualified = try container.decodeIfPresent(Bool.self, forKey: ._qualified) ?? false
self.finalRanking = try container.decodeIfPresent(Int.self, forKey: ._finalRanking) ?? nil
self.pointsEarned = try container.decodeIfPresent(Int.self, forKey: ._pointsEarned) ?? nil
try super.init(from: decoder)
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.id, forKey: ._id)
try container.encode(self.tournament, forKey: ._tournament)
try container.encode(self.groupStage, forKey: ._groupStage)
try container.encode(self.registrationDate, forKey: ._registrationDate)
try container.encode(self.callDate, forKey: ._callDate)
try container.encode(self.bracketPosition, forKey: ._bracketPosition)
try container.encode(self.groupStagePosition, forKey: ._groupStagePosition)
try container.encode(self.comment, forKey: ._comment)
try container.encode(self.source, forKey: ._source)
try container.encode(self.sourceValue, forKey: ._sourceValue)
try container.encode(self.logo, forKey: ._logo)
try container.encode(self.name, forKey: ._name)
try container.encode(self.walkOut, forKey: ._walkOut)
try container.encode(self.wildCardBracket, forKey: ._wildCardBracket)
try container.encode(self.wildCardGroupStage, forKey: ._wildCardGroupStage)
try container.encode(self.weight, forKey: ._weight)
try container.encode(self.lockedWeight, forKey: ._lockedWeight)
try container.encode(self.confirmationDate, forKey: ._confirmationDate)
try container.encode(self.qualified, forKey: ._qualified)
try container.encode(self.finalRanking, forKey: ._finalRanking)
try container.encode(self.pointsEarned, forKey: ._pointsEarned)
try super.encode(to: encoder)
}
func groupStageValue() -> GroupStage? {
guard let groupStage = self.groupStage else { return nil }
return self.store?.findById(groupStage)
}
func copy(from other: any Storable) {
guard let teamregistration = other as? BaseTeamRegistration else { return }
self.id = teamregistration.id
self.tournament = teamregistration.tournament
self.groupStage = teamregistration.groupStage
self.registrationDate = teamregistration.registrationDate
self.callDate = teamregistration.callDate
self.bracketPosition = teamregistration.bracketPosition
self.groupStagePosition = teamregistration.groupStagePosition
self.comment = teamregistration.comment
self.source = teamregistration.source
self.sourceValue = teamregistration.sourceValue
self.logo = teamregistration.logo
self.name = teamregistration.name
self.walkOut = teamregistration.walkOut
self.wildCardBracket = teamregistration.wildCardBracket
self.wildCardGroupStage = teamregistration.wildCardGroupStage
self.weight = teamregistration.weight
self.lockedWeight = teamregistration.lockedWeight
self.confirmationDate = teamregistration.confirmationDate
self.qualified = teamregistration.qualified
self.finalRanking = teamregistration.finalRanking
self.pointsEarned = teamregistration.pointsEarned
}
static func relationships() -> [Relationship] {
return [
Relationship(type: GroupStage.self, keyPath: \BaseTeamRegistration.groupStage),
]
}
}

@ -0,0 +1,95 @@
// Generated by SwiftModelGenerator
// Do not modify this file manually
import Foundation
import LeStorage
import SwiftUI
@Observable
class BaseTeamScore: SyncedModelObject, SyncedStorable {
static func resourceName() -> String { return "team-scores" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
var id: String = Store.randomId()
var match: String = ""
var teamRegistration: String? = nil
var score: String? = nil
var walkOut: Int? = nil
var luckyLoser: Int? = nil
init(
id: String = Store.randomId(),
match: String = "",
teamRegistration: String? = nil,
score: String? = nil,
walkOut: Int? = nil,
luckyLoser: Int? = nil
) {
super.init()
self.id = id
self.match = match
self.teamRegistration = teamRegistration
self.score = score
self.walkOut = walkOut
self.luckyLoser = luckyLoser
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _match = "match"
case _teamRegistration = "teamRegistration"
case _score = "score"
case _walkOut = "walkOut"
case _luckyLoser = "luckyLoser"
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId()
self.match = try container.decodeIfPresent(String.self, forKey: ._match) ?? ""
self.teamRegistration = try container.decodeIfPresent(String.self, forKey: ._teamRegistration) ?? nil
self.score = try container.decodeIfPresent(String.self, forKey: ._score) ?? nil
self.walkOut = try container.decodeIfPresent(Int.self, forKey: ._walkOut) ?? nil
self.luckyLoser = try container.decodeIfPresent(Int.self, forKey: ._luckyLoser) ?? nil
try super.init(from: decoder)
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.id, forKey: ._id)
try container.encode(self.match, forKey: ._match)
try container.encode(self.teamRegistration, forKey: ._teamRegistration)
try container.encode(self.score, forKey: ._score)
try container.encode(self.walkOut, forKey: ._walkOut)
try container.encode(self.luckyLoser, forKey: ._luckyLoser)
try super.encode(to: encoder)
}
func matchValue() -> Match? {
return self.store?.findById(match)
}
func teamRegistrationValue() -> TeamRegistration? {
guard let teamRegistration = self.teamRegistration else { return nil }
return self.store?.findById(teamRegistration)
}
func copy(from other: any Storable) {
guard let teamscore = other as? BaseTeamScore else { return }
self.id = teamscore.id
self.match = teamscore.match
self.teamRegistration = teamscore.teamRegistration
self.score = teamscore.score
self.walkOut = teamscore.walkOut
self.luckyLoser = teamscore.luckyLoser
}
static func relationships() -> [Relationship] {
return [
Relationship(type: Match.self, keyPath: \BaseTeamScore.match),
Relationship(type: TeamRegistration.self, keyPath: \BaseTeamScore.teamRegistration),
]
}
}

@ -0,0 +1,473 @@
// Generated by SwiftModelGenerator
// Do not modify this file manually
import Foundation
import LeStorage
import SwiftUI
@Observable
class BaseTournament: SyncedModelObject, SyncedStorable {
static func resourceName() -> String { return "tournaments" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
var id: String = Store.randomId()
var event: String? = nil
var name: String? = nil
var startDate: Date = Date()
var endDate: Date? = nil
var creationDate: Date = Date()
var isPrivate: Bool = false
var groupStageFormat: MatchFormat? = nil
var roundFormat: MatchFormat? = nil
var loserRoundFormat: MatchFormat? = nil
var groupStageSortMode: GroupStageOrderingMode = GroupStageOrderingMode.snake
var groupStageCount: Int = 0
var rankSourceDate: Date? = nil
var dayDuration: Int = 0
var teamCount: Int = 0
var teamSorting: TeamSortingType = TeamSortingType.inscriptionDate
var federalCategory: TournamentCategory = TournamentCategory.men
var federalLevelCategory: TournamentLevel = TournamentLevel.unlisted
var federalAgeCategory: FederalTournamentAge = FederalTournamentAge.unlisted
var closedRegistrationDate: Date? = nil
var groupStageAdditionalQualified: Int = 0
var courtCount: Int = 2
var prioritizeClubMembers: Bool = false
var qualifiedPerGroupStage: Int = 0
var teamsPerGroupStage: Int = 0
var entryFee: Double? = nil
var payment: TournamentPayment? = nil
var additionalEstimationDuration: Int = 0
var isDeleted: Bool = false
var isCanceled: Bool = false
var publishTeams: Bool = false
var publishSummons: Bool = false
var publishGroupStages: Bool = false
var publishBrackets: Bool = false
var shouldVerifyGroupStage: Bool = false
var shouldVerifyBracket: Bool = false
var hideTeamsWeight: Bool = false
var publishTournament: Bool = false
var hidePointsEarned: Bool = false
var publishRankings: Bool = false
var loserBracketMode: LoserBracketMode = .automatic
var initialSeedRound: Int = 0
var initialSeedCount: Int = 0
var enableOnlineRegistration: Bool = false
var registrationDateLimit: Date? = nil
var openingRegistrationDate: Date? = nil
var waitingListLimit: Int? = nil
var accountIsRequired: Bool = true
var licenseIsRequired: Bool = true
var minimumPlayerPerTeam: Int = 2
var maximumPlayerPerTeam: Int = 2
var information: String? = nil
init(
id: String = Store.randomId(),
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 = GroupStageOrderingMode.snake,
groupStageCount: Int = 0,
rankSourceDate: Date? = nil,
dayDuration: Int = 0,
teamCount: Int = 0,
teamSorting: TeamSortingType = TeamSortingType.inscriptionDate,
federalCategory: TournamentCategory = TournamentCategory.men,
federalLevelCategory: TournamentLevel = TournamentLevel.unlisted,
federalAgeCategory: FederalTournamentAge = FederalTournamentAge.unlisted,
closedRegistrationDate: Date? = nil,
groupStageAdditionalQualified: Int = 0,
courtCount: Int = 2,
prioritizeClubMembers: Bool = false,
qualifiedPerGroupStage: Int = 0,
teamsPerGroupStage: Int = 0,
entryFee: Double? = nil,
payment: TournamentPayment? = nil,
additionalEstimationDuration: Int = 0,
isDeleted: Bool = false,
isCanceled: Bool = false,
publishTeams: Bool = false,
publishSummons: Bool = false,
publishGroupStages: Bool = false,
publishBrackets: Bool = false,
shouldVerifyGroupStage: Bool = false,
shouldVerifyBracket: Bool = false,
hideTeamsWeight: Bool = false,
publishTournament: Bool = false,
hidePointsEarned: Bool = false,
publishRankings: Bool = false,
loserBracketMode: LoserBracketMode = .automatic,
initialSeedRound: Int = 0,
initialSeedCount: Int = 0,
enableOnlineRegistration: Bool = false,
registrationDateLimit: Date? = nil,
openingRegistrationDate: Date? = nil,
waitingListLimit: Int? = nil,
accountIsRequired: Bool = true,
licenseIsRequired: Bool = true,
minimumPlayerPerTeam: Int = 2,
maximumPlayerPerTeam: Int = 2,
information: String? = nil
) {
super.init()
self.id = id
self.event = event
self.name = name
self.startDate = startDate
self.endDate = endDate
self.creationDate = creationDate
self.isPrivate = isPrivate
self.groupStageFormat = groupStageFormat
self.roundFormat = roundFormat
self.loserRoundFormat = loserRoundFormat
self.groupStageSortMode = groupStageSortMode
self.groupStageCount = groupStageCount
self.rankSourceDate = rankSourceDate
self.dayDuration = dayDuration
self.teamCount = teamCount
self.teamSorting = teamSorting
self.federalCategory = federalCategory
self.federalLevelCategory = federalLevelCategory
self.federalAgeCategory = federalAgeCategory
self.closedRegistrationDate = closedRegistrationDate
self.groupStageAdditionalQualified = groupStageAdditionalQualified
self.courtCount = courtCount
self.prioritizeClubMembers = prioritizeClubMembers
self.qualifiedPerGroupStage = qualifiedPerGroupStage
self.teamsPerGroupStage = teamsPerGroupStage
self.entryFee = entryFee
self.payment = payment
self.additionalEstimationDuration = additionalEstimationDuration
self.isDeleted = isDeleted
self.isCanceled = isCanceled
self.publishTeams = publishTeams
self.publishSummons = publishSummons
self.publishGroupStages = publishGroupStages
self.publishBrackets = publishBrackets
self.shouldVerifyGroupStage = shouldVerifyGroupStage
self.shouldVerifyBracket = shouldVerifyBracket
self.hideTeamsWeight = hideTeamsWeight
self.publishTournament = publishTournament
self.hidePointsEarned = hidePointsEarned
self.publishRankings = publishRankings
self.loserBracketMode = loserBracketMode
self.initialSeedRound = initialSeedRound
self.initialSeedCount = initialSeedCount
self.enableOnlineRegistration = enableOnlineRegistration
self.registrationDateLimit = registrationDateLimit
self.openingRegistrationDate = openingRegistrationDate
self.waitingListLimit = waitingListLimit
self.accountIsRequired = accountIsRequired
self.licenseIsRequired = licenseIsRequired
self.minimumPlayerPerTeam = minimumPlayerPerTeam
self.maximumPlayerPerTeam = maximumPlayerPerTeam
self.information = information
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _event = "event"
case _name = "name"
case _startDate = "startDate"
case _endDate = "endDate"
case _creationDate = "creationDate"
case _isPrivate = "isPrivate"
case _groupStageFormat = "groupStageFormat"
case _roundFormat = "roundFormat"
case _loserRoundFormat = "loserRoundFormat"
case _groupStageSortMode = "groupStageSortMode"
case _groupStageCount = "groupStageCount"
case _rankSourceDate = "rankSourceDate"
case _dayDuration = "dayDuration"
case _teamCount = "teamCount"
case _teamSorting = "teamSorting"
case _federalCategory = "federalCategory"
case _federalLevelCategory = "federalLevelCategory"
case _federalAgeCategory = "federalAgeCategory"
case _closedRegistrationDate = "closedRegistrationDate"
case _groupStageAdditionalQualified = "groupStageAdditionalQualified"
case _courtCount = "courtCount"
case _prioritizeClubMembers = "prioritizeClubMembers"
case _qualifiedPerGroupStage = "qualifiedPerGroupStage"
case _teamsPerGroupStage = "teamsPerGroupStage"
case _entryFee = "entryFee"
case _payment = "payment"
case _additionalEstimationDuration = "additionalEstimationDuration"
case _isDeleted = "isDeleted"
case _isCanceled = "isCanceled"
case _publishTeams = "publishTeams"
case _publishSummons = "publishSummons"
case _publishGroupStages = "publishGroupStages"
case _publishBrackets = "publishBrackets"
case _shouldVerifyGroupStage = "shouldVerifyGroupStage"
case _shouldVerifyBracket = "shouldVerifyBracket"
case _hideTeamsWeight = "hideTeamsWeight"
case _publishTournament = "publishTournament"
case _hidePointsEarned = "hidePointsEarned"
case _publishRankings = "publishRankings"
case _loserBracketMode = "loserBracketMode"
case _initialSeedRound = "initialSeedRound"
case _initialSeedCount = "initialSeedCount"
case _enableOnlineRegistration = "enableOnlineRegistration"
case _registrationDateLimit = "registrationDateLimit"
case _openingRegistrationDate = "openingRegistrationDate"
case _waitingListLimit = "waitingListLimit"
case _accountIsRequired = "accountIsRequired"
case _licenseIsRequired = "licenseIsRequired"
case _minimumPlayerPerTeam = "minimumPlayerPerTeam"
case _maximumPlayerPerTeam = "maximumPlayerPerTeam"
case _information = "information"
}
private static func _decodePayment(container: KeyedDecodingContainer<CodingKeys>) throws -> TournamentPayment? {
let data = try container.decodeIfPresent(Data.self, forKey: ._payment)
if let data {
do {
let decoded: String = try data.decryptData(pass: CryptoKey.pass.rawValue)
let sequence = decoded.compactMap { NumberFormatter.standard.number(from: String($0))?.intValue }
return TournamentPayment(rawValue: sequence[18])
} catch {
Logger.error(error)
}
}
return nil
}
private func _encodePayment(container: inout KeyedEncodingContainer<CodingKeys>) throws {
guard let payment else {
try container.encodeNil(forKey: ._payment)
return
}
let max: Int = TournamentPayment.allCases.count
var sequence = (1...18).map { _ in Int.random(in: (0..<max)) }
sequence.append(payment.rawValue)
sequence.append(contentsOf: (1...13).map { _ in Int.random(in: (0..<max ))} )
let stringCombo: [String] = sequence.map { $0.formatted() }
let joined: String = stringCombo.joined(separator: "")
if let data = joined.data(using: .utf8) {
let encryped: Data = try data.encrypt(pass: CryptoKey.pass.rawValue)
try container.encodeIfPresent(encryped, forKey: ._payment)
}
}
private static func _decodeIscanceled(container: KeyedDecodingContainer<CodingKeys>) throws -> Bool {
let data = try container.decodeIfPresent(Data.self, forKey: ._isCanceled)
if let data {
do {
let decoded: String = try data.decryptData(pass: CryptoKey.pass.rawValue)
let sequence = decoded.compactMap { NumberFormatter.standard.number(from: String($0))?.intValue }
return Bool.decodeInt(sequence[18])
} catch {
Logger.error(error)
}
}
return false
}
private func _encodeIscanceled(container: inout KeyedEncodingContainer<CodingKeys>) throws {
let max: Int = 9
var sequence = (1...18).map { _ in Int.random(in: (0...max)) }
sequence.append(self.isCanceled.encodedValue)
sequence.append(contentsOf: (1...13).map { _ in Int.random(in: (0...max ))} )
let stringCombo: [String] = sequence.map { $0.formatted() }
let joined: String = stringCombo.joined(separator: "")
if let data = joined.data(using: .utf8) {
let encryped: Data = try data.encrypt(pass: CryptoKey.pass.rawValue)
try container.encode(encryped, forKey: ._isCanceled)
}
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId()
self.event = try container.decodeIfPresent(String.self, forKey: ._event) ?? nil
self.name = try container.decodeIfPresent(String.self, forKey: ._name) ?? nil
self.startDate = try container.decodeIfPresent(Date.self, forKey: ._startDate) ?? Date()
self.endDate = try container.decodeIfPresent(Date.self, forKey: ._endDate) ?? nil
self.creationDate = try container.decodeIfPresent(Date.self, forKey: ._creationDate) ?? Date()
self.isPrivate = try container.decodeIfPresent(Bool.self, forKey: ._isPrivate) ?? false
self.groupStageFormat = try container.decodeIfPresent(MatchFormat.self, forKey: ._groupStageFormat) ?? nil
self.roundFormat = try container.decodeIfPresent(MatchFormat.self, forKey: ._roundFormat) ?? nil
self.loserRoundFormat = try container.decodeIfPresent(MatchFormat.self, forKey: ._loserRoundFormat) ?? nil
self.groupStageSortMode = try container.decodeIfPresent(GroupStageOrderingMode.self, forKey: ._groupStageSortMode) ?? GroupStageOrderingMode.snake
self.groupStageCount = try container.decodeIfPresent(Int.self, forKey: ._groupStageCount) ?? 0
self.rankSourceDate = try container.decodeIfPresent(Date.self, forKey: ._rankSourceDate) ?? nil
self.dayDuration = try container.decodeIfPresent(Int.self, forKey: ._dayDuration) ?? 0
self.teamCount = try container.decodeIfPresent(Int.self, forKey: ._teamCount) ?? 0
self.teamSorting = try container.decodeIfPresent(TeamSortingType.self, forKey: ._teamSorting) ?? TeamSortingType.inscriptionDate
self.federalCategory = try container.decodeIfPresent(TournamentCategory.self, forKey: ._federalCategory) ?? TournamentCategory.men
self.federalLevelCategory = try container.decodeIfPresent(TournamentLevel.self, forKey: ._federalLevelCategory) ?? TournamentLevel.unlisted
self.federalAgeCategory = try container.decodeIfPresent(FederalTournamentAge.self, forKey: ._federalAgeCategory) ?? FederalTournamentAge.unlisted
self.closedRegistrationDate = try container.decodeIfPresent(Date.self, forKey: ._closedRegistrationDate) ?? nil
self.groupStageAdditionalQualified = try container.decodeIfPresent(Int.self, forKey: ._groupStageAdditionalQualified) ?? 0
self.courtCount = try container.decodeIfPresent(Int.self, forKey: ._courtCount) ?? 2
self.prioritizeClubMembers = try container.decodeIfPresent(Bool.self, forKey: ._prioritizeClubMembers) ?? false
self.qualifiedPerGroupStage = try container.decodeIfPresent(Int.self, forKey: ._qualifiedPerGroupStage) ?? 0
self.teamsPerGroupStage = try container.decodeIfPresent(Int.self, forKey: ._teamsPerGroupStage) ?? 0
self.entryFee = try container.decodeIfPresent(Double.self, forKey: ._entryFee) ?? nil
self.payment = try Self._decodePayment(container: container)
self.additionalEstimationDuration = try container.decodeIfPresent(Int.self, forKey: ._additionalEstimationDuration) ?? 0
self.isDeleted = try container.decodeIfPresent(Bool.self, forKey: ._isDeleted) ?? false
self.isCanceled = try Self._decodeIscanceled(container: container)
self.publishTeams = try container.decodeIfPresent(Bool.self, forKey: ._publishTeams) ?? false
self.publishSummons = try container.decodeIfPresent(Bool.self, forKey: ._publishSummons) ?? false
self.publishGroupStages = try container.decodeIfPresent(Bool.self, forKey: ._publishGroupStages) ?? false
self.publishBrackets = try container.decodeIfPresent(Bool.self, forKey: ._publishBrackets) ?? false
self.shouldVerifyGroupStage = try container.decodeIfPresent(Bool.self, forKey: ._shouldVerifyGroupStage) ?? false
self.shouldVerifyBracket = try container.decodeIfPresent(Bool.self, forKey: ._shouldVerifyBracket) ?? false
self.hideTeamsWeight = try container.decodeIfPresent(Bool.self, forKey: ._hideTeamsWeight) ?? false
self.publishTournament = try container.decodeIfPresent(Bool.self, forKey: ._publishTournament) ?? false
self.hidePointsEarned = try container.decodeIfPresent(Bool.self, forKey: ._hidePointsEarned) ?? false
self.publishRankings = try container.decodeIfPresent(Bool.self, forKey: ._publishRankings) ?? false
self.loserBracketMode = try container.decodeIfPresent(LoserBracketMode.self, forKey: ._loserBracketMode) ?? .automatic
self.initialSeedRound = try container.decodeIfPresent(Int.self, forKey: ._initialSeedRound) ?? 0
self.initialSeedCount = try container.decodeIfPresent(Int.self, forKey: ._initialSeedCount) ?? 0
self.enableOnlineRegistration = try container.decodeIfPresent(Bool.self, forKey: ._enableOnlineRegistration) ?? false
self.registrationDateLimit = try container.decodeIfPresent(Date.self, forKey: ._registrationDateLimit) ?? nil
self.openingRegistrationDate = try container.decodeIfPresent(Date.self, forKey: ._openingRegistrationDate) ?? nil
self.waitingListLimit = try container.decodeIfPresent(Int.self, forKey: ._waitingListLimit) ?? nil
self.accountIsRequired = try container.decodeIfPresent(Bool.self, forKey: ._accountIsRequired) ?? true
self.licenseIsRequired = try container.decodeIfPresent(Bool.self, forKey: ._licenseIsRequired) ?? true
self.minimumPlayerPerTeam = try container.decodeIfPresent(Int.self, forKey: ._minimumPlayerPerTeam) ?? 2
self.maximumPlayerPerTeam = try container.decodeIfPresent(Int.self, forKey: ._maximumPlayerPerTeam) ?? 2
self.information = try container.decodeIfPresent(String.self, forKey: ._information) ?? nil
try super.init(from: decoder)
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.id, forKey: ._id)
try container.encode(self.event, forKey: ._event)
try container.encode(self.name, forKey: ._name)
try container.encode(self.startDate, forKey: ._startDate)
try container.encode(self.endDate, forKey: ._endDate)
try container.encode(self.creationDate, forKey: ._creationDate)
try container.encode(self.isPrivate, forKey: ._isPrivate)
try container.encode(self.groupStageFormat, forKey: ._groupStageFormat)
try container.encode(self.roundFormat, forKey: ._roundFormat)
try container.encode(self.loserRoundFormat, forKey: ._loserRoundFormat)
try container.encode(self.groupStageSortMode, forKey: ._groupStageSortMode)
try container.encode(self.groupStageCount, forKey: ._groupStageCount)
try container.encode(self.rankSourceDate, forKey: ._rankSourceDate)
try container.encode(self.dayDuration, forKey: ._dayDuration)
try container.encode(self.teamCount, forKey: ._teamCount)
try container.encode(self.teamSorting, forKey: ._teamSorting)
try container.encode(self.federalCategory, forKey: ._federalCategory)
try container.encode(self.federalLevelCategory, forKey: ._federalLevelCategory)
try container.encode(self.federalAgeCategory, forKey: ._federalAgeCategory)
try container.encode(self.closedRegistrationDate, forKey: ._closedRegistrationDate)
try container.encode(self.groupStageAdditionalQualified, forKey: ._groupStageAdditionalQualified)
try container.encode(self.courtCount, forKey: ._courtCount)
try container.encode(self.prioritizeClubMembers, forKey: ._prioritizeClubMembers)
try container.encode(self.qualifiedPerGroupStage, forKey: ._qualifiedPerGroupStage)
try container.encode(self.teamsPerGroupStage, forKey: ._teamsPerGroupStage)
try container.encode(self.entryFee, forKey: ._entryFee)
try _encodePayment(container: &container)
try container.encode(self.additionalEstimationDuration, forKey: ._additionalEstimationDuration)
try container.encode(self.isDeleted, forKey: ._isDeleted)
try _encodeIscanceled(container: &container)
try container.encode(self.publishTeams, forKey: ._publishTeams)
try container.encode(self.publishSummons, forKey: ._publishSummons)
try container.encode(self.publishGroupStages, forKey: ._publishGroupStages)
try container.encode(self.publishBrackets, forKey: ._publishBrackets)
try container.encode(self.shouldVerifyGroupStage, forKey: ._shouldVerifyGroupStage)
try container.encode(self.shouldVerifyBracket, forKey: ._shouldVerifyBracket)
try container.encode(self.hideTeamsWeight, forKey: ._hideTeamsWeight)
try container.encode(self.publishTournament, forKey: ._publishTournament)
try container.encode(self.hidePointsEarned, forKey: ._hidePointsEarned)
try container.encode(self.publishRankings, forKey: ._publishRankings)
try container.encode(self.loserBracketMode, forKey: ._loserBracketMode)
try container.encode(self.initialSeedRound, forKey: ._initialSeedRound)
try container.encode(self.initialSeedCount, forKey: ._initialSeedCount)
try container.encode(self.enableOnlineRegistration, forKey: ._enableOnlineRegistration)
try container.encode(self.registrationDateLimit, forKey: ._registrationDateLimit)
try container.encode(self.openingRegistrationDate, forKey: ._openingRegistrationDate)
try container.encode(self.waitingListLimit, forKey: ._waitingListLimit)
try container.encode(self.accountIsRequired, forKey: ._accountIsRequired)
try container.encode(self.licenseIsRequired, forKey: ._licenseIsRequired)
try container.encode(self.minimumPlayerPerTeam, forKey: ._minimumPlayerPerTeam)
try container.encode(self.maximumPlayerPerTeam, forKey: ._maximumPlayerPerTeam)
try container.encode(self.information, forKey: ._information)
try super.encode(to: encoder)
}
func eventValue() -> Event? {
guard let event = self.event else { return nil }
return Store.main.findById(event)
}
func copy(from other: any Storable) {
guard let tournament = other as? BaseTournament else { return }
self.id = tournament.id
self.event = tournament.event
self.name = tournament.name
self.startDate = tournament.startDate
self.endDate = tournament.endDate
self.creationDate = tournament.creationDate
self.isPrivate = tournament.isPrivate
self.groupStageFormat = tournament.groupStageFormat
self.roundFormat = tournament.roundFormat
self.loserRoundFormat = tournament.loserRoundFormat
self.groupStageSortMode = tournament.groupStageSortMode
self.groupStageCount = tournament.groupStageCount
self.rankSourceDate = tournament.rankSourceDate
self.dayDuration = tournament.dayDuration
self.teamCount = tournament.teamCount
self.teamSorting = tournament.teamSorting
self.federalCategory = tournament.federalCategory
self.federalLevelCategory = tournament.federalLevelCategory
self.federalAgeCategory = tournament.federalAgeCategory
self.closedRegistrationDate = tournament.closedRegistrationDate
self.groupStageAdditionalQualified = tournament.groupStageAdditionalQualified
self.courtCount = tournament.courtCount
self.prioritizeClubMembers = tournament.prioritizeClubMembers
self.qualifiedPerGroupStage = tournament.qualifiedPerGroupStage
self.teamsPerGroupStage = tournament.teamsPerGroupStage
self.entryFee = tournament.entryFee
self.payment = tournament.payment
self.additionalEstimationDuration = tournament.additionalEstimationDuration
self.isDeleted = tournament.isDeleted
self.isCanceled = tournament.isCanceled
self.publishTeams = tournament.publishTeams
self.publishSummons = tournament.publishSummons
self.publishGroupStages = tournament.publishGroupStages
self.publishBrackets = tournament.publishBrackets
self.shouldVerifyGroupStage = tournament.shouldVerifyGroupStage
self.shouldVerifyBracket = tournament.shouldVerifyBracket
self.hideTeamsWeight = tournament.hideTeamsWeight
self.publishTournament = tournament.publishTournament
self.hidePointsEarned = tournament.hidePointsEarned
self.publishRankings = tournament.publishRankings
self.loserBracketMode = tournament.loserBracketMode
self.initialSeedRound = tournament.initialSeedRound
self.initialSeedCount = tournament.initialSeedCount
self.enableOnlineRegistration = tournament.enableOnlineRegistration
self.registrationDateLimit = tournament.registrationDateLimit
self.openingRegistrationDate = tournament.openingRegistrationDate
self.waitingListLimit = tournament.waitingListLimit
self.accountIsRequired = tournament.accountIsRequired
self.licenseIsRequired = tournament.licenseIsRequired
self.minimumPlayerPerTeam = tournament.minimumPlayerPerTeam
self.maximumPlayerPerTeam = tournament.maximumPlayerPerTeam
self.information = tournament.information
}
static func relationships() -> [Relationship] {
return [
Relationship(type: Event.self, keyPath: \BaseTournament.event),
]
}
}

@ -0,0 +1,92 @@
{
"models": [
{
"name": "Club",
"synchronizable": true,
"observable": true,
"properties": [
{
"name": "id",
"type": "String",
"defaultValue": "Store.randomId()"
},
{
"name": "creator",
"type": "String",
"optional": true,
"defaultValue": "nil",
"foreignKey": "CustomUser"
},
{
"name": "name",
"type": "String",
"defaultValue": "\"\""
},
{
"name": "acronym",
"type": "String",
"defaultValue": "\"\""
},
{
"name": "phone",
"type": "String",
"optional": true,
"defaultValue": "nil"
},
{
"name": "code",
"type": "String",
"optional": true,
"defaultValue": "nil"
},
{
"name": "address",
"type": "String",
"optional": true,
"defaultValue": "nil"
},
{
"name": "city",
"type": "String",
"optional": true,
"defaultValue": "nil"
},
{
"name": "zipCode",
"type": "String",
"optional": true,
"defaultValue": "nil"
},
{
"name": "latitude",
"type": "Double",
"optional": true,
"defaultValue": "nil"
},
{
"name": "longitude",
"type": "Double",
"optional": true,
"defaultValue": "nil"
},
{
"name": "courtCount",
"type": "Int",
"defaultValue": "2"
},
{
"name": "broadcastCode",
"type": "String",
"optional": true,
"defaultValue": "nil"
},
{
"name": "timezone",
"type": "String",
"optional": true,
"defaultValue": "TimeZone.current.identifier"
}
]
}
]
}

@ -0,0 +1,42 @@
{
"models": [
{
"name": "Court",
"synchronizable": true,
"observable": true,
"properties": [
{
"name": "id",
"type": "String",
"defaultValue": "Store.randomId()"
},
{
"name": "index",
"type": "Int"
},
{
"name": "club",
"type": "String",
"foreignKey": "Club"
},
{
"name": "name",
"type": "String",
"optional": true,
"defaultValue": "nil"
},
{
"name": "exitAllowed",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "indoor",
"type": "Bool",
"defaultValue": "false"
}
],
"tokenExemptedMethods": []
}
]
}

@ -0,0 +1,136 @@
{
"models": [
{
"name": "CustomUser",
"resource_name": "users",
"synchronizable": true,
"observable": true,
"tokenExemptedMethods": ["post"],
"properties": [
{
"name": "id",
"type": "String",
"defaultValue": "Store.randomId()"
},
{
"name": "username",
"type": "String"
},
{
"name": "email",
"type": "String"
},
{
"name": "clubs",
"type": "[String]",
"defaultValue": "[]"
},
{
"name": "umpireCode",
"type": "String",
"optional": true,
"defaultValue": "nil"
},
{
"name": "licenceId",
"type": "String",
"optional": true,
"defaultValue": "nil"
},
{
"name": "firstName",
"type": "String"
},
{
"name": "lastName",
"type": "String"
},
{
"name": "phone",
"type": "String",
"optional": true,
"defaultValue": "nil"
},
{
"name": "country",
"type": "String",
"optional": true,
"defaultValue": "nil"
},
{
"name": "summonsMessageBody",
"type": "String",
"optional": true,
"defaultValue": "nil"
},
{
"name": "summonsMessageSignature",
"type": "String",
"optional": true,
"defaultValue": "nil"
},
{
"name": "summonsAvailablePaymentMethods",
"type": "String",
"optional": true,
"defaultValue": "nil"
},
{
"name": "summonsDisplayFormat",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "summonsDisplayEntryFee",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "summonsUseFullCustomMessage",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "matchFormatsDefaultDuration",
"type": "[MatchFormat: Int]",
"optional": true,
"defaultValue": "nil"
},
{
"name": "bracketMatchFormatPreference",
"type": "MatchFormat",
"optional": true,
"defaultValue": "nil"
},
{
"name": "groupStageMatchFormatPreference",
"type": "MatchFormat",
"optional": true,
"defaultValue": "nil"
},
{
"name": "loserBracketMatchFormatPreference",
"type": "MatchFormat",
"optional": true,
"defaultValue": "nil"
},
{
"name": "loserBracketMode",
"type": "LoserBracketMode",
"defaultValue": ".automatic"
},
{
"name": "deviceId",
"type": "String",
"optional": true,
"defaultValue": "nil"
},
{
"name": "agents",
"type": "[String]",
"defaultValue": "[]"
}
]
}
]
}

@ -0,0 +1,33 @@
{
"models": [
{
"name": "DateInterval",
"synchronizable": true,
"observable": true,
"properties": [
{
"name": "id",
"type": "String",
"defaultValue": "Store.randomId()"
},
{
"name": "event",
"type": "String"
},
{
"name": "courtIndex",
"type": "Int"
},
{
"name": "startDate",
"type": "Date"
},
{
"name": "endDate",
"type": "Date"
}
],
"tokenExemptedMethods": []
}
]
}

@ -0,0 +1,47 @@
{
"models": [
{
"name": "DrawLog",
"synchronizable": true,
"sideStorable": true,
"observable": true,
"relationshipNames": [],
"properties": [
{
"name": "id",
"type": "String",
"defaultValue": "Store.randomId()"
},
{
"name": "tournament",
"type": "String",
"foreignKey": "Tournament"
},
{
"name": "drawDate",
"type": "Date",
"defaultValue": "Date()"
},
{
"name": "drawSeed",
"type": "Int"
},
{
"name": "drawMatchIndex",
"type": "Int"
},
{
"name": "drawTeamPosition",
"type": "TeamPosition",
"defaultValue": "TeamPosition.one"
},
{
"name": "drawType",
"type": "DrawType",
"defaultValue": "DrawType.seed"
}
]
}
]
}

@ -0,0 +1,48 @@
{
"models": [
{
"name": "Event",
"synchronizable": true,
"observable": true,
"properties": [
{
"name": "id",
"type": "String",
"defaultValue": "Store.randomId()"
},
{
"name": "creator",
"type": "String",
"optional": true,
"defaultValue": "nil",
"foreignKey": "CustomUser"
},
{
"name": "club",
"type": "String",
"optional": true,
"defaultValue": "nil",
"foreignKey": "Club"
},
{
"name": "creationDate",
"type": "Date",
"defaultValue": "Date()"
},
{
"name": "name",
"type": "String",
"optional": true,
"defaultValue": "nil"
},
{
"name": "tenupId",
"type": "String",
"optional": true,
"defaultValue": "nil"
}
],
"tokenExemptedMethods": []
}
]
}

@ -0,0 +1,53 @@
{
"models": [
{
"name": "GroupStage",
"synchronizable": true,
"observable": true,
"properties": [
{
"name": "id",
"type": "String",
"defaultValue": "Store.randomId()"
},
{
"name": "tournament",
"type": "String",
"foreignKey": "Tournament"
},
{
"name": "index",
"type": "Int"
},
{
"name": "size",
"type": "Int"
},
{
"name": "format",
"type": "MatchFormat",
"optional": true,
"defaultValue": "nil"
},
{
"name": "startDate",
"type": "Date",
"optional": true,
"defaultValue": "nil"
},
{
"name": "name",
"type": "String",
"optional": true,
"defaultValue": "nil"
},
{
"name": "step",
"type": "Int",
"defaultValue": "0"
}
],
"tokenExemptedMethods": []
}
]
}

@ -0,0 +1,83 @@
{
"models": [
{
"name": "Match",
"synchronizable": true,
"observable": true,
"tokenExemptedMethods": [],
"properties": [
{
"name": "id",
"type": "String",
"defaultValue": "Store.randomId()"
},
{
"name": "round",
"type": "String",
"optional": true,
"foreignKey": "Round*"
},
{
"name": "groupStage",
"type": "String",
"optional": true,
"foreignKey": "GroupStage*"
},
{
"name": "startDate",
"type": "Date",
"optional": true
},
{
"name": "endDate",
"type": "Date",
"optional": true
},
{
"name": "index",
"type": "Int"
},
{
"name": "format",
"type": "MatchFormat",
"optional": true
},
{
"name": "servingTeamId",
"type": "String",
"optional": true
},
{
"name": "winningTeamId",
"type": "String",
"optional": true
},
{
"name": "losingTeamId",
"type": "String",
"optional": true
},
{
"name": "name",
"type": "String",
"optional": true
},
{
"name": "disabled",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "courtIndex",
"type": "Int",
"optional": true
},
{
"name": "confirmed",
"type": "Bool",
"defaultValue": "false"
}
]
}
]
}

@ -0,0 +1,84 @@
{
"models": [
{
"name": "MatchScheduler",
"resource_name": "match-scheduler",
"synchronizable": false,
"observable": true,
"tokenExemptedMethods": [],
"properties": [
{
"name": "id",
"type": "String",
"defaultValue": "Store.randomId()"
},
{
"name": "tournament",
"type": "String",
"foreignKey": "Tournament"
},
{
"name": "timeDifferenceLimit",
"type": "Int"
},
{
"name": "loserBracketRotationDifference",
"type": "Int"
},
{
"name": "upperBracketRotationDifference",
"type": "Int"
},
{
"name": "accountUpperBracketBreakTime",
"type": "Bool"
},
{
"name": "accountLoserBracketBreakTime",
"type": "Bool"
},
{
"name": "randomizeCourts",
"type": "Bool"
},
{
"name": "rotationDifferenceIsImportant",
"type": "Bool"
},
{
"name": "shouldHandleUpperRoundSlice",
"type": "Bool"
},
{
"name": "shouldEndRoundBeforeStartingNext",
"type": "Bool"
},
{
"name": "groupStageChunkCount",
"type": "Int",
"optional": true
},
{
"name": "overrideCourtsUnavailability",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "shouldTryToFillUpCourtsAvailable",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "courtsAvailable",
"type": "Set<Int>",
"defaultValue": "Set<Int>()"
},
{
"name": "simultaneousStart",
"type": "Bool",
"defaultValue": "true"
}
]
}
]
}

@ -0,0 +1,71 @@
{
"models": [
{
"name": "MonthData",
"resource_name": "month-data",
"synchronizable": false,
"observable": true,
"tokenExemptedMethods": [],
"properties": [
{
"name": "id",
"type": "String",
"defaultValue": "Store.randomId()"
},
{
"name": "monthKey",
"type": "String"
},
{
"name": "creationDate",
"type": "Date"
},
{
"name": "maleUnrankedValue",
"type": "Int",
"optional": true,
"defaultValue": "nil"
},
{
"name": "femaleUnrankedValue",
"type": "Int",
"optional": true,
"defaultValue": "nil"
},
{
"name": "maleCount",
"type": "Int",
"optional": true,
"defaultValue": "nil"
},
{
"name": "femaleCount",
"type": "Int",
"optional": true,
"defaultValue": "nil"
},
{
"name": "anonymousCount",
"type": "Int",
"optional": true,
"defaultValue": "nil"
},
{
"name": "incompleteMode",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "dataModelIdentifier",
"type": "String",
"optional": true
},
{
"name": "fileModelIdentifier",
"type": "String",
"optional": true
}
]
}
]
}

@ -0,0 +1,122 @@
{
"models": [
{
"name": "PlayerRegistration",
"synchronizable": true,
"sideStorable": true,
"observable": true,
"relationshipNames": ["teamRegistration"],
"properties": [
{
"name": "id",
"type": "String",
"defaultValue": "Store.randomId()"
},
{
"name": "teamRegistration",
"type": "String",
"optional": true,
"foreignKey": "TeamRegistration"
},
{
"name": "firstName",
"type": "String"
},
{
"name": "lastName",
"type": "String"
},
{
"name": "licenceId",
"type": "String",
"optional": true
},
{
"name": "rank",
"type": "Int",
"optional": true
},
{
"name": "paymentType",
"type": "PlayerPaymentType",
"optional": true
},
{
"name": "sex",
"type": "PlayerSexType",
"optional": true
},
{
"name": "tournamentPlayed",
"type": "Int",
"optional": true
},
{
"name": "points",
"type": "Double",
"optional": true
},
{
"name": "clubName",
"type": "String",
"optional": true
},
{
"name": "ligueName",
"type": "String",
"optional": true
},
{
"name": "assimilation",
"type": "String",
"optional": true
},
{
"name": "phoneNumber",
"type": "String",
"optional": true
},
{
"name": "email",
"type": "String",
"optional": true
},
{
"name": "birthdate",
"type": "String",
"optional": true
},
{
"name": "computedRank",
"type": "Int",
"defaultValue": "0"
},
{
"name": "source",
"type": "PlayerRegistration.PlayerDataSource",
"optional": true
},
{
"name": "hasArrived",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "coach",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "captain",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "registeredOnline",
"type": "Bool",
"defaultValue": "false"
}
]
}
]
}

@ -0,0 +1,51 @@
{
"models": [
{
"name": "Purchase",
"synchronizable": true,
"properties": [
{
"name": "id",
"type": "UInt64",
"defaultValue": "0"
},
{
"name": "user",
"type": "String",
"defaultValue": "\"\"",
"encryption": "standard",
"foreignKey": "CustomUser"
},
{
"name": "purchaseDate",
"type": "Date"
},
{
"name": "productId",
"type": "String",
"defaultValue": "\"\""
},
{
"name": "quantity",
"type": "Int",
"optional": true,
"defaultValue": "nil"
},
{
"name": "revocationDate",
"type": "Date",
"optional": true,
"defaultValue": "nil"
},
{
"name": "expirationDate",
"type": "Date",
"optional": true,
"defaultValue": "nil"
}
],
"tokenExemptedMethods": [],
"relationshipNames": []
}
]
}

@ -0,0 +1,53 @@
{
"models": [
{
"name": "Round",
"synchronizable": true,
"sideStorable": true,
"observable": true,
"relationshipNames": [],
"properties": [
{
"name": "id",
"type": "String",
"defaultValue": "Store.randomId()"
},
{
"name": "tournament",
"type": "String",
"foreignKey": "Tournament"
},
{
"name": "index",
"type": "Int"
},
{
"name": "parent",
"type": "String",
"optional": true
},
{
"name": "format",
"type": "MatchFormat",
"optional": true,
"private": true
},
{
"name": "startDate",
"type": "Date",
"optional": true
},
{
"name": "groupStageLoserBracket",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "loserBracketMode",
"type": "LoserBracketMode",
"defaultValue": ".automatic"
}
]
}
]
}

@ -0,0 +1,118 @@
{
"models": [
{
"name": "TeamRegistration",
"synchronizable": true,
"sideStorable": true,
"observable": true,
"relationshipNames": [],
"properties": [
{
"name": "id",
"type": "String",
"defaultValue": "Store.randomId()"
},
{
"name": "tournament",
"type": "String"
},
{
"name": "groupStage",
"type": "String",
"optional": true,
"foreignKey": "GroupStage*"
},
{
"name": "registrationDate",
"type": "Date",
"optional": true
},
{
"name": "callDate",
"type": "Date",
"optional": true
},
{
"name": "bracketPosition",
"type": "Int",
"optional": true
},
{
"name": "groupStagePosition",
"type": "Int",
"optional": true
},
{
"name": "comment",
"type": "String",
"optional": true
},
{
"name": "source",
"type": "String",
"optional": true
},
{
"name": "sourceValue",
"type": "String",
"optional": true
},
{
"name": "logo",
"type": "String",
"optional": true
},
{
"name": "name",
"type": "String",
"optional": true
},
{
"name": "walkOut",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "wildCardBracket",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "wildCardGroupStage",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "weight",
"type": "Int",
"defaultValue": "0"
},
{
"name": "lockedWeight",
"type": "Int",
"optional": true
},
{
"name": "confirmationDate",
"type": "Date",
"optional": true
},
{
"name": "qualified",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "finalRanking",
"type": "Int",
"optional": true
},
{
"name": "pointsEarned",
"type": "Int",
"optional": true
}
]
}
]
}

@ -0,0 +1,44 @@
{
"models": [
{
"name": "TeamScore",
"synchronizable": true,
"sideStorable": true,
"observable": true,
"relationshipNames": ["match"],
"properties": [
{
"name": "id",
"type": "String",
"defaultValue": "Store.randomId()"
},
{
"name": "match",
"type": "String",
"foreignKey": "Match*"
},
{
"name": "teamRegistration",
"type": "String",
"optional": true,
"foreignKey": "TeamRegistration*"
},
{
"name": "score",
"type": "String",
"optional": true
},
{
"name": "walkOut",
"type": "Int",
"optional": true
},
{
"name": "luckyLoser",
"type": "Int",
"optional": true
}
]
}
]
}

@ -0,0 +1,271 @@
{
"models": [
{
"name": "Tournament",
"synchronizable": true,
"copyable": true,
"observable": true,
"relationshipNames": [],
"properties": [
{
"name": "id",
"type": "String",
"defaultValue": "Store.randomId()"
},
{
"name": "event",
"type": "String",
"optional": true,
"foreignKey": "Event"
},
{
"name": "name",
"type": "String",
"optional": true
},
{
"name": "startDate",
"type": "Date"
},
{
"name": "endDate",
"type": "Date",
"optional": true
},
{
"name": "creationDate",
"type": "Date",
"private": true
},
{
"name": "isPrivate",
"type": "Bool"
},
{
"name": "groupStageFormat",
"type": "MatchFormat",
"optional": true,
"private": true
},
{
"name": "roundFormat",
"type": "MatchFormat",
"optional": true,
"private": true
},
{
"name": "loserRoundFormat",
"type": "MatchFormat",
"optional": true,
"private": true
},
{
"name": "groupStageSortMode",
"type": "GroupStageOrderingMode",
"defaultValue": "GroupStageOrderingMode.snake"
},
{
"name": "groupStageCount",
"type": "Int"
},
{
"name": "rankSourceDate",
"type": "Date",
"optional": true
},
{
"name": "dayDuration",
"type": "Int"
},
{
"name": "teamCount",
"type": "Int"
},
{
"name": "teamSorting",
"type": "TeamSortingType",
"defaultValue": "TeamSortingType.inscriptionDate"
},
{
"name": "federalCategory",
"type": "TournamentCategory",
"defaultValue": "TournamentCategory.men"
},
{
"name": "federalLevelCategory",
"type": "TournamentLevel",
"defaultValue": "TournamentLevel.unlisted"
},
{
"name": "federalAgeCategory",
"type": "FederalTournamentAge",
"defaultValue": "FederalTournamentAge.unlisted"
},
{
"name": "closedRegistrationDate",
"type": "Date",
"optional": true
},
{
"name": "groupStageAdditionalQualified",
"type": "Int"
},
{
"name": "courtCount",
"type": "Int",
"defaultValue": "2"
},
{
"name": "prioritizeClubMembers",
"type": "Bool"
},
{
"name": "qualifiedPerGroupStage",
"type": "Int"
},
{
"name": "teamsPerGroupStage",
"type": "Int"
},
{
"name": "entryFee",
"type": "Double",
"optional": true
},
{
"name": "payment",
"type": "TournamentPayment",
"optional": true,
"defaultValue": "nil",
"encryption": "tournament_payment"
},
{
"name": "additionalEstimationDuration",
"type": "Int",
"defaultValue": "0"
},
{
"name": "isDeleted",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "isCanceled",
"type": "Bool",
"defaultValue": "false",
"encryption": "tournament_iscanceled"
},
{
"name": "publishTeams",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "publishSummons",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "publishGroupStages",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "publishBrackets",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "shouldVerifyGroupStage",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "shouldVerifyBracket",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "hideTeamsWeight",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "publishTournament",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "hidePointsEarned",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "publishRankings",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "loserBracketMode",
"type": "LoserBracketMode",
"defaultValue": ".automatic"
},
{
"name": "initialSeedRound",
"type": "Int",
"defaultValue": "0"
},
{
"name": "initialSeedCount",
"type": "Int",
"defaultValue": "0"
},
{
"name": "enableOnlineRegistration",
"type": "Bool",
"defaultValue": "false"
},
{
"name": "registrationDateLimit",
"type": "Date",
"optional": true
},
{
"name": "openingRegistrationDate",
"type": "Date",
"optional": true
},
{
"name": "waitingListLimit",
"type": "Int",
"optional": true
},
{
"name": "accountIsRequired",
"type": "Bool",
"defaultValue": "true"
},
{
"name": "licenseIsRequired",
"type": "Bool",
"defaultValue": "true"
},
{
"name": "minimumPlayerPerTeam",
"type": "Int",
"defaultValue": 2
},
{
"name": "maximumPlayerPerTeam",
"type": "Int",
"defaultValue": 2
},
{
"name": "information",
"type": "String",
"optional": true
}
]
}
]
}

@ -0,0 +1,566 @@
import json
import re
import os
from pathlib import Path
from typing import Dict, List, Any
import argparse
import sys
import logging
from datetime import datetime
import inflect
class SwiftModelGenerator:
def __init__(self, json_data: Dict[str, Any]):
self.data = json_data
self.pluralizer = inflect.engine()
def generate_model(self, model_data: Dict[str, Any]) -> str:
model_name = model_data["name"]
is_sync = model_data.get("synchronizable", False)
is_observable = model_data.get("observable", False)
properties = model_data["properties"]
# Get protocol specific configurations
resource = self.make_resource_name(model_name)
resource_name = model_data.get("resource_name", resource)
token_exempted = model_data.get("tokenExemptedMethods", [])
lines = ["// Generated by SwiftModelGenerator", "// Do not modify this file manually", ""]
# Import statement
lines.append("import Foundation")
lines.append("import LeStorage")
if is_observable:
lines.append("import SwiftUI")
lines.append("")
# Class declaration
if is_observable:
lines.append("@Observable")
protocol = "SyncedStorable" if is_sync else "Storable"
base_class = "SyncedModelObject" if is_sync else "BaseModelObject"
lines.append(f"class Base{model_name}: {base_class}, {protocol} {{")
lines.append("")
# Add SyncedStorable protocol requirements
lines.extend(self._generate_protocol_requirements(resource_name, token_exempted))
lines.append("")
# Properties
for prop in properties:
swift_type = prop["type"]
if prop.get("optional", False):
swift_type += "?"
default_value = prop.get("defaultValue", "nil" if prop.get("optional", False) else self._get_default_value(swift_type))
lines.append(f" var {prop['name']}: {swift_type} = {default_value}")
lines.append("")
# Add constructor
lines.extend(self._generate_constructor(model_name, properties))
lines.append("")
# CodingKeys
lines.extend(self._generate_coding_keys(properties, is_observable, is_sync))
lines.append("")
# Encryption methods
encrypted_props = [p for p in properties if "encryption" in p]
if encrypted_props:
lines.extend(self._generate_encryption_methods(properties))
lines.append("")
# Codable implementation
lines.extend(self._generate_decoder(model_name, properties, is_observable, is_sync))
lines.append("")
lines.extend(self._generate_encoder(properties, is_observable, is_sync))
lines.append("")
# Foreign Key convenience
foreign_key_methods = self._generate_foreign_key_methods(properties)
if foreign_key_methods:
lines.extend(foreign_key_methods)
# lines.append("")
# Copy method
lines.extend(self._generate_copy_method(model_name, properties))
lines.append("")
# Add relationships function
lines.extend(self._generate_relationships(model_name, properties))
lines.append("")
lines.append("}")
return "\n".join(lines)
def _generate_constructor(self, model_name: str, properties: List[Dict[str, Any]]) -> List[str]:
"""Generate a constructor with all properties as parameters with default values."""
lines = [" init("]
# Generate parameter list
params = []
for prop in properties:
name = prop['name']
type_name = prop['type']
is_optional = prop.get("optional", False)
# Always include a default value
default_value = prop.get("defaultValue")
if default_value is None:
if is_optional:
default_value = "nil"
else:
default_value = self._get_default_value(type_name)
# Format the parameter with its type and default value
param = f" {name}: {type_name}"
if is_optional:
param += "?"
param += f" = {default_value}"
params.append(param)
# Join parameters with commas
lines.extend([f"{param}," for param in params[:-1]])
lines.append(f"{params[-1]}") # Last parameter without comma
# Constructor body
lines.extend([
" ) {",
" super.init()",
])
# Property assignments
for prop in properties:
name = prop['name']
lines.append(f" self.{name} = {name}")
lines.append(" }")
return lines
def _generate_foreign_key_methods(self, properties: List[Dict[str, Any]]) -> List[str]:
lines = []
for prop in properties:
if "foreignKey" in prop:
foreign_key = prop["foreignKey"]
prop_name = prop["name"]
method_name = f"{prop_name}Value"
is_optional = prop.get("optional", False)
lines.extend([f" func {method_name}() -> {foreign_key.rstrip('*')}? {{"])
if is_optional:
lines.extend([
f" guard let {prop_name} = self.{prop_name} else {{ return nil }}"
])
if foreign_key.endswith("*"):
foreign_key = foreign_key[:-1] # Remove the asterisk
lines.extend([
f" return self.store?.findById({prop_name})"
])
else:
lines.extend([
f" return Store.main.findById({prop_name})"
])
lines.extend([" }", ""]) # Close the method and add a blank line
return lines
def _generate_coding_keys(self, properties: List[Dict[str, Any]], is_observable: bool, is_sync: bool = False) -> List[str]:
lines = [" enum CodingKeys: String, CodingKey {"]
for prop in properties:
name = prop['name']
# Add underscore prefix to case name if observable, but keep the string value without underscore
case_name = f"_{name}" if is_observable else name
lines.append(f" case {case_name} = \"{name}\"")
lines.append(" }")
return lines
def _generate_encryption_methods(self, properties: List[Dict[str, Any]]) -> List[str]:
lines = []
for prop in properties:
if "encryption" in prop:
name = prop['name']
enc_type = prop['encryption']
if enc_type == "tournament_payment":
lines.extend([
f" private static func _decode{name.capitalize()}(container: KeyedDecodingContainer<CodingKeys>) throws -> TournamentPayment? {{",
f" let data = try container.decodeIfPresent(Data.self, forKey: ._{name})",
" ",
" if let data {",
" do {",
" let decoded: String = try data.decryptData(pass: CryptoKey.pass.rawValue)",
" let sequence = decoded.compactMap { NumberFormatter.standard.number(from: String($0))?.intValue }",
" return TournamentPayment(rawValue: sequence[18])",
" } catch {",
" Logger.error(error)",
" }",
" }",
" return nil",
" }",
"",
f" private func _encode{name.capitalize()}(container: inout KeyedEncodingContainer<CodingKeys>) throws {{",
f" guard let {name} else {{",
f" try container.encodeNil(forKey: ._{name})",
" return",
" }",
" ",
" let max: Int = TournamentPayment.allCases.count",
" var sequence = (1...18).map { _ in Int.random(in: (0..<max)) }",
f" sequence.append({name}.rawValue)",
" sequence.append(contentsOf: (1...13).map { _ in Int.random(in: (0..<max ))} )",
"",
" let stringCombo: [String] = sequence.map { $0.formatted() }",
" let joined: String = stringCombo.joined(separator: \"\")",
" if let data = joined.data(using: .utf8) {",
" let encryped: Data = try data.encrypt(pass: CryptoKey.pass.rawValue)",
f" try container.encodeIfPresent(encryped, forKey: ._{name})",
" }",
" }"
])
elif enc_type == "tournament_iscanceled":
lines.extend([
f" private static func _decode{name.capitalize()}(container: KeyedDecodingContainer<CodingKeys>) throws -> Bool {{",
f" let data = try container.decodeIfPresent(Data.self, forKey: ._{name})",
" if let data {",
" do {",
" let decoded: String = try data.decryptData(pass: CryptoKey.pass.rawValue)",
" let sequence = decoded.compactMap { NumberFormatter.standard.number(from: String($0))?.intValue }",
" return Bool.decodeInt(sequence[18])",
" } catch {",
" Logger.error(error)",
" }",
" }",
" return false",
" }",
"",
f" private func _encode{name.capitalize()}(container: inout KeyedEncodingContainer<CodingKeys>) throws {{",
" let max: Int = 9",
" var sequence = (1...18).map { _ in Int.random(in: (0...max)) }",
f" sequence.append(self.{name}.encodedValue)",
" sequence.append(contentsOf: (1...13).map { _ in Int.random(in: (0...max ))} )",
" ",
" let stringCombo: [String] = sequence.map { $0.formatted() }",
" let joined: String = stringCombo.joined(separator: \"\")",
" if let data = joined.data(using: .utf8) {",
" let encryped: Data = try data.encrypt(pass: CryptoKey.pass.rawValue)",
f" try container.encode(encryped, forKey: ._{name})",
" }",
" }"
])
return lines
def _generate_decoder(self, model_name: str, properties: List[Dict[str, Any]], is_observable: bool, is_sync: bool = False) -> List[str]:
lines = [" required init(from decoder: Decoder) throws {",
" let container = try decoder.container(keyedBy: CodingKeys.self)"]
for prop in properties:
name = prop['name']
type_name = prop['type']
is_optional = prop.get("optional", False)
default_value = prop.get("defaultValue", "nil" if is_optional else self._get_default_value(type_name))
option = prop.get("option")
# Use the correct case reference based on observable flag
case_ref = f"_{name}" if is_observable else name
if "encryption" in prop:
enc_type = prop['encryption']
if enc_type == "standard":
if is_optional:
lines.append(f" self.{name} = try container.decodeEncryptedIfPresent(key: .{case_ref})")
else:
lines.append(f" self.{name} = try container.decodeEncrypted(key: .{case_ref})")
elif enc_type in ["tournament_payment", "tournament_iscanceled"]:
lines.append(f" self.{name} = try Self._decode{name.capitalize()}(container: container)")
else:
# Handle Date with fractional option
if type_name == "Date" and option == "fractional":
if is_optional:
lines.append(f" if let dateString = try container.decodeIfPresent(String.self, forKey: .{case_ref}) {{")
lines.append(f" self.{name} = Date.iso8601FractionalFormatter.date(from: dateString)")
lines.append(" } else {")
lines.append(f" self.{name} = {default_value}")
lines.append(" }")
else:
lines.append(f" let dateString = try container.decode(String.self, forKey: .{case_ref})")
lines.append(f" self.{name} = Date.iso8601FractionalFormatter.date(from: dateString) ?? {default_value}")
else:
lines.append(f" self.{name} = try container.decodeIfPresent({type_name}.self, forKey: .{case_ref}) ?? {default_value}")
lines.append(" try super.init(from: decoder)")
lines.append(" }")
return lines
def _generate_encoder(self, properties: List[Dict[str, Any]], is_observable: bool, is_sync: bool = False) -> List[str]:
lines = [" override func encode(to encoder: Encoder) throws {",
" var container = encoder.container(keyedBy: CodingKeys.self)"]
for prop in properties:
name = prop['name']
type_name = prop['type']
is_optional = prop.get('optional', False)
option = prop.get("option")
# Use the correct case reference based on observable flag
case_ref = f"_{name}" if is_observable else name
if "encryption" in prop:
enc_type = prop['encryption']
if enc_type == "standard":
if is_optional:
lines.append(f" try container.encodeAndEncryptIfPresent(self.{name}?.data(using: .utf8), forKey: .{case_ref})")
else:
lines.append(f" try container.encodeAndEncryptIfPresent(self.{name}.data(using: .utf8), forKey: .{case_ref})")
elif enc_type in ["tournament_payment", "tournament_iscanceled"]:
lines.append(f" try _encode{name.capitalize()}(container: &container)")
else:
if type_name == "Date" and option == "fractional":
if is_optional:
lines.append(f" if let date = self.{name} {{")
lines.append(f" try container.encode(Date.iso8601FractionalFormatter.string(from: date), forKey: .{case_ref})")
lines.append(" } else {")
lines.append(f" try container.encodeNil(forKey: .{case_ref})")
lines.append(" }")
else:
lines.append(f" try container.encode(Date.iso8601FractionalFormatter.string(from: self.{name}), forKey: .{case_ref})")
else:
lines.append(f" try container.encode(self.{name}, forKey: .{case_ref})")
lines.append(" try super.encode(to: encoder)")
lines.append(" }")
return lines
def _generate_copy_method(self, model_name: str, properties: List[Dict[str, Any]]) -> List[str]:
model_variable = model_name.lower()
lines = [f" func copy(from other: any Storable) {{"]
lines.append(f" guard let {model_variable} = other as? Base{model_name} else {{ return }}")
for prop in properties:
name = prop['name']
lines.append(f" self.{name} = {model_variable}.{name}")
lines.append(" }")
return lines
def _generate_protocol_requirements(self, resource_name: str, token_exempted: List[str]) -> List[str]:
"""Generate the static functions required by SyncedStorable protocol."""
# Convert HTTP methods to proper format
formatted_methods = [f".{method.lower()}" for method in token_exempted]
methods_str = ", ".join(formatted_methods) if formatted_methods else ""
return [
f" static func resourceName() -> String {{ return \"{resource_name}\" }}",
f" static func tokenExemptedMethods() -> [HTTPMethod] {{ return [{methods_str}] }}",
]
def _generate_relationships(self, model_name, properties: List[Dict[str, Any]]) -> List[str]:
# Find all properties with foreign keys
foreign_key_props = [p for p in properties if "foreignKey" in p]
if not foreign_key_props:
# If no foreign keys, return empty array
return [
" static func relationships() -> [Relationship] {",
" return []",
" }"
]
lines = [
" static func relationships() -> [Relationship] {",
" return ["
]
# Generate relationship entries
for prop in foreign_key_props:
name = prop["name"]
foreign_key = prop["foreignKey"].rstrip('*') # Remove asterisk if present
lines.append(f" Relationship(type: {foreign_key}.self, keyPath: \\Base{model_name}.{name}),")
# Close the array and function
lines.extend([
" ]",
" }"
])
return lines
def _get_default_value(self, type_name: str) -> str:
"""Get default value for non-optional types"""
if "String" in type_name:
return "\"\""
elif "Int" in type_name:
return "0"
elif "Double" in type_name:
return "0.0"
elif "Bool" in type_name:
return "false"
elif "Date" in type_name:
return "Date()"
else:
return "nil" # For any other type
def get_output_filename(self, model_name: str) -> str:
"""Generate the output filename for a model."""
return f"Base{model_name}.swift"
def make_resource_name(self, text):
p = inflect.engine()
# Split camelCase into words
words = re.findall('[A-Z][^A-Z]*', text)
if not words:
words = [text]
words = [word.lower() for word in words]
words[-1] = p.plural(words[-1])
return '-'.join(words)
def process_directory(input_dir: str, output_dir: str, logger: logging.Logger, dry_run: bool = False) -> int:
"""Process all JSON files in the input directory and generate Swift models."""
try:
input_path = validate_directory(input_dir)
if not dry_run:
output_path = validate_directory(output_dir, create=True)
json_files = list(input_path.glob("*.json"))
if not json_files:
logger.warning(f"No JSON files found in '{input_dir}'")
return 0
logger.info(f"Found {len(json_files)} JSON files to process")
successful_files = 0
for json_file in json_files:
try:
with open(json_file, 'r') as f:
json_data = json.load(f)
generator = SwiftModelGenerator(json_data)
# Generate each model in the JSON file
for model in json_data["models"]:
model_name = model["name"]
swift_code = generator.generate_model(model)
if dry_run:
logger.info(f"Would generate Base{model_name}.swift")
continue
# Write to output file with Base prefix
output_file = output_path / generator.get_output_filename(model_name)
with open(output_file, 'w') as f:
f.write(swift_code)
logger.info(f"Generated Base{model_name}.swift")
successful_files += 1
except json.JSONDecodeError as e:
logger.error(f"Error parsing {json_file.name}: {e}")
except KeyError as e:
logger.error(f"Missing required key in {json_file.name}: {e}")
except Exception as e:
logger.error(f"Error processing {json_file.name}: {e}")
return successful_files
except Exception as e:
logger.error(f"Fatal error: {e}")
return 0
def setup_logging(verbose: bool) -> logging.Logger:
"""Configure logging based on verbosity level."""
logger = logging.getLogger('SwiftModelGenerator')
handler = logging.StreamHandler()
if verbose:
logger.setLevel(logging.DEBUG)
handler.setFormatter(logging.Formatter(
'%(asctime)s - %(levelname)s - %(message)s'
))
else:
logger.setLevel(logging.INFO)
handler.setFormatter(logging.Formatter('%(message)s'))
logger.addHandler(handler)
return logger
def validate_directory(path: str, create: bool = False) -> Path:
"""Validate and optionally create a directory."""
dir_path = Path(path)
if dir_path.exists():
if not dir_path.is_dir():
raise ValueError(f"'{path}' exists but is not a directory")
elif create:
dir_path.mkdir(parents=True)
else:
raise ValueError(f"Directory '{path}' does not exist")
return dir_path
def main():
parser = argparse.ArgumentParser(
description="Generate Swift model classes from JSON definitions",
epilog="Example: %(prog)s -i ./model_definitions -o ../MyProject/Models"
)
parser.add_argument(
"-i", "--input-dir",
required=True,
help="Directory containing JSON model definitions"
)
parser.add_argument(
"-o", "--output-dir",
required=True,
help="Directory where Swift files will be generated"
)
parser.add_argument(
"-v", "--verbose",
action="store_true",
help="Enable verbose output"
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Show what would be generated without actually creating files"
)
parser.add_argument(
"--version",
action="version",
version="%(prog)s 1.0.0"
)
args = parser.parse_args()
# Setup logging
logger = setup_logging(args.verbose)
# Process the directories
start_time = datetime.now()
logger.info("Starting model generation...")
successful_files = process_directory(
args.input_dir,
args.output_dir,
logger,
args.dry_run
)
# Report results
duration = datetime.now() - start_time
logger.info(f"\nGeneration completed in {duration.total_seconds():.2f} seconds")
logger.info(f"Successfully processed {successful_files} files")
# Return appropriate exit code
sys.exit(0 if successful_files > 0 else 1)
if __name__ == "__main__":
main()

@ -11,20 +11,7 @@ import Algorithms
import SwiftUI
@Observable
final class GroupStage: ModelObject, Storable {
static func resourceName() -> String { "group-stages" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
static func filterByStoreIdentifier() -> Bool { return true }
static var relationshipNames: [String] = []
var id: String = Store.randomId()
var tournament: String
var index: Int
var size: Int
private var format: MatchFormat?
var startDate: Date?
var name: String?
var step: Int = 0
final class GroupStage: BaseGroupStage, SideStorable {
var matchFormat: MatchFormat {
get {
@ -35,24 +22,15 @@ final class GroupStage: ModelObject, Storable {
}
}
internal init(tournament: String, index: Int, size: Int, matchFormat: MatchFormat? = nil, startDate: Date? = nil, name: String? = nil, step: Int = 0) {
self.tournament = tournament
self.index = index
self.size = size
self.format = matchFormat
self.startDate = startDate
self.name = name
self.step = step
}
var tournamentStore: TournamentStore {
return TournamentStore.instance(tournamentId: self.tournament)
var tournamentStore: TournamentStore? {
return TournamentLibrary.shared.store(tournamentId: self.tournament)
}
// MARK: - Computed dependencies
func _matches() -> [Match] {
return self.tournamentStore.matches.filter { $0.groupStage == self.id }.sorted(by: \.index)
guard let tournamentStore = self.tournamentStore else { return [] }
return tournamentStore.matches.filter { $0.groupStage == self.id }.sorted(by: \.index)
// Store.main.filter { $0.groupStage == self.id }
}
@ -109,10 +87,10 @@ final class GroupStage: ModelObject, Storable {
fileprivate func _createMatch(index: Int) -> Match {
let match: Match = Match(groupStage: self.id,
index: index,
matchFormat: self.matchFormat,
format: self.matchFormat,
name: self.localizedMatchUpLabel(for: index))
match.store = self.store
print("_createMatch(index)", index)
// print("_createMatch(index)", index)
return match
}
@ -124,7 +102,7 @@ final class GroupStage: ModelObject, Storable {
returnMatches = returnMatches.filter({ $0.index >= matchCount * matchPhaseCount })
}
do {
try self.tournamentStore.matches.delete(contentOfs: returnMatches)
try self.tournamentStore?.matches.delete(contentOfs: returnMatches)
} catch {
Logger.error(error)
}
@ -150,12 +128,8 @@ final class GroupStage: ModelObject, Storable {
matches.append(newMatch)
}
do {
try self.tournamentStore.matches.addOrUpdate(contentOfs: matches)
try self.tournamentStore.teamScores.addOrUpdate(contentOfs: teamScores)
} catch {
Logger.error(error)
}
self.tournamentStore?.matches.addOrUpdate(contentOfs: matches)
self.tournamentStore?.teamScores.addOrUpdate(contentOfs: teamScores)
}
func buildMatches(keepExistingMatches: Bool = false) {
@ -180,8 +154,8 @@ final class GroupStage: ModelObject, Storable {
}
do {
try self.tournamentStore.matches.addOrUpdate(contentOfs: matches)
try self.tournamentStore.teamScores.addOrUpdate(contentOfs: teamScores)
try self.tournamentStore?.matches.addOrUpdate(contentOfs: matches)
try self.tournamentStore?.teamScores.addOrUpdate(contentOfs: teamScores)
} catch {
Logger.error(error)
}
@ -211,7 +185,7 @@ final class GroupStage: ModelObject, Storable {
clearScoreCache()
if hasEnded(), let tournament = tournamentObject() {
do {
let teams = teams(true)
for (index, team) in teams.enumerated() {
team.qualified = index < tournament.qualifiedPerGroupStage
@ -219,13 +193,11 @@ final class GroupStage: ModelObject, Storable {
tournamentObject()?.shouldVerifyBracket = true
}
}
try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams)
try self.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams)
if let tournamentObject = tournamentObject() {
try DataStore.shared.tournaments.addOrUpdate(instance: tournamentObject)
}
} catch {
Logger.error(error)
}
self.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams)
let groupStagesAreOverAtFirstStep = tournament.groupStagesAreOver(atStep: 0)
let nextStepGroupStages = tournament.groupStages(atStep: 1)
@ -233,11 +205,7 @@ final class GroupStage: ModelObject, Storable {
if groupStagesAreOverAtFirstStep, nextStepGroupStages.isEmpty || groupStagesAreOverAtSecondStep == true, tournament.groupStageLoserBracketAreOver(), tournament.rounds().isEmpty {
tournament.endDate = Date()
do {
try DataStore.shared.tournaments.addOrUpdate(instance: tournament)
} catch {
Logger.error(error)
}
DataStore.shared.tournaments.addOrUpdate(instance: tournament)
}
tournament.updateTournamentState()
@ -423,11 +391,7 @@ final class GroupStage: ModelObject, Storable {
}
private func _removeMatches() {
do {
try self.tournamentStore.matches.delete(contentOfs: _matches())
} catch {
Logger.error(error)
}
self.tournamentStore?.matches.delete(contentOfs: _matches())
}
private func _numberOfMatchesToBuild() -> Int {
@ -483,10 +447,11 @@ final class GroupStage: ModelObject, Storable {
}
func unsortedTeams() -> [TeamRegistration] {
guard let tournamentStore = self.tournamentStore else { return [] }
if step > 0 {
return self.tournamentStore.groupStages.filter({ $0.step == step - 1 }).compactMap({ $0.teams(true)[safe: index] })
return tournamentStore.groupStages.filter({ $0.step == step - 1 }).compactMap({ $0.teams(true)[safe: index] })
}
return self.tournamentStore.teamRegistrations.filter { $0.groupStage == self.id && $0.groupStagePosition != nil }
return tournamentStore.teamRegistrations.filter { $0.groupStage == self.id && $0.groupStagePosition != nil }
}
@ -613,11 +578,7 @@ final class GroupStage: ModelObject, Storable {
playedMatches.forEach { match in
match.matchFormat = matchFormat
}
do {
try self.tournamentStore.matches.addOrUpdate(contentOfs: playedMatches)
} catch {
Logger.error(error)
}
self.tournamentStore?.matches.addOrUpdate(contentOfs: playedMatches)
}
func pasteData() -> String {
@ -635,42 +596,46 @@ final class GroupStage: ModelObject, Storable {
return teams(true).firstIndex(of: team)
}
override func deleteDependencies() throws {
override func deleteDependencies() {
let matches = self._matches()
for match in matches {
try match.deleteDependencies()
match.deleteDependencies()
}
self.tournamentStore.matches.deleteDependencies(matches)
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: ._id)
tournament = try container.decode(String.self, forKey: ._tournament)
index = try container.decode(Int.self, forKey: ._index)
size = try container.decode(Int.self, forKey: ._size)
format = try container.decodeIfPresent(MatchFormat.self, forKey: ._format)
startDate = try container.decodeIfPresent(Date.self, forKey: ._startDate)
name = try container.decodeIfPresent(String.self, forKey: ._name)
step = try container.decodeIfPresent(Int.self, forKey: ._step) ?? 0
self.tournamentStore?.matches.deleteDependencies(matches)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: ._id)
try container.encode(tournament, forKey: ._tournament)
try container.encode(index, forKey: ._index)
try container.encode(size, forKey: ._size)
try container.encode(format, forKey: ._format)
try container.encode(startDate, forKey: ._startDate)
try container.encode(name, forKey: ._name)
try container.encode(step, forKey: ._step)
}
// init(from decoder: Decoder) throws {
// let container = try decoder.container(keyedBy: CodingKeys.self)
//
// id = try container.decode(String.self, forKey: ._id)
// storeId = try container.decode(String.self, forKey: ._storeId)
// lastUpdate = try container.decode(Date.self, forKey: ._lastUpdate)
// tournament = try container.decode(String.self, forKey: ._tournament)
// index = try container.decode(Int.self, forKey: ._index)
// size = try container.decode(Int.self, forKey: ._size)
// format = try container.decodeIfPresent(MatchFormat.self, forKey: ._format)
// startDate = try container.decodeIfPresent(Date.self, forKey: ._startDate)
// name = try container.decodeIfPresent(String.self, forKey: ._name)
// step = try container.decodeIfPresent(Int.self, forKey: ._step) ?? 0
// }
//
// func encode(to encoder: Encoder) throws {
// var container = encoder.container(keyedBy: CodingKeys.self)
//
// 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(index, forKey: ._index)
// try container.encode(size, forKey: ._size)
// try container.encode(format, forKey: ._format)
// try container.encode(startDate, forKey: ._startDate)
// try container.encode(name, forKey: ._name)
// try container.encode(step, forKey: ._step)
// }
func insertOnServer() {
self.tournamentStore.groupStages.writeChangeAndInsertOnServer(instance: self)
self.tournamentStore?.groupStages.writeChangeAndInsertOnServer(instance: self)
for match in self._matches() {
match.insertOnServer()
}
@ -681,6 +646,8 @@ final class GroupStage: ModelObject, Storable {
extension GroupStage {
enum CodingKeys: String, CodingKey {
case _id = "id"
case _storeId = "storeId"
case _lastUpdate = "lastUpdate"
case _tournament = "tournament"
case _index = "index"
case _size = "size"

@ -9,16 +9,12 @@ import Foundation
import LeStorage
@Observable
final class Match: ModelObject, Storable, Equatable {
final class Match: BaseMatch, SideStorable {
static func == (lhs: Match, rhs: Match) -> Bool {
lhs.id == rhs.id && lhs.startDate == rhs.startDate
}
static func resourceName() -> String { "matches" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
static func filterByStoreIdentifier() -> Bool { return true }
static var relationshipNames: [String] = ["round", "groupStage"]
static func setServerTitle(upperRound: Round, matchIndex: Int) -> String {
if upperRound.index == 0 { return upperRound.roundTitle() }
return upperRound.roundTitle() + " #" + (matchIndex + 1).formatted()
@ -26,41 +22,14 @@ final class Match: ModelObject, Storable, Equatable {
var byeState: Bool = false
var id: String = Store.randomId()
var round: String?
var groupStage: String?
var startDate: Date?
var endDate: Date?
var index: Int
private var format: MatchFormat?
//var court: String?
var servingTeamId: String?
var winningTeamId: String?
var losingTeamId: String?
//var broadcasted: Bool
var name: String?
//var order: Int
var disabled: Bool = false
private(set) var courtIndex: Int?
var 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.round = round
self.groupStage = groupStage
self.startDate = startDate
self.endDate = endDate
self.index = index
self.format = matchFormat
//self.court = court
self.servingTeamId = servingTeamId
self.winningTeamId = winningTeamId
self.losingTeamId = losingTeamId
self.disabled = disabled
self.name = name
self.courtIndex = courtIndex
self.confirmed = confirmed
// self.broadcasted = broadcasted
// self.order = order
init(round: String? = nil, groupStage: String? = nil, startDate: Date? = nil, endDate: Date? = nil, index: Int, format: MatchFormat? = nil, servingTeamId: String? = nil, winningTeamId: String? = nil, losingTeamId: String? = nil, name: String? = nil, disabled: Bool = false, courtIndex: Int? = nil, confirmed: Bool = false) {
super.init(round: round, groupStage: groupStage, startDate: startDate, endDate: endDate, index: index, format: format, servingTeamId: servingTeamId, winningTeamId: winningTeamId, losingTeamId: losingTeamId, name: name, disabled: disabled, courtIndex: courtIndex, confirmed: confirmed)
}
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
}
func setMatchName(_ serverName: String?) {
@ -77,9 +46,9 @@ final class Match: ModelObject, Storable, Equatable {
}
}
var tournamentStore: TournamentStore {
if let store = self.store as? TournamentStore {
return store
var tournamentStore: TournamentStore? {
if let id = self.store?.identifier {
return TournamentLibrary.shared.store(tournamentId: id)
}
fatalError("missing store for \(String(describing: type(of: self)))")
}
@ -91,20 +60,18 @@ final class Match: ModelObject, Storable, Equatable {
// MARK: - Computed dependencies
var teamScores: [TeamScore] {
return self.tournamentStore.teamScores.filter { $0.match == self.id }
guard let tournamentStore = self.tournamentStore else { return [] }
return tournamentStore.teamScores.filter { $0.match == self.id }
}
// MARK: -
override func deleteDependencies() throws {
guard let tournament = self.currentTournament() else {
return
}
override func deleteDependencies() {
let teamScores = self.teamScores
for teamScore in teamScores {
try teamScore.deleteDependencies()
teamScore.deleteDependencies()
}
tournament.tournamentStore.teamScores.deleteDependencies(teamScores)
self.tournamentStore?.teamScores.deleteDependencies(teamScores)
}
func indexInRound(in matches: [Match]? = nil) -> Int {
@ -183,12 +150,12 @@ defer {
func winner() -> TeamRegistration? {
guard let winningTeamId else { return nil }
return self.tournamentStore.teamRegistrations.findById(winningTeamId)
return self.tournamentStore?.teamRegistrations.findById(winningTeamId)
}
func loser() -> TeamRegistration? {
guard let losingTeamId else { return nil }
return self.tournamentStore.teamRegistrations.findById(losingTeamId)
return self.tournamentStore?.teamRegistrations.findById(losingTeamId)
}
@ -217,11 +184,7 @@ defer {
endDate = nil
followingMatch()?.cleanScheduleAndSave(nil)
_loserMatch()?.cleanScheduleAndSave(nil)
do {
try self.tournamentStore.matches.addOrUpdate(instance: self)
} catch {
Logger.error(error)
}
self.tournamentStore?.matches.addOrUpdate(instance: self)
}
func resetMatch() {
@ -237,22 +200,14 @@ defer {
func resetScores() {
teamScores.forEach({ $0.score = nil })
do {
try self.tournamentStore.teamScores.addOrUpdate(contentOfs: teamScores)
} catch {
Logger.error(error)
}
self.tournamentStore?.teamScores.addOrUpdate(contentOfs: teamScores)
}
func teamWillBeWalkOut(_ team: TeamRegistration) {
resetMatch()
let existingTeamScore = teamScore(ofTeam: team) ?? TeamScore(match: id, team: team)
existingTeamScore.walkOut = 1
do {
try self.tournamentStore.teamScores.addOrUpdate(instance: existingTeamScore)
} catch {
Logger.error(error)
}
self.tournamentStore?.teamScores.addOrUpdate(instance: existingTeamScore)
}
func luckyLosers() -> [TeamRegistration] {
@ -270,19 +225,11 @@ defer {
let position = matchIndex * 2 + teamPosition.rawValue
let previousScores = teamScores.filter({ $0.luckyLoser == position })
do {
try self.tournamentStore.teamScores.delete(contentOfs: previousScores)
} catch {
Logger.error(error)
}
self.tournamentStore?.teamScores.delete(contentOfs: previousScores)
let teamScoreLuckyLoser = teamScore(ofTeam: team) ?? TeamScore(match: id, team: team)
teamScoreLuckyLoser.luckyLoser = position
do {
try self.tournamentStore.teamScores.addOrUpdate(instance: teamScoreLuckyLoser)
} catch {
Logger.error(error)
}
self.tournamentStore?.teamScores.addOrUpdate(instance: teamScoreLuckyLoser)
}
func disableMatch() {
@ -317,14 +264,14 @@ defer {
let isTopMatch = topMatchIndex + 1 == index
let lookingForIndex = isTopMatch ? topMatchIndex : bottomMatchIndex
return self.tournamentStore.matches.first(where: { $0.round == round && $0.index == lookingForIndex })
return self.tournamentStore?.matches.first(where: { $0.round == round && $0.index == lookingForIndex })
}
private func _forwardMatch(inRound round: Round) -> Match? {
guard let roundObjectNextRound = round.nextRound() else { return nil }
let nextIndex = (index - 1) / 2
return self.tournamentStore.matches.first(where: { $0.round == roundObjectNextRound.id && $0.index == nextIndex })
return self.tournamentStore?.matches.first(where: { $0.round == roundObjectNextRound.id && $0.index == nextIndex })
}
func _toggleForwardMatchDisableState(_ state: Bool) {
@ -389,9 +336,10 @@ defer {
let currentState = disabled
disabled = state
if disabled != currentState {
do {
try self.tournamentStore.teamScores.delete(contentOfs: teamScores)
try self.tournamentStore?.teamScores.delete(contentOfs: teamScores)
} catch {
Logger.error(error)
}
@ -401,11 +349,7 @@ defer {
for team in teams {
if isSeededBy(team: team) {
team.bracketPosition = nil
do {
try self.tournamentStore.teamRegistrations.addOrUpdate(instance: team)
} catch {
Logger.error(error)
}
self.tournamentStore?.teamRegistrations.addOrUpdate(instance: team)
}
}
}
@ -415,7 +359,7 @@ defer {
roundObject?._cachedSeedInterval = nil
name = nil
do {
try self.tournamentStore.matches.addOrUpdate(instance: self)
try self.tournamentStore?.matches.addOrUpdate(instance: self)
} catch {
Logger.error(error)
}
@ -433,7 +377,8 @@ defer {
}
func next() -> Match? {
let matches: [Match] = self.tournamentStore.matches.filter { $0.round == round && $0.index > index && $0.disabled == false }
guard let tournamentStore = self.tournamentStore else { return nil }
let matches: [Match] = tournamentStore.matches.filter { $0.round == round && $0.index > index && $0.disabled == false }
return matches.sorted(by: \.index).first
}
@ -443,7 +388,7 @@ defer {
}
func getFollowingMatch(fromNextRoundId nextRoundId: String) -> Match? {
return self.tournamentStore.matches.first(where: { $0.round == nextRoundId && $0.index == (index - 1) / 2 })
return self.tournamentStore?.matches.first(where: { $0.round == nextRoundId && $0.index == (index - 1) / 2 })
}
func getDuration() -> Int {
@ -476,7 +421,7 @@ defer {
guard let roundObject else { return nil }
let topPreviousRoundMatchIndex = topPreviousRoundMatchIndex()
let roundObjectPreviousRoundId = roundObject.previousRound()?.id
return self.tournamentStore.matches.first(where: { match in
return self.tournamentStore?.matches.first(where: { match in
match.round != nil && match.round == roundObjectPreviousRoundId && match.index == topPreviousRoundMatchIndex
})
}
@ -485,7 +430,7 @@ defer {
guard let roundObject else { return nil }
let bottomPreviousRoundMatchIndex = bottomPreviousRoundMatchIndex()
let roundObjectPreviousRoundId = roundObject.previousRound()?.id
return self.tournamentStore.matches.first(where: { match in
return self.tournamentStore?.matches.first(where: { match in
match.round != nil && match.round == roundObjectPreviousRoundId && match.index == bottomPreviousRoundMatchIndex
})
}
@ -522,8 +467,10 @@ defer {
func previousMatches() -> [Match] {
guard let roundObject else { return [] }
guard let tournamentStore = self.tournamentStore else { return [] }
let roundObjectPreviousRoundId = roundObject.previousRound()?.id
return self.tournamentStore.matches.filter { match in
return tournamentStore.matches.filter { match in
match.round == roundObjectPreviousRoundId && (match.index == topPreviousRoundMatchIndex() || match.index == bottomPreviousRoundMatchIndex())
}.sorted(by: \.index)
}
@ -545,7 +492,7 @@ defer {
teamScoreWinning.walkOut = nil
teamScoreWinning.score = matchFormat.defaultWalkOutScore(false).compactMap({ String($0) }).joined(separator: ",")
do {
try self.tournamentStore.teamScores.addOrUpdate(contentOfs: [teamScoreWalkout, teamScoreWinning])
try self.tournamentStore?.teamScores.addOrUpdate(contentOfs: [teamScoreWalkout, teamScoreWinning])
} catch {
Logger.error(error)
}
@ -613,7 +560,7 @@ defer {
}
do {
try self.tournamentStore.teamScores.addOrUpdate(contentOfs: [teamScoreOne, teamScoreTwo])
try self.tournamentStore?.teamScores.addOrUpdate(contentOfs: [teamScoreOne, teamScoreTwo])
} catch {
Logger.error(error)
}
@ -629,11 +576,7 @@ defer {
let ids = newTeamScores.map { $0.id }
let teamScores = teamScores.filter({ ids.contains($0.id) == false })
if teamScores.isEmpty == false {
do {
try self.tournamentStore.teamScores.delete(contentOfs: teamScores)
} catch {
Logger.error(error)
}
self.tournamentStore?.teamScores.delete(contentOfs: teamScores)
followingMatch()?.resetTeamScores(outsideOf: [])
_loserMatch()?.resetTeamScores(outsideOf: [])
}
@ -655,11 +598,8 @@ defer {
func updateTeamScores() {
let teams = getOrCreateTeamScores()
do {
try self.tournamentStore.teamScores.addOrUpdate(contentOfs: teams)
} catch {
Logger.error(error)
}
self.tournamentStore?.teamScores.addOrUpdate(contentOfs: teams)
resetTeamScores(outsideOf: teams)
if teams.isEmpty == false {
updateFollowingMatchTeamScore()
@ -813,7 +753,8 @@ defer {
}
func scores() -> [TeamScore] {
return self.tournamentStore.teamScores.filter { $0.match == id }
guard let tournamentStore = self.tournamentStore else { return [] }
return tournamentStore.teamScores.filter { $0.match == id }
}
func teams() -> [TeamRegistration] {
@ -945,12 +886,12 @@ defer {
var roundObject: Round? {
guard let round else { return nil }
return self.tournamentStore.rounds.findById(round)
return self.tournamentStore?.rounds.findById(round)
}
var groupStageObject: GroupStage? {
guard let groupStage else { return nil }
return self.tournamentStore.groupStages.findById(groupStage)
return self.tournamentStore?.groupStages.findById(groupStage)
}
var isLoserBracket: Bool {
@ -1082,49 +1023,10 @@ defer {
[MatchSpot(match: self, teamPosition: .one), MatchSpot(match: self, teamPosition: .two)]
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _round = "round"
case _groupStage = "groupStage"
case _startDate = "startDate"
case _endDate = "endDate"
case _index = "index"
case _format = "format"
// case _court = "court"
case _courtIndex = "courtIndex"
case _servingTeamId = "servingTeamId"
case _winningTeamId = "winningTeamId"
case _losingTeamId = "losingTeamId"
// case _broadcasted = "broadcasted"
case _name = "name"
// case _order = "order"
case _disabled = "disabled"
case _confirmed = "confirmed"
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: ._id)
try container.encode(round, forKey: ._round)
try container.encode(groupStage, forKey: ._groupStage)
try container.encode(startDate, forKey: ._startDate)
try container.encode(endDate, forKey: ._endDate)
try container.encode(format, forKey: ._format)
try container.encode(servingTeamId, forKey: ._servingTeamId)
try container.encode(index, forKey: ._index)
try container.encode(winningTeamId, forKey: ._winningTeamId)
try container.encode(losingTeamId, forKey: ._losingTeamId)
try container.encode(name, forKey: ._name)
try container.encode(disabled, forKey: ._disabled)
try container.encode(courtIndex, forKey: ._courtIndex)
try container.encode(confirmed, forKey: ._confirmed)
}
func insertOnServer() {
self.tournamentStore.matches.writeChangeAndInsertOnServer(instance: self)
self.tournamentStore?.matches.writeChangeAndInsertOnServer(instance: self)
for teamScore in self.teamScores {
try teamScore.insertOnServer()
teamScore.insertOnServer()
}
}

@ -10,80 +10,44 @@ import LeStorage
import SwiftUI
@Observable
final class MatchScheduler : ModelObject, Storable {
static func resourceName() -> String { return "match-scheduler" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
static func filterByStoreIdentifier() -> Bool { return false }
static var relationshipNames: [String] = []
private(set) var id: String = Store.randomId()
var tournament: String
var timeDifferenceLimit: Int
var loserBracketRotationDifference: Int
var upperBracketRotationDifference: Int
var accountUpperBracketBreakTime: Bool
var accountLoserBracketBreakTime: Bool
var randomizeCourts: Bool
var rotationDifferenceIsImportant: Bool
var shouldHandleUpperRoundSlice: Bool
var shouldEndRoundBeforeStartingNext: Bool
var groupStageChunkCount: Int?
var overrideCourtsUnavailability: Bool = false
var shouldTryToFillUpCourtsAvailable: Bool = true
var courtsAvailable: Set<Int> = Set<Int>()
var simultaneousStart: Bool = true
init(tournament: String,
timeDifferenceLimit: Int = 5,
loserBracketRotationDifference: Int = 0,
upperBracketRotationDifference: Int = 1,
accountUpperBracketBreakTime: Bool = true,
accountLoserBracketBreakTime: Bool = false,
randomizeCourts: Bool = true,
rotationDifferenceIsImportant: Bool = false,
shouldHandleUpperRoundSlice: Bool = false,
shouldEndRoundBeforeStartingNext: Bool = true,
groupStageChunkCount: Int? = nil,
overrideCourtsUnavailability: Bool = false,
shouldTryToFillUpCourtsAvailable: Bool = true,
courtsAvailable: Set<Int> = Set<Int>(),
simultaneousStart: Bool = true) {
self.tournament = tournament
self.timeDifferenceLimit = timeDifferenceLimit
self.loserBracketRotationDifference = loserBracketRotationDifference
self.upperBracketRotationDifference = upperBracketRotationDifference
self.accountUpperBracketBreakTime = accountUpperBracketBreakTime
self.accountLoserBracketBreakTime = accountLoserBracketBreakTime
self.randomizeCourts = randomizeCourts
self.rotationDifferenceIsImportant = rotationDifferenceIsImportant
self.shouldHandleUpperRoundSlice = shouldHandleUpperRoundSlice
self.shouldEndRoundBeforeStartingNext = shouldEndRoundBeforeStartingNext
self.groupStageChunkCount = groupStageChunkCount
self.overrideCourtsUnavailability = overrideCourtsUnavailability
self.shouldTryToFillUpCourtsAvailable = shouldTryToFillUpCourtsAvailable
self.courtsAvailable = courtsAvailable
self.simultaneousStart = simultaneousStart
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _tournament = "tournament"
case _timeDifferenceLimit = "timeDifferenceLimit"
case _loserBracketRotationDifference = "loserBracketRotationDifference"
case _upperBracketRotationDifference = "upperBracketRotationDifference"
case _accountUpperBracketBreakTime = "accountUpperBracketBreakTime"
case _accountLoserBracketBreakTime = "accountLoserBracketBreakTime"
case _randomizeCourts = "randomizeCourts"
case _rotationDifferenceIsImportant = "rotationDifferenceIsImportant"
case _shouldHandleUpperRoundSlice = "shouldHandleUpperRoundSlice"
case _shouldEndRoundBeforeStartingNext = "shouldEndRoundBeforeStartingNext"
case _groupStageChunkCount = "groupStageChunkCount"
case _overrideCourtsUnavailability = "overrideCourtsUnavailability"
case _shouldTryToFillUpCourtsAvailable = "shouldTryToFillUpCourtsAvailable"
case _courtsAvailable = "courtsAvailable"
case _simultaneousStart = "simultaneousStart"
}
final class MatchScheduler: BaseMatchScheduler, SideStorable {
//
// init(tournament: String,
// timeDifferenceLimit: Int = 5,
// loserBracketRotationDifference: Int = 0,
// upperBracketRotationDifference: Int = 1,
// accountUpperBracketBreakTime: Bool = true,
// accountLoserBracketBreakTime: Bool = false,
// randomizeCourts: Bool = true,
// rotationDifferenceIsImportant: Bool = false,
// shouldHandleUpperRoundSlice: Bool = false,
// shouldEndRoundBeforeStartingNext: Bool = true,
//<<<<<<< HEAD
// groupStageChunkCount: Int? = nil, overrideCourtsUnavailability: Bool = false, shouldTryToFillUpCourtsAvailable: Bool = false) {
// super.init()
//=======
// groupStageChunkCount: Int? = nil,
// overrideCourtsUnavailability: Bool = false,
// shouldTryToFillUpCourtsAvailable: Bool = true,
// courtsAvailable: Set<Int> = Set<Int>(),
// simultaneousStart: Bool = true) {
//>>>>>>> main
// self.tournament = tournament
// self.timeDifferenceLimit = timeDifferenceLimit
// self.loserBracketRotationDifference = loserBracketRotationDifference
// self.upperBracketRotationDifference = upperBracketRotationDifference
// self.accountUpperBracketBreakTime = accountUpperBracketBreakTime
// self.accountLoserBracketBreakTime = accountLoserBracketBreakTime
// self.randomizeCourts = randomizeCourts
// self.rotationDifferenceIsImportant = rotationDifferenceIsImportant
// self.shouldHandleUpperRoundSlice = shouldHandleUpperRoundSlice
// self.shouldEndRoundBeforeStartingNext = shouldEndRoundBeforeStartingNext
// self.groupStageChunkCount = groupStageChunkCount
// self.overrideCourtsUnavailability = overrideCourtsUnavailability
// self.shouldTryToFillUpCourtsAvailable = shouldTryToFillUpCourtsAvailable
// self.courtsAvailable = courtsAvailable
// self.simultaneousStart = simultaneousStart
// }
var courtsUnavailability: [DateInterval]? {
guard let event = tournamentObject()?.eventObject() else { return nil }
@ -94,8 +58,9 @@ final class MatchScheduler : ModelObject, Storable {
return tournamentObject()?.additionalEstimationDuration ?? 0
}
var tournamentStore: TournamentStore {
return TournamentStore.instance(tournamentId: self.tournament)
var tournamentStore: TournamentStore? {
return TournamentLibrary.shared.store(tournamentId: self.tournament)
// TournamentStore.instance(tournamentId: self.tournament)
}
func tournamentObject() -> Tournament? {
@ -155,7 +120,7 @@ final class MatchScheduler : ModelObject, Storable {
groupStages.filter({ $0.startDate == nil || times.contains($0.startDate!) == false }).chunked(into: computedGroupStageChunkCount).forEach { groups in
groups.forEach({ $0.startDate = lastDate })
do {
try self.tournamentStore.groupStages.addOrUpdate(contentOfs: groups)
try self.tournamentStore?.groupStages.addOrUpdate(contentOfs: groups)
} catch {
Logger.error(error)
}
@ -176,7 +141,7 @@ final class MatchScheduler : ModelObject, Storable {
}
}
do {
try self.tournamentStore.matches.addOrUpdate(contentOfs: matches)
try self.tournamentStore?.matches.addOrUpdate(contentOfs: matches)
} catch {
Logger.error(error)
}
@ -770,7 +735,7 @@ final class MatchScheduler : ModelObject, Storable {
}
do {
try self.tournamentStore.matches.addOrUpdate(contentOfs: allMatches)
try self.tournamentStore?.matches.addOrUpdate(contentOfs: allMatches)
} catch {
Logger.error(error)
}

@ -10,44 +10,16 @@ import SwiftUI
import LeStorage
@Observable
final class MonthData : ModelObject, Storable {
static func resourceName() -> String { return "month-data" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
static func filterByStoreIdentifier() -> Bool { return false }
static var relationshipNames: [String] = []
private(set) var id: String = Store.randomId()
private(set) var monthKey: String
private(set) var creationDate: Date
var maleUnrankedValue: Int? = nil
var femaleUnrankedValue: Int? = nil
var maleCount: Int? = nil
var femaleCount: Int? = nil
var anonymousCount: Int? = nil
var incompleteMode: Bool = false
var dataModelIdentifier: String?
var fileModelIdentifier: String?
final class MonthData: BaseMonthData {
init(monthKey: String) {
super.init()
self.monthKey = monthKey
self.creationDate = Date()
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: ._id)
monthKey = try container.decode(String.self, forKey: ._monthKey)
creationDate = try container.decode(Date.self, forKey: ._creationDate)
maleUnrankedValue = try container.decodeIfPresent(Int.self, forKey: ._maleUnrankedValue)
femaleUnrankedValue = try container.decodeIfPresent(Int.self, forKey: ._femaleUnrankedValue)
maleCount = try container.decodeIfPresent(Int.self, forKey: ._maleCount)
femaleCount = try container.decodeIfPresent(Int.self, forKey: ._femaleCount)
anonymousCount = try container.decodeIfPresent(Int.self, forKey: ._anonymousCount)
incompleteMode = try container.decodeIfPresent(Bool.self, forKey: ._incompleteMode) ?? false
dataModelIdentifier = try container.decodeIfPresent(String.self, forKey: ._dataModelIdentifier) ?? nil
fileModelIdentifier = try container.decodeIfPresent(String.self, forKey: ._fileModelIdentifier) ?? nil
try super.init(from: decoder)
}
func total() -> Int {
@ -77,28 +49,9 @@ final class MonthData : ModelObject, Storable {
currentMonthData.femaleUnrankedValue = lastDataSourceFemaleUnranked?.0
currentMonthData.femaleCount = lastDataSourceFemaleUnranked?.1
currentMonthData.anonymousCount = anonymousCount
do {
try DataStore.shared.monthData.addOrUpdate(instance: currentMonthData)
} catch {
Logger.error(error)
}
}
}
DataStore.shared.monthData.addOrUpdate(instance: currentMonthData)
override func deleteDependencies() throws {
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _monthKey = "monthKey"
case _creationDate = "creationDate"
case _maleUnrankedValue = "maleUnrankedValue"
case _femaleUnrankedValue = "femaleUnrankedValue"
case _maleCount = "maleCount"
case _femaleCount = "femaleCount"
case _anonymousCount = "anonymousCount"
case _incompleteMode = "incompleteMode"
case _dataModelIdentifier = "dataModelIdentifier"
case _fileModelIdentifier = "fileModelIdentifier"
}
}

@ -0,0 +1,53 @@
//
// PlayerPaymentType.swift
// PadelClub
//
// Created by Laurent Morvillier on 11/02/2025.
//
import Foundation
enum PlayerPaymentType: Int, CaseIterable, Identifiable, Codable {
init?(rawValue: Int?) {
guard let value = rawValue else { return nil }
self.init(rawValue: value)
}
var id: Self {
self
}
case cash = 0
case lydia = 1
case gift = 2
case check = 3
case paylib = 4
case bankTransfer = 5
case clubHouse = 6
case creditCard = 7
case forfeit = 8
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .check:
return "Chèque"
case .cash:
return "Cash"
case .lydia:
return "Lydia"
case .paylib:
return "Paylib"
case .bankTransfer:
return "Virement"
case .clubHouse:
return "Clubhouse"
case .creditCard:
return "CB"
case .forfeit:
return "Forfait"
case .gift:
return "Offert"
}
}
}

@ -9,38 +9,7 @@ import Foundation
import LeStorage
@Observable
final class PlayerRegistration: ModelObject, Storable {
static func resourceName() -> String { "player-registrations" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
static func filterByStoreIdentifier() -> Bool { return true }
static var relationshipNames: [String] = ["teamRegistration"]
var id: String = Store.randomId()
var teamRegistration: String?
var firstName: String
var lastName: String
var licenceId: String?
var rank: Int?
var paymentType: PlayerPaymentType?
var sex: PlayerSexType?
var tournamentPlayed: Int?
var points: Double?
var clubName: String?
var ligueName: String?
var assimilation: String?
var phoneNumber: String?
var email: String?
var birthdate: String?
var computedRank: Int = 0
var source: PlayerDataSource?
var hasArrived: Bool = false
var coach: Bool = false
var captain: Bool = false
var registeredOnline: Bool = false
final class PlayerRegistration: BasePlayerRegistration, SideStorable {
func localizedSourceLabel() -> String {
switch source {
@ -57,8 +26,8 @@ final class PlayerRegistration: ModelObject, Storable {
}
}
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, coach: Bool = false, captain: Bool = false, registeredOnline: 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: PlayerRegistration.PlayerDataSource? = nil, hasArrived: Bool = false) {
super.init()
self.teamRegistration = teamRegistration
self.firstName = firstName
self.lastName = lastName
@ -77,12 +46,10 @@ final class PlayerRegistration: ModelObject, Storable {
self.computedRank = computedRank
self.source = source
self.hasArrived = hasArrived
self.captain = captain
self.coach = coach
self.registeredOnline = registeredOnline
}
internal init(importedPlayer: ImportedPlayer) {
super.init()
self.teamRegistration = ""
self.firstName = (importedPlayer.firstName ?? "").prefixTrimmed(50).capitalized
self.lastName = (importedPlayer.lastName ?? "").prefixTrimmed(50).uppercased()
@ -99,6 +66,7 @@ final class PlayerRegistration: ModelObject, Storable {
}
internal init?(federalData: [String], sex: Int, sexUnknown: Bool) {
super.init()
let _lastName = federalData[0].trimmed.uppercased()
let _firstName = federalData[1].trimmed.capitalized
if _lastName.isEmpty && _firstName.isEmpty { return nil }
@ -136,11 +104,18 @@ final class PlayerRegistration: ModelObject, Storable {
}
}
var tournamentStore: TournamentStore {
if let store = self.store as? TournamentStore {
return store
required init(from decoder: any Decoder) throws {
try super.init(from: decoder)
}
var tournamentStore: TournamentStore? {
guard let storeId else {
fatalError("missing store id for \(String(describing: type(of: self)))")
}
fatalError("missing store for \(String(describing: type(of: self)))")
return TournamentLibrary.shared.store(tournamentId: storeId)
// if let store = self.store as? TournamentStore {
// return store
// }
}
var computedAge: Int? {
@ -207,7 +182,7 @@ final class PlayerRegistration: ModelObject, Storable {
func team() -> TeamRegistration? {
guard let teamRegistration else { return nil }
return self.tournamentStore.teamRegistrations.findById(teamRegistration)
return self.tournamentStore?.teamRegistrations.findById(teamRegistration)
}
func isHere() -> Bool {
@ -393,154 +368,11 @@ final class PlayerRegistration: ModelObject, Storable {
return false
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _teamRegistration = "teamRegistration"
case _firstName = "firstName"
case _lastName = "lastName"
case _licenceId = "licenceId"
case _rank = "rank"
case _paymentType = "paymentType"
case _sex = "sex"
case _tournamentPlayed = "tournamentPlayed"
case _points = "points"
case _clubName = "clubName"
case _ligueName = "ligueName"
case _assimilation = "assimilation"
case _birthdate = "birthdate"
case _phoneNumber = "phoneNumber"
case _email = "email"
case _computedRank = "computedRank"
case _source = "source"
case _hasArrived = "hasArrived"
case _coach = "coach"
case _captain = "captain"
case _registeredOnline = "registeredOnline"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// Non-optional properties
id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId()
firstName = try container.decode(String.self, forKey: ._firstName)
lastName = try container.decode(String.self, forKey: ._lastName)
computedRank = try container.decodeIfPresent(Int.self, forKey: ._computedRank) ?? 0
hasArrived = try container.decodeIfPresent(Bool.self, forKey: ._hasArrived) ?? false
coach = try container.decodeIfPresent(Bool.self, forKey: ._coach) ?? false
captain = try container.decodeIfPresent(Bool.self, forKey: ._captain) ?? false
// Optional properties
teamRegistration = try container.decodeIfPresent(String.self, forKey: ._teamRegistration)
licenceId = try container.decodeIfPresent(String.self, forKey: ._licenceId)
rank = try container.decodeIfPresent(Int.self, forKey: ._rank)
paymentType = try container.decodeIfPresent(PlayerPaymentType.self, forKey: ._paymentType)
sex = try container.decodeIfPresent(PlayerSexType.self, forKey: ._sex)
tournamentPlayed = try container.decodeIfPresent(Int.self, forKey: ._tournamentPlayed)
points = try container.decodeIfPresent(Double.self, forKey: ._points)
clubName = try container.decodeIfPresent(String.self, forKey: ._clubName)
ligueName = try container.decodeIfPresent(String.self, forKey: ._ligueName)
assimilation = try container.decodeIfPresent(String.self, forKey: ._assimilation)
phoneNumber = try container.decodeIfPresent(String.self, forKey: ._phoneNumber)
email = try container.decodeIfPresent(String.self, forKey: ._email)
birthdate = try container.decodeIfPresent(String.self, forKey: ._birthdate)
source = try container.decodeIfPresent(PlayerDataSource.self, forKey: ._source)
registeredOnline = try container.decodeIfPresent(Bool.self, forKey: ._registeredOnline) ?? false
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: ._id)
try container.encode(teamRegistration, forKey: ._teamRegistration)
try container.encode(firstName, forKey: ._firstName)
try container.encode(lastName, forKey: ._lastName)
try container.encode(licenceId, forKey: ._licenceId)
try container.encode(rank, forKey: ._rank)
try container.encode(paymentType, forKey: ._paymentType)
try container.encode(sex, forKey: ._sex)
try container.encode(tournamentPlayed, forKey: ._tournamentPlayed)
try container.encode(points, forKey: ._points)
try container.encode(clubName, forKey: ._clubName)
try container.encode(ligueName, forKey: ._ligueName)
try container.encode(assimilation, forKey: ._assimilation)
try container.encode(phoneNumber, forKey: ._phoneNumber)
try container.encode(email, forKey: ._email)
try container.encode(birthdate, forKey: ._birthdate)
try container.encode(computedRank, forKey: ._computedRank)
try container.encode(source, forKey: ._source)
try container.encode(hasArrived, forKey: ._hasArrived)
try container.encode(captain, forKey: ._captain)
try container.encode(coach, forKey: ._coach)
try container.encode(registeredOnline, forKey: ._registeredOnline)
}
enum PlayerDataSource: Int, Codable {
case frenchFederation = 0
case beachPadel = 1
}
enum PlayerSexType: Int, Hashable, CaseIterable, Identifiable, Codable {
init?(rawValue: Int?) {
guard let value = rawValue else { return nil }
self.init(rawValue: value)
}
var id: Self {
self
}
case female = 0
case male = 1
}
enum PlayerPaymentType: Int, CaseIterable, Identifiable, Codable {
init?(rawValue: Int?) {
guard let value = rawValue else { return nil }
self.init(rawValue: value)
}
var id: Self {
self
}
case cash = 0
case lydia = 1
case gift = 2
case check = 3
case paylib = 4
case bankTransfer = 5
case clubHouse = 6
case creditCard = 7
case forfeit = 8
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
switch self {
case .check:
return "Chèque"
case .cash:
return "Cash"
case .lydia:
return "Lydia"
case .paylib:
return "Paylib"
case .bankTransfer:
return "Virement"
case .clubHouse:
return "Clubhouse"
case .creditCard:
return "CB"
case .forfeit:
return "Forfait"
case .gift:
return "Offert"
}
}
}
static func addon(for playerRank: Int, manMax: Int, womanMax: Int) -> Int {
switch playerRank {
case 0: return 0
@ -552,21 +384,11 @@ final class PlayerRegistration: ModelObject, Storable {
}
func insertOnServer() {
self.tournamentStore.playerRegistrations.writeChangeAndInsertOnServer(instance: self)
self.tournamentStore?.playerRegistrations.writeChangeAndInsertOnServer(instance: self)
}
}
extension PlayerRegistration: Hashable {
static func == (lhs: PlayerRegistration, rhs: PlayerRegistration) -> Bool {
lhs.id == rhs.id
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}
extension PlayerRegistration: PlayerHolder {
func getAssimilatedAsMaleRank() -> Int? {
nil
@ -617,3 +439,22 @@ extension PlayerRegistration: PlayerHolder {
computedRank
}
}
enum PlayerDataSource: Int, Codable {
case frenchFederation = 0
case beachPadel = 1
}
enum PlayerSexType: Int, Hashable, CaseIterable, Identifiable, Codable {
init?(rawValue: Int?) {
guard let value = rawValue else { return nil }
self.init(rawValue: value)
}
var id: Self {
self
}
case female = 0
case male = 1
}

@ -1,15 +1,28 @@
# Procédure de création de classe
Dans Swift:
- Dans Data > Gen > créer un nouveau fichier json pour la classe
- Le paramètre "foreignKey" permet de générer une méthode qui récupère l'objet parent. Ajouter une étoile permet d'indiquer que l'on cherche l'objet dans le Store de l'objet et non le Store.main.
- Pour générer les fichiers, on se place dans le répertoire de generator.py et on lance la commande : python generator.py -i . -o .
Dans Django:
- Les modèles de base doivent étendre BaseModel
- Les modèles stockés dans des répertoires doivent étendre SideStoreModel
- Les classes d'admin doivent étendre SyncedObjectAdmin
- Les ForeignKey doivent toujours avoir on_delete=models.SET_NULL
- Pour se faciliter la vie dans l'admin, on veut que les delete effacent les enfants malgré tout. Il faut donc implémenter la méthode delete_dependencies(self) de la classe
# Procédure d'ajout de champ dans une classe
Dans Swift:
- Ajouter le champ dans classe
- Ajouter le champ dans le constructeur si possible
- Ajouter la codingKey correspondante
- Ajouter le champ dans l'encoding/decoding
- Ouvrir le fichier .json correspondant à la classe
- Regénérer la classe, voir ci-dessus pour la commande
- Ouvrir **ServerDataTests** et ajouter un test sur le champ
- Pour que les tests sur les dates fonctionnent, on peut tester date.formatted() par exemple
Dans Django:
- Ajouter le champ dans la classe
- Si c'est une ForeignKey, *toujours* mettre un related_name sinon la synchro casse
- Si c'est un champ dans **CustomUser**:
- Ajouter le champ à la méthode fields_for_update
- Ajouter le champ dans UserSerializer > create > create_user dans serializers.py
@ -18,4 +31,3 @@ Dans Django:
Enfin, revenir dans Xcode, ouvrir ServerDataTests et lancer le test mis à jour

@ -10,36 +10,32 @@ import LeStorage
import SwiftUI
@Observable
final class Round: ModelObject, Storable {
static func resourceName() -> String { "rounds" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
static func filterByStoreIdentifier() -> Bool { return true }
static var relationshipNames: [String] = []
var id: String = Store.randomId()
var tournament: String
var index: Int
var parent: String?
private(set) var format: MatchFormat?
var startDate: Date?
var groupStageLoserBracket: Bool = false
var loserBracketMode: LoserBracketMode = .automatic
final class Round: BaseRound, SideStorable {
var _cachedSeedInterval: SeedInterval?
internal init(tournament: String, index: Int, parent: String? = nil, matchFormat: MatchFormat? = nil, startDate: Date? = nil, groupStageLoserBracket: Bool = false, loserBracketMode: LoserBracketMode = .automatic) {
self.tournament = tournament
self.index = index
self.parent = parent
self.format = matchFormat
self.startDate = startDate
self.groupStageLoserBracket = groupStageLoserBracket
self.loserBracketMode = loserBracketMode
super.init(tournament: tournament, index: index, parent: parent, format: matchFormat, startDate: startDate, groupStageLoserBracket: groupStageLoserBracket, loserBracketMode: loserBracketMode)
// self.lastUpdate = Date()
// self.tournament = tournament
// self.index = index
// self.parent = parent
// self.format = matchFormat
// self.startDate = startDate
// self.groupStageLoserBracket = groupStageLoserBracket
// self.loserBracketMode = loserBracketMode
}
required init(from decoder: any Decoder) throws {
try super.init(from: decoder)
}
// MARK: - Computed dependencies
var tournamentStore: TournamentStore {
return TournamentStore.instance(tournamentId: self.tournament)
var tournamentStore: TournamentStore? {
return TournamentLibrary.shared.store(tournamentId: self.tournament)
}
func tournamentObject() -> Tournament? {
@ -47,13 +43,13 @@ final class Round: ModelObject, Storable {
}
func _matches() -> [Match] {
return self.tournamentStore.matches.filter { $0.round == self.id }.sorted(by: \.index)
// return Store.main.filter { $0.round == self.id }
guard let tournamentStore = self.tournamentStore else { return [] }
return tournamentStore.matches.filter { $0.round == self.id }.sorted(by: \.index)
}
func getDisabledMatches() -> [Match] {
return self.tournamentStore.matches.filter { $0.round == self.id && $0.disabled == true }
// return Store.main.filter { $0.round == self.id && $0.disabled == true }
guard let tournamentStore = self.tournamentStore else { return [] }
return tournamentStore.matches.filter { $0.round == self.id && $0.disabled == true }
}
// MARK: -
@ -90,8 +86,9 @@ final class Round: ModelObject, Storable {
func previousMatches(ofMatch match: Match) -> [Match] {
guard let previousRound = previousRound() else { return [] }
guard let tournamentStore = self.tournamentStore else { return [] }
return self.tournamentStore.matches.filter {
return tournamentStore.matches.filter {
$0.round == previousRound.id && ($0.index == match.topPreviousRoundMatchIndex() || $0.index == match.bottomPreviousRoundMatchIndex())
}
@ -118,7 +115,7 @@ final class Round: ModelObject, Storable {
}
func seed(_ team: TeamPosition, inMatchIndex matchIndex: Int) -> TeamRegistration? {
return self.tournamentStore.teamRegistrations.first(where: {
return self.tournamentStore?.teamRegistrations.first(where: {
$0.bracketPosition != nil
&& ($0.bracketPosition! / 2) == matchIndex
&& ($0.bracketPosition! % 2) == team.rawValue
@ -126,7 +123,8 @@ final class Round: ModelObject, Storable {
}
func seeds(inMatchIndex matchIndex: Int) -> [TeamRegistration] {
return self.tournamentStore.teamRegistrations.filter {
guard let tournamentStore = self.tournamentStore else { return [] }
return tournamentStore.teamRegistrations.filter {
$0.tournament == tournament
&& $0.bracketPosition != nil
@ -141,9 +139,10 @@ final class Round: ModelObject, Storable {
}
func seeds() -> [TeamRegistration] {
guard let tournamentStore = self.tournamentStore else { return [] }
let initialMatchIndex = RoundRule.matchIndex(fromRoundIndex: index)
let numberOfMatches = RoundRule.numberOfMatches(forRoundIndex: index)
return self.tournamentStore.teamRegistrations.filter {
return tournamentStore.teamRegistrations.filter {
$0.bracketPosition != nil
&& ($0.bracketPosition! / 2) >= initialMatchIndex
&& ($0.bracketPosition! / 2) < initialMatchIndex + numberOfMatches
@ -162,12 +161,12 @@ final class Round: ModelObject, Storable {
func losers() -> [TeamRegistration] {
let teamIds: [String] = self._matches().compactMap { $0.losingTeamId }
return teamIds.compactMap { self.tournamentStore.teamRegistrations.findById($0) }
return teamIds.compactMap { self.tournamentStore?.teamRegistrations.findById($0) }
}
func winners() -> [TeamRegistration] {
let teamIds: [String] = self._matches().compactMap { $0.winningTeamId }
return teamIds.compactMap { self.tournamentStore.teamRegistrations.findById($0) }
return teamIds.compactMap { self.tournamentStore?.teamRegistrations.findById($0) }
}
func teams() -> [TeamRegistration] {
@ -192,24 +191,24 @@ defer {
return luckyLoser.team
} else if let previousMatch = topPreviousRoundMatch(ofMatch: match, previousRound: previousRound) {
if let teamId = previousMatch.winningTeamId {
return self.tournamentStore.teamRegistrations.findById(teamId)
return self.tournamentStore?.teamRegistrations.findById(teamId)
} else if previousMatch.disabled {
return previousMatch.teams().first
}
} else if let parent = upperBracketTopMatch(ofMatchIndex: match.index, previousRound: previousRound)?.losingTeamId {
return tournamentStore.findById(parent)
return self.store?.findById(parent)
}
case .two:
if let luckyLoser = match.teamScores.first(where: { $0.luckyLoser == match.index * 2 + 1 }) {
return luckyLoser.team
} else if let previousMatch = bottomPreviousRoundMatch(ofMatch: match, previousRound: previousRound) {
if let teamId = previousMatch.winningTeamId {
return self.tournamentStore.teamRegistrations.findById(teamId)
return self.tournamentStore?.teamRegistrations.findById(teamId)
} else if previousMatch.disabled {
return previousMatch.teams().first
}
} else if let parent = upperBracketBottomMatch(ofMatchIndex: match.index, previousRound: previousRound)?.losingTeamId {
return tournamentStore.findById(parent)
return self.store?.findById(parent)
}
}
@ -269,7 +268,7 @@ defer {
guard let previousRound else { return nil }
let topPreviousRoundMatchIndex = match.topPreviousRoundMatchIndex()
return self.tournamentStore.matches.first(where: {
return self.tournamentStore?.matches.first(where: {
$0.round == previousRound.id && $0.index == topPreviousRoundMatchIndex
})
}
@ -285,20 +284,21 @@ defer {
guard let previousRound else { return nil }
let bottomPreviousRoundMatchIndex = match.bottomPreviousRoundMatchIndex()
return self.tournamentStore.matches.first(where: {
return self.tournamentStore?.matches.first(where: {
$0.round == previousRound.id && $0.index == bottomPreviousRoundMatchIndex
})
}
func getMatch(atMatchIndexInRound matchIndexInRound: Int) -> Match? {
self.tournamentStore.matches.first(where: {
self.tournamentStore?.matches.first(where: {
let index = RoundRule.matchIndexWithinRound(fromMatchIndex: $0.index)
return $0.round == id && index == matchIndexInRound
})
}
func enabledMatches() -> [Match] {
return self.tournamentStore.matches.filter { $0.round == self.id && $0.disabled == false }.sorted(by: \.index)
guard let tournamentStore = self.tournamentStore else { return [] }
return tournamentStore.matches.filter { $0.round == self.id && $0.disabled == false }.sorted(by: \.index)
}
// func displayableMatches() -> [Match] {
@ -335,11 +335,11 @@ defer {
print("func previousRound of: ", id, duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
return self.tournamentStore.rounds.first(where: { $0.parent == parent && $0.index == index + 1 })
return self.tournamentStore?.rounds.first(where: { $0.parent == parent && $0.index == index + 1 })
}
func nextRound() -> Round? {
return self.tournamentStore.rounds.first(where: { $0.parent == parent && $0.index == index - 1 })
return self.tournamentStore?.rounds.first(where: { $0.parent == parent && $0.index == index - 1 })
}
func loserRounds(forRoundIndex roundIndex: Int) -> [Round] {
@ -406,11 +406,7 @@ defer {
// Logger.error(error)
// }
}
do {
try self.tournamentStore.matches.addOrUpdate(contentOfs: _matches)
} catch {
Logger.error(error)
}
self.tournamentStore?.matches.addOrUpdate(contentOfs: _matches)
}
var cumulativeMatchCount: Int {
@ -470,10 +466,13 @@ defer {
}
#endif
let initialMatchIndexFromRoundIndex = RoundRule.matchIndex(fromRoundIndex: index)
let seedsAfterThisRound: [TeamRegistration] = self.tournamentStore.teamRegistrations.filter {
var seedsAfterThisRound: [TeamRegistration] = []
if let tournamentStore = tournamentStore {
seedsAfterThisRound = tournamentStore.teamRegistrations.filter {
$0.bracketPosition != nil
&& ($0.bracketPosition! / 2) < initialMatchIndexFromRoundIndex
}
}
// let seedsAfterThisRound : [TeamRegistration] = Store.main.filter(isIncluded: {
// $0.tournament == tournament
@ -531,10 +530,13 @@ defer {
//print(seedInterval.localizedLabel())
return seedInterval
} else {
let seedsAfterThisRound : [TeamRegistration] = self.tournamentStore.teamRegistrations.filter {
var seedsAfterThisRound : [TeamRegistration] = []
if let tournamentStore = self.tournamentStore {
seedsAfterThisRound = tournamentStore.teamRegistrations.filter {
$0.bracketPosition != nil
&& ($0.bracketPosition! / 2) < initialMatchIndexFromRoundIndex
}
}
var seedsCount = seedsAfterThisRound.count
if seedsAfterThisRound.isEmpty {
@ -577,7 +579,7 @@ defer {
if let seedInterval = seedInterval(initialMode: initialMode) {
return seedInterval.localizedLabel(displayStyle)
}
print("Round pas trouvé", id, parent, index)
// print("Round pas trouvé", id, parent, index)
return "Match de classement"
}
return RoundRule.roundName(fromRoundIndex: index, displayStyle: displayStyle)
@ -587,11 +589,7 @@ defer {
let tournamentObject = tournamentObject()
if let tournamentObject, index == 0, isUpperBracket(), hasEnded() {
tournamentObject.endDate = Date()
do {
try DataStore.shared.tournaments.addOrUpdate(instance: tournamentObject)
} catch {
Logger.error(error)
}
DataStore.shared.tournaments.addOrUpdate(instance: tournamentObject)
}
tournamentObject?.updateTournamentState()
@ -609,7 +607,7 @@ defer {
}
func loserRounds() -> [Round] {
guard let tournamentStore = self.tournamentStore else { return [] }
#if _DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
@ -618,7 +616,7 @@ defer {
}
#endif
return self.tournamentStore.rounds.filter( { $0.parent == id }).sorted(by: \.index).reversed()
return tournamentStore.rounds.filter( { $0.parent == id }).sorted(by: \.index).reversed()
}
func loserRoundsAndChildren() -> [Round] {
@ -635,12 +633,8 @@ defer {
}
func deleteLoserBracket() {
do {
let loserRounds = loserRounds()
try self.tournamentStore.rounds.delete(contentOfs: loserRounds)
} catch {
Logger.error(error)
}
self.tournamentStore?.rounds.delete(contentOfs: loserRounds)
}
func buildLoserBracket() {
@ -659,26 +653,17 @@ defer {
round.parent = id //parent
return round
}
do {
try self.tournamentStore.rounds.addOrUpdate(contentOfs: rounds)
} catch {
Logger.error(error)
}
self.tournamentStore?.rounds.addOrUpdate(contentOfs: rounds)
let matchCount = RoundRule.numberOfMatches(forTeams: currentRoundMatchCount)
let matches = (0..<matchCount).map { //0 is final match
let roundIndex = RoundRule.roundIndex(fromMatchIndex: $0)
let round = rounds[roundIndex]
return Match(round: round.id, index: $0, matchFormat: loserBracketMatchFormat, name: round.roundTitle(initialMode: true))
return Match(round: round.id, index: $0, format: loserBracketMatchFormat, name: round.roundTitle(initialMode: true))
//initial mode let the roundTitle give a name without considering the playable match
}
do {
try self.tournamentStore.matches.addOrUpdate(contentOfs: matches)
} catch {
Logger.error(error)
}
self.tournamentStore?.matches.addOrUpdate(contentOfs: matches)
loserRounds().forEach { round in
round.buildLoserBracket()
@ -687,7 +672,7 @@ defer {
var parentRound: Round? {
guard let parent = parent else { return nil }
return self.tournamentStore.rounds.findById(parent)
return self.tournamentStore?.rounds.findById(parent)
}
func nextRoundsDisableMatches() -> Int {
@ -719,69 +704,71 @@ defer {
playedMatches.forEach { match in
match.matchFormat = updatedMatchFormat
}
do {
try self.tournamentStore.matches.addOrUpdate(contentOfs: playedMatches)
} catch {
Logger.error(error)
}
self.tournamentStore?.matches.addOrUpdate(contentOfs: playedMatches)
}
override func deleteDependencies() throws {
override func deleteDependencies() {
let matches = self._matches()
for match in matches {
try match.deleteDependencies()
match.deleteDependencies()
}
self.tournamentStore.matches.deleteDependencies(matches)
self.tournamentStore?.matches.deleteDependencies(matches)
let loserRounds = self.loserRounds()
for round in loserRounds {
try round.deleteDependencies()
}
self.tournamentStore.rounds.deleteDependencies(loserRounds)
round.deleteDependencies()
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _tournament = "tournament"
case _index = "index"
case _parent = "parent"
case _format = "format"
case _startDate = "startDate"
case _groupStageLoserBracket = "groupStageLoserBracket"
case _loserBracketMode = "loserBracketMode"
self.tournamentStore?.rounds.deleteDependencies(loserRounds)
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: ._id)
tournament = try container.decode(String.self, forKey: ._tournament)
index = try container.decode(Int.self, forKey: ._index)
parent = try container.decodeIfPresent(String.self, forKey: ._parent)
format = try container.decodeIfPresent(MatchFormat.self, forKey: ._format)
startDate = try container.decodeIfPresent(Date.self, forKey: ._startDate)
groupStageLoserBracket = try container.decodeIfPresent(Bool.self, forKey: ._groupStageLoserBracket) ?? false
loserBracketMode = try container.decodeIfPresent(LoserBracketMode.self, forKey: ._loserBracketMode) ?? .automatic
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: ._id)
try container.encode(tournament, forKey: ._tournament)
try container.encode(index, forKey: ._index)
try container.encode(groupStageLoserBracket, forKey: ._groupStageLoserBracket)
try container.encode(loserBracketMode, forKey: ._loserBracketMode)
try container.encode(parent, forKey: ._parent)
try container.encode(format, forKey: ._format)
try container.encode(startDate, forKey: ._startDate)
}
// enum CodingKeys: String, CodingKey {
// case _id = "id"
// case _storeId = "storeId"
// case _lastUpdate = "lastUpdate"
// case _tournament = "tournament"
// case _index = "index"
// case _parent = "parent"
// case _format = "format"
// case _startDate = "startDate"
// case _groupStageLoserBracket = "groupStageLoserBracket"
// case _loserBracketMode = "loserBracketMode"
// }
//
// required init(from decoder: Decoder) throws {
// let container = try decoder.container(keyedBy: CodingKeys.self)
// id = try container.decode(String.self, forKey: ._id)
// storeId = try container.decode(String.self, forKey: ._storeId)
// lastUpdate = try container.decodeIfPresent(Date.self, forKey: ._lastUpdate) ?? Date()
// tournament = try container.decode(String.self, forKey: ._tournament)
// index = try container.decode(Int.self, forKey: ._index)
// parent = try container.decodeIfPresent(String.self, forKey: ._parent)
// format = try container.decodeIfPresent(MatchFormat.self, forKey: ._format)
// startDate = try container.decodeIfPresent(Date.self, forKey: ._startDate)
// groupStageLoserBracket = try container.decodeIfPresent(Bool.self, forKey: ._groupStageLoserBracket) ?? false
// loserBracketMode = try container.decodeIfPresent(LoserBracketMode.self, forKey: ._loserBracketMode) ?? .automatic
// }
//
// func encode(to encoder: Encoder) throws {
// var container = encoder.container(keyedBy: CodingKeys.self)
//
// 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(index, forKey: ._index)
// try container.encode(groupStageLoserBracket, forKey: ._groupStageLoserBracket)
// try container.encode(loserBracketMode, forKey: ._loserBracketMode)
//
// try container.encode(parent, forKey: ._parent)
// try container.encode(format, forKey: ._format)
// try container.encode(startDate, forKey: ._startDate)
//
// }
func insertOnServer() {
self.tournamentStore.rounds.writeChangeAndInsertOnServer(instance: self)
self.tournamentStore?.rounds.writeChangeAndInsertOnServer(instance: self)
for match in self._matches() {
match.insertOnServer()
}
@ -789,11 +776,7 @@ defer {
}
extension Round: Selectable, Equatable {
static func == (lhs: Round, rhs: Round) -> Bool {
lhs.id == rhs.id
}
extension Round: Selectable {
func selectionLabel(index: Int) -> String {
if let parentRound {

@ -10,49 +10,51 @@ import LeStorage
import SwiftUI
@Observable
final class TeamRegistration: ModelObject, Storable {
static func resourceName() -> String { "team-registrations" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
static func filterByStoreIdentifier() -> Bool { return true }
static var relationshipNames: [String] = []
var id: String = Store.randomId()
var tournament: String
var groupStage: String?
var registrationDate: Date?
var callDate: Date?
var bracketPosition: Int?
var groupStagePosition: Int?
var comment: String?
var source: String?
var sourceValue: String?
var logo: String?
var name: String?
var walkOut: Bool = false
var wildCardBracket: Bool = false
var wildCardGroupStage: Bool = false
var weight: Int = 0
var lockedWeight: Int?
var confirmationDate: Date?
var qualified: Bool = false
var finalRanking: Int?
var pointsEarned: Int?
func hasRegisteredOnline() -> Bool {
players().anySatisfy({ $0.registeredOnline })
}
func unrankedOrUnknown() -> Bool {
players().anySatisfy({ $0.source == nil })
}
func isOutOfTournament() -> Bool {
walkOut
}
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, finalRanking: Int? = nil, pointsEarned: Int? = nil) {
final class TeamRegistration: BaseTeamRegistration, SideStorable {
// static func resourceName() -> String { "team-registrations" }
// static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
// static func filterByStoreIdentifier() -> Bool { return true }
// static var relationshipNames: [String] = []
//
// var id: String = Store.randomId()
// var lastUpdate: Date
// var tournament: String
// var groupStage: String?
// var registrationDate: Date?
// var callDate: Date?
// var bracketPosition: Int?
// var groupStagePosition: Int?
// var comment: String?
// var source: String?
// var sourceValue: String?
// var logo: String?
// var name: String?
//
// var walkOut: Bool = false
// var wildCardBracket: Bool = false
// var wildCardGroupStage: Bool = false
// var weight: Int = 0
// var lockedWeight: Int?
// var confirmationDate: Date?
// var qualified: Bool = false
// var finalRanking: 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
) {
super.init()
// self.storeId = tournament
self.tournament = tournament
self.groupStage = groupStage
self.registrationDate = registrationDate ?? Date()
@ -71,53 +73,63 @@ final class TeamRegistration: ModelObject, Storable {
self.lockedWeight = lockedWeight
self.confirmationDate = confirmationDate
self.qualified = qualified
self.finalRanking = finalRanking
self.pointsEarned = pointsEarned
}
var tournamentStore: TournamentStore {
return TournamentStore.instance(tournamentId: self.tournament)
func hasRegisteredOnline() -> Bool {
players().anySatisfy({ $0.registeredOnline })
}
func unrankedOrUnknown() -> Bool {
players().anySatisfy({ $0.source == nil })
}
func isOutOfTournament() -> Bool {
walkOut
}
required init(from decoder: any Decoder) throws {
try super.init(from: decoder)
}
var tournamentStore: TournamentStore? {
return TournamentLibrary.shared.store(tournamentId: self.tournament)
}
// MARK: - Computed dependencies
func unsortedPlayers() -> [PlayerRegistration] {
return self.tournamentStore.playerRegistrations.filter { $0.teamRegistration == self.id && $0.coach == false }
guard let tournamentStore = self.tournamentStore else { return [] }
return tournamentStore.playerRegistrations.filter {
$0.teamRegistration == self.id && $0.coach == false
}
}
// MARK: -
func deleteTeamScores() {
let ts = self.tournamentStore.teamScores.filter({ $0.teamRegistration == id })
do {
try self.tournamentStore.teamScores.delete(contentOfs: ts)
} catch {
Logger.error(error)
}
guard let tournamentStore = self.tournamentStore else { return }
let ts = tournamentStore.teamScores.filter({ $0.teamRegistration == id })
tournamentStore.teamScores.delete(contentOfs: ts)
}
override func deleteDependencies() throws {
override func deleteDependencies() {
let unsortedPlayers = unsortedPlayers()
for player in unsortedPlayers {
try player.deleteDependencies()
player.deleteDependencies()
}
self.tournamentStore.playerRegistrations.deleteDependencies(unsortedPlayers)
self.tournamentStore?.playerRegistrations.deleteDependencies(unsortedPlayers)
let teamScores = teamScores()
for teamScore in teamScores {
try teamScore.deleteDependencies()
teamScore.deleteDependencies()
}
self.tournamentStore.teamScores.deleteDependencies(teamScores)
self.tournamentStore?.teamScores.deleteDependencies(teamScores)
}
func hasArrived(isHere: Bool = false) {
let unsortedPlayers = unsortedPlayers()
unsortedPlayers.forEach({ $0.hasArrived = !isHere })
do {
try self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: unsortedPlayers)
} catch {
Logger.error(error)
}
self.tournamentStore?.playerRegistrations.addOrUpdate(contentOfs: unsortedPlayers)
}
func isHere() -> Bool {
@ -126,20 +138,21 @@ final class TeamRegistration: ModelObject, Storable {
return unsortedPlayers.allSatisfy({ $0.hasArrived })
}
func isSeedable() -> Bool {
bracketPosition == nil && groupStage == nil
}
func setSeedPosition(inSpot match: Match, slot: TeamPosition?, opposingSeeding: Bool) {
var teamPosition : TeamPosition {
var teamPosition: TeamPosition {
if let slot {
return slot
} else {
let matchIndex = match.index
let seedRound = RoundRule.roundIndex(fromMatchIndex: matchIndex)
let numberOfMatches = RoundRule.numberOfMatches(forRoundIndex: seedRound)
let isUpper = RoundRule.matchIndexWithinRound(fromMatchIndex: matchIndex) < (numberOfMatches / 2)
let isUpper =
RoundRule.matchIndexWithinRound(fromMatchIndex: matchIndex)
< (numberOfMatches / 2)
var teamPosition = slot ?? (isUpper ? .one : .two)
if opposingSeeding {
teamPosition = slot ?? (isUpper ? .two : .one)
@ -156,9 +169,11 @@ final class TeamRegistration: ModelObject, Storable {
}
if let tournament = tournamentObject() {
if let index = index(in: tournament.selectedSortedTeams()) {
let drawLog = DrawLog(tournament: tournament.id, drawSeed: index, drawMatchIndex: match.index, drawTeamPosition: teamPosition, drawType: .seed)
let drawLog = DrawLog(
tournament: tournament.id, drawSeed: index, drawMatchIndex: match.index,
drawTeamPosition: teamPosition, drawType: .seed)
do {
try tournamentStore.drawLogs.addOrUpdate(instance: drawLog)
try tournamentStore?.drawLogs.addOrUpdate(instance: drawLog)
} catch {
Logger.error(error)
}
@ -217,19 +232,23 @@ final class TeamRegistration: ModelObject, Storable {
}
func teamScores() -> [TeamScore] {
return self.tournamentStore.teamScores.filter({ $0.teamRegistration == id })
guard let tournamentStore = self.tournamentStore else { return [] }
return tournamentStore.teamScores.filter({ $0.teamRegistration == id })
}
func wins() -> [Match] {
return self.tournamentStore.matches.filter({ $0.winningTeamId == id })
guard let tournamentStore = self.tournamentStore else { return [] }
return tournamentStore.matches.filter({ $0.winningTeamId == id })
}
func loses() -> [Match] {
return self.tournamentStore.matches.filter({ $0.losingTeamId == id })
guard let tournamentStore = self.tournamentStore else { return [] }
return tournamentStore.matches.filter({ $0.losingTeamId == id })
}
func matches() -> [Match] {
return self.tournamentStore.matches.filter({ $0.losingTeamId == id || $0.winningTeamId == id })
guard let tournamentStore = self.tournamentStore else { return [] }
return tournamentStore.matches.filter({ $0.losingTeamId == id || $0.winningTeamId == id })
}
var tournamentCategory: TournamentCategory {
@ -244,7 +263,8 @@ final class TeamRegistration: ModelObject, Storable {
func hasMemberOfClub(_ codeClubOrClubName: String?) -> Bool {
guard let codeClubOrClubName else { return true }
return unsortedPlayers().anySatisfy({
$0.clubName?.contains(codeClubOrClubName) == true || $0.clubName?.contains(codeClubOrClubName) == true
$0.clubName?.contains(codeClubOrClubName) == true
|| $0.clubName?.contains(codeClubOrClubName) == true
})
}
@ -252,13 +272,19 @@ final class TeamRegistration: ModelObject, Storable {
self.setWeight(from: self.players(), inTournamentCategory: tournamentCategory)
}
func teamLabel(_ displayStyle: DisplayStyle = .wide, twoLines: Bool = false, separator: String = "&") -> String {
func teamLabel(
_ displayStyle: DisplayStyle = .wide, twoLines: Bool = false, separator: String = "&"
) -> String {
if let name { return name }
return players().map { $0.playerLabel(displayStyle) }.joined(separator: twoLines ? "\n" : " \(separator) ")
return players().map { $0.playerLabel(displayStyle) }.joined(
separator: twoLines ? "\n" : " \(separator) ")
}
func teamLabelRanked(displayRank: Bool, displayTeamName: Bool) -> String {
[displayTeamName ? name : nil, displayRank ? seedIndex() : nil, displayTeamName ? (name == nil ? teamLabel() : name) : teamLabel()].compactMap({ $0 }).joined(separator: " ")
[
displayTeamName ? name : nil, displayRank ? seedIndex() : nil,
displayTeamName ? (name == nil ? teamLabel() : name) : teamLabel(),
].compactMap({ $0 }).joined(separator: " ")
}
func seedIndex() -> String? {
@ -281,13 +307,17 @@ final class TeamRegistration: ModelObject, Storable {
}
func contains(_ searchField: String) -> Bool {
return unsortedPlayers().anySatisfy({ $0.contains(searchField) }) || self.name?.localizedCaseInsensitiveContains(searchField) == true
return unsortedPlayers().anySatisfy({ $0.contains(searchField) })
|| self.name?.localizedCaseInsensitiveContains(searchField) == true
}
func containsExactlyPlayerLicenses(_ playerLicenses: [String?]) -> Bool {
let arrayOfIds : [String] = unsortedPlayers().compactMap({ $0.licenceId?.strippedLicense?.canonicalVersion })
let ids : Set<String> = Set<String>(arrayOfIds.sorted())
let searchedIds = Set<String>(playerLicenses.compactMap({ $0?.strippedLicense?.canonicalVersion }).sorted())
let arrayOfIds: [String] = unsortedPlayers().compactMap({
$0.licenceId?.strippedLicense?.canonicalVersion
})
let ids: Set<String> = Set<String>(arrayOfIds.sorted())
let searchedIds = Set<String>(
playerLicenses.compactMap({ $0?.strippedLicense?.canonicalVersion }).sorted())
if ids.isEmpty || searchedIds.isEmpty { return false }
return ids.hashValue == searchedIds.hashValue
}
@ -312,7 +342,8 @@ final class TeamRegistration: ModelObject, Storable {
let unsortedPlayers = unsortedPlayers()
if unsortedPlayers.isEmpty { return false }
return matches().isEmpty == false || unsortedPlayers.allSatisfy({ $0.hasPaid() || $0.hasArrived })
return matches().isEmpty == false
|| unsortedPlayers.allSatisfy({ $0.hasPaid() || $0.hasArrived })
}
func availableForSeedPick() -> Bool {
@ -349,14 +380,15 @@ final class TeamRegistration: ModelObject, Storable {
}
func resetGroupeStagePosition() {
guard let tournamentStore = self.tournamentStore else { return }
if let groupStage {
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) })
do {
try tournamentStore.teamScores.delete(contentOfs: teamScores)
} catch {
Logger.error(error)
let matches = tournamentStore.matches.filter({ $0.groupStage == groupStage }).map {
$0.id
}
let teamScores = tournamentStore.teamScores.filter({
$0.teamRegistration == id && matches.contains($0.match)
})
tournamentStore.teamScores.delete(contentOfs: teamScores)
}
//groupStageObject()?._matches().forEach({ $0.updateTeamScores() })
groupStage = nil
@ -364,13 +396,12 @@ final class TeamRegistration: ModelObject, Storable {
}
func resetBracketPosition() {
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) })
do {
try tournamentStore.teamScores.delete(contentOfs: teamScores)
} catch {
Logger.error(error)
}
guard let tournamentStore = self.tournamentStore else { return }
let matches = tournamentStore.matches.filter({ $0.groupStage == nil }).map { $0.id }
let teamScores = tournamentStore.teamScores.filter({
$0.teamRegistration == id && matches.contains($0.match)
})
tournamentStore.teamScores.delete(contentOfs: teamScores)
self.bracketPosition = nil
}
@ -383,9 +414,13 @@ final class TeamRegistration: ModelObject, Storable {
func pasteData(_ exportFormat: ExportFormat = .rawText, _ index: Int = 0) -> String {
switch exportFormat {
case .rawText:
return [playersPasteData(exportFormat), formattedInscriptionDate(exportFormat), name].compactMap({ $0 }).joined(separator: exportFormat.newLineSeparator())
return [playersPasteData(exportFormat), formattedInscriptionDate(exportFormat), name]
.compactMap({ $0 }).joined(separator: exportFormat.newLineSeparator())
case .csv:
return [index.formatted(), playersPasteData(exportFormat), isWildCard() ? "WC" : weight.formatted()].joined(separator: exportFormat.separator())
return [
index.formatted(), playersPasteData(exportFormat),
isWildCard() ? "WC" : weight.formatted(),
].joined(separator: exportFormat.separator())
}
}
@ -396,7 +431,8 @@ final class TeamRegistration: ModelObject, Storable {
func formattedInscriptionDate(_ exportFormat: ExportFormat = .rawText) -> String? {
guard let registrationDate else { return nil }
let formattedDate = registrationDate.formatted(.dateTime.weekday().day().month().hour().minute())
let formattedDate = registrationDate.formatted(
.dateTime.weekday().day().month().hour().minute())
let onlineSuffix = hasRegisteredOnline() ? " en ligne" : ""
switch exportFormat {
@ -412,7 +448,8 @@ final class TeamRegistration: ModelObject, Storable {
switch exportFormat {
case .rawText:
if let callDate {
return "Convoqué le " + callDate.formatted(.dateTime.weekday().day().month().hour().minute())
return "Convoqué le "
+ callDate.formatted(.dateTime.weekday().day().month().hour().minute())
} else {
return nil
}
@ -428,18 +465,27 @@ final class TeamRegistration: ModelObject, Storable {
func playersPasteData(_ exportFormat: ExportFormat = .rawText) -> String {
switch exportFormat {
case .rawText:
return players().map { $0.pasteData(exportFormat) }.joined(separator: exportFormat.newLineSeparator())
return players().map { $0.pasteData(exportFormat) }.joined(
separator: exportFormat.newLineSeparator())
case .csv:
return players().map { [$0.pasteData(exportFormat), isWildCard() ? "WC" : $0.computedRank.formatted() ].joined(separator: exportFormat.separator()) }.joined(separator: exportFormat.separator())
return players().map {
[$0.pasteData(exportFormat), isWildCard() ? "WC" : $0.computedRank.formatted()]
.joined(separator: exportFormat.separator())
}.joined(separator: exportFormat.separator())
}
}
func updatePlayers(_ players: Set<PlayerRegistration>, inTournamentCategory tournamentCategory: TournamentCategory) {
func updatePlayers(
_ players: Set<PlayerRegistration>,
inTournamentCategory tournamentCategory: TournamentCategory
) {
let previousPlayers = Set(unsortedPlayers())
players.forEach { player in
previousPlayers.forEach { oldPlayer in
if player.licenceId?.strippedLicense == oldPlayer.licenceId?.strippedLicense, player.licenceId?.strippedLicense != nil {
if player.licenceId?.strippedLicense == oldPlayer.licenceId?.strippedLicense,
player.licenceId?.strippedLicense != nil
{
player.registeredOnline = oldPlayer.registeredOnline
player.coach = oldPlayer.coach
player.tournamentPlayed = oldPlayer.tournamentPlayed
@ -451,24 +497,19 @@ final class TeamRegistration: ModelObject, Storable {
}
}
let playersToRemove = previousPlayers.subtracting(players)
do {
try self.tournamentStore.playerRegistrations.delete(contentOfs: playersToRemove)
} catch {
Logger.error(error)
}
self.tournamentStore?.playerRegistrations.delete(contentOfs: Array(playersToRemove))
setWeight(from: Array(players), inTournamentCategory: tournamentCategory)
players.forEach { player in
player.teamRegistration = id
}
// do {
// try self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players)
// } catch {
// Logger.error(error)
// }
// do {
// try self.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players)
// } catch {
// Logger.error(error)
// }
}
typealias TeamRange = (left: TeamRegistration?, right: TeamRegistration?)
@ -490,14 +531,18 @@ final class TeamRegistration: ModelObject, Storable {
if groupStagePosition == 0 {
left = tournamentObject.seeds().last
} else {
let previousHat = selectedSortedTeams.filter({ $0.groupStagePosition == groupStagePosition - 1 }).sorted(by: \.weight)
let previousHat = selectedSortedTeams.filter({
$0.groupStagePosition == groupStagePosition - 1
}).sorted(by: \.weight)
left = previousHat.last
}
var right: TeamRegistration? = nil
if groupStagePosition == tournamentObject.teamsPerGroupStage - 1 {
right = nil
} else {
let previousHat = selectedSortedTeams.filter({ $0.groupStagePosition == groupStagePosition + 1 }).sorted(by: \.weight)
let previousHat = selectedSortedTeams.filter({
$0.groupStagePosition == groupStagePosition + 1
}).sorted(by: \.weight)
right = previousHat.first
}
return (left: left, right: right)
@ -511,8 +556,8 @@ final class TeamRegistration: ModelObject, Storable {
let predicates: [AreInIncreasingOrder] = [
{ $0.sex?.rawValue ?? 0 < $1.sex?.rawValue ?? 0 },
{ $0.rank ?? Int.max < $1.rank ?? Int.max },
{ $0.lastName < $1.lastName},
{ $0.firstName < $1.firstName }
{ $0.lastName < $1.lastName },
{ $0.firstName < $1.firstName },
]
for predicate in predicates {
@ -528,10 +573,14 @@ final class TeamRegistration: ModelObject, Storable {
}
func coaches() -> [PlayerRegistration] {
tournamentStore.playerRegistrations.filter({ $0.coach })
guard let store = self.tournamentStore else { return [] }
return store.playerRegistrations.filter { $0.coach }
}
func setWeight(from players: [PlayerRegistration], inTournamentCategory tournamentCategory: TournamentCategory) {
func setWeight(
from players: [PlayerRegistration],
inTournamentCategory tournamentCategory: TournamentCategory
) {
let significantPlayerCount = significantPlayerCount()
let sortedPlayers = players.sorted(by: \.computedRank, order: .ascending)
weight = (sortedPlayers.prefix(significantPlayerCount).map { $0.computedRank } + missingPlayerType(inTournamentCategory: tournamentCategory).map { unrankValue(for: $0 == 1 ? true : false ) }).prefix(significantPlayerCount).reduce(0,+)
@ -560,24 +609,25 @@ final class TeamRegistration: ModelObject, Storable {
func groupStageObject() -> GroupStage? {
guard let groupStage else { return nil }
return self.tournamentStore.groupStages.findById(groupStage)
return self.tournamentStore?.groupStages.findById(groupStage)
}
func initialRound() -> Round? {
guard let bracketPosition else { return nil }
let roundIndex = RoundRule.roundIndex(fromMatchIndex: bracketPosition / 2)
return self.tournamentStore.rounds.first(where: { $0.index == roundIndex })
return self.tournamentStore?.rounds.first(where: { $0.index == roundIndex })
}
func initialMatch() -> Match? {
guard let bracketPosition else { return nil }
guard let initialRoundObject = initialRound() else { return nil }
return self.tournamentStore.matches.first(where: { $0.round == initialRoundObject.id && $0.index == bracketPosition / 2 })
return self.tournamentStore?.matches.first(where: {
$0.round == initialRoundObject.id && $0.index == bracketPosition / 2
})
}
func toggleSummonConfirmation() {
if confirmationDate == nil { confirmationDate = Date() }
else { confirmationDate = nil }
if confirmationDate == nil { confirmationDate = Date() } else { confirmationDate = nil }
}
func didConfirmSummon() -> Bool {
@ -611,7 +661,9 @@ final class TeamRegistration: ModelObject, Storable {
func restingTime() -> Date? {
if let _cachedRestingTime { return _cachedRestingTime.1 }
let restingTime = matches().filter({ $0.hasEnded() }).sorted(by: \.computedEndDateForSorting).last?.endDate
let restingTime = matches().filter({ $0.hasEnded() }).sorted(
by: \.computedEndDateForSorting
).last?.endDate
_cachedRestingTime = (true, restingTime)
return restingTime
}
@ -691,86 +743,8 @@ final class TeamRegistration: ModelObject, Storable {
return nil
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _tournament = "tournament"
case _groupStage = "groupStage"
case _registrationDate = "registrationDate"
case _callDate = "callDate"
case _bracketPosition = "bracketPosition"
case _groupStagePosition = "groupStagePosition"
case _comment = "comment"
case _source = "source"
case _sourceValue = "sourceValue"
case _logo = "logo"
case _name = "name"
case _wildCardBracket = "wildCardBracket"
case _wildCardGroupStage = "wildCardGroupStage"
case _weight = "weight"
case _walkOut = "walkOut"
case _lockedWeight = "lockedWeight"
case _confirmationDate = "confirmationDate"
case _qualified = "qualified"
case _finalRanking = "finalRanking"
case _pointsEarned = "pointsEarned"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// Non-optional properties
id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId()
tournament = try container.decode(String.self, forKey: ._tournament)
walkOut = try container.decodeIfPresent(Bool.self, forKey: ._walkOut) ?? false
wildCardBracket = try container.decodeIfPresent(Bool.self, forKey: ._wildCardBracket) ?? false
wildCardGroupStage = try container.decodeIfPresent(Bool.self, forKey: ._wildCardGroupStage) ?? false
weight = try container.decodeIfPresent(Int.self, forKey: ._weight) ?? 0
qualified = try container.decodeIfPresent(Bool.self, forKey: ._qualified) ?? false
// Optional properties
groupStage = try container.decodeIfPresent(String.self, forKey: ._groupStage)
registrationDate = try container.decodeIfPresent(Date.self, forKey: ._registrationDate)
callDate = try container.decodeIfPresent(Date.self, forKey: ._callDate)
bracketPosition = try container.decodeIfPresent(Int.self, forKey: ._bracketPosition)
groupStagePosition = try container.decodeIfPresent(Int.self, forKey: ._groupStagePosition)
comment = try container.decodeIfPresent(String.self, forKey: ._comment)
source = try container.decodeIfPresent(String.self, forKey: ._source)
sourceValue = try container.decodeIfPresent(String.self, forKey: ._sourceValue)
logo = try container.decodeIfPresent(String.self, forKey: ._logo)
name = try container.decodeIfPresent(String.self, forKey: ._name)
lockedWeight = try container.decodeIfPresent(Int.self, forKey: ._lockedWeight)
confirmationDate = try container.decodeIfPresent(Date.self, forKey: ._confirmationDate)
finalRanking = try container.decodeIfPresent(Int.self, forKey: ._finalRanking)
pointsEarned = try container.decodeIfPresent(Int.self, forKey: ._pointsEarned)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: ._id)
try container.encode(tournament, forKey: ._tournament)
try container.encode(groupStage, forKey: ._groupStage)
try container.encode(registrationDate, forKey: ._registrationDate)
try container.encode(callDate, forKey: ._callDate)
try container.encode(bracketPosition, forKey: ._bracketPosition)
try container.encode(groupStagePosition, forKey: ._groupStagePosition)
try container.encode(comment, forKey: ._comment)
try container.encode(source, forKey: ._source)
try container.encode(sourceValue, forKey: ._sourceValue)
try container.encode(logo, forKey: ._logo)
try container.encode(name, forKey: ._name)
try container.encode(walkOut, forKey: ._walkOut)
try container.encode(wildCardBracket, forKey: ._wildCardBracket)
try container.encode(wildCardGroupStage, forKey: ._wildCardGroupStage)
try container.encode(weight, forKey: ._weight)
try container.encode(lockedWeight, forKey: ._lockedWeight)
try container.encode(confirmationDate, forKey: ._confirmationDate)
try container.encode(qualified, forKey: ._qualified)
try container.encode(finalRanking, forKey: ._finalRanking)
try container.encode(pointsEarned, forKey: ._pointsEarned)
}
func insertOnServer() {
self.tournamentStore.teamRegistrations.writeChangeAndInsertOnServer(instance: self)
self.tournamentStore?.teamRegistrations.writeChangeAndInsertOnServer(instance: self)
for playerRegistration in self.unsortedPlayers() {
playerRegistration.insertOnServer()
}
@ -778,16 +752,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 {
case beachPadel
}

@ -9,59 +9,70 @@ import Foundation
import LeStorage
@Observable
final class TeamScore: ModelObject, Storable {
final class TeamScore: BaseTeamScore, SideStorable {
static func resourceName() -> String { "team-scores" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
static func filterByStoreIdentifier() -> Bool { return true }
static var relationshipNames: [String] = ["match"]
var id: String = Store.randomId()
var match: String
var teamRegistration: String?
//var playerRegistrations: [String] = []
var score: String?
var walkOut: Int?
var luckyLoser: Int?
// static func resourceName() -> String { "team-scores" }
// static func tokenExemptedMethods() -> [HTTPMethod] { return [] }
// static func filterByStoreIdentifier() -> Bool { return true }
// static var relationshipNames: [String] = ["match"]
//
// var id: String = Store.randomId()
// var lastUpdate: Date
// var match: String
// var teamRegistration: String?
// //var playerRegistrations: [String] = []
// var score: String?
// var walkOut: Int?
// var luckyLoser: Int?
//
// var storeId: String? = nil
init(match: String, teamRegistration: String? = nil, score: String? = nil, walkOut: Int? = nil, luckyLoser: Int? = nil) {
self.match = match
self.teamRegistration = teamRegistration
// self.playerRegistrations = playerRegistrations
self.score = score
self.walkOut = walkOut
self.luckyLoser = luckyLoser
super.init(match: match, teamRegistration: teamRegistration, score: score, walkOut: walkOut, luckyLoser: luckyLoser)
// self.match = match
// self.teamRegistration = teamRegistration
//// self.playerRegistrations = playerRegistrations
// self.score = score
// self.walkOut = walkOut
// self.luckyLoser = luckyLoser
}
init(match: String, team: TeamRegistration?) {
self.match = match
super.init(match: match)
if let team {
self.teamRegistration = team.id
//self.playerRegistrations = team.players().map { $0.id }
}
self.score = nil
self.walkOut = nil
self.luckyLoser = nil
}
var tournamentStore: TournamentStore {
if let store = self.store as? TournamentStore {
return store
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
}
var tournamentStore: TournamentStore? {
guard let storeId else {
fatalError("missing store id for \(String(describing: type(of: self)))")
}
fatalError("missing store for \(String(describing: type(of: self)))")
return TournamentLibrary.shared.store(tournamentId: storeId)
//
// if let store = self.store as? TournamentStore {
// return store
// }
// fatalError("missing store for \(String(describing: type(of: self)))")
}
// MARK: - Computed dependencies
func matchObject() -> Match? {
return self.tournamentStore.matches.findById(self.match)
return self.tournamentStore?.matches.findById(self.match)
}
var team: TeamRegistration? {
guard let teamRegistration else {
return nil
}
return self.tournamentStore.teamRegistrations.findById(teamRegistration)
return self.tournamentStore?.teamRegistrations.findById(teamRegistration)
}
// MARK: -
@ -70,29 +81,33 @@ final class TeamScore: ModelObject, Storable {
return walkOut != nil
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _match = "match"
case _teamRegistration = "teamRegistration"
//case _playerRegistrations = "playerRegistrations"
case _score = "score"
case _walkOut = "walkOut"
case _luckyLoser = "luckyLoser"
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: ._id)
try container.encode(match, forKey: ._match)
try container.encode(teamRegistration, forKey: ._teamRegistration)
try container.encode(score, forKey: ._score)
try container.encode(walkOut, forKey: ._walkOut)
try container.encode(luckyLoser, forKey: ._luckyLoser)
}
// enum CodingKeys: String, CodingKey {
// case _id = "id"
// case _storeId = "storeId"
// case _lastUpdate = "lastUpdate"
// case _match = "match"
// case _teamRegistration = "teamRegistration"
// //case _playerRegistrations = "playerRegistrations"
// case _score = "score"
// case _walkOut = "walkOut"
// case _luckyLoser = "luckyLoser"
// }
//
// func encode(to encoder: Encoder) throws {
// var container = encoder.container(keyedBy: CodingKeys.self)
//
// 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(teamRegistration, forKey: ._teamRegistration)
// try container.encode(score, forKey: ._score)
// try container.encode(walkOut, forKey: ._walkOut)
// try container.encode(luckyLoser, forKey: ._luckyLoser)
// }
func insertOnServer() {
self.tournamentStore.teamScores.writeChangeAndInsertOnServer(instance: self)
self.tournamentStore?.teamScores.writeChangeAndInsertOnServer(instance: self)
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,33 @@
//
// TournamentLibrary.swift
// PadelClub
//
// Created by Laurent Morvillier on 11/11/2024.
//
import Foundation
import LeStorage
class TournamentLibrary {
static let shared: TournamentLibrary = TournamentLibrary()
fileprivate var _stores: [String : TournamentStore] = [:]
func store(tournamentId: String) -> TournamentStore? {
guard let tournament = DataStore.shared.tournaments.first(where: { $0.id == tournamentId }) else { return nil }
if let store = self._stores[tournamentId] {
return store
}
let store = StoreCenter.main.store(identifier: tournamentId)
let tournamentStore = TournamentStore(store: store)
self._stores[tournamentId] = tournamentStore
return tournamentStore
}
func reset() {
self._stores.removeAll()
}
}

@ -9,14 +9,9 @@ import Foundation
import LeStorage
import SwiftUI
class TournamentStore: Store, ObservableObject {
class TournamentStore: ObservableObject {
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")
}
var store: Store
fileprivate(set) var groupStages: StoredCollection<GroupStage> = StoredCollection.placeholder()
fileprivate(set) var matches: StoredCollection<Match> = StoredCollection.placeholder()
@ -28,13 +23,18 @@ class TournamentStore: Store, ObservableObject {
fileprivate(set) var matchSchedulers: StoredCollection<MatchScheduler> = StoredCollection.placeholder()
fileprivate(set) var drawLogs: StoredCollection<DrawLog> = StoredCollection.placeholder()
convenience init(tournament: Tournament) {
self.init(identifier: tournament.id, parameter: "tournament")
}
// convenience init(tournament: Tournament) {
// let store = StoreCenter.main.store(identifier: tournament.id)
// self.init(store: store)
// self._initialize()
// }
required init(identifier: String, parameter: String) {
init(store: Store) {
self.store = store
self._initialize()
}
super.init(identifier: identifier, parameter: parameter)
fileprivate func _initialize() {
var synchronized: Bool = true
let indexed: Bool = true
@ -45,17 +45,31 @@ class TournamentStore: Store, ObservableObject {
}
#endif
self.groupStages = self.registerCollection(synchronized: synchronized, indexed: indexed)
self.rounds = self.registerCollection(synchronized: synchronized, indexed: indexed)
self.teamRegistrations = self.registerCollection(synchronized: synchronized, indexed: indexed)
self.playerRegistrations = self.registerCollection(synchronized: synchronized, indexed: indexed)
self.matches = self.registerCollection(synchronized: synchronized, indexed: indexed)
self.teamScores = self.registerCollection(synchronized: synchronized, indexed: indexed)
self.matchSchedulers = self.registerCollection(synchronized: false, indexed: indexed)
self.drawLogs = self.registerCollection(synchronized: synchronized, indexed: indexed)
self.groupStages = self.store.registerSynchronizedCollection(indexed: indexed)
self.rounds = self.store.registerSynchronizedCollection(indexed: indexed)
self.teamRegistrations = self.store.registerSynchronizedCollection(indexed: indexed)
self.playerRegistrations = self.store.registerSynchronizedCollection(indexed: indexed)
self.matches = self.store.registerSynchronizedCollection(indexed: indexed)
self.teamScores = self.store.registerSynchronizedCollection(indexed: indexed)
self.matchSchedulers = self.store.registerCollection(indexed: indexed)
self.drawLogs = self.store.registerCollection(indexed: indexed)
self.loadCollectionsFromServerIfNoFile()
self.store.loadCollectionsFromServerIfNoFile()
NotificationCenter.default.addObserver(
self,
selector: #selector(_leStorageDidSynchronize),
name: NSNotification.Name.LeStorageDidSynchronize,
object: nil)
}
@objc func _leStorageDidSynchronize(notification: Notification) {
// Logger.log("SYNCED > teamRegistrations count = \(self.teamRegistrations.count)")
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}

@ -1,250 +0,0 @@
//
// User.swift
// PadelClub
//
// Created by Laurent Morvillier on 21/02/2024.
//
import Foundation
import LeStorage
enum UserRight: Int, Codable {
case none = 0
case edition = 1
case creation = 2
}
@Observable
class User: ModelObject, UserBase, Storable {
static func resourceName() -> String { "users" }
static func tokenExemptedMethods() -> [HTTPMethod] { return [.post] }
static func filterByStoreIdentifier() -> Bool { return false }
static var relationshipNames: [String] = []
public var id: String = Store.randomId()
public var username: String
public var email: String
var clubs: [String] = []
var umpireCode: String?
var licenceId: String?
var firstName: String
var lastName: String
var phone: String?
var country: String?
var summonsMessageBody : String? = nil
var summonsMessageSignature: String? = nil
var summonsAvailablePaymentMethods: String? = nil
var summonsDisplayFormat: Bool = false
var summonsDisplayEntryFee: Bool = false
var summonsUseFullCustomMessage: Bool = false
var matchFormatsDefaultDuration: [MatchFormat: Int]? = nil
var bracketMatchFormatPreference: MatchFormat?
var groupStageMatchFormatPreference: MatchFormat?
var loserBracketMatchFormatPreference: MatchFormat?
var loserBracketMode: LoserBracketMode = .automatic
var deviceId: String?
init(username: String, email: String, firstName: String, lastName: String, phone: String?, country: String?, loserBracketMode: LoserBracketMode = .automatic) {
self.username = username
self.firstName = firstName
self.lastName = lastName
self.email = email
self.phone = phone
self.country = country
self.loserBracketMode = loserBracketMode
}
public func uuid() throws -> UUID {
if let uuid = UUID(uuidString: self.id) {
return uuid
}
throw UUIDError.cantConvertString(string: self.id)
}
func currentPlayerData() -> ImportedPlayer? {
guard let licenceId else { return nil }
let federalContext = PersistenceController.shared.localContainer.viewContext
let fetchRequest = ImportedPlayer.fetchRequest()
let predicate = NSPredicate(format: "license == %@", licenceId)
fetchRequest.predicate = predicate
return try? federalContext.fetch(fetchRequest).first
}
func defaultSignature() -> String {
return "Sportivement,\n\(firstName) \(lastName), votre JAP."
}
func fullName() -> String? {
guard firstName.isEmpty == false && lastName.isEmpty == false else {
return nil
}
return "\(firstName) \(lastName)"
}
func hasTenupClubs() -> Bool {
self.clubsObjects().filter({ $0.code != nil }).isEmpty == false
}
func hasFavoriteClubsAndCreatedClubs() -> Bool {
clubsObjects(includeCreated: true).isEmpty == false
}
func setUserClub(_ userClub: Club) {
self.clubs.insert(userClub.id, at: 0)
}
func clubsObjects(includeCreated: Bool = false) -> [Club] {
return DataStore.shared.clubs.filter({ (includeCreated && $0.creator == id) || clubs.contains($0.id) })
}
func createdClubsObjectsNotFavorite() -> [Club] {
return DataStore.shared.clubs.filter({ ($0.creator == id) && clubs.contains($0.id) == false })
}
func saveMatchFormatsDefaultDuration(_ matchFormat: MatchFormat, estimatedDuration: Int) {
if estimatedDuration == matchFormat.defaultEstimatedDuration {
matchFormatsDefaultDuration?.removeValue(forKey: matchFormat)
} else {
matchFormatsDefaultDuration = matchFormatsDefaultDuration ?? [MatchFormat: Int]()
matchFormatsDefaultDuration?[matchFormat] = estimatedDuration
}
}
func addClub(_ club: Club) {
if !self.clubs.contains(where: { $0.id == club.id }) {
self.clubs.append(club.id)
}
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _username = "username"
case _email = "email"
case _clubs = "clubs"
case _umpireCode = "umpireCode"
case _licenceId = "licenceId"
case _firstName = "firstName"
case _lastName = "lastName"
case _phone = "phone"
case _country = "country"
case _summonsMessageBody = "summonsMessageBody"
case _summonsMessageSignature = "summonsMessageSignature"
case _summonsAvailablePaymentMethods = "summonsAvailablePaymentMethods"
case _summonsDisplayFormat = "summonsDisplayFormat"
case _summonsDisplayEntryFee = "summonsDisplayEntryFee"
case _summonsUseFullCustomMessage = "summonsUseFullCustomMessage"
case _matchFormatsDefaultDuration = "matchFormatsDefaultDuration"
case _bracketMatchFormatPreference = "bracketMatchFormatPreference"
case _groupStageMatchFormatPreference = "groupStageMatchFormatPreference"
case _loserBracketMatchFormatPreference = "loserBracketMatchFormatPreference"
case _deviceId = "deviceId"
case _loserBracketMode = "loserBracketMode"
}
public required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// Required properties
id = try container.decodeIfPresent(String.self, forKey: ._id) ?? Store.randomId()
username = try container.decode(String.self, forKey: ._username)
email = try container.decode(String.self, forKey: ._email)
firstName = try container.decode(String.self, forKey: ._firstName)
lastName = try container.decode(String.self, forKey: ._lastName)
// Optional properties
clubs = try container.decodeIfPresent([String].self, forKey: ._clubs) ?? []
umpireCode = try container.decodeIfPresent(String.self, forKey: ._umpireCode)
licenceId = try container.decodeIfPresent(String.self, forKey: ._licenceId)
phone = try container.decodeIfPresent(String.self, forKey: ._phone)
country = try container.decodeIfPresent(String.self, forKey: ._country)
// Summons-related properties
summonsMessageBody = try container.decodeIfPresent(String.self, forKey: ._summonsMessageBody)
summonsMessageSignature = try container.decodeIfPresent(String.self, forKey: ._summonsMessageSignature)
summonsAvailablePaymentMethods = try container.decodeIfPresent(String.self, forKey: ._summonsAvailablePaymentMethods)
summonsDisplayFormat = try container.decodeIfPresent(Bool.self, forKey: ._summonsDisplayFormat) ?? false
summonsDisplayEntryFee = try container.decodeIfPresent(Bool.self, forKey: ._summonsDisplayEntryFee) ?? false
summonsUseFullCustomMessage = try container.decodeIfPresent(Bool.self, forKey: ._summonsUseFullCustomMessage) ?? false
// Match-related properties
matchFormatsDefaultDuration = try container.decodeIfPresent([MatchFormat: Int].self, forKey: ._matchFormatsDefaultDuration)
bracketMatchFormatPreference = try container.decodeIfPresent(MatchFormat.self, forKey: ._bracketMatchFormatPreference)
groupStageMatchFormatPreference = try container.decodeIfPresent(MatchFormat.self, forKey: ._groupStageMatchFormatPreference)
loserBracketMatchFormatPreference = try container.decodeIfPresent(MatchFormat.self, forKey: ._loserBracketMatchFormatPreference)
loserBracketMode = try container.decodeIfPresent(LoserBracketMode.self, forKey: ._loserBracketMode) ?? .automatic
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: ._id)
try container.encode(username, forKey: ._username)
try container.encode(email, forKey: ._email)
try container.encode(clubs, forKey: ._clubs)
try container.encode(umpireCode, forKey: ._umpireCode)
try container.encode(licenceId, forKey: ._licenceId)
try container.encode(firstName, forKey: ._firstName)
try container.encode(lastName, forKey: ._lastName)
try container.encode(phone, forKey: ._phone)
try container.encode(country, forKey: ._country)
try container.encode(summonsMessageBody, forKey: ._summonsMessageBody)
try container.encode(summonsMessageSignature, forKey: ._summonsMessageSignature)
try container.encode(summonsAvailablePaymentMethods, forKey: ._summonsAvailablePaymentMethods)
try container.encode(summonsDisplayFormat, forKey: ._summonsDisplayFormat)
try container.encode(summonsDisplayEntryFee, forKey: ._summonsDisplayEntryFee)
try container.encode(summonsUseFullCustomMessage, forKey: ._summonsUseFullCustomMessage)
try container.encode(matchFormatsDefaultDuration, forKey: ._matchFormatsDefaultDuration)
try container.encode(bracketMatchFormatPreference, forKey: ._bracketMatchFormatPreference)
try container.encode(groupStageMatchFormatPreference, forKey: ._groupStageMatchFormatPreference)
try container.encode(loserBracketMatchFormatPreference, forKey: ._loserBracketMatchFormatPreference)
try container.encode(deviceId, forKey: ._deviceId)
try container.encode(loserBracketMode, forKey: ._loserBracketMode)
}
static func placeHolder() -> User {
return User(username: "", email: "", firstName: "", lastName: "", phone: nil, country: nil)
}
}
class UserCreationForm: User, UserPasswordBase {
init(user: User, username: String, password: String, firstName: String, lastName: String, email: String, phone: String?, country: String?) {
self.password = password
super.init(username: username, email: email, firstName: firstName, lastName: lastName, phone: phone, country: country)
self.summonsMessageBody = user.summonsMessageBody
self.summonsMessageSignature = user.summonsMessageSignature
self.summonsAvailablePaymentMethods = user.summonsAvailablePaymentMethods
self.summonsDisplayFormat = user.summonsDisplayFormat
self.summonsDisplayEntryFee = user.summonsDisplayEntryFee
self.summonsUseFullCustomMessage = user.summonsUseFullCustomMessage
self.matchFormatsDefaultDuration = user.matchFormatsDefaultDuration
self.bracketMatchFormatPreference = user.bracketMatchFormatPreference
self.groupStageMatchFormatPreference = user.groupStageMatchFormatPreference
self.loserBracketMatchFormatPreference = user.loserBracketMatchFormatPreference
}
required init(from decoder: Decoder) throws {
fatalError("init(from:) has not been implemented")
}
public var password: String
private enum CodingKeys: String, CodingKey {
case password
}
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.password, forKey: .password)
}
}

@ -27,13 +27,17 @@ extension KeyedDecodingContainer {
extension KeyedEncodingContainer {
mutating func encodeAndEncrypt(_ value: Data, forKey key: Key) throws {
let encryped: Data = try value.encrypt(pass: CryptoKey.pass.rawValue)
try self.encode(encryped, forKey: key)
}
mutating func encodeAndEncryptIfPresent(_ value: Data?, forKey key: Key) throws {
guard let value else {
try encodeNil(forKey: key)
return
}
let encryped: Data = try value.encrypt(pass: CryptoKey.pass.rawValue)
try self.encode(encryped, forKey: key)
try self.encodeAndEncrypt(value, forKey: key)
}
}

@ -13,4 +13,8 @@ extension NumberFormatter {
formatter.numberStyle = .ordinal
return formatter
}
static var standard: NumberFormatter {
return NumberFormatter()
}
}

@ -450,7 +450,7 @@ class FileImportManager {
let data = player.components(separatedBy: separator)
let lastName : String = data[safe: 2]?.prefixTrimmed(50) ?? ""
let firstName : String = data[safe: 3]?.prefixTrimmed(50) ?? ""
let sex: PlayerRegistration.PlayerSexType = data[safe: 0] == "f" ? PlayerRegistration.PlayerSexType.female : PlayerRegistration.PlayerSexType.male
let sex: PlayerSexType = data[safe: 0] == "f" ? PlayerSexType.female : PlayerSexType.male
if data[safe: 1]?.trimmed != nil {
teamName = data[safe: 1]?.trimmed
}

@ -14,6 +14,7 @@ enum PatchError: Error {
enum Patch: String, CaseIterable {
case cleanLogs
case syncUpgrade
var id: String {
return "padelclub.app.patch.\(self.rawValue)"
@ -43,121 +44,50 @@ class Patcher {
fileprivate static func _applyPatch(_ patch: Patch) throws {
switch patch {
case .cleanLogs: self._cleanLogs()
case .syncUpgrade: self._syncUpgrade()
}
}
//
// fileprivate static func _patchAlexisLeDu() {
// guard StoreCenter.main.userId == "94f45ed2-8938-4c32-a4b6-e4525073dd33" else { return }
//
// let clubs = DataStore.shared.clubs
// StoreCenter.main.resetApiCalls(collection: clubs)
//// clubs.resetApiCalls()
//
// for club in clubs.filter({ $0.creator == "d5060b89-e979-4c19-bf78-e459a6ed5318"}) {
// club.creator = StoreCenter.main.userId
// clubs.writeChangeAndInsertOnServer(instance: club)
// }
//
// }
//
// fileprivate static func _importDataFromDev() throws {
//
// let devServices = Services(url: "https://xlr.alwaysdata.net/roads/")
// guard devServices.hasToken() else {
// return
// }
// guard StoreCenter.main.synchronizationApiURL == "https://padelclub.app/roads/" else {
// return
// }
//
// guard let userId = StoreCenter.main.userId else {
// return
// }
//
// try StoreCenter.main.migrateToken(devServices)
//
//
// let myClubs: [Club] = DataStore.shared.clubs.filter { $0.creator == userId }
// let clubIds: [String] = myClubs.map { $0.id }
//
// myClubs.forEach { club in
// DataStore.shared.clubs.insertIntoCurrentService(item: club)
//
// let courts = DataStore.shared.courts.filter { clubIds.contains($0.club) }
// for court in courts {
// DataStore.shared.courts.insertIntoCurrentService(item: court)
// }
// }
//
// DataStore.shared.user.clubs = Array(clubIds)
// DataStore.shared.saveUser()
//
// DataStore.shared.events.insertAllIntoCurrentService()
// DataStore.shared.tournaments.insertAllIntoCurrentService()
// DataStore.shared.dateIntervals.insertAllIntoCurrentService()
//
// for tournament in DataStore.shared.tournaments {
// let store = tournament.tournamentStore
//
// Task { // need to wait for the collections to load
// try await Task.sleep(until: .now + .seconds(2))
//
// store.teamRegistrations.insertAllIntoCurrentService()
// store.rounds.insertAllIntoCurrentService()
// store.groupStages.insertAllIntoCurrentService()
// store.matches.insertAllIntoCurrentService()
// store.playerRegistrations.insertAllIntoCurrentService()
// store.teamScores.insertAllIntoCurrentService()
//
// }
// }
//
// }
//
// fileprivate static func _patchMissingMatches() {
//
// guard let url = StoreCenter.main.synchronizationApiURL else {
// return
// }
// guard url == "https://padelclub.app/roads/" else {
// return
// }
// let services = Services(url: url)
//
// for tournament in DataStore.shared.tournaments {
//
// let store = tournament.tournamentStore
// let identifier = StoreIdentifier(value: tournament.id, parameterName: "tournament")
//
// Task {
//
// do {
// // if nothing is online we upload the data
// let matches: [Match] = try await services.get(identifier: identifier)
// if matches.isEmpty {
// store.matches.insertAllIntoCurrentService()
// }
//
// let playerRegistrations: [PlayerRegistration] = try await services.get(identifier: identifier)
// if playerRegistrations.isEmpty {
// store.playerRegistrations.insertAllIntoCurrentService()
// }
//
// let teamScores: [TeamScore] = try await services.get(identifier: identifier)
// if teamScores.isEmpty {
// store.teamScores.insertAllIntoCurrentService()
// }
//
// } catch {
// Logger.error(error)
// }
//
// }
// }
//
// }
fileprivate static func _cleanLogs() {
StoreCenter.main.resetLoggingCollections()
}
fileprivate static func _syncUpgrade() {
for tournament in DataStore.shared.tournaments {
let id = tournament.id
guard let store = TournamentLibrary.shared.store(tournamentId: tournament.id) else { continue }
for round in store.rounds {
round.storeId = id
}
store.rounds.addOrUpdate(contentOfs: store.rounds)
for groupStage in store.groupStages {
groupStage.storeId = id
}
store.groupStages.addOrUpdate(contentOfs: store.groupStages)
for teamRegistration in store.teamRegistrations {
teamRegistration.storeId = id
}
store.teamRegistrations.addOrUpdate(contentOfs: store.teamRegistrations)
for pr in store.playerRegistrations {
pr.storeId = id
}
store.playerRegistrations.addOrUpdate(contentOfs: store.playerRegistrations)
for match in store.matches {
match.storeId = id
}
store.matches.addOrUpdate(contentOfs: store.matches)
for ts in store.teamScores {
ts.storeId = id
}
store.teamScores.addOrUpdate(contentOfs: store.teamScores)
for ms in store.matchSchedulers {
ms.storeId = id
}
store.matchSchedulers.addOrUpdate(contentOfs: store.matchSchedulers)
}
}
}

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

@ -8,23 +8,23 @@
import Foundation
enum URLs: String, Identifiable {
// case httpScheme = "https://"
#if DEBUG
case activationHost = "xlr.alwaysdata.net"
case main = "https://xlr.alwaysdata.net/"
case api = "https://xlr.alwaysdata.net/roads/"
// case api = "https://xlr.alwaysdata.net/roads/"
#elseif TESTFLIGHT
case activationHost = "xlr.alwaysdata.net"
case main = "https://xlr.alwaysdata.net/"
case api = "https://xlr.alwaysdata.net/roads/"
// case api = "https://xlr.alwaysdata.net/roads/"
#elseif PRODTEST
case activationHost = "padelclub.app"
case main = "https://padelclub.app/"
case api = "https://padelclub.app/roads/"
// case api = "https://padelclub.app/roads/"
#else
case activationHost = "padelclub.app"
case main = "https://padelclub.app/"
case api = "https://padelclub.app/roads/"
// case api = "https://padelclub.app/roads/"
#endif
case subscriptions = "https://apple.co/2Th4vqI"

@ -20,6 +20,7 @@ enum Screen: String, Codable {
case broadcast
case event
case print
case share
case restingTime
case stateSettings
}

@ -15,7 +15,7 @@ struct CallSettingsView: View {
@State private var showSendToAllView: Bool = false
@State private var addLink: Bool = false
var tournamentStore: TournamentStore {
var tournamentStore: TournamentStore? {
return self.tournament.tournamentStore
}
@ -66,7 +66,7 @@ struct CallSettingsView: View {
team.callDate = nil
}
do {
try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams)
try tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams)
} catch {
Logger.error(error)
}
@ -84,7 +84,7 @@ struct CallSettingsView: View {
}
}
do {
try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams)
try tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams)
} catch {
Logger.error(error)
}

@ -104,7 +104,7 @@ struct CallView: View {
}
}
var tournamentStore: TournamentStore {
var tournamentStore: TournamentStore? {
return self.tournament.tournamentStore
}
@ -130,7 +130,7 @@ struct CallView: View {
}
}
do {
try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: calledTeams)
try self.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: calledTeams)
} catch {
Logger.error(error)
}

@ -32,7 +32,7 @@ struct SendToAllView: View {
@State var summonParamByMessage: Bool = false
@State var summonParamReSummon: Bool = false
var tournamentStore: TournamentStore {
var tournamentStore: TournamentStore? {
return self.tournament.tournamentStore
}
@ -234,12 +234,14 @@ struct SendToAllView: View {
}
func _roundTeams() -> [TeamRegistration] {
let rounds: [Round] = contactRecipients.compactMap { self.tournamentStore.rounds.findById($0) }
guard let tournamentStore = self.tournamentStore else { return [] }
let rounds: [Round] = contactRecipients.compactMap { tournamentStore.rounds.findById($0) }
return rounds.flatMap { $0.teams() }
}
func _groupStagesTeams() -> [TeamRegistration] {
let groupStages : [GroupStage] = contactRecipients.compactMap { self.tournamentStore.groupStages.findById($0) }
guard let tournamentStore = self.tournamentStore else { return [] }
let groupStages : [GroupStage] = contactRecipients.compactMap { tournamentStore.groupStages.findById($0) }
return groupStages.flatMap { $0.teams() }
}

@ -135,7 +135,7 @@ struct CallMenuOptionsView: View {
} set: { _ in
team.toggleSummonConfirmation()
do {
try self.tournament.tournamentStore.teamRegistrations.addOrUpdate(instance: team)
try self.tournament.tournamentStore?.teamRegistrations.addOrUpdate(instance: team)
} catch {
Logger.error(error)
}
@ -170,7 +170,7 @@ struct CallMenuOptionsView: View {
RowButtonView("Effacer la date de convocation", role: .destructive) {
team.callDate = nil
do {
try self.tournament.tournamentStore.teamRegistrations.addOrUpdate(instance: team)
try self.tournament.tournamentStore?.teamRegistrations.addOrUpdate(instance: team)
} catch {
Logger.error(error)
}
@ -183,7 +183,7 @@ struct CallMenuOptionsView: View {
RowButtonView("Indiquer comme convoquée", role: .destructive) {
team.callDate = team.initialMatch()?.startDate ?? tournament.startDate
do {
try self.tournament.tournamentStore.teamRegistrations.addOrUpdate(instance: team)
try self.tournament.tournamentStore?.teamRegistrations.addOrUpdate(instance: team)
} catch {
Logger.error(error)
}

@ -76,7 +76,7 @@ struct CashierDetailView: View {
private func _tournamentsCashierDetailView(_ tournaments: [Tournament]) -> some View {
DisclosureGroup {
ForEach(PlayerRegistration.PlayerPaymentType.allCases) { type in
ForEach(PlayerPaymentType.allCases) { type in
PaymentTypeCashierRowView(tournaments: tournaments, type: type)
}
} label: {
@ -137,7 +137,7 @@ struct CashierDetailView: View {
struct PaymentTypeCashierRowView: View {
let tournaments: [Tournament]
let type: PlayerRegistration.PlayerPaymentType
let type: PlayerPaymentType
@State private var value: Double?
@ -167,7 +167,7 @@ struct CashierDetailView: View {
var body: some View {
DisclosureGroup {
let selectedPlayers = tournament.selectedPlayers()
ForEach(PlayerRegistration.PlayerPaymentType.allCases) { type in
ForEach(PlayerPaymentType.allCases) { type in
let count = selectedPlayers.filter({ $0.paymentType == type }).count
if count > 0 {
LabeledContent {

@ -35,7 +35,6 @@ struct CashierSettingsView: View {
Text("Si vous souhaitez que Padel Club vous aide à suivre les encaissements, indiquer un prix d'inscription. Sinon Padel Club vous aidera à suivre simplement l'arrivée et la présence des joueurs.")
}
let players = tournament.selectedPlayers()
if players.anySatisfy({ $0.hasArrived == false }) {
@ -138,19 +137,11 @@ struct CashierSettingsView: View {
}
private func _save(players: [PlayerRegistration]) {
do {
try tournament.tournamentStore.playerRegistrations.addOrUpdate(contentOfs: players)
} catch {
Logger.error(error)
}
tournament.tournamentStore?.playerRegistrations.addOrUpdate(contentOfs: players)
}
private func _save() {
do {
try dataStore.tournaments.addOrUpdate(instance: tournament)
} catch {
Logger.error(error)
}
dataStore.tournaments.addOrUpdate(instance: tournament)
}
}

@ -251,11 +251,7 @@ struct ClubDetailView: View {
}
.onDisappear {
if displayContext == .edition && clubDeleted == false {
do {
try dataStore.clubs.addOrUpdate(instance: club)
} catch {
Logger.error(error)
}
dataStore.clubs.addOrUpdate(instance: club)
}
}
.onAppear {
@ -295,15 +291,11 @@ struct ClubDetailView: View {
}
private func _deleteClub() {
do {
clubDeleted = true
dataStore.user.clubs.removeAll(where: { $0 == club.id })
self.dataStore.saveUser()
try dataStore.clubs.deleteById(club.id)
dataStore.clubs.delete(instance: club)
dismiss()
} catch {
Logger.error(error)
}
}
}

@ -324,6 +324,8 @@ struct ClubSearchView: View {
if clubToEdit.hasBeenCreated(by: StoreCenter.main.userId) {
if clubToEdit.name.isEmpty {
clubToEdit.name = clubMarker.nom.capitalized
}
if clubToEdit.acronym.isEmpty {
clubToEdit.acronym = clubToEdit.automaticShortName().uppercased()
}
clubToEdit.code = clubMarker.clubID

@ -31,6 +31,12 @@ struct LabelDelete: View {
}
}
struct ShareLabel: View {
var body: some View {
Label("Partager", systemImage: "square.and.arrow.up.fill")
}
}
struct LabelFilter: View {
var body: some View {
Label("Filtrer", systemImage: "line.3.horizontal.decrease.circle")

@ -97,7 +97,7 @@ struct RowButtonView: View {
.confirmationDialog("Confirmation",
isPresented: $askConfirmation,
titleVisibility: .visible) {
Button("OK") {
Button("OK", role: self.role) {
if let action {
action()
} else if let asyncAction {

@ -29,7 +29,7 @@ struct GroupStageSettingsView: View {
_courtIndex = .init(wrappedValue: groupStage._matches().first?.courtIndex ?? 0)
}
var tournamentStore: TournamentStore {
var tournamentStore: TournamentStore? {
return self.tournament.tournamentStore
}
@ -70,7 +70,7 @@ struct GroupStageSettingsView: View {
}
do {
try tournamentStore.matches.addOrUpdate(contentOfs: groupStage._matches())
try tournamentStore?.matches.addOrUpdate(contentOfs: groupStage._matches())
} catch {
Logger.error(error)
}
@ -93,7 +93,7 @@ struct GroupStageSettingsView: View {
groupStage._matches().forEach({ $0.updateTeamScores() })
}
do {
try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams)
try tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams)
} catch {
Logger.error(error)
}
@ -116,7 +116,7 @@ struct GroupStageSettingsView: View {
}
do {
try tournamentStore.matches.addOrUpdate(contentOfs: groupStage._matches())
try tournamentStore?.matches.addOrUpdate(contentOfs: groupStage._matches())
} catch {
Logger.error(error)
}
@ -132,7 +132,7 @@ struct GroupStageSettingsView: View {
groupStage._matches().forEach({ $0.updateTeamScores() })
}
do {
try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams)
try tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams)
} catch {
Logger.error(error)
}
@ -243,7 +243,7 @@ struct GroupStageSettingsView: View {
private func _save() {
do {
try tournamentStore.groupStages.addOrUpdate(instance: groupStage)
try tournamentStore?.groupStages.addOrUpdate(instance: groupStage)
} catch {
Logger.error(error)
}

@ -23,7 +23,7 @@ struct GroupStageTeamView: View {
let groupStage: GroupStage
var team: TeamRegistration
var tournamentStore: TournamentStore {
var tournamentStore: TournamentStore? {
return self.tournament.tournamentStore
}
@ -189,7 +189,7 @@ struct GroupStageTeamView: View {
private func _save() {
do {
try tournamentStore.teamRegistrations.addOrUpdate(instance: team)
try tournamentStore?.teamRegistrations.addOrUpdate(instance: team)
} catch {
Logger.error(error)
}

@ -22,7 +22,7 @@ struct GroupStageView: View {
_sortingMode = .init(wrappedValue: groupStage.hasEnded() ? .score : .weight)
}
var tournamentStore: TournamentStore {
var tournamentStore: TournamentStore? {
return self.tournament.tournamentStore
}
@ -118,7 +118,7 @@ struct GroupStageView: View {
let scores: [GroupStage.TeamGroupStageScore]?
let teams: [TeamRegistration]
var tournamentStore: TournamentStore {
var tournamentStore: TournamentStore? {
return self.tournament.tournamentStore
}
@ -217,7 +217,7 @@ struct GroupStageView: View {
Button {
player.hasArrived.toggle()
do {
try self.tournamentStore.playerRegistrations.addOrUpdate(instance: player)
try self.tournamentStore?.playerRegistrations.addOrUpdate(instance: player)
} catch {
Logger.error(error)
}
@ -246,7 +246,7 @@ struct GroupStageView: View {
team.groupStagePosition = index
groupStage._matches().forEach({ $0.updateTeamScores() })
do {
try tournamentStore.teamRegistrations.addOrUpdate(instance: team)
try tournamentStore?.teamRegistrations.addOrUpdate(instance: team)
} catch {
Logger.error(error)
}
@ -276,7 +276,7 @@ struct GroupStageView: View {
private func _save() {
do {
try tournamentStore.groupStages.addOrUpdate(instance: groupStage)
try tournamentStore?.groupStages.addOrUpdate(instance: groupStage)
} catch {
Logger.error(error)
}

@ -15,7 +15,7 @@ struct GroupStagesSettingsView: View {
@State private var generationDoneMessage: String?
let step: Int
var tournamentStore: TournamentStore {
var tournamentStore: TournamentStore? {
return self.tournament.tournamentStore
}
@ -26,7 +26,7 @@ struct GroupStagesSettingsView: View {
let issues = tournament.groupStageTeamPlacementIssue()
DisclosureGroup {
ForEach(issues.shouldBeInIt, id: \.self) { id in
if let team: TeamRegistration = self.tournamentStore.teamRegistrations.findById(id) {
if let team: TeamRegistration = self.tournamentStore?.teamRegistrations.findById(id) {
TeamRowView(team: team)
}
}
@ -39,12 +39,12 @@ struct GroupStagesSettingsView: View {
}
DisclosureGroup {
ForEach(issues.shouldNotBeInIt, id: \.self) { id in
if let team = self.tournamentStore.teamRegistrations.findById(id) {
if let team = self.tournamentStore?.teamRegistrations.findById(id) {
Menu {
Button("Retirer de sa poule") {
team.resetGroupeStagePosition()
do {
try self.tournamentStore.teamRegistrations.addOrUpdate(instance: team)
try self.tournamentStore?.teamRegistrations.addOrUpdate(instance: team)
} catch {
Logger.error(error)
}
@ -80,7 +80,7 @@ struct GroupStagesSettingsView: View {
let round = Round(tournament: tournament.id, index: 0, matchFormat: tournament.loserRoundFormat, groupStageLoserBracket: true)
do {
try tournamentStore.rounds.addOrUpdate(instance: round)
try tournamentStore?.rounds.addOrUpdate(instance: round)
} catch {
Logger.error(error)
}
@ -89,7 +89,7 @@ struct GroupStagesSettingsView: View {
} else if let groupStageLoserBracket = tournament.groupStageLoserBracket() {
RowButtonView("Supprimer les matchs de classements", role: .destructive) {
do {
try tournamentStore.rounds.delete(instance: groupStageLoserBracket)
try tournamentStore?.rounds.delete(instance: groupStageLoserBracket)
} catch {
Logger.error(error)
}
@ -110,7 +110,7 @@ struct GroupStagesSettingsView: View {
RowButtonView("Supprimer cette phase de poule", role: .destructive) {
let groupStages = tournament.groupStages(atStep: tournament.lastStep())
do {
try tournament.tournamentStore.groupStages.delete(contentOfs: groupStages)
try self.tournamentStore?.groupStages.delete(contentOfs: groupStages)
} catch {
Logger.error(error)
}
@ -150,7 +150,7 @@ struct GroupStagesSettingsView: View {
}
do {
try tournamentStore.matches.addOrUpdate(contentOfs: matches)
try tournamentStore?.matches.addOrUpdate(contentOfs: matches)
} catch {
Logger.error(error)
}
@ -210,7 +210,7 @@ struct GroupStagesSettingsView: View {
}
}
do {
try self.tournament.tournamentStore.groupStages.addOrUpdate(contentOfs: groupStages)
try self.tournament.tournamentStore?.groupStages.addOrUpdate(contentOfs: groupStages)
} catch {
Logger.error(error)
}
@ -224,7 +224,7 @@ struct GroupStagesSettingsView: View {
groupStage.name = nil
}
do {
try self.tournament.tournamentStore.groupStages.addOrUpdate(contentOfs: groupStages)
try self.tournament.tournamentStore?.groupStages.addOrUpdate(contentOfs: groupStages)
} catch {
Logger.error(error)
}
@ -241,7 +241,7 @@ struct GroupStagesSettingsView: View {
groupStage._matches().forEach({ $0.updateTeamScores() })
}
do {
try tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams)
try tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams)
} catch {
Logger.error(error)
}

@ -197,7 +197,7 @@ struct GroupStagesView: View {
results.forEach { drawResult in
missingQualifiedFromGroupStages[drawResult.drawIndex].qualified = true
do {
try self.tournament.tournamentStore.teamRegistrations.addOrUpdate(instance: missingQualifiedFromGroupStages[drawResult.drawIndex])
try self.tournament.tournamentStore?.teamRegistrations.addOrUpdate(instance: missingQualifiedFromGroupStages[drawResult.drawIndex])
} catch {
Logger.error(error)
}

@ -21,7 +21,7 @@ struct LoserBracketFromGroupStageView: View {
_isEditingLoserBracketGroupStage = .init(wrappedValue: loserBracket._matches().isEmpty)
}
var tournamentStore: TournamentStore {
var tournamentStore: TournamentStore? {
return self.tournament.tournamentStore
}
@ -107,23 +107,16 @@ struct LoserBracketFromGroupStageView: View {
private func _addNewMatch() {
let currentGroupStageLoserBracketsInitialPlace = tournament.groupStageLoserBracketsInitialPlace()
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, format: loserBracket.matchFormat)
match.setMatchName("\(placeCount)\(placeCount.ordinalFormattedSuffix()) place")
do {
try tournamentStore.matches.addOrUpdate(instance: match)
} catch {
Logger.error(error)
}
tournamentStore?.matches.addOrUpdate(instance: match)
}
private func _deleteAllMatches() {
let displayableMatches = loserBracket.playedMatches().sorted(by: \.index)
do {
try tournamentStore.matches.delete(contentOfs: displayableMatches)
} catch {
Logger.error(error)
}
tournamentStore?.matches.delete(contentOfs: displayableMatches)
}
@ -171,7 +164,7 @@ struct GroupStageLoserBracketMatchFooterView: View {
Spacer()
FooterButtonView("Effacer", role: .destructive) {
do {
try match.tournamentStore.matches.delete(instance: match)
try match.tournamentStore?.matches.delete(instance: match)
} catch {
Logger.error(error)
}
@ -206,15 +199,7 @@ struct GroupStageLoserBracketMatchFooterView: View {
match.setMatchName("\(newIndexValidated)\(newIndexValidated.ordinalFormattedSuffix()) place")
do {
try match.tournamentStore.teamScores.addOrUpdate(contentOfs: teamScores)
} catch {
Logger.error(error)
}
do {
try match.tournamentStore.matches.addOrUpdate(instance: match)
} catch {
Logger.error(error)
}
match.tournamentStore?.teamScores.addOrUpdate(contentOfs: teamScores)
match.tournamentStore?.matches.addOrUpdate(instance: match)
}
}

@ -191,7 +191,7 @@ struct MatchDateView: View {
}
do {
try self.match.tournamentStore.matches.addOrUpdate(instance: match)
try self.match.tournamentStore?.matches.addOrUpdate(instance: match)
} catch {
Logger.error(error)
}

@ -38,7 +38,7 @@ struct MatchDetailView: View {
@State private var presentRanking: Bool = false
@State private var confirmScoreEdition: Bool = false
var tournamentStore: TournamentStore {
var tournamentStore: TournamentStore? {
return match.tournamentStore
}
@ -121,8 +121,10 @@ struct MatchDetailView: View {
DisclosureGroup {
ForEach(unpaid) { player in
LabeledContent {
if let store = self.tournamentStore {
PlayerPayView(player: player)
.environmentObject(tournamentStore)
.environmentObject(store)
}
} label: {
Text(player.playerLabel())
}
@ -140,8 +142,12 @@ struct MatchDetailView: View {
menuView
}
.sheet(isPresented: $showDetails) {
if let store = self.tournamentStore {
MatchTeamDetailView(match: match).tint(.master)
.environmentObject(tournamentStore)
.environmentObject(store)
} else {
Text("no store")
}
}
.sheet(isPresented: self.$showSubscriptionView, content: {
NavigationStack {
@ -364,7 +370,7 @@ struct MatchDetailView: View {
ForEach(match.teamScores) { teamScore in
Button(role: .destructive) {
do {
try tournamentStore.teamScores.delete(instance: teamScore)
try tournamentStore?.teamScores.delete(instance: teamScore)
} catch {
Logger.error(error)
}
@ -566,7 +572,7 @@ struct MatchDetailView: View {
if match.isReady() == false && match.teams().count == 2 {
let teamsScores = match.getOrCreateTeamScores()
do {
try tournamentStore.teamScores.addOrUpdate(contentOfs: teamsScores)
try tournamentStore?.teamScores.addOrUpdate(contentOfs: teamsScores)
} catch {
Logger.error(error)
}
@ -615,7 +621,7 @@ struct MatchDetailView: View {
private func _saveMatch() {
do {
try tournamentStore.matches.addOrUpdate(instance: match)
try tournamentStore?.matches.addOrUpdate(instance: match)
} catch {
Logger.error(error)
}

@ -69,7 +69,7 @@ struct MatchRowView: View {
Button {
player.hasArrived.toggle()
do {
try player.tournamentStore.playerRegistrations.addOrUpdate(instance: player)
try player.tournamentStore?.playerRegistrations.addOrUpdate(instance: player)
} catch {
Logger.error(error)
}

@ -16,7 +16,7 @@ struct MatchSetupView: View {
@State var match: Match
@State private var seedGroup: SeedInterval?
var tournamentStore: TournamentStore {
var tournamentStore: TournamentStore? {
return match.tournamentStore
}
@ -68,19 +68,19 @@ struct MatchSetupView: View {
if walkOutSpot || team.bracketPosition != nil || matchTypeContext == .loserBracket {
match.setLuckyLoser(team: team, teamPosition: teamPosition)
do {
try tournamentStore.matches.addOrUpdate(instance: match)
try tournamentStore?.matches.addOrUpdate(instance: match)
} catch {
Logger.error(error)
}
} else if team.bracketPosition == nil {
team.setSeedPosition(inSpot: match, slot: teamPosition, opposingSeeding: false)
do {
try tournamentStore.matches.addOrUpdate(instance: match)
try tournamentStore?.matches.addOrUpdate(instance: match)
} catch {
Logger.error(error)
}
do {
try tournamentStore.teamRegistrations.addOrUpdate(instance: team)
try tournamentStore?.teamRegistrations.addOrUpdate(instance: team)
} catch {
Logger.error(error)
}
@ -97,7 +97,7 @@ struct MatchSetupView: View {
if let randomTeam = luckyLosers.randomElement() {
match.setLuckyLoser(team: randomTeam, teamPosition: teamPosition)
do {
try tournamentStore.matches.addOrUpdate(instance: match)
try tournamentStore?.matches.addOrUpdate(instance: match)
} catch {
Logger.error(error)
}
@ -112,12 +112,12 @@ struct MatchSetupView: View {
if let randomTeam = availableQualifiedTeams.randomElement() {
randomTeam.setSeedPosition(inSpot: match, slot: teamPosition, opposingSeeding: false)
do {
try tournamentStore.matches.addOrUpdate(instance: match)
try tournamentStore?.matches.addOrUpdate(instance: match)
} catch {
Logger.error(error)
}
do {
try tournamentStore.teamRegistrations.addOrUpdate(instance: randomTeam)
try tournamentStore?.teamRegistrations.addOrUpdate(instance: randomTeam)
} catch {
Logger.error(error)
}
@ -132,12 +132,12 @@ struct MatchSetupView: View {
if let randomTeam = tournament.randomSeed(fromSeedGroup: seedGroup) {
randomTeam.setSeedPosition(inSpot: match, slot: teamPosition, opposingSeeding: false)
do {
try tournamentStore.matches.addOrUpdate(instance: match)
try tournamentStore?.matches.addOrUpdate(instance: match)
} catch {
Logger.error(error)
}
do {
try tournamentStore.teamRegistrations.addOrUpdate(instance: randomTeam)
try tournamentStore?.teamRegistrations.addOrUpdate(instance: randomTeam)
} catch {
Logger.error(error)
}
@ -155,12 +155,12 @@ struct MatchSetupView: View {
if let randomTeam = tournament.randomSeed(fromSeedGroup: seedGroup) {
randomTeam.setSeedPosition(inSpot: match, slot: teamPosition, opposingSeeding: false)
do {
try tournamentStore.matches.addOrUpdate(instance: match)
try tournamentStore?.matches.addOrUpdate(instance: match)
} catch {
Logger.error(error)
}
do {
try tournamentStore.teamRegistrations.addOrUpdate(instance: randomTeam)
try tournamentStore?.teamRegistrations.addOrUpdate(instance: randomTeam)
} catch {
Logger.error(error)
}
@ -185,7 +185,7 @@ struct MatchSetupView: View {
Button {
match.unlockSeedPosition(atTeamPosition: teamPosition)
do {
try tournamentStore.matches.addOrUpdate(instance: match)
try tournamentStore?.matches.addOrUpdate(instance: match)
} catch {
Logger.error(error)
}
@ -197,7 +197,7 @@ struct MatchSetupView: View {
ConfirmButtonView(shouldConfirm: shouldConfirm, message: MatchSetupView.confirmationMessage) {
_ = match.lockAndGetSeedPosition(atTeamPosition: teamPosition)
do {
try tournamentStore.matches.addOrUpdate(instance: match)
try tournamentStore?.matches.addOrUpdate(instance: match)
} catch {
Logger.error(error)
}
@ -220,7 +220,7 @@ struct MatchSetupView: View {
if match.isSeededBy(team: team, inTeamPosition: teamPosition) {
if let score = match.teamScore(ofTeam: team) {
do {
try tournamentStore.teamScores.delete(instance: score)
try tournamentStore?.teamScores.delete(instance: score)
} catch {
Logger.error(error)
}
@ -228,7 +228,7 @@ struct MatchSetupView: View {
team.bracketPosition = nil
do {
try tournamentStore.teamRegistrations.addOrUpdate(instance: team)
try tournamentStore?.teamRegistrations.addOrUpdate(instance: team)
} catch {
Logger.error(error)
}
@ -236,7 +236,7 @@ struct MatchSetupView: View {
if previousMatch.disabled {
previousMatch.enableMatch()
do {
try tournamentStore.matches.addOrUpdate(instance: previousMatch)
try tournamentStore?.matches.addOrUpdate(instance: previousMatch)
} catch {
Logger.error(error)
}
@ -244,14 +244,14 @@ struct MatchSetupView: View {
}
do {
try tournamentStore.matches.addOrUpdate(instance: match)
try tournamentStore?.matches.addOrUpdate(instance: match)
} catch {
Logger.error(error)
}
} else if match.isLoserBracket {
if let score = match.teamScore(ofTeam: team) {
do {
try tournamentStore.teamScores.delete(instance: score)
try tournamentStore?.teamScores.delete(instance: score)
} catch {
Logger.error(error)
}
@ -259,7 +259,7 @@ struct MatchSetupView: View {
} else {
match.teamWillBeWalkOut(team)
do {
try tournamentStore.matches.addOrUpdate(instance: match)
try tournamentStore?.matches.addOrUpdate(instance: match)
} catch {
Logger.error(error)
}

@ -17,6 +17,8 @@ struct EventListView: View {
let tournaments: [FederalTournamentHolder]
let sortAscending: Bool
@State var showUserSearch: Bool = false
var lastDataSource: Date? {
guard let _lastDataSource = dataStore.appSettings.lastDataSource else { return nil }
return URL.importDateFormatter.date(from: _lastDataSource)
@ -343,19 +345,16 @@ struct EventListView: View {
#if DEBUG
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
Button(role: .destructive) {
do {
let event = tournament.eventObject()
let isLastTournament = event?.tournaments.count == 1
try dataStore.tournaments.delete(instance: tournament)
if let event, isLastTournament {
try dataStore.events.delete(instance: event)
}
} catch {
Logger.error(error)
}
dataStore.deleteTournament(tournament)
} label: {
LabelDelete()
}
// Button() {
// self.showUserSearch = true
// } label: {
// ShareLabel().tint(.orange)
// }
}
#endif
}

@ -13,7 +13,7 @@ struct TournamentSubscriptionView: View {
let federalTournament: FederalTournament
let build: any TournamentBuildHolder
let user: User
let user: CustomUser
@State private var selectedPlayers: [ImportedPlayer]
@State private var contactType: ContactType? = nil
@ -21,7 +21,7 @@ struct TournamentSubscriptionView: View {
@State private var didSendMessage: 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.build = build
self.user = user

@ -47,12 +47,8 @@ struct MainView: View {
}
)}
private func _isConnected() -> Bool {
return StoreCenter.main.hasToken() && StoreCenter.main.userId != nil
}
var badgeText: Text? {
return (dataStore.appSettings.didCreateAccount && _isConnected() == false) ? Text("!").font(.headline) : nil
return (dataStore.appSettings.didCreateAccount && StoreCenter.main.isAuthenticated == false) ? Text("!").font(.headline) : nil
}
var body: some View {
@ -95,8 +91,8 @@ struct MainView: View {
}
.id(mainViewId)
.onChange(of: dataStore.user.id) {
print("dataStore.user.id", dataStore.user.id)
print("StoreCenter.main.userId", StoreCenter.main.userId)
print("dataStore.user.id = ", dataStore.user.id)
print("StoreCenter.main.userId = ", StoreCenter.main.userId ?? "")
if StoreCenter.main.userId == nil { // user disconnected
navigation.path.removeLast(navigation.path.count)
mainViewId = UUID()
@ -105,7 +101,7 @@ struct MainView: View {
.environmentObject(dataStore)
.task {
//await self._checkSourceFileAvailability()
if StoreCenter.main.hasToken() {
if StoreCenter.main.isAuthenticated {
do {
try await dataStore.clubs.loadDataFromServerIfAllowed()
} catch {

@ -10,32 +10,96 @@ import LeStorage
struct APICallsListView: View {
@State var collections: [String] = []
@State var descriptors: [CollectionDescriptor] = []
var body: some View {
List {
ForEach(self.collections) { name in
NavigationLink(name) {
APICallsView(name: name)
ForEach(self.descriptors) { descriptor in
NavigationLink {
if let syncedType = descriptor.type as? any SyncedStorable.Type {
APICallsView(name: descriptor.name, type: syncedType)
}
} label: {
LabeledContent(descriptor.name, value: descriptor.count.string)
}
}
}.onAppear {
self.collections = Store.main.collectionNames()
self.load()
}
}
func load() {
Task {
self.descriptors = await StoreCenter.main.apiCollectionDescriptors().map {
CollectionDescriptor(name: $0.0, type: $0.1)
}.sorted(by: \.name, order: .ascending)
for descriptor in self.descriptors {
if let type = descriptor.type as? any SyncedStorable.Type {
await self.loadCount(type, descriptor)
}
}
}
}
func loadCount<T: SyncedStorable>(_ type: T.Type, _ descriptor: CollectionDescriptor) async {
let calls = await StoreCenter.main.apiCalls(type: type)
descriptor.count = calls.count
// Logger.log("\(descriptor.name), count = \(calls.count)")
}
}
@Observable
class CollectionDescriptor: Identifiable, Hashable {
var id: UUID = UUID()
var name: String
var type: any Storable.Type
var count: Int = 0
init(name: String, type: any Storable.Type) {
self.name = name
self.type = type
}
static func == (lhs: CollectionDescriptor, rhs: CollectionDescriptor) -> Bool {
return lhs.id == rhs.id
}
func hash(into hasher: inout Hasher) {
hasher.combine(self.id)
}
}
struct APICallsView: View {
var name: String
@State var text: String = ""
var type: any SyncedStorable.Type
// @State var text: String = ""
@State var apiCalls: [any SomeCall] = []
var body: some View {
List {
Text(self.text).lineLimit(nil)
}.onAppear {
Section(header:
Text("\(apiCalls.count) API calls")
.font(.headline)
.foregroundColor(.primary)
) {
ForEach(self.apiCalls, id: \.id) { apiCall in
NavigationLink {
APICallView(apiCall: apiCall, type: self.type)
} label: {
LabeledContent(apiCall.method.rawValue, value: apiCall.attemptsCount.string)
}
}
}
}.navigationTitle(self.name)
.onAppear {
self._load()
}
}
@ -43,10 +107,126 @@ struct APICallsView: View {
@MainActor
fileprivate func _load() {
Task {
self.text = await StoreCenter.main.apiCallsFileContent(resourceName: self.name)
await self.load(self.type)
// self.text = await StoreCenter.main.apiCallsFileContent(resourceName: self.name)
}
}
func load<T: SyncedStorable>(_ type: T.Type) async {
let calls: [ApiCall<T>] = await StoreCenter.main.apiCalls(type: type)
self.apiCalls = calls
}
}
struct APICallView: View {
var apiCall: any SomeCall
var type: any SyncedStorable.Type
@State private var errorMessage: String? = nil
@State private var isLoading: Bool = false
@State private var showSuccessPopover: Bool = false
var body: some View {
Form {
Section {
LabeledContent("Method", value: apiCall.method.rawValue)
LabeledContent("Data id", value: apiCall.stringId)
LabeledContent("Attempts", value: apiCall.attemptsCount.string)
}
Section {
if isLoading {
ProgressView()
.padding()
} else {
Button("Execute") {
self.execute()
// Clear previous error when attempting again
errorMessage = nil
}
.disabled(isLoading)
}
if let errorMessage = errorMessage {
LabeledContent("Error", value: errorMessage)
// VStack(alignment: .leading) {
// Text("Error:")
// .fontWeight(.bold)
// .foregroundColor(.red)
// Text(errorMessage)
// .foregroundColor(.red)
// }
}
if showSuccessPopover {
VStack {
Image(systemName: "checkmark.circle.fill")
.foregroundColor(.green)
.font(.title)
Text("Success")
.foregroundColor(.green)
}
.padding()
.background(
RoundedRectangle(cornerRadius: 8)
.fill(Color.white)
.shadow(radius: 2)
)
.transition(.scale.combined(with: .opacity))
}
}
Section {
Text(apiCall.dataContent ?? "none").monospaced().font(.caption)
}
}
.animation(.easeInOut, value: showSuccessPopover)
.navigationTitle(String(describing: self.type))
}
func execute() {
self.execute(type: self.type)
}
func execute<T: SyncedStorable>(type: T.Type) {
if let call = self.apiCall as? ApiCall<T> {
Task {
await MainActor.run {
isLoading = true
}
do {
let results = try await StoreCenter.main.execute(apiCalls: [call])
await MainActor.run {
isLoading = false
if let result = results.first {
errorMessage = result.message
showSuccessPopover = (200..<300).contains(result.status)
} else {
errorMessage = "no results"
}
}
// Update UI on the main thread
// await MainActor.run {
// errorMessage = nil
// isLoading = false
//
// // Show success popover
// showSuccessPopover = true
// }
} catch {
// Update UI on the main thread
await MainActor.run {
errorMessage = error.localizedDescription
isLoading = false
}
}
}
}
}
}
//#Preview {

@ -11,29 +11,40 @@ import LeStorage
struct DebugSettingsView: View {
var body: some View {
List {
Section("Status") {
LabeledContent("Has Websocket Manager", value: self._hasWebSocketManager)
LabeledContent("Websocket ping", value: self._wsPingStatus)
LabeledContent("Websocket failure", value: self._wsFailure)
LabeledContent("Last sync date", value: self._lastSyncDate)
}
Section("Settings") {
LabeledContent("UUID", value: self._userId)
LabeledContent("User Name", value: self._userName)
LabeledContent("Token", value: self._token)
LabeledContent("Server", value: self._server)
LabeledContent("Server", value: self._apiURL)
LabeledContent("Synchronized", value: self._synchronized)
LabeledContent("CollectionsCanSynchronize", value: self._canSynchronize)
}
}
}
fileprivate var _userId: String {
return StoreCenter.main.userId ?? ""
}
fileprivate var _userName: String {
return StoreCenter.main.userName() ?? ""
return StoreCenter.main.userName ?? ""
}
fileprivate var _token: String {
return StoreCenter.main.token() ?? ""
let token = try? StoreCenter.main.token()
return token ?? ""
}
fileprivate var _server: String {
return PListReader.readString(plist: "local", key: "server") ?? ""
fileprivate var _apiURL: String {
return StoreCenter.main.apiURL ?? "not configured"
}
fileprivate var _synchronized: String {
@ -47,6 +58,19 @@ struct DebugSettingsView: View {
fileprivate var _canSynchronize: String {
return "\(StoreCenter.main.collectionsCanSynchronize)"
}
fileprivate var _wsPingStatus: String {
return "\(StoreCenter.main.websocketPingStatus)"
}
fileprivate var _wsFailure: String {
return "\(StoreCenter.main.websocketFailure)"
}
fileprivate var _hasWebSocketManager: String {
return "\(StoreCenter.main.hasWebSocketManager)"
}
fileprivate var _lastSyncDate: String {
return "\(StoreCenter.main.lastSyncDate)"
}
}
//#Preview {

@ -15,6 +15,12 @@ struct ToolboxView: View {
@Environment(NavigationViewModel.self) private var navigation: NavigationViewModel
@State private var didResetApiCalls: Bool = false
@State var showDebugViews: Bool = false
@State private var tapCount = 0
@State private var lastTapTime: Date? = nil
private let tapTimeThreshold: TimeInterval = 1.0
var lastDataSource: String? {
dataStore.appSettings.lastDataSource
}
@ -51,82 +57,10 @@ struct ToolboxView: View {
}
}
#if DEBUG
Section {
NavigationLink("Settings") {
DebugSettingsView()
}
NavigationLink("API calls") {
APICallsListView()
}
}
Section {
RowButtonView("Reset ALL API Calls") {
StoreCenter.main.resetApiCalls()
Logger.log("Api calls reset")
}
}
Section {
RowButtonView("Fix Names") {
for tournament in dataStore.tournaments {
let store = tournament.tournamentStore
let playerRegistrations = store.playerRegistrations
playerRegistrations.forEach { player in
player.firstName = player.firstName.trimmed.capitalized
player.lastName = player.lastName.trimmed.uppercased()
}
do {
try store.playerRegistrations.addOrUpdate(contentOfs: playerRegistrations)
} catch {
Logger.error(error)
}
}
}
if self.showDebugViews {
DebugView()
}
Section {
RowButtonView("Delete teams") {
for tournament in DataStore.shared.tournaments {
let store: TournamentStore = tournament.tournamentStore
let teamRegistrations = store.teamRegistrations.filter({ $0.tournamentObject() == nil })
do {
try store.teamRegistrations.delete(contentOfs: teamRegistrations)
} catch {
Logger.error(error)
}
}
}
}
Section {
// TODO
RowButtonView("Delete players") {
for tournament in DataStore.shared.tournaments {
let store: TournamentStore = tournament.tournamentStore
let playersRegistrations = store.playerRegistrations.filter({ $0.team() == nil })
do {
try store.playerRegistrations.delete(contentOfs: playersRegistrations)
} catch {
Logger.error(error)
}
}
}
}
#endif
Section {
NavigationLink {
SelectablePlayerListView(isPresented: false, lastDataSource: true)
@ -205,6 +139,16 @@ struct ToolboxView: View {
}
}
}
.onAppear {
#if DEBUG
self.showDebugViews = true
#endif
#if TESTFLIGHT
self.showDebugViews = true
#endif
}
.overlay(alignment: .bottom) {
if didResetApiCalls {
Label("logs effacés", systemImage: "checkmark")
@ -217,8 +161,16 @@ struct ToolboxView: View {
}
}
}
.navigationTitle(TabDestination.toolbox.title)
// .navigationBarTitleDisplayMode(.large)
// .navigationTitle(TabDestination.toolbox.title)
.toolbar {
ToolbarItem(placement: .principal) {
Text(TabDestination.toolbox.title)
.font(.headline)
.onTapGesture {
_handleTitleTap()
}
}
ToolbarItem(placement: .topBarLeading) {
Link(destination: URLs.appStore.url) {
Text("v\(PadelClubApp.appVersion)")
@ -240,12 +192,119 @@ struct ToolboxView: View {
}
}
}
private func _handleTitleTap() {
// Reset counter if too much time elapsed since last tap
if let lastTime = lastTapTime, Date().timeIntervalSince(lastTime) > tapTimeThreshold {
tapCount = 0
}
// Update tap count and time
tapCount += 1
lastTapTime = Date()
print("Tap count: \(tapCount)")
// Check if we've reached 4 taps
if tapCount == 4 {
_showDebugView()
tapCount = 0
}
}
private func _showDebugView() {
self.showDebugViews = true
}
}
//#Preview {
// ToolboxView()
//}
struct DebugView: View {
@EnvironmentObject var dataStore: DataStore
var body: some View {
Section {
NavigationLink("Settings") {
DebugSettingsView()
}
NavigationLink("API calls") {
APICallsListView()
}
}
Section {
RowButtonView("Reset ALL API Calls") {
StoreCenter.main.resetApiCalls()
Logger.log("Api calls reset")
}
}
Section {
RowButtonView("Fix Names") {
for tournament in dataStore.tournaments {
if let store = tournament.tournamentStore {
let playerRegistrations = store.playerRegistrations
playerRegistrations.forEach { player in
player.firstName = player.firstName.trimmed.capitalized
player.lastName = player.lastName.trimmed.uppercased()
}
do {
try store.playerRegistrations.addOrUpdate(contentOfs: playerRegistrations)
} catch {
Logger.error(error)
}
}
}
}
}
Section {
RowButtonView("Delete teams") {
for tournament in DataStore.shared.tournaments {
if let store: TournamentStore = tournament.tournamentStore {
let teamRegistrations = store.teamRegistrations.filter({ $0.tournamentObject() == nil })
do {
try store.teamRegistrations.delete(contentOfs: teamRegistrations)
} catch {
Logger.error(error)
}
}
}
}
}
Section {
// TODO
RowButtonView("Delete players") {
for tournament in DataStore.shared.tournaments {
if let store: TournamentStore = tournament.tournamentStore {
let playersRegistrations = store.playerRegistrations.filter({ $0.team() == nil })
do {
try store.playerRegistrations.delete(contentOfs: playersRegistrations)
} catch {
Logger.error(error)
}
}
}
}
}
}
}
struct ZipLog: Transferable {
private func _getZip() -> URL? {

@ -0,0 +1,45 @@
//
// NetworkStatusView.swift
// PadelClub
//
// Created by Laurent Morvillier on 25/10/2024.
//
import SwiftUI
import LeStorage
struct NetworkStatusView: View {
@State private var isConnected = false
@State private var timer: Timer?
var body: some View {
Image(systemName: self.isConnected ? "network" : "network.slash")
.resizable()
.scaledToFit()
.onAppear {
self._defineStatus()
// Start the timer when the view appears
timer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true) { _ in
withAnimation {
self._defineStatus()
}
}
}
.onDisappear {
// Clean up timer when view disappears
timer?.invalidate()
timer = nil
}
}
fileprivate func _defineStatus() {
self.isConnected = LeStorage.NetworkMonitor.shared.isConnected
}
}
#Preview {
NetworkStatusView()
}

@ -40,10 +40,10 @@ struct PadelClubView: View {
if let currentMonth = monthData.first, currentMonth.incompleteMode {
Section {
Text("Attention, depuis Août 2024, les données fédérales publiques des joueurs (messieurs) récupérables sont incomplètes car limité au 80.000 premiers joueurs.")
Text("Attention, depuis Août 2024, les données fédérales publiques des joueurs (messieurs) récupérables sont incomplètes car limitées aux 80 000 premiers joueurs.")
if currentMonth.maleUnrankedValue == nil {
Text("Le rang d'un joueur non-classé n'est donc pas calculable pour le moment, Padel Club utilisera une valeur par défaut de de 90.000.")
Text("Le rang d'un joueur non-classé n'est donc pas calculable pour le moment, Padel Club utilisera une valeur par défaut de 90 000.")
}
Text("Un classement souligné comme ci-dessous indiquera que l'information provient d'un mois précédent.")

@ -55,7 +55,7 @@ struct UmpireView: View {
}
}
if self._isConnected() {
if StoreCenter.main.isAuthenticated {
NavigationLink {
AccountView(user: dataStore.user) { }
} label: {
@ -186,11 +186,12 @@ struct UmpireView: View {
.toolbar {
#if DEBUG
ToolbarItem(placement: .topBarTrailing) {
if StoreCenter.main.collectionsCanSynchronize {
Image(systemName: "checkmark.icloud")
} else {
Image(systemName: "icloud.slash")
}
NetworkStatusView()
// if StoreCenter.main.collectionsCanSynchronize {
// Image(systemName: "checkmark.icloud")
// } else {
// Image(systemName: "icloud.slash")
// }
}
#endif
}
@ -241,10 +242,6 @@ struct UmpireView: View {
}
}
fileprivate func _isConnected() -> Bool {
return dataStore.user.username.count > 0 && StoreCenter.main.hasToken()
}
}
struct AccountRowView: View {
@ -253,17 +250,17 @@ struct AccountRowView: View {
var userName: String
var body: some View {
let hasToken = StoreCenter.main.hasToken()
let isAuthenticated = StoreCenter.main.isAuthenticated
LabeledContent {
if hasToken {
if isAuthenticated {
Text(self.userName)
} else if StoreCenter.main.userName() != nil {
} else if StoreCenter.main.userName != nil {
Image(systemName: "xmark.circle.fill")
.foregroundStyle(.logoRed)
}
} label: {
Label("Mon compte", systemImage: "person.fill")
if hasToken && dataStore.user.email.isEmpty == false {
if isAuthenticated && dataStore.user.email.isEmpty == false {
Text(dataStore.user.email)
}
}

@ -17,7 +17,7 @@ struct GroupStageScheduleEditorView: View {
@State private var startDate: Date
@State private var currentDate: Date?
var tournamentStore: TournamentStore {
var tournamentStore: TournamentStore? {
return self.tournament.tournamentStore
}
@ -42,7 +42,7 @@ struct GroupStageScheduleEditorView: View {
private func _save() {
do {
try tournamentStore.groupStages.addOrUpdate(instance: groupStage)
try tournamentStore?.groupStages.addOrUpdate(instance: groupStage)
} catch {
Logger.error(error)
}

@ -19,7 +19,7 @@ struct LoserRoundScheduleEditorView: View {
@State private var matchFormat: MatchFormat
@State private var currentDate: Date?
var tournamentStore: TournamentStore {
var tournamentStore: TournamentStore? {
return self.tournament.tournamentStore
}
@ -92,7 +92,7 @@ struct LoserRoundScheduleEditorView: View {
private func _save() {
do {
try self.tournamentStore.rounds.addOrUpdate(contentOfs: upperRound.loserRounds())
try self.tournamentStore?.rounds.addOrUpdate(contentOfs: upperRound.loserRounds())
} catch {
Logger.error(error)
}

@ -20,7 +20,7 @@ struct LoserRoundStepScheduleEditorView: View {
@State private var startDate: Date
//@State private var matchFormat: MatchFormat
var tournamentStore: TournamentStore {
var tournamentStore: TournamentStore? {
return self.tournament.tournamentStore
}
@ -79,7 +79,7 @@ struct LoserRoundStepScheduleEditorView: View {
private func _save() {
do {
try tournamentStore.rounds.addOrUpdate(contentOfs: upperRound.loserRounds(forRoundIndex: round.index))
try tournamentStore?.rounds.addOrUpdate(contentOfs: upperRound.loserRounds(forRoundIndex: round.index))
} catch {
Logger.error(error)
}

@ -14,7 +14,7 @@ struct MatchScheduleEditorView: View {
@State private var startDate: Date
@State private var currentDate: Date?
var tournamentStore: TournamentStore {
var tournamentStore: TournamentStore? {
return self.tournament.tournamentStore
}
@ -54,7 +54,7 @@ struct MatchScheduleEditorView: View {
private func _save() {
do {
try tournamentStore.matches.addOrUpdate(instance: match)
try tournamentStore?.matches.addOrUpdate(instance: match)
} catch {
Logger.error(error)
}

@ -25,7 +25,7 @@ struct PlanningSettingsView: View {
@State private var deletingDone: Bool = false
@State private var presentFormatHelperView: Bool = false
var tournamentStore: TournamentStore {
var tournamentStore: TournamentStore? {
return self.tournament.tournamentStore
}
@ -171,7 +171,7 @@ struct PlanningSettingsView: View {
.headerProminence(.increased)
.onAppear {
do {
try self.tournamentStore.matchSchedulers.addOrUpdate(instance: matchScheduler)
try self.tournamentStore?.matchSchedulers.addOrUpdate(instance: matchScheduler)
} catch {
Logger.error(error)
}
@ -251,7 +251,7 @@ struct PlanningSettingsView: View {
$0.startDate = nil
$0.confirmed = false
})
try self.tournamentStore.matches.addOrUpdate(contentOfs: allMatches)
try self.tournamentStore?.matches.addOrUpdate(contentOfs: allMatches)
deletingDateMatchesDone = true
} catch {
Logger.error(error)
@ -268,7 +268,7 @@ struct PlanningSettingsView: View {
do {
deletingDone = false
allGroupStages.forEach({ $0.startDate = nil })
try self.tournamentStore.groupStages.addOrUpdate(contentOfs: allGroupStages)
try self.tournamentStore?.groupStages.addOrUpdate(contentOfs: allGroupStages)
deletingDone = true
} catch {
@ -284,7 +284,7 @@ struct PlanningSettingsView: View {
do {
deletingDone = false
allRounds.forEach({ $0.startDate = nil })
try self.tournamentStore.rounds.addOrUpdate(contentOfs: allRounds)
try self.tournamentStore?.rounds.addOrUpdate(contentOfs: allRounds)
deletingDone = true
} catch {
Logger.error(error)
@ -304,13 +304,13 @@ struct PlanningSettingsView: View {
$0.startDate = nil
$0.confirmed = false
})
try self.tournamentStore.matches.addOrUpdate(contentOfs: allMatches)
try self.tournamentStore?.matches.addOrUpdate(contentOfs: allMatches)
allGroupStages.forEach({ $0.startDate = nil })
try self.tournamentStore.groupStages.addOrUpdate(contentOfs: allGroupStages)
try self.tournamentStore?.groupStages.addOrUpdate(contentOfs: allGroupStages)
allRounds.forEach({ $0.startDate = nil })
try self.tournamentStore.rounds.addOrUpdate(contentOfs: allRounds)
try self.tournamentStore?.rounds.addOrUpdate(contentOfs: allRounds)
deletingDone = true
} catch {
Logger.error(error)
@ -331,13 +331,13 @@ struct PlanningSettingsView: View {
$0.endDate = nil
$0.confirmed = false
})
try self.tournamentStore.matches.addOrUpdate(contentOfs: tournament.allMatches())
try self.tournamentStore?.matches.addOrUpdate(contentOfs: tournament.allMatches())
allGroupStages.forEach({ $0.startDate = nil })
try self.tournamentStore.groupStages.addOrUpdate(contentOfs: allGroupStages)
try self.tournamentStore?.groupStages.addOrUpdate(contentOfs: allGroupStages)
allRounds.forEach({ $0.startDate = nil })
try self.tournamentStore.rounds.addOrUpdate(contentOfs: allRounds)
try self.tournamentStore?.rounds.addOrUpdate(contentOfs: allRounds)
deletingDone = true
} catch {
Logger.error(error)
@ -508,7 +508,7 @@ struct PlanningSettingsView: View {
private func _save() {
do {
try self.tournamentStore.matchSchedulers.addOrUpdate(instance: matchScheduler)
try self.tournamentStore?.matchSchedulers.addOrUpdate(instance: matchScheduler)
try dataStore.tournaments.addOrUpdate(instance: tournament)
} catch {
Logger.error(error)

@ -97,7 +97,7 @@ struct PlanningView: View {
ToolbarItem(placement: .topBarTrailing) {
Button("Sauver") {
do {
try self.tournament.tournamentStore.matches.addOrUpdate(contentOfs: allMatches)
try self.tournament.tournamentStore?.matches.addOrUpdate(contentOfs: allMatches)
} catch {
Logger.error(error)
}
@ -503,7 +503,7 @@ struct PlanningView: View {
// }
// }
//
// try? self.tournament.tournamentStore.matches.addOrUpdate(contentOfs: matches)
// try? self.tournament.tournamentStore?.matches.addOrUpdate(contentOfs: matches)
// }
//
//

@ -16,7 +16,7 @@ struct RoundScheduleEditorView: View {
@State private var startDate: Date
@State private var currentDate: Date?
var tournamentStore: TournamentStore {
var tournamentStore: TournamentStore? {
return self.tournament.tournamentStore
}
@ -71,7 +71,7 @@ struct RoundScheduleEditorView: View {
private func _save() {
do {
try tournamentStore.rounds.addOrUpdate(instance: round)
try tournamentStore?.rounds.addOrUpdate(instance: round)
} catch {
Logger.error(error)
}

@ -26,7 +26,7 @@ struct SchedulerView: View {
var tournament: Tournament
var destination: ScheduleDestination
var tournamentStore: TournamentStore {
var tournamentStore: TournamentStore? {
return self.tournament.tournamentStore
}
@ -49,7 +49,7 @@ struct SchedulerView: View {
}
do {
try dataStore.tournaments.addOrUpdate(instance: tournament)
try self.tournamentStore.groupStages.addOrUpdate(contentOfs: groupStages)
try self.tournamentStore?.groupStages.addOrUpdate(contentOfs: groupStages)
} catch {
Logger.error(error)
}
@ -90,7 +90,7 @@ struct SchedulerView: View {
do {
try dataStore.tournaments.addOrUpdate(instance: tournament)
try self.tournamentStore.groupStages.addOrUpdate(contentOfs: groupStages)
try self.tournamentStore?.groupStages.addOrUpdate(contentOfs: groupStages)
} catch {
Logger.error(error)
}
@ -131,7 +131,7 @@ struct SchedulerView: View {
Button {
round.updateMatchFormatAndAllMatches(federalFormat)
do {
try self.tournamentStore.rounds.addOrUpdate(instance: round)
try self.tournamentStore?.rounds.addOrUpdate(instance: round)
} catch {
Logger.error(error)
}
@ -176,7 +176,7 @@ struct SchedulerView: View {
Button {
round.updateMatchFormatAndAllMatches(federalFormat)
do {
try self.tournamentStore.rounds.addOrUpdate(instance: round)
try self.tournamentStore?.rounds.addOrUpdate(instance: round)
} catch {
Logger.error(error)
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save