Class MbraBar
In: app/models/competitions/mbra_bar.rb
Parent: Competition

MBRA Best All-around Rider Competition. Calculates a BAR for each Discipline The BAR categories and disciplines are all configured in the databsase. 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.

This class implements a number of MBRA-specific rules.

Methods

Public Class methods

[Source]

    # File app/models/competitions/mbra_bar.rb, line 10
10:   def MbraBar.calculate!(year = Date.today.year)
11:     benchmark(name, Logger::INFO, false) {
12:       transaction do
13:         year = year.to_i if year.is_a?(String)
14:         date = Date.new(year, 1, 1)
15: 
16:         # Age Graded BAR and Overall BAR do their own calculations
17:         Discipline.find_all_bar.reject { |discipline|
18:           discipline == Discipline[:age_graded] || discipline == Discipline[:overall]
19:         }.each do |discipline|
20:           bar = MbraBar.find(:first, :conditions => { :date => date, :discipline => discipline.name })
21:           unless bar
22:             bar = MbraBar.create!(
23:               :name => "#{year} #{discipline.name} BAR",
24:               :date => date,
25:               :discipline => discipline.name
26:             )
27:           end
28:         end
29: 
30:         MbraBar.find(:all, :conditions => { :date => date }).each do |bar|
31:           bar.destroy_races
32:           bar.create_races
33:           bar.calculate_threshold_number_of_races
34:           bar.calculate!
35:           bar.after_create_all_results
36:         end
37:       end
38:     }
39:     # Don't return the entire populated instance!
40:     true
41:   end

[Source]

    # File app/models/competitions/mbra_bar.rb, line 43
43:   def MbraBar.find_by_year_and_discipline(year, discipline_name)
44:     MbraBar.find(:first, :conditions => { :date => Date.new(year), :discipline => discipline_name })
45:   end

Public Instance methods

[Source]

     # File app/models/competitions/mbra_bar.rb, line 135
135:   def after_create_all_results
136:     # Riders upgrading during the season will take 1/2 of their points (up to 30 points max)
137:     # with them to the higher category. The upgrading rider's accumulated points are allowed to stand at the
138:     # lower category.
139:     # After computing BAR results, look for Cat n BAR results for anyone in the Cat n - 1 BAR and add in half their Cat n points.
140:     [
141:       ["cat_up" => "Cat 1/2 Men", "cat_down" => "Cat 3 Men"],
142:       ["cat_up" => "Cat 3 Men", "cat_down" => "Cat 4 Men"],
143:       ["cat_up" => "Cat 4 Men", "cat_down" => "Cat 5 Men"],
144:       ["cat_up" => "Cat 1/2/3 Women", "cat_down" => "Cat 4 Women"]
145:     ].each do |category_pair|
146:       cat = category_pair[0]["cat_up"]
147:       cat_up_race = self.races.detect { |r| r.category.name == category_pair[0]["cat_up"] }
148:       cat_down_race = self.races.detect { |r| r.category.name == category_pair[0]["cat_down"] }
149:       cat_up_race.results.each do |up_result|
150:         take_with_result = cat_down_race.results.detect { |down_result| down_result.person == up_result.person }
151:         unless take_with_result.blank?
152:           upgrade_points = (take_with_result.points/2).to_i
153:           upgrade_points = 30 if upgrade_points > 30
154:           up_result.points += upgrade_points
155:           upgrade_note = "Point total includes #{upgrade_points} upgrade points."
156:           if up_result.notes.blank?
157:             up_result.notes = upgrade_note
158:           else  
159:             up_result.notes += " #{upgrade_note}"
160:           end
161:           up_result.save!
162:         end
163:       end unless (cat_up_race.blank? || cat_down_race.blank?)
164: 
165:     end
166:   end

[Source]

     # File app/models/competitions/mbra_bar.rb, line 118
118:   def after_create_competition_results_for(race)
119:     # 70% of the races (rounded to the nearest whole number) count in the
120:     # individual series standings. Riders who compete in more than 70% of the
121:     # races will throw out their weakest results.
122:     race.results.each do |result|
123:       # Don't bother sorting scores unless we need to drop some
124:       if result.scores.size > @threshold_number_of_races
125:         result.scores.sort! { |x, y| y.points <=> x.points }
126:         for lowest_score in result.scores[@threshold_number_of_races..(result.scores.size - 1)]
127:           result.scores.destroy(lowest_score)
128:         end
129:         # Rails destroys Score in database, but doesn't update the current association
130:         result.scores(true)
131:       end
132:     end
133:   end

Riders obtain points inversely proportional to the starting field size, plus bonuses for first, second and third place (6, 3, and 1 points respectively). DNF: 1/2 point

[Source]

    # File app/models/competitions/mbra_bar.rb, line 59
59:   def calculate_point_schedule(field_size)
60:     @point_schedule = [0]
61:     field_size.downto(1) { |i| @point_schedule << i }
62:     @point_schedule[1] += 6 if @point_schedule.length > 1
63:     @point_schedule[2] += 3 if @point_schedule.length > 2
64:     @point_schedule[3] += 1 if @point_schedule.length > 3
65:   end

[Source]

    # File app/models/competitions/mbra_bar.rb, line 47
47:   def calculate_threshold_number_of_races
48:     # 70% of the races (rounded to the nearest whole number) count in the BAR standings.
49:     @threshold_number_of_races = (Event.find_all_bar_for_discipline(self.discipline, self.date.year).size * 0.7).round.to_i
50:   end

[Source]

     # File app/models/competitions/mbra_bar.rb, line 168
168:   def create_races
169:     Discipline[discipline].bar_categories.each do |category|
170:       races.create!(:category => category)
171:       logger.debug("#{self.class.name} created BAR race in discipline #{discipline} for category '#{category}'") if logger.debug?
172:     end
173:   end

[Source]

     # File app/models/competitions/mbra_bar.rb, line 179
179:   def friendly_name
180:     'MBRA BAR'
181:   end

[Source]

     # File app/models/competitions/mbra_bar.rb, line 175
175:   def members_only?
176:     false
177:   end

[Source]

    # File app/models/competitions/mbra_bar.rb, line 52
52:   def point_schedule
53:     @point_schedule = @point_schedule || []
54:   end

Apply points from point_schedule

[Source]

     # File app/models/competitions/mbra_bar.rb, line 102
102:   def points_for(source_result, team_size = nil)
103:     calculate_point_schedule(source_result.race.field_size)
104:     points = 0
105:     MbraBar.benchmark('points_for') {
106:       if source_result.place.strip.downcase == "dnf"
107:         points = 0.5
108:       else
109:         # if multiple riders got the same place (must be a TTT or tandem team or... ?), then they split the points...
110:         #this screws up the scoring of match sprints where riders eliminatined in qualifying heats all earn the same place
111:         #team_size = team_size || Result.count(:conditions => ["race_id =? and place = ?", source_result.race.id, source_result.place])
112:         points = point_schedule[source_result.place.to_i] * source_result.race.bar_points #/ team_size.to_f
113:       end
114:     }
115:     points
116:   end

Source result = counts towards the BAR "race" and BAR results Example: Piece of Cake RR, 6th, Jon Knowlson

bar_result, bar_race = BAR itself and placing in the BAR Example: Senior Men BAR, 130th, Jon Knowlson, 45 points

BAR results add scoring results as scores Example: Senior Men BAR, 130th, Jon Knowlson, 18 points

 - Piece of Cake RR, 6th, Jon Knowlson 10 points
 - Silverton RR, 8th, Jon Knowlson 8 points

[Source]

    # File app/models/competitions/mbra_bar.rb, line 78
78:   def source_results(race)
79:     race_disciplines = "'#{race.discipline}'"
80:     category_ids = category_ids_for(race)
81: 
82:     Result.find(:all,
83:                 :include => [:race, {:person => :team}, :team, {:race => [{:event => { :parent => :parent }}, :category]}],
84:                 :conditions => [%Q{
85:                     (events.type in ('Event', 'SingleDayEvent', 'MultiDayEvent') or events.type is NULL)
86:                     and bar = true
87:                     and categories.id in (#{category_ids})
88:                     and (events.discipline in (#{race_disciplines})
89:                       or (events.discipline is null and parents_events.discipline in (#{race_disciplines}))
90:                       or (events.discipline is null and parents_events.discipline is null and parents_events_2.discipline in (#{race_disciplines})))
91:                     and (races.bar_points > 0
92:                       or (races.bar_points is null and events.bar_points > 0)
93:                       or (races.bar_points is null and events.bar_points is null and parents_events.bar_points > 0)
94:                       or (races.bar_points is null and events.bar_points is null and parents_events.bar_points is null and parents_events_2.bar_points > 0))
95:                     and events.date between '#{date.year}-01-01' and '#{date.year}-12-31'
96:                 }],
97:                 :order => 'person_id'
98:       )
99:   end

[Source]

     # File app/models/competitions/mbra_bar.rb, line 183
183:   def to_s
184:     "#<#{self.class.name} #{id} #{discipline} #{name} #{date}>"
185:   end

[Validate]