add federal table structure selection

paca_championship
Raz 1 year ago
parent c21d4688e3
commit bfb5d78b04
  1. 11
      PadelClub/Data/TeamRegistration.swift
  2. 56
      PadelClub/Data/Tournament.swift
  3. 191
      PadelClub/Utils/PadelRule.swift
  4. 6
      PadelClub/Views/Team/EditingTeamView.swift
  5. 4
      PadelClub/Views/Team/TeamRowView.swift
  6. 36
      PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift
  7. 48
      PadelClub/Views/Tournament/Screen/TableStructureView.swift

@ -42,7 +42,7 @@ final class TeamRegistration: ModelObject, Storable {
init(tournament: String, groupStage: String? = nil, registrationDate: Date? = nil, callDate: Date? = nil, bracketPosition: Int? = nil, groupStagePosition: Int? = nil, comment: String? = nil, source: String? = nil, sourceValue: String? = nil, logo: String? = nil, name: String? = nil, walkOut: Bool = false, wildCardBracket: Bool = false, wildCardGroupStage: Bool = false, weight: Int = 0, lockedWeight: Int? = nil, confirmationDate: Date? = nil, qualified: Bool = false) {
self.tournament = tournament
self.groupStage = groupStage
self.registrationDate = registrationDate
self.registrationDate = registrationDate ?? Date()
self.callDate = callDate
self.bracketPosition = bracketPosition
self.groupStagePosition = groupStagePosition
@ -523,6 +523,15 @@ final class TeamRegistration: ModelObject, Storable {
return nil
}
func wildcardLabel() -> String? {
if isWildCard() {
let wildcardLabel: String = ["wildcard", (wildCardBracket ? "tableau" : "poule")].joined(separator: " ")
return wildcardLabel
} else {
return nil
}
}
enum CodingKeys: String, CodingKey {
case _id = "id"
case _tournament = "tournament"

@ -1661,16 +1661,61 @@ defer {
deleteGroupStages()
switch preset {
case .manual:
buildGroupStages()
buildBracket()
case .doubleGroupStage:
buildGroupStages()
addNewGroupStageStep()
qualifiedPerGroupStage = 0
groupStageAdditionalQualified = 0
default:
buildGroupStages()
buildBracket()
}
}
func addWildCardIfNeeded(_ count: Int, _ type: MatchType) {
let currentCount = selectedSortedTeams().filter({
if type == .bracket {
return $0.wildCardBracket
} else {
return $0.wildCardGroupStage
}
}).count
if currentCount < count {
let _diff = count - currentCount
addWildCard(_diff, type)
}
}
func addWildCard(_ count: Int, _ type: MatchType) {
let wcs = (0..<count).map { _ in
let team = TeamRegistration(tournament: id)
if type == .bracket {
team.wildCardBracket = true
} else {
team.wildCardGroupStage = true
}
return team
}
do {
try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: wcs)
} catch {
Logger.error(error)
}
}
func addEmptyTeamRegistration(_ count: Int) {
let teams = (0..<count).map { _ in
let team = TeamRegistration(tournament: id)
return team
}
do {
try self.tournamentStore.teamRegistrations.addOrUpdate(contentOfs: teams)
} catch {
Logger.error(error)
}
}
@ -1851,8 +1896,7 @@ defer {
}
func addTeam(_ players: Set<PlayerRegistration>, registrationDate: Date? = nil, name: String? = nil) -> TeamRegistration {
let date: Date = registrationDate ?? Date()
let team = TeamRegistration(tournament: id, registrationDate: date, name: name)
let team = TeamRegistration(tournament: id, registrationDate: registrationDate, name: name)
team.setWeight(from: Array(players), inTournamentCategory: tournamentCategory)
players.forEach { player in
player.teamRegistration = team.id

@ -1680,6 +1680,113 @@ enum PadelTournamentStructurePreset: Int, Identifiable, CaseIterable {
case manual
case doubleGroupStage
case federalStructure_8
case federalStructure_12
case federalStructure_16
case federalStructure_20
case federalStructure_24
case federalStructure_32
case federalStructure_48
case federalStructure_64
// Maximum qualified pairs based on the structure preset
func tableDimension() -> Int {
switch self {
case .federalStructure_8:
return 8
case .federalStructure_12:
return 12
case .federalStructure_16:
return 16
case .federalStructure_20:
return 20
case .federalStructure_24:
return 24
case .federalStructure_32:
return 32
case .federalStructure_48:
return 48
case .federalStructure_64:
return 64
case .manual:
return 24
case .doubleGroupStage:
return 9
}
}
// Wildcards allowed in the Qualifiers
func wildcardBrackets() -> Int {
switch self {
case .federalStructure_8:
return 0
case .federalStructure_12:
return 1
case .federalStructure_16, .federalStructure_20, .federalStructure_24, .federalStructure_32:
return 2
case .federalStructure_48, .federalStructure_64:
return 4
case .manual, .doubleGroupStage:
return 0
}
}
// Wildcards allowed in the Qualifiers
func wildcardQualifiers() -> Int {
switch self {
case .federalStructure_8:
return 0
case .federalStructure_12, .federalStructure_16:
return 1
case .federalStructure_20, .federalStructure_24:
return 2
case .federalStructure_32:
return 4
case .federalStructure_48:
return 6
case .federalStructure_64:
return 8
case .manual, .doubleGroupStage:
return 0
}
}
// Number of teams admitted to the Qualifiers
func teamsInQualifiers() -> Int {
switch self {
case .federalStructure_8:
return 8
case .federalStructure_12:
return 12
case .federalStructure_16:
return 16
case .federalStructure_20:
return 20
case .federalStructure_24:
return 24
case .federalStructure_32:
return 32
case .federalStructure_48:
return 48
case .federalStructure_64:
return 64
case .manual, .doubleGroupStage:
return 0
}
}
// Maximum teams that can qualify from the Qualifiers to the Final Table
func maxTeamsFromQualifiers() -> Int {
switch self {
case .federalStructure_8, .federalStructure_12:
return 2
case .federalStructure_16, .federalStructure_20, .federalStructure_24:
return 4
case .federalStructure_32, .federalStructure_48, .federalStructure_64:
return 8
case .manual, .doubleGroupStage:
return 0
}
}
func localizedStructurePresetTitle() -> String {
switch self {
@ -1687,6 +1794,22 @@ enum PadelTournamentStructurePreset: Int, Identifiable, CaseIterable {
return "Défaut"
case .doubleGroupStage:
return "2 phases de poules"
case .federalStructure_8:
return "Structure fédérale 8"
case .federalStructure_12:
return "Structure fédérale 12"
case .federalStructure_16:
return "Structure fédérale 16"
case .federalStructure_20:
return "Structure fédérale 20"
case .federalStructure_24:
return "Structure fédérale 24"
case .federalStructure_32:
return "Structure fédérale 32"
case .federalStructure_48:
return "Structure fédérale 48"
case .federalStructure_64:
return "Structure fédérale 64"
}
}
@ -1695,9 +1818,75 @@ enum PadelTournamentStructurePreset: Int, Identifiable, CaseIterable {
case .manual:
return "24 équipes, 4 poules de 4, 1 qualifié par poule"
case .doubleGroupStage:
return "Poules qui enchaîne sur une autre phase de poule : les premiers de chaque se retrouve ensemble, puis les 2èmes, etc."
return "Poules qui enchaînent sur une autre phase de poules: les premiers de chaque se retrouvent ensemble, puis les deuxièmes, etc."
case .federalStructure_8:
return "Tableau final à 8 paires, dont 2 qualifiées sortant de qualifications à 8 paires maximum. Aucune wildcard."
case .federalStructure_12, .federalStructure_16, .federalStructure_20, .federalStructure_24, .federalStructure_32, .federalStructure_48, .federalStructure_64:
return "Tableau final à \(tableDimension()) paires, dont \(maxTeamsFromQualifiers()) qualifiées sortant de qualifications à \(teamsInQualifiers()) paires maximum. \(wildcardBrackets()) wildcard\(wildcardBrackets().pluralSuffix) en tableau et \(wildcardQualifiers()) wildcard\(wildcardQualifiers().pluralSuffix) en qualifications."
}
}
func groupStageCount() -> Int {
switch self {
case .manual:
4
case .doubleGroupStage:
3
case .federalStructure_8:
2
case .federalStructure_12:
2
case .federalStructure_16:
4
case .federalStructure_20:
4
case .federalStructure_24:
4
case .federalStructure_32:
8
case .federalStructure_48:
8
case .federalStructure_64:
8
}
}
func teamsPerGroupStage() -> Int {
switch self {
case .manual:
4
case .doubleGroupStage:
3
case .federalStructure_8:
4
case .federalStructure_12:
6
case .federalStructure_16:
4
case .federalStructure_20:
5
case .federalStructure_24:
6
case .federalStructure_32:
4
case .federalStructure_48:
6
case .federalStructure_64:
8
}
}
func qualifiedPerGroupStage() -> Int {
switch self {
case .doubleGroupStage:
0
default:
1
}
}
func hasWildcards() -> Bool {
wildcardBrackets() > 0 || wildcardQualifiers() > 0
}
}
enum TournamentDeadlineType: String, CaseIterable {

@ -88,8 +88,10 @@ struct EditingTeamView: View {
Text("Cette équipe n'a pas été convoquée")
}
Toggle(isOn: hasArrived) {
Text("Équipe sur place")
if team.unsortedPlayers().isEmpty == false {
Toggle(isOn: hasArrived) {
Text("Équipe sur place")
}
}
Toggle(isOn: .init(get: {

@ -30,8 +30,8 @@ struct TeamRowView: View {
Text(round.roundTitle(.wide))
}
if team.isWildCard() {
Text("wildcard").italic().foregroundStyle(.red).font(.caption)
if let wildcardLabel = team.wildcardLabel() {
Text(wildcardLabel).italic().foregroundStyle(.red).font(.caption)
}
}

@ -26,7 +26,7 @@ struct InscriptionManagerView: View {
@Environment(\.dismiss) var dismiss
var tournament: Tournament
@Bindable var tournament: Tournament
var cancelShouldDismiss: Bool = false
@State private var searchField: String = ""
@ -326,6 +326,27 @@ struct InscriptionManagerView: View {
} label: {
Label("Clôturer", systemImage: "lock")
}
Divider()
Section {
Button("+1 en tableau") {
tournament.addWildCard(1, .bracket)
}
if tournament.groupStageCount > 0 {
Button("+1 en poules") {
tournament.addWildCard(1, .groupStage)
}
}
} header: {
Text("Ajout de wildcards")
}
Button("Bloquer une place") {
tournament.addEmptyTeamRegistration(1)
}
Divider()
_sharingTeamsMenuView()
Button {
@ -350,6 +371,14 @@ struct InscriptionManagerView: View {
}
}
} else {
Button("Bloquer une place") {
tournament.addEmptyTeamRegistration(1)
}
Toggle(isOn: $tournament.hideTeamsWeight) {
Text("Masquer les poids des équipes")
}
rankingDateSourcePickerView(showDateInLabel: true)
Divider()
@ -368,7 +397,7 @@ struct InscriptionManagerView: View {
if tournament.inscriptionClosed() == false {
LabelOptions()
} else {
Label("Clôturer", systemImage: "lock")
Label("Clôturé", systemImage: "lock")
}
}
}
@ -376,6 +405,9 @@ struct InscriptionManagerView: View {
.toolbarBackground(.visible, for: .navigationBar)
.navigationTitle("Inscriptions")
.navigationBarTitleDisplayMode(.inline)
.onChange(of: tournament.hideTeamsWeight) {
_save()
}
}
private func _sharingTeamsMenuView() -> some View {

@ -20,6 +20,7 @@ struct TableStructureView: View {
@State private var groupStageAdditionalQualified: Int = 0
@State private var updatedElements: Set<StructureElement> = Set()
@State private var structurePreset: PadelTournamentStructurePreset = .manual
@State private var buildWildcards: Bool = true
@FocusState private var stepperFieldIsFocused: Bool
var qualifiedFromGroupStage: Int {
@ -71,20 +72,11 @@ struct TableStructureView: View {
Text(structurePreset.localizedDescriptionStructurePresetTitle())
}
.onChange(of: structurePreset) {
switch structurePreset {
case .manual:
teamCount = 24
groupStageCount = 4
teamsPerGroupStage = 4
qualifiedPerGroupStage = 1
groupStageAdditionalQualified = 0
case .doubleGroupStage:
teamCount = 9
groupStageCount = 3
teamsPerGroupStage = 3
qualifiedPerGroupStage = 0
groupStageAdditionalQualified = 0
}
teamCount = structurePreset.tableDimension() + structurePreset.teamsInQualifiers() - structurePreset.qualifiedPerGroupStage() * structurePreset.groupStageCount()
groupStageCount = structurePreset.groupStageCount()
teamsPerGroupStage = structurePreset.teamsPerGroupStage()
qualifiedPerGroupStage = structurePreset.qualifiedPerGroupStage()
groupStageAdditionalQualified = 0
}
}
@ -112,7 +104,7 @@ struct TableStructureView: View {
Text("Équipes par poule")
}
if structurePreset == .manual {
if structurePreset != .doubleGroupStage {
LabeledContent {
StepperView(count: $qualifiedPerGroupStage, minimum: 0, maximum: (teamsPerGroupStage-1))
} label: {
@ -136,7 +128,7 @@ struct TableStructureView: View {
}
if groupStageCount > 0 && teamsPerGroupStage > 0 {
if structurePreset == .manual {
if structurePreset != .doubleGroupStage {
LabeledContent {
let mp = teamsPerGroupStage * (teamsPerGroupStage - 1) / 2
Text(mp.formatted())
@ -179,7 +171,7 @@ struct TableStructureView: View {
Section {
let tf = max(teamCount - teamsFromGroupStages + qualifiedFromGroupStage + (groupStageCount > 0 ? groupStageAdditionalQualified : 0), 0)
if groupStageCount > 0 {
if structurePreset == .manual {
if structurePreset != .doubleGroupStage {
LabeledContent {
Text(teamsFromGroupStages.formatted())
} label: {
@ -195,7 +187,7 @@ struct TableStructureView: View {
}
}
if structurePreset == .manual {
if structurePreset != .doubleGroupStage {
LabeledContent {
Text(tsPure.formatted())
} label: {
@ -220,17 +212,25 @@ struct TableStructureView: View {
}
}
} footer: {
if tsPure > 0 && structurePreset == .manual {
if tsPure > 0 && structurePreset != .doubleGroupStage {
if tsPure > teamCount / 2 {
Text("Le nombre de têtes de série ne devrait pas être supérieur à la moitié de l'effectif").foregroundStyle(.red)
Text("Le nombre de têtes de série ne devrait pas être supérieur à la moitié de l'effectif.").foregroundStyle(.red)
} else if tsPure < teamCount / 8 {
Text("À partir du moment où vous avez des têtes de série, leur nombre ne devrait pas être inférieur à 1/8ème de l'effectif").foregroundStyle(.red)
Text("À partir du moment où vous avez des têtes de série, leur nombre ne devrait pas être inférieur à 1/8ème de l'effectif.").foregroundStyle(.red)
} else if tsPure < qualifiedFromGroupStage + groupStageAdditionalQualified {
Text("Le nombre de têtes de série ne devrait pas être inférieur au nombre de paires qualifiées sortantes").foregroundStyle(.red)
Text("Le nombre de têtes de série ne devrait pas être inférieur au nombre de paires qualifiées sortantes.").foregroundStyle(.red)
}
}
}
if structurePreset.hasWildcards() {
Section {
Toggle("Avec wildcards", isOn: $buildWildcards)
} footer: {
Text("Padel Club réservera des places pour eux dans votre liste d'inscription.")
}
}
if tournament.state() != .initial {
Section {
@ -387,6 +387,10 @@ struct TableStructureView: View {
if rebuildEverything {
tournament.deleteAndBuildEverything(preset: structurePreset)
if structurePreset.hasWildcards(), buildWildcards {
tournament.addWildCardIfNeeded(structurePreset.wildcardBrackets(), .bracket)
tournament.addWildCardIfNeeded(structurePreset.wildcardQualifiers(), .groupStage)
}
} else if (rebuildEverything == false && requirements.contains(.groupStage)) {
tournament.deleteGroupStages()
tournament.buildGroupStages()

Loading…
Cancel
Save