| Class | Event |
| In: |
app/models/event.rb
|
| Parent: | ActiveRecord::Base |
Superclass for anything that can have results:
Examples: Alpenrose Challenge Morning Session, Alpenrose Challenge Afternoon Session)
instructional: class or clinc practice: training session
Events have four similar, but distinct relationships to other Events:
A purer children association would return all child Events and Competitions
(that's how they are in the database). But we almost always want just the child Events, and not the Competitions.
Changes to parent Event‘s attributes are propogated to children, unless the children‘s attributes are already different. See PROPOGATED_ATTRIBUTES
It‘s debatable whether we need STI subclasses or not.
All notification code just supports combined TT results, and should be moved to background processing
| PROPOGATED_ATTRIBUTES | = | %w{ city discipline flyer name number_issuer_id promoter_id prize_list sanctioned_by state time velodrome_id time postponed cancelled flyer_approved instructional practice sanctioned_by email phone team_id beginner_friendly } unless defined?(PROPOGATED_ATTRIBUTES) |
| new_promoter_name | [R] | |
| new_team_name | [R] |
# File app/models/export/events.rb, line 5 5: def Event.export 6: Event.export_head 7: Event.export_data 8: end
# File app/models/export/events.rb, line 34
34: def Event.export_columns
35: [
36: "id", "parent_id", "name", "discipline", "date", "time",
37: "sanctioned_by", "type", "city", "state", "instructional", "practice",
38: "postponed", "beginner_friendly", "cancelled"
39: ]
40: end
# File app/models/export/events.rb, line 16
16: def Event.export_data
17: Base.export(export_data_sql, "events.csv")
18: end
# File app/models/export/events.rb, line 26
26: def Event.export_data_sql
27: "SELECT #{Event.export_columns.join(",")}
28: INTO OUTFILE '%s'
29: FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"'
30: LINES TERMINATED BY '\\n'
31: FROM events"
32: end
# File app/models/export/events.rb, line 12
12: def Event.export_head
13: Base.export(export_head_sql, "events.txt")
14: end
# File app/models/export/events.rb, line 20
20: def Event.export_head_sql
21: "SELECT '#{Event.export_columns.join("','")}'
22: INTO OUTFILE '%s'
23: FIELDS TERMINATED BY '\\n'"
24: end
# File app/models/event.rb, line 158
158: def Event.find_all_bar_for_discipline(discipline, year = Date.today.year)
159: first_of_year = Date.new(year, 1, 1)
160: last_of_year = Date.new(year + 1, 1, 1) - 1
161: discipline_names = [discipline]
162: discipline_names << 'Circuit' if discipline.downcase == 'road'
163: Set.new(Event.find(
164: :all,
165: :select => "distinct events.id, events.*",
166: :conditions => [%Q{
167: events.date between ? and ?
168: and events.discipline in (?)
169: and bar_points > 0
170: }, first_of_year, last_of_year, discipline_names]
171: ))
172:
173: end
Return [weekly_series, events] that have results Honors RacingAssociation.current.show_only_association_sanctioned_races_on_calendar
# File app/models/event.rb, line 113
113: def Event.find_all_with_results(year = Date.today.year, discipline = nil)
114: # Maybe this should be its own class, since it has knowledge of Event and Result?
115: first_of_year = Date.new(year, 1, 1)
116: last_of_year = Date.new(year + 1, 1, 1) - 1
117:
118: if discipline
119: discipline_names = [discipline.name]
120: if discipline == Discipline['road']
121: discipline_names << 'Circuit'
122: end
123: events = Set.new(Event.find(
124: :all,
125: :select => "distinct events.id, events.*",
126: :joins => { :races => :results },
127: :include => { :parent => :parent },
128: :conditions => [%Q{
129: events.date between ? and ?
130: and events.discipline in (?)
131: }, first_of_year, last_of_year, discipline_names]
132: ))
133:
134: else
135: events = Set.new(Event.find(
136: :all,
137: :select => "distinct events.id, events.*",
138: :joins => { :races => :results },
139: :include => { :parent => :parent },
140: :conditions => ["events.date between ? and ?", first_of_year, last_of_year]
141: ))
142: end
143:
144: if RacingAssociation.current.show_only_association_sanctioned_races_on_calendar
145: events.reject! do |event|
146: event.sanctioned_by != RacingAssociation.current.default_sanctioned_by
147: end
148: end
149:
150: events.map!(&:root)
151:
152: weekly_series, events = events.partition { |event| event.is_a?(WeeklySeries) }
153: competitions, events = events.partition { |event| event.is_a?(Competition) }
154:
155: [ weekly_series, events, competitions ]
156: end
Return list of every year that has at least one event
# File app/models/event.rb, line 97
97: def Event.find_all_years
98: years = [ RacingAssociation.current.effective_year ] +
99: connection.select_values(
100: "select distinct extract(year from date) from events"
101: ).map(&:to_i)
102: years = years.uniq.sort
103:
104: if years.size == 1
105: years
106: else
107: ((years.first)..(years.last)).to_a.reverse
108: end
109: end
# File app/models/event.rb, line 175
175: def Event.friendly_class_name
176: name.underscore.humanize.titleize
177: end
# File app/models/event.rb, line 617
617: def <=>(other)
618: return -1 if other.nil?
619:
620: if date
621: if other.date
622: return date <=> other.date
623: else
624: return -1
625: end
626: elsif other.date
627: return 1
628: end
629:
630: unless new_record? || other.new_record?
631: return id <=> other.id
632: end
633:
634: 0
635: end
# File app/models/event.rb, line 593
593: def ==(other)
594: if self.equal?(other)
595: return true
596: end
597:
598: if other.nil? || new_record? || other.new_record?
599: return false
600: end
601:
602: id == other.id
603: end
# File app/models/event.rb, line 577
577: def ancestors
578: node, nodes = self, []
579: nodes << node = node.parent while node.parent
580: nodes
581: end
All races’ Categories and all children‘s races’ Categories
# File app/models/event.rb, line 299
299: def categories
300: _categories = races.map(&:category)
301: children.inject(_categories) { |cats, child| cats + child.categories }
302: end
Returns only the children and child child_competitions with results
# File app/models/event.rb, line 276
276: def children_and_child_competitions_with_results(reload = false)
277: children_and_child_competitions(reload).select(&:has_results_including_children?)
278: end
# File app/models/event.rb, line 331
331: def children_changed(child)
332: # Don't trigger callbacks
333: Event.update_all ["updated_at = ?", Time.zone.now], ["id = ?", id]
334: true
335: end
Returns only the children with results
# File app/models/event.rb, line 271
271: def children_with_results(reload = false)
272: children(reload).select(&:has_results_including_children?)
273: end
# File app/models/event.rb, line 417
417: def city_state
418: if city.present?
419: if state.present?
420: "#{city}, #{state}"
421: else
422: city
423: end
424: else
425: if state.present?
426: state
427: else
428: ''
429: end
430: end
431: end
# File app/models/event.rb, line 502
502: def date_range_long_s
503: date.to_s :long_with_week_day
504: end
# File app/models/event.rb, line 494
494: def date_range_s(format = :short)
495: if format == :long
496: date.strftime('%m/%d/%Y')
497: else
498: "#{date.month}/#{date.day}"
499: end
500: end
# File app/models/event.rb, line 210
210: def default_date
211: if parent.present?
212: parent.date
213: else
214: Date.today
215: end
216: end
# File app/models/event.rb, line 226
226: def default_name
227: "New Event #{self.date.strftime("%m-%d-%Y")}"
228: end
# File app/models/event.rb, line 238
238: def default_number_issuer
239: NumberIssuer.find_by_name(RacingAssociation.current.short_name)
240: end
# File app/models/event.rb, line 234
234: def default_sanctioned_by
235: RacingAssociation.current.default_sanctioned_by
236: end
# File app/models/event.rb, line 230
230: def default_state
231: RacingAssociation.current.state
232: end
# File app/models/event.rb, line 285
285: def destroy_races
286: disable_notification!
287: if combined_results
288: combined_results.destroy_races
289: combined_results.destroy
290: end
291: races.each do |race|
292: race.results.clear
293: end
294: races.clear
295: enable_notification!
296: end
Update database immediately with save!
# File app/models/event.rb, line 338
338: def disable_notification!
339: if notification_enabled?
340: ActiveRecord::Base.lock_optimistically = false
341: # Don't trigger after_save callback just because we're enabling notification
342: self.notification = false
343: Event.update_all("notification = false", ["id = ?", id])
344: children.each(&:disable_notification!)
345: ActiveRecord::Base.lock_optimistically = true
346: end
347: false
348: end
# File app/models/event.rb, line 449
449: def discipline_id
450: Discipline[discipline].id if Discipline[discipline]
451: end
Update database immediately with save!
# File app/models/event.rb, line 351
351: def enable_notification!
352: unless notification_enabled?
353: ActiveRecord::Base.lock_optimistically = false
354: # Don't trigger after_save callback just because we're enabling notification
355: self.notification = true
356: Event.update_all("notification = true", ["id = ?", id])
357: children.each(&:enable_notification!)
358: ActiveRecord::Base.lock_optimistically = true
359: end
360: true
361: end
# File app/models/event.rb, line 396
396: def end_date
397: if children.any?
398: children.sort.last.date
399: else
400: start_date
401: end
402: end
# File app/models/event.rb, line 605
605: def eql?(other)
606: if self.equal?(other)
607: return true
608: end
609:
610: if other.nil? || new_record? || other.new_record?
611: return false
612: end
613:
614: id == other.id
615: end
# File app/models/event.rb, line 589
589: def friendly_class_name
590: self.class.friendly_class_name
591: end
Try to intelligently combined parent name and child name for schedule pages
# File app/models/event.rb, line 520
520: def full_name
521: if parent.nil?
522: name
523: elsif parent.full_name == name
524: name
525: elsif name[ parent.full_name ]
526: name
527: else
528: "#{parent.full_name}: #{name}"
529: end
530: end
Will return false-positive if there are only overall series results, but those should only exist if there are "real" results. The results page should show the results in that case.
# File app/models/event.rb, line 260
260: def has_results?(reload = false)
261: self.races(reload).any? { |r| !r.results(reload).empty? }
262: end
Will return false-positive if there are only overall series results, but those should only exist if there are "real" results. The results page should show the results in that case.
# File app/models/event.rb, line 266
266: def has_results_including_children?(reload = false)
267: races(reload).any? { |r| !r.results(reload).empty? } || children(reload).any? { |event| event.has_results?(reload) || event.has_results_including_children? }
268: end
# File app/models/event.rb, line 637
637: def inspect_debug
638: puts("#{self.class.name.ljust(20)} #{self.date} #{self.name} #{self.discipline} #{self.id}")
639: self.races(true).each {|r|
640: puts("#{r.class.name.ljust(20)} #{r.name}")
641: r.results(true).sort.each {|result|
642: puts("#{result.class.name.ljust(20)} #{result.to_long_s}")
643: result.scores(true).each{|score|
644: puts("#{score.class.name.ljust(20)} #{score.source_result.place} #{score.source_result.race.event.name} #{score.source_result.race.name} #{score.points}")
645: }
646: }
647: }
648:
649: self.children(true).each do |event|
650: event.inspect_debug
651: end
652:
653: ""
654: end
Will split on comma if city, state
# File app/models/event.rb, line 438
438: def location=(value)
439: if value.present?
440: self.city, self.state = value.split(",")
441: self.city = city.strip if city
442: self.state = state.strip if state
443: else
444: self.city = nil
445: self.state = nil
446: end
447: end
Always return false
# File app/models/event.rb, line 534
534: def missing_parent?
535: false
536: end
# File app/models/event.rb, line 551
551: def multi_day_event_children_with_no_parent
552: return [] unless name && date
553:
554: @multi_day_event_children_with_no_parent ||= SingleDayEvent.find(
555: :all,
556: :conditions => [
557: "parent_id is null and name = ? and extract(year from date) = ?
558: and ((select count(*) from events where name = ? and extract(year from date) = ? and type in ('MultiDayEvent', 'Series', 'WeeklySeries')) = 0)",
559: self.name, self.date.year, self.name, self.date.year])
560: # Could do this in SQL
561: if @multi_day_event_children_with_no_parent.size == 1
562: @multi_day_event_children_with_no_parent = []
563: end
564: @multi_day_event_children_with_no_parent
565: end
# File app/models/event.rb, line 547
547: def multi_day_event_children_with_no_parent?
548: multi_day_event_children_with_no_parent.any?
549: end
# File app/models/event.rb, line 515
515: def name_with_date
516: "#{name} (#{short_date})"
517: end
Child results fire change notifications? Set to false before bulk changes like event results import to prevent many pointless change notifications and CombinedTimeTrialResults recalcs Check database to ensure most recent value is used, and not a association‘s out-of-date cached value
# File app/models/event.rb, line 367
367: def notification_enabled?
368: connection.select_value("select notification from events where id = #{id}") == 1
369: end
# File app/models/event.rb, line 583
583: def parent_is_not_self
584: if parent_id && parent_id == id
585: errors.add("parent", "Event cannot be its own parent")
586: end
587: end
Parent‘s name. Own name if no parent
# File app/models/event.rb, line 507
507: def parent_name
508: if parent.nil?
509: name
510: else
511: parent.name
512: end
513: end
# File app/models/event.rb, line 457
457: def promoter_name=(value)
458: @new_promoter_name = value
459: end
Returns only the Races with results
# File app/models/event.rb, line 281
281: def races_with_results
282: races.select { |race| !race.results.empty? }.sort
283: end
# File app/models/event.rb, line 571
571: def root
572: node = self
573: node = node.parent while node.parent
574: node
575: end
Defaults state to RacingAssociation.current.state, date to today, name to New Event mm-dd-yyyy NumberIssuer: RacingAssociation.current.short_name Child events use their parent‘s values unless explicity override. And you cannot override parent values by passing in blank or nil attributes to initialize, as there is no way to differentiate missing values from nils or blanks.
# File app/models/event.rb, line 188
188: def set_defaults
189: if new_record?
190: if parent
191: PROPOGATED_ATTRIBUTES.each { |attr|
192: (self[attr] = parent[attr]) if self[attr].blank?
193: }
194: end
195: self.bar_points = default_bar_points if self[:bar_points].nil?
196: self.date = default_date if self[:date].nil?
197: self.discipline = default_discipline if self[:discipline].nil?
198: self.name = default_name if self[:name].nil?
199: self.ironman = default_ironman if self[:ironman].nil?
200: self.number_issuer = default_number_issuer if number_issuer.nil?
201: self.sanctioned_by = default_sanctioned_by if (parent.nil? && self[:sanctioned_by].nil?) || (parent && parent[:sanctioned_by].nil?)
202: self.state = default_state if (parent.nil? && self[:state].nil?) || (parent && parent[:state].nil?)
203: end
204: end
Set point value/factor for this Competition. Convenience method to hide CompetitionEventMembership complexity.
# File app/models/event.rb, line 372
372: def set_points_for(competition, points)
373: # For now, allow Nil exception, but probably will want to auto-create membership in the future
374: competition_event_membership = competition_event_memberships.detect { |cem| cem.competition == competition }
375: competition_event_membership.points_factor = points
376: competition_event_membership.save!
377: end
# File app/models/event.rb, line 461
461: def set_promoter
462: if new_promoter_name.present?
463: promoters = Person.find_all_by_name_or_alias(new_promoter_name)
464: case promoters.size
465: when 0
466: self.promoter = Person.create!(:name => new_promoter_name)
467: when 1
468: self.promoter = promoters.first
469: else
470: self.promoter = promoters.detect { |promoter| promoter.id == promoter_id } || promoters.first
471: end
472: elsif new_promoter_name == ""
473: self.promoter = nil
474: end
475: end
Format for schedule page primarily
# File app/models/event.rb, line 380
380: def short_date
381: return '' unless date
382: prefix = ' ' if date.month < 10
383: suffix = ' ' if date.day < 10
384: "#{prefix}#{date.month}/#{date.day}#{suffix}"
385: end
# File app/models/event.rb, line 481
481: def team_name=(value)
482: @new_team_name = value
483: end
# File app/models/event.rb, line 656
656: def to_s
657: "<#{self.class} #{id} #{discipline} #{name} #{date}>"
658: end
Update child events from parents’ attributes if child attribute has the same value as the parent before update
# File app/models/event.rb, line 306
306: def update_children
307: return true if new_record? || children.count == 0
308: changes.select { |key, value| PROPOGATED_ATTRIBUTES.include?(key) }.each do |change|
309: attribute = change.first
310: was = change.last.first
311: if was.blank?
312: SingleDayEvent.update_all(
313: ["#{attribute}=?", self[attribute]],
314: ["(#{attribute}=? or #{attribute} is null or #{attribute} = '') and parent_id=?", was,
315: self[:id]]
316: )
317: else
318: SingleDayEvent.update_all ["#{attribute}=?", self[attribute]], ["#{attribute}=? and parent_id=?", was, self[:id]]
319: end
320: end
321:
322: children.each { |child| child.update_children }
323: true
324: end
Does nothing. Allows us to treat Events and MultiDayEvents the same.
# File app/models/event.rb, line 414
414: def update_date
415: end
# File app/models/event.rb, line 242
242: def validate_no_results
243: races(true).each do |race|
244: if race.results(true).any?
245: errors.add('results', 'Cannot destroy event with results')
246: return false
247: end
248: end
249:
250: children(true).each do |event|
251: errors.add('results', 'Cannot destroy event with children with results')
252: return false unless event.validate_no_results
253: end
254:
255: true
256: end