class PostRunner::Activity
Constants
- ActivitySubTypes
- ActivityTypes
Attributes
db[R]
fit_activity[R]
fit_file[R]
name[R]
Public Class Methods
new(db, fit_file, fit_activity, name = nil)
click to toggle source
# File lib/postrunner/Activity.rb, line 169 def initialize(db, fit_file, fit_activity, name = nil) @fit_file = fit_file @fit_activity = fit_activity @name = name || fit_file @unset_variables = [] late_init(db) @@CachedActivityValues.each do |v| v_str = "@#{v}" instance_variable_set(v_str, fit_activity.send(v)) self.class.send(:attr_reader, v.to_sym) end end
Public Instance Methods
activity_sub_type()
click to toggle source
# File lib/postrunner/Activity.rb, line 449 def activity_sub_type ActivitySubTypes[@sub_sport] || "Undefined #{@sub_sport}" end
activity_type()
click to toggle source
# File lib/postrunner/Activity.rb, line 445 def activity_type ActivityTypes[@sport] || 'Undefined' end
check()
click to toggle source
# File lib/postrunner/Activity.rb, line 200 def check generate_html_view register_records Log.info "FIT file #{@fit_file} is OK" end
distance(timestamp, unit_system)
click to toggle source
# File lib/postrunner/Activity.rb, line 453 def distance(timestamp, unit_system) @fit_activity = load_fit_file unless @fit_activity @fit_activity.records.each do |record| if record.timestamp >= timestamp unit = { :metric => 'km', :statute => 'mi'}[unit_system] value = record.get_as('distance', unit) return '-' unless value return "#{'%.2f %s' % [value, unit]}" end end '-' end
dump(filter)
click to toggle source
# File lib/postrunner/Activity.rb, line 206 def dump(filter) @fit_activity = load_fit_file(filter) end
encode_with(coder)
click to toggle source
This method is called during Activity::to_yaml() calls. It’s being used to prevent some instance variables from being saved in the YAML file. Only attributes that are listed in @@CachedAttributes are being saved.
# File lib/postrunner/Activity.rb, line 239 def encode_with(coder) instance_variables.each do |a| name_with_at = a.to_s name_without_at = name_with_at[1..-1] next unless @@CachedAttributes.include?(name_without_at) coder[name_without_at] = instance_variable_get(name_with_at) end end
events()
click to toggle source
# File lib/postrunner/Activity.rb, line 268 def events @fit_activity = load_fit_file unless @fit_activity puts EventList.new(self, @db.cfg[:unit_system]).to_s end
generate_html_view()
click to toggle source
# File lib/postrunner/Activity.rb, line 440 def generate_html_view @fit_activity = load_fit_file unless @fit_activity ActivityView.new(self, @db.cfg[:unit_system]) end
has_records?()
click to toggle source
Return true if this activity generated any personal records.
# File lib/postrunner/Activity.rb, line 436 def has_records? !@db.records.activity_records(self).empty? end
init_with(coder)
click to toggle source
This method is called during YAML::load() to initialize the class objects. The initialize() is NOT called during YAML::load(). Any additional initialization work is done in late_init
().
# File lib/postrunner/Activity.rb, line 213 def init_with(coder) @unset_variables = [] @@CachedAttributes.each do |name_without_at| # Create attr_readers for cached variables. self.class.send(:attr_reader, name_without_at.to_sym) if coder.map.include?(name_without_at) # The YAML file has a value for the instance variable. So just set # it. instance_variable_set('@' + name_without_at, coder[name_without_at]) else if @@CachedActivityValues.include?(name_without_at) @unset_variables << name_without_at elsif name_without_at == 'norecord' @norecord = false else Log.fatal "Don't know how to initialize the instance variable " + "#{name_without_at}." end end end end
late_init(db)
click to toggle source
YAML::load() does not call initialize(). We don’t have all attributes stored in the YAML file, so we need to make sure these are properly set after a YAML::load().
# File lib/postrunner/Activity.rb, line 186 def late_init(db) @db = db @html_file = File.join(@db.cfg[:html_dir], "#{@fit_file[0..-5]}.html") @unset_variables.each do |name_without_at| # The YAML file does not yet have the instance variable cached. # Load the Activity data and extract the value to set the instance # variable. @fit_activity = load_fit_file unless @fit_activity instance_variable_set('@' + name_without_at, @fit_activity.send(name_without_at)) end end
query(key)
click to toggle source
# File lib/postrunner/Activity.rb, line 249 def query(key) unless @@Schemata.include?(key) raise ArgumentError, "Unknown key '#{key}' requested in query" end schema = @@Schemata[key] if schema.func value = send(schema.func) else unless instance_variable_defined?(key) raise ArgumentError, "Don't know how to query '#{key}'" end value = instance_variable_get(key) end QueryResult.new(value, schema) end
register_records()
click to toggle source
# File lib/postrunner/Activity.rb, line 330 def register_records # If we have the @norecord flag set, we ignore this Activity for the # record collection. return if @norecord 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 = {} segment_start_time = @fit_activity.sessions[0].start_time segment_start_distance = 0.0 sport = nil last_timestamp = nil last_distance = nil @fit_activity.records.each do |record| if record.timestamp.nil? # All records must have a valid timestamp Log.warn "Found a record without a valid timestamp" return end 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 at #{record.timestamp} without a " + "valid distance" 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 @sport == 'multisport' || @sport == 'all' sport = record.activity_type else sport = @sport end return unless PersonalRecords::SpeedRecordDistances.include?(sport) record_distances = PersonalRecords::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?(@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 = @fit_activity.sessions[0].timestamp if distance_record_sport @db.records.register_result(self, distance_record_sport, distance_record, nil, start_time) end speed_records.each do |dist, info| @db.records.register_result(self, info[:sport], dist, info[:time], start_time) end end
rename(name)
click to toggle source
# File lib/postrunner/Activity.rb, line 292 def rename(name) @name = name generate_html_view end
set(attribute, value)
click to toggle source
# File lib/postrunner/Activity.rb, line 297 def set(attribute, value) case attribute when 'name' @name = value when 'type' @fit_activity = load_fit_file unless @fit_activity unless ActivityTypes.values.include?(value) Log.fatal "Unknown activity type '#{value}'. Must be one of " + ActivityTypes.values.join(', ') end @sport = ActivityTypes.invert[value] # Since the activity changes the records from this Activity need to be # removed and added again. @db.records.delete_activity(self) register_records when 'subtype' unless ActivitySubTypes.values.include?(value) Log.fatal "Unknown activity subtype '#{value}'. Must be one of " + ActivitySubTypes.values.join(', ') end @sub_sport = ActivitySubTypes.invert[value] when 'norecord' unless %w( true false).include?(value) Log.fatal "norecord must either be 'true' or 'false'" end @norecord = value == 'true' else Log.fatal "Unknown activity attribute '#{attribute}'. Must be one of " + 'name, type or subtype' end generate_html_view end
show()
click to toggle source
# File lib/postrunner/Activity.rb, line 273 def show generate_html_view #unless File.exist?(@html_file) @db.show_in_browser(@html_file) end
sources()
click to toggle source
# File lib/postrunner/Activity.rb, line 279 def sources @fit_activity = load_fit_file unless @fit_activity puts DataSources.new(self, @db.cfg[:unit_system]).to_s end
summary()
click to toggle source
# File lib/postrunner/Activity.rb, line 284 def summary @fit_activity = load_fit_file unless @fit_activity puts ActivitySummary.new(self, @db.cfg[:unit_system], { :name => @name, :type => activity_type, :sub_type => activity_sub_type }).to_s end
Private Instance Methods
load_fit_file(filter = nil)
click to toggle source
# File lib/postrunner/Activity.rb, line 470 def load_fit_file(filter = nil) fit_file = File.join(@db.fit_dir, @fit_file) begin fit_activity = Fit4Ruby.read(fit_file, filter) rescue Fit4Ruby::Error Log.fatal "#{@fit_file} corrupted: #{$!}" end unless fit_activity Log.fatal "#{fit_file} does not contain any activity records" end fit_activity end