optimus-ep 0.5.6 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,11 @@
1
+ == 0.6.0 7/14/2008
2
+ * New features:
3
+ * Added extract_timings, a script to pull stimulus timing data from eprime
4
+ files.
5
+ * Added a GenericRunner -- a class that greatly eases end-to-end
6
+ transformation of eprime data. See extract_timings for an example of
7
+ its use.
8
+
1
9
  == 0.5.5 7/09/2008
2
10
  * New features:
3
11
  * Supports making multiple passes through data, to allow extracting multiple
data/Manifest.txt CHANGED
@@ -5,6 +5,7 @@ Manifest.txt
5
5
  README.txt
6
6
  Rakefile
7
7
  bin/eprime2tabfile
8
+ bin/extract_timings
8
9
  lib/calculator.rb
9
10
  lib/eprime.rb
10
11
  lib/eprime_data.rb
@@ -12,6 +13,7 @@ lib/eprime_reader.rb
12
13
  lib/eprimetab_parser.rb
13
14
  lib/excel_parser.rb
14
15
  lib/log_file_parser.rb
16
+ lib/runners/generic_runner.rb
15
17
  lib/tabfile_parser.rb
16
18
  lib/tabfile_writer.rb
17
19
  lib/transformers/basic_transformer.rb
@@ -27,6 +29,7 @@ spec/eprime_reader_spec.rb
27
29
  spec/eprimetab_parser_spec.rb
28
30
  spec/excel_parser_spec.rb
29
31
  spec/log_file_parser_spec.rb
32
+ spec/runners/generic_runner_spec.rb
30
33
  spec/samples/bad_excel_tsv.txt
31
34
  spec/samples/corrupt_log_file.txt
32
35
  spec/samples/eprime_tsv.txt
@@ -44,4 +47,5 @@ spec/transformers/basic_transformer_spec.rb
44
47
  spec/transformers/column_calculator_spec.rb
45
48
  spec/transformers/multipasser_spec.rb
46
49
  spec/transformers/row_filter_spec.rb
50
+ spec/transformers/timing_extractor_spec.rb
47
51
  spec/writers/stimtimes_writer_spec.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,14 +109,14 @@ 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
  }
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Part of the Optimus package for managing E-Prime data
4
+ #
5
+ # Copyright (C) 2008 Board of Regents of the University of Wisconsin System
6
+ #
7
+ # Written by Nathan Vack <njvack@wisc.edu>, at the Waisman Laborotory for Brain
8
+ # Imaging and Behavior, University of Wisconsin - Madison
9
+
10
+ require 'rubygems'
11
+ require 'optparse'
12
+ gem 'optimus-ep'
13
+ require 'runners/generic_runner'
14
+
15
+ script_name = File.basename(__FILE__)
16
+
17
+ begin
18
+ txr = Eprime::Runners::GenericRunner.new(Eprime::Transformers::TimingExtractor, script_name, ARGV)
19
+ txr.process!
20
+ rescue ArgumentError => e
21
+ STDERR.puts e.message
22
+ exit 1
23
+ rescue Exception => e
24
+ STDERR.puts e.message
25
+ exit 2
26
+ end
data/lib/eprime_data.rb CHANGED
@@ -80,6 +80,14 @@ module Eprime
80
80
  return self
81
81
  end
82
82
 
83
+ def sort!(&block)
84
+ @rows = @rows.sort(&block)
85
+ end
86
+
87
+ def sort_by!(&block)
88
+ @rows = @rows.sort_by(&block)
89
+ end
90
+
83
91
  def dup
84
92
  Eprime::Data.new().merge!(self)
85
93
  end
@@ -0,0 +1,157 @@
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
+ # 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
+ #
16
+ # This class should handle argument processing, file I/O, and such.
17
+
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
+
22
+ require 'eprime'
23
+ require 'eprime_reader'
24
+ require 'tabfile_writer'
25
+ require 'transformers/timing_extractor'
26
+ require 'optparse'
27
+ require 'ostruct'
28
+
29
+ module Eprime
30
+ module Runners
31
+ class GenericRunner
32
+ include ::Eprime::Transformers
33
+
34
+ attr_accessor :out, :err
35
+ def initialize(extractor_class, script_name, *args)
36
+ @extractor_class = extractor_class
37
+ @script_name = script_name
38
+ @out = STDOUT
39
+ @err = STDERR
40
+ @args = args
41
+ @data = nil
42
+ @timing_extractor = nil
43
+ end
44
+
45
+ def process!
46
+ process_arguments(*@args)
47
+ validate
48
+ read_data
49
+ extract_timings
50
+ write_timings
51
+ end
52
+
53
+ def read_data
54
+ data = Eprime::Data.new
55
+ @options.input_files.each do |infile|
56
+ File.open(infile) do |f|
57
+ new_data = Eprime::Reader.new(f).eprime_data
58
+ data.merge!(new_data)
59
+ end
60
+ end
61
+ @data = data
62
+ end
63
+
64
+ def extract_timings
65
+ @timing_extractor = @extractor_class.new(@data)
66
+ template_code = ''
67
+ File.open(@options.template_file) { |f|
68
+ template_code = f.read
69
+ }
70
+ @timing_extractor.instance_eval(template_code)
71
+ end
72
+
73
+ def write_timings
74
+ if @options.outfile
75
+ @out = File.open(@options.outfile, 'w')
76
+ end
77
+ writer = TabfileWriter.new(
78
+ @timing_extractor.extracted_data, @out,
79
+ {:column_labels => @options.column_labels})
80
+ begin
81
+ writer.write
82
+ rescue Errno::EPIPE => e
83
+ # This is OK
84
+ ensure
85
+ if @options.outfile
86
+ @out.close
87
+ end
88
+ end
89
+ end
90
+
91
+ def validate
92
+ if @options.help || @args.flatten.size == 0
93
+ show_help! and raise Exception.new()
94
+ end
95
+ if @options.input_files.empty?
96
+ raise ArgumentError.new("no input files given\n#{usage}")
97
+ end
98
+ if !@options.template_file
99
+ raise ArgumentError.new("no template file given\n#{usage}")
100
+ end
101
+ if !File.readable?(@options.template_file)
102
+ raise ArgumentError.new("can't read #{@options.template_file}\n#{usage}")
103
+ end
104
+ return true
105
+ end
106
+
107
+ def show_help!
108
+ @err.puts @op.to_s
109
+ end
110
+
111
+ def usage
112
+ "#{@op.banner.to_s} \n#{@script_name} --help for help"
113
+ end
114
+
115
+ private
116
+ def process_arguments(*args)
117
+ @options = OpenStruct.new(
118
+ :help => false,
119
+ :outfile => nil,
120
+ :column_labels => true,
121
+ :template_file => nil,
122
+ :input_files => []
123
+ )
124
+
125
+ op = OptionParser.new() do |op|
126
+ op.banner = "Usage: extract_timings --template TEMPLATE_FILE [OPTIONS] INPUT_FILES"
127
+ op.separator ''
128
+
129
+ op.on('-t', '--template=TEMPLATE_FILE', String,
130
+ 'A template containing commands describing',
131
+ 'how to process these files'
132
+ ) { |t| @options.template_file = t }
133
+
134
+ op.separator ''
135
+ op.on('--[no-]column-labels',
136
+ 'Print column lablels in the first row.',
137
+ 'If not specified, do print labels.'
138
+ ) { |l| @options.column_labels = l }
139
+
140
+ op.separator ''
141
+ op.on('-o', '--outfile=OUTFILE',
142
+ "The name of the file to save to. If not",
143
+ "given, print to standard output."
144
+ ) { |o| @options.outfile = o }
145
+
146
+ op.separator ''
147
+ op.on_tail('-h', '--help',
148
+ 'Print this message.'
149
+ ) { |h| @options.help = h }
150
+ end
151
+ @options.input_files = op.parse(*args) || []
152
+ @op = op
153
+ end
154
+
155
+ end
156
+ end
157
+ end
@@ -20,10 +20,17 @@ module Eprime
20
20
  # :write_top_line => true, if you want to include the filename
21
21
  # (if it's a file output stream) as the first line output
22
22
  def initialize(eprime_data, outstream, options = {})
23
+ standard_options = {
24
+ :write_top_line => false,
25
+ :columns => nil,
26
+ :column_labels => true
27
+ }
28
+ good_opts = standard_options.merge(options)
23
29
  @eprime = eprime_data
24
30
  @outstream = outstream
25
- @write_top_line = options[:write_top_line]
26
- @columns = options[:columns] || @eprime.columns
31
+ @write_top_line = good_opts[:write_top_line]
32
+ @columns = good_opts[:columns] || @eprime.columns
33
+ @column_labels = good_opts[:column_labels]
27
34
  end
28
35
 
29
36
  # Write to the output stream.
@@ -33,7 +40,9 @@ module Eprime
33
40
  name = @outstream.respond_to?(:path) ? File.expand_path(@outstream.path.to_s) : ''
34
41
  tsv << [name]
35
42
  end
36
- tsv << @columns
43
+ if @column_labels
44
+ tsv << @columns
45
+ end
37
46
  @eprime.each do |row|
38
47
  vals = @columns.map { |col_name| row[col_name] }
39
48
  tsv << vals
@@ -58,6 +58,8 @@ module Eprime
58
58
  @row_filter = filter
59
59
  end
60
60
 
61
+ alias :row_filter :row_filter=
62
+
61
63
  def add_pass(*args)
62
64
  reset!
63
65
  p = Multipasser::Pass.new(*args)
@@ -16,14 +16,57 @@
16
16
  # In an experiment, this will take, as an argument, a template written in ruby
17
17
  # that will be eval'd in the context of this instance -- that will contain
18
18
  # the guts of the logic to extract stimuli.
19
+ require 'transformers/basic_transformer'
19
20
 
20
21
  module Eprime
21
- class TimingExtractor
22
- def initialize(argv)
22
+ module Transformers
23
+ class TimingExtractor < BasicTransformer
24
+ def initialize(data)
25
+ super(data)
26
+ @stim_schemas = []
27
+ @extracted_data = nil
28
+ end
23
29
 
24
- end
25
-
26
- def extract
30
+ def extract_stimulus(
31
+ name_column,
32
+ onset_column,
33
+ offset_column,
34
+ row_filter = (lambda { |r| true })
35
+ )
36
+ @stim_schemas << {
37
+ 'name_column' => name_column,
38
+ 'onset_column' => onset_column,
39
+ 'offset_column' => offset_column,
40
+ 'row_filter' => row_filter
41
+ }
42
+ @extracted_data = nil
43
+ end
44
+
45
+ def extracted_data
46
+ extract!
47
+ return @extracted_data
48
+ end
49
+
50
+ private
51
+ def extract_reset!
52
+ @extracted_data = nil
53
+ end
54
+
55
+ def extract!
56
+ return if @extracted_data
57
+ @extracted_data = Eprime::Data.new
58
+ @stim_schemas.each do |ss|
59
+ matches = processed.find_all(&ss['row_filter'])
60
+ matches.each do |row|
61
+ nr = @extracted_data.add_row
62
+ nr['presented'] = row[ss['name_column']]
63
+ nr['onset'] = row[ss['onset_column']]
64
+ nr['offset'] = row[ss['offset_column']]
65
+ nr.sort_value = nr['onset'].to_f
66
+ end
67
+ end
68
+ @extracted_data.sort!
69
+ end
27
70
  end
28
71
  end
29
72
  end
data/lib/version.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  module Eprime
2
2
  module VERSION
3
3
  MAJOR = 0
4
- MINOR = 5
5
- TINY = 6
4
+ MINOR = 6
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -0,0 +1,32 @@
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
+ # Part of the Optimus package for managing E-Prime data
9
+ #
10
+ # Copyright (C) 2008 Board of Regents of the University of Wisconsin System
11
+ #
12
+ # Written by Nathan Vack <njvack@wisc.edu>, at the Waisman Laborotory for Brain
13
+ # Imaging and Behavior, University of Wisconsin - Madison
14
+
15
+ require File.join(File.dirname(__FILE__),'../spec_helper')
16
+ require File.join(File.dirname(__FILE__), '../../lib/eprime')
17
+ require 'runners/generic_runner'
18
+ include EprimeTestHelper
19
+
20
+ describe Eprime::Runners::GenericRunner do
21
+ before :each do
22
+ @txr = Eprime::Runners::GenericRunner.new
23
+ end
24
+
25
+ it "should start with stdout in @out" do
26
+ @txr.out.should == STDOUT
27
+ end
28
+
29
+ it "should start with stderr in @err" do
30
+ @txr.err.should == STDERR
31
+ end
32
+ end
@@ -88,4 +88,17 @@ describe "Eprime::TabfileWriter" do
88
88
  lambda { @writer.write }.should raise_error(IndexError)
89
89
  end
90
90
  end
91
+
92
+ describe "with column_labels set to false" do
93
+ before :each do
94
+ @writer = Eprime::TabfileWriter.new(@eprime_data, @out_s, {:column_labels => false})
95
+ @writer.write
96
+ @out_s.rewind
97
+ end
98
+
99
+ it "should not write a line with column labels" do
100
+ @out_s.readlines.size.should == @eprime_data.size
101
+ end
102
+
103
+ end
91
104
  end
@@ -0,0 +1,90 @@
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
+ require File.join(File.dirname(__FILE__),'../spec_helper')
9
+ require File.join(File.dirname(__FILE__), '../../lib/eprime')
10
+ require 'transformers/timing_extractor'
11
+ include EprimeTestHelper
12
+ include Eprime::Transformers
13
+
14
+ describe Eprime::Transformers::TimingExtractor do
15
+ before :each do
16
+ @data = mock_edata
17
+ @tx = TimingExtractor.new(@data)
18
+ end
19
+
20
+ it "should be a BasicTransformer" do
21
+ @tx.should be_a_kind_of(BasicTransformer)
22
+ end
23
+
24
+ it "should accept extract_stimulus" do
25
+ lambda {
26
+ @tx.extract_stimulus('stim_time', 'stim_time', 'stim_time')
27
+ }.should_not raise_error
28
+ end
29
+
30
+ it "should have nothing in extracted_data when no stimuli are extracted" do
31
+ @tx.extracted_data.size.should == 0
32
+ end
33
+
34
+ it "should return rows when extracting stim_time" do
35
+ # These results will not be very meaningful
36
+ @tx.extract_stimulus('stim_time', 'stim_time', 'stim_time')
37
+ @tx.extracted_data.size.should == @data.size
38
+ end
39
+
40
+ it "should accept columns" do
41
+ lambda { @tx.columns }.should_not raise_error
42
+ end
43
+
44
+ it "should extract from computed columns" do
45
+ @tx.computed_column 'foo', 'a'
46
+ @tx.columns.should include('foo')
47
+ @tx.extract_stimulus('foo', 'foo', 'foo')
48
+ @tx.extracted_data.size.should == @data.size
49
+ end
50
+
51
+ it "should honor row filters in stim extraction" do
52
+ @tx.extract_stimulus(
53
+ 'stim_time',
54
+ 'stim_time',
55
+ 'stim_time',
56
+ lambda {|row| !row['sparse'].to_s.empty? }
57
+ )
58
+ count = @data.find_all { |r| !r['sparse'].to_s.empty? }.size
59
+ @tx.extracted_data.size.should == count
60
+ end
61
+
62
+ describe "(extracting two stimuli)" do
63
+ before :each do
64
+ @data = mock_edata
65
+ @tx = TimingExtractor.new(@data)
66
+ @tx.computed_column('stim_name','stim')
67
+ @tx.computed_column('fix_name', 'fixation')
68
+ @tx.computed_column('stim_offset', '{stim_time} + 500 - {run_start}')
69
+ @tx.computed_column('fix_offset', '{fix_time}+130-{run_start}')
70
+ @tx.extract_stimulus('stim_name', 'stim_time', 'stim_time')
71
+ @tx.extract_stimulus('fix_name', 'fix_time', 'fix_offset')
72
+ @ed = @tx.extracted_data
73
+ end
74
+
75
+ it "should have columns presented, onset, and offset" do
76
+ @ed.columns.should == %w(presented onset offset)
77
+ end
78
+
79
+ it "should have twice as many rows as @data" do
80
+ @ed.size.should == @data.size*2
81
+ end
82
+
83
+ it "should be ordered by onset" do
84
+ ordered = @ed.sort_by { |r| r['onset'].to_f }
85
+ ordered.each_index do |i|
86
+ ordered[i]['onset'].to_s.should == @ed[i]['onset'].to_s
87
+ end
88
+ end
89
+ end
90
+ 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.5.6
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Vack
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-07-09 00:00:00 -05:00
12
+ date: 2008-07-14 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -26,6 +26,7 @@ email:
26
26
  - njvack@freshforever.net
27
27
  executables:
28
28
  - eprime2tabfile
29
+ - extract_timings
29
30
  extensions: []
30
31
 
31
32
  extra_rdoc_files:
@@ -51,6 +52,7 @@ files:
51
52
  - README.txt
52
53
  - Rakefile
53
54
  - bin/eprime2tabfile
55
+ - bin/extract_timings
54
56
  - lib/calculator.rb
55
57
  - lib/eprime.rb
56
58
  - lib/eprime_data.rb
@@ -58,6 +60,7 @@ files:
58
60
  - lib/eprimetab_parser.rb
59
61
  - lib/excel_parser.rb
60
62
  - lib/log_file_parser.rb
63
+ - lib/runners/generic_runner.rb
61
64
  - lib/tabfile_parser.rb
62
65
  - lib/tabfile_writer.rb
63
66
  - lib/transformers/basic_transformer.rb
@@ -73,6 +76,7 @@ files:
73
76
  - spec/eprimetab_parser_spec.rb
74
77
  - spec/excel_parser_spec.rb
75
78
  - spec/log_file_parser_spec.rb
79
+ - spec/runners/generic_runner_spec.rb
76
80
  - spec/samples/bad_excel_tsv.txt
77
81
  - spec/samples/corrupt_log_file.txt
78
82
  - spec/samples/eprime_tsv.txt
@@ -90,6 +94,7 @@ files:
90
94
  - spec/transformers/column_calculator_spec.rb
91
95
  - spec/transformers/multipasser_spec.rb
92
96
  - spec/transformers/row_filter_spec.rb
97
+ - spec/transformers/timing_extractor_spec.rb
93
98
  - spec/writers/stimtimes_writer_spec.rb
94
99
  has_rdoc: true
95
100
  homepage: http://code.google.com/p/optimus-ep