1
0
mirror of https://github.com/jcwimer/wrestlingApp synced 2026-03-24 17:04:43 +00:00
Files
wrestlingdev.com/app/models/mat.rb

290 lines
6.8 KiB
Ruby

class Mat < ApplicationRecord
include ActionView::RecordIdentifier
belongs_to :tournament
has_many :matches, dependent: :nullify
has_many :mat_assignment_rules, dependent: :destroy
validates :name, presence: true
QUEUE_SLOTS = %w[queue1 queue2 queue3 queue4].freeze
after_save :clear_queue_matches_cache
after_commit :broadcast_up_matches_board, on: :update, if: :up_matches_queue_changed?
def assign_next_match
slot = first_empty_queue_slot
return true unless slot
match = next_eligible_match
return false unless match
place_match_in_empty_slot!(match, slot)
true
end
def advance_queue!(finished_match = nil)
self.class.transaction do
if finished_match
position = queue_position_for_match(finished_match)
if position == 1
shift_queue_forward!
fill_queue_slots!
elsif position
remove_match_from_queue_and_collapse!(finished_match.id)
else
fill_queue_slots!
end
else
if queue1_match&.finished == 1
shift_queue_forward!
end
fill_queue_slots!
end
end
broadcast_current_match
true
end
def next_eligible_match
# Start with all matches that are either unfinished (nil or 0), have a bout number, and are ordered by bout_number
filtered_matches = Match.where(tournament_id: tournament_id)
.where(finished: [nil, 0]) # finished is nil or 0
.where(mat_id: nil) # mat_id is nil
.where.not(bout_number: nil) # bout_number is not nil
.order(:bout_number)
# Filter out BYE matches
filtered_matches = filtered_matches
.where("loser1_name != ? OR loser1_name IS NULL", "BYE")
.where("loser2_name != ? OR loser2_name IS NULL", "BYE")
# Filter out matches without a wrestlers
filtered_matches = filtered_matches
.where("w1 IS NOT NULL")
.where("w2 IS NOT NULL")
# Apply mat assignment rules
mat_assignment_rules.each do |rule|
if rule.weight_classes.any?
# Ensure weight_classes is treated as an array
filtered_matches = filtered_matches.where(weight_id: Array(rule.weight_classes).map(&:to_i))
end
if rule.bracket_positions.any?
# Ensure bracket_positions is treated as an array
filtered_matches = filtered_matches.where(bracket_position: Array(rule.bracket_positions).map(&:to_s))
end
if rule.rounds.any?
# Ensure rounds is treated as an array
filtered_matches = filtered_matches.where(round: Array(rule.rounds).map(&:to_i))
end
end
# Return the first match in filtered results, or nil if none are left
filtered_matches.first
end
def queue_match_ids
QUEUE_SLOTS.map { |slot| public_send(slot) }
end
# used to prevent N+1 query on each mat
def queue_matches
slot_ids = queue_match_ids
if @queue_matches.nil? || @queue_match_slot_ids != slot_ids
ids = slot_ids.compact
@queue_matches = if ids.empty?
[nil, nil, nil, nil]
else
matches_by_id = Match.where(id: ids)
.includes({ wrestler1: :school }, { wrestler2: :school }, { weight: :matches })
.index_by(&:id)
slot_ids.map { |match_id| match_id ? matches_by_id[match_id] : nil }
end
@queue_match_slot_ids = slot_ids
end
@queue_matches
end
def queue1_match
queue_match_at(1)
end
def queue2_match
queue_match_at(2)
end
def queue3_match
queue_match_at(3)
end
def queue4_match
queue_match_at(4)
end
def queue_position_for_match(match)
return nil unless match
return 1 if queue1 == match.id
return 2 if queue2 == match.id
return 3 if queue3 == match.id
return 4 if queue4 == match.id
nil
end
def remove_match_from_queue_and_collapse!(match_id)
queue_ids = queue_match_ids
return if queue_ids.none? { |id| id == match_id }
queue_ids.map! { |id| id == match_id ? nil : id }
queue_ids = queue_ids.compact
queue_ids += [nil] * (4 - queue_ids.size)
update!(
queue1: queue_ids[0],
queue2: queue_ids[1],
queue3: queue_ids[2],
queue4: queue_ids[3]
)
fill_queue_slots!
broadcast_current_match
end
def assign_match_to_queue!(match, position)
position = position.to_i
raise ArgumentError, "Queue position must be 1-4" unless (1..4).cover?(position)
self.class.transaction do
match.update!(mat_id: id)
remove_match_from_other_mats!(match.id)
queue_ids = queue_match_ids.map { |id| id == match.id ? nil : id }
queue_ids = queue_ids.compact
queue_ids.insert(position - 1, match.id)
bumped_match_id = queue_ids.length > 4 ? queue_ids.pop : nil
queue_ids += [nil] * (4 - queue_ids.length)
update!(
queue1: queue_ids[0],
queue2: queue_ids[1],
queue3: queue_ids[2],
queue4: queue_ids[3]
)
bumped_match = Match.find_by(id: bumped_match_id)
if bumped_match && bumped_match.finished != 1
bumped_match.update!(mat_id: nil)
end
end
broadcast_current_match
end
def clear_queue!
update!(queue1: nil, queue2: nil, queue3: nil, queue4: nil)
broadcast_current_match
end
def unfinished_matches
matches.select{|m| m.finished != 1}.sort_by{|m| m.bout_number}
end
private
def clear_queue_matches_cache
@queue_matches = nil
@queue_match_slot_ids = nil
end
def queue_match_at(position)
queue_matches[position - 1]
end
def first_empty_queue_slot
QUEUE_SLOTS.each_with_index do |slot, index|
return index + 1 if public_send(slot).nil?
end
nil
end
def shift_queue_forward!
update!(
queue1: queue2,
queue2: queue3,
queue3: queue4,
queue4: nil
)
end
def fill_queue_slots!
queue_ids = queue_match_ids
updated = false
QUEUE_SLOTS.each_with_index do |_slot, index|
next if queue_ids[index].present?
match = next_eligible_match
break unless match
queue_ids[index] = match.id
match.update!(mat_id: id)
updated = true
end
if updated
update!(
queue1: queue_ids[0],
queue2: queue_ids[1],
queue3: queue_ids[2],
queue4: queue_ids[3]
)
end
end
def remove_match_from_other_mats!(match_id)
self.class.where.not(id: id)
.where("queue1 = :match_id OR queue2 = :match_id OR queue3 = :match_id OR queue4 = :match_id", match_id: match_id)
.find_each do |mat|
mat.remove_match_from_queue_and_collapse!(match_id)
end
end
def place_match_in_empty_slot!(match, slot)
self.class.transaction do
match.update!(mat_id: id)
remove_match_from_other_mats!(match.id)
update!(slot_key(slot) => match.id)
end
broadcast_current_match
end
def slot_key(slot)
"queue#{slot}"
end
def broadcast_current_match
Turbo::StreamsChannel.broadcast_update_to(
self,
target: dom_id(self, :current_match),
partial: "mats/current_match",
locals: {
mat: self,
match: queue1_match,
next_match: queue2_match,
show_next_bout_button: true
}
)
end
def broadcast_up_matches_board
Tournament.broadcast_up_matches_board(tournament_id)
end
def up_matches_queue_changed?
saved_change_to_queue1? || saved_change_to_queue2? || saved_change_to_queue3? || saved_change_to_queue4?
end
end