typingpool 0.7.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.
Files changed (90) hide show
  1. data/Rakefile +23 -0
  2. data/bin/tp-assign +240 -0
  3. data/bin/tp-collect +50 -0
  4. data/bin/tp-config +114 -0
  5. data/bin/tp-finish +101 -0
  6. data/bin/tp-make +169 -0
  7. data/bin/tp-review +175 -0
  8. data/lib/typingpool/amazon.rb +732 -0
  9. data/lib/typingpool/app.rb +634 -0
  10. data/lib/typingpool/config.rb +344 -0
  11. data/lib/typingpool/error.rb +22 -0
  12. data/lib/typingpool/filer.rb +396 -0
  13. data/lib/typingpool/project.rb +593 -0
  14. data/lib/typingpool/template.rb +175 -0
  15. data/lib/typingpool/templates/assignment/amazon-init.js +38 -0
  16. data/lib/typingpool/templates/assignment/interview/nameless.html.erb +13 -0
  17. data/lib/typingpool/templates/assignment/interview/noisy.html.erb +12 -0
  18. data/lib/typingpool/templates/assignment/interview/partials/voices.html.erb +10 -0
  19. data/lib/typingpool/templates/assignment/interview/phone.html.erb +12 -0
  20. data/lib/typingpool/templates/assignment/interview.html.erb +11 -0
  21. data/lib/typingpool/templates/assignment/main.css +20 -0
  22. data/lib/typingpool/templates/assignment/partials/entry.html.erb +19 -0
  23. data/lib/typingpool/templates/assignment/partials/footer.html.erb +3 -0
  24. data/lib/typingpool/templates/assignment/partials/header.html.erb +11 -0
  25. data/lib/typingpool/templates/assignment/partials/labeling-example.html.erb +4 -0
  26. data/lib/typingpool/templates/assignment/partials/labeling.html.erb +5 -0
  27. data/lib/typingpool/templates/assignment/partials/length-description.html.erb +6 -0
  28. data/lib/typingpool/templates/assignment/partials/voices.html.erb +10 -0
  29. data/lib/typingpool/templates/assignment/speech.html.erb +11 -0
  30. data/lib/typingpool/templates/config.yml +21 -0
  31. data/lib/typingpool/templates/project/audio/chunks/.empty_directory +0 -0
  32. data/lib/typingpool/templates/project/audio/originals/.empty_directory +0 -0
  33. data/lib/typingpool/templates/project/data/.empty_directory +0 -0
  34. data/lib/typingpool/templates/project/etc/ About these files - read me.txt +8 -0
  35. data/lib/typingpool/templates/project/etc/audio-compat.js +25 -0
  36. data/lib/typingpool/templates/project/etc/player/audio-player.js +4 -0
  37. data/lib/typingpool/templates/project/etc/player/license.txt +19 -0
  38. data/lib/typingpool/templates/project/etc/player/player.swf +0 -0
  39. data/lib/typingpool/templates/project/etc/transcript.css +49 -0
  40. data/lib/typingpool/templates/transcript.html.erb +23 -0
  41. data/lib/typingpool/test/fixtures/amazon-question-html.html +95 -0
  42. data/lib/typingpool/test/fixtures/amazon-question-url.txt +1 -0
  43. data/lib/typingpool/test/fixtures/audio/mp3/interview.1.mp3 +0 -0
  44. data/lib/typingpool/test/fixtures/audio/mp3/interview.2.mp3 +0 -0
  45. data/lib/typingpool/test/fixtures/audio/wma/VN620007.WMA +0 -0
  46. data/lib/typingpool/test/fixtures/audio/wma/VN620052.WMA +0 -0
  47. data/lib/typingpool/test/fixtures/config-1 +20 -0
  48. data/lib/typingpool/test/fixtures/config-2 +25 -0
  49. data/lib/typingpool/test/fixtures/not_yaml.txt +4 -0
  50. data/lib/typingpool/test/fixtures/template-2.html.erb +10 -0
  51. data/lib/typingpool/test/fixtures/template-3.html.erb +22 -0
  52. data/lib/typingpool/test/fixtures/template.html.erb +10 -0
  53. data/lib/typingpool/test/fixtures/tp_collect_id.txt +1 -0
  54. data/lib/typingpool/test/fixtures/tp_collect_sandbox-assignment.csv +8 -0
  55. data/lib/typingpool/test/fixtures/tp_review_id.txt +1 -0
  56. data/lib/typingpool/test/fixtures/tp_review_sandbox-assignment.csv +8 -0
  57. data/lib/typingpool/test/fixtures/transcript-chunks.csv +226 -0
  58. data/lib/typingpool/test/fixtures/utf8_transcript.txt +7 -0
  59. data/lib/typingpool/test/fixtures/vcr/tp-collect-1.yml +2712 -0
  60. data/lib/typingpool/test/fixtures/vcr/tp-collect-2.yml +2718 -0
  61. data/lib/typingpool/test/fixtures/vcr/tp-collect-3.yml +2768 -0
  62. data/lib/typingpool/test/fixtures/vcr/tp-review-1.yml +570 -0
  63. data/lib/typingpool/test/fixtures/vcr/tp-review-2.yml +351 -0
  64. data/lib/typingpool/test.rb +418 -0
  65. data/lib/typingpool/transcript.rb +181 -0
  66. data/lib/typingpool/utility.rb +272 -0
  67. data/lib/typingpool.rb +500 -0
  68. data/test/make_amazon_question_fixture.rb +24 -0
  69. data/test/make_tp_collect_fixture_1.rb +26 -0
  70. data/test/make_tp_collect_fixture_2.rb +16 -0
  71. data/test/make_tp_collect_fixture_3.rb +15 -0
  72. data/test/make_tp_collect_fixture_4.rb +17 -0
  73. data/test/make_tp_review_fixture_1.rb +26 -0
  74. data/test/make_tp_review_fixture_2.rb +30 -0
  75. data/test/make_transcript_chunks_fixture.rb +53 -0
  76. data/test/test_integration_script_1_tp_config.rb +108 -0
  77. data/test/test_integration_script_2_tp_make.rb +119 -0
  78. data/test/test_integration_script_3_tp_assign.rb +152 -0
  79. data/test/test_integration_script_4_tp_review.rb +72 -0
  80. data/test/test_integration_script_5_tp_collect.rb +44 -0
  81. data/test/test_integration_script_6_tp_finish.rb +123 -0
  82. data/test/test_unit_amazon.rb +153 -0
  83. data/test/test_unit_config.rb +94 -0
  84. data/test/test_unit_filer.rb +202 -0
  85. data/test/test_unit_project.rb +168 -0
  86. data/test/test_unit_project_local.rb +68 -0
  87. data/test/test_unit_project_remote.rb +157 -0
  88. data/test/test_unit_template.rb +111 -0
  89. data/test/test_unit_transcript.rb +77 -0
  90. metadata +234 -0
@@ -0,0 +1,153 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(File.dirname($0)), 'lib')
4
+
5
+ require 'typingpool'
6
+ require 'typingpool/test'
7
+ require 'uri'
8
+ require 'cgi'
9
+
10
+ class TestAmazon < Typingpool::Test
11
+
12
+ def test_amazon_base
13
+ setup_result = Typingpool::Amazon.setup(:sandbox => true, :config => dummy_config)
14
+ assert_match(setup_result, /amazonaws/)
15
+ assert(Typingpool::Amazon.cache)
16
+ assert_instance_of(PStore, Typingpool::Amazon.cache)
17
+ assert_equal(dummy_config.cache, Typingpool::Amazon.cache.path)
18
+ assert(full_rturk_hit = Typingpool::Amazon.rturk_hit_full('test'))
19
+ assert_instance_of(RTurk::Hit, full_rturk_hit)
20
+ end
21
+
22
+ def test_amazon_question
23
+ assert(question = dummy_question)
24
+ assert_instance_of(Typingpool::Amazon::Question, question)
25
+ assert_equal(question_url, question.url)
26
+ assert_equal(question_html, question.html)
27
+ assert_match(question.title, /Transcribe MP3 of/i)
28
+ assert_match(question.description, /telephone conversation/i)
29
+ assert_match(question.annotation, /\S/)
30
+ assert(decoded_annotation = URI.decode_www_form(CGI.unescapeHTML(question.annotation)))
31
+ decoded_annotation = Hash[*decoded_annotation.flatten]
32
+ assert_match(decoded_annotation[Typingpool::Amazon::HIT.url_at], /^http/i)
33
+ assert_match(decoded_annotation[Typingpool::Amazon::HIT.id_at], /\S/)
34
+ end
35
+
36
+ def test_amazon_hit_create
37
+ with_dummy_hit_or_skip('test_amazon_hit_create') do |hit, config|
38
+ assert_equal(hit.full.external_question_url, dummy_question.url)
39
+ assert_equal(config.assign.deadline, hit.full.assignments_duration.to_i)
40
+ assert(rturk_hit = hit.at_amazon)
41
+ assert_equal(dummy_question.annotation.to_s, CGI.escapeHTML(rturk_hit.annotation.to_s))
42
+ assert_equal(dummy_question.title.strip, rturk_hit.title.strip)
43
+ assert_equal(dummy_question.description.strip, rturk_hit.description.strip)
44
+ assert_equal(config.assign.reward.to_f, rturk_hit.reward.to_f)
45
+ assert_equal(config.assign.keywords.first.to_s, rturk_hit.keywords.first.to_s)
46
+ end #with_dummy_hit
47
+ end
48
+
49
+ #fails to test all_reviewable or all_approved - those require a VCR fixture (TODO)
50
+ def test_amazon_hit_retrievers
51
+ with_dummy_hit_or_skip('test_amazon_hit_retrievers') do |hit, config|
52
+ assert(result = Typingpool::Amazon::HIT.with_ids([hit.id]))
53
+ assert_equal(1, result.count)
54
+ assert_equal(hit.id, result.first.id)
55
+ assert(result = Typingpool::Amazon::HIT.all_for_project(hit.project_id))
56
+ assert_equal(1, result.count)
57
+ assert_equal(hit.id, result.first.id)
58
+ assert(results = Typingpool::Amazon::HIT.all)
59
+ assert(results.count > 0)
60
+ assert(results = Typingpool::Amazon::HIT.all{|incoming_hit| incoming_hit.id == hit.id })
61
+ assert_equal(1, results.count)
62
+ assert_equal(hit.id, result.first.id)
63
+ end #with_dummy_hit
64
+ end
65
+
66
+ #fails to properly test approved?, rejected?, submitted?, assignment - those require a VCR ficture (TODO)
67
+ def test_amazon_hit_base
68
+ with_dummy_hit_or_skip('test_amazon_hit_base') do |hit, config|
69
+ assert_instance_of(Typingpool::Amazon::HIT, hit)
70
+ assert_match(hit.id, /\S/)
71
+ assert_match(hit.url, /^http/i)
72
+ assert_match(hit.project_id, /\S/)
73
+ assert_match(hit.project_title_from_url, /\S/)
74
+ assert(not(hit.approved?))
75
+ assert(not(hit.rejected?))
76
+ assert(not(hit.submitted?))
77
+ assert(hit.ours?)
78
+ assert_instance_of(Typingpool::Transcript::Chunk, hit.transcript)
79
+ assert_kind_of(RTurk::Hit, hit.at_amazon)
80
+ assert_instance_of(Typingpool::Amazon::HIT::Full, hit.full)
81
+ assert_instance_of(Typingpool::Amazon::HIT::Assignment::Empty, hit.assignment)
82
+ end #with_dummy_hit_or_skip
83
+ end
84
+
85
+ #fails to test external_question* methods
86
+ def test_amazon_hit_full
87
+ with_dummy_hit_or_skip('test_amazon_hit_full') do |hit, config|
88
+ assert(full = hit.full)
89
+ assert_instance_of(Typingpool::Amazon::HIT::Full, full)
90
+ [:id, :type_id].each{|attr| assert_match(full.send(attr), /\S/) }
91
+ assert(not(full.expired?))
92
+ assert(not(full.expired_and_overdue?))
93
+ assert_equal('Assignable', full.status)
94
+ assert_match(full.external_question_url, /^http/i)
95
+ [:assignments_completed, :assignments_pending].each{|attr| assert_match(full.send(attr).to_s, /^\d+$/) }
96
+ assert_kind_of(Time, full.expires_at)
97
+ assert_instance_of(Hash, full.annotation)
98
+ assert_match(full.annotation[Typingpool::Amazon::HIT.url_at], /^http/i)
99
+ assert_match(full.annotation[Typingpool::Amazon::HIT.id_at], /\S/)
100
+ end #with_dummy_hit_or_skip
101
+ end
102
+
103
+ def test_amazon_hit_full_fromsearchhits
104
+ with_dummy_hit_or_skip('test_amazon_hit_full_fromsearchhits') do |hit, config|
105
+ assert(full = hit.full)
106
+ assert_instance_of(Typingpool::Amazon::HIT::Full, full)
107
+ assert(hit2 = Typingpool::Amazon::HIT.all{|incoming_hit| incoming_hit.id == hit.id }.first)
108
+ assert_equal(hit.id, hit2.id)
109
+ assert(full2 = hit2.full)
110
+ assert_instance_of(Typingpool::Amazon::HIT::Full::FromSearchHITs, full2)
111
+ assert_equal(full.annotation.to_s, full2.annotation.to_s)
112
+ [:assignments_completed, :assignments_pending, :id, :type_id, :status, :expires_at, :assignments_duration, :external_question_url].each{|attr| assert_equal(full.send(attr).to_s, full2.send(attr).to_s) }
113
+ end #with_dummy_hit_or_skip
114
+ end
115
+
116
+ #Lacks test for HIT::Assignment - needs VCR fixture (TODO)
117
+
118
+ def question_html
119
+ File.read(File.join(fixtures_dir, 'amazon-question-html.html'))
120
+ end
121
+
122
+ def question_url
123
+ File.read(File.join(fixtures_dir, 'amazon-question-url.txt')).strip
124
+ end
125
+
126
+ def dummy_question
127
+ Typingpool::Amazon::Question.new(question_url, question_html)
128
+ end
129
+
130
+ def dummy_hit(config)
131
+ Typingpool::Amazon::HIT.create(dummy_question, config.assign)
132
+ end
133
+
134
+ def with_dummy_hit_or_skip(skipping_what)
135
+ config = self.config
136
+ skip_if_no_amazon_credentials(skipping_what, config)
137
+ config.assign.reward = '0.01'
138
+ cache = Tempfile.new('typingpool_cache')
139
+ begin
140
+ config.cache = cache.path
141
+ Typingpool::Amazon.setup(:sandbox => true, :config => config)
142
+ hit = dummy_hit(config)
143
+ begin
144
+ yield(hit, config)
145
+ ensure
146
+ hit.remove_from_amazon
147
+ end #begin
148
+ ensure
149
+ cache.close
150
+ cache.unlink
151
+ end #begin
152
+ end
153
+ end #TestAmazon
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(File.dirname($0)), 'lib')
4
+
5
+ require 'typingpool'
6
+ require 'typingpool/test'
7
+
8
+ class TestConfig < Typingpool::Test
9
+
10
+ def test_config_regular_file
11
+ assert(config = Typingpool::Config.file(File.join(fixtures_dir, 'config-1')))
12
+ assert_equal('~/Documents/Transcripts/', config['transcripts'])
13
+ assert_match(config.transcripts, /Transcripts$/)
14
+ refute_match(config.transcripts, /~/)
15
+ %w(key secret).each do |param|
16
+ regex = /test101010/
17
+ assert_match(config.amazon.send(param), regex)
18
+ assert_match(config.amazon[param], regex)
19
+ assert_match(config.amazon.to_hash[param], regex)
20
+ end
21
+ assert_equal(0.75, config.assign.reward.to_f)
22
+ assert_equal(3*60*60, config.assign.deadline.to_i)
23
+ assert_equal('3h', config.assign['deadline'])
24
+ assert_equal(60*60*24*2, config.assign.lifetime.to_i)
25
+ assert_equal('2d', config.assign['lifetime'])
26
+ assert_equal(3, config.assign.keywords.count)
27
+ assert_kind_of(Typingpool::Config::Root::Assign::Qualification, config.assign.qualify.first)
28
+ assert_equal(:approval_rate, config.assign.qualify.first.to_arg[0])
29
+ assert_equal(:gte, config.assign.qualify.first.to_arg[1].keys.first)
30
+ assert_equal('95', config.assign.qualify.first.to_arg[1].values.first.to_s)
31
+ end
32
+
33
+ def test_config_sftp
34
+ assert(config = Typingpool::Config.file(File.join(fixtures_dir, 'config-2')))
35
+ assert_equal('ryan', config.sftp.user)
36
+ assert_equal('public_html/transfer/', config.sftp['path'])
37
+ assert_equal('public_html/transfer', config.sftp.path)
38
+ assert_equal('http://example.com/mturk/', config.sftp['url'])
39
+ assert_equal('http://example.com/mturk', config.sftp.url)
40
+ end
41
+
42
+ def test_config_screwy_file
43
+ assert(config = Typingpool::Config.file(File.join(fixtures_dir, 'config-2')))
44
+
45
+ exception = assert_raises(Typingpool::Error::Argument) do
46
+ config.assign.qualify
47
+ end
48
+ assert_match(exception.message, /Unknown qualification type/i)
49
+
50
+ config.assign['qualify'] = [config.assign['qualify'].pop]
51
+ exception = assert_raises(Typingpool::Error::Argument) do
52
+ config.assign.qualify
53
+ end
54
+ assert_match(exception.message, /Unknown comparator/i)
55
+
56
+ assert_equal('3z', config.assign['deadline'])
57
+ exception = assert_raises(Typingpool::Error::Argument::Format) do
58
+ config.assign.deadline
59
+ end
60
+ assert_match(exception.message, /can't convert/i)
61
+
62
+ config.assign['reward'] = 'foo'
63
+ exception = assert_raises(Typingpool::Error::Argument::Format) do
64
+ config.assign.reward
65
+ end
66
+ assert_match(exception.message, /\bformat should\b/i)
67
+ end
68
+
69
+ def test_config_regular_input
70
+ assert(config = Typingpool::Config.file(File.join(fixtures_dir, 'config-1')))
71
+ new_reward = '0.80'
72
+ refute_equal(config.assign.reward, new_reward)
73
+ assert(config.assign.reward = new_reward)
74
+ assert_equal(new_reward, config.assign.reward)
75
+
76
+ new_time = '11d'
77
+ refute_equal(new_time, config.assign.approval)
78
+ assert(config.assign.approval = new_time)
79
+ assert_equal(950400, config.assign.approval)
80
+ end
81
+
82
+ def test_config_screwy_input
83
+ exception = assert_raises(Typingpool::Error::Argument::Format) do
84
+ config.assign.reward = 'foo'
85
+ end
86
+ assert_match(exception.message, /\bformat should\b/i)
87
+
88
+ exception = assert_raises(Typingpool::Error::Argument::Format) do
89
+ config.assign.approval = '11f'
90
+ end
91
+ assert_match(exception.message, /can't convert/i)
92
+
93
+ end
94
+ end #TestConfig
@@ -0,0 +1,202 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(File.dirname($0)), 'lib')
4
+
5
+ require 'typingpool'
6
+ require 'typingpool/test'
7
+ require 'fileutils'
8
+
9
+ class TestFiler < Typingpool::Test
10
+
11
+ def test_filer_base
12
+ path = File.join(fixtures_dir, 'config-1')
13
+ assert(filer = Typingpool::Filer.new(path))
14
+ assert_equal(path, "#{filer}")
15
+ assert(text = filer.read)
16
+ assert_match(text, /^amazon:\n/)
17
+ assert_match(text, /transcripts: ~\/Documents\/Transcripts\//)
18
+ assert_match(text, /- mp3\s*$/)
19
+ assert_equal(fixtures_dir, filer.dir.path)
20
+ in_temp_dir do |dir|
21
+ path = File.join(dir, 'filer-temp')
22
+ assert(filer = Typingpool::Filer.new(path))
23
+ assert_instance_of(Typingpool::Filer::CSV, filer_csv = filer.as(:csv))
24
+ assert_equal(filer.path, filer_csv.path)
25
+ assert_nil(filer.read)
26
+ data = "foo\nbar\nbaz."
27
+ assert(filer.write(data))
28
+ assert_equal(data, filer.read)
29
+ assert(path = filer.mv!(File.join(dir, 'filer-temp-2')))
30
+ assert(File.exists? filer.path)
31
+ assert_equal('filer-temp-2', File.basename(filer.path))
32
+ end #in_temp_dir
33
+ end
34
+
35
+ def test_filer_csv
36
+ path = File.join(fixtures_dir, 'tp_review_sandbox-assignment.csv')
37
+ assert(filer = Typingpool::Filer::CSV.new(path))
38
+ assert_equal(path, "#{filer}")
39
+ assert(data = filer.read)
40
+ assert_instance_of(Array, data)
41
+ assert_instance_of(Hash, data.first)
42
+ assert_respond_to(filer, :each)
43
+ assert_respond_to(filer, :map)
44
+ assert_respond_to(filer, :select)
45
+ assert(data.first['audio_url'])
46
+ assert_match(data.first['audio_url'], /^https?:\/\/\w/)
47
+ assert(filer.select{|r| r['audio_url'] }.count > 0)
48
+ in_temp_dir do |dir|
49
+ path = File.join(dir, 'filer-temp')
50
+ assert(filer2 = Typingpool::Filer::CSV.new(path))
51
+ assert_equal([], filer2.read)
52
+ assert(filer2.write(data))
53
+ assert_equal(Typingpool::Filer.new(filer.path).read, Typingpool::Filer.new(filer2.path).read)
54
+ assert_equal(filer.count, filer2.count)
55
+ filer2.each! do |row|
56
+ row['audio_url'] = row['audio_url'].reverse
57
+ end
58
+ rewritten = Typingpool::Filer.new(filer2.path).read
59
+ assert(Typingpool::Filer.new(filer.path).read != rewritten)
60
+ keys = filer2.first.keys
61
+ filer2.write_arrays(filer2.map{|row| keys.map{|key| row[key] } }, keys)
62
+ assert_equal(rewritten, Typingpool::Filer.new(filer2.path).read)
63
+ end #in_temp_dir
64
+ end
65
+
66
+ #This might be more comprehensive if it looped and ran once with
67
+ #Encoding set to 'US-ASCII' - but we'd have to be carefult to reset
68
+ #Encoding back to orig value. For now I just run ruby -E 'US-ASCII'
69
+ #test_unit_filer.rb now and again.
70
+ def test_filer_csv_utf8
71
+ path = File.join(fixtures_dir, 'tp_review_sandbox-assignment.csv')
72
+ assert(filer = Typingpool::Filer::CSV.new(path))
73
+ assert(data = filer.read)
74
+ assert_instance_of(Array, data)
75
+ assert_instance_of(Hash, data.first)
76
+ in_temp_dir do |dir|
77
+ path = File.join(dir, 'filer-temp')
78
+ assert(filer2 = Typingpool::Filer::CSV.new(path))
79
+ assert_equal([], filer2.read)
80
+ assert(filer2.write(data))
81
+ assert_equal(Typingpool::Filer.new(filer.path).read, Typingpool::Filer.new(filer2.path).read)
82
+ refute_empty(assignments = filer2.read)
83
+ assert(assignment = assignments.pop)
84
+ assert(assignment['transcript'] = File.read(File.join(fixtures_dir, 'utf8_transcript.txt'), :encoding => 'UTF-8'))
85
+ assignments.push(assignment)
86
+ assert(filer2.write(assignments))
87
+ refute_empty(filer2.read) #will throw ArgumentError: invalid byte sequence in US-ASCII in degenerate case
88
+ end #in_temp_dir
89
+ end
90
+
91
+ def test_filer_audio
92
+ mp3 = Typingpool::Filer::Audio.new(files_from(File.join(audio_dir, 'mp3')).first)
93
+ wma = Typingpool::Filer::Audio.new(files_from(File.join(audio_dir, 'wma')).first)
94
+ assert(mp3.mp3?)
95
+ assert(not(wma.mp3?))
96
+ in_temp_dir do |dir|
97
+ [mp3, wma].each do |file|
98
+ FileUtils.cp(file, dir)
99
+ end
100
+ mp3 = Typingpool::Filer::Audio.new(File.join(dir, File.basename(mp3)))
101
+ wma = Typingpool::Filer::Audio.new(File.join(dir, File.basename(wma)))
102
+ dest = Typingpool::Filer::Audio.new(File.join(dir, 'filer-temp.mp3'))
103
+ assert(converted = wma.to_mp3(dest))
104
+ assert_equal(dest.path, converted.path)
105
+ assert(wma.bitrate >= 30)
106
+ assert(wma.bitrate <= 40)
107
+ assert(converted.bitrate)
108
+ assert(converted.mp3?)
109
+ assert(chunks = mp3.split('0.25', 'filer-temp', Typingpool::Filer::Dir.new(dir)))
110
+ assert(not(chunks.to_a.empty?))
111
+ assert_equal(3, chunks.count)
112
+ chunks.each{|chunk| assert(File.exists? chunk) }
113
+ assert(chunks.first.offset)
114
+ assert_match(chunks.first.offset, /0\.00\b/)
115
+ assert_match(chunks.to_a[1].offset, /0\.25\b/)
116
+ end #in_temp_dir
117
+ end
118
+
119
+ def files_from(dir)
120
+ Dir.entries(dir).map{|entry| File.join(dir, entry) }.select{|path| File.file? path }.reject{|path| path.match(/^\./) }
121
+ end
122
+
123
+ def test_filer_files_base
124
+ file_selector = /tp[_-]collect/
125
+ dir = fixtures_dir
126
+ files = files_from(dir).select{|path| path.match(file_selector) }
127
+ dir = File.join(fixtures_dir, 'vcr')
128
+ files.push(*files_from(dir).select{|path| path.match(file_selector) })
129
+ assert(files.count > 0)
130
+ assert(filer = Typingpool::Filer::Files.new(files.map{|path| Typingpool::Filer.new(path) }))
131
+ assert_equal(filer.files.count, files.count)
132
+ assert_respond_to(filer, :each)
133
+ assert_respond_to(filer, :select)
134
+ assert_respond_to(filer, :map)
135
+ assert_instance_of(Typingpool::Filer::Files::Audio, filer.as(:audio))
136
+ end
137
+
138
+ def test_filer_files_audio
139
+ mp3s = files_from(File.join(audio_dir, 'mp3')).map{|path| Typingpool::Filer::Audio.new(path) }
140
+ wmas = files_from(File.join(audio_dir, 'wma')).map{|path| Typingpool::Filer::Audio.new(path) }
141
+ assert(mp3s.count > 0)
142
+ assert(wmas.count > 0)
143
+ assert(filer_mp3 = Typingpool::Filer::Files::Audio.new(mp3s))
144
+ assert(filer_wma = Typingpool::Filer::Files::Audio.new(wmas))
145
+ assert_equal(mp3s.count, filer_mp3.files.count)
146
+ assert_equal(wmas.count, filer_wma.files.count)
147
+ in_temp_dir do |dir|
148
+ dest_filer = Typingpool::Filer::Dir.new(dir)
149
+ assert(filer_conversion = filer_wma.to_mp3(dest_filer))
150
+ assert_equal(filer_wma.files.count, filer_conversion.files.count)
151
+ assert_equal(filer_wma.files.count, filer_conversion.select{|file| File.exists? file }.count)
152
+ assert_equal(filer_wma.files.count, filer_conversion.select{|file| file.mp3? }.count)
153
+ assert_equal(filer_conversion.files.count, dest_filer.files.count)
154
+ temp_path = File.join(dir, 'temp.mp3')
155
+ assert(filer_merged = filer_mp3.merge(Typingpool::Filer.new(temp_path)))
156
+ assert(File.size(filer_merged) > File.size(filer_mp3.first))
157
+ assert(filer_merged.mp3?)
158
+ assert(filer_merged.path != filer_mp3.first.path)
159
+ assert(filer_merged.path != filer_mp3.to_a[1].path)
160
+ end #in_temp_dir
161
+ end
162
+
163
+ def test_filer_dir
164
+ assert(dir = Typingpool::Filer::Dir.new(fixtures_dir))
165
+ assert_equal(fixtures_dir, dir.path)
166
+ dir2_path = File.join(fixtures_dir, 'doesntexist')
167
+ assert(not(File.exists? dir2_path))
168
+ assert(dir2 = Typingpool::Filer::Dir.new(dir2_path))
169
+ in_temp_dir do |dir|
170
+ dir3_path = File.join(dir, 'filer-dir-temp')
171
+ assert(not(File.exists? dir3_path))
172
+ assert(dir3 = Typingpool::Filer::Dir.create(dir3_path))
173
+ assert(File.exists? dir3_path)
174
+ assert_instance_of(Typingpool::Filer::Dir, dir3)
175
+ assert_nil(dir2 = Typingpool::Filer::Dir.named(File.basename(dir2_path), File.dirname(dir2_path)))
176
+ assert(dir3 = Typingpool::Filer::Dir.named(File.basename(dir3_path), File.dirname(dir3_path)))
177
+ assert_instance_of(Typingpool::Filer::Dir, dir3)
178
+ assert_equal(dir3_path, dir3.to_s)
179
+ assert_equal(dir3_path, dir3.to_str)
180
+ assert(filer = dir3.file('doesntexist'))
181
+ end #in_temp_dir
182
+ assert(filer = dir.file('vcr', 'tp-collect-1.yml'))
183
+ assert(File.exists? filer.path)
184
+ assert_instance_of(Typingpool::Filer, filer)
185
+ assert(csv = dir.file('tp_collect_sandbox-assignment.csv').as(:csv))
186
+ assert(File.exists? csv.path)
187
+ assert_instance_of(Typingpool::Filer::CSV, csv)
188
+ dir4 = Typingpool::Filer::Dir.new(audio_dir)
189
+ assert(audio = dir4.file('mp3', 'interview.1.mp3').as(:audio))
190
+ assert(File.exists? audio.path)
191
+ assert_instance_of(Typingpool::Filer::Audio, audio)
192
+ assert(filers = dir.files)
193
+ assert(not(filers.empty?))
194
+ assert_kind_of(Typingpool::Filer, filers.first)
195
+ assert(File.exists? filers.first.path)
196
+ dir_files = Dir.entries(dir.path).map{|entry| File.join(dir.path, entry)}.select{|path| File.file?(path) }.reject{|path| File.basename(path).match(/^\./) }
197
+ assert_equal(dir_files.count, filers.count)
198
+ assert(dir5 = dir.subdir('vcr'))
199
+ assert(File.exists? dir5.path)
200
+ assert_instance_of(Typingpool::Filer::Dir, dir5)
201
+ end
202
+ end #TestFiler
@@ -0,0 +1,168 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(File.dirname($0)), 'lib')
4
+
5
+ require 'typingpool'
6
+ require 'typingpool/test'
7
+ require 'tmpdir'
8
+ require 'fileutils'
9
+ require 'uri'
10
+
11
+ class TestProject < Typingpool::Test
12
+ def test_project_base_new
13
+ assert(project = Typingpool::Project.new(project_default[:title], dummy_config))
14
+ assert_instance_of(Typingpool::Project, project)
15
+ assert_equal(project_default[:title], project.name)
16
+ assert_equal(dummy_config.to_hash.to_s, project.config.to_hash.to_s)
17
+ assert_raise(Typingpool::Error::Argument::Format) do
18
+ Typingpool::Project.new('one/two', dummy_config)
19
+ end #assert_raise...
20
+ end
21
+
22
+ def test_project_base_bitrate
23
+ assert(project = Typingpool::Project.new(project_default[:title], dummy_config))
24
+ bitrate = rand(999) + 1
25
+ assert(project.bitrate = bitrate)
26
+ assert_equal(bitrate, project.bitrate)
27
+ bitrate = rand
28
+ assert_raises(Typingpool::Error::Argument::Format) do
29
+ project.bitrate = bitrate
30
+ end
31
+ end
32
+
33
+ def test_project_base_interval
34
+ assert(project = Typingpool::Project.new(project_default[:title], dummy_config))
35
+ assert(project.interval = 120)
36
+ assert_equal(120, project.interval)
37
+ assert_equal(1, set_and_return_interval(project, '01'))
38
+ assert_equal(1, set_and_return_interval(project, 1))
39
+ assert_equal("0:01", project.send(:interval_as_time_string))
40
+ assert_equal(2, set_and_return_interval(project, 2))
41
+ assert_equal("0:02", project.send(:interval_as_time_string))
42
+ assert_raises(Typingpool::Error::Argument::Format) do
43
+ set_and_return_interval(project, 2.1)
44
+ end
45
+ assert_equal(11, set_and_return_interval(project,11))
46
+ assert_equal("0:11", project.send(:interval_as_time_string))
47
+ assert_equal(1, set_and_return_interval(project, '00:01'))
48
+ assert_equal(60, set_and_return_interval(project, '01:00'))
49
+ assert_equal("1:00", project.send(:interval_as_time_string))
50
+ assert_equal(60, set_and_return_interval(project, '1:00'))
51
+ assert_equal(3552, set_and_return_interval(project, '59:12'))
52
+ assert_equal("59:12", project.send(:interval_as_time_string))
53
+ assert_equal(3680, set_and_return_interval(project, '61:20'))
54
+ assert_equal("1:01:20", project.send(:interval_as_time_string))
55
+ assert_raises(Typingpool::Error::Argument::Format) do
56
+ set_and_return_interval(project, '61:20.1')
57
+ end
58
+ assert_equal(7152, set_and_return_interval(project, '01:59:12'))
59
+ assert_equal("1:59:12", project.send(:interval_as_time_string))
60
+ assert_equal(7152, set_and_return_interval(project, '1:59:12'))
61
+ assert_raises(Typingpool::Error::Argument::Format) do
62
+ set_and_return_interval(project, '01:59:12.01')
63
+ end
64
+ assert_equal(43152, set_and_return_interval(project, '11:59:12'))
65
+ assert_equal("11:59:12", project.send(:interval_as_time_string))
66
+ end
67
+
68
+ def test_project_base_interval_as_mds
69
+ assert(project = Typingpool::Project.new(project_default[:title], dummy_config))
70
+ assert_equal('0.1', set_and_return_interval_as_mds(project, 1))
71
+ assert_equal('0.2', set_and_return_interval_as_mds(project, 2))
72
+ assert_equal('0.11', set_and_return_interval_as_mds(project,11))
73
+ assert_equal('1.0', set_and_return_interval_as_mds(project, '01:00'))
74
+ assert_equal('59.12', set_and_return_interval_as_mds(project, '59:12'))
75
+ assert_equal('61.20', set_and_return_interval_as_mds(project, '61:20'))
76
+ assert_equal('119.12', set_and_return_interval_as_mds(project, '1:59:12'))
77
+ assert_equal('719.12', set_and_return_interval_as_mds(project, '11:59:12'))
78
+ end
79
+
80
+ def test_project_base_local
81
+ config = dummy_config
82
+ config.transcripts = fixtures_dir
83
+ assert(project = Typingpool::Project.new('project', config))
84
+ assert_nil(project.local)
85
+ valid_transcript_dir = File.join(Typingpool::Utility.lib_dir, 'templates')
86
+ assert_kind_of(Typingpool::Project::Local, project.local(valid_transcript_dir))
87
+ config.transcripts = valid_transcript_dir
88
+ assert(project = Typingpool::Project.new('project', config))
89
+ assert_kind_of(Typingpool::Project::Local, project.local)
90
+ end
91
+
92
+ def test_project_base_remote
93
+ assert(project = Typingpool::Project.new(project_default[:title], dummy_config(1)))
94
+ assert_instance_of(Typingpool::Project::Remote::S3, project.remote)
95
+ assert(project = Typingpool::Project.new(project_default[:title], dummy_config(2)))
96
+ assert_instance_of(Typingpool::Project::Remote::SFTP, project.remote)
97
+ config = dummy_config(2)
98
+ config.to_hash.delete('sftp')
99
+ assert(project = Typingpool::Project.new(project_default[:title], config))
100
+ assert_raises(Typingpool::Error) do
101
+ project.remote
102
+ end
103
+ end
104
+
105
+ def test_project_base_create_local
106
+ config = dummy_config
107
+ in_temp_dir do |dir|
108
+ config.transcripts = dir
109
+ assert(project = Typingpool::Project.new(project_default[:title], config))
110
+ assert_nil(project.local)
111
+ assert_kind_of(Typingpool::Project::Local, project.create_local)
112
+ assert_kind_of(Typingpool::Project::Local, project.local)
113
+ end #in_temp_dir do
114
+ end
115
+
116
+ def test_project_base_create_assignment_csv
117
+ config = dummy_config
118
+ in_temp_dir do |dir|
119
+ config.transcripts = dir
120
+ assert(project = Typingpool::Project.new(project_default[:title], config))
121
+ project.create_local
122
+ assert(project.interval = '1:00')
123
+ assert_kind_of(Typingpool::Project::Local, project.local)
124
+ dummy_remote_files = (1..5).map{|n| "#{project_default[:title]}.#{n}" }
125
+ relative_path = ['data', 'assignment.csv']
126
+ voices = project_default[:voice].map do |voice|
127
+ spec = voice.split(/,\s*/)
128
+ hash = {:name => spec[0]}
129
+ hash[:description] = spec[1] if spec[1]
130
+ hash
131
+ end
132
+ assert(result = project.create_assignment_csv(:path => relative_path, :urls => dummy_remote_files, :unusual => project_default[:unusual], :voices => voices, ))
133
+ assert_includes(result, dir)
134
+ csv_file = File.join(dir, project_default[:title], *relative_path)
135
+ assert(File.exists? csv_file)
136
+ assert(File.file? csv_file)
137
+ assert(parsed = CSV.read(csv_file))
138
+ assignment_headers = parsed.shift
139
+ assert_equal(dummy_remote_files.count, parsed.count)
140
+ assert(chunk_index = assignment_headers.find_index('chunk'))
141
+ assert_equal('1:00', parsed.first[chunk_index].to_s)
142
+ assert(chunk_minutes_index = assignment_headers.find_index('chunk_minutes'))
143
+ assert_equal(1, parsed.first[chunk_minutes_index].to_i)
144
+ assert(chunk_seconds_index = assignment_headers.find_index('chunk_seconds'))
145
+ assert_empty(parsed.first[chunk_seconds_index].to_s)
146
+ assert(voices_count_index = assignment_headers.find_index('voices_count'))
147
+ assert_equal(voices.count, parsed.first[voices_count_index].to_i)
148
+ assert(voice_2_title_index = assignment_headers.find_index('voice2title'))
149
+ refute_empty(parsed.first[voice_2_title_index].to_s)
150
+ end #in_temp_dir do
151
+ end
152
+
153
+ def test_local_basename_from_url
154
+ url = ['http://example.com/dir/', URI.escape('Example Title With Spaces & Ampersand.html')].join
155
+ assert_match(url, /%20/)
156
+ #assert(basename = Typingpool::Project.local_basename_from_url.u)
157
+ end
158
+
159
+ def set_and_return_interval(project, interval)
160
+ project.interval = interval
161
+ project.interval
162
+ end
163
+
164
+ def set_and_return_interval_as_mds(project, interval)
165
+ project.interval = interval
166
+ project.interval_as_min_dot_sec
167
+ end
168
+ end #TestProject
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(File.dirname($0)), 'lib')
4
+
5
+ require 'typingpool'
6
+ require 'typingpool/test'
7
+
8
+ class TestProjectLocal < Typingpool::Test
9
+ def test_project_local_ours
10
+ assert(File.exists?(non_project_dir))
11
+ assert(File.directory?(non_project_dir))
12
+ refute(Typingpool::Project::Local.ours?(Typingpool::Filer::Dir.new(non_project_dir)))
13
+ assert(File.exists?(project_template_dir))
14
+ assert(Typingpool::Project::Local.ours?(Typingpool::Filer::Dir.new(project_template_dir)))
15
+ end
16
+
17
+ def test_project_local_named
18
+ assert_nil(Typingpool::Project::Local.named(project_default[:title], fixtures_dir))
19
+ assert_kind_of(Typingpool::Project::Local, local = Typingpool::Project::Local.named('project', project_template_dir_parent))
20
+ end
21
+
22
+ def test_project_local_valid_name
23
+ assert(Typingpool::Project::Local.valid_name?('hello, world'))
24
+ refute(Typingpool::Project::Local.valid_name?('hello / world'))
25
+ end
26
+
27
+ def test_project_local_create
28
+ in_temp_dir do |dir|
29
+ assert(local = create_project_local(dir))
30
+ assert(File.exists?(local.path))
31
+ assert(File.directory?(local.path))
32
+ assert_kind_of(Typingpool::Project::Local, local)
33
+ refute_nil(local.id)
34
+ end
35
+ end
36
+
37
+ def test_project_instance
38
+ in_temp_dir do |dir|
39
+ assert(local = create_project_local(dir))
40
+ refute_nil(local.id)
41
+ assert_raise(Typingpool::Error) do
42
+ local.create_id
43
+ end
44
+ assert_nil(local.subtitle)
45
+ [:subtitle].each do |accessor|
46
+ text = 'hello, world'
47
+ assert(local.send("#{accessor.to_s}=".to_sym, text))
48
+ assert_equal(text, local.send(accessor))
49
+ end #[].each do...
50
+ end #in_temp_dir do..
51
+ end
52
+
53
+ def create_project_local(dir)
54
+ Typingpool::Project::Local.create(project_default[:title], dir, project_template_dir)
55
+ end
56
+
57
+ def project_template_dir
58
+ File.join(project_template_dir_parent, 'project')
59
+ end
60
+
61
+ def project_template_dir_parent
62
+ File.join(Typingpool::Utility.lib_dir, 'templates')
63
+ end
64
+
65
+ def non_project_dir
66
+ File.join(fixtures_dir, 'vcr')
67
+ end
68
+ end #TestProjectLocal