optimus-ep 0.6.0 → 0.6.5

Sign up to get free protection for your applications and to get access to all the features.
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