typingpool 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +23 -0
- data/bin/tp-assign +240 -0
- data/bin/tp-collect +50 -0
- data/bin/tp-config +114 -0
- data/bin/tp-finish +101 -0
- data/bin/tp-make +169 -0
- data/bin/tp-review +175 -0
- data/lib/typingpool/amazon.rb +732 -0
- data/lib/typingpool/app.rb +634 -0
- data/lib/typingpool/config.rb +344 -0
- data/lib/typingpool/error.rb +22 -0
- data/lib/typingpool/filer.rb +396 -0
- data/lib/typingpool/project.rb +593 -0
- data/lib/typingpool/template.rb +175 -0
- data/lib/typingpool/templates/assignment/amazon-init.js +38 -0
- data/lib/typingpool/templates/assignment/interview/nameless.html.erb +13 -0
- data/lib/typingpool/templates/assignment/interview/noisy.html.erb +12 -0
- data/lib/typingpool/templates/assignment/interview/partials/voices.html.erb +10 -0
- data/lib/typingpool/templates/assignment/interview/phone.html.erb +12 -0
- data/lib/typingpool/templates/assignment/interview.html.erb +11 -0
- data/lib/typingpool/templates/assignment/main.css +20 -0
- data/lib/typingpool/templates/assignment/partials/entry.html.erb +19 -0
- data/lib/typingpool/templates/assignment/partials/footer.html.erb +3 -0
- data/lib/typingpool/templates/assignment/partials/header.html.erb +11 -0
- data/lib/typingpool/templates/assignment/partials/labeling-example.html.erb +4 -0
- data/lib/typingpool/templates/assignment/partials/labeling.html.erb +5 -0
- data/lib/typingpool/templates/assignment/partials/length-description.html.erb +6 -0
- data/lib/typingpool/templates/assignment/partials/voices.html.erb +10 -0
- data/lib/typingpool/templates/assignment/speech.html.erb +11 -0
- data/lib/typingpool/templates/config.yml +21 -0
- data/lib/typingpool/templates/project/audio/chunks/.empty_directory +0 -0
- data/lib/typingpool/templates/project/audio/originals/.empty_directory +0 -0
- data/lib/typingpool/templates/project/data/.empty_directory +0 -0
- data/lib/typingpool/templates/project/etc/ About these files - read me.txt +8 -0
- data/lib/typingpool/templates/project/etc/audio-compat.js +25 -0
- data/lib/typingpool/templates/project/etc/player/audio-player.js +4 -0
- data/lib/typingpool/templates/project/etc/player/license.txt +19 -0
- data/lib/typingpool/templates/project/etc/player/player.swf +0 -0
- data/lib/typingpool/templates/project/etc/transcript.css +49 -0
- data/lib/typingpool/templates/transcript.html.erb +23 -0
- data/lib/typingpool/test/fixtures/amazon-question-html.html +95 -0
- data/lib/typingpool/test/fixtures/amazon-question-url.txt +1 -0
- data/lib/typingpool/test/fixtures/audio/mp3/interview.1.mp3 +0 -0
- data/lib/typingpool/test/fixtures/audio/mp3/interview.2.mp3 +0 -0
- data/lib/typingpool/test/fixtures/audio/wma/VN620007.WMA +0 -0
- data/lib/typingpool/test/fixtures/audio/wma/VN620052.WMA +0 -0
- data/lib/typingpool/test/fixtures/config-1 +20 -0
- data/lib/typingpool/test/fixtures/config-2 +25 -0
- data/lib/typingpool/test/fixtures/not_yaml.txt +4 -0
- data/lib/typingpool/test/fixtures/template-2.html.erb +10 -0
- data/lib/typingpool/test/fixtures/template-3.html.erb +22 -0
- data/lib/typingpool/test/fixtures/template.html.erb +10 -0
- data/lib/typingpool/test/fixtures/tp_collect_id.txt +1 -0
- data/lib/typingpool/test/fixtures/tp_collect_sandbox-assignment.csv +8 -0
- data/lib/typingpool/test/fixtures/tp_review_id.txt +1 -0
- data/lib/typingpool/test/fixtures/tp_review_sandbox-assignment.csv +8 -0
- data/lib/typingpool/test/fixtures/transcript-chunks.csv +226 -0
- data/lib/typingpool/test/fixtures/utf8_transcript.txt +7 -0
- data/lib/typingpool/test/fixtures/vcr/tp-collect-1.yml +2712 -0
- data/lib/typingpool/test/fixtures/vcr/tp-collect-2.yml +2718 -0
- data/lib/typingpool/test/fixtures/vcr/tp-collect-3.yml +2768 -0
- data/lib/typingpool/test/fixtures/vcr/tp-review-1.yml +570 -0
- data/lib/typingpool/test/fixtures/vcr/tp-review-2.yml +351 -0
- data/lib/typingpool/test.rb +418 -0
- data/lib/typingpool/transcript.rb +181 -0
- data/lib/typingpool/utility.rb +272 -0
- data/lib/typingpool.rb +500 -0
- data/test/make_amazon_question_fixture.rb +24 -0
- data/test/make_tp_collect_fixture_1.rb +26 -0
- data/test/make_tp_collect_fixture_2.rb +16 -0
- data/test/make_tp_collect_fixture_3.rb +15 -0
- data/test/make_tp_collect_fixture_4.rb +17 -0
- data/test/make_tp_review_fixture_1.rb +26 -0
- data/test/make_tp_review_fixture_2.rb +30 -0
- data/test/make_transcript_chunks_fixture.rb +53 -0
- data/test/test_integration_script_1_tp_config.rb +108 -0
- data/test/test_integration_script_2_tp_make.rb +119 -0
- data/test/test_integration_script_3_tp_assign.rb +152 -0
- data/test/test_integration_script_4_tp_review.rb +72 -0
- data/test/test_integration_script_5_tp_collect.rb +44 -0
- data/test/test_integration_script_6_tp_finish.rb +123 -0
- data/test/test_unit_amazon.rb +153 -0
- data/test/test_unit_config.rb +94 -0
- data/test/test_unit_filer.rb +202 -0
- data/test/test_unit_project.rb +168 -0
- data/test/test_unit_project_local.rb +68 -0
- data/test/test_unit_project_remote.rb +157 -0
- data/test/test_unit_template.rb +111 -0
- data/test/test_unit_transcript.rb +77 -0
- metadata +234 -0
@@ -0,0 +1,175 @@
|
|
1
|
+
module Typingpool
|
2
|
+
#Model class that wraps ERB and adds a few Typingpool-specific
|
3
|
+
#capabilities: The ability to look in an array of search paths for a
|
4
|
+
#particular relative path, neccesary to support the Config#template
|
5
|
+
#dir on top of the built-in app template dir. Also makes it easy to
|
6
|
+
#pass in a hash and render the template against that hash, rather
|
7
|
+
#than against all the variables in the current namespace.
|
8
|
+
class Template
|
9
|
+
require 'erb'
|
10
|
+
class << self
|
11
|
+
#Constructor. Takes a relative template path and an optional
|
12
|
+
#config file. Default config is Config.file.
|
13
|
+
def from_config(path, config=Config.file)
|
14
|
+
validate_config(config)
|
15
|
+
new(path, look_in_from_config(config))
|
16
|
+
end
|
17
|
+
|
18
|
+
#private
|
19
|
+
|
20
|
+
def look_in_from_config(config)
|
21
|
+
look_in = [File.join(Utility.lib_dir, 'templates'), '']
|
22
|
+
look_in.unshift(config.templates) if config.templates
|
23
|
+
look_in
|
24
|
+
end
|
25
|
+
|
26
|
+
def validate_config(config)
|
27
|
+
if config.templates
|
28
|
+
File.exists?(config.templates) or raise Error::File::NotExists, "No such templates dir: #{config.templates}"
|
29
|
+
File.directory?(config.templates) or raise Error::File::NotExists, "Templates dir not a directory: #{config.templates}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end #class << self
|
33
|
+
|
34
|
+
#An array of base paths to be searched when we're given a relative
|
35
|
+
#path to the template. Normally this includes the user's
|
36
|
+
#Config#template attribute, if any, followed by the built-in app
|
37
|
+
#template dir.
|
38
|
+
attr_reader :look_in
|
39
|
+
|
40
|
+
#Constructor. Takes a relative path and an array of base paths to
|
41
|
+
#search for relative template paths. See look_in docs. Template
|
42
|
+
#should be an ERB template.
|
43
|
+
def initialize(path, look_in)
|
44
|
+
@path = path
|
45
|
+
@look_in = look_in
|
46
|
+
full_path or raise Error, "Could not find template path '#{path}' in #{look_in.join(',')}"
|
47
|
+
end
|
48
|
+
|
49
|
+
#Takes a hash to pass to an ERB template and returns the text from
|
50
|
+
#rendering the template against that hash (the hash becomes the
|
51
|
+
#top-level namespace of the template, so the keys are accessed
|
52
|
+
#just as you'd normally access a variable in an ERB template).
|
53
|
+
def render(hash)
|
54
|
+
render_with_binding(Env.new(hash, self).get_binding)
|
55
|
+
end
|
56
|
+
|
57
|
+
#Like render, but takes a binding instead of hash
|
58
|
+
def render_with_binding(binding)
|
59
|
+
ERB.new(read, nil, '<>').result(binding)
|
60
|
+
end
|
61
|
+
|
62
|
+
#Returns the raw text of the template, unrendered.
|
63
|
+
def read
|
64
|
+
IO.read(full_path)
|
65
|
+
end
|
66
|
+
|
67
|
+
#Returns the path to the template after searching the various
|
68
|
+
#look_in dirs for the relative path. Returns nil if the template
|
69
|
+
#cannot be located.
|
70
|
+
def full_path
|
71
|
+
look_in.each do |dir|
|
72
|
+
extensions.each do |ext|
|
73
|
+
path = File.join(dir, [@path, ext].join)
|
74
|
+
if File.exists?(path) && File.file?(path)
|
75
|
+
return path
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
return
|
80
|
+
end
|
81
|
+
|
82
|
+
protected
|
83
|
+
|
84
|
+
def extensions
|
85
|
+
['.html.erb', '.erb', '']
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
#A Template::Assignment works just like a regular template, except
|
90
|
+
#that within each transcript dir (Config#transcript and the
|
91
|
+
#built-in app template dir) we search within a subdir called
|
92
|
+
#'assignment' first, then, after all the 'assignment' subdirs have
|
93
|
+
#been search, we look in the original template dirs.
|
94
|
+
class Assignment < Template
|
95
|
+
def self.look_in_from_config(*args)
|
96
|
+
look_in = super(*args)
|
97
|
+
look_in.unshift(look_in.reject{|dir| dir.empty? }.map{|dir| File.join(dir, 'assignment') })
|
98
|
+
look_in.flatten
|
99
|
+
end
|
100
|
+
end #Assignment
|
101
|
+
|
102
|
+
#This subclass provides two utility methods to all templates:
|
103
|
+
#read, for including the text of another template, and render, for
|
104
|
+
#rendering another template. Read takes a relative path,
|
105
|
+
#documented below. Render is passed the same hash as the parent
|
106
|
+
#template, merged with an optional override hash, as documented
|
107
|
+
#below.
|
108
|
+
#
|
109
|
+
#This subclass also makes it easier to use a hash as the top-level
|
110
|
+
#variable namespace when rendering ERB templates.
|
111
|
+
class Env
|
112
|
+
|
113
|
+
#Construtor. Takes a hash to be passed to the template and a
|
114
|
+
#template (ERB).
|
115
|
+
def initialize(hash, template)
|
116
|
+
@hash = hash
|
117
|
+
@template = template
|
118
|
+
end
|
119
|
+
|
120
|
+
#Method passed into each template. Takes a relative path and
|
121
|
+
#returns the text of the file at that path.
|
122
|
+
#
|
123
|
+
#The relative path is resolved as in look_in above, with the
|
124
|
+
#following difference: the current directory and each parent
|
125
|
+
#directory of the active template is searched first, up to the
|
126
|
+
#root transcript directory (either Config#template, the built-in
|
127
|
+
#app template dir, or any dir that has been manually added to
|
128
|
+
#look_in).
|
129
|
+
def read(path)
|
130
|
+
@template.class.new(path, localized_look_in).read.strip
|
131
|
+
end
|
132
|
+
|
133
|
+
#Method passed into each template. Takes a reltive path and
|
134
|
+
#returns the *rendered* text of the ERB template at that
|
135
|
+
#path. Can also take an optional hash, which will be merged into
|
136
|
+
#the parent template's hash and passed to the included
|
137
|
+
#template. If the optional hash it not passed, the parent
|
138
|
+
#template's hash will be passed to the included template
|
139
|
+
#unmodified.
|
140
|
+
#
|
141
|
+
#The relative path is resolved as described in the docs for
|
142
|
+
#Template::Env#read.
|
143
|
+
def render(path, hash={})
|
144
|
+
@template.class.new(path, localized_look_in).render(@hash.merge(hash)).strip
|
145
|
+
end
|
146
|
+
|
147
|
+
def get_binding
|
148
|
+
binding()
|
149
|
+
end
|
150
|
+
|
151
|
+
protected
|
152
|
+
|
153
|
+
def localized_look_in
|
154
|
+
look_in = []
|
155
|
+
path = @template.full_path
|
156
|
+
until @template.look_in.include? path = File.dirname(path)
|
157
|
+
look_in.push(path)
|
158
|
+
end
|
159
|
+
look_in.push(path, (@template.look_in - [path])).flatten
|
160
|
+
end
|
161
|
+
|
162
|
+
def method_missing(key, value=nil)
|
163
|
+
if value
|
164
|
+
key = key.to_s.sub(/=$/, '')
|
165
|
+
@hash[key.to_sym] = value
|
166
|
+
end
|
167
|
+
if @hash.has_key? key
|
168
|
+
@hash[key]
|
169
|
+
elsif @hash.has_key? key.to_s
|
170
|
+
@hash[key.to_s]
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end #Env
|
174
|
+
end #Template
|
175
|
+
end #Typingpool
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
function setAmazonAssignmentId() {
|
3
|
+
var params = window.location.search.substring(1).split('&');
|
4
|
+
var i = 0;
|
5
|
+
for (i = 0; i < params.length; i++) {
|
6
|
+
var param = params[i].split('=');
|
7
|
+
var key = param[0];
|
8
|
+
var value = param[1];
|
9
|
+
if (key === 'assignmentId') {
|
10
|
+
if (value === 'ASSIGNMENT_ID_NOT_AVAILABLE') {
|
11
|
+
var input = document.getElementById('disable_on_preview');
|
12
|
+
if (input) {
|
13
|
+
input.setAttribute('disabled', 'disabled');
|
14
|
+
var span = document.createElement('span');
|
15
|
+
span.setAttribute('class', 'disabled_message');
|
16
|
+
span.appendChild(document.createTextNode(' Disabled because you are previewing this HIT.'));
|
17
|
+
var inputSibling = input.nextSibling;
|
18
|
+
if (inputSibling) {
|
19
|
+
input.parentNode.insertBefore(span, inputSibling);
|
20
|
+
}
|
21
|
+
else {
|
22
|
+
input.parentNode.appendChild(span);
|
23
|
+
}
|
24
|
+
}
|
25
|
+
}
|
26
|
+
else {
|
27
|
+
document.getElementById('assignmentId').value = value;
|
28
|
+
}
|
29
|
+
}
|
30
|
+
else if (key === 'turkSubmitTo') {
|
31
|
+
var form = document.getElementById('turkForm');
|
32
|
+
if (form) {
|
33
|
+
form.setAttribute('action', decodeURIComponent(value) + '/mturk/externalSubmit');
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<% self.title = 'Transcribe MP3 of ' + render('partials/length-description') + ' interview' %>
|
2
|
+
<%= render 'partials/header' %>
|
3
|
+
<p id="description">I split up an interview into <%= render 'partials/length-description' %> parts, you transcribe the audio for one <%= render 'partials/length-description' %> part.</p>
|
4
|
+
|
5
|
+
<h3>Labeling</h3>
|
6
|
+
<p>Label each speaker with a gender and, if needed, a number. Use the labels "MALE:", "FEMALE:", "MALE 2:", "FEMALE 2:", "MALE 3:", "FEMALE 3:", etc., like this:
|
7
|
+
|
8
|
+
<% self.voice1 = 'MALE' %><% self.voice2 = 'MALE 2' %>
|
9
|
+
<%= render 'partials/labeling-example' %>
|
10
|
+
|
11
|
+
<%= render 'partials/entry' %>
|
12
|
+
|
13
|
+
<%= render 'partials/footer' %>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<% self.title = 'Transcribe MP3 of ' + render('partials/length-description') + ' interview (background noise)' %>
|
2
|
+
<%= render 'partials/header' %>
|
3
|
+
<p id="description">I split up an interview into <%= render 'partials/length-description' %> parts, you transcribe the audio for one <%= render 'partials/length-description' %> part. There is background noise.</p>
|
4
|
+
|
5
|
+
<%= render 'partials/voices' %>
|
6
|
+
|
7
|
+
<%= render 'partials/labeling' %>
|
8
|
+
|
9
|
+
<% self.important = ['The interview takes place in a noisy environment with audible chatter. You should transcribe <strong>only</strong> the comments from the people involved in the interview.'] %>
|
10
|
+
<%= render 'partials/entry' %>
|
11
|
+
|
12
|
+
<%= render 'partials/footer' %>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<% if voices_count > 0 %>
|
2
|
+
<h3>Names</h3>
|
3
|
+
<p>There are <%= voices_count %> speakers:
|
4
|
+
<ul>
|
5
|
+
<% 1.upto(voices_count) do |i| %>
|
6
|
+
<li><strong><%= send("voice#{i}") %></strong><% if send("voice#{i}title").to_s.empty? %><% if i == 1 %>, the person asking the questions<% end %><% else %>, <%= send("voice#{i}title") %><% end %>.
|
7
|
+
<% end %>
|
8
|
+
</ul>
|
9
|
+
<% end %>
|
10
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<% self.title = 'Transcribe MP3 of ' + render('partials/length-description') + ' phone call' %>
|
2
|
+
<%= render 'partials/header' %>
|
3
|
+
|
4
|
+
<p id="description">I split up a telephone conversation into <%= render 'partials/length-description' %> parts, you transcribe the audio for one <%= render 'partials/length-description' %> part.</p>
|
5
|
+
|
6
|
+
<h3>Labeling</h3>
|
7
|
+
<p>There are two speakers. The person asking the questions is <strong><%= voice1 %></strong>. The person answering the questions, on the distant end of the telephone line, is <strong><%= voice2 %></strong><% unless voice2title.to_s.empty? %>, <%= voice2title %><% end %>. Label each speaker with a colon, like this:</p>
|
8
|
+
<%= render 'partials/labeling-example' %>
|
9
|
+
|
10
|
+
<%= render 'partials/entry' %>
|
11
|
+
|
12
|
+
<%= render 'partials/footer' %>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<% self.title = 'Transcribe MP3 of ' + render('partials/length-description') + ' interview' %>
|
2
|
+
<%= render 'partials/header' %>
|
3
|
+
<p id="description">I split up an interview into <%= render 'partials/length-description' %> parts, you transcribe the audio for one <%= render 'partials/length-description' %> part.</p>
|
4
|
+
|
5
|
+
<%= render 'interview/partials/voices' %>
|
6
|
+
|
7
|
+
<%= render 'partials/labeling' %>
|
8
|
+
|
9
|
+
<%= render 'partials/entry' %>
|
10
|
+
|
11
|
+
<%= render 'partials/footer' %>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
body {
|
3
|
+
color: black;
|
4
|
+
background: white;
|
5
|
+
font-family: Georgia, Times, "Times New Roman", serif;
|
6
|
+
}
|
7
|
+
|
8
|
+
h1, h2, h3, h4, h5, .disabled_message {
|
9
|
+
font-family: Helvetica, Arial, sans-serif;
|
10
|
+
}
|
11
|
+
|
12
|
+
p {
|
13
|
+
line-height: 140%;
|
14
|
+
}
|
15
|
+
|
16
|
+
.disabled_message {
|
17
|
+
color: gray;
|
18
|
+
font-size: small;
|
19
|
+
}
|
20
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<h3>Important</h3>
|
2
|
+
<ul>
|
3
|
+
<li><strong>Unusual words</strong> you might hear: <strong><%= unusual %></strong></li>
|
4
|
+
<li><strong>Don't</strong> type short or filler responses like <strong>uh</strong>, <strong>you know</strong>, <strong>I mean</strong>, <strong>ya</strong>, <strong>OK</strong>, <strong>Right</strong>, <strong>I see</strong>, <strong>Oh</strong>, and <strong>Ah</strong>.</li>
|
5
|
+
<li>For <strong>inaudible</strong> words, type <strong>??</strong></li>
|
6
|
+
<% if important %>
|
7
|
+
<% important.each do |note| %>
|
8
|
+
<li><%= note %></li>
|
9
|
+
<% end %>
|
10
|
+
<% end %>
|
11
|
+
</ul>
|
12
|
+
|
13
|
+
<h3>Instructions</h3>
|
14
|
+
<p>Please <strong>transcribe</strong> this <%= render 'length-description' %> MP3: <a target="_blank" href="<%= audio_url %>"><%= audio_url %></a></p>
|
15
|
+
<form action="https://www.mturk.com/mturk/externalSubmit" method="POST" id="turkForm">
|
16
|
+
<p><input name="assignmentId" type="hidden" id="assignmentId"><input name="typingpool_url" type="hidden" value="<%= audio_url %>" /><input name="typingpool_project_id" type="hidden" value="<%= project_id %>" />Enter your transcription below:</p>
|
17
|
+
<p><textarea name="transcription" cols="80" rows="15"></textarea></p>
|
18
|
+
<p><input type="submit" value="Submit" id="disable_on_preview"></p>
|
19
|
+
</form>
|
@@ -0,0 +1,4 @@
|
|
1
|
+
<blockquote>
|
2
|
+
<p><%= voice1 %>: Do you think it was a mistake to set up Saturn as its own Corporation?</p>
|
3
|
+
<p><%= voice2 %>: In hindsight, you can look at it that way, and I think a lot of people do, quite honestly. They don't think GM needed another brand. But at the time this was all developed. It sure seemed like a good idea.</p>
|
4
|
+
</blockquote>
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<% length_description = [] %>
|
2
|
+
<% length_description.push("#{chunk_hours} hour") if chunk_hours %>
|
3
|
+
<% length_description.push("#{chunk_minutes} minute") if chunk_minutes %>
|
4
|
+
<% length_description.push("#{chunk_seconds} second") if chunk_seconds %>
|
5
|
+
<% length_description = length_description.join(' ') %>
|
6
|
+
<%= length_description %>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<% if voices_count > 0 %>
|
2
|
+
<h3>Names</h3>
|
3
|
+
<p>There are <%= voices_count %> speakers:
|
4
|
+
<ul>
|
5
|
+
<% 1.upto(voices_count) do |i| %>
|
6
|
+
<li><strong><%= send("voice#{i}") %></strong><% unless send("voice#{i}title").to_s.empty? %>, <%= send("voice#{i}title") %><% end %>.
|
7
|
+
<% end %>
|
8
|
+
</ul>
|
9
|
+
<% end %>
|
10
|
+
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<% self.title = 'Transcribe MP3 of ' + render('partials/length-description') + ' speech' %>
|
2
|
+
|
3
|
+
<%= render 'partials/header' %>
|
4
|
+
<p id="description">I split up a speech into <%= render 'partials/length-description' %> parts, you transcribe the audio for one <%= render 'partials/length-description' %> part.</p>
|
5
|
+
|
6
|
+
<h3>Formatting</h3>
|
7
|
+
<p>There should only be one speaker. Simply type what the person says. No need to label. If the speaker changes, type [SPEAKER CHANGES].</p>
|
8
|
+
|
9
|
+
<%= render 'partials/entry' %>
|
10
|
+
|
11
|
+
<%= render 'partials/footer' %>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
amazon:
|
2
|
+
key:
|
3
|
+
secret:
|
4
|
+
bucket:
|
5
|
+
transcripts:
|
6
|
+
templates:
|
7
|
+
cache: ~/.typingpool.cache
|
8
|
+
assign:
|
9
|
+
confirm: yes
|
10
|
+
reward: 0.75
|
11
|
+
deadline: 3h
|
12
|
+
approval: 1d
|
13
|
+
lifetime: 2d
|
14
|
+
qualify:
|
15
|
+
- approval_rate >= 95
|
16
|
+
- hits_approved >= 100
|
17
|
+
keywords:
|
18
|
+
- transcription
|
19
|
+
- audio
|
20
|
+
- mp3
|
21
|
+
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,8 @@
|
|
1
|
+
Deleting any files in this folder, in the data folder, or in the
|
2
|
+
audio/chunks folder will make it impossible for typingpool to function
|
3
|
+
properly.
|
4
|
+
|
5
|
+
You may safely delete files in the audio/originals folder.
|
6
|
+
|
7
|
+
You may safely edit transcript.html, etc/transcript.css, and
|
8
|
+
data/subtitle.txt.
|
@@ -0,0 +1,25 @@
|
|
1
|
+
function audioCompat() {
|
2
|
+
var audioTag = window.document.createElement('audio');
|
3
|
+
//Check for HTML5 audio tag compatibility
|
4
|
+
if (!(audioTag.canPlayType && audioTag.canPlayType('audio/mpeg') && (audioTag.canPlayType('audio/mpeg') != 'no'))){
|
5
|
+
//not compatible - fallback to Flash
|
6
|
+
AudioPlayer.setup("etc/player/player.swf", { width: 290 });
|
7
|
+
var audioTags = window.document.getElementsByTagName('audio');
|
8
|
+
var audioTagMeta = [];
|
9
|
+
for (var i = 0; i < audioTags.length; i++) {
|
10
|
+
var tag = audioTags[i];
|
11
|
+
if (! tag.id){
|
12
|
+
tag.id = '_typingpool_audio_' + i;
|
13
|
+
}
|
14
|
+
audioTagMeta.push({'id':tag.id, 'src':tag.src});
|
15
|
+
}
|
16
|
+
for (var i = 0; i < audioTagMeta.length; i++) {
|
17
|
+
var tagMeta = audioTagMeta[i];
|
18
|
+
AudioPlayer.embed(tagMeta.id, {'soundFile': tagMeta.src, 'noinfo': 'yes'});
|
19
|
+
}
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
if (window.addEventListener){ window.addEventListener('load', audioCompat, false) }
|
24
|
+
else if (document.addEventListener){ document.addEventListener('load', audioCompat, false) }
|
25
|
+
else if (window.attachEvent){ window.attachEvent('onload', audioCompat) }
|
@@ -0,0 +1,4 @@
|
|
1
|
+
/* SWFObject v2.2 <http://code.google.com/p/swfobject/>
|
2
|
+
is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
|
3
|
+
*/
|
4
|
+
var audioplayer_swfobject=function(){var d="undefined",R="object",s="Shockwave Flash",w="ShockwaveFlash.ShockwaveFlash",Q="application/x-shockwave-flash",r="SWFObjectExprInst",X="onreadystatechange",o=window,J=document,T=navigator,t=false,u=[H],O=[],n=[],i=[],L,q,e,b,j=false,A=false,N,g,M=true,m=function(){var AA=typeof J.getElementById!=d&&typeof J.getElementsByTagName!=d&&typeof J.createElement!=d,AH=T.userAgent.toLowerCase(),y=T.platform.toLowerCase(),AE=y?/win/.test(y):/win/.test(AH),AC=y?/mac/.test(y):/mac/.test(AH),AF=/webkit/.test(AH)?parseFloat(AH.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,x=!+"\v1",AG=[0,0,0],AB=null;if(typeof T.plugins!=d&&typeof T.plugins[s]==R){AB=T.plugins[s].description;if(AB&&!(typeof T.mimeTypes!=d&&T.mimeTypes[Q]&&!T.mimeTypes[Q].enabledPlugin)){t=true;x=false;AB=AB.replace(/^.*\s+(\S+\s+\S+$)/,"$1");AG[0]=parseInt(AB.replace(/^(.*)\..*$/,"$1"),10);AG[1]=parseInt(AB.replace(/^.*\.(.*)\s.*$/,"$1"),10);AG[2]=/[a-zA-Z]/.test(AB)?parseInt(AB.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof o.ActiveXObject!=d){try{var AD=new ActiveXObject(w);if(AD){AB=AD.GetVariable("$version");if(AB){x=true;AB=AB.split(" ")[1].split(",");AG=[parseInt(AB[0],10),parseInt(AB[1],10),parseInt(AB[2],10)]}}}catch(z){}}}return{w3:AA,pv:AG,wk:AF,ie:x,win:AE,mac:AC}}(),K=function(){if(!m.w3){return }if((typeof J.readyState!=d&&J.readyState=="complete")||(typeof J.readyState==d&&(J.getElementsByTagName("body")[0]||J.body))){F()}if(!j){if(typeof J.addEventListener!=d){J.addEventListener("DOMContentLoaded",F,false)}if(m.ie&&m.win){J.attachEvent(X,function(){if(J.readyState=="complete"){J.detachEvent(X,arguments.callee);F()}});if(o==top){(function(){if(j){return }try{J.documentElement.doScroll("left")}catch(x){setTimeout(arguments.callee,0);return }F()})()}}if(m.wk){(function(){if(j){return }if(!/loaded|complete/.test(J.readyState)){setTimeout(arguments.callee,0);return }F()})()}S(F)}}();function F(){if(j){return }try{var z=J.getElementsByTagName("body")[0].appendChild(c("span"));z.parentNode.removeChild(z)}catch(AA){return }j=true;var x=u.length;for(var y=0;y<x;y++){u[y]()}}function k(x){if(j){x()}else{u[u.length]=x}}function S(y){if(typeof o.addEventListener!=d){o.addEventListener("load",y,false)}else{if(typeof J.addEventListener!=d){J.addEventListener("load",y,false)}else{if(typeof o.attachEvent!=d){I(o,"onload",y)}else{if(typeof o.onload=="function"){var x=o.onload;o.onload=function(){x();y()}}else{o.onload=y}}}}}function H(){if(t){v()}else{h()}}function v(){var x=J.getElementsByTagName("body")[0];var AA=c(R);AA.setAttribute("type",Q);var z=x.appendChild(AA);if(z){var y=0;(function(){if(typeof z.GetVariable!=d){var AB=z.GetVariable("$version");if(AB){AB=AB.split(" ")[1].split(",");m.pv=[parseInt(AB[0],10),parseInt(AB[1],10),parseInt(AB[2],10)]}}else{if(y<10){y++;setTimeout(arguments.callee,10);return }}x.removeChild(AA);z=null;h()})()}else{h()}}function h(){var AG=O.length;if(AG>0){for(var AF=0;AF<AG;AF++){var y=O[AF].id;var AB=O[AF].callbackFn;var AA={success:false,id:y};if(m.pv[0]>0){var AE=C(y);if(AE){if(f(O[AF].swfVersion)&&!(m.wk&&m.wk<312)){W(y,true);if(AB){AA.success=true;AA.ref=Z(y);AB(AA)}}else{if(O[AF].expressInstall&&a()){var AI={};AI.data=O[AF].expressInstall;AI.width=AE.getAttribute("width")||"0";AI.height=AE.getAttribute("height")||"0";if(AE.getAttribute("class")){AI.styleclass=AE.getAttribute("class")}if(AE.getAttribute("align")){AI.align=AE.getAttribute("align")}var AH={};var x=AE.getElementsByTagName("param");var AC=x.length;for(var AD=0;AD<AC;AD++){if(x[AD].getAttribute("name").toLowerCase()!="movie"){AH[x[AD].getAttribute("name")]=x[AD].getAttribute("value")}}p(AI,AH,y,AB)}else{P(AE);if(AB){AB(AA)}}}}}else{W(y,true);if(AB){var z=Z(y);if(z&&typeof z.SetVariable!=d){AA.success=true;AA.ref=z}AB(AA)}}}}}function Z(AA){var x=null;var y=C(AA);if(y&&y.nodeName=="OBJECT"){if(typeof y.SetVariable!=d){x=y}else{var z=y.getElementsByTagName(R)[0];if(z){x=z}}}return x}function a(){return !A&&f("6.0.65")&&(m.win||m.mac)&&!(m.wk&&m.wk<312)}function p(AA,AB,x,z){A=true;e=z||null;b={success:false,id:x};var AE=C(x);if(AE){if(AE.nodeName=="OBJECT"){L=G(AE);q=null}else{L=AE;q=x}AA.id=r;if(typeof AA.width==d||(!/%$/.test(AA.width)&&parseInt(AA.width,10)<310)){AA.width="310"}if(typeof AA.height==d||(!/%$/.test(AA.height)&&parseInt(AA.height,10)<137)){AA.height="137"}J.title=J.title.slice(0,47)+" - Flash Player Installation";var AD=m.ie&&m.win?"ActiveX":"PlugIn",AC="MMredirectURL="+o.location.toString().replace(/&/g,"%26")+"&MMplayerType="+AD+"&MMdoctitle="+J.title;if(typeof AB.flashvars!=d){AB.flashvars+="&"+AC}else{AB.flashvars=AC}if(m.ie&&m.win&&AE.readyState!=4){var y=c("div");x+="SWFObjectNew";y.setAttribute("id",x);AE.parentNode.insertBefore(y,AE);AE.style.display="none";(function(){if(AE.readyState==4){AE.parentNode.removeChild(AE)}else{setTimeout(arguments.callee,10)}})()}U(AA,AB,x)}}function P(y){if(m.ie&&m.win&&y.readyState!=4){var x=c("div");y.parentNode.insertBefore(x,y);x.parentNode.replaceChild(G(y),x);y.style.display="none";(function(){if(y.readyState==4){y.parentNode.removeChild(y)}else{setTimeout(arguments.callee,10)}})()}else{y.parentNode.replaceChild(G(y),y)}}function G(AB){var AA=c("div");if(m.win&&m.ie){AA.innerHTML=AB.innerHTML}else{var y=AB.getElementsByTagName(R)[0];if(y){var AC=y.childNodes;if(AC){var x=AC.length;for(var z=0;z<x;z++){if(!(AC[z].nodeType==1&&AC[z].nodeName=="PARAM")&&!(AC[z].nodeType==8)){AA.appendChild(AC[z].cloneNode(true))}}}}}return AA}function U(AI,AG,y){var x,AA=C(y);if(m.wk&&m.wk<312){return x}if(AA){if(typeof AI.id==d){AI.id=y}if(m.ie&&m.win){var AH="";for(var AE in AI){if(AI[AE]!=Object.prototype[AE]){if(AE.toLowerCase()=="data"){AG.movie=AI[AE]}else{if(AE.toLowerCase()=="styleclass"){AH+=' class="'+AI[AE]+'"'}else{if(AE.toLowerCase()!="classid"){AH+=" "+AE+'="'+AI[AE]+'"'}}}}}var AF="";for(var AD in AG){if(AG[AD]!=Object.prototype[AD]){AF+='<param name="'+AD+'" value="'+AG[AD]+'" />'}}AA.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+AH+">"+AF+"</object>";n[n.length]=AI.id;x=C(AI.id)}else{var z=c(R);z.setAttribute("type",Q);for(var AC in AI){if(AI[AC]!=Object.prototype[AC]){if(AC.toLowerCase()=="styleclass"){z.setAttribute("class",AI[AC])}else{if(AC.toLowerCase()!="classid"){z.setAttribute(AC,AI[AC])}}}}for(var AB in AG){if(AG[AB]!=Object.prototype[AB]&&AB.toLowerCase()!="movie"){E(z,AB,AG[AB])}}AA.parentNode.replaceChild(z,AA);x=z}}return x}function E(z,x,y){var AA=c("param");AA.setAttribute("name",x);AA.setAttribute("value",y);z.appendChild(AA)}function Y(y){var x=C(y);if(x&&x.nodeName=="OBJECT"){if(m.ie&&m.win){x.style.display="none";(function(){if(x.readyState==4){B(y)}else{setTimeout(arguments.callee,10)}})()}else{x.parentNode.removeChild(x)}}}function B(z){var y=C(z);if(y){for(var x in y){if(typeof y[x]=="function"){y[x]=null}}y.parentNode.removeChild(y)}}function C(z){var x=null;try{x=J.getElementById(z)}catch(y){}return x}function c(x){return J.createElement(x)}function I(z,x,y){z.attachEvent(x,y);i[i.length]=[z,x,y]}function f(z){var y=m.pv,x=z.split(".");x[0]=parseInt(x[0],10);x[1]=parseInt(x[1],10)||0;x[2]=parseInt(x[2],10)||0;return(y[0]>x[0]||(y[0]==x[0]&&y[1]>x[1])||(y[0]==x[0]&&y[1]==x[1]&&y[2]>=x[2]))?true:false}function V(AC,y,AD,AB){if(m.ie&&m.mac){return }var AA=J.getElementsByTagName("head")[0];if(!AA){return }var x=(AD&&typeof AD=="string")?AD:"screen";if(AB){N=null;g=null}if(!N||g!=x){var z=c("style");z.setAttribute("type","text/css");z.setAttribute("media",x);N=AA.appendChild(z);if(m.ie&&m.win&&typeof J.styleSheets!=d&&J.styleSheets.length>0){N=J.styleSheets[J.styleSheets.length-1]}g=x}if(m.ie&&m.win){if(N&&typeof N.addRule==R){N.addRule(AC,y)}}else{if(N&&typeof J.createTextNode!=d){N.appendChild(J.createTextNode(AC+" {"+y+"}"))}}}function W(z,x){if(!M){return }var y=x?"visible":"hidden";if(j&&C(z)){C(z).style.visibility=y}else{V("#"+z,"visibility:"+y)}}function l(y){var z=/[\\\"<>\.;]/;var x=z.exec(y)!=null;return x&&typeof encodeURIComponent!=d?encodeURIComponent(y):y}var D=function(){if(m.ie&&m.win){window.attachEvent("onunload",function(){var AC=i.length;for(var AB=0;AB<AC;AB++){i[AB][0].detachEvent(i[AB][1],i[AB][2])}var z=n.length;for(var AA=0;AA<z;AA++){Y(n[AA])}for(var y in m){m[y]=null}m=null;for(var x in audioplayer_swfobject){audioplayer_swfobject[x]=null}audioplayer_swfobject=null})}}();return{registerObject:function(AB,x,AA,z){if(m.w3&&AB&&x){var y={};y.id=AB;y.swfVersion=x;y.expressInstall=AA;y.callbackFn=z;O[O.length]=y;W(AB,false)}else{if(z){z({success:false,id:AB})}}},getObjectById:function(x){if(m.w3){return Z(x)}},embedSWF:function(AB,AH,AE,AG,y,AA,z,AD,AF,AC){var x={success:false,id:AH};if(m.w3&&!(m.wk&&m.wk<312)&&AB&&AH&&AE&&AG&&y){W(AH,false);k(function(){AE+="";AG+="";var AJ={};if(AF&&typeof AF===R){for(var AL in AF){AJ[AL]=AF[AL]}}AJ.data=AB;AJ.width=AE;AJ.height=AG;var AM={};if(AD&&typeof AD===R){for(var AK in AD){AM[AK]=AD[AK]}}if(z&&typeof z===R){for(var AI in z){if(typeof AM.flashvars!=d){AM.flashvars+="&"+AI+"="+z[AI]}else{AM.flashvars=AI+"="+z[AI]}}}if(f(y)){var AN=U(AJ,AM,AH);if(AJ.id==AH){W(AH,true)}x.success=true;x.ref=AN}else{if(AA&&a()){AJ.data=AA;p(AJ,AM,AH,AC);return }else{W(AH,true)}}if(AC){AC(x)}})}else{if(AC){AC(x)}}},switchOffAutoHideShow:function(){M=false},ua:m,getFlashPlayerVersion:function(){return{major:m.pv[0],minor:m.pv[1],release:m.pv[2]}},hasFlashPlayerVersion:f,createSWF:function(z,y,x){if(m.w3){return U(z,y,x)}else{return undefined}},showExpressInstall:function(z,AA,x,y){if(m.w3&&a()){p(z,AA,x,y)}},removeSWF:function(x){if(m.w3){Y(x)}},createCSS:function(AA,z,y,x){if(m.w3){V(AA,z,y,x)}},addDomLoadEvent:k,addLoadEvent:S,getQueryParamValue:function(AA){var z=J.location.search||J.location.hash;if(z){if(/\?/.test(z)){z=z.split("?")[1]}if(AA==null){return l(z)}var y=z.split("&");for(var x=0;x<y.length;x++){if(y[x].substring(0,y[x].indexOf("="))==AA){return l(y[x].substring((y[x].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(A){var x=C(r);if(x&&L){x.parentNode.replaceChild(L,x);if(q){W(q,true);if(m.ie&&m.win){L.style.display="block"}}if(e){e(b)}}A=false}}}}();var AudioPlayer=function(){var H=[];var D;var F="";var A={};var E=-1;var G="9";function B(I){if(document.all&&!window[I]){for(var J=0;J<document.forms.length;J++){if(document.forms[J][I]){return document.forms[J][I];break}}}return document.all?window[I]:document[I]}function C(I,J,K){B(I).addListener(J,K)}return{setup:function(J,I){F=J;A=I;if(audioplayer_swfobject.hasFlashPlayerVersion(G)){audioplayer_swfobject.switchOffAutoHideShow();audioplayer_swfobject.createCSS("p.audioplayer_container span","visibility:hidden;height:24px;overflow:hidden;padding:0;border:none;")}},getPlayer:function(I){return B(I)},addListener:function(I,J,K){C(I,J,K)},embed:function(I,K){var N={};var L;var J={};var O={};var M={};for(L in A){N[L]=A[L]}for(L in K){N[L]=K[L]}if(N.transparentpagebg=="yes"){J.bgcolor="#FFFFFF";J.wmode="transparent"}else{if(N.pagebg){J.bgcolor="#"+N.pagebg}J.wmode="opaque"}J.menu="false";for(L in N){if(L=="pagebg"||L=="width"||L=="transparentpagebg"){continue}O[L]=N[L]}M.name=I;M.style="outline: none";O.playerID=I;audioplayer_swfobject.embedSWF(F,I,N.width.toString(),"24",G,false,O,J,M);H.push(I)},syncVolumes:function(I,K){E=K;for(var J=0;J<H.length;J++){if(H[J]!=I){B(H[J]).setVolume(E)}}},activate:function(I,J){if(D&&D!=I){B(D).close()}D=I},load:function(K,I,L,J){B(K).load(I,L,J)},close:function(I){B(I).close();if(I==D){D=null}},open:function(I,J){if(J==undefined){J=1}B(I).open(J==undefined?0:J-1)},getVolume:function(I){return E}}}();
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2010 Martin Laine
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
Binary file
|
@@ -0,0 +1,49 @@
|
|
1
|
+
body {
|
2
|
+
color: black;
|
3
|
+
background: white;
|
4
|
+
font-family: Georgia, Times, "Times New Roman", serif;
|
5
|
+
}
|
6
|
+
|
7
|
+
h1, h2, h3, h4, h5 {
|
8
|
+
font-family: Helvetica, Arial, sans-serif;
|
9
|
+
font-weight: normal;
|
10
|
+
}
|
11
|
+
|
12
|
+
h1{
|
13
|
+
margin-bottom: 0.33em
|
14
|
+
}
|
15
|
+
|
16
|
+
h2{
|
17
|
+
font-size: 1.125em;
|
18
|
+
margin-top: 0.125em;
|
19
|
+
}
|
20
|
+
|
21
|
+
h3, h4 {
|
22
|
+
color:gray;
|
23
|
+
font-size: 0.875em;
|
24
|
+
}
|
25
|
+
|
26
|
+
h3{
|
27
|
+
margin-top: 0.5em;
|
28
|
+
margin-bottom: 0.125em;
|
29
|
+
line-height: auto;
|
30
|
+
}
|
31
|
+
|
32
|
+
h4{
|
33
|
+
margin-top: 0.125em;
|
34
|
+
margin-bottom: 0.125em;
|
35
|
+
}
|
36
|
+
|
37
|
+
p{
|
38
|
+
margin-top: 0.125em;
|
39
|
+
margin-bottom: 2em;
|
40
|
+
line-height: 140%;
|
41
|
+
}
|
42
|
+
|
43
|
+
a[href], a[href]:link {
|
44
|
+
color: gray;
|
45
|
+
}
|
46
|
+
|
47
|
+
a[href]:visited {
|
48
|
+
color: silver;
|
49
|
+
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8" />
|
5
|
+
<title><%= transcript.title %> transcript</title>
|
6
|
+
<link rel="stylesheet" href="etc/transcript.css">
|
7
|
+
<script src="etc/player/audio-player.js" type="text/javascript"></script>
|
8
|
+
<script src="etc/audio-compat.js" type="text/javascript"></script>
|
9
|
+
</head>
|
10
|
+
<body>
|
11
|
+
<h1><%= transcript.title %> transcript</h1>
|
12
|
+
<h2><% if transcript.subtitle %><%= transcript.subtitle %><% end %></h2>
|
13
|
+
|
14
|
+
<% transcript.sort.each do |chunk| %>
|
15
|
+
|
16
|
+
<h3><%= chunk.offset %></h3>
|
17
|
+
<h3><audio src="audio/chunks/<%= chunk.filename_local %>" preload="none" controls></audio></h3>
|
18
|
+
<h4><a href="https://requester.mturk.com/bulk/workers/<%= chunk.worker %>">Worker</a> | <a href="https://requester.mturk.com/mturk/manageHIT?HITId=<%= chunk.hit %>&viewableEditPane=manageHIT_downloadResults">HIT</a></h4>
|
19
|
+
|
20
|
+
<p><%= chunk.body_as_html %>
|
21
|
+
<% end %>
|
22
|
+
</body>
|
23
|
+
</html>
|