optimus-ep 0.5.6 → 0.6.0

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/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