summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Schlaeger <chris@linux.com>2016-01-17 05:46:08 +0100
committerChris Schlaeger <chris@linux.com>2016-01-17 05:46:08 +0100
commited1d5cde7f92d9d7c7de28aa4b516da3e0baf8e5 (patch)
treec6d9eb0720c260cad354d152d7e8c6633458773c
parent998bc46bdf995963a237fccf51e75317d08cd608 (diff)
downloadpostrunner-ed1d5cde7f92d9d7c7de28aa4b516da3e0baf8e5.zip
Wrapping up switch to PEROBS as database back-end.
-rw-r--r--lib/postrunner/FFS_Activity.rb49
-rw-r--r--lib/postrunner/FFS_Device.rb36
-rw-r--r--lib/postrunner/FitFileStore.rb32
-rw-r--r--lib/postrunner/Main.rb13
-rw-r--r--lib/postrunner/PersonalRecords.rb53
-rw-r--r--postrunner.gemspec6
-rw-r--r--spec/PostRunner_spec.rb59
-rw-r--r--spec/spec_helper.rb7
8 files changed, 160 insertions, 95 deletions
diff --git a/lib/postrunner/FFS_Activity.rb b/lib/postrunner/FFS_Activity.rb
index 309db1e..3921301 100644
--- a/lib/postrunner/FFS_Activity.rb
+++ b/lib/postrunner/FFS_Activity.rb
@@ -101,37 +101,34 @@ module PostRunner
attr_reader :fit_activity
# Create a new FFS_Activity object.
- # @param store [PEROBS::Store] The data base
+ # @param p [PEROBS::Handle] PEROBS handle
# @param fit_file_name [String] The fully qualified file name of the FIT
# file to add
# @param fit_entity [Fit4Ruby::FitEntity] The content of the loaded FIT
# file
- def initialize(store, device = nil, fit_file_name = nil, fit_entity = nil)
- super(store)
- init_attr(:device, device)
- init_attr(:fit_file_name, fit_file_name ?
- File.basename(fit_file_name) : nil)
- init_attr(:name, fit_file_name ? File.basename(fit_file_name) : nil)
- init_attr(:norecord, false)
+ def initialize(p, device, fit_file_name, fit_entity)
+ super(p)
+
+ self.device = device
+ self.fit_file_name = fit_file_name ? File.basename(fit_file_name) : nil
+ self.name = fit_file_name ? File.basename(fit_file_name) : nil
+ self.norecord = false
if (@fit_activity = fit_entity)
- init_attr(:timestamp, fit_entity.timestamp)
- init_attr(:total_timer_time, fit_entity.total_timer_time)
- init_attr(:sport, fit_entity.sport)
- init_attr(:sub_sport, fit_entity.sub_sport)
- init_attr(:total_distance, fit_entity.total_distance)
- init_attr(:avg_speed, fit_entity.avg_speed)
+ self.timestamp = fit_entity.timestamp
+ self.total_timer_time = fit_entity.total_timer_time
+ self.sport = fit_entity.sport
+ self.sub_sport = fit_entity.sub_sport
+ self.total_distance = fit_entity.total_distance
+ self.avg_speed = fit_entity.avg_speed
end
end
- def post_restore
- raise RuntimeError unless @device.is_a?(PEROBS::ObjectBase)
- end
-
# Store a copy of the given FIT file in the corresponding directory.
# @param fit_file_name [String] Fully qualified name of the FIT file.
def store_fit_file(fit_file_name)
# Get the right target directory for this particular FIT file.
- dir = fit_file_dir(File.basename(fit_file_name))
+ dir = @store['file_store'].fit_file_dir(File.basename(fit_file_name),
+ @device.long_uid, 'activity')
# Create the necessary directories if they don't exist yet.
create_directory(dir, 'Device activity diretory')
@@ -278,7 +275,9 @@ module PostRunner
def load_fit_file(filter = nil)
return if @fit_activity
- fit_file = File.join(fit_file_dir(@fit_file_name), @fit_file_name)
+ dir = @store['file_store'].fit_file_dir(@fit_file_name,
+ @device.long_uid, 'activity')
+ fit_file = File.join(dir, @fit_file_name)
begin
@fit_activity = Fit4Ruby.read(fit_file, filter)
rescue Fit4Ruby::Error
@@ -292,16 +291,6 @@ module PostRunner
private
- # Determine the right directory for the given FIT file. The resulting path
- # looks something like /home/user/.postrunner/devices/garmin-fenix3-1234/
- # activity/5A.
- def fit_file_dir(fit_file_base_name)
- # The first letter of the FIT file specifies the creation year.
- # The second letter of the FIT file specifies the creation month.
- dir = File.join(@store['config']['devices_dir'], @device.long_uid,
- 'activity', fit_file_base_name[0..1])
- end
-
end
end
diff --git a/lib/postrunner/FFS_Device.rb b/lib/postrunner/FFS_Device.rb
index 47c473b..5cde333 100644
--- a/lib/postrunner/FFS_Device.rb
+++ b/lib/postrunner/FFS_Device.rb
@@ -22,19 +22,24 @@ module PostRunner
# dashes. All objects are transparently stored in the PEROBS::Store.
class FFS_Device < PEROBS::Object
- po_attr :activities, :monitors, :short_uid, :long_uid
+ po_attr :activities, :monitorings, :short_uid, :long_uid
# Create a new FFS_Device object.
- # @param store [PEROBS::Store] The store to persist the data
+ # @param cf [PEROBS::ConstructorForm] cf
# @param short_uid [Fixnum] A random number used a unique ID
# @param long_uid [String] A string consisting of the manufacturer and
# product name and the serial number.
- def initialize(store, short_uid = nil, long_uid = nil)
- super(store)
- init_attr(:short_uid, short_uid)
- init_attr(:long_uid, long_uid)
- init_attr(:activities, @store.new(PEROBS::Array))
- init_attr(:monitorings, @store.new(PEROBS::Array))
+ def initialize(cf, short_uid, long_uid)
+ super(cf)
+ self.short_uid = short_uid
+ self.long_uid = long_uid
+ restore
+ end
+
+ # Handle initialization of persistent attributes.
+ def restore
+ attr_init(:activities) { @store.new(PEROBS::Array) }
+ attr_init(:monitorings) { @store.new(PEROBS::Array) }
end
# Add a new FIT file for this device.
@@ -49,10 +54,12 @@ module PostRunner
when Fit4Ruby::Activity.class
entity = activity_by_file_name(File.basename(fit_file_name))
entities = @activities
+ type = 'activity'
new_entity_class = FFS_Activity
when Fit4Ruby::Monitoring.class
entity = monitoring_by_file_name(File.basename(fit_file_name))
entities = @monitorings
+ type = 'monitoring'
new_entity_class = FFS_Monitoring
else
Log.fatal "Unsupported FIT entity #{fit_entity.class}"
@@ -69,6 +76,13 @@ module PostRunner
return nil
end
else
+ # Don't add the entity if has deleted before and overwrite isn't true.
+ path = @store['file_store'].fit_file_dir(File.basename(fit_file_name),
+ long_uid, type)
+ fq_fit_file_name = File.join(path, File.basename(fit_file_name))
+ if File.exists?(fq_fit_file_name) && !overwrite
+ return nil
+ end
# Add the new file to the list.
entity = @store.new(new_entity_class, myself, fit_file_name, fit_entity)
end
@@ -86,6 +100,12 @@ module PostRunner
entity
end
+ # Delete the given activity from the activity list.
+ # @param activity [FFS_Activity] activity to delete
+ def delete_activity(activity)
+ @activities.delete(activity)
+ end
+
# Return the activity with the given file name.
# @param file_name [String] Base name of the fit file.
# @return [FFS_Activity] Corresponding FFS_Activity or nil.
diff --git a/lib/postrunner/FitFileStore.rb b/lib/postrunner/FitFileStore.rb
index c52d5a5..09bf3ca 100644
--- a/lib/postrunner/FitFileStore.rb
+++ b/lib/postrunner/FitFileStore.rb
@@ -32,11 +32,15 @@ module PostRunner
attr_reader :store, :views
# Create a new FIT file store.
- # @param store [PEROBS::Store] Data store
- # @param cfg [RuntimeConfig] Runtime configuration data
- def initialize(store)
- super
- @data_dir = store['config']['data_dir']
+ # @param p [PEROBS::Handle] PEROBS handle
+ def initialize(p)
+ super(p)
+ restore
+ end
+
+ # Setup non-persistent variables.
+ def restore
+ @data_dir = @store['config']['data_dir']
# Ensure that we have an Array in the store to hold all known devices.
@store['devices'] = @store.new(PEROBS::Hash) unless @store['devices']
@@ -123,7 +127,7 @@ module PostRunner
pred = predecessor(activity)
succ = successor(activity)
- activity.device.activities.delete(activity)
+ activity.device.delete_activity(activity)
# The HTML activity views contain links to their predecessors and
# successors. After deleting an activity, we need to re-generate these
@@ -170,6 +174,20 @@ module PostRunner
@store['records'].generate_html_reports
generate_html_index_pages
end
+ # Determine the right directory for the given FIT file. The resulting path
+ # looks something like /home/user/.postrunner/devices/garmin-fenix3-1234/
+ # activity/5A.
+ # @param fit_file_base_name [String] The base name of the fit file
+ # @param long_uid [String] the long UID of the device
+ # @param type [String] 'activity' or 'monitoring'
+ # @return [String] the full path name of the archived FIT file
+ def fit_file_dir(fit_file_base_name, long_uid, type)
+ # The first letter of the FIT file specifies the creation year.
+ # The second letter of the FIT file specifies the creation month.
+ File.join(@store['config']['devices_dir'],
+ long_uid, type, fit_file_base_name[0..1])
+ end
+
# @return [Array of FFS_Device] List of registered devices.
@@ -340,7 +358,7 @@ module PostRunner
def generate_html_index_pages
# Ensure that HTML index is up-to-date.
- ActivityListView.new(self).update_index_pages
+ ActivityListView.new(myself).update_index_pages
end
end
diff --git a/lib/postrunner/Main.rb b/lib/postrunner/Main.rb
index 29b8e47..75af05b 100644
--- a/lib/postrunner/Main.rb
+++ b/lib/postrunner/Main.rb
@@ -33,6 +33,7 @@ module PostRunner
def initialize(args)
@filter = nil
@name = nil
+ @force = false
@attribute = nil
@value = nil
@db_dir = File.join(ENV['HOME'], '.postrunner')
@@ -41,6 +42,9 @@ module PostRunner
create_directory(@db_dir, 'PostRunner data')
@db = PEROBS::Store.new(File.join(@db_dir, 'database'))
+ if (errors = @db.check) != 0
+ Log.fatal "Postrunner DB is contains #{errors} errors"
+ end
# Create a hash to store configuration data in the store unless it
# exists already.
unless @db['config']
@@ -97,6 +101,11 @@ EOT
@filter = Fit4Ruby::FitFilter.new unless @filter
@filter.ignore_undef = true
end
+ opts.on('--force',
+ 'Import files even if they have been deleted from the ' +
+ 'database before.') do
+ @force = true
+ end
opts.separator ""
opts.separator "Options for the 'import' command:"
@@ -276,7 +285,7 @@ EOT
when 'list'
@ffs.list_activities
when 'records'
- @ffs.show_records
+ puts @records.to_s
when 'rename'
unless (@name = args.shift)
Log.fatal 'You must provide a new name for the activity'
@@ -391,7 +400,7 @@ EOT
end
if fit_entity.is_a?(Fit4Ruby::Activity)
- return @ffs.add_fit_file(fit_file_name, fit_entity)
+ return @ffs.add_fit_file(fit_file_name, fit_entity, @force)
#elsif fit_entity.is_a?(Fit4Ruby::Monitoring_B)
# return @monitoring.add(fit_file_name, fit_entity)
else
diff --git a/lib/postrunner/PersonalRecords.rb b/lib/postrunner/PersonalRecords.rb
index a509abb..098badd 100644
--- a/lib/postrunner/PersonalRecords.rb
+++ b/lib/postrunner/PersonalRecords.rb
@@ -78,8 +78,7 @@ module PostRunner
attr_reader :activity, :sport, :distance, :duration, :start_time
- def initialize(activity = nil, sport = nil, distance = nil,
- duration = nil, start_time = nil)
+ def initialize(activity, sport, distance, duration, start_time)
@activity = activity
@sport = sport
@distance = distance
@@ -97,15 +96,14 @@ module PostRunner
po_attr :activity, :sport, :distance, :duration, :start_time
- def initialize(store, result = nil)
- super(store)
- if result
- init_attr(:activity, result.activity)
- init_attr(:sport, result.sport)
- init_attr(:distance, result.distance)
- init_attr(:duration, result.duration)
- init_attr(:start_time, result.start_time)
- end
+ def initialize(p, result)
+ super(p)
+
+ self.activity = result.activity
+ self.sport = result.sport
+ self.distance = result.distance
+ self.duration = result.duration
+ self.start_time = result.start_time
end
def to_table_row(t)
@@ -127,12 +125,13 @@ module PostRunner
po_attr :sport, :year, :distance_record, :speed_records
- def initialize(store, sport = nil, year = nil)
- super(store)
- init_attr(:sport, sport)
- init_attr(:year, year)
- init_attr(:distance_record, nil)
- init_attr(:speed_records, @store.new(PEROBS::Hash))
+ def initialize(p, sport, year)
+ super(p)
+
+ self.sport = sport
+ self.year = year
+ self.distance_record = nil
+ self.speed_records = @store.new(PEROBS::Hash)
if sport
PersonalRecords::SpeedRecordDistances[sport].each_key do |dist|
@speed_records[dist.to_s] = nil
@@ -249,11 +248,12 @@ module PostRunner
po_attr :sport, :all_time, :yearly
- def initialize(store, sport = nil)
- super(store)
- init_attr(:sport, sport)
- init_attr(:all_time, @store.new(RecordSet, sport, nil))
- init_attr(:yearly, @store.new(PEROBS::Hash))
+ def initialize(p, sport)
+ super(p)
+
+ self.sport = sport
+ self.all_time = @store.new(RecordSet, sport, nil)
+ self.yearly = @store.new(PEROBS::Hash)
end
def register_result(result)
@@ -324,10 +324,11 @@ module PostRunner
end
- def initialize(store)
- super
- init_attr(:sport_records, @store.new(PEROBS::Hash))
- delete_all_records if @sport_records.empty?
+ def initialize(p)
+ super(p)
+
+ self.sport_records = @store.new(PEROBS::Hash)
+ delete_all_records
end
def scan_activity_for_records(activity, report_update_requested = false)
diff --git a/postrunner.gemspec b/postrunner.gemspec
index f23524a..555852f 100644
--- a/postrunner.gemspec
+++ b/postrunner.gemspec
@@ -7,7 +7,7 @@ Gem::Specification.new do |spec|
spec.name = "postrunner"
spec.version = PostRunner::VERSION
spec.authors = ["Chris Schlaeger"]
- spec.email = ["chris@taskjuggler.org"]
+ spec.email = ["cs@taskjuggler.org"]
spec.summary = %q{Application to manage and analyze Garmin FIT files.}
spec.description = %q{This application will allow you to manage and analyze .FIT files such as those generated by Garmin GPS devices. The application was developed and tested with the Garmin Forerunner 620 and Fenix 3. Other devices may work as well. They need to export the data as USB Mass Storage devices. It is an offline alternative to Garmin Connect.}
spec.homepage = 'https://github.com/scrapper/postrunner'
@@ -20,11 +20,11 @@ Gem::Specification.new do |spec|
spec.required_ruby_version = '>=2.0'
spec.add_dependency 'fit4ruby', '~> 0.0.8'
- spec.add_dependency 'perobs', '~> 1.0.0'
+ spec.add_dependency 'perobs', '~> 2.0.1'
spec.add_dependency 'nokogiri', '~> 1.6'
spec.add_development_dependency 'bundler', '~> 1.6'
spec.add_development_dependency 'rake', '~> 0.9.6'
- spec.add_development_dependency 'rspec', '~> 2.14.1'
+ spec.add_development_dependency 'rspec', '~> 3.4.1'
spec.add_development_dependency 'yard', '~> 0.8.7'
end
diff --git a/spec/PostRunner_spec.rb b/spec/PostRunner_spec.rb
index 24ae0fa..437438e 100644
--- a/spec/PostRunner_spec.rb
+++ b/spec/PostRunner_spec.rb
@@ -21,6 +21,8 @@ describe PostRunner::Main do
args = [ '--dbdir', @db_dir ] + args
old_stdout = $stdout
$stdout = (stdout = StringIO.new)
+ @postrunner = nil
+ GC.start
@postrunner = PostRunner::Main.new(args)
$stdout = old_stdout
stdout.string
@@ -31,10 +33,13 @@ describe PostRunner::Main do
create_working_dirs
@db_dir = File.join(@work_dir, '.postrunner')
- @file1 = File.join(@work_dir, 'FILE1.FIT')
- @file2 = File.join(@work_dir, 'FILE2.FIT')
- create_fit_file(@file1, '2014-07-01-8:00')
- create_fit_file(@file2, '2014-07-02-8:00')
+ @opts = { :t => '2014-07-01-8:00', :speed => 11.0 }
+ @file1 = create_fit_activity_file(@work_dir, @opts)
+ @opts[:t] = '2014-07-02-8:00'
+ @file2 = create_fit_activity_file(@work_dir, @opts)
+ @opts[:t] = '2014-07-03-8:00'
+ @opts[:speed] = 12.5
+ @file3 = create_fit_activity_file(@work_dir, @opts)
end
after(:all) do
@@ -74,34 +79,34 @@ describe PostRunner::Main do
end
it 'should list the imported file' do
- expect(postrunner(%w( list )).index('FILE1')).to be_a(Fixnum)
+ expect(postrunner(%w( list )).index(File.basename(@file1))).to be_a(Fixnum)
end
- it 'should import the other FIT file' do
- postrunner([ 'import', @work_dir ])
+ it 'should import the 2nd FIT file' do
+ postrunner([ 'import', @file2 ])
list = postrunner(%w( list ))
- expect(list.index('FILE1.FIT')).to be_a(Fixnum)
- expect(list.index('FILE2.FIT')).to be_a(Fixnum)
+ expect(list.index(File.basename(@file1))).to be_a(Fixnum)
+ expect(list.index(File.basename(@file2))).to be_a(Fixnum)
end
it 'should delete the first file' do
postrunner(%w( delete :2 ))
list = postrunner(%w( list ))
- expect(list.index('FILE1.FIT')).to be_nil
- expect(list.index('FILE2.FIT')).to be_a(Fixnum)
+ expect(list.index(File.basename(@file1))).to be_nil
+ expect(list.index(File.basename(@file2))).to be_a(Fixnum)
end
it 'should not import the deleted file again' do
- postrunner(%w( import . ))
+ postrunner([ 'import', @file1 ])
list = postrunner(%w( list ))
- expect(list.index('FILE1.FIT')).to be_nil
- expect(list.index('FILE2.FIT')).to be_a(Fixnum)
+ expect(list.index(File.basename(@file1))).to be_nil
+ expect(list.index(File.basename(@file2))).to be_a(Fixnum)
end
it 'should rename FILE2.FIT activity' do
postrunner(%w( rename foobar :1 ))
list = postrunner(%w( list ))
- expect(list.index('FILE2.FIT')).to be_nil
+ expect(list.index(File.basename(@file2))).to be_nil
expect(list.index('foobar')).to be_a(Fixnum)
end
@@ -109,14 +114,14 @@ describe PostRunner::Main do
expect { postrunner(%w( set foo bar :1)) }.to raise_error(Fit4Ruby::Error)
end
- it 'should set name for FILE2.FIT activity' do
+ it 'should set name for 2nd activity' do
postrunner(%w( set name foobar :1 ))
list = postrunner(%w( list ))
expect(list.index(@file2)).to be_nil
expect(list.index('foobar')).to be_a(Fixnum)
end
- it 'should set activity type for FILE2.FIT activity' do
+ it 'should set activity type for 2nd activity' do
postrunner(%w( set type Cycling :1 ))
list = postrunner(%w( summary :1 ))
expect(list.index('Running')).to be_nil
@@ -162,5 +167,25 @@ describe PostRunner::Main do
postrunner(%w( units metric ))
end
+ it 'should list records' do
+ # Add slow running activity
+ postrunner([ 'import', '--force', @file1 ])
+ list = postrunner([ 'records' ])
+ expect(list.index(File.basename(@file1))).to be_a(Fixnum)
+
+ # Add fast running activity
+ postrunner([ 'import', @file3 ])
+ list = postrunner([ 'records' ])
+ expect(list.index(File.basename(@file3))).to be_a(Fixnum)
+ expect(list.index(File.basename(@file1))).to be_nil
+ end
+
+ it 'should ignore records of an activity' do
+ postrunner(%w( set norecord true :1 ))
+ list = postrunner([ 'records' ])
+ expect(list.index(File.basename(@file1))).to be_a(Fixnum)
+ expect(list.index(File.basename(@file3))).to be_nil
+ end
+
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index dba6be6..2d93e12 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -96,14 +96,17 @@ def create_fit_activity(config)
:garmin_product => 'sdm4',
:device_index => 1, :battery_status => 'ok' })
laps = 0
+ curr_speed = (config[:speed] ? config[:speed] : 10.0) / 3.6 # as m/s
+ curr_distance = 0.0 # as m
0.upto((a.total_timer_time / 60) - 1) do |mins|
+ curr_speed -= 0.05 if curr_speed > 2.5
a.new_record({
:timestamp => ts,
:position_lat => 51.5512 - mins * 0.0008,
:position_long => 11.647 + mins * 0.002,
- :distance => 200.0 * mins,
+ :distance => curr_distance += curr_speed * 60,
:altitude => 100 + mins * 3,
- :speed => 3.1,
+ :speed => curr_speed,
:vertical_oscillation => 90 + mins * 0.2,
:stance_time => 235.0 * mins * 0.01,
:stance_time_percent => 32.0,