fit4ruby 0.0.1

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.
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env ruby -w
2
+ # encoding: UTF-8
3
+ #
4
+ # = Log.rb -- Fit4Ruby - FIT file processing library for Ruby
5
+ #
6
+ # Copyright (c) 2014 by Chris Schlaeger <cs@taskjuggler.org>
7
+ #
8
+ # This program is free software; you can redistribute it and/or modify
9
+ # it under the terms of version 2 of the GNU General Public License as
10
+ # published by the Free Software Foundation.
11
+ #
12
+
13
+ require 'logger'
14
+
15
+ module Fit4Ruby
16
+
17
+ # This is the Exception type that will be thrown for all unrecoverable
18
+ # errors.
19
+ class Error < StandardError ; end
20
+
21
+ class ILogger < Logger
22
+
23
+ def fatal(msg)
24
+ super
25
+ exit 1
26
+ end
27
+
28
+ def critical(msg, exception = nil)
29
+ if exception
30
+ raise Error, "#{msg}: #{exception.message}", exception.backtrace
31
+ else
32
+ raise Error, msg
33
+ end
34
+ end
35
+
36
+ end
37
+
38
+ Log = ILogger.new(STDOUT)
39
+ Log.level = Logger::WARN
40
+ Log.formatter = proc do |severity, time, progname, msg|
41
+ msg + "\n"
42
+ end
43
+
44
+ end
45
+
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env ruby -w
2
+ # encoding: UTF-8
3
+ #
4
+ # = Record.rb -- Fit4Ruby - FIT file processing library for Ruby
5
+ #
6
+ # Copyright (c) 2014 by Chris Schlaeger <cs@taskjuggler.org>
7
+ #
8
+ # This program is free software; you can redistribute it and/or modify
9
+ # it under the terms of version 2 of the GNU General Public License as
10
+ # published by the Free Software Foundation.
11
+ #
12
+
13
+ module Fit4Ruby
14
+
15
+ class Record
16
+
17
+ attr_reader :timestamp, :latitude, :longitude, :altitude, :distance,
18
+ :speed, :vertical_oscillation, :cadence, :stance_time
19
+
20
+ def initialize
21
+ end
22
+
23
+ def set(field, value)
24
+ case field
25
+ when 'timestamp'
26
+ @timestamp = value
27
+ when 'position_lat'
28
+ @latitude = value
29
+ when 'position_long'
30
+ @longitude = value
31
+ when 'altitude'
32
+ @altitude = value
33
+ when 'distance'
34
+ @distance = value
35
+ when 'speed'
36
+ @speed = value
37
+ when 'vertical_oscillation'
38
+ @vertical_oscillation = value
39
+ when 'cadence'
40
+ @cadence = 2 * value
41
+ when 'fractional_cadence'
42
+ @cadence += 2 * value if @cadence
43
+ when 'stance_time'
44
+ @stance_time = value
45
+ else
46
+ end
47
+ end
48
+
49
+ def pace
50
+ 1000.0 / (@speed * 60.0)
51
+ end
52
+
53
+ end
54
+
55
+ end
56
+
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env ruby -w
2
+ # encoding: UTF-8
3
+ #
4
+ # = Session.rb -- Fit4Ruby - FIT file processing library for Ruby
5
+ #
6
+ # Copyright (c) 2014 by Chris Schlaeger <cs@taskjuggler.org>
7
+ #
8
+ # This program is free software; you can redistribute it and/or modify
9
+ # it under the terms of version 2 of the GNU General Public License as
10
+ # published by the Free Software Foundation.
11
+ #
12
+
13
+ require 'fit4ruby/Converters'
14
+
15
+ module Fit4Ruby
16
+
17
+ class Session
18
+
19
+ include Converters
20
+
21
+ attr_reader :start_time, :duration, :distance, :strides, :ascend,
22
+ :descent, :calories, :avg_speed, :avg_heart_rate,
23
+ :max_heart_rate, :avg_vertical_oscillation, :avg_stance_time,
24
+ :avg_running_cadence, :avg_running_cadence, :training_effect,
25
+ :first_lap_index, :num_laps
26
+
27
+ def initialize
28
+ @laps = []
29
+ end
30
+
31
+ def check(activity)
32
+ @first_lap_index.upto(@first_lap_index - @num_laps) do |i|
33
+ if (lap = activity.lap[i])
34
+ @laps << lap
35
+ else
36
+ Log.error "Session references lap #{i} which is not contained in "
37
+ "the FIT file."
38
+ end
39
+ end
40
+ end
41
+
42
+ def set(field, value)
43
+ return unless value
44
+
45
+ case field
46
+ when 'start_time'
47
+ @start_time = value
48
+ when 'total_timer_time'
49
+ @duration = value
50
+ when 'total_distance'
51
+ @distance = value
52
+ when 'total_strides'
53
+ @strides = value
54
+ when 'total_ascent'
55
+ @ascend = value
56
+ when 'total_descent'
57
+ @descent = value
58
+ when 'total_calories'
59
+ @calories = value
60
+ when 'avg_speed'
61
+ @avg_speed = value
62
+ when 'avg_heart_rate'
63
+ @avg_heart_rate = value
64
+ when 'max_heart_rate'
65
+ @max_heart_rate = value
66
+ when 'avg_vertical_oscillation'
67
+ @avg_vertical_oscillation = value
68
+ when 'avg_stance_time'
69
+ @avg_stance_time = value
70
+ when 'avg_running_cadence'
71
+ @avg_running_cadence = 2 * value
72
+ when 'avg_fraction_cadence'
73
+ @avg_running_cadence += 2 * value
74
+ when 'total_training_effect'
75
+ @training_effect = value
76
+ when 'first_lap_index'
77
+ @first_lap_index = value
78
+ when 'num_laps'
79
+ @num_laps = value
80
+ else
81
+ end
82
+ end
83
+
84
+ def avg_stride_length
85
+ @distance / @strides
86
+ end
87
+
88
+ def to_s
89
+ <<"EOT"
90
+ Date: #{@start_time}
91
+ Distance: #{'%.2f' % (@distance / 1000.0)} km
92
+ Time: #{secsToHMS(@duration)}
93
+ Avg Pace: #{speedToPace(@avg_speed)} min/km
94
+ Total Ascend: #{@ascend} m
95
+ Total Descend: #{@descent} m
96
+ Calories: #{@calories} kCal
97
+ Avg HR: #{@avg_heart_rate} bpm
98
+ Max HR: #{@max_heart_rate} bpm
99
+ Training Effect: #{@training_effect}
100
+ Avg Run Cadence: #{@avg_running_cadence.round} spm
101
+ Avg Vertical Oscillation: #{'%.1f' % (@avg_vertical_oscillation / 10)} cm
102
+ Avg Ground Contact Time: #{@avg_stance_time.round} ms
103
+ Avg Stride Length: #{'%.2f' % (avg_stride_length / 2)} m
104
+ EOT
105
+ end
106
+
107
+ end
108
+
109
+ end
110
+
@@ -0,0 +1,3 @@
1
+ module Fit4Ruby
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,169 @@
1
+ require 'time'
2
+
3
+ desc 'Generate the CHANGELOG file'
4
+ task :changelog do
5
+
6
+ class Entry
7
+
8
+ attr_reader :type
9
+
10
+ def initialize(ref, author, time, message)
11
+ @ref = ref
12
+ @author = author
13
+ @time = time
14
+ @message = message
15
+ if (m = /New: (.*)/.match(@message))
16
+ @type = :feature
17
+ @message = m[1]
18
+ elsif (m = /Fix: (.*)/.match(@message))
19
+ @type = :bugfix
20
+ @message = m[1]
21
+ else
22
+ @type = :other
23
+ end
24
+ end
25
+
26
+ def to_s
27
+ " * #{@message}\n"
28
+ end
29
+
30
+ end
31
+
32
+ class Release
33
+
34
+ attr_reader :date, :version, :tag
35
+
36
+ def initialize(tag, predecessor)
37
+ @tag = tag
38
+ # We only support release tags in the form X.X.X
39
+ @version = /\d+\.\d+\.\d+/.match(tag)
40
+
41
+ # Construct a Git range.
42
+ interval = predecessor ? "#{predecessor.tag}..#{@tag}" : @tag
43
+
44
+ # Get the date of the release
45
+ date = Time.parse(/Date: (.*)/.match(`git show #{tag}`)[1]).utc
46
+ @date = date.strftime("%Y-%m-%d")
47
+
48
+ @entries = []
49
+ # Use -z option for git-log to get 0 bytes as separators.
50
+ `git log -z #{interval}`.split("\0").each do |commit|
51
+ # We ignore merges.
52
+ next if commit =~ /^Merge: \d*/
53
+
54
+ ref, author, time, _, message = commit.split("\n", 5)
55
+ ref = ref[/commit ([0-9a-f]+)/, 1]
56
+ author = author[/Author: (.*)/, 1].strip
57
+ time = Time.parse(time[/Date: (.*)/, 1]).utc
58
+ # Eleminate git-svn-id: lines
59
+ message.gsub!(/git-svn-id: .*\n/, '')
60
+ # Eliminate Signed-off-by: lines
61
+ message.gsub!(/Signed-off-by: .*\n/, '')
62
+ message.strip!
63
+ @entries << Entry.new(ref, author, time, message)
64
+ end
65
+ end
66
+
67
+ def empty?
68
+ @entries.empty?
69
+ end
70
+
71
+ def to_s
72
+ s = ''
73
+ if hasFeatures? || hasFixes?
74
+ if hasFeatures?
75
+ s << "== New Features\n\n"
76
+ @entries.each do |entry|
77
+ s << entry.to_s if entry.type == :feature
78
+ end
79
+ s << "\n"
80
+ end
81
+ if hasFixes?
82
+ s << "== Bug Fixes\n\n"
83
+ @entries.each do |entry|
84
+ s << entry.to_s if entry.type == :bugfix
85
+ end
86
+ s << "\n"
87
+ end
88
+ else
89
+ @entries.each do |entry|
90
+ s << entry.to_s
91
+ end
92
+ end
93
+ s
94
+ end
95
+
96
+ private
97
+
98
+ def hasFeatures?
99
+ @entries.each do |entry|
100
+ return true if entry.type == :feature
101
+ end
102
+ false
103
+ end
104
+
105
+ def hasFixes?
106
+ @entries.each do |entry|
107
+ return true if entry.type == :bugfix
108
+ end
109
+ false
110
+ end
111
+
112
+ end
113
+
114
+ class ChangeLog
115
+
116
+ def initialize
117
+ @releases = []
118
+ predecessor = nil
119
+ getReleaseVersions.each do |version|
120
+ @releases << (predecessor = Release.new(version, predecessor))
121
+ end
122
+ end
123
+
124
+ def to_s
125
+ s = ''
126
+ @releases.reverse.each do |release|
127
+ next if release.empty?
128
+
129
+ # We use RDOC markup syntax to generate a title
130
+ if release.version
131
+ s << "= Release #{release.version} (#{release.date})\n\n"
132
+ else
133
+ s << "= Next Release (Some Day)\n\n"
134
+ end
135
+ s << release.to_s + "\n"
136
+ end
137
+ s
138
+ end
139
+
140
+ private
141
+
142
+ # 'git tag' is not sorted numerically. This function implements a
143
+ # numerical comparison for tag versions of the format 'release-X.X.X'. X
144
+ # can be a multi-digit number.
145
+ def compareTags(a, b)
146
+
147
+ def versionToComparable(v)
148
+ /\d+\.\d+\.\d+/.match(v)[0].split('.').map{ |l| sprintf("%03d", l.to_i)}.
149
+ join('.')
150
+ end
151
+
152
+ versionToComparable(a) <=> versionToComparable(b)
153
+ end
154
+
155
+ def getReleaseVersions
156
+ # Get list of release tags from Git repository
157
+ releaseVersions = `git tag`.split("\n").map { |r| r.chomp }.
158
+ delete_if { |r| ! (/release-\d+\.\d+\.\d+/ =~ r) }.
159
+ sort{ |a, b| compareTags(a, b) }
160
+ releaseVersions << 'HEAD'
161
+ end
162
+
163
+ end
164
+
165
+ File.open('CHANGELOG', 'w+') do |changelog|
166
+ changelog.puts ChangeLog.new.to_s
167
+ end
168
+
169
+ end
@@ -0,0 +1,52 @@
1
+ # GEM TASK
2
+ require 'find'
3
+ require 'rubygems'
4
+ require 'rubygems/package'
5
+
6
+ # Unfortunately Rake::GemPackageTest cannot deal with files that are generated
7
+ # by Rake targets. So we have to write our own packaging task.
8
+ desc 'Build the gem package'
9
+ task :gem => [:clobber] do
10
+ Rake::Task[:changelog].invoke
11
+ Rake::Task[:permissions].invoke
12
+
13
+ load 'fit4ruby.gemspec';
14
+
15
+ # Build the gem file according to the loaded spec.
16
+ if RUBY_VERSION >= "2.0.0"
17
+ Gem::Package.build(GEM_SPEC)
18
+ else
19
+ Gem::Builder.new(GEM_SPEC).build
20
+ end
21
+ pkgBase = "#{GEM_SPEC.name}-#{GEM_SPEC.version}"
22
+ # Create a pkg directory if it doesn't exist already.
23
+ FileUtils.mkdir_p('pkg')
24
+ # Move the gem file into the pkg directory.
25
+ verbose(true) { FileUtils.mv("#{pkgBase}.gem", "pkg/#{pkgBase}.gem")}
26
+ end
27
+
28
+ desc 'Make sure all files and directories are readable'
29
+ task :permissions do
30
+ # Find the bin and test directories relative to this file.
31
+ baseDir = File.expand_path('..', File.dirname(__FILE__))
32
+
33
+ execs = Dir.glob("#{baseDir}/bin/*") +
34
+ Dir.glob("#{baseDir}/test/**/genrefs")
35
+
36
+ Find.find(baseDir) do |f|
37
+ # Ignore the whoke pkg directory as it may contain links to the other
38
+ # directories.
39
+ next if Regexp.new("#{baseDir}/pkg/*").match(f)
40
+
41
+ FileUtils.chmod_R((FileTest.directory?(f) ||
42
+ execs.include?(f) ? 0755 : 0644), f)
43
+ end
44
+ end
45
+
46
+ desc 'Run all tests and build scripts and create the gem package'
47
+ task :release do
48
+ Rake::Task[:test].invoke
49
+ Rake::Task[:yard].invoke
50
+ Rake::Task[:gem].invoke
51
+ end
52
+
@@ -0,0 +1,14 @@
1
+ if RUBY_VERSION < '1.9.3'
2
+ require 'rake/rdoctask'
3
+ else
4
+ require 'rdoc/task'
5
+ end
6
+
7
+ # RDOC TASK
8
+ Rake::RDocTask.new(:rdoc) do |t|
9
+ t.rdoc_files = %w( README.rdoc COPYING CHANGELOG ) +
10
+ `git ls-files -- lib`.split("\n")
11
+ t.title = "TaskJuggler API documentation"
12
+ t.main = 'README.rdoc'
13
+ t.rdoc_dir = 'doc'
14
+ end