scottwillson/racing_on_rails

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

Summary

Maintainability
A
1 hr
Test Coverage
# frozen_string_literal: true

module Competitions
  # See Bar class.
  class OverallBar < Competition
    include OverallBars::Categories
    include OverallBars::Races

    def create_children
      Discipline
        .find_all_bar
        .reject { |discipline| [Discipline[:age_graded], Discipline[:overall], Discipline[:team]].include?(discipline) }
        .reject { |discipline| Bar.year(year).exists?(discipline: discipline.name) }
        .each { |discipline| create_child discipline }
    end

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

    # BAR _does_ have categories, but selection is different than default
    def categories?
      false
    end

    def source_results_query(race)
      super
        .where(
          "(events.discipline not in (:disciplines) and races.category_id in (:categories))
          or (events.discipline in (:disciplines) and races.category_id in (:mtb_categories))",
          disciplines: Discipline["Mountain Bike"].names,
          categories: categories_for(race),
          mtb_categories: mtb_categories_for(race)
        )
    end

    # Array of ids (integers)
    # +race+ category, +race+ category's siblings, and any competition categories
    # Overall BAR does some awesome mappings for MTB and DH
    def mtb_categories_for(race)
      return [] unless race.category

      case race.category.name
      when "Senior Men"
        [
          ::Category.find_or_create_by(name: "Pro Men"),
          ::Category.find_or_create_by(name: "Elite Men"),
          ::Category.find_or_create_by(name: "Category 1 Men")
        ]
      when "Senior Women"
        [
          ::Category.find_or_create_by(name: "Pro Women"),
          ::Category.find_or_create_by(name: "Elite Women"),
          ::Category.find_or_create_by(name: "Category 1 Women")
        ]
      when "Category 3 Men"
        [::Category.find_or_create_by(name: "Category 2 Men")]
      when "Category 3 Women"
        [::Category.find_or_create_by(name: "Category 2 Women")]
      when "Category 4 Men"
        [::Category.find_or_create_by(name: "Category 3 Men")]
      when "Category 5 Men"
        [::Category.find_or_create_by(name: "Category 4 Men"), ::Category.find_or_create_by(name: "Category 5 Men")]
      when "Category 4 Women"
        [::Category.find_or_create_by(name: "Category 3 Women")]
      when "Category 5 Women"
        [::Category.find_or_create_by(name: "Category 4 Women"), ::Category.find_or_create_by(name: "Category 5 Women")]
      else
        [race.category]
      end
    end

    def category_names
      [
        "Athena",
        "Clydesdale",
        "Category 3 Men",
        "Category 3 Women",
        "Category 4 Women",
        "Category 5 Women",
        "Category 4 Men",
        "Category 5 Men",
        "Junior Men",
        "Junior Women",
        "Masters Men 4/5",
        "Masters Men",
        "Masters Women 4",
        "Masters Women",
        "Senior Men",
        "Senior Women",
        "Singlespeed/Fixed",
        "Tandem"
      ]
    end

    def source_event_types
      [Competitions::Bar]
    end

    def after_source_results(results, _)
      results = reject_duplicate_discipline_results(results)
      # BAR Results with the same place are always ties, and never team results
      set_team_size_to_one results
    end

    # If person scored in more than one category that maps to same overall category in a discipline,
    # count only highest-placing category.
    # This typically happens for age-based categories like Masters and Juniors
    # Assume scores sorted in preferred order (usually by points descending)
    def reject_duplicate_discipline_results(source_results)
      filtered_results = []

      source_results.group_by { |r| r["participant_id"] }.each do |_, results|
        results.group_by { |r| r["discipline"] }.each do |_, discipline_results|
          filtered_results << discipline_results.max_by { |r| r["points"].to_f }
        end
      end

      filtered_results
    end

    # 300 points for first place, 299 for second, etc.
    def point_schedule
      (1..300).to_a.reverse
    end

    def maximum_events(_)
      5
    end

    def friendly_name
      "Overall BAR"
    end

    def default_discipline
      "Overall"
    end
  end
end