Filtra le partite visibili lato client/server, corregge il parsing date Rails su Android, semplifica lo step Partita del wizard e restringe l'API ai campi effettivamente usati (campionato, set e scoring_rules). Co-authored-by: Cursor <cursoragent@cursor.com>
95 lines
2.7 KiB
Ruby
95 lines
2.7 KiB
Ruby
class Match < ApplicationRecord
|
|
belongs_to :team
|
|
has_many :stream_sessions, dependent: :destroy
|
|
|
|
ACTIVE_SESSION_STATUSES = %w[live connecting reconnecting paused].freeze
|
|
|
|
validates :opponent_name, presence: true
|
|
validates :sets_to_win, numericality: { greater_than: 0, less_than: 6 }
|
|
validate :scoring_rules_shape, if: -> { scoring_rules.present? }
|
|
|
|
# category: campionato / descrizione torneo (facoltativo, es. "Serie C").
|
|
|
|
# Partite ancora da giocare in diretta (orario nel futuro).
|
|
scope :scheduled_for_live, -> {
|
|
where.not(scheduled_at: nil).where("scheduled_at >= ?", Time.zone.now)
|
|
}
|
|
|
|
scope :scheduled_upcoming, -> { scheduled_for_live }
|
|
|
|
def scheduled_upcoming?
|
|
scheduled_at.present? && scheduled_at >= Time.zone.now
|
|
end
|
|
|
|
def scheduled_on_calendar?
|
|
scheduled_at.present? && scheduled_at >= Time.zone.now.beginning_of_day
|
|
end
|
|
|
|
def active_stream_session
|
|
stream_sessions.sort_by { |s| s.created_at }.reverse
|
|
.find { |s| !%w[ended error].include?(s.status) }
|
|
end
|
|
|
|
def deletable?
|
|
session = active_stream_session
|
|
session.nil? || !session.resumable?
|
|
end
|
|
|
|
def public_status_label
|
|
active = active_stream_session
|
|
return "In corso / da riprendere" if active
|
|
return "Programmata" if scheduled_upcoming?
|
|
return "Pronta" if scheduled_at.present?
|
|
|
|
"Senza orario"
|
|
end
|
|
|
|
scope :search_teams_or_opponents, lambda { |query|
|
|
q = query.to_s.strip
|
|
return all if q.blank?
|
|
|
|
term = "%#{sanitize_sql_like(q)}%"
|
|
joins(team: :club).where(
|
|
"teams.name ILIKE :term OR matches.opponent_name ILIKE :term OR clubs.name ILIKE :term OR matches.location ILIKE :term",
|
|
term: term
|
|
)
|
|
}
|
|
|
|
def broadcast_active?
|
|
stream_sessions.where(status: ACTIVE_SESSION_STATUSES).exists?
|
|
end
|
|
|
|
def stream_completed?
|
|
stream_sessions.where(status: %w[ended error]).exists?
|
|
end
|
|
|
|
# Hub app coach: dirette da riprendere o partite programmate future.
|
|
# Le bozze «Avversario» abbandonate (senza sessione) non compaiono.
|
|
def coach_hub_visible?
|
|
active = active_stream_session
|
|
return true if active&.resumable?
|
|
return true if active&.idle?
|
|
|
|
return false if stream_completed?
|
|
|
|
scheduled_upcoming?
|
|
end
|
|
|
|
private
|
|
|
|
def scoring_rules_shape
|
|
rules = scoring_rules.is_a?(Hash) ? scoring_rules : {}
|
|
%w[points_per_set points_deciding_set min_point_lead].each do |key|
|
|
val = rules[key] || rules[key.to_sym]
|
|
next if val.blank?
|
|
|
|
unless val.is_a?(Integer) || val.to_s.match?(/\A\d+\z/)
|
|
errors.add(:scoring_rules, "#{key} non valido")
|
|
next
|
|
end
|
|
int_val = val.to_i
|
|
errors.add(:scoring_rules, "#{key} deve essere positivo") if int_val < 1
|
|
end
|
|
end
|
|
end
|