scottwillson/racing_on_rails

View on GitHub
app/models/competitions/calculations/points.rb

Summary

Maintainability
A
25 mins
Test Coverage
# frozen_string_literal: true

module Competitions
  module Calculations
    module Points
      def points(result, rules)
        if rules[:use_source_result_points]
          result.points

        elsif result.upgrade
          upgrade_points(result, rules)

        elsif numeric_place?(result)
          points_from_point_schedule result, rules

        elsif rules[:dnf_points] && result.place == "DNF"
          rules[:dnf_points]

        else
          0
        end
      end

      def upgrade_points(result, rules)
        [result.points * rules[:upgrade_points_multiplier], rules[:maximum_upgrade_points]].min
      end

      def numeric_place?(result)
        numeric_place(result) < Float::INFINITY
      end

      def points_from_point_schedule(result, rules)
        return 1 unless point_schedule?(rules)

        return rules[:missing_result_penalty] if rules[:missing_result_penalty] && numeric_place(result) > rules[:missing_result_penalty]

        points = points_from_place(result, rules) || 0
        bonus = result.bonus_points || 0
        place_bonus = place_bonus_points(result, rules)
        team_size = (result.team_size || 1.0).to_f

        ((points + bonus + place_bonus) / team_size) *
          multiplier(result) *
          last_event_multiplier(result, rules) *
          field_size_multiplier(result, rules)
      end

      def point_schedule?(rules)
        rules[:point_schedule] || rules[:points_schedule_from_field_size]
      end

      # Basic: this place is worth this many points
      def points_from_place(result, rules)
        if rules[:points_schedule_from_field_size]
          (result.field_size - numeric_place(result)) + 1
        else
          point_schedule_for_event(result, rules)[numeric_place(result) - 1]
        end
      end

      def point_schedule_for_event(result, rules)
        if rules[:point_schedule].respond_to?(:keys)
          rules[:point_schedule][result.event_id]
        else
          rules[:point_schedule]
        end
      end

      def place_bonus_points(result, rules)
        if rules[:place_bonus] && numeric_place(result) > 0 && numeric_place?(result)
          (rules[:place_bonus][numeric_place(result) - 1]) || 0
        else
          0
        end
      end

      def multiplier(result)
        (result.multiplier || 1).to_f
      end

      def last_event_multiplier(result, rules)
        if rules[:double_points_for_last_event] && last_event?(result, rules)
          2
        else
          1
        end
      end

      def last_event?(result, rules)
        raise(ArgumentError, "End date required to check for last event") unless rules[:end_date]

        result.date == rules[:end_date]
      end

      def field_size_multiplier(result, rules)
        field_size_bonus = rules[:field_size_bonus]
        if (result.multiplier.nil? || result.multiplier == 1) && field_size_bonus && result.field_size >= 75
          1.5
        else
          1.0
        end
      end
    end
  end
end