class RaceNumber

Number used to identify a Person during a Race: bib number. RaceNumbers are issued from a NumberIssuer, which is usually a racing Association, but sometimes an Event.

In the past, RaceNumbers had to be unique for NumberIssuer, Discipline and year. But we allow duplicates now.

Value is the number on the physical number plate. RaceNumber values can have letters and numbers

This all may seem to be a case or over-modelling, but it refleccts how numbers are used by promoters and associations. PersonNumbers are also used to differentiate between People with the same name, and to identify person results with misspelled names.

Public Class Methods

discipline_id(discipline) click to toggle source

Dupe of lousy code from Discipline

# File app/models/race_number.rb, line 44
def RaceNumber.discipline_id(discipline)
  case Discipline[discipline]
  when Discipline[:road], Discipline[:track], Discipline[:time_trial], Discipline[:circuit], Discipline[:criterium]
    Discipline[:road].id
  when Discipline[:cyclocross]
    Discipline[:cyclocross].id
  when Discipline[:mountain_bike], Discipline[:super_d]
    Discipline[:mountain_bike].id
  when Discipline[:downhill]
    Discipline[:downhill].id
  else
    Discipline[:road].id
  end
end
find_all_by_value_and_event(value, _event) click to toggle source
# File app/models/race_number.rb, line 27
def RaceNumber.find_all_by_value_and_event(value, _event)
  return [] if _event.nil? || value.blank? || _event.number_issuer.nil?
  
  discipline_id = RaceNumber.discipline_id(_event.discipline)
  return [] unless discipline_id
  
  race_numbers = RaceNumber.all(
    :conditions => ['value=? and discipline_id = ? and number_issuer_id=? and year=?',
                    value, 
                    discipline_id, 
                    _event.number_issuer.id, 
                    _event.date.year],
    :include => :person
  )
end
rental?(number, discipline = Discipline[:road]) click to toggle source

Different disciplines have different rules about what is a rental number

# File app/models/race_number.rb, line 60
def RaceNumber.rental?(number, discipline = Discipline[:road])
  if RacingAssociation.current.rental_numbers.nil?
    return false
  end
  
  if number.blank?
    return true
  end

  if RacingAssociation.current.rental_numbers.nil? || discipline == Discipline[:mountain_bike] || discipline == Discipline[:downhill] || number.strip[%r^\d+$/].nil?
    return false
  end
  
  numeric_value = number.to_i    
  if RacingAssociation.current.rental_numbers.include?(numeric_value)
    return true
  end
  
  false
end

Public Instance Methods

<=>(other) click to toggle source
# File app/models/race_number.rb, line 137
def <=>(other)
  if other
    value <=> other.value
  else
    -1
  end
end
defaults() click to toggle source

Default to Road, RacingAssociation.current, and current year

# File app/models/race_number.rb, line 82
def defaults
  self.discipline = Discipline[:road] unless self.discipline
  self.number_issuer = NumberIssuer.find_by_name(RacingAssociation.current.short_name) unless self.number_issuer
  self.year = RacingAssociation.current.effective_year unless (self.year and self.year > 1800)
end
get_person_id() click to toggle source
# File app/models/race_number.rb, line 88
def get_person_id
  if person && (person.new_record? || person.changed?)
    person.reload
  end
end
to_s() click to toggle source
# File app/models/race_number.rb, line 145
def to_s
  "<RaceNumber (#{id}) (#{value}) (#{person_id}) (#{number_issuer_id}) (#{discipline_id}) (#{year})>"
end
unique_number() click to toggle source

Checks that Person doesn’t already have this number.

Numbers are unique by value, Person, Discipline, NumberIssuer, and year.

Skips check if person is not set. Typically, this happens when importing a Result that has a number, but no person

OBRA rental numbers (11-99) are not valid

# File app/models/race_number.rb, line 106
def unique_number 
  _discipline = Discipline.find(self[:discipline_id])
  if RaceNumber.rental?(self[:value], _discipline)
    errors.add('value', "#{value} is a rental number. #{RacingAssociation.current.short_name} rental numbers: #{RacingAssociation.current.rental_numbers}")
    person.errors.add('value', "#{value} is a rental number. #{RacingAssociation.current.short_name} rental numbers: #{RacingAssociation.current.rental_numbers}")
    return false 
  end
  
  return true if person.nil?

  if new_record?
    existing_numbers = RaceNumber.count(
      :conditions => ['value=? and discipline_id=? and number_issuer_id=? and year=? and person_id = ?', 
      self[:value], self[:discipline_id], self[:number_issuer_id], self[:year], person.id])
  else
    existing_numbers = RaceNumber.count(
      :conditions => ['value=? and discipline_id=? and number_issuer_id=? and year=? and id<>? and person_id = ?', 
      self[:value], self[:discipline_id], self[:number_issuer_id], self[:year], self.id, person.id])
  end
    
  unless existing_numbers == 0
    person_id = person.id
    errors.add('value', "Number '#{value}' can't be used for #{person.name}. Already used as #{year} #{number_issuer.name} #{discipline.name.downcase} number.")
    person.errors.add('value', "Number '#{value}' can't be used for #{person.name}. Already used as #{year} #{number_issuer.name} #{discipline.name.downcase} number.")
    if existing_numbers.size > 1
      logger.warn("Race number '#{value}' found #{existing_numbers} times for discipline #{discipline_id}, number issuer #{number_issuer_id}, year #{year}, person #{person_id}")
    end
    return false
  end
end
validate_year() click to toggle source
# File app/models/race_number.rb, line 94
def validate_year
  self.year > 1800
end