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