Class Competition
In: app/models/competitions/competition.rb
Parent: Event

Results that derive their results from other Events. Se TYPES. Year-long: BAR, Ironman, WSBA Rider Rankings, Oregon Cup. Event-based: Cross Crusade, Mount Tabor Series.

point_schedule: Array. How many points for each place? source_events: Which events count toward Competition? Not all Competitions use this relationship. Many dynamically choose their source events each time they calculate. competition_event_memberships: Relationship between source_events and competition.

+calculate!+ is the main method

Methods

Included Modules

FileUtils

Constants

TYPES = %w{ AgeGradedBar Bar CascadeCrossOverall Cat4WomensRaceSeries CrossCrusadeOverall CrossCrusadeTeamCompetition Ironman OregonCup OregonJuniorCyclocrossSeries OverallBar TaborOverall TeamBar }

Attributes

point_schedule  [RW] 

Public Class methods

Update results based on source event results. (Calculate clashes with internal Rails method) Destroys existing Competition for the year first.

[Source]

    # File app/models/competitions/competition.rb, line 52
52:   def Competition.calculate!(year = Date.today.year)
53:     benchmark(name, Logger::INFO, false) {
54:       transaction do
55:         year = year.to_i if year.is_a?(String)
56:         date = Date.new(year, 1, 1)
57:         competition = self.find_or_create_by_date(date)
58:         raise(ActiveRecord::ActiveRecordError, competition.errors.full_messages) unless competition.errors.empty?
59:         competition.destroy_races
60:         competition.create_races
61:         competition.create_children
62:         # Could bulk load all Event and Races at this point, but hardly seems to matter
63:         competition.calculate_members_only_places
64:         competition.calculate!
65:       end
66:     }
67:     # Don't return the entire populated instance!
68:     true
69:   end

[Source]

    # File app/models/competitions/competition.rb, line 41
41:   def self.find_for_year(year = RacingAssociation.current.year)
42:     self.find_by_date(Date.new(year, 1, 1))
43:   end

[Source]

    # File app/models/competitions/competition.rb, line 45
45:   def self.find_or_create_for_year(year = RacingAssociation.current.year)
46:     self.find_for_year(year) || self.create(:date => (Date.new(year, 1, 1)))
47:   end

Public Instance methods

By default, does nothing. Useful to apply rule like:

  • Any results after the first four only get 50-point bonus
  • Drop lowest-scoring result

[Source]

     # File app/models/competitions/competition.rb, line 219
219:   def after_create_competition_results_for(race)
220:   end

[Source]

     # File app/models/competitions/competition.rb, line 226
226:   def ascending_points?
227:     true
228:   end

[Source]

     # File app/models/competitions/competition.rb, line 222
222:   def break_ties?
223:     true
224:   end

Rebuild results

[Source]

     # File app/models/competitions/competition.rb, line 152
152:   def calculate!
153:     races.each do |race|
154:       results = source_results_with_benchmark(race)
155:       create_competition_results_for(results, race)
156:       after_create_competition_results_for(race)
157:       race.place_results_by_points(break_ties?, ascending_points?)
158:     end
159:     
160:     # Explicity mark as updated to make it "dirty"
161:     self.updated_at_will_change!
162:     save!
163:   end

Some competitions are only open to RacingAssociation members, and non-members are dropped from the results.

[Source]

     # File app/models/competitions/competition.rb, line 138
138:   def calculate_members_only_places
139:     if place_members_only?
140:       # Uses batch_size, Rails 2.3 db cursor, to limit load on memory
141:       Race.find_each(:include => :event,
142:                 :conditions => [ "events.type != ? and events.date between ? and ?", 
143:                                  self.class.name.demodulize, start_date, end_date ],
144:                 :batch_size => 50
145:                 ) do |r|
146:                   r.calculate_members_only_places!
147:                 end
148:     end
149:   end

Array of ids (integers) race category, race category‘s siblings, and any competition categories

[Source]

     # File app/models/competitions/competition.rb, line 176
176:   def category_ids_for(race)
177:     ids = [ race.category_id ]
178:     ids = ids + race.category.descendants.map(&:id)
179:     ids.join(', ')
180:   end

Override in superclass for Competitions like OBRA OverallBAR

[Source]

     # File app/models/competitions/competition.rb, line 133
133:   def create_children
134:     true
135:   end

If same rider places twice in same race, only highest result counts TODO Replace ifs with methods

[Source]

     # File app/models/competitions/competition.rb, line 184
184:   def create_competition_results_for(results, race)
185:     competition_result = nil
186:     results.each_with_index do |source_result, index|
187:       logger.debug("#{self.class.name} scoring result: #{source_result.date} race: #{source_result.race.name} pl: #{source_result.place} mem pl: #{source_result.members_only_place if place_members_only?} #{source_result.last_name} #{source_result.team_name}") if logger.debug?
188: 
189:       person = source_result.person
190:       points = points_for(source_result)
191:       if points > 0.0 && (!members_only? || member?(person, source_result.date))
192:  
193:         if first_result_for_person(source_result, competition_result)
194:           # Intentionally not using results association create method. No need to hang on to all competition results.
195:           # In fact, this could cause serious memory issues with the Ironman
196:           competition_result = Result.create!(
197:              :person => person, 
198:              :team => (person ? person.team : nil),
199:              :race => race)
200:         end
201:  
202:         competition_result.scores.create_if_best_result_for_race(
203:           :source_result => source_result, 
204:           :competition_result => competition_result, 
205:           :points => points
206:         )
207:       end
208:       
209:       # Aggressive memory management. If competition has a race with many results, 
210:       # the results array can become a large, uneeded, structure
211:       results[index] = nil
212:       GC.start if index > 0 && index % 1000 == 0
213:     end
214:   end

[Source]

     # File app/models/competitions/competition.rb, line 127
127:   def create_races
128:     category = Category.find_or_create_by_name(friendly_name)
129:     self.races.create(:category => category)
130:   end

[Source]

     # File app/models/competitions/competition.rb, line 101
101:   def date_range_long_s
102:     if multiple_days?
103:       "#{start_date.strftime('%a, %B %d')} to #{end_date.strftime('%a, %B %d, %Y')}"
104:     else
105:       start_date.strftime('%a, %B %d')
106:     end
107:   end

[Source]

    # File app/models/competitions/competition.rb, line 75
75:   def default_bar_points
76:     0
77:   end

[Source]

    # File app/models/competitions/competition.rb, line 71
71:   def default_date
72:     Time.new.beginning_of_year
73:   end

[Source]

    # File app/models/competitions/competition.rb, line 79
79:   def default_ironman
80:     false
81:   end

[Source]

    # File app/models/competitions/competition.rb, line 83
83:   def default_name
84:     name
85:   end

This method does nothing, and always returns true. Competitions don‘t participate in event notification.

[Source]

     # File app/models/competitions/competition.rb, line 280
280:   def disable_notification!
281:     true
282:   end

This method does nothing, and always returns true. Competitions don‘t participate in event notification.

[Source]

     # File app/models/competitions/competition.rb, line 285
285:   def enable_notification!
286:     true
287:   end

Last day of year for date

[Source]

    # File app/models/competitions/competition.rb, line 97
97:   def end_date
98:     Date.new(date.year, 12, 31)
99:   end

[Source]

     # File app/models/competitions/competition.rb, line 294
294:   def expire_cache
295:     if ApplicationController.perform_caching
296:       ApplicationController.expire_cache
297:     end
298:   end

[Source]

     # File app/models/competitions/competition.rb, line 245
245:   def first_result_for_person(source_result, competition_result)
246:     competition_result.nil? || source_result.person != competition_result.person
247:   end

[Source]

     # File app/models/competitions/competition.rb, line 249
249:   def first_result_for_team(source_result, competition_result)
250:     competition_result.nil? || source_result.team != competition_result.team
251:   end

[Source]

    # File app/models/competitions/competition.rb, line 87
87:   def friendly_name
88:     'Competition'
89:   end

Member this date year?

[Source]

     # File app/models/competitions/competition.rb, line 241
241:   def member?(person_or_team, date)
242:     person_or_team && person_or_team.member_in_year?(date)
243:   end

Only members can score points?

[Source]

     # File app/models/competitions/competition.rb, line 236
236:   def members_only?
237:     true 
238:   end

[Source]

     # File app/models/competitions/competition.rb, line 109
109:   def multiple_days?
110:     source_events.count > 1
111:   end

[Source]

     # File app/models/competitions/competition.rb, line 123
123:   def name
124:     self[:name] ||= "#{self.date.year} #{friendly_name}"
125:   end

This method always returns false. Competitions don‘t participate in event notification.

[Source]

     # File app/models/competitions/competition.rb, line 290
290:   def notification_enabled?
291:     false
292:   end

Use the recorded place with all finishers? Or only place with just Assoication member finishers?

[Source]

     # File app/models/competitions/competition.rb, line 231
231:   def place_members_only?
232:     false
233:   end

[Source]

     # File app/models/competitions/competition.rb, line 165
165:   def point_schedule
166:     @point_schedule = @point_schedule || []
167:   end

multiplier from the CompetitionEventsMembership if it exists

[Source]

     # File app/models/competitions/competition.rb, line 269
269:   def points_factor(source_result)
270:     cem = source_result.event.competition_event_memberships.detect{|comp| comp.competition_id == self.id}
271:     # factor is one if membership is not found
272:     cem ? cem.points_factor : 1 
273:   end

Apply points from point_schedule, and split across team

[Source]

     # File app/models/competitions/competition.rb, line 254
254:   def points_for(source_result, team_size = nil)
255:     team_size = team_size || Result.count(:conditions => ["race_id =? and place = ?", source_result.race.id, source_result.place])
256:     if place_members_only?
257:       points = point_schedule[source_result.members_only_place.to_i].to_f
258:     else
259:       points = point_schedule[source_result.place.to_i].to_f
260:     end
261:     if points
262:       points * points_factor(source_result) / team_size.to_f
263:     else
264:       0
265:     end
266:   end

[Source]

     # File app/models/competitions/competition.rb, line 275
275:   def requires_combined_results?
276:     false
277:   end

source_results must be in person, place ascending order

[Source]

     # File app/models/competitions/competition.rb, line 170
170:   def source_results(race)
171:     []
172:   end

Same as date. Should always be January 1st

[Source]

    # File app/models/competitions/competition.rb, line 92
92:   def start_date
93:     date
94:   end

[Source]

     # File app/models/competitions/competition.rb, line 300
300:   def to_s
301:     "#<#{self.class} #{id} #{name} #{start_date} #{end_date}>"
302:   end

Assert start and end dates are first and last days of the year

[Source]

     # File app/models/competitions/competition.rb, line 114
114:   def valid_dates
115:     if !start_date or start_date.month != 1 or start_date.day != 1
116:       errors.add("start_date", "Start date must be January 1st")
117:     end
118:     if !end_date or end_date.month != 12 or end_date.day != 31
119:       errors.add("end_date", "End date must be December 31st")
120:     end
121:   end

Protected Instance methods

[Source]

     # File app/models/competitions/competition.rb, line 306
306:   def source_results_with_benchmark(race)
307:     results = []
308:     Competition.benchmark("#{self.class.name} source_results", Logger::DEBUG, false) {
309:       results = source_results(race)
310:     }
311:     logger.debug("#{self.class.name} Found #{results.size} source results for '#{race.name}'") if logger.debug?
312:     results
313:   end

[Validate]