module Tournaments class Standings Row = Struct.new( :participant, :played, :won, :lost, :drawn, :points, :sets_for, :sets_against, :score_for, :score_against, keyword_init: true ) def self.call(group) new(group).call end def initialize(group) @group = group @tournament = group.tournament end def call rows = @group.participants.with_attached_logo_file.map { |p| blank_row(p) }.index_by { |r| r.participant.id } matches = @group.matches.where(result_status: %w[played walkover_home walkover_away]) matches.find_each do |match| apply_match!(rows, match) end rows.values.sort_by { |row| [-row.points, -set_diff(row), -score_diff(row), row.participant.name] } end private def blank_row(participant) Row.new( participant: participant, played: 0, won: 0, lost: 0, drawn: 0, points: 0, sets_for: 0, sets_against: 0, score_for: 0, score_against: 0 ) end def apply_match!(rows, match) home = rows[match.home_participant_id] away = rows[match.away_participant_id] return unless home && away hs = match.home_score.to_i as = match.away_score.to_i home.played += 1 away.played += 1 home.sets_for += hs home.sets_against += as away.sets_for += as away.sets_against += hs home.score_for += hs home.score_against += as away.score_for += as away.score_against += hs if hs == as home.drawn += 1 away.drawn += 1 home.points += split_draw_points away.points += split_draw_points return end if hs > as home.won += 1 away.lost += 1 home.points += win_points(hs, as) away.points += loss_points(hs, as) else away.won += 1 home.lost += 1 away.points += win_points(as, hs) home.points += loss_points(as, hs) end end def settings @settings ||= (@tournament.scoring_settings || {}).stringify_keys end def split_sets? ActiveModel::Type::Boolean.new.cast(settings["split_sets"]) end def win_points(winner_sets, loser_sets) return settings.fetch("win_points", 3).to_i unless split_sets? if winner_sets - loser_sets >= 2 settings.fetch("win_3_0_or_3_1", 3).to_i else settings.fetch("win_3_2", 2).to_i end end def loss_points(winner_sets, loser_sets) return settings.fetch("loss_points", 0).to_i unless split_sets? if winner_sets - loser_sets >= 2 settings.fetch("loss_0_3_or_1_3", 0).to_i else settings.fetch("loss_2_3", 1).to_i end end def split_draw_points settings.fetch("draw_points", 1).to_i end def set_diff(row) row.sets_for - row.sets_against end def score_diff(row) row.score_for - row.score_against end end end