fix delete dependencies

fix structure deletion / rebuilding
fix spin draw
club_update
Razmig Sarkissian 1 year ago
parent 38bbbb3246
commit 3ced902613
  1. 7
      PadelClub/Data/Event.swift
  2. 6
      PadelClub/Data/GroupStage.swift
  3. 6
      PadelClub/Data/Match.swift
  4. 73
      PadelClub/Data/Round.swift
  5. 49
      PadelClub/Data/Tournament.swift
  6. 25
      PadelClub/ViewModel/SeedInterval.swift
  7. 42
      PadelClub/Views/Components/FortuneWheelView.swift
  8. 100
      PadelClub/Views/Round/RoundView.swift

@ -32,7 +32,12 @@ final class Event: ModelObject, Storable {
}
override func deleteDependencies() throws {
DataStore.shared.tournaments.deleteDependencies(self.tournaments)
let tournaments = self.tournaments
for tournament in tournaments {
try tournament.deleteDependencies()
}
DataStore.shared.tournaments.deleteDependencies(tournaments)
DataStore.shared.dateIntervals.deleteDependencies(self.courtsUnavailability)
}

@ -381,7 +381,11 @@ final class GroupStage: ModelObject, Storable {
}
override func deleteDependencies() throws {
self.tournamentStore.matches.deleteDependencies(self._matches())
let matches = self._matches()
for match in matches {
try match.deleteDependencies()
}
self.tournamentStore.matches.deleteDependencies(matches)
}
func encode(to encoder: Encoder) throws {

@ -78,7 +78,11 @@ final class Match: ModelObject, Storable {
guard let tournament = self.currentTournament() else {
return
}
tournament.tournamentStore.teamScores.deleteDependencies(self.teamScores)
let teamScores = self.teamScores
for teamScore in teamScores {
try teamScore.deleteDependencies()
}
tournament.tournamentStore.teamScores.deleteDependencies(teamScores)
}
func indexInRound(in matches: [Match]? = nil) -> Int {

@ -455,12 +455,12 @@ defer {
return nextRound()?.isRankDisabled() == false
}
func seedInterval(expanded: Bool = false) -> SeedInterval? {
func seedInterval(initialMode: Bool = false) -> SeedInterval? {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
let duration = Duration.milliseconds(Date().timeIntervalSince(start) * 1_000)
print("func seedInterval(expanded: Bool = false)", duration.formatted(.units(allowed: [.seconds, .milliseconds])))
print("func seedInterval(initialMode)", initialMode, duration.formatted(.units(allowed: [.seconds, .milliseconds])))
}
#endif
@ -477,24 +477,24 @@ defer {
}
if let previousRound = previousRound() {
if previousRound.enabledMatches().isEmpty == false && expanded == false {
return previousRound.seedInterval()?.chunks()?.first
if (previousRound.enabledMatches().isEmpty == false || initialMode) {
return previousRound.seedInterval(initialMode: initialMode)?.chunks()?.first
} else {
return previousRound.previousRound()?.seedInterval()
return previousRound.previousRound()?.seedInterval(initialMode: initialMode)
}
} else if let parentRound {
if parentRound.parent == nil && expanded == false {
return parentRound.seedInterval()
if parentRound.parent == nil {
return parentRound.seedInterval(initialMode: initialMode)
}
return parentRound.seedInterval()?.chunks()?.last
return parentRound.seedInterval(initialMode: initialMode)?.chunks()?.last
}
return nil
}
func roundTitle(_ displayStyle: DisplayStyle = .wide) -> String {
func roundTitle(_ displayStyle: DisplayStyle = .wide, initialMode: Bool = false) -> String {
if parent != nil {
if let seedInterval = seedInterval() {
if let seedInterval = seedInterval(initialMode: initialMode) {
return seedInterval.localizedLabel(displayStyle)
}
print("Round pas trouvé", id, parent, index)
@ -574,19 +574,8 @@ defer {
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)
}
do {
try self.tournamentStore.matches.addOrUpdate(contentOfs: matches)
} catch {
Logger.error(error)
}
matches.forEach { //0 is final match
let roundIndex = RoundRule.roundIndex(fromMatchIndex: $0.index)
let round = rounds[roundIndex]
$0.name = round.roundTitle()
return Match(round: round.id, index: $0, matchFormat: loserBracketMatchFormat, name: round.roundTitle(initialMode: true))
//initial mode let the roundTitle give a name without considering the playable match
}
do {
@ -598,29 +587,6 @@ defer {
loserRounds().forEach { round in
round.buildLoserBracket()
}
/*
return Match(round: round.id, index: $0, matchFormat: loserBracketMatchFormat)
}
do {
try DataStore.shared.matches.addOrUpdate(contentOfs: matches)
} catch {
Logger.error(error)
}
matches.forEach {
$0.name = $0.roundObject?.roundTitle()
}
do {
try DataStore.shared.matches.addOrUpdate(contentOfs: matches)
} catch {
Logger.error(error)
}
*/
}
var parentRound: Round? {
@ -657,8 +623,19 @@ defer {
}
override func deleteDependencies() throws {
self.tournamentStore.matches.deleteDependencies(self._matches())
self.tournamentStore.rounds.deleteDependencies(self.loserRoundsAndChildren())
let matches = self._matches()
for match in matches {
try match.deleteDependencies()
}
self.tournamentStore.matches.deleteDependencies(matches)
let loserRounds = self.loserRounds()
for round in loserRounds {
try round.deleteDependencies()
}
self.tournamentStore.rounds.deleteDependencies(loserRounds)
}
enum CodingKeys: String, CodingKey {

@ -347,9 +347,24 @@ final class Tournament : ModelObject, Storable {
override func deleteDependencies() throws {
let store = self.tournamentStore
store.teamRegistrations.deleteDependencies(self.unsortedTeams())
store.groupStages.deleteDependencies(self.groupStages())
store.rounds.deleteDependencies(self.rounds())
let teams = self.unsortedTeams()
for team in teams {
try team.deleteDependencies()
}
store.teamRegistrations.deleteDependencies(teams)
let groups = self.groupStages()
for group in groups {
try group.deleteDependencies()
}
store.groupStages.deleteDependencies(groups)
let rounds = self.rounds()
for round in rounds {
try round.deleteDependencies()
}
store.rounds.deleteDependencies(rounds)
store.matchSchedulers.deleteDependencies(self._matchSchedulers())
}
@ -1172,12 +1187,12 @@ defer {
let disabledIds = playedMatches.flatMap({ $0.teamScores.compactMap({ $0.teamRegistration }) }).filter({ ids.contains($0) == false })
if disabledIds.isEmpty == false {
_removeStrings(from: &teams, stringsToRemove: disabledIds)
teams[interval.computedLast] = disabledIds
teams[interval.last] = disabledIds
let teamNames : [String] = disabledIds.compactMap {
let t : TeamRegistration? = Store.main.findById($0)
return t
}.map { $0.canonicalName }
print("winners.isEmpty", "\(interval.computedLast) : ", teamNames)
print("winners.isEmpty", "\(interval.last) : ", teamNames)
disabledIds.forEach {
ids.insert($0)
}
@ -1185,23 +1200,23 @@ defer {
} else {
if winners.isEmpty == false {
_removeStrings(from: &teams, stringsToRemove: winners)
teams[interval.computedFirst + winners.count - 1] = winners
teams[interval.first + winners.count - 1] = winners
let teamNames : [String] = winners.compactMap {
let t: TeamRegistration? = Store.main.findById($0)
return t
}.map { $0.canonicalName }
print("winners", "\(interval.computedFirst + winners.count - 1) : ", teamNames)
print("winners", "\(interval.last + winners.count - 1) : ", teamNames)
winners.forEach { ids.insert($0) }
}
if losers.isEmpty == false {
_removeStrings(from: &teams, stringsToRemove: losers)
teams[interval.computedLast] = losers
teams[interval.last] = losers
let loserTeamNames : [String] = losers.compactMap {
let t: TeamRegistration? = Store.main.findById($0)
return t
}.map { $0.canonicalName }
print("losers", "\(interval.computedLast) : ", loserTeamNames)
print("losers", "\(interval.last) : ", loserTeamNames)
losers.forEach { ids.insert($0) }
}
}
@ -1514,6 +1529,7 @@ defer {
}
func deleteAndBuildEverything() {
resetBracketPosition()
deleteStructure()
deleteGroupStages()
buildGroupStages()
@ -1576,7 +1592,7 @@ defer {
Logger.error(error)
}
self.rounds().forEach { round in
rounds.forEach { round in
round.buildLoserBracket()
}
}
@ -1609,6 +1625,9 @@ defer {
} catch {
Logger.error(error)
}
}
func resetBracketPosition() {
unsortedTeams().forEach({ $0.bracketPosition = nil })
do {
try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: unsortedTeams())
@ -1623,16 +1642,6 @@ defer {
} catch {
Logger.error(error)
}
do {
unsortedTeams().forEach({
$0.groupStage = nil
$0.groupStagePosition = nil
})
try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: unsortedTeams())
} catch {
Logger.error(error)
}
}
func refreshGroupStages() {

@ -10,11 +10,10 @@ import Foundation
struct SeedInterval: Hashable, Comparable {
let first: Int
let last: Int
var reduce: Int = 0
func pointsRange(tournamentLevel: TournamentLevel, teamsCount: Int) -> String {
let range = [tournamentLevel.points(for: last - 1 - reduce, count: teamsCount),
tournamentLevel.points(for: first - 1 - reduce, count: teamsCount)]
let range = [tournamentLevel.points(for: last - 1, count: teamsCount),
tournamentLevel.points(for: first - 1, count: teamsCount)]
return range.map { $0.formatted(.number.sign(strategy: .always())) }.joined(separator: " / ") + " pts"
}
@ -38,12 +37,12 @@ struct SeedInterval: Hashable, Comparable {
if dimension > 3 {
let split = dimension / 2
if split%2 == 0 {
let firstHalf = SeedInterval(first: first, last: first + split - 1, reduce: reduce)
let secondHalf = SeedInterval(first: first + split, last: last, reduce: reduce)
let firstHalf = SeedInterval(first: first, last: first + split - 1)
let secondHalf = SeedInterval(first: first + split, last: last)
return [firstHalf, secondHalf]
} else {
let firstHalf = SeedInterval(first: first, last: first + split, reduce: reduce)
let secondHalf = SeedInterval(first: first + split + 1, last: last, reduce: reduce)
let firstHalf = SeedInterval(first: first, last: first + split)
let secondHalf = SeedInterval(first: first + split + 1, last: last)
return [firstHalf, secondHalf]
}
} else {
@ -51,19 +50,11 @@ struct SeedInterval: Hashable, Comparable {
}
}
var computedLast: Int {
last - reduce
}
var computedFirst: Int {
first - reduce
}
func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
if dimension < 2 {
return "#\(first - reduce) / #\(last - reduce)"
return "#\(first) / #\(last)"
} else {
return "#\(first - reduce) à #\(last - reduce)"
return "#\(first) à #\(last)"
}
}
}

@ -62,13 +62,18 @@ struct SpinDrawView: View {
let drawees: [any SpinDrawable]
@State var segments: [any SpinDrawable]
var autoMode: Bool = false
let completion: ([DrawResult]) async -> Void // Completion closure
let completion: ([DrawResult]) -> Void // Completion closure
@State private var drawCount: Int = 0
@State private var draws: [DrawResult] = [DrawResult]()
@State private var drawOptions: [DrawOption] = [DrawOption]()
@State private var selectedIndex: Int?
@State private var disabled: Bool = false
@State private var validating: Bool = false
var scrollDisabled: Bool {
drawCount < drawees.count || selectedIndex != nil
}
var body: some View {
List {
@ -77,11 +82,9 @@ struct SpinDrawView: View {
_validationLabelView(drawee: drawCount, result: segments[draws.last!.drawIndex])
if autoMode == false || drawCount == drawees.count {
RowButtonView("Valider le tirage") {
await completion(draws)
completion(draws)
dismiss()
}
} else {
Text("Prochain tirage en préparation")
}
}
} else if drawCount < drawees.count {
@ -135,16 +138,17 @@ struct SpinDrawView: View {
}
}
} else {
Section {
Text("Tous les tirages sont terminés")
ForEach(draws) { drawResult in
ForEach(draws) { drawResult in
Section {
_validationLabelView(drawee: drawResult.drawee, result: segments[drawResult.drawIndex])
}
}
RowButtonView("Valider les tirages") {
await completion(draws)
dismiss()
Section {
RowButtonView("Valider les tirages") {
await completion(draws)
dismiss()
}
}
}
@ -164,6 +168,22 @@ struct SpinDrawView: View {
}
.disabled(disabled || autoMode)
}
if scrollDisabled == false {
ToolbarItem(placement: .topBarTrailing) {
Button {
validating = true
completion(draws)
dismiss()
} label: {
Text("Tout valider")
}
}
ToolbarItem(placement: .status) {
Text("Tous les tirages sont terminés")
}
}
}
.navigationBarBackButtonHidden()
.navigationTitle("Tirage au sort")
@ -171,7 +191,7 @@ struct SpinDrawView: View {
.toolbarBackground(.visible, for: .navigationBar)
.toolbar(.hidden, for: .tabBar)
.listStyle(.insetGrouped)
.scrollDisabled(true)
.scrollDisabled(scrollDisabled)
.interactiveDismissDisabled()
.onAppear {
for (index, segment) in segments.enumerated() {

@ -28,7 +28,7 @@ struct RoundView: View {
return self.tournament.tournamentStore
}
private func _getAvailableSeedGroup() async {
private func _getAvailableSeedGroup() {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
@ -40,7 +40,7 @@ struct RoundView: View {
availableSeedGroup = tournament.seedGroupAvailable(atRoundIndex: upperRound.round.index)
}
private func _getSpaceLeft() async {
private func _getSpaceLeft() {
#if DEBUG_TIME //DEBUGING TIME
let start = Date()
defer {
@ -130,12 +130,11 @@ struct RoundView: View {
Section {
RowButtonView("Placer \(availableSeedGroup.localizedLabel())" + ((availableSeedGroup.isFixed() == false) ? " au hasard" : "")) {
tournament.setSeeds(inRoundIndex: upperRound.round.index, inSeedGroup: availableSeedGroup)
await _save()
_save()
if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty {
self.isEditingTournamentSeed.wrappedValue = false
}
await _getSpaceLeft()
await _getAvailableSeedGroup()
_prepareRound()
}
} footer: {
if availableSeedGroup.isFixed() == false {
@ -161,17 +160,14 @@ struct RoundView: View {
ForEach(availableQualifiedTeams) { team in
NavigationLink {
SpinDrawView(drawees: [team], segments: spaceLeft) { results in
Task {
results.forEach { drawResult in
team.setSeedPosition(inSpot: spaceLeft[drawResult.drawIndex], slot: nil, opposingSeeding: true)
}
await _save()
if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty {
self.isEditingTournamentSeed.wrappedValue = false
}
await _getSpaceLeft()
await _getAvailableSeedGroup()
results.forEach { drawResult in
team.setSeedPosition(inSpot: spaceLeft[drawResult.drawIndex], slot: nil, opposingSeeding: true)
}
_save()
if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty {
self.isEditingTournamentSeed.wrappedValue = false
}
_prepareRound()
}
} label: {
TeamRowView(team: team, displayCallDate: false)
@ -193,17 +189,14 @@ struct RoundView: View {
ForEach(availableSeeds) { team in
NavigationLink {
SpinDrawView(drawees: [team], segments: seedSpaceLeft) { results in
Task {
results.forEach { drawResult in
team.setSeedPosition(inSpot: seedSpaceLeft[drawResult.drawIndex], slot: nil, opposingSeeding: false)
}
await _save()
if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty {
self.isEditingTournamentSeed.wrappedValue = false
}
await _getSpaceLeft()
await _getAvailableSeedGroup()
results.forEach { drawResult in
team.setSeedPosition(inSpot: seedSpaceLeft[drawResult.drawIndex], slot: nil, opposingSeeding: false)
}
_save()
if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty {
self.isEditingTournamentSeed.wrappedValue = false
}
_prepareRound()
}
} label: {
TeamRowView(team: team, displayCallDate: false)
@ -221,17 +214,14 @@ struct RoundView: View {
ForEach(availableSeeds) { team in
NavigationLink {
SpinDrawView(drawees: [team], segments: spaceLeft) { results in
Task {
results.forEach { drawResult in
team.setSeedPosition(inSpot: spaceLeft[drawResult.drawIndex], slot: nil, opposingSeeding: true)
}
await _save()
if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty {
self.isEditingTournamentSeed.wrappedValue = false
}
await _getSpaceLeft()
await _getAvailableSeedGroup()
results.forEach { drawResult in
team.setSeedPosition(inSpot: spaceLeft[drawResult.drawIndex], slot: nil, opposingSeeding: true)
}
_save()
if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty {
self.isEditingTournamentSeed.wrappedValue = false
}
_prepareRound()
}
} label: {
TeamRowView(team: team, displayCallDate: false)
@ -283,9 +273,7 @@ struct RoundView: View {
PrintSettingsView(tournament: tournament)
}
.onAppear {
Task {
await _prepareRound()
}
_prepareRound()
let seeds = upperRound.round.seeds()
SlideToDeleteSeedTip.seeds = seeds.count
PrintTip.seeds = seeds.count
@ -294,17 +282,19 @@ struct RoundView: View {
.fullScreenCover(isPresented: showVisualDrawView) {
if let availableSeedGroup = selectedSeedGroup {
let seeds = tournament.seeds(inSeedGroup: availableSeedGroup)
let availableSeedSpot = tournament.availableSeedSpot(inRoundIndex: upperRound.round.index)
let opposingSeeding = seedSpaceLeft.isEmpty ? true : false
let availableSeedSpot = opposingSeeding ? spaceLeft : seedSpaceLeft
NavigationStack {
SpinDrawView(drawees: seeds, segments: availableSeedSpot, autoMode: true) { draws in
Task {
draws.forEach { drawResult in
seeds[drawResult.drawee].setSeedPosition(inSpot: availableSeedSpot[drawResult.drawIndex], slot: nil, opposingSeeding: false)
}
await _save()
if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty {
self.isEditingTournamentSeed.wrappedValue = false
}
draws.forEach { drawResult in
seeds[drawResult.drawee].setSeedPosition(inSpot: availableSeedSpot[drawResult.drawIndex], slot: nil, opposingSeeding: opposingSeeding)
}
_save()
_prepareRound()
if tournament.availableSeeds().isEmpty && tournament.availableQualifiedTeams().isEmpty {
self.isEditingTournamentSeed.wrappedValue = false
}
}
}
@ -315,9 +305,7 @@ struct RoundView: View {
ToolbarItem(placement: .topBarTrailing) {
Button(isEditingTournamentSeed.wrappedValue == true ? "Valider" : "Modifier") {
if isEditingTournamentSeed.wrappedValue == true {
Task {
await _save()
}
_save()
}
isEditingTournamentSeed.wrappedValue.toggle()
}
@ -325,7 +313,7 @@ struct RoundView: View {
}
}
private func _save() async {
private func _save() {
do {
try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: tournament.unsortedTeams())
} catch {
@ -348,13 +336,9 @@ struct RoundView: View {
}
}
private func _prepareRound() async {
Task {
await _getSpaceLeft()
}
Task {
await _getAvailableSeedGroup()
}
private func _prepareRound() {
_getSpaceLeft()
_getAvailableSeedGroup()
}
}

Loading…
Cancel
Save