scottwillson/racing_on_rails

View on GitHub
app/models/competitions/bar.rb

Summary

Maintainability
A
1 hr
Test Coverage
# frozen_string_literal: true

module Competitions
  # Best All-around Rider Competition. Assigns points for each top-15 placing in major Disciplines: road, track, etc.
  # Calculates a BAR for each Discipline, and an Overall BAR that combines all the discipline BARs.
  # Also calculates a team BAR.
  # Recalculating the BAR at years-end can take nearly 30 minutes even on a fast server. Recalculation must be manually triggered.
  # This class implements a number of OBRA-specific rules and should be generalized and simplified.
  # The BAR categories and disciplines are all configured in the database. Race categories need to have a bar_category_id to
  # show up in the BAR; disciplines must exist in the disciplines table and discipline_bar_categories.
  class Bar < Competition
    include Bars::Categories
    include Bars::Discipline
    include Bars::Points

    def self.calculate!(year = Time.zone.today.year)
      ActiveSupport::Notifications.instrument "calculate.#{name}.competitions.racing_on_rails" do
        transaction do
          year = year.to_i if year.is_a?(String)
          date = Date.new(year, 1, 1)

          overall_bar = OverallBar.find_or_create_for_year(year)
          overall_bar.set_date

          # Age Graded BAR, Team BAR and Overall BAR do their own calculations
          ::Discipline.find_all_bar.reject do |discipline|
            [::Discipline[:age_graded], ::Discipline[:overall], ::Discipline[:team]].include?(discipline)
          end.each do |discipline|
            bar = Bar.where(date: date, discipline: discipline.name).first
            next if bar

            Bar.create!(
              parent: overall_bar,
              name: "#{year} #{discipline.name} BAR",
              date: date,
              discipline: discipline.name
            )
          end

          Bar.where(date: date).find_each do |bar|
            bar.set_date
            bar.delete_races
            bar.create_races
            # Could bulk load all Event and Races at this point, but hardly seems to matter
            bar.calculate!
          end
        end
      end
      # Don't return the entire populated instance!
      true
    end

    def field_size_bonus?
      true
    end

    def source_event_types
      [Event,
       SingleDayEvent,
       MultiDayEvent,
       Series,
       WeeklySeries,
       Competitions::BlindDateAtTheDairyMonthlyStandings,
       Competitions::PortlandShortTrackSeries::MonthlyStandings,
       Competitions::PortlandShortTrackSeries::Overall,
       Competitions::TaborOverall]
    end

    def source_results_query(race)
      super
        .where(bar: true)
        .where("events.sanctioned_by" => RacingAssociation.current.default_sanctioned_by)
        .where("events.discipline in (:disciplines)
            or (events.discipline is null and parents_events.discipline in (:disciplines))
            or (events.discipline is null and parents_events.discipline is null and parents_events_2.discipline in (:disciplines))",
               disciplines: disciplines_for(race))
    end

    def after_source_results(results, _race)
      results.each do |result|
        result["multiplier"] = result["race_bar_points"] || result["event_bar_points"] || result["parent_bar_points"] || result["parent_parent_bar_points"]
      end
    end

    def category_names
      ::Discipline[discipline].bar_categories.map(&:name)
    end

    def friendly_name
      "BAR"
    end
  end
end