postrunner 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/COPYING +339 -0
- data/Rakefile +6 -0
- data/lib/postrunner/ActivitiesDB.rb +113 -37
- data/lib/postrunner/Activity.rb +55 -12
- data/lib/postrunner/ActivityReport.rb +102 -0
- data/lib/postrunner/ActivityView.rb +133 -0
- data/lib/postrunner/ChartView.rb +195 -0
- data/lib/postrunner/FlexiTable.rb +289 -0
- data/lib/postrunner/HTMLBuilder.rb +67 -0
- data/lib/postrunner/Main.rb +74 -34
- data/lib/postrunner/PersonalRecords.rb +145 -0
- data/lib/postrunner/TrackView.rb +129 -0
- data/lib/postrunner/ViewWidgets.rb +46 -0
- data/lib/postrunner/version.rb +1 -1
- data/lib/postrunner.rb +0 -1
- data/postrunner.gemspec +5 -0
- data/spec/FlexiTable_spec.rb +14 -0
- data/spec/PostRunner_spec.rb +122 -0
- metadata +72 -4
- data/lib/postrunner/RuntimeConfig.rb +0 -20
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'fit4ruby'
|
2
|
+
|
3
|
+
require 'postrunner/ViewWidgets'
|
4
|
+
|
5
|
+
module PostRunner
|
6
|
+
|
7
|
+
class TrackView
|
8
|
+
|
9
|
+
include ViewWidgets
|
10
|
+
|
11
|
+
def initialize(activity)
|
12
|
+
@activity = activity
|
13
|
+
end
|
14
|
+
|
15
|
+
def head(doc)
|
16
|
+
doc.link({ 'rel' => 'stylesheet',
|
17
|
+
'href' => 'openlayers/theme/default/style.css',
|
18
|
+
'type' => 'text/css' })
|
19
|
+
doc.style(style)
|
20
|
+
doc.script({ 'src' => 'openlayers/OpenLayers.js' })
|
21
|
+
doc.script(java_script)
|
22
|
+
end
|
23
|
+
|
24
|
+
def div(doc)
|
25
|
+
frame(doc, 'Map') {
|
26
|
+
doc.div({ 'id' => 'map', 'class' => 'trackmap' })
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def style
|
33
|
+
<<EOT
|
34
|
+
.olControlAttribution {
|
35
|
+
bottom: 5px;
|
36
|
+
}
|
37
|
+
|
38
|
+
.trackmap {
|
39
|
+
width: 570px;
|
40
|
+
height: 400px;
|
41
|
+
border: 2px solid #545454;
|
42
|
+
}
|
43
|
+
EOT
|
44
|
+
end
|
45
|
+
|
46
|
+
def java_script
|
47
|
+
js = <<EOT
|
48
|
+
var map;
|
49
|
+
|
50
|
+
function init() {
|
51
|
+
var mercator = new OpenLayers.Projection("EPSG:900913");
|
52
|
+
var geographic = new OpenLayers.Projection("EPSG:4326");
|
53
|
+
EOT
|
54
|
+
|
55
|
+
session = @activity.fit_activity.sessions[0]
|
56
|
+
center_long = session.swc_long +
|
57
|
+
(session.nec_long - session.swc_long) / 2.0
|
58
|
+
center_lat = session.swc_lat +
|
59
|
+
(session.nec_lat - session.swc_lat) / 2.0
|
60
|
+
last_lap = @activity.fit_activity.laps[-1]
|
61
|
+
|
62
|
+
js << <<EOT
|
63
|
+
map = new OpenLayers.Map({
|
64
|
+
div: "map",
|
65
|
+
projection: mercator,
|
66
|
+
layers: [ new OpenLayers.Layer.OSM() ],
|
67
|
+
center: new OpenLayers.LonLat(#{center_long}, #{center_lat}).transform(geographic, mercator),
|
68
|
+
zoom: 13
|
69
|
+
});
|
70
|
+
EOT
|
71
|
+
js << <<"EOT"
|
72
|
+
track_layer = new OpenLayers.Layer.PointTrack("Track",
|
73
|
+
{style: {strokeColor: '#FF0000', strokeWidth: 5}});
|
74
|
+
map.addLayer(track_layer);
|
75
|
+
track_layer.addNodes([
|
76
|
+
EOT
|
77
|
+
track_points(js)
|
78
|
+
|
79
|
+
js << <<"EOT"
|
80
|
+
]);
|
81
|
+
var markers = new OpenLayers.Layer.Markers( "Markers" );
|
82
|
+
map.addLayer(markers);
|
83
|
+
|
84
|
+
var size = new OpenLayers.Size(21,25);
|
85
|
+
var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);
|
86
|
+
EOT
|
87
|
+
set_marker(js, 'marker-green', session.start_position_long,
|
88
|
+
session.start_position_lat)
|
89
|
+
@activity.fit_activity.laps[0..-2].each do |lap|
|
90
|
+
set_marker(js, 'marker-blue',
|
91
|
+
lap.end_position_long, lap.end_position_lat)
|
92
|
+
end
|
93
|
+
set_marker(js, 'marker',
|
94
|
+
last_lap.end_position_long, last_lap.end_position_lat)
|
95
|
+
js << "\n};"
|
96
|
+
|
97
|
+
js
|
98
|
+
end
|
99
|
+
|
100
|
+
def track_points(script)
|
101
|
+
first = true
|
102
|
+
@activity.fit_activity.sessions.each do |session|
|
103
|
+
session.laps.each do |lap|
|
104
|
+
lap.records.each do |record|
|
105
|
+
long = record.position_long
|
106
|
+
lat = record.position_lat
|
107
|
+
if first
|
108
|
+
first = false
|
109
|
+
else
|
110
|
+
script << ","
|
111
|
+
end
|
112
|
+
script << <<"EOT"
|
113
|
+
new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(#{long}, #{lat}).transform(geographic, mercator))
|
114
|
+
EOT
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def set_marker(script, type, long, lat)
|
121
|
+
script << <<"EOT"
|
122
|
+
markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(#{long},#{lat}).transform(geographic, mercator),new OpenLayers.Icon('openlayers/img/#{type}.png',size,offset)));
|
123
|
+
EOT
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module PostRunner
|
2
|
+
|
3
|
+
module ViewWidgets
|
4
|
+
|
5
|
+
def view_widgets_style(doc)
|
6
|
+
doc.style(<<EOT
|
7
|
+
.widget_frame {
|
8
|
+
box-sizing: border-box;
|
9
|
+
width: 600px;
|
10
|
+
padding: 10px 15px 15px 15px;
|
11
|
+
margin: 15px auto 15px auto;
|
12
|
+
border: 1px solid #ddd;
|
13
|
+
background: #fff;
|
14
|
+
background: linear-gradient(#f6f6f6 0, #fff 50px);
|
15
|
+
background: -o-linear-gradient(#f6f6f6 0, #fff 50px);
|
16
|
+
background: -ms-linear-gradient(#f6f6f6 0, #fff 50px);
|
17
|
+
background: -moz-linear-gradient(#f6f6f6 0, #fff 50px);
|
18
|
+
background: -webkit-linear-gradient(#f6f6f6 0, #fff 50px);
|
19
|
+
box-shadow: 0 3px 10px rgba(0,0,0,0.15);
|
20
|
+
-o-box-shadow: 0 3px 10px rgba(0,0,0,0.1);
|
21
|
+
-ms-box-shadow: 0 3px 10px rgba(0,0,0,0.1);
|
22
|
+
-moz-box-shadow: 0 3px 10px rgba(0,0,0,0.1);
|
23
|
+
-webkit-box-shadow: 0 3px 10px rgba(0,0,0,0.1);
|
24
|
+
}
|
25
|
+
.widget_frame_title {
|
26
|
+
padding-bottom: 5px;
|
27
|
+
}
|
28
|
+
EOT
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
def frame(doc, title)
|
33
|
+
doc.div({ 'class' => 'widget_frame' }) {
|
34
|
+
doc.div({ 'class' => 'widget_frame_title' }) {
|
35
|
+
doc.b(title)
|
36
|
+
}
|
37
|
+
doc.div {
|
38
|
+
yield if block_given?
|
39
|
+
}
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
data/lib/postrunner/version.rb
CHANGED
data/lib/postrunner.rb
CHANGED
data/postrunner.gemspec
CHANGED
@@ -19,6 +19,11 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
spec.required_ruby_version = '>=2.0'
|
21
21
|
|
22
|
+
spec.add_dependency "fit4ruby"
|
23
|
+
spec.add_dependency "nokogiri", "~> 1.6"
|
24
|
+
|
22
25
|
spec.add_development_dependency "bundler", "~> 1.6"
|
23
26
|
spec.add_development_dependency "rake"
|
27
|
+
spec.add_development_dependency "rspec"
|
28
|
+
spec.add_development_dependency "yard"
|
24
29
|
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
require 'postrunner/Main'
|
4
|
+
|
5
|
+
describe PostRunner::Main do
|
6
|
+
|
7
|
+
def postrunner(args)
|
8
|
+
args = [ '--dbdir', @db_dir ] + args
|
9
|
+
old_stdout = $stdout
|
10
|
+
$stdout = (stdout = StringIO.new)
|
11
|
+
PostRunner::Main.new(args)
|
12
|
+
$stdout = old_stdout
|
13
|
+
stdout.string
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_fit_file(name, date)
|
17
|
+
a = Fit4Ruby::Activity.new
|
18
|
+
a.timestamp = Time.parse(date)
|
19
|
+
a.total_timer_time = 30 * 60
|
20
|
+
0.upto(30) do |mins|
|
21
|
+
r = a.new_record('record')
|
22
|
+
r.timestamp = a.timestamp + mins * 60
|
23
|
+
r.distance = 200.0 * mins
|
24
|
+
r.cadence = 75
|
25
|
+
|
26
|
+
if mins > 0 && mins % 5 == 0
|
27
|
+
s = a.new_record('laps')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
a.new_record('session')
|
31
|
+
a.aggregate
|
32
|
+
Fit4Ruby.write(name, a)
|
33
|
+
end
|
34
|
+
|
35
|
+
before(:all) do
|
36
|
+
@db_dir = File.join(File.dirname(__FILE__), '.postrunner')
|
37
|
+
FileUtils.rm_rf(@db_dir)
|
38
|
+
FileUtils.rm_rf('FILE1.FIT')
|
39
|
+
create_fit_file('FILE1.FIT', '2014-07-01-8:00')
|
40
|
+
create_fit_file('FILE2.FIT', '2014-07-02-8:00')
|
41
|
+
end
|
42
|
+
|
43
|
+
after(:all) do
|
44
|
+
FileUtils.rm_rf(@db_dir)
|
45
|
+
FileUtils.rm_rf('FILE1.FIT')
|
46
|
+
FileUtils.rm_rf('FILE2.FIT')
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should abort without arguments' do
|
50
|
+
lambda { postrunner([]) }.should raise_error SystemExit
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should abort with bad command' do
|
54
|
+
lambda { postrunner(%w( foobar)) }.should raise_error SystemExit
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should support the -v option' do
|
58
|
+
postrunner(%w( --version ))
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should check a FIT file' do
|
62
|
+
postrunner(%w( check FILE1.FIT ))
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should list and empty archive' do
|
66
|
+
postrunner(%w( list ))
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should import a FIT file' do
|
70
|
+
postrunner(%w( import FILE1.FIT ))
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should check the imported file' do
|
74
|
+
postrunner(%w( check :1 ))
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should check a FIT file' do
|
78
|
+
postrunner(%w( check FILE2.FIT ))
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should list the imported file' do
|
82
|
+
postrunner(%w( list )).index('FILE1.FIT').should be_a(Fixnum)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should import another FIT file' do
|
86
|
+
postrunner(%w( import FILE2.FIT ))
|
87
|
+
list = postrunner(%w( list ))
|
88
|
+
list.index('FILE1.FIT').should be_a(Fixnum)
|
89
|
+
list.index('FILE2.FIT').should be_a(Fixnum)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should delete the first file' do
|
93
|
+
postrunner(%w( delete :2 ))
|
94
|
+
list = postrunner(%w( list ))
|
95
|
+
list.index('FILE1.FIT').should be_nil
|
96
|
+
list.index('FILE2.FIT').should be_a(Fixnum)
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should not import the deleted file again' do
|
100
|
+
postrunner(%w( import . ))
|
101
|
+
list = postrunner(%w( list ))
|
102
|
+
list.index('FILE1.FIT').should be_nil
|
103
|
+
list.index('FILE2.FIT').should be_a(Fixnum)
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'should rename FILE2.FIT activity' do
|
107
|
+
postrunner(%w( rename :1 --name foobar ))
|
108
|
+
list = postrunner(%w( list ))
|
109
|
+
list.index('FILE2.FIT').should be_nil
|
110
|
+
list.index('foobar').should be_a(Fixnum)
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'should dump an activity from the archive' do
|
114
|
+
postrunner(%w( dump :1 ))
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'should dump a FIT file' do
|
118
|
+
postrunner(%w( dump FILE1.FIT ))
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: postrunner
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Schlaeger
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-08-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: fit4ruby
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: nokogiri
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.6'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.6'
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: bundler
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -38,6 +66,34 @@ dependencies:
|
|
38
66
|
- - '>='
|
39
67
|
- !ruby/object:Gem::Version
|
40
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: yard
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
41
97
|
description: This application will allow you to manage and analyze .FIT files as generated
|
42
98
|
by Garmin GPS devices. The application was developed for the Garmin Forerunner 620.
|
43
99
|
Other devices may or may not work. Only devices that expose themselves as USB Mass
|
@@ -50,6 +106,7 @@ extensions: []
|
|
50
106
|
extra_rdoc_files: []
|
51
107
|
files:
|
52
108
|
- .gitignore
|
109
|
+
- COPYING
|
53
110
|
- Gemfile
|
54
111
|
- LICENSE.txt
|
55
112
|
- README.md
|
@@ -58,10 +115,19 @@ files:
|
|
58
115
|
- lib/postrunner.rb
|
59
116
|
- lib/postrunner/ActivitiesDB.rb
|
60
117
|
- lib/postrunner/Activity.rb
|
118
|
+
- lib/postrunner/ActivityReport.rb
|
119
|
+
- lib/postrunner/ActivityView.rb
|
120
|
+
- lib/postrunner/ChartView.rb
|
121
|
+
- lib/postrunner/FlexiTable.rb
|
122
|
+
- lib/postrunner/HTMLBuilder.rb
|
61
123
|
- lib/postrunner/Main.rb
|
62
|
-
- lib/postrunner/
|
124
|
+
- lib/postrunner/PersonalRecords.rb
|
125
|
+
- lib/postrunner/TrackView.rb
|
126
|
+
- lib/postrunner/ViewWidgets.rb
|
63
127
|
- lib/postrunner/version.rb
|
64
128
|
- postrunner.gemspec
|
129
|
+
- spec/FlexiTable_spec.rb
|
130
|
+
- spec/PostRunner_spec.rb
|
65
131
|
homepage: https://github.com/scrapper/postrunner
|
66
132
|
licenses:
|
67
133
|
- GNU GPL version 2
|
@@ -86,5 +152,7 @@ rubygems_version: 2.0.3
|
|
86
152
|
signing_key:
|
87
153
|
specification_version: 4
|
88
154
|
summary: Application to manage and analyze Garmin FIT files.
|
89
|
-
test_files:
|
155
|
+
test_files:
|
156
|
+
- spec/FlexiTable_spec.rb
|
157
|
+
- spec/PostRunner_spec.rb
|
90
158
|
has_rdoc:
|
@@ -1,20 +0,0 @@
|
|
1
|
-
module PostRunner
|
2
|
-
|
3
|
-
class RuntimeConfig
|
4
|
-
|
5
|
-
def initialize
|
6
|
-
@settings = {}
|
7
|
-
@settings['data_dir'] = File.join(ENV['HOME'], '.postrunner')
|
8
|
-
@settings['fit_dir'] = File.join(@settings['data_dir'], 'fit')
|
9
|
-
end
|
10
|
-
|
11
|
-
def [](key)
|
12
|
-
@settings[key]
|
13
|
-
end
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
Config = RuntimeConfig.new
|
18
|
-
|
19
|
-
end
|
20
|
-
|