class PostRunner::PersonalRecords

The PersonalRecords class stores the various records. Records are grouped by specific year or all-time records.

Constants

SpeedRecordDistances

List of popular distances for each sport.

Public Class Methods

new(p) click to toggle source
Calls superclass method
# File lib/postrunner/PersonalRecords.rb, line 331
def initialize(p)
  super(p)

  self.sport_records = @store.new(PEROBS::Hash)
  delete_all_records
end

Public Instance Methods

activity_records(activity) click to toggle source

Return an Array of all the records associated with the given Activity.

# File lib/postrunner/PersonalRecords.rb, line 510
def activity_records(activity)
  records = []
  each do |record|
    if record.activity.equal?(activity)
      records << record
    end
  end

  records
end
delete_activity(activity) click to toggle source
# File lib/postrunner/PersonalRecords.rb, line 472
def delete_activity(activity)
  record_deleted = false
  @sport_records.each_value do |r|
    record_deleted = true if r.delete_activity(activity)
  end

  record_deleted
end
delete_all_records() click to toggle source
# File lib/postrunner/PersonalRecords.rb, line 465
def delete_all_records
  @sport_records.clear
  SpeedRecordDistances.keys.each do |sport|
    @sport_records[sport] = @store.new(SportRecords, sport)
  end
end
each(&block) click to toggle source

Iterator for all Record objects that are stored in this data structure.

# File lib/postrunner/PersonalRecords.rb, line 505
def each(&block)
  @sport_records.each_value { |r| r.each(&block) }
end
generate_html_reports() click to toggle source
# File lib/postrunner/PersonalRecords.rb, line 481
def generate_html_reports
  non_empty_records = @sport_records.select { |s, r| !r.empty? }
  max = non_empty_records.length
  i = 0
  non_empty_records.each do |sport, record|
    output_file = File.join(@store['config']['html_dir'],
                            "records-#{i}.html")
    RecordListPageView.new(@store['file_store'], record, max, i).
                           write(output_file)
    i += 1
  end
end
register_result(activity, sport, distance, duration, start_time) click to toggle source
# File lib/postrunner/PersonalRecords.rb, line 453
def register_result(activity, sport, distance, duration, start_time)
  unless @sport_records.include?(sport)
    Log.info "Ignoring records for activity type '#{sport}' in " +
             "#{activity.fit_file_name}"
    return false
  end

  result = ActivityResult.new(activity, sport, distance, duration,
                              start_time)
  @sport_records[sport].register_result(result)
end
scan_activity_for_records(activity, report_update_requested = false) click to toggle source
# File lib/postrunner/PersonalRecords.rb, line 338
def scan_activity_for_records(activity, report_update_requested = false)
  # If we have the @norecord flag set, we ignore this Activity for the
  # record collection.
  return if activity.norecord

  activity.load_fit_file

  distance_record = 0.0
  distance_record_sport = nil
  # Array with popular distances (in meters) in ascending order.
  record_distances = nil
  # Speed records for popular distances (seconds hashed by distance in
  # meters)
  speed_records = {}

  # Ignore FIT files that don't have an activity or session
  return unless activity.fit_activity && activity.fit_activity.sessions

  segment_start_time = activity.fit_activity.sessions[0].start_time
  segment_start_distance = 0.0

  sport = nil
  last_timestamp = nil
  last_distance = nil

  activity.fit_activity.records.each do |record|
    if record.distance.nil?
      # All records must have a valid distance mark or the activity does
      # not qualify for a personal record.
      Log.warn "Found a record without a valid distance"
      return
    end
    if record.timestamp.nil?
      Log.warn "Found a record without a valid timestamp"
      return
    end

    unless sport
      # If the Activity has sport set to 'multisport' or 'all' we pick up
      # the sport from the FIT records. Otherwise, we just use whatever
      # sport the Activity provides.
      if activity.sport == 'multisport' || activity.sport == 'all'
        sport = record.activity_type
      else
        sport = activity.sport
      end
      return unless SpeedRecordDistances.include?(sport)

      record_distances = SpeedRecordDistances[sport].
        keys.sort
    end

    segment_start_distance = record.distance unless segment_start_distance
    segment_start_time = record.timestamp unless segment_start_time

    # Total distance covered in this segment so far
    segment_distance = record.distance - segment_start_distance
    # Check if we have reached the next popular distance.
    if record_distances.first &&
       segment_distance >= record_distances.first
      segment_duration = record.timestamp - segment_start_time
      # The distance may be somewhat larger than a popular distance. We
      # normalize the time to the norm distance.
      norm_duration = segment_duration / segment_distance *
        record_distances.first
      # Save the time for this distance.
      speed_records[record_distances.first] = {
        :time => norm_duration, :sport => sport
      }
      # Switch to the next popular distance.
      record_distances.shift
    end

    # We've reached the end of a segment if the sport type changes, we
    # detect a pause of more than 30 seconds or when we've reached the
    # last record.
    if (record.activity_type && sport && record.activity_type != sport) ||
       (last_timestamp && (record.timestamp - last_timestamp) > 30) ||
       record.equal?(activity.fit_activity.records.last)

      # Check for a total distance record
      if segment_distance > distance_record
        distance_record = segment_distance
        distance_record_sport = sport
      end

      # Prepare for the next segment in this Activity
      segment_start_distance = nil
      segment_start_time = nil
      sport = nil
    end

    last_timestamp = record.timestamp
    last_distance = record.distance
  end

  # Store the found records
  start_time = activity.fit_activity.sessions[0].timestamp
  update_reports = false
  if distance_record_sport
    if register_result(activity, distance_record_sport, distance_record,
                       nil, start_time)
      update_reports = true
    end
  end
  speed_records.each do |dist, info|
    if register_result(activity, info[:sport], dist, info[:time],
                       start_time)
      update_reports = true
    end
  end

  generate_html_reports if update_reports && report_update_requested
end
to_s() click to toggle source
# File lib/postrunner/PersonalRecords.rb, line 494
def to_s
  str = ''
  @sport_records.each do |sport, record|
    next if record.empty?
    str += "Records for activity type #{sport}:\n\n#{record.to_s}"
  end

  str
end