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 CHANGED
@@ -13,6 +13,7 @@ lib/eprime_reader.rb
13
13
  lib/eprimetab_parser.rb
14
14
  lib/excel_parser.rb
15
15
  lib/log_file_parser.rb
16
+ lib/raw_tab_parser.rb
16
17
  lib/runners/generic_runner.rb
17
18
  lib/tabfile_parser.rb
18
19
  lib/tabfile_writer.rb
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, script_name, ARGV)
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.each do |col|
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
- d = Eprime::Data.new
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.each do |row|
73
- r = self.add_row
74
- row.columns.each do |col|
75
- r[col] = row[col]
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
- if col_id.is_a? Fixnum
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 = 1
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
- vals = []
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
- TYPES = {:log => LogfileParser, :excel => ExcelParser, :eprime => EprimetabParser}
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
- return unless @type && TYPES[@type]
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 [:log, :eprime_csv, :excel_csv, nil]
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
- if first_lines[0].index("*** Header Start ***")
95
- return :log
96
- elsif (first_lines[0]["\t"].nil? and first_lines[1].split("\t").size >= 3)
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
@@ -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
@@ -16,6 +16,11 @@ module Eprime
16
16
  options = options.merge(:skip_lines => 1)
17
17
  super(file, options)
18
18
  end
19
+
20
+ def self.can_parse?(lines)
21
+ ary = lines.map {|l| l.strip.split("\t")}
22
+ ary[0].size == 1 and ary[1].size >= 3
23
+ end
19
24
  end
20
25
  end
21
26
  end
@@ -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, script_name, *args)
25
+ def initialize(extractor_class, *args)
36
26
  @extractor_class = extractor_class
37
- @script_name = script_name
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
- new_data = Eprime::Reader.new(f).eprime_data
58
- data.merge!(new_data)
49
+ data.merge!(Eprime::Reader.new(f).eprime_data)
59
50
  end
60
51
  end
61
52
  @data = data
@@ -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
- col_data.each_index do |i|
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
- Eprime::Data.new().merge!(self)
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
- compute_data! unless @computed
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 = false
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
- @data.each_index do |row_index|
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 = false
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
- @rows = []
154
- @data.each_index do |row_index|
155
- row = Row.new(self, @data[row_index])
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
- row.sort_value = sv
177
- @rows << row
180
+ new_row = @computed.add_row
181
+ new_row.sort_value = sv
182
+ new_row.values = row.values
178
183
  end
179
- @computed = true
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 :computed_data
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
- @computed_data = []
295
- # Add all the data columns to computed_data
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
- @computed_data[index] = rowdata[dcol_name]
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 @computed_data[@parent.column_index(col_id)]
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
- @computed_data[index] = col.compute(self)
326
- return @computed_data[index]
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
- @columns = find_columns(expr_string).freeze
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
- @computed = false
46
+ reset!
47
47
  end
48
48
 
49
49
  def columns
50
- compute! unless @computed
51
- @all_data.columns.dup
50
+ computed.columns
52
51
  end
53
52
 
54
53
  def data=(data)
55
54
  @data = data
56
- @computed = false
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 = false
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
- compute! unless @computed
78
- return @all_data[index]
73
+ computed[index]
79
74
  end
80
75
 
81
76
  private
82
- def compute!
83
- @all_data = Eprime::Data.new
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 = cur_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
- @all_data.merge!(filtered)
98
+ @computed.merge!(filtered)
100
99
  end
101
- @all_data = Eprime::Data.new.merge(@all_data.sort)
102
- @computed = true
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
- Eprime::Data.new().merge!(self)
24
+ computed
24
25
  end
25
-
26
- def each
27
- @data.each do |row|
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
@@ -2,7 +2,7 @@ module Eprime
2
2
  module VERSION
3
3
  MAJOR = 0
4
4
  MINOR = 6
5
- TINY = 0
5
+ TINY = 5
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -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 {
@@ -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 == :log
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 == :excel
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 == :eprime
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 be_an_instance_of(Eprime::Transformers::ColumnCalculator::Row)
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].compute('stim_time').should == @edata[0]['stim_time']
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].compute("always_1").should == "1"
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].compute("test").should == ((3+2)*4).to_s
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].compute("always_1").should == "1"
159
- @calc[0].compute("test").should == ((3+2)*4).to_s
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].compute('nonexistent')
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
@@ -29,4 +29,5 @@ describe Eprime::Transformers::RowFilter do
29
29
  row['run_start'].should == '2400'
30
30
  end
31
31
  end
32
+
32
33
  end
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.0
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-07-14 00:00:00 -05:00
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.1
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