Gooby 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. data/README +218 -0
  2. data/bin/forerunner_xml_parser.rb +28 -0
  3. data/bin/forerunner_xml_parser.sh +16 -0
  4. data/bin/forerunner_xml_splitter.rb +60 -0
  5. data/bin/gen_gmap.rb +30 -0
  6. data/bin/gen_gmap.sh +19 -0
  7. data/bin/tests_gen.rb +13 -0
  8. data/data/20041113_richmond_marathon.csv +1036 -0
  9. data/data/20041113_richmond_marathon.xml +8663 -0
  10. data/data/20050305_corporate_cup_hm.csv +251 -0
  11. data/data/20050305_corporate_cup_hm.xml +2208 -0
  12. data/data/20050430_nashville_marathon.csv +1208 -0
  13. data/data/20050430_nashville_marathon.xml +10043 -0
  14. data/data/20051119_dowd_ymca_hm.csv +251 -0
  15. data/data/20051119_dowd_ymca_hm.xml +2210 -0
  16. data/data/20051124_hyatt_turkey_trot_8K.csv +321 -0
  17. data/data/20051124_hyatt_turkey_trot_8K.xml +2651 -0
  18. data/data/forerunner_2005.xml +259620 -0
  19. data/data/forerunner_2006.xml +190853 -0
  20. data/data/forerunner_2007.xml +259014 -0
  21. data/data/geo_data.txt +171 -0
  22. data/data/phx.csv +1280 -0
  23. data/data/phx.xml +10620 -0
  24. data/data/run_2007_01_01_16_38_27.xml +2020 -0
  25. data/data/run_2007_01_06_15_27_31.xml +2020 -0
  26. data/data/run_2007_01_10_12_25_47.xml +820 -0
  27. data/data/run_2007_01_10_22_44_54.csv +112 -0
  28. data/data/run_2007_01_10_22_44_54.xml +908 -0
  29. data/data/run_2007_01_11_10_48_45.xml +1292 -0
  30. data/data/run_2007_01_13_15_37_06.xml +1964 -0
  31. data/data/run_2007_01_14_15_46_02.xml +1368 -0
  32. data/data/run_2007_01_15_14_01_48.xml +1868 -0
  33. data/data/run_2007_01_20_16_22_05.xml +1702 -0
  34. data/data/run_2007_01_27_17_32_13.xml +3626 -0
  35. data/data/run_2007_01_28_19_14_52.xml +2538 -0
  36. data/data/run_2007_02_03_14_30_20.xml +2016 -0
  37. data/data/run_2007_02_04_18_02_30.xml +1476 -0
  38. data/data/run_2007_02_17_16_29_35.xml +1236 -0
  39. data/data/run_2007_02_19_14_44_33.xml +1816 -0
  40. data/data/run_2007_02_23_15_53_55.xml +36 -0
  41. data/data/run_2007_02_23_15_55_20.xml +1296 -0
  42. data/data/run_2007_02_24_15_01_35.csv +484 -0
  43. data/data/run_2007_02_24_15_01_35.xml +3884 -0
  44. data/data/test1.txt +4 -0
  45. data/lib/cls_counter_hash.rb +83 -0
  46. data/lib/cls_delim_line.rb +40 -0
  47. data/lib/cls_dttm.rb +84 -0
  48. data/lib/cls_duration.rb +87 -0
  49. data/lib/cls_forerunner_xml_parser.rb +183 -0
  50. data/lib/cls_forerunner_xml_splitter.rb +113 -0
  51. data/lib/cls_geo_data.rb +186 -0
  52. data/lib/cls_gooby_object.rb +23 -0
  53. data/lib/cls_google_map_generator.rb +368 -0
  54. data/lib/cls_history.rb +38 -0
  55. data/lib/cls_lap.rb +27 -0
  56. data/lib/cls_line.rb +78 -0
  57. data/lib/cls_options.rb +74 -0
  58. data/lib/cls_position.rb +49 -0
  59. data/lib/cls_run.rb +199 -0
  60. data/lib/cls_simple_xml_parser.rb +46 -0
  61. data/lib/cls_test_regen.rb +187 -0
  62. data/lib/cls_track.rb +52 -0
  63. data/lib/cls_trackpoint.rb +205 -0
  64. data/lib/mod_introspect.rb +33 -0
  65. data/lib/mod_io.rb +65 -0
  66. data/lib/mod_project_info.rb +81 -0
  67. data/lib/mod_string.rb +26 -0
  68. data/lib/mod_test_helper.rb +22 -0
  69. data/samples/20041113_richmond_marathon.html +532 -0
  70. data/samples/20050305_corporate_cup_hm.html +448 -0
  71. data/samples/20050430_nashville_marathon.html +530 -0
  72. data/samples/gps_point_capture.html +54 -0
  73. data/samples/phoenix_marathon.html +542 -0
  74. data/samples/run_2007_01_10_22_44_54.html +146 -0
  75. data/samples/run_2007_02_24_15_01_35.html +298 -0
  76. data/tests/tst_cls_counter_hash.rb +105 -0
  77. data/tests/tst_cls_delim_line.rb +72 -0
  78. data/tests/tst_cls_dttm.rb +129 -0
  79. data/tests/tst_cls_duration.rb +49 -0
  80. data/tests/tst_cls_forerunner_xml_parser.rb +68 -0
  81. data/tests/tst_cls_geo_data.rb +69 -0
  82. data/tests/tst_cls_gooby_object.rb +24 -0
  83. data/tests/tst_cls_google_map_generator.rb +107 -0
  84. data/tests/tst_cls_history.rb +44 -0
  85. data/tests/tst_cls_lap.rb +36 -0
  86. data/tests/tst_cls_line.rb +108 -0
  87. data/tests/tst_cls_options.rb +77 -0
  88. data/tests/tst_cls_position.rb +64 -0
  89. data/tests/tst_cls_run.rb +140 -0
  90. data/tests/tst_cls_simple_xml_parser.rb +48 -0
  91. data/tests/tst_cls_track.rb +68 -0
  92. data/tests/tst_cls_trackpoint.rb +143 -0
  93. data/tests/tst_gooby.rb +28 -0
  94. data/tests/tst_mod_introspect.rb +30 -0
  95. data/tests/tst_mod_io.rb +51 -0
  96. data/tests/tst_mod_project_info.rb +77 -0
  97. data/tests/tst_mod_string.rb +56 -0
  98. metadata +142 -0
@@ -0,0 +1,46 @@
1
+ =begin rdoc
2
+
3
+ Sample implementation of a REXML::StreamListener SAX parser.
4
+
5
+ Gooby - Copyright 2007 by Chris Joakim.
6
+ Gooby is available under GNU General Public License (GPL) license.
7
+
8
+ =end
9
+
10
+ module Gooby
11
+
12
+ class SimpleXmlParser
13
+
14
+ include REXML::StreamListener
15
+
16
+ attr_accessor :tag_count, :watched_tags
17
+
18
+ def initialize
19
+ @tag_count = 0
20
+ @counter_hash = CounterHash.new
21
+ end
22
+
23
+ public
24
+
25
+ # SAX API method. Increments the tagname in the counter hash.
26
+ def tag_start(tag_name, attrs)
27
+ @tag_count = @tag_count + 1
28
+ @counter_hash.increment(tag_name)
29
+ end
30
+
31
+ # SAX API method. No impl.
32
+ def tag_end(tagname)
33
+ end
34
+
35
+ # SAX API method. No impl.
36
+ def text(txt)
37
+ end
38
+
39
+ # Prints the state of this object (the counter hash).
40
+ def dump
41
+ puts @counter_hash.to_s
42
+ end
43
+
44
+ end
45
+
46
+ end
@@ -0,0 +1,187 @@
1
+ =begin rdoc
2
+
3
+ This class is used to generate, on an ongoing basis, the various Gooby test
4
+ classes. Regeneration retains the current test methods, and adds stubs for
5
+ new test methods. All methods, old and new, appear in the merged output in
6
+ propper sort sequence.
7
+
8
+ Gooby - Copyright 2007 by Chris Joakim.
9
+ Gooby is available under GNU General Public License (GPL) license.
10
+
11
+ =end
12
+
13
+ module Gooby
14
+
15
+ class TestGenerator < GoobyObject
16
+
17
+ def initialize
18
+ tested_files.each { | base_rb_file |
19
+ test_file = "tst_#{base_rb_file}"
20
+ if true
21
+ @codeLines = read_lines("lib/#{base_rb_file}")
22
+ @testLines = read_lines("tests/#{test_file}")
23
+ @codeHash = Hash.new
24
+ @testHash = Hash.new
25
+ @mergedHash = Hash.new
26
+ @excludeClasses = %w( TestHelper TestGenerator )
27
+ puts "current codeLines = #{@codeLines.size}"
28
+ puts "current testLines = #{@testLines.size}"
29
+ parse_code_lines
30
+ parse_test_lines
31
+ merge_keys
32
+ regenerate(test_file)
33
+ end
34
+ }
35
+ end
36
+
37
+ private
38
+
39
+ # Produce a Hash with keys and values like the following:
40
+ # test|class|Trackpoint|deg2rad Trackpoint|deg2rad(degrees)
41
+
42
+ def parse_code_lines
43
+ type = ''
44
+ type_name = ''
45
+ meth_name = ''
46
+ @codeLines.each { | line |
47
+ line.strip!
48
+ if (line.match(/^module /))
49
+ type = 'module'
50
+ tokens = line.split
51
+ type_name = tokens[1]
52
+ elsif (line.match(/^class /))
53
+ type = 'class'
54
+ tokens = line.split
55
+ type_name = tokens[1]
56
+ elsif (line.match(/^def /))
57
+ signature = line[4...999]
58
+ short_method = parse_meth_name("#{signature}")
59
+ @codeHash["test_#{type}_#{type_name}"] = "#{type_name}"
60
+ @codeHash["test_#{type}_#{type_name}_#{short_method}"] = "#{type_name}|#{signature}"
61
+ end
62
+ }
63
+ end
64
+
65
+ def parse_meth_name(string)
66
+ string.gsub!('(', ' ')
67
+ string.gsub!(')', ' ')
68
+ tokens = string.split
69
+ tokens[0]
70
+ end
71
+
72
+ def parse_test_lines
73
+ in_method = true
74
+ method_name = 'a_start'
75
+ method_lines = Array.new
76
+ line_num = 0
77
+
78
+ @testLines.each { | line |
79
+ line_num = line_num + 1
80
+ line.chomp!
81
+ prefix = line[0...5] # ' def'
82
+ prefix21 = line[0...21] # ' ### Start of tests.'
83
+
84
+ if ((prefix == ' def') || (prefix == "\tdef"))
85
+ in_method = true
86
+ tokens = line.split
87
+ method_name = tokens[1]
88
+ method_lines = Array.new
89
+ end
90
+ if in_method
91
+ method_lines << "#{line}"
92
+ end
93
+ if prefix21 == ' ### Start of tests.'
94
+ in_method = false
95
+ @testHash["#{method_name}"] = method_lines
96
+ end
97
+ if ((prefix == ' end') || (prefix == "\tend"))
98
+ in_method = false
99
+ @testHash["#{method_name}"] = method_lines
100
+ end
101
+ }
102
+ end
103
+
104
+ def merge_keys
105
+ @codeHash.keys.sort.each { |key| @mergedHash["#{key}"] = "code" }
106
+ @testHash.keys.sort.each { |key| @mergedHash["#{key}"] = "test" }
107
+ end
108
+
109
+ def regenerate(test_file)
110
+ code = ''
111
+ @mergedHash.keys.sort.each { |key|
112
+
113
+ tokens = key.split('_')
114
+ type, name, meth = tokens[1], tokens[2], tokens[3]
115
+
116
+ processThisKey = true
117
+ @excludeClasses.each { |xc|
118
+ if xc == name
119
+ processThisKey = false
120
+ end
121
+ }
122
+
123
+ next if !processThisKey
124
+
125
+ if @testHash.has_key?(key)
126
+ # We already have a test method written in the test class,
127
+ # so keep this currently existing test code!
128
+
129
+ if @codeHash.has_key?(key)
130
+ comment = nil
131
+ else
132
+ if key != 'a_start'
133
+ comment = "# Warning: possible obsolete test method - #{key}"
134
+ end
135
+ end
136
+
137
+ code << "\n"
138
+ if comment != nil
139
+ code << "\n#{comment}"
140
+ code << "\n"
141
+ end
142
+ array = @testHash["#{key}"]
143
+ array.each { |line| code << "\n#{line}" }
144
+ else
145
+ # We don't have this test method in the current test class,
146
+ # so generate a test method stub.
147
+
148
+ code << "\n"
149
+ code << "\n def #{key}"
150
+ code << "\n"
151
+
152
+ if @gen_impl_stub
153
+ if type = 'class'
154
+ code << "\n #obj = #{type}.new"
155
+ code << "\n #result = obj.#{meth}"
156
+ code << "\n #expected = ''"
157
+ s = "\n"
158
+ s << ' #assert_equal(expected, actual, "'
159
+ s << "#{type}.#{meth} "
160
+ s << 'values are not as expected; #{result} vs #{expected}")'
161
+ code << s
162
+ else
163
+ code << "\n #result = #{type}.#{meth}"
164
+ code << "\n #expected = ''"
165
+ s = "\n"
166
+ s << ' #assert_equal(expected, actual, "'
167
+ s << "#{type}.#{meth} "
168
+ s << 'values are not as expected; #{result} vs #{expected}")'
169
+ code << s
170
+ end
171
+ end
172
+ code << "\n end"
173
+ end
174
+ }
175
+ code << "\nend" # end of class
176
+ code << "\n"
177
+ fn = "tests/#{test_file}"
178
+ out = File.new fn, "w+"
179
+ out.write code
180
+ out.flush
181
+ out.close
182
+ puts "file written: #{fn}"
183
+ end
184
+
185
+ end
186
+
187
+ end
data/lib/cls_track.rb ADDED
@@ -0,0 +1,52 @@
1
+ =begin rdoc
2
+
3
+ Instances of this class represent a <Track> aggregate object from a Forerunner
4
+ XML file. Note that a <Run> may contain more than one <Track> aggregates.
5
+
6
+ Gooby - Copyright 2007 by Chris Joakim.
7
+ Gooby is available under GNU General Public License (GPL) license.
8
+
9
+ =end
10
+
11
+ module Gooby
12
+
13
+ class Track < GoobyObject
14
+
15
+ attr_reader :number, :descr, :trackpoints
16
+
17
+ def initialize(num=0, descr='')
18
+ @number = num
19
+ @descr = descr
20
+ @trackpoints = Array.new
21
+ end
22
+
23
+ public
24
+
25
+ def add_trackpoint(tkpt)
26
+ @trackpoints.push(tkpt)
27
+ end
28
+
29
+ def size
30
+ @trackpoints.size
31
+ end
32
+
33
+ def first_tkpt
34
+ @trackpoints.size > 0 ? @trackpoints[0] : nil
35
+ end
36
+
37
+ def last_tkpt
38
+ @trackpoints.size > 0 ? @trackpoints[-1] : nil
39
+ end
40
+
41
+ def to_s
42
+ return "Trk: #{@descr} tkpts: #{size}"
43
+ end
44
+
45
+ def dump
46
+ puts "Track: '#{@descr}' tkpts: #{size}"
47
+ @trackpoints.each { |tkpt| puts tkpt.to_csv } # to_geo_s
48
+ end
49
+
50
+ end
51
+
52
+ end
@@ -0,0 +1,205 @@
1
+ =begin rdoc
2
+
3
+ Instances of this class represent a <Trackpoint> aggregate from a Forerunner
4
+ XML file. Additionally, there is distance, pace, and Google Map generation
5
+ logic in this class.
6
+
7
+ <Trackpoint>
8
+ <Position>
9
+ <Latitude>35.49577</Latitude>
10
+ <Longitude>-80.83281</Longitude>
11
+ <Altitude>232.296</Altitude>
12
+ </Position>
13
+ <Time>2007-01-06T15:27:51Z</Time>
14
+ </Trackpoint>
15
+
16
+ Gooby - Copyright 2007 by Chris Joakim.
17
+ Gooby is available under GNU General Public License (GPL) license.
18
+
19
+ =end
20
+
21
+ module Gooby
22
+
23
+ class Trackpoint < Position
24
+
25
+ attr_reader :first, :last, :number, :runNumber, :dttm, :prevTkpt, :descr
26
+ attr_reader :cumulativeDistance, :cumulativePace, :incrementalDistance, :split, :prevSplit
27
+ attr_writer :first, :last, :runNumber
28
+
29
+ def initialize(num, lat, lng, alt, time_string, descr='')
30
+ @number = num
31
+ @runNumber = 0
32
+
33
+ # initialize superclass variables:
34
+ @latitude = lat.to_s
35
+ @longitude = lng.to_s
36
+ @altitude = alt.to_s
37
+ @note = note.to_s
38
+ @dttm = DtTm.new(time_string)
39
+ @first = false
40
+ @last = false
41
+ @prevTkpt = nil
42
+ @cumulativeDistance, @split = 0.0, 0.0
43
+ @cumulativePace = ""
44
+ @descr = descr
45
+ end
46
+
47
+ public
48
+
49
+ def position
50
+ Position.new(@latitude, @longitude, @altitude, @note)
51
+ end
52
+
53
+ def to_s
54
+ "Tkpt: #{@number} #{super.to_s} date: #{@dttm.to_s} cdist: #{@cumulativeDistance}"
55
+ end
56
+
57
+ def to_csv
58
+ ss = position.to_csv
59
+ "#{@runNumber} | #{@dttm.to_s} | #{@number} | #{ss} | #{@cumulativeDistance} "
60
+ end
61
+
62
+ def to_geo_s
63
+ ss = position.to_csv
64
+ "Tkpt: #{@number} | #{ss} | #{@descr}"
65
+ end
66
+
67
+ def compute_distance_and_pace(curr_index, start_dttm, prev_cumulative_dist, prev_trackpoint, units)
68
+ @prev_tkpt = prev_trackpoint
69
+ @cumulativeDistance = prev_cumulative_dist.to_f
70
+
71
+ if @prev_tkpt
72
+ arg1 = latitude().to_f
73
+ arg2 = @prev_tkpt.latitude().to_f
74
+ arg3 = latitude().to_f
75
+ arg4 = @prev_tkpt.latitude().to_f
76
+ theta = longitude().to_f - @prev_tkpt.longitude().to_f
77
+
78
+ res1 = Math.sin(deg2rad(arg1))
79
+ res2 = Math.sin(deg2rad(arg2))
80
+ res3 = Math.cos(deg2rad(arg3))
81
+ res4 = Math.cos(deg2rad(arg4))
82
+ res5 = Math.cos(deg2rad(theta.to_f))
83
+
84
+ incremental_distance = ((res1 * res2) + (res3 * res4 * res5)).to_f
85
+
86
+ if (!incremental_distance.nan?)
87
+ incremental_distance = Math.acos(incremental_distance.to_f)
88
+ if (!incremental_distance.nan?)
89
+ incremental_distance = rad2deg(incremental_distance)
90
+ if (!incremental_distance.nan?)
91
+ incremental_distance = incremental_distance * 60 * 1.1515;
92
+ if (!incremental_distance.nan?)
93
+ if units == "K"
94
+ incremental_distance = incremental_distance * 1.609344;
95
+ end
96
+ if units == "N"
97
+ incremental_distance = incremental_distance * 0.8684;
98
+ end
99
+ @cumulativeDistance = @cumulativeDistance + incremental_distance.to_f
100
+ end
101
+ end
102
+ end
103
+ end
104
+ compute_cumulative_pace(start_dttm)
105
+ @cumulativeDistance
106
+ else
107
+ 0
108
+ end
109
+ end
110
+
111
+ def compute_cumulative_pace(start_dttm)
112
+ if @cumulativeDistance > 0
113
+ secsDiff = @dttm.seconds_diff(start_dttm)
114
+ secsMile = ((secsDiff.to_f) / (@cumulativeDistance.to_f))
115
+ minsMile = (secsMile / 60)
116
+ wholeMins = minsMile.floor
117
+ secsBal = secsMile - (wholeMins * 60)
118
+ s1 = "#{secsDiff} #{secsMile} #{minsMile} #{wholeMins} #{secsBal} #{@cumulativeDistance} | "
119
+ s2 = sprintf("%d:%2.1f", minsMile, secsBal)
120
+ @cumulativePace = "#{s2}"
121
+ else
122
+ @cumulativePace = ""
123
+ end
124
+ end
125
+
126
+ def set_split(n, tkpt)
127
+ @split, @prevSplit = n, tkpt
128
+ end
129
+
130
+ def is_split()
131
+ (@split >= 1)
132
+ end
133
+
134
+ def is_first()
135
+ @first
136
+ end
137
+
138
+ def is_last()
139
+ @last
140
+ end
141
+
142
+ def split_info(dtTm)
143
+ if is_split
144
+ hhmmss = ''
145
+ if @prevSplit
146
+ return "#{@split} #{@dttm.hhmmss_diff(@prevSplit.dttm())}"
147
+ else
148
+ return "#{@split} #{@dttm.hhmmss_diff(dtTm)}"
149
+ end
150
+ else
151
+ ""
152
+ end
153
+ end
154
+
155
+ private
156
+
157
+ def deg2rad(degrees)
158
+ (((0 + degrees) * Math::PI) / 180)
159
+ end
160
+
161
+ def rad2deg(radians)
162
+ (((0 + radians) * 180) / Math::PI)
163
+ end
164
+
165
+ public
166
+
167
+ def as_glatlng(comments, tkpt_count, curr_idx, gpoint_count, start_dttm)
168
+ if comments
169
+ secs_diff = @dttm.seconds_diff(start_dttm)
170
+ fmt_time = @dttm.hhmmss_diff(start_dttm)
171
+ "\n points.push(new GLatLng(#{latitude_as_float},#{longitude_as_float})); " +
172
+ "// #{gpoint_count} (#{curr_idx + 1} of #{tkpt_count}) #{@dttm.to_s} #{secs_diff} #{fmt_time} #{@cumulativeDistance} #{split_info(start_dttm)} "
173
+ else
174
+ "\n points.push(new GLatLng(#{latitude_as_float},#{longitude_as_float})); "
175
+ end
176
+ end
177
+
178
+ def as_info_window_html(checkpoint, start_dttm)
179
+ s = "\"<table align='left'>"
180
+ if checkpoint
181
+ secs_diff = @dttm.seconds_diff(start_dttm)
182
+ fmt_time = @dttm.hhmmss_diff(start_dttm)
183
+
184
+ if checkpoint == 'Start'
185
+ s << "<tr><td colspan='2'><b>Start!</b></td></tr>"
186
+ elsif checkpoint == 'Finish'
187
+ s << "<tr><td colspan='2'><b>Finish!</b></td></tr>"
188
+ else
189
+ s << "<tr><td colspan='2'><b>Checkpoint #{checkpoint}</b></td></tr>"
190
+ end
191
+ s << "<tr><td>Distance: </td><td>#{@cumulativeDistance}</td></tr>"
192
+ s << "<tr><td>Time of Day: </td><td>#{@dttm.to_s} </td></tr>"
193
+ s << "<tr><td>Elapsed Time: </td><td>#{fmt_time} </td></tr>"
194
+ s << "<tr><td>Average Pace: </td><td>#{@cumulativePace} </td></tr>"
195
+ s << "<tr><td>Lat/Lng: </td><td>#{latitude_as_float} , #{longitude_as_float} </td></tr>"
196
+ s << "<tr><td>Altitude: </td><td>#{altitude_as_float}m </td></tr>"
197
+ s
198
+ end
199
+ s << "</table>\""
200
+ s
201
+ end
202
+
203
+ end
204
+
205
+ end