| 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
| TYPES | = | %w{ AgeGradedBar Bar CascadeCrossOverall Cat4WomensRaceSeries CrossCrusadeOverall CrossCrusadeTeamCompetition Ironman OregonCup OregonJuniorCyclocrossSeries OverallBar TaborOverall TeamBar } |
| point_schedule | [RW] |
Update results based on source event results. (Calculate clashes with internal Rails method) Destroys existing Competition for the year first.
# 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
# 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
# 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
By default, does nothing. Useful to apply rule like:
# File app/models/competitions/competition.rb, line 219
219: def after_create_competition_results_for(race)
220: end
# File app/models/competitions/competition.rb, line 226
226: def ascending_points?
227: true
228: end
Rebuild results
# 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.
# 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
# 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
# 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
# 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
# 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
# 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
# File app/models/competitions/competition.rb, line 71
71: def default_date
72: Time.new.beginning_of_year
73: end
This method does nothing, and always returns true. Competitions don‘t participate in event notification.
# 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.
# File app/models/competitions/competition.rb, line 285
285: def enable_notification!
286: true
287: end
Last day of year for date
# File app/models/competitions/competition.rb, line 97
97: def end_date
98: Date.new(date.year, 12, 31)
99: end
# 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
# 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
# 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
# File app/models/competitions/competition.rb, line 87
87: def friendly_name
88: 'Competition'
89: end
Member this date year?
# 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?
# File app/models/competitions/competition.rb, line 236
236: def members_only?
237: true
238: end
# File app/models/competitions/competition.rb, line 109
109: def multiple_days?
110: source_events.count > 1
111: end
# 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.
# 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?
# File app/models/competitions/competition.rb, line 231
231: def place_members_only?
232: false
233: end
# 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
# 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
# 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
# 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
# File app/models/competitions/competition.rb, line 170
170: def source_results(race)
171: []
172: end
Same as date. Should always be January 1st
# File app/models/competitions/competition.rb, line 92
92: def start_date
93: date
94: end
# 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
# 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
# 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