drawlog wip

paca_championship
Raz 1 year ago
parent 8eadd30759
commit 9291d63d05
  1. 16
      PadelClub.xcodeproj/project.pbxproj
  2. 80
      PadelClub/Data/DrawLog.swift
  3. 16
      PadelClub/Data/Match.swift
  4. 28
      PadelClub/Data/TeamRegistration.swift
  5. 42
      PadelClub/Data/Tournament.swift
  6. 2
      PadelClub/Data/TournamentStore.swift
  7. 9
      PadelClub/Utils/PadelRule.swift
  8. 51
      PadelClub/Views/Round/DrawLogsView.swift
  9. 23
      PadelClub/Views/Round/RoundSettingsView.swift

@ -424,6 +424,12 @@
FF6087EC2BE26A2F004E1E47 /* BroadcastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6087EB2BE26A2F004E1E47 /* BroadcastView.swift */; };
FF6525C32C8C61B400B9498E /* LoserBracketFromGroupStageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6525C22C8C61B400B9498E /* LoserBracketFromGroupStageView.swift */; };
FF663FBE2BE019EC0031AE83 /* TournamentFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF663FBD2BE019EC0031AE83 /* TournamentFilterView.swift */; };
FF6761532CC77D2100CC9BF2 /* DrawLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6761522CC77D1900CC9BF2 /* DrawLog.swift */; };
FF6761542CC77D2100CC9BF2 /* DrawLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6761522CC77D1900CC9BF2 /* DrawLog.swift */; };
FF6761552CC77D2100CC9BF2 /* DrawLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6761522CC77D1900CC9BF2 /* DrawLog.swift */; };
FF6761572CC7803600CC9BF2 /* DrawLogsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6761562CC7803600CC9BF2 /* DrawLogsView.swift */; };
FF6761582CC7803600CC9BF2 /* DrawLogsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6761562CC7803600CC9BF2 /* DrawLogsView.swift */; };
FF6761592CC7803600CC9BF2 /* DrawLogsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6761562CC7803600CC9BF2 /* DrawLogsView.swift */; };
FF6EC8F72B94773200EA7F5A /* RowButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8F62B94773100EA7F5A /* RowButtonView.swift */; };
FF6EC8FB2B94788600EA7F5A /* TournamentButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8FA2B94788600EA7F5A /* TournamentButtonView.swift */; };
FF6EC8FE2B94792300EA7F5A /* Screen.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF6EC8FD2B94792300EA7F5A /* Screen.swift */; };
@ -1066,6 +1072,8 @@
FF6087EB2BE26A2F004E1E47 /* BroadcastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BroadcastView.swift; sourceTree = "<group>"; };
FF6525C22C8C61B400B9498E /* LoserBracketFromGroupStageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoserBracketFromGroupStageView.swift; sourceTree = "<group>"; };
FF663FBD2BE019EC0031AE83 /* TournamentFilterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentFilterView.swift; sourceTree = "<group>"; };
FF6761522CC77D1900CC9BF2 /* DrawLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DrawLog.swift; sourceTree = "<group>"; };
FF6761562CC7803600CC9BF2 /* DrawLogsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DrawLogsView.swift; sourceTree = "<group>"; };
FF6EC8F62B94773100EA7F5A /* RowButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RowButtonView.swift; sourceTree = "<group>"; };
FF6EC8FA2B94788600EA7F5A /* TournamentButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentButtonView.swift; sourceTree = "<group>"; };
FF6EC8FD2B94792300EA7F5A /* Screen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Screen.swift; sourceTree = "<group>"; };
@ -1356,6 +1364,7 @@
FFDB1C6C2BB2A02000F1E467 /* AppSettings.swift */,
FFC91B002BD85C2F00B29808 /* Court.swift */,
FFF116E02BD2A9B600A33B06 /* DateInterval.swift */,
FF6761522CC77D1900CC9BF2 /* DrawLog.swift */,
FF6EC9012B94799200EA7F5A /* Coredata */,
FF6EC9022B9479B900EA7F5A /* Federal */,
);
@ -1871,6 +1880,7 @@
FFC2DCB12BBE75D40046DB9F /* LoserRoundView.swift */,
FFC2DCB32BBE9ECD0046DB9F /* LoserRoundsView.swift */,
FF5647122C0B6F380081F995 /* LoserRoundSettingsView.swift */,
FF6761562CC7803600CC9BF2 /* DrawLogsView.swift */,
);
path = Round;
sourceTree = "<group>";
@ -2331,6 +2341,7 @@
C44B79112BBDA63A00906534 /* Locale+Extensions.swift in Sources */,
FF1F4B742BFA00FC000B4573 /* HtmlService.swift in Sources */,
FF967CEA2BAEC70100A9A3BD /* GroupStage.swift in Sources */,
FF6761542CC77D2100CC9BF2 /* DrawLog.swift in Sources */,
FF1162812BCF945C000C4809 /* TournamentCashierView.swift in Sources */,
C4A47D902B7BBBEC00ADC637 /* StoreManager.swift in Sources */,
FF4AB6BB2B9256D50002987F /* SearchViewModel.swift in Sources */,
@ -2391,6 +2402,7 @@
FF2B51552C7A4DAF00FFF126 /* PlanningByCourtView.swift in Sources */,
FFA6D7852BB0B795003A31F3 /* FileImportManager.swift in Sources */,
FF6EC8FB2B94788600EA7F5A /* TournamentButtonView.swift in Sources */,
FF6761582CC7803600CC9BF2 /* DrawLogsView.swift in Sources */,
FFF8ACCD2B92367B008466FA /* FederalPlayer.swift in Sources */,
FFBF06602BBD9F6D009D6715 /* NavigationViewModel.swift in Sources */,
FF6EC9092B947A5300EA7F5A /* FixedWidthInteger+Extensions.swift in Sources */,
@ -2605,6 +2617,7 @@
FF4CBF952C996C0600151637 /* Locale+Extensions.swift in Sources */,
FF4CBF962C996C0600151637 /* HtmlService.swift in Sources */,
FF4CBF972C996C0600151637 /* GroupStage.swift in Sources */,
FF6761532CC77D2100CC9BF2 /* DrawLog.swift in Sources */,
FF4CBF982C996C0600151637 /* TournamentCashierView.swift in Sources */,
FF4CBF992C996C0600151637 /* StoreManager.swift in Sources */,
FF4CBF9A2C996C0600151637 /* SearchViewModel.swift in Sources */,
@ -2665,6 +2678,7 @@
FF4CBFD12C996C0600151637 /* PlanningByCourtView.swift in Sources */,
FF4CBFD22C996C0600151637 /* FileImportManager.swift in Sources */,
FF4CBFD32C996C0600151637 /* TournamentButtonView.swift in Sources */,
FF6761592CC7803600CC9BF2 /* DrawLogsView.swift in Sources */,
FF4CBFD42C996C0600151637 /* FederalPlayer.swift in Sources */,
FF4CBFD52C996C0600151637 /* NavigationViewModel.swift in Sources */,
FF4CBFD62C996C0600151637 /* FixedWidthInteger+Extensions.swift in Sources */,
@ -2858,6 +2872,7 @@
FF70FB142C90584900129CC2 /* Locale+Extensions.swift in Sources */,
FF70FB152C90584900129CC2 /* HtmlService.swift in Sources */,
FF70FB162C90584900129CC2 /* GroupStage.swift in Sources */,
FF6761552CC77D2100CC9BF2 /* DrawLog.swift in Sources */,
FF70FB172C90584900129CC2 /* TournamentCashierView.swift in Sources */,
FF70FB182C90584900129CC2 /* StoreManager.swift in Sources */,
FF70FB192C90584900129CC2 /* SearchViewModel.swift in Sources */,
@ -2918,6 +2933,7 @@
FF70FB502C90584900129CC2 /* PlanningByCourtView.swift in Sources */,
FF70FB512C90584900129CC2 /* FileImportManager.swift in Sources */,
FF70FB522C90584900129CC2 /* TournamentButtonView.swift in Sources */,
FF6761572CC7803600CC9BF2 /* DrawLogsView.swift in Sources */,
FF70FB532C90584900129CC2 /* FederalPlayer.swift in Sources */,
FF70FB542C90584900129CC2 /* NavigationViewModel.swift in Sources */,
FF70FB552C90584900129CC2 /* FixedWidthInteger+Extensions.swift in Sources */,

@ -0,0 +1,80 @@
//
// DrawLog.swift
// PadelClub
//
// Created by razmig on 22/10/2024.
//
import Foundation
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 false }
static var relationshipNames: [String] = []
var id: String = Store.randomId()
var tournament: String
var drawDate: Date = Date()
var drawSeed: Int?
var drawPosition: Int
var drawTeamPosition: TeamPosition
internal init(id: String = Store.randomId(), tournament: String, drawDate: Date = Date(), drawSeed: Int?, drawPosition: Int, drawTeamPosition: TeamPosition) {
self.id = id
self.tournament = tournament
self.drawDate = drawDate
self.drawSeed = drawSeed
self.drawPosition = drawPosition
self.drawTeamPosition = drawTeamPosition
}
func exportedDrawLog() -> String {
[drawDate.localizedDate(), localizedDrawLogLabel(), localizedDrawBranch()].joined(separator: " ")
}
func localizedDrawSeedLabel() -> String {
if let drawSeed {
return "Tête de série #\(drawSeed + 1)"
} else {
return "Tête de série non trouvé"
}
}
func localizedDrawLogLabel() -> String {
return [localizedDrawSeedLabel(), positionLabel()].joined(separator: " -> ")
}
func localizedDrawBranch() -> String {
drawTeamPosition.localizedBranchLabel()
}
func positionLabel() -> String {
let roundIndex = RoundRule.roundIndex(fromMatchIndex: drawPosition)
return tournamentStore.rounds.first(where: { $0.parent == nil && $0.index == roundIndex })?._matches().first(where: { $0.index == drawPosition })?.roundAndMatchTitle() ?? ""
}
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 _drawPosition = "drawPosition"
case _drawTeamPosition = "drawTeamPosition"
}
func insertOnServer() throws {
self.tournamentStore.drawLogs.writeChangeAndInsertOnServer(instance: self)
}
}

@ -164,22 +164,8 @@ defer {
}
@discardableResult
func lockAndGetSeedPosition(atTeamPosition slot: TeamPosition?, opposingSeeding: Bool = false) -> Int {
func lockAndGetSeedPosition(atTeamPosition teamPosition: TeamPosition) -> Int {
let matchIndex = index
var teamPosition : TeamPosition {
if let slot {
return slot
} else {
let seedRound = RoundRule.roundIndex(fromMatchIndex: matchIndex)
let numberOfMatches = RoundRule.numberOfMatches(forRoundIndex: seedRound)
let isUpper = RoundRule.matchIndexWithinRound(fromMatchIndex: matchIndex) < (numberOfMatches / 2)
var teamPosition = slot ?? (isUpper ? .one : .two)
if opposingSeeding {
teamPosition = slot ?? (isUpper ? .two : .one)
}
return teamPosition
}
}
previousMatch(teamPosition)?.disableMatch()
return matchIndex * 2 + teamPosition.rawValue
}

@ -116,13 +116,37 @@ final class TeamRegistration: ModelObject, Storable {
}
func setSeedPosition(inSpot match: Match, slot: TeamPosition?, opposingSeeding: Bool) {
let seedPosition: Int = match.lockAndGetSeedPosition(atTeamPosition: slot, opposingSeeding: opposingSeeding)
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)
var teamPosition = slot ?? (isUpper ? .one : .two)
if opposingSeeding {
teamPosition = slot ?? (isUpper ? .two : .one)
}
return teamPosition
}
}
let seedPosition: Int = match.lockAndGetSeedPosition(atTeamPosition: teamPosition)
tournamentObject()?.resetTeamScores(in: bracketPosition)
self.bracketPosition = seedPosition
if groupStagePosition != nil && qualified == false {
qualified = true
}
tournamentObject()?.updateTeamScores(in: bracketPosition)
if let tournament = tournamentObject() {
let drawLog = DrawLog(tournament: tournament.id, drawSeed: index(in: tournament.selectedSortedTeams()), drawPosition: match.index, drawTeamPosition: teamPosition)
do {
try tournamentStore.drawLogs.addOrUpdate(instance: drawLog)
} catch {
Logger.error(error)
}
tournament.updateTeamScores(in: bracketPosition)
}
}
func expectedSummonDate() -> Date? {

@ -335,6 +335,12 @@ final class Tournament : ModelObject, Storable {
override func deleteDependencies() throws {
let store = self.tournamentStore
let drawLogs = self.tournamentStore.drawLogs
for drawLog in drawLogs {
try drawLog.deleteDependencies()
}
store.drawLogs.deleteDependencies(drawLogs)
let teams = self.tournamentStore.teamRegistrations
for team in teams {
try team.deleteDependencies()
@ -727,16 +733,13 @@ defer {
let availableSeedOpponentSpot = availableSeedOpponentSpot(inRoundIndex: roundIndex)
let availableSeeds = seeds(inSeedGroup: seedGroup)
if seedGroup == SeedInterval(first: 3, last: 4) {
print("availableSeedGroup == SeedInterval(first: 3, last: 4)")
if availableSeedSpot.count == 6 {
var spots = [Match]()
spots.append(availableSeedSpot[1])
spots.append(availableSeedSpot[4])
spots = spots.shuffled()
for (index, seed) in availableSeeds.enumerated() {
seed.setSeedPosition(inSpot: spots[index], slot: nil, opposingSeeding: false)
}
if seedGroup == SeedInterval(first: 3, last: 4), availableSeedSpot.count == 6 {
var spots = [Match]()
spots.append(availableSeedSpot[1])
spots.append(availableSeedSpot[4])
spots = spots.shuffled()
for (index, seed) in availableSeeds.enumerated() {
seed.setSeedPosition(inSpot: spots[index], slot: nil, opposingSeeding: false)
}
} else {
if availableSeeds.count <= availableSeedSpot.count {
@ -2260,6 +2263,25 @@ defer {
rounds().flatMap { $0.loserRoundsAndChildren().flatMap({ $0._matches() }) }
}
func seedsCount() -> Int {
teamCount - groupStageSpots()
}
func lastDrawnDate() -> Date? {
drawLogs().last?.drawDate
}
func drawLogs() -> [DrawLog] {
self.tournamentStore.drawLogs.sorted(by: \.drawDate)
}
func exportedDrawLogs() -> String {
var logs : [String] = ["Journal des tirages\n\n"]
logs.append(drawLogs().map { $0.exportedDrawLog() }.joined(separator: "\n\n"))
return logs.joined()
}
// MARK: -
func insertOnServer() throws {

@ -26,6 +26,7 @@ class TournamentStore: Store, ObservableObject {
fileprivate(set) var teamScores: StoredCollection<TeamScore> = StoredCollection.placeholder()
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")
@ -51,6 +52,7 @@ class TournamentStore: Store, ObservableObject {
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: false, indexed: indexed)
self.loadCollectionsFromServerIfNoFile()

@ -996,6 +996,15 @@ enum TeamPosition: Int, Identifiable, Hashable, Codable, CaseIterable {
return shortName
}
}
func localizedBranchLabel() -> String {
switch self {
case .one:
return "Branche du haut"
case .two:
return "Branche du bas"
}
}
}
enum SetFormat: Int, Hashable, Codable {

@ -0,0 +1,51 @@
//
// DrawLogsView.swift
// PadelClub
//
// Created by razmig on 22/10/2024.
//
import SwiftUI
import LeStorage
struct DrawLogsView: View {
@Environment(Tournament.self) var tournament
var drawLogs: [DrawLog] {
tournament.drawLogs().reversed()
}
var body: some View {
List {
ForEach(drawLogs) { drawLog in
LabeledContent {
Text(drawLog.localizedDrawBranch())
} label: {
Text(drawLog.localizedDrawSeedLabel())
Text(drawLog.positionLabel())
Text(drawLog.drawDate.localizedDate())
}
}
}
.overlay(content: {
if drawLogs.isEmpty {
ContentUnavailableView("Aucun tirage", systemImage: "dice", description: Text("Aucun tirage au sort n'a été effectué."))
}
})
.toolbar(content: {
ToolbarItem(placement: .topBarTrailing) {
Menu {
ShareLink(item: tournament.exportedDrawLogs()) {
Label("Partager les tirages", systemImage: "square.and.arrow.up")
.labelStyle(.titleAndIcon)
}
} label: {
LabelOptions()
}
}
})
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(.visible, for: .navigationBar)
.navigationTitle("Journal des tirages")
}
}

@ -74,6 +74,29 @@ struct RoundSettingsView: View {
}
}
Section {
NavigationLink {
DrawLogsView()
.environment(tournament)
} label: {
Text("Gestionnaire des tirages au sort")
}
if tournament.rounds().flatMap({ $0.seeds() }).count < tournament.seedsCount(), tournament.lastDrawnDate() != nil {
NavigationLink {
} label: {
Text("Aperçu du décalage")
}
RowButtonView("Décaler les têtes de série", role: .destructive) {
}
}
} footer: {
Text("Vous avez une place libre dans votre tableau. Pour respecter le tirage au sort effectué, vous pouvez décaler les têtes de séries.")
}
// Section {
// RowButtonView("Enabled", role: .destructive) {
// let allMatches = tournament._allMatchesIncludingDisabled()

Loading…
Cancel
Save