castle-log 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,87 @@
1
+ require 'open-uri'
2
+
3
+ module Castle
4
+ module Log
5
+
6
+ # Represents a single Castle data logger file.
7
+ class File
8
+
9
+ attr_reader :date_saved
10
+
11
+ # @return [String] Notes at the beginning of the file
12
+ attr_reader :notes
13
+
14
+ # @return [Array<Session>] Sessions contained in this file
15
+ attr_reader :sessions
16
+
17
+ def initialize uri
18
+ @sessions = []
19
+
20
+ open(uri, 'rb') do |file|
21
+
22
+ i = 0
23
+ in_comment = false
24
+ lines = file.readlines.map(&:strip).group_by do |line|
25
+ if line.start_with?('#') && !in_comment
26
+ in_comment = true
27
+ i += 1
28
+ elsif !line.start_with?('#') && in_comment
29
+ in_comment = false
30
+ end
31
+ i
32
+ end
33
+
34
+ # session 1 has some extra metadata, scrape that out first
35
+ notes = lines[1].select { |line| line.start_with?('# Note') }
36
+ meta = lines[1].select { |line| line.start_with?('# Graph Data') }
37
+
38
+ @sessions = extract_sessions(lines)
39
+
40
+ @notes = (notes.map { |line| /Note -(?<note>.*)$/.match(line)[:note].strip }).join("\n")
41
+
42
+ # TODO validate expected number of sessions from meta
43
+ # TODO extract date_saved
44
+
45
+ end
46
+
47
+ rescue
48
+ raise ArgumentError, 'File does not appear to be an Castle data log'
49
+ end
50
+
51
+ private
52
+
53
+ def extract_sessions line_hash
54
+
55
+ keys = line_hash.keys.sort!
56
+ keys.map do |session_index|
57
+ lines = line_hash[session_index]
58
+ start = lines.select { |line| line.start_with?('# Start Of Session') }
59
+ reset = lines.select { |line| line.start_with?('# Reset Source') }
60
+ tick = /Sample Time: (?<tick>\d+.\d+)/.match(start[0])[:tick].strip.to_f
61
+ source = /\(Cause of reset\): (?<source>.*)/.match(reset[0])[:source].strip
62
+
63
+ # XXX can we use CSV for this instead?
64
+
65
+ # strip out comment rows
66
+ raw_rows = lines.reject { |line| line.start_with?('#') }
67
+
68
+ # pop off header rows
69
+ headers = raw_rows.shift.split(',').map(&:strip)
70
+ booleans = raw_rows.shift.split(',').map(&:strip) # XXX unused, what is this?
71
+
72
+ # split the rows up and flip them into columns of data
73
+ columns = (raw_rows.map { |row| row.split(',').map(&:strip) }).transpose
74
+
75
+ # build the data hash as expected by the session
76
+ data = (columns.each_with_index.map { |column, idx| { headers[idx] => column } }).reduce({}, :merge)
77
+
78
+ Session.new(tick, source, data)
79
+
80
+ end
81
+
82
+ end
83
+
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,24 @@
1
+ module Castle
2
+ module Log
3
+
4
+ # Represents a single session from a Castle data logger file.
5
+ class Session
6
+
7
+ attr_reader :tick
8
+
9
+ attr_reader :source
10
+
11
+ def initialize tick, source, data
12
+ @tick = tick
13
+ @source = source
14
+ @data = data
15
+ end
16
+
17
+ def duration
18
+ tick * @data.values[0].length
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,5 @@
1
+ module Castle
2
+ module Log
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
data/lib/castle/log.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'castle/log/file'
2
+ require 'castle/log/session'
3
+ require 'castle/log/version'
data/spec/file_spec.rb ADDED
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ describe Castle::Log::File do
4
+
5
+ context 'data file long-flight.csv' do
6
+
7
+ subject { Castle::Log::File.new(data_file('long-flight.csv')) }
8
+
9
+ it { should have(1).sessions }
10
+
11
+ end
12
+
13
+ context 'data file multi-line-notes.csv' do
14
+
15
+ subject { Castle::Log::File.new(data_file('multi-line-notes.csv')) }
16
+
17
+ it { should have(1).sessions }
18
+
19
+ it 'should have a multi-line note field' do
20
+ subject.notes.split("\n").should have(3).lines
21
+ end
22
+
23
+ end
24
+
25
+ context 'data file multi-session-1.csv' do
26
+
27
+ subject { Castle::Log::File.new(data_file('multi-session-1.csv')) }
28
+
29
+ it { should have(5).sessions }
30
+
31
+ its(:notes) { should eql('Agri-duck 14 sep 12') }
32
+
33
+ end
34
+
35
+ context 'data file multi-session-2.csv' do
36
+
37
+ subject { Castle::Log::File.new(data_file('multi-session-2.csv')) }
38
+
39
+ it { should have(7).sessions }
40
+
41
+ end
42
+
43
+ context 'data file sample-1.csv' do
44
+
45
+ subject { Castle::Log::File.new(data_file('sample-1.csv')) }
46
+
47
+ it { should have(1).sessions }
48
+
49
+ end
50
+
51
+ it 'should raise on bad input' do
52
+ expect { Castle::Log::File.new(__FILE__) }.to raise_error
53
+ end
54
+
55
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe Castle::Log::Session do
4
+
5
+ context 'data file long-flight.csv' do
6
+
7
+ let(:file) { Castle::Log::File.new(data_file('long-flight.csv')) }
8
+
9
+ subject { file }
10
+
11
+ it { should have(1).sessions }
12
+
13
+ context 'session 1' do
14
+
15
+ subject { file.sessions[0] }
16
+
17
+ its(:duration) { should be_within(0.1).of(569.2) }
18
+
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,19 @@
1
+ require 'pathname'
2
+ require 'castle/log'
3
+
4
+ RSpec.configure do |config|
5
+ config.treat_symbols_as_metadata_keys_with_true_values = true
6
+ config.run_all_when_everything_filtered = true
7
+ config.filter_run :focus
8
+
9
+ # Run specs in random order to surface order dependencies. If you find an
10
+ # order dependency and want to debug it, you can fix the order by providing
11
+ # the seed, which is printed after each run.
12
+ # --seed 1234
13
+ config.order = 'random'
14
+ end
15
+
16
+ def data_file(name)
17
+ path = Pathname.new("#{File.dirname(__FILE__)}/../data/#{name}")
18
+ path.realpath
19
+ end
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: castle-log
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Nick Veys
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-06-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: ci_reporter
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 1.8.4
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 1.8.4
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '0.8'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '0.8'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '2.12'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '2.12'
69
+ description: Read and interpret Castle Creations data log files.
70
+ email:
71
+ - nick@codelever.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .gitignore
77
+ - Gemfile
78
+ - LICENSE.txt
79
+ - README.md
80
+ - Rakefile
81
+ - castle-log.gemspec
82
+ - data/long-flight.csv
83
+ - data/multi-line-notes.csv
84
+ - data/multi-session-1.csv
85
+ - data/multi-session-2.csv
86
+ - data/sample-1.csv
87
+ - lib/castle/log.rb
88
+ - lib/castle/log/file.rb
89
+ - lib/castle/log/session.rb
90
+ - lib/castle/log/version.rb
91
+ - spec/file_spec.rb
92
+ - spec/session_spec.rb
93
+ - spec/spec_helper.rb
94
+ homepage: ''
95
+ licenses:
96
+ - MIT
97
+ metadata: {}
98
+ post_install_message:
99
+ rdoc_options: []
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ! '>='
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ! '>='
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ requirements: []
113
+ rubyforge_project:
114
+ rubygems_version: 2.0.3
115
+ signing_key:
116
+ specification_version: 4
117
+ summary: Castle Creations data log file reader
118
+ test_files:
119
+ - spec/file_spec.rb
120
+ - spec/session_spec.rb
121
+ - spec/spec_helper.rb
122
+ has_rdoc: