optimus-ep 0.6.0 → 0.6.5
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.
- data/Manifest.txt +1 -0
- data/bin/eprime2tabfile +6 -6
- data/bin/extract_timings +2 -4
- data/lib/eprime_data.rb +41 -24
- data/lib/eprime_reader.rb +8 -18
- data/lib/eprimetab_parser.rb +10 -0
- data/lib/excel_parser.rb +5 -0
- data/lib/log_file_parser.rb +5 -0
- data/lib/raw_tab_parser.rb +27 -0
- data/lib/runners/generic_runner.rb +6 -15
- data/lib/tabfile_parser.rb +1 -3
- data/lib/transformers/column_calculator.rb +53 -35
- data/lib/transformers/multipasser.rb +25 -22
- data/lib/transformers/row_filter.rb +21 -7
- data/lib/version.rb +1 -1
- data/spec/eprime_data_spec.rb +4 -0
- data/spec/eprime_reader_spec.rb +15 -3
- data/spec/runners/generic_runner_spec.rb +1 -1
- data/spec/spec_helper.rb +7 -0
- data/spec/transformers/column_calculator_spec.rb +38 -7
- data/spec/transformers/multipasser_spec.rb +1 -0
- data/spec/transformers/row_filter_spec.rb +1 -0
- metadata +15 -3
data/Manifest.txt
CHANGED
data/bin/eprime2tabfile
CHANGED
@@ -84,7 +84,7 @@ module Eprime
|
|
84
84
|
opts.on('-o', '--outfile=OUTFILE', String,
|
85
85
|
'The name of the file to create. If this',
|
86
86
|
'isn\'t specified, print to the standard',
|
87
|
-
"output"
|
87
|
+
"output."
|
88
88
|
) { |val|
|
89
89
|
option_hash[:outfile] = val
|
90
90
|
}
|
@@ -92,14 +92,14 @@ module Eprime
|
|
92
92
|
|
93
93
|
opts.on('-c', '--columns=COLUMN_FILE', String,
|
94
94
|
'A tab-separated file containing the columns',
|
95
|
-
"in the order you want your output"
|
95
|
+
"in the order you want your output."
|
96
96
|
) { |val|
|
97
97
|
option_hash[:column_file] = val
|
98
98
|
}
|
99
99
|
opts.separator ""
|
100
100
|
|
101
101
|
opts.on('--filter-columns', TrueClass,
|
102
|
-
'Write out only the columns in COLUMN_FILE',
|
102
|
+
'Write out only the columns in COLUMN_FILE.',
|
103
103
|
'Requires the use of --columns'
|
104
104
|
) {
|
105
105
|
option_hash[:filter_columns] = true
|
@@ -109,21 +109,21 @@ module Eprime
|
|
109
109
|
|
110
110
|
opts.on('-a', '--add-filename-line', TrueClass,
|
111
111
|
'Print the filename as the first line of',
|
112
|
-
"your output, just like E-DataAid"
|
112
|
+
"your output, just like E-DataAid."
|
113
113
|
) {
|
114
114
|
option_hash[:add_filename_line] = true
|
115
115
|
}
|
116
116
|
opts.separator ""
|
117
117
|
|
118
118
|
opts.on('-f', '--force', TrueClass,
|
119
|
-
"Continue processing even there are errors"
|
119
|
+
"Continue processing even there are errors."
|
120
120
|
) {
|
121
121
|
option_hash[:force] = true
|
122
122
|
}
|
123
123
|
opts.separator ""
|
124
124
|
|
125
125
|
opts.on('-h', '--help', TrueClass,
|
126
|
-
'Print this message'
|
126
|
+
'Print this message.'
|
127
127
|
) {
|
128
128
|
option_hash[:help] = true
|
129
129
|
}
|
data/bin/extract_timings
CHANGED
@@ -12,10 +12,8 @@ require 'optparse'
|
|
12
12
|
gem 'optimus-ep'
|
13
13
|
require 'runners/generic_runner'
|
14
14
|
|
15
|
-
script_name = File.basename(__FILE__)
|
16
|
-
|
17
15
|
begin
|
18
|
-
txr = Eprime::Runners::GenericRunner.new(Eprime::Transformers::TimingExtractor,
|
16
|
+
txr = Eprime::Runners::GenericRunner.new(Eprime::Transformers::TimingExtractor, ARGV)
|
19
17
|
txr.process!
|
20
18
|
rescue ArgumentError => e
|
21
19
|
STDERR.puts e.message
|
@@ -23,4 +21,4 @@ rescue ArgumentError => e
|
|
23
21
|
rescue Exception => e
|
24
22
|
STDERR.puts e.message
|
25
23
|
exit 2
|
26
|
-
end
|
24
|
+
end
|
data/lib/eprime_data.rb
CHANGED
@@ -52,9 +52,7 @@ module Eprime
|
|
52
52
|
@column_hash = {}
|
53
53
|
@columns_set_in_initialize = false
|
54
54
|
if (columns && columns.length > 0)
|
55
|
-
columns
|
56
|
-
idx = self.find_or_add_column_index(col)
|
57
|
-
end
|
55
|
+
add_columns!(columns)
|
58
56
|
@columns_set_in_initialize = true
|
59
57
|
end
|
60
58
|
end
|
@@ -62,19 +60,31 @@ module Eprime
|
|
62
60
|
# Returns a new Eprime::Data object containing the data from this
|
63
61
|
# and all other data sets
|
64
62
|
def merge(*datasets)
|
65
|
-
|
63
|
+
cols = [self, *datasets].map { |d| d.columns }.flatten.uniq
|
64
|
+
d = Eprime::Data.new(cols)
|
66
65
|
return d.merge!(self, *datasets)
|
67
66
|
end
|
68
67
|
|
69
68
|
# Combine more Eprime::Data objects into this one, in-place
|
70
69
|
def merge!(*datasets)
|
71
70
|
datasets.each do |source|
|
72
|
-
source.
|
73
|
-
|
74
|
-
|
75
|
-
|
71
|
+
add_columns!(source.columns)
|
72
|
+
if source.columns == self.columns
|
73
|
+
# The fast option
|
74
|
+
source.each do |row|
|
75
|
+
r = self.add_row
|
76
|
+
r.sort_value = row.sort_value
|
77
|
+
r.values = row.values
|
78
|
+
end
|
79
|
+
else
|
80
|
+
# The general case option
|
81
|
+
source.each do |row|
|
82
|
+
r = self.add_row
|
83
|
+
row.columns.each do |col|
|
84
|
+
r[col] = row[col]
|
85
|
+
end
|
86
|
+
r.sort_value = row.sort_value
|
76
87
|
end
|
77
|
-
r.sort_value = row.sort_value
|
78
88
|
end
|
79
89
|
end
|
80
90
|
return self
|
@@ -99,18 +109,18 @@ module Eprime
|
|
99
109
|
@rows.send method, *args, &block
|
100
110
|
end
|
101
111
|
|
102
|
-
def add_row
|
112
|
+
def add_row()
|
103
113
|
row = Row.new(self)
|
104
114
|
@rows << row
|
105
115
|
return row
|
106
116
|
end
|
107
117
|
|
118
|
+
#def add_row_values(values, sort_value = 1)
|
119
|
+
# r = Row.new(self, values, sort_value)
|
120
|
+
#end
|
121
|
+
|
108
122
|
def find_column_index(col_id)
|
109
|
-
|
110
|
-
return (col_id < @columns.size) ? col_id : nil
|
111
|
-
end
|
112
|
-
# Short-circuit this
|
113
|
-
@column_hash[col_id] if @column_hash[col_id]
|
123
|
+
@column_hash[col_id]
|
114
124
|
end
|
115
125
|
|
116
126
|
def find_or_add_column_index(col_id)
|
@@ -120,23 +130,31 @@ module Eprime
|
|
120
130
|
# indexes.
|
121
131
|
return index_id if index_id or col_id.is_a?(Fixnum)
|
122
132
|
# In this case, we're adding a column...
|
133
|
+
index = @columns.size
|
123
134
|
@columns << col_id
|
124
|
-
index = @columns.length - 1
|
125
135
|
@column_hash[col_id] = index
|
136
|
+
@column_hash[index] = index
|
126
137
|
if @columns_set_in_initialize and not @options[:ignore_warnings]
|
127
138
|
raise ColumnAddedWarning.new("Error: Added column #{col_id} after specifying columns at init", index)
|
128
139
|
end
|
129
140
|
return index
|
130
141
|
end
|
131
142
|
|
143
|
+
private
|
144
|
+
def add_columns!(col_arr)
|
145
|
+
col_arr.each do |c|
|
146
|
+
find_or_add_column_index(c)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
132
150
|
class Row
|
133
151
|
attr_accessor :sort_value
|
134
152
|
|
135
|
-
def initialize(parent)
|
136
|
-
@data = []
|
153
|
+
def initialize(parent, data = [], sort_value = 1)
|
137
154
|
@parent = parent
|
155
|
+
@data = data
|
138
156
|
# Ensure it's comparable
|
139
|
-
@sort_value =
|
157
|
+
@sort_value = sort_value
|
140
158
|
end
|
141
159
|
|
142
160
|
def [](index)
|
@@ -164,13 +182,12 @@ module Eprime
|
|
164
182
|
end
|
165
183
|
|
166
184
|
def values
|
167
|
-
|
168
|
-
@parent.columns.each_index do |i|
|
169
|
-
vals[i] = @data[i]
|
170
|
-
end
|
171
|
-
return vals
|
185
|
+
return @data
|
172
186
|
end
|
173
187
|
|
188
|
+
def values=(ar)
|
189
|
+
@data = ar
|
190
|
+
end
|
174
191
|
end
|
175
192
|
end
|
176
193
|
|
data/lib/eprime_reader.rb
CHANGED
@@ -8,6 +8,7 @@
|
|
8
8
|
require 'log_file_parser'
|
9
9
|
require 'excel_parser'
|
10
10
|
require 'eprimetab_parser'
|
11
|
+
require 'raw_tab_parser'
|
11
12
|
|
12
13
|
module Eprime
|
13
14
|
|
@@ -19,7 +20,8 @@ module Eprime
|
|
19
20
|
attr_reader :type, :parser, :input
|
20
21
|
attr_accessor :options
|
21
22
|
|
22
|
-
|
23
|
+
PARSERS = [LogfileParser, ExcelParser, EprimetabParser, RawTabParser]
|
24
|
+
|
23
25
|
def initialize(input = nil, options = {})
|
24
26
|
@options = options || {}
|
25
27
|
set_input(input) unless input.nil?
|
@@ -36,7 +38,6 @@ module Eprime
|
|
36
38
|
|
37
39
|
def options=(options)
|
38
40
|
@options = options || {}
|
39
|
-
set_parser!
|
40
41
|
end
|
41
42
|
|
42
43
|
private
|
@@ -72,17 +73,12 @@ module Eprime
|
|
72
73
|
if @type.nil?
|
73
74
|
raise UnknownTypeError.new("Can't determine the type of #{file.path}")
|
74
75
|
end
|
75
|
-
set_parser!
|
76
|
-
end
|
77
|
-
|
78
|
-
def set_parser!
|
79
76
|
@eprime_data = nil
|
80
|
-
|
81
|
-
@parser = TYPES[@type].new(@file, @options)
|
77
|
+
@parser = @type.new(@file, @options)
|
82
78
|
end
|
83
79
|
|
84
80
|
# Determines the type of an eprime file, based on its first two lines.
|
85
|
-
# Returns one of
|
81
|
+
# Returns one of the elements of PARSERS or nil
|
86
82
|
def determine_file_type(first_lines)
|
87
83
|
# Log files start with *** Header Start ***
|
88
84
|
#
|
@@ -91,15 +87,9 @@ module Eprime
|
|
91
87
|
#
|
92
88
|
# eprime CSV files will have at least three tab-delimited elements on the first line
|
93
89
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
return :excel
|
98
|
-
elsif (first_lines[0].split("\t").size >= 3 and first_lines[1].split("\t").size >= 3)
|
99
|
-
return :eprime
|
100
|
-
end
|
101
|
-
# Don't know? Return nil.
|
102
|
-
return nil
|
90
|
+
return PARSERS.detect { |parser_class|
|
91
|
+
parser_class.can_parse?(first_lines)
|
92
|
+
}
|
103
93
|
end
|
104
94
|
end
|
105
95
|
end
|
data/lib/eprimetab_parser.rb
CHANGED
@@ -16,6 +16,16 @@ module Eprime
|
|
16
16
|
options = options.merge(:skip_lines => 3)
|
17
17
|
super(file, options)
|
18
18
|
end
|
19
|
+
|
20
|
+
def self.can_parse?(lines)
|
21
|
+
divided = lines.map { |l| l.strip.split("\t") }
|
22
|
+
return (
|
23
|
+
divided[0].size >= 3 and
|
24
|
+
divided[0].size == divided[1].size and
|
25
|
+
divided[0][0] == 'STRING' and
|
26
|
+
divided[1][0] == 'EXPNAME'
|
27
|
+
)
|
28
|
+
end
|
19
29
|
end
|
20
30
|
end
|
21
31
|
end
|
data/lib/excel_parser.rb
CHANGED
data/lib/log_file_parser.rb
CHANGED
@@ -11,6 +11,7 @@ module Eprime
|
|
11
11
|
# Reads and parses E-Prime log files (the ones that start with
|
12
12
|
# *** Header Start ***) and transforms them into an Eprime::Data structure
|
13
13
|
|
14
|
+
|
14
15
|
class LogfileParser
|
15
16
|
# Handles parsing eprime log files, which are essentially a blow-by-blow
|
16
17
|
# log of everything that happened during an eprime run.
|
@@ -93,6 +94,10 @@ module Eprime
|
|
93
94
|
@skip_columns[col_name] = true
|
94
95
|
end
|
95
96
|
|
97
|
+
def self.can_parse?(lines)
|
98
|
+
lines[0].include?('*** Header Start ***')
|
99
|
+
end
|
100
|
+
|
96
101
|
private
|
97
102
|
# iterate over each line, strip it, look for *** LogFrame Start *** and
|
98
103
|
# *** LogFrame End *** -- the content between those goes into a frame array.
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Part of the Optimus package for managing E-Prime data
|
2
|
+
#
|
3
|
+
# Copyright (C) 2008 Board of Regents of the University of Wisconsin System
|
4
|
+
#
|
5
|
+
# Written by Nathan Vack <njvack@wisc.edu>, at the Waisman Laborotory for Brain
|
6
|
+
# Imaging and Behavior, University of Wisconsin - Madison
|
7
|
+
|
8
|
+
# This almost completely delegates to TabfileParser
|
9
|
+
# It differs from EprimetabParser only in that it doesn't skip any lines.
|
10
|
+
|
11
|
+
require 'tabfile_parser'
|
12
|
+
|
13
|
+
module Eprime
|
14
|
+
class Reader
|
15
|
+
class RawTabParser < TabfileParser
|
16
|
+
def initialize(file, options = {})
|
17
|
+
options = options.merge(:skip_lines => 0)
|
18
|
+
super(file, options)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.can_parse?(lines)
|
22
|
+
ary = lines.map { |l| l.strip.split("\t") }
|
23
|
+
ary[0].size > 1 and ary.all? {|e| e.size == ary[0].size}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -5,19 +5,9 @@
|
|
5
5
|
# Written by Nathan Vack <njvack@wisc.edu>, at the Waisman Laborotory for Brain
|
6
6
|
# Imaging and Behavior, University of Wisconsin - Madison
|
7
7
|
|
8
|
-
# A runner to take eprime data files, chew them through a pesudo-templater,
|
9
|
-
# and produce delicious files for importing into other packages. They'll look
|
10
|
-
# like:
|
11
|
-
#
|
12
|
-
# presented onset offset
|
13
|
-
# stim1 5992 6493
|
14
|
-
# stim2 7294 7981
|
15
8
|
#
|
16
9
|
# This class should handle argument processing, file I/O, and such.
|
17
10
|
|
18
|
-
# TODO: Think up a clever way to make this handle arbitrary transformers
|
19
|
-
# Probably this is possible by passing the class of the transformer to
|
20
|
-
# this runner, and instance_eval()'ing the template file in its presence.
|
21
11
|
|
22
12
|
require 'eprime'
|
23
13
|
require 'eprime_reader'
|
@@ -32,9 +22,11 @@ module Eprime
|
|
32
22
|
include ::Eprime::Transformers
|
33
23
|
|
34
24
|
attr_accessor :out, :err
|
35
|
-
def initialize(extractor_class,
|
25
|
+
def initialize(extractor_class, *args)
|
36
26
|
@extractor_class = extractor_class
|
37
|
-
|
27
|
+
# caller() returns an array of 'filename:line' -- the last element
|
28
|
+
# should contain the name of the script that started this process
|
29
|
+
@script_name = File.basename(caller.last.split(':').first)
|
38
30
|
@out = STDOUT
|
39
31
|
@err = STDERR
|
40
32
|
@args = args
|
@@ -51,11 +43,10 @@ module Eprime
|
|
51
43
|
end
|
52
44
|
|
53
45
|
def read_data
|
54
|
-
data = Eprime::Data.new
|
46
|
+
data = Eprime::Data.new()
|
55
47
|
@options.input_files.each do |infile|
|
56
48
|
File.open(infile) do |f|
|
57
|
-
|
58
|
-
data.merge!(new_data)
|
49
|
+
data.merge!(Eprime::Reader.new(f).eprime_data)
|
59
50
|
end
|
60
51
|
end
|
61
52
|
@data = data
|
data/lib/tabfile_parser.rb
CHANGED
@@ -44,9 +44,7 @@ module Eprime
|
|
44
44
|
if col_data.size != expected_size
|
45
45
|
raise DamagedFileError.new("In #{@file.path}, line #{current_line} should have #{expected_size} columns but had #{col_data.size}.")
|
46
46
|
end
|
47
|
-
|
48
|
-
row[i] = col_data[i]
|
49
|
-
end
|
47
|
+
row.values = col_data
|
50
48
|
end
|
51
49
|
return data
|
52
50
|
end
|
@@ -32,6 +32,7 @@ module Eprime
|
|
32
32
|
@columns = []
|
33
33
|
@columns_intern = []
|
34
34
|
@column_indexes = {}
|
35
|
+
@computed = nil
|
35
36
|
@rows = []
|
36
37
|
COLUMN_TYPES.each do |type|
|
37
38
|
instance_variable_set("@#{type}", [])
|
@@ -42,12 +43,11 @@ module Eprime
|
|
42
43
|
|
43
44
|
# Makes this into a static Eprime::Data object
|
44
45
|
def to_eprime_data
|
45
|
-
|
46
|
+
computed
|
46
47
|
end
|
47
48
|
|
48
49
|
def data=(data)
|
49
50
|
@data = data
|
50
|
-
|
51
51
|
@data_cols = []
|
52
52
|
@data.columns.each do |col_name|
|
53
53
|
@data_cols << DataColumn.new(col_name, @data)
|
@@ -56,26 +56,28 @@ module Eprime
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def [](index)
|
59
|
-
|
60
|
-
return @rows[index]
|
59
|
+
computed[index]
|
61
60
|
end
|
62
61
|
|
63
62
|
def column_index(col_id)
|
64
|
-
if col_id.is_a? Fixnum
|
65
|
-
return (col_id >= 0 and col_id < @columns.size) ? col_id : nil
|
66
|
-
end
|
67
63
|
return @column_indexes[col_id]
|
68
64
|
end
|
65
|
+
|
66
|
+
alias :find_column_index :column_index
|
69
67
|
|
70
68
|
def column(col_id)
|
71
69
|
index = column_index(col_id)
|
72
70
|
raise IndexError.new("#{col_id} does not exist") if index.nil?
|
73
71
|
return @columns_intern[index]
|
74
72
|
end
|
75
|
-
|
73
|
+
|
76
74
|
def size
|
77
75
|
@data.size
|
78
76
|
end
|
77
|
+
|
78
|
+
def columns
|
79
|
+
@columns.dup
|
80
|
+
end
|
79
81
|
|
80
82
|
def computed_column(name, expression)
|
81
83
|
@computed_cols << ComputedColumn.new(name, Expression.new(expression))
|
@@ -95,18 +97,15 @@ module Eprime
|
|
95
97
|
def sort_expression=(expr)
|
96
98
|
# The name 'sorter' is utterly arbitrary and never used
|
97
99
|
@sorter = ComputedColumn.new('sorter', Expression.new(expr))
|
98
|
-
@computed =
|
100
|
+
@computed = nil
|
99
101
|
end
|
100
102
|
|
101
103
|
def sort_expression
|
102
104
|
@sorter.to_s
|
103
105
|
end
|
104
106
|
|
105
|
-
def each
|
106
|
-
|
107
|
-
yield self[row_index]
|
108
|
-
end
|
109
|
-
@rows
|
107
|
+
def each(&block)
|
108
|
+
computed.each(&block)
|
110
109
|
end
|
111
110
|
|
112
111
|
def self.compute(numeric_expression)
|
@@ -114,18 +113,23 @@ module Eprime
|
|
114
113
|
end
|
115
114
|
|
116
115
|
private
|
117
|
-
|
116
|
+
|
117
|
+
def computed
|
118
|
+
@computed || compute_data!
|
119
|
+
end
|
120
|
+
|
118
121
|
def add_column(column)
|
119
122
|
# Raise an error if the column already exists
|
120
123
|
if @column_indexes[column.name]
|
121
124
|
raise ComputationError.new("#{column.name} already exists!")
|
122
125
|
end
|
123
126
|
# Save the index
|
127
|
+
@column_indexes[@columns_intern.size] = @columns_intern.size
|
124
128
|
@column_indexes[column.name] = @columns_intern.size
|
125
129
|
@columns_intern << column
|
126
130
|
@columns << column.name
|
127
131
|
end
|
128
|
-
|
132
|
+
|
129
133
|
def set_columns!
|
130
134
|
@columns = []
|
131
135
|
@columns_intern = []
|
@@ -136,9 +140,9 @@ module Eprime
|
|
136
140
|
add_column(col)
|
137
141
|
end
|
138
142
|
end
|
139
|
-
@computed =
|
143
|
+
@computed = nil
|
140
144
|
end
|
141
|
-
|
145
|
+
|
142
146
|
# Creates the infix calculator -- called at class instantiation time
|
143
147
|
def self.make_calculator
|
144
148
|
@@calculator = ::Eprime::Calculator.new
|
@@ -150,9 +154,9 @@ module Eprime
|
|
150
154
|
# because copydown and counter columns depend on the values of previous
|
151
155
|
# rows.
|
152
156
|
def compute_data!
|
153
|
-
@
|
154
|
-
@data.each_index do |
|
155
|
-
row = Row.new(self, @data[
|
157
|
+
@computed = Eprime::Data.new(columns)
|
158
|
+
@data.each_index do |i|
|
159
|
+
row = Row.new(self, @data[i])
|
156
160
|
# Loop over each column type -- it's still (slighyly) important that
|
157
161
|
# we go over each column type specifically. When counter columns
|
158
162
|
# work better, we can rearchitect this a bit.
|
@@ -173,12 +177,13 @@ module Eprime
|
|
173
177
|
rescue ArgumentError
|
174
178
|
# If this fails, it's OK -- we just won't convert.
|
175
179
|
end
|
176
|
-
|
177
|
-
|
180
|
+
new_row = @computed.add_row
|
181
|
+
new_row.sort_value = sv
|
182
|
+
new_row.values = row.values
|
178
183
|
end
|
179
|
-
@computed
|
184
|
+
@computed
|
180
185
|
end
|
181
|
-
|
186
|
+
|
182
187
|
class Column
|
183
188
|
attr_accessor :name
|
184
189
|
|
@@ -233,6 +238,10 @@ module Eprime
|
|
233
238
|
if path.include?(@name)
|
234
239
|
raise ComputationError.new("#{compute_str} contains a loop with #{@name} -- can't compute")
|
235
240
|
end
|
241
|
+
|
242
|
+
# Allow defining the column computation as a lambda
|
243
|
+
return @expression.expr.call(row) if @expression.expr.is_a? Proc
|
244
|
+
|
236
245
|
|
237
246
|
column_names = @expression.columns
|
238
247
|
column_names.each do |col_name|
|
@@ -285,17 +294,23 @@ module Eprime
|
|
285
294
|
end
|
286
295
|
|
287
296
|
class Row
|
288
|
-
attr_reader :
|
297
|
+
attr_reader :values
|
289
298
|
attr_accessor :sort_value
|
290
299
|
|
291
300
|
def initialize(parent, rowdata)
|
292
301
|
@parent = parent
|
293
302
|
@rowdata = rowdata
|
294
|
-
@
|
295
|
-
# Add all the data columns to
|
303
|
+
@values = []
|
304
|
+
# Add all the data columns to @values
|
296
305
|
rowdata.columns.each do |dcol_name|
|
297
306
|
index = @parent.column_index(dcol_name)
|
298
|
-
|
307
|
+
rd = rowdata[dcol_name]
|
308
|
+
begin
|
309
|
+
ci = values[index]
|
310
|
+
rescue Exception => e
|
311
|
+
raise e
|
312
|
+
end
|
313
|
+
@values[index] = rowdata[dcol_name]
|
299
314
|
end
|
300
315
|
@sort_value = 1
|
301
316
|
end
|
@@ -304,7 +319,7 @@ module Eprime
|
|
304
319
|
if @parent.column_index(col_id).nil?
|
305
320
|
raise IndexError.new("#{col_id} does not exist")
|
306
321
|
end
|
307
|
-
return @
|
322
|
+
return @values[@parent.column_index(col_id)]
|
308
323
|
end
|
309
324
|
|
310
325
|
def find_column(column_name)
|
@@ -322,28 +337,31 @@ module Eprime
|
|
322
337
|
|
323
338
|
index = @parent.column_index(col_name)
|
324
339
|
col = @parent.column(col_name)
|
325
|
-
|
326
|
-
|
340
|
+
val = col.compute(self)
|
341
|
+
@values[index] = val
|
342
|
+
return val
|
327
343
|
end
|
328
344
|
|
329
345
|
def <=>(other_row)
|
330
346
|
@sort_value <=> other_row.sort_value
|
331
347
|
end
|
332
|
-
|
333
348
|
end
|
334
349
|
|
335
350
|
|
336
351
|
class Expression
|
337
352
|
attr_reader :columns
|
353
|
+
attr_reader :expr
|
338
354
|
|
339
355
|
COLUMN_FINDER = /\{([^}]*)\}/ # Finds strings like {foo} and {bar}
|
340
356
|
def initialize(expr_string)
|
341
357
|
@expr = expr_string
|
342
|
-
|
358
|
+
unless expr_string.is_a? Proc
|
359
|
+
@columns = find_columns(expr_string).freeze
|
360
|
+
end
|
343
361
|
end
|
344
362
|
|
345
363
|
def to_s
|
346
|
-
@expr.dup
|
364
|
+
@expr.to_s.dup
|
347
365
|
end
|
348
366
|
|
349
367
|
private
|
@@ -43,28 +43,20 @@ module Eprime
|
|
43
43
|
def initialize(data = nil)
|
44
44
|
@data = data
|
45
45
|
@passes = []
|
46
|
-
|
46
|
+
reset!
|
47
47
|
end
|
48
48
|
|
49
49
|
def columns
|
50
|
-
|
51
|
-
@all_data.columns.dup
|
50
|
+
computed.columns
|
52
51
|
end
|
53
52
|
|
54
53
|
def data=(data)
|
55
54
|
@data = data
|
56
|
-
|
57
|
-
end
|
58
|
-
|
59
|
-
def each
|
60
|
-
compute! unless @computed
|
61
|
-
@all_data.each do |row|
|
62
|
-
yield row
|
63
|
-
end
|
55
|
+
reset!
|
64
56
|
end
|
65
57
|
|
66
58
|
def add_pass(*args)
|
67
|
-
@computed =
|
59
|
+
@computed = nil
|
68
60
|
if args[0].instance_of? Pass
|
69
61
|
p = args[0]
|
70
62
|
else
|
@@ -73,33 +65,44 @@ module Eprime
|
|
73
65
|
@passes << p and return p
|
74
66
|
end
|
75
67
|
|
68
|
+
def each(&block)
|
69
|
+
computed.each(&block)
|
70
|
+
end
|
71
|
+
|
76
72
|
def [](index)
|
77
|
-
|
78
|
-
return @all_data[index]
|
73
|
+
computed[index]
|
79
74
|
end
|
80
75
|
|
81
76
|
private
|
82
|
-
def
|
83
|
-
@
|
77
|
+
def computed
|
78
|
+
return @computed if @computed
|
79
|
+
if @passes.empty?
|
80
|
+
@computed = @data
|
81
|
+
return @computed
|
82
|
+
end
|
83
|
+
@computed = Eprime::Data.new
|
84
84
|
# Just add a simple pass if we don't have any...
|
85
|
-
add_pass if @passes.empty?
|
86
85
|
@passes.each do |pass|
|
87
86
|
# We want to duplicate the data, add a sort expression to it, add
|
88
87
|
# computed columns, filter it, and then merge it into the complete
|
89
88
|
# dataset.
|
90
|
-
cur_data = @data.to_eprime_data
|
89
|
+
#cur_data = @data.to_eprime_data
|
91
90
|
comp_data = ColumnCalculator.new
|
92
|
-
comp_data.data =
|
91
|
+
comp_data.data = @data
|
93
92
|
comp_data.sort_expression = pass.sort_expression
|
94
93
|
pass.computed_columns.each do |col|
|
95
94
|
name, expr = *col
|
96
95
|
comp_data.computed_column(name, expr)
|
97
96
|
end
|
98
97
|
filtered = RowFilter.new(comp_data, pass.row_filter)
|
99
|
-
@
|
98
|
+
@computed.merge!(filtered)
|
100
99
|
end
|
101
|
-
@
|
102
|
-
@computed
|
100
|
+
@computed.sort!
|
101
|
+
return @computed
|
102
|
+
end
|
103
|
+
|
104
|
+
def reset!
|
105
|
+
@computed = nil
|
103
106
|
end
|
104
107
|
end
|
105
108
|
end
|
@@ -17,18 +17,32 @@ module Eprime
|
|
17
17
|
def initialize(data, filter)
|
18
18
|
@data = data
|
19
19
|
@filter = filter
|
20
|
+
@computed = nil
|
20
21
|
end
|
21
22
|
|
22
23
|
def to_eprime_data
|
23
|
-
|
24
|
+
computed
|
24
25
|
end
|
25
|
-
|
26
|
-
def
|
27
|
-
|
28
|
-
yield row if match?(row)
|
29
|
-
end
|
26
|
+
|
27
|
+
def method_missing(method, *args, &block)
|
28
|
+
computed.send method, *args, &block
|
30
29
|
end
|
31
|
-
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def computed
|
34
|
+
return @computed if @computed
|
35
|
+
@computed = Eprime::Data.new(@data.columns)
|
36
|
+
@data.find_all{ |row|
|
37
|
+
match?(row)
|
38
|
+
}.each { |row|
|
39
|
+
r = @computed.add_row
|
40
|
+
r.values = row.values
|
41
|
+
r.sort_value = row.sort_value
|
42
|
+
}
|
43
|
+
return @computed
|
44
|
+
end
|
45
|
+
|
32
46
|
def match?(row)
|
33
47
|
if @filter.is_a? Proc
|
34
48
|
return @filter.call(row)
|
data/lib/version.rb
CHANGED
data/spec/eprime_data_spec.rb
CHANGED
@@ -45,6 +45,10 @@ shared_examples_for "Eprime::Data with one row" do
|
|
45
45
|
@row[index] = "bitten"
|
46
46
|
@row[index].should == "bitten"
|
47
47
|
end
|
48
|
+
|
49
|
+
it "should not lose columns when duplicating" do
|
50
|
+
@data.columns.should == @data.dup.columns
|
51
|
+
end
|
48
52
|
|
49
53
|
it "should raise when setting out-of-bound column value" do
|
50
54
|
lambda {
|
data/spec/eprime_reader_spec.rb
CHANGED
@@ -40,7 +40,7 @@ describe Eprime::Reader do
|
|
40
40
|
|
41
41
|
it "should detect log files" do
|
42
42
|
@reader.input = (@file)
|
43
|
-
@reader.type.should ==
|
43
|
+
@reader.type.should == Eprime::Reader::LogfileParser
|
44
44
|
end
|
45
45
|
|
46
46
|
it "should return the Eprime::Reader::LogfileParser" do
|
@@ -72,7 +72,7 @@ describe Eprime::Reader do
|
|
72
72
|
|
73
73
|
it "should detect excel csv files" do
|
74
74
|
@reader.input = @file
|
75
|
-
@reader.type.should ==
|
75
|
+
@reader.type.should == Eprime::Reader::ExcelParser
|
76
76
|
end
|
77
77
|
|
78
78
|
it "should resutn the Eprime::Reader::ExcelParser" do
|
@@ -96,7 +96,7 @@ describe Eprime::Reader do
|
|
96
96
|
|
97
97
|
it "should detect eprime csv files" do
|
98
98
|
@reader.input = @file
|
99
|
-
@reader.type.should ==
|
99
|
+
@reader.type.should == Eprime::Reader::EprimetabParser
|
100
100
|
end
|
101
101
|
|
102
102
|
it "should return the Eprime::Reader::EprimetabParser" do
|
@@ -111,5 +111,17 @@ describe Eprime::Reader do
|
|
111
111
|
data.columns.sort.should == SORTED_COLUMNS
|
112
112
|
end
|
113
113
|
end
|
114
|
+
|
115
|
+
describe "with raw tsv files" do
|
116
|
+
before :each do
|
117
|
+
@file = File.open(RAW_TSV_FILE)
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
it "should detect tsv files" do
|
122
|
+
@reader.input = @file
|
123
|
+
@reader.type.should == Eprime::Reader::RawTabParser
|
124
|
+
end
|
125
|
+
end
|
114
126
|
|
115
127
|
end
|
@@ -19,7 +19,7 @@ include EprimeTestHelper
|
|
19
19
|
|
20
20
|
describe Eprime::Runners::GenericRunner do
|
21
21
|
before :each do
|
22
|
-
@txr = Eprime::Runners::GenericRunner.new
|
22
|
+
@txr = Eprime::Runners::GenericRunner.new(Eprime::Transformers::BasicTransformer)
|
23
23
|
end
|
24
24
|
|
25
25
|
it "should start with stdout in @out" do
|
data/spec/spec_helper.rb
CHANGED
@@ -12,6 +12,7 @@ module EprimeTestHelper
|
|
12
12
|
EXCEL_FILE = File.join(SAMPLE_DIR, 'excel_tsv.txt')
|
13
13
|
BAD_EXCEL_FILE = File.join(SAMPLE_DIR, 'bad_excel_tsv.txt')
|
14
14
|
EPRIME_FILE = File.join(SAMPLE_DIR, 'eprime_tsv.txt')
|
15
|
+
RAW_TSV_FILE = File.join(SAMPLE_DIR, 'raw_tsv.txt')
|
15
16
|
UNKNOWN_FILE = File.join(SAMPLE_DIR, 'unknown_type.txt')
|
16
17
|
UNREADABLE_FILE = File.join(SAMPLE_DIR, 'unreadable_file')
|
17
18
|
CORRUPT_LOG_FILE = File.join(SAMPLE_DIR, 'corrupt_log_file.txt')
|
@@ -103,4 +104,10 @@ module EprimeTestHelper
|
|
103
104
|
CONST_EXPRS[sym][1].call.to_s
|
104
105
|
end
|
105
106
|
|
107
|
+
def time
|
108
|
+
t1 = Time.now.to_f
|
109
|
+
yield
|
110
|
+
return Time.now.to_f - t1
|
111
|
+
end
|
112
|
+
|
106
113
|
end
|
@@ -9,6 +9,7 @@ require File.join(File.dirname(__FILE__),'../spec_helper')
|
|
9
9
|
require File.join(File.dirname(__FILE__), '../../lib/eprime')
|
10
10
|
|
11
11
|
require 'transformers/column_calculator'
|
12
|
+
require 'transformers/row_filter'
|
12
13
|
|
13
14
|
include EprimeTestHelper
|
14
15
|
|
@@ -19,8 +20,12 @@ shared_examples_for "Eprime::Transformers::ColumnCalculator with edata" do
|
|
19
20
|
@calc.size.should == @edata.size
|
20
21
|
end
|
21
22
|
|
23
|
+
it "should not be nil" do
|
24
|
+
@calc[0].should_not be_nil
|
25
|
+
end
|
26
|
+
|
22
27
|
it "should allow accessing rows" do
|
23
|
-
@calc[0].should
|
28
|
+
@calc[0].should be_a_kind_of(Eprime::Data::Row)
|
24
29
|
end
|
25
30
|
|
26
31
|
it "should return data" do
|
@@ -118,6 +123,17 @@ shared_examples_for "Eprime::Transformers::ColumnCalculator with edata" do
|
|
118
123
|
}.should raise_error(Eprime::Transformers::ColumnCalculator::ComputationError)
|
119
124
|
end
|
120
125
|
|
126
|
+
it "should work in a RowFilter" do
|
127
|
+
d = mock_edata
|
128
|
+
cc = Eprime::Transformers::ColumnCalculator.new
|
129
|
+
cc.data = d
|
130
|
+
cc.sort_expression = '1'
|
131
|
+
filtered = Eprime::Transformers::RowFilter.new(cc, lambda {|r| true})
|
132
|
+
filtered.each do |r|
|
133
|
+
r.should_not be_nil
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
121
137
|
end
|
122
138
|
|
123
139
|
describe Eprime::Transformers::ColumnCalculator do
|
@@ -139,31 +155,36 @@ describe Eprime::Transformers::ColumnCalculator do
|
|
139
155
|
|
140
156
|
it "should return data for data columns" do
|
141
157
|
@edata[0]['stim_time'].should_not be_nil
|
142
|
-
@calc[0]
|
158
|
+
@calc[0]['stim_time'].should == @edata[0]['stim_time']
|
143
159
|
end
|
144
160
|
|
145
161
|
it "should compute static columns on single rows" do
|
146
162
|
@calc.computed_column "always_1", "1"
|
147
|
-
@calc[0]
|
163
|
+
@calc[0]["always_1"].should == "1"
|
148
164
|
end
|
149
165
|
|
150
166
|
it "should compute on single rows with some math" do
|
151
167
|
@calc.computed_column "test", "(3+2)*4"
|
152
|
-
@calc[0]
|
168
|
+
@calc[0]["test"].should == ((3+2)*4).to_s
|
153
169
|
end
|
154
170
|
|
155
171
|
it "should allow adding two columns" do
|
156
172
|
@calc.computed_column "always_1", "1"
|
157
173
|
@calc.computed_column "test", "(3+2)*4"
|
158
|
-
@calc[0]
|
159
|
-
@calc[0]
|
174
|
+
@calc[0]["always_1"].should == "1"
|
175
|
+
@calc[0]["test"].should == ((3+2)*4).to_s
|
160
176
|
end
|
161
177
|
|
162
178
|
it "should raise when computing nonexistent column" do
|
163
179
|
lambda {
|
164
|
-
@calc[0]
|
180
|
+
@calc[0]['nonexistent']
|
165
181
|
}.should raise_error(IndexError)
|
166
182
|
end
|
183
|
+
|
184
|
+
it "should allow computing via lambda" do
|
185
|
+
@calc.computed_column "always_1", lambda {|r| "1"}
|
186
|
+
@calc[0]["always_1"].should == "1"
|
187
|
+
end
|
167
188
|
|
168
189
|
it "should compute constants via indexing" do
|
169
190
|
@calc.computed_column "always_1", "1"
|
@@ -198,6 +219,16 @@ describe Eprime::Transformers::ColumnCalculator do
|
|
198
219
|
end
|
199
220
|
end
|
200
221
|
|
222
|
+
it "should compute based on data columns by lambda" do
|
223
|
+
@calc.computed_column "stim_time_s", lambda { |row|
|
224
|
+
row['stim_time'].to_f/1000
|
225
|
+
}
|
226
|
+
@calc.columns.should include('stim_time_s')
|
227
|
+
@calc.each do |row|
|
228
|
+
row['stim_time_s'].should == row['stim_time'].to_f/1000
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
201
232
|
it "should allow math between two columns" do
|
202
233
|
@calc.computed_column "stim_from_run", "{stim_time}-{run_start}"
|
203
234
|
@calc.each do |row|
|
@@ -9,6 +9,7 @@ require File.join(File.dirname(__FILE__),'../spec_helper')
|
|
9
9
|
require File.join(File.dirname(__FILE__), '../../lib/eprime')
|
10
10
|
require 'transformers/multipasser'
|
11
11
|
|
12
|
+
include Eprime::Transformers
|
12
13
|
include EprimeTestHelper
|
13
14
|
|
14
15
|
describe Eprime::Transformers::Multipasser do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: optimus-ep
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nathan Vack
|
@@ -9,11 +9,12 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-09-02 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rparsec
|
17
|
+
type: :runtime
|
17
18
|
version_requirement:
|
18
19
|
version_requirements: !ruby/object:Gem::Requirement
|
19
20
|
requirements:
|
@@ -21,6 +22,16 @@ dependencies:
|
|
21
22
|
- !ruby/object:Gem::Version
|
22
23
|
version: "1.0"
|
23
24
|
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: hoe
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.7.0
|
34
|
+
version:
|
24
35
|
description: A collection of utilities for working with Eprime data files
|
25
36
|
email:
|
26
37
|
- njvack@freshforever.net
|
@@ -60,6 +71,7 @@ files:
|
|
60
71
|
- lib/eprimetab_parser.rb
|
61
72
|
- lib/excel_parser.rb
|
62
73
|
- lib/log_file_parser.rb
|
74
|
+
- lib/raw_tab_parser.rb
|
63
75
|
- lib/runners/generic_runner.rb
|
64
76
|
- lib/tabfile_parser.rb
|
65
77
|
- lib/tabfile_writer.rb
|
@@ -119,7 +131,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
119
131
|
requirements: []
|
120
132
|
|
121
133
|
rubyforge_project: optimus-ep
|
122
|
-
rubygems_version: 1.0
|
134
|
+
rubygems_version: 1.2.0
|
123
135
|
signing_key:
|
124
136
|
specification_version: 2
|
125
137
|
summary: A collection of utilities for working with Eprime data files
|