typingpool 0.8.11 → 0.8.13

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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/bin/tp-assign +5 -4
  3. data/bin/tp-collect +16 -6
  4. data/bin/tp-finish +5 -4
  5. data/bin/tp-make +5 -4
  6. data/bin/tp-review +52 -26
  7. data/lib/typingpool/test.rb +4 -126
  8. data/lib/typingpool/test/fixtures/tp_collect_id.txt +1 -1
  9. data/lib/typingpool/test/fixtures/tp_collect_sandbox-assignment.csv +7 -8
  10. data/lib/typingpool/test/fixtures/tp_review2a_id.txt +1 -0
  11. data/lib/typingpool/test/fixtures/tp_review2a_sandbox-assignment.csv +7 -0
  12. data/lib/typingpool/test/fixtures/tp_review2b_id.txt +1 -0
  13. data/lib/typingpool/test/fixtures/tp_review2b_sandbox-assignment.csv +7 -0
  14. data/lib/typingpool/test/fixtures/tp_review3_id.txt +1 -0
  15. data/lib/typingpool/test/fixtures/tp_review3_sandbox-assignment.csv +7 -0
  16. data/lib/typingpool/test/fixtures/tp_review_id.txt +1 -1
  17. data/lib/typingpool/test/fixtures/tp_review_sandbox-assignment.csv +7 -8
  18. data/lib/typingpool/test/fixtures/vcr/tp-collect-1.yml +3176 -1814
  19. data/lib/typingpool/test/fixtures/vcr/tp-collect-2.yml +116 -2641
  20. data/lib/typingpool/test/fixtures/vcr/tp-collect-3.yml +119 -2693
  21. data/lib/typingpool/test/fixtures/vcr/tp-review-1.yml +353 -355
  22. data/lib/typingpool/test/fixtures/vcr/tp-review-2.yml +161 -242
  23. data/lib/typingpool/test/fixtures/vcr/tp-review-3.yml +4716 -0
  24. data/lib/typingpool/test/fixtures/vcr/tp-review-4.yml +741 -0
  25. data/lib/typingpool/test/fixtures/vcr/tp-review-5.yml +248 -0
  26. data/lib/typingpool/test/fixtures/vcr/tp-review-6.yml +233 -0
  27. data/lib/typingpool/test/fixtures/vcr/tp-review-7.yml +4479 -0
  28. data/lib/typingpool/test/fixtures/vcr/tp-review-8.yml +188 -0
  29. data/lib/typingpool/test/fixtures/vcr/tp-review-9.yml +187 -0
  30. data/lib/typingpool/test/script.rb +13 -320
  31. data/lib/typingpool/utility/test.rb +175 -0
  32. data/lib/typingpool/utility/test/script.rb +342 -0
  33. data/lib/typingpool/version.rb +1 -1
  34. data/test/make_amazon_question_fixture.rb +1 -1
  35. data/test/make_tp_collect_fixture_1.rb +18 -19
  36. data/test/make_tp_collect_fixture_2.rb +16 -10
  37. data/test/make_tp_collect_fixture_3.rb +15 -9
  38. data/test/make_tp_collect_fixture_4.rb +19 -11
  39. data/test/make_tp_collect_fixture_5.rb +25 -0
  40. data/test/make_tp_collect_fixture_6.rb +22 -0
  41. data/test/make_tp_collect_fixture_7.rb +22 -0
  42. data/test/make_tp_collect_fixture_8.rb +26 -0
  43. data/test/make_tp_review_fixture_1.rb +18 -19
  44. data/test/make_tp_review_fixture_2.rb +43 -24
  45. data/test/make_tp_review_fixture_3.rb +35 -0
  46. data/test/make_tp_review_fixture_4.rb +56 -0
  47. data/test/test_integration_script_1_tp_config.rb +7 -7
  48. data/test/test_integration_script_2_tp_make.rb +12 -12
  49. data/test/test_integration_script_3_tp_assign.rb +23 -21
  50. data/test/test_integration_script_4_tp_review.rb +156 -23
  51. data/test/test_integration_script_5_tp_collect.rb +33 -24
  52. data/test/test_integration_script_6_tp_finish.rb +23 -20
  53. data/test/test_unit_amazon.rb +4 -2
  54. data/test/test_unit_filer.rb +6 -6
  55. data/test/test_unit_project.rb +2 -2
  56. data/test/test_unit_project_local.rb +2 -2
  57. data/test/test_unit_project_remote.rb +3 -1
  58. data/test/test_unit_template.rb +6 -6
  59. data/test/test_unit_test.rb +3 -3
  60. metadata +23 -3
  61. data/lib/typingpool/app/test.rb +0 -69
@@ -0,0 +1,175 @@
1
+ module Typingpool
2
+ module Utility
3
+ module Test
4
+ require 'vcr'
5
+ require 'uri'
6
+
7
+ def fixtures_dir
8
+ File.join(Utility.lib_dir, 'test', 'fixtures')
9
+ end
10
+
11
+ def audio_dir
12
+ File.join(fixtures_dir, 'audio')
13
+ end
14
+
15
+ def vcr_dir
16
+ File.join(fixtures_dir, 'vcr')
17
+ end
18
+
19
+ def delete_vcr_fixture(fixture_name)
20
+ fixture_path = File.join(vcr_dir, [fixture_name, '.yml'].join)
21
+ File.delete(fixture_path) if File.exist? fixture_path
22
+ end
23
+
24
+ def cleared_vcr_fixture_path_for(fixture_name)
25
+ if Typingpool::Test.record
26
+ delete_vcr_fixture(fixture_name)
27
+ end
28
+ if (Typingpool::Test.record || not(Typingpool::Test.live))
29
+ File.join(vcr_dir, fixture_name)
30
+ end
31
+ end
32
+
33
+ def with_vcr(fixture_name, config, opts={})
34
+ if fixture = cleared_vcr_fixture_path_for(fixture_name)
35
+ read_only = not(Typingpool::Test.record)
36
+ vcr_load(fixture, config, read_only, opts)
37
+ end
38
+ begin
39
+ yield
40
+ ensure
41
+ vcr_stop
42
+ end
43
+ end
44
+
45
+ #Loads an HTTP mock fixture for playback (default) or
46
+ #recording. Used in automated tests. Uses the great VCR gem.
47
+ #
48
+ #Automatically filters your Config#amazon#key and
49
+ #Config#amazon#secret from the recorded fixture, and
50
+ #automatically determines the "cassette" name and "cassette
51
+ #library" dir from the supplied path.
52
+ # ==== Params
53
+ # [fixture_path] Path to where you want the HTTP fixture
54
+ # recorded, including filename.
55
+ # [config] A Config instance, used to extract the
56
+ # Config#amazon#secret and Config#amazon#key that
57
+ # will be filtered from the fixture.
58
+ # [read_only] Default is true. Set to false to enable recording.
59
+ # [vcr_params] Default is nil. A hash of params to pass to
60
+ # VCR.insert_cassette (same set of params that
61
+ # can be passed to VCR.use_cassette), like
62
+ # :preserve_exact_body_bytes or
63
+ # :match_requests_on => [:url, :matcher]. If nil,
64
+ # no extra params will be passed.
65
+ # ==== Returns
66
+ # Result of calling VCR.insert_cassette.
67
+ def vcr_load(fixture_path, config, read_only=true, vcr_params=nil)
68
+ VCR.configure do |c|
69
+ c.cassette_library_dir = File.dirname(fixture_path)
70
+ c.hook_into :webmock
71
+ c.filter_sensitive_data('<AWS_KEY>'){ config.amazon.key }
72
+ c.filter_sensitive_data('<AWS_SECRET>'){ config.amazon.secret }
73
+ c.before_record do |interaction|
74
+ if interaction.request.body.size > 10000
75
+ interaction.request.body = '<BIG_UPLOAD>'
76
+ end
77
+ end #c.before_record do...
78
+ end
79
+ WebMock.allow_net_connect!
80
+ opts = {:record => (read_only ? :none : :once)}
81
+ opts.merge!(vcr_params) if vcr_params
82
+ VCR.turn_on!
83
+ VCR.insert_cassette(File.basename(fixture_path, '.*'),
84
+ opts
85
+ )
86
+
87
+ end
88
+
89
+ #Stops playing/recording from the last call to vcr_load. Returns the
90
+ #result of VCR.eject_cassette.
91
+ def vcr_stop
92
+ VCR.eject_cassette
93
+ VCR.turn_off!
94
+ end
95
+
96
+ #great for s3 because we don't have to worry about changing
97
+ #bucket names, only matches as far as s3.amazonaws.com
98
+ def vcr_core_host_matcher
99
+ lambda do |request1, request2|
100
+ core_host = lambda{|host| host.split(/\./).reverse.slice(0, 3).reverse.join('.')}
101
+ core_host.call(URI(request1.uri).host) == core_host.call(URI(request2.uri).host)
102
+ end #lambda do...
103
+ end
104
+
105
+ def config
106
+ if File.exist?(File.expand_path(Config.default_file))
107
+ Config.file
108
+ else
109
+ Config.from_bundled_template
110
+ end
111
+ end
112
+
113
+ def amazon_credentials?(config=self.config)
114
+ config.amazon && config.amazon.key && config.amazon.secret
115
+ end
116
+
117
+ def s3_credentials?(config)
118
+ amazon_credentials?(config) && config.amazon.bucket
119
+ end
120
+
121
+ def sftp_credentials?(config)
122
+ config.sftp && config.sftp.user && config.sftp.host && config.sftp.url
123
+ end
124
+
125
+ def add_goodbye_message(msg)
126
+ at_exit do
127
+ STDERR.puts msg
128
+ end
129
+ end
130
+
131
+ def dummy_config(number=1)
132
+ Typingpool::Config.file(File.join(fixtures_dir, "config-#{number}"))
133
+ end
134
+
135
+
136
+ def project_default
137
+ Hash[
138
+ :config_filename => '.config',
139
+ :subtitle => "Typingpool's test interview transcription",
140
+ :title => "Typingpool's Test & Interview",
141
+ :chunks => '0:22',
142
+ :unusual => ['Hack Day', 'Sunnyvale', 'Chad D'],
143
+ :voice => ['Ryan', 'Havi, hacker'],
144
+ ]
145
+ end
146
+
147
+ def works_eventually?(max_seconds=10, min_tries=2)
148
+ start = Time.now.to_i
149
+ tries = 0
150
+ wait = 0
151
+ until ((tries >= min_tries) && ((Time.now.to_i + wait - start) >= max_seconds)) do
152
+ sleep wait
153
+ return true if yield
154
+ wait = wait > 0 ? wait * 2 : 1
155
+ tries += 1
156
+ end
157
+ false
158
+ end
159
+
160
+ def working_url_eventually?(url, max_seconds=10, min_tries=2, max_redirects=6)
161
+ works_eventually?(max_seconds, min_tries) do
162
+ Utility.working_url?(url, max_redirects)
163
+ end
164
+ end
165
+
166
+ def broken_url_eventually?(url, max_seconds=10, min_tries=2, max_redirects=6)
167
+ works_eventually?(max_seconds, min_tries) do
168
+ not(Utility.working_url?(url, max_redirects))
169
+ end
170
+ end
171
+
172
+
173
+ end #Test
174
+ end #Utility
175
+ end #Typingpool
@@ -0,0 +1,342 @@
1
+ module Typingpool
2
+ module Utility
3
+ module Test
4
+ module Script
5
+ require 'yaml'
6
+ require 'fileutils'
7
+ require 'open3'
8
+ require 'tmpdir'
9
+ require 'typingpool/utility/test'
10
+
11
+ include Typingpool::Utility::Test
12
+
13
+
14
+ @@readymade_project_path = nil
15
+
16
+ def with_temp_readymade_project
17
+ Dir.mktmpdir('typingpool_') do |transcripts_dir|
18
+ FileUtils.cp_r(File.join(readymade_project_path, '.'), transcripts_dir)
19
+ config = reconfigure_for_transcripts_dir(
20
+ Config.file(config_path(transcripts_dir)),
21
+ transcripts_dir)
22
+ write_config(transcripts_dir, config)
23
+ reconfigure_project(Project.new(project_default[:title], config))
24
+ yield(transcripts_dir)
25
+ end
26
+ end
27
+
28
+ def with_temp_transcripts_dir
29
+ Dir.mktmpdir('typingpool_') do |transcripts_dir|
30
+ write_testing_config_for_transcripts_dir(transcripts_dir, self.config)
31
+ yield(transcripts_dir)
32
+ end
33
+ end
34
+
35
+ def reconfigure_for_s3(config)
36
+ unless s3_credentials?(config)
37
+ raise Error::Test, "No S3 credentials available"
38
+ end
39
+ config.to_hash.delete('sftp')
40
+ config
41
+ end
42
+
43
+ def reconfigure_for_transcripts_dir(config, transcripts_dir)
44
+ config.transcripts = transcripts_dir
45
+ config.cache = File.join(transcripts_dir, '.cache')
46
+ config
47
+ end
48
+
49
+ def reconfigure_for_testing(config)
50
+ config['assign']['reward'] = '0.02'
51
+ config.assign.to_hash.delete('qualify')
52
+ config
53
+ end
54
+
55
+ def write_config(dir, config, filename=project_default[:config_filename])
56
+ path = File.join(dir, filename)
57
+ File.write(path, YAML.dump(config.to_hash))
58
+ path
59
+ end
60
+
61
+ def write_testing_config_for_transcripts_dir(transcripts_dir, config=self.config)
62
+ write_config(
63
+ transcripts_dir,
64
+ reconfigure_for_transcripts_dir(reconfigure_for_testing(config), transcripts_dir),
65
+ project_default[:config_filename])
66
+ end
67
+
68
+
69
+ def readymade_project_path
70
+ unless @@readymade_project_path
71
+ transcripts_dir = @@readymade_project_path = Dir.mktmpdir('typingpool_')
72
+ do_later{ FileUtils.remove_entry_secure(transcripts_dir) }
73
+ write_testing_config_for_transcripts_dir(transcripts_dir, reconfigure_for_s3(self.config))
74
+ tp_make(transcripts_dir, config_path(transcripts_dir), 'mp3', true)
75
+ end
76
+ @@readymade_project_path
77
+ end
78
+
79
+ def config_path(dir)
80
+ File.join(dir, project_default[:config_filename])
81
+ end
82
+
83
+ #Intended to be overriden by some classes that mixin this
84
+ #module
85
+ def do_later
86
+ at_exit{ yield }
87
+ end
88
+
89
+ def reconfigure_project(project)
90
+ #rewrite URLs in assignment.csv according to config at config_path
91
+ File.delete(project.local.file('data', 'id.txt'))
92
+ project.local.create_id
93
+ reconfigure_project_csv(project)
94
+ project
95
+ end
96
+
97
+ def reconfigure_project_csv(project)
98
+ assignments = project.local.file('data', 'assignment.csv').as(:csv)
99
+ urls = project.create_remote_names(assignments.map{|assignment| Project.local_basename_from_url(assignment['audio_url']) }).map{|file| project.remote.file_to_url(file) }
100
+ assignments.each! do |assignment|
101
+ assignment['audio_url'] = urls.shift
102
+ assignment['project_id'] = project.local.id
103
+ end
104
+ assignments
105
+ end
106
+
107
+ def path_to_script(script_name)
108
+ File.join(Utility.app_dir, 'bin', script_name)
109
+ end
110
+
111
+
112
+ def call_script(script_name, *args)
113
+ out, err, status = Open3.capture3(path_to_script(script_name), *args)
114
+ if status.success?
115
+ return [out.to_s.chomp, err.to_s.chomp]
116
+ else
117
+ if err
118
+ raise Error::Shell, err.chomp
119
+ else
120
+ raise Error::Shell
121
+ end
122
+ end
123
+ end
124
+
125
+ def tp_make(in_dir, config=config_path(in_dir), audio_subdir='mp3', devtest_mode_skipping_upload=false, *args)
126
+ commands = [
127
+ '--config', config,
128
+ '--chunks', project_default[:chunks],
129
+ *[:title, :subtitle].map{|param| ["--#{param}", project_default[param]] }.flatten,
130
+ *[:voice, :unusual].map{|param| project_default[param].map{|value| ["--#{param}", value] } }.flatten,
131
+ *audio_files(audio_subdir).map{|path| ['--file', path]}.flatten,
132
+ *args
133
+ ]
134
+ commands.push('--testnoupload', '--testkeepmergefile') if devtest_mode_skipping_upload
135
+ call_script('tp-make', *commands)
136
+ end
137
+
138
+ def tp_make_with_vcr(dir, fixture_name, config_path=config_path(dir))
139
+ tp_make(dir, config_path, 'mp3', false, *vcr_args(fixture_name))
140
+ end
141
+
142
+ def vcr_args(fixture_name)
143
+ args = []
144
+ if fixture = cleared_vcr_fixture_path_for(fixture_name)
145
+ args.push('--testfixture', fixture)
146
+ if Typingpool::Test.record
147
+ args.push('--testfixturerecord')
148
+ end
149
+ end #if fixture = ...
150
+ args
151
+ end
152
+
153
+ def tp_finish(dir, config_path=config_path(dir), project_title=project_default[:title], *args)
154
+ tp_finish_inside_sandbox(dir, config_path, project_title, *args)
155
+ tp_finish_outside_sandbox(dir, config_path, project_title, *args)
156
+ end
157
+
158
+
159
+ def tp_finish_inside_sandbox(dir, config_path=config_path(dir), project_title=project_default[:title], *args)
160
+ tp_finish_outside_sandbox(dir, config_path, project_title, '--sandbox', *args)
161
+ end
162
+
163
+ def tp_finish_outside_sandbox(dir, config_path=config_path(dir), project_title=project_default[:title], *args)
164
+ call_script('tp-finish', project_title, '--config', config_path, *args)
165
+ end
166
+
167
+ def audio_files(subdir='mp3')
168
+ dir = File.join(audio_dir, subdir)
169
+ Dir.entries(dir).reject{|entry| entry.match(/^\./) }.map{|entry| File.join(dir, entry)}.select{|path| File.file?(path) }
170
+ end
171
+
172
+ def assign_default
173
+ Hash[
174
+ :template => 'interview/phone',
175
+ :deadline => '5h',
176
+ :lifetime => '10h',
177
+ :approval => '10h',
178
+ :qualify => ['approval_rate >= 90', 'hits_approved > 10'],
179
+ :keyword => ['test', 'mp3', 'typingpooltest']
180
+ ]
181
+ end
182
+
183
+ def tp_assign(dir, config_path=config_path(dir), project_title=project_default[:title], *args)
184
+ call_script(
185
+ 'tp-assign',
186
+ '--sandbox',
187
+ project_title,
188
+ assign_default[:template],
189
+ '--config', config_path,
190
+ *[:deadline, :lifetime, :approval].map{|param| ["--#{param}", assign_default[param]] }.flatten,
191
+ *[:qualify, :keyword].map{|param| assign_default[param].map{|value| ["--#{param}", value] } }.flatten,
192
+ *args)
193
+
194
+ end
195
+
196
+ def tp_assign_with_vcr(dir, fixture_name, config_path=config_path(dir), project_title=project_default[:title])
197
+ project = Project.new(project_default[:title], Typingpool::Config.file(config_path))
198
+ args = [dir, config_path, project_title, *vcr_args(fixture_name)]
199
+ unless (Typingpool::Test.live || Typingpool::Test.record)
200
+ args.push('--testtime', project_time(project).to_i.to_s)
201
+ end
202
+ tp_assign(*args)
203
+ end
204
+
205
+ def copy_tp_assign_fixtures(dir, fixture_prefix, config_path=config_path(dir), project_title=project_default[:title])
206
+ project = Project.new(project_title, Config.file(config_path))
207
+ if Typingpool::Test.record
208
+ project_time(project, Time.now)
209
+ with_fixtures_in_transcripts_dir(dir, "#{fixture_prefix}_", project_title) do |fixture_path, project_path|
210
+ FileUtils.cp(project_path, fixture_path)
211
+ end
212
+ elsif not(Typingpool::Test.live)
213
+ copy_fixtures_to_project_dir("#{fixture_prefix}_", File.join(dir, project_title))
214
+ reconfigure_project_csv(project)
215
+ end
216
+ end
217
+
218
+ def tp_collect_with_fixture(dir, fixture_name, are_recording=false)
219
+ fixture_handle = File.join(vcr_dir, fixture_name)
220
+ args = ['tp-collect', '--sandbox', '--testfixture', fixture_handle, '--config', config_path(dir)]
221
+ if are_recording
222
+ delete_vcr_fixture(fixture_name)
223
+ args.push('--testfixturerecord')
224
+ end
225
+ call_script(*args)
226
+ end
227
+
228
+ def tp_review_with_fixture(transcripts_dir, fixture_name, choices, are_recording=false, project_name=nil)
229
+ fixture_handle = File.join(vcr_dir, fixture_name)
230
+ output = {}
231
+ args = [
232
+ File.join(Utility.app_dir, 'bin', 'tp-review'),
233
+ '--sandbox',
234
+ '--config', config_path(transcripts_dir),
235
+ '--testfixture', fixture_handle
236
+ ]
237
+ args.push(project_name) if project_name
238
+ if are_recording
239
+ delete_vcr_fixture(fixture_name)
240
+ args.push('--testfixturerecord')
241
+ end
242
+
243
+ Open3.popen3(*args) do |stdin, stdout, stderr, wait_thr|
244
+ choices.each do |choice|
245
+ stdin.puts(choice)
246
+ if choice.strip.match(/^r/i)
247
+ stdin.puts("No reason - this is a test")
248
+ end
249
+ end
250
+ output[:out] = stdout.gets(nil)
251
+ output[:err] = stderr.gets(nil)
252
+ [stdin, stdout, stderr].each{|stream| stream.close }
253
+ output[:status] = wait_thr.value
254
+ end
255
+ output
256
+ end
257
+
258
+ def tp_config_with_input(args, input)
259
+ output = {}
260
+ Open3.popen3(path_to_script('tp-config'), *args) do |stdin, stdout, stderr, wait_thr|
261
+ input.each do |sending|
262
+ stdin.puts(sending)
263
+ end
264
+ output[:out] = stdout.gets(nil)
265
+ output[:err] = stderr.gets(nil)
266
+ [stdin, stdout, stderr].each{|stream| stream.close }
267
+ output[:status] = wait_thr.value
268
+ end #Open3.popen3...
269
+ output
270
+ end
271
+
272
+ def project_time(project, time=nil)
273
+ file = project.local.file('data', 'time.txt')
274
+ if time
275
+ file.write(time.to_i)
276
+ else
277
+ time = Time.at(file.read.to_i)
278
+ end
279
+ time
280
+ end
281
+
282
+ def simulate_failed_audio_upload_in(dir, config_path=config_path(dir))
283
+ project = Typingpool::Project.new(project_default[:title], Config.file(config_path))
284
+ csv = project.local.file('data', 'assignment.csv').as(:csv)
285
+ csv.each!{|a| a['audio_uploaded'] = 'maybe'}
286
+ end
287
+
288
+ def make_fixture_transcripts_dir(name)
289
+ dir = File.join(fixtures_dir, name)
290
+ if File.exist? dir
291
+ raise Error::Test, "Fixture transcript dir already exists for #{name} at #{dir}"
292
+ end
293
+ Dir.mkdir(dir)
294
+ dir
295
+ end
296
+
297
+ def with_fixtures_in_project_dir(fixture_prefix, project_path)
298
+ fixtures = Dir.entries(fixtures_dir).select{|entry| entry.include?(fixture_prefix) && entry.index(fixture_prefix) == 0 }.select{|entry| File.file?(File.join(fixtures_dir, entry)) }
299
+ fixtures.map!{|fixture| fixture[fixture_prefix.size .. -1] }
300
+ fixtures.each do |fixture|
301
+ project_fixture_path = File.join(project_path, 'data', fixture)
302
+ source_fixture_path = File.join(fixtures_dir, [fixture_prefix, fixture].join )
303
+ yield(source_fixture_path, project_fixture_path)
304
+ end
305
+ end
306
+
307
+ def copy_fixtures_to_project_dir(fixture_prefix, project_path)
308
+ copies = 0
309
+ with_fixtures_in_project_dir(fixture_prefix, project_path) do |source_fixture_path, project_fixture_path|
310
+ if File.exist? project_fixture_path
311
+ FileUtils.mv(project_fixture_path, File.join(File.dirname(project_fixture_path), "orig_#{File.basename(project_fixture_path)}"))
312
+ end
313
+ FileUtils.cp(source_fixture_path, project_fixture_path)
314
+ copies += 1
315
+ end
316
+ copies > 0 or raise Error, "No fixtures to copy with prefix #{fixture_prefix}"
317
+ copies
318
+ end
319
+
320
+ def restore_project_dir_from_fixtures(fixture_prefix, project_path)
321
+ with_fixtures_in_project_dir(fixture_prefix, project_path) do |source_fixture_path, project_fixture_path|
322
+ FileUtils.rm(project_fixture_path)
323
+ path_to_orig = File.join(File.dirname(project_fixture_path), "orig_#{File.basename(project_fixture_path)}")
324
+ if File.exist?(path_to_orig)
325
+ FileUtils.mv(path_to_orig, project_fixture_path)
326
+ end
327
+ end #with_fixtures_in_transctips_dir
328
+ end
329
+
330
+ def project_transcript_count(project, which_csv)
331
+ project.local.file('data', which_csv).as(:csv).reject{|assignment| assignment['transcript'].to_s.empty?}.size
332
+ end
333
+
334
+ def split_reviews(output)
335
+ output.split(/Transcript for\b/)
336
+ end
337
+
338
+
339
+ end #Script
340
+ end #Test
341
+ end #Utility
342
+ end #Typingpool