mturk 1.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +2 -0
- data/.gemtest +0 -0
- data/History.md +105 -0
- data/LICENSE.txt +202 -0
- data/Manifest.txt +72 -0
- data/NOTICE.txt +4 -0
- data/README.md +100 -0
- data/Rakefile +33 -0
- data/bin/mturk +9 -0
- data/lib/amazon/util.rb +10 -0
- data/lib/amazon/util/binder.rb +48 -0
- data/lib/amazon/util/data_reader.rb +169 -0
- data/lib/amazon/util/filter_chain.rb +79 -0
- data/lib/amazon/util/hash_nesting.rb +93 -0
- data/lib/amazon/util/lazy_results.rb +59 -0
- data/lib/amazon/util/logging.rb +23 -0
- data/lib/amazon/util/paginated_iterator.rb +70 -0
- data/lib/amazon/util/proactive_results.rb +116 -0
- data/lib/amazon/util/threadpool.rb +129 -0
- data/lib/amazon/util/user_data_store.rb +100 -0
- data/lib/amazon/webservices/mechanical_turk.rb +123 -0
- data/lib/amazon/webservices/mechanical_turk_requester.rb +285 -0
- data/lib/amazon/webservices/mturk/mechanical_turk_error_handler.rb +153 -0
- data/lib/amazon/webservices/mturk/question_generator.rb +58 -0
- data/lib/amazon/webservices/util/amazon_authentication_relay.rb +72 -0
- data/lib/amazon/webservices/util/command_line.rb +155 -0
- data/lib/amazon/webservices/util/convenience_wrapper.rb +90 -0
- data/lib/amazon/webservices/util/filter_proxy.rb +45 -0
- data/lib/amazon/webservices/util/mock_transport.rb +70 -0
- data/lib/amazon/webservices/util/request_signer.rb +42 -0
- data/lib/amazon/webservices/util/rest_transport.rb +120 -0
- data/lib/amazon/webservices/util/soap_simplifier.rb +48 -0
- data/lib/amazon/webservices/util/soap_transport.rb +20 -0
- data/lib/amazon/webservices/util/soap_transport_header_handler.rb +27 -0
- data/lib/amazon/webservices/util/unknown_result_exception.rb +27 -0
- data/lib/amazon/webservices/util/validation_exception.rb +55 -0
- data/lib/amazon/webservices/util/xml_simplifier.rb +61 -0
- data/lib/mturk.rb +19 -0
- data/lib/mturk/version.rb +6 -0
- data/run_rcov.sh +1 -0
- data/samples/best_image/BestImage.rb +61 -0
- data/samples/best_image/best_image.properties +39 -0
- data/samples/best_image/best_image.question +82 -0
- data/samples/blank_slate/BlankSlate.rb +63 -0
- data/samples/blank_slate/BlankSlate_multithreaded.rb +67 -0
- data/samples/helloworld/MTurkHelloWorld.rb +56 -0
- data/samples/helloworld/mturk.yml +8 -0
- data/samples/review_policy/ReviewPolicy.rb +139 -0
- data/samples/review_policy/review_policy.question +30 -0
- data/samples/reviewer/Reviewer.rb +103 -0
- data/samples/reviewer/mturk.yml +8 -0
- data/samples/simple_survey/SimpleSurvey.rb +98 -0
- data/samples/simple_survey/simple_survey.question +30 -0
- data/samples/site_category/SiteCategory.rb +87 -0
- data/samples/site_category/externalpage.htm +71 -0
- data/samples/site_category/site_category.input +6 -0
- data/samples/site_category/site_category.properties +56 -0
- data/samples/site_category/site_category.question +9 -0
- data/test/mturk/test_changehittypeofhit.rb +130 -0
- data/test/mturk/test_error_handler.rb +403 -0
- data/test/mturk/test_mechanical_turk_requester.rb +178 -0
- data/test/mturk/test_mock_mechanical_turk_requester.rb +205 -0
- data/test/test_mturk.rb +21 -0
- data/test/unit/test_binder.rb +89 -0
- data/test/unit/test_data_reader.rb +135 -0
- data/test/unit/test_exceptions.rb +32 -0
- data/test/unit/test_hash_nesting.rb +99 -0
- data/test/unit/test_lazy_results.rb +89 -0
- data/test/unit/test_mock_transport.rb +132 -0
- data/test/unit/test_paginated_iterator.rb +58 -0
- data/test/unit/test_proactive_results.rb +108 -0
- data/test/unit/test_question_generator.rb +55 -0
- data/test/unit/test_threadpool.rb +50 -0
- data/test/unit/test_user_data_store.rb +80 -0
- metadata +225 -0
- metadata.gz.sig +0 -0
data/Rakefile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
require './lib/mturk/version.rb'
|
6
|
+
|
7
|
+
Hoe.spec 'mturk' do
|
8
|
+
self.version = MTurk::VERSION.dup
|
9
|
+
developer 'David J Parrott', 'valthon@nothlav.net'
|
10
|
+
license 'APLv2'
|
11
|
+
extra_deps << ['highline','>= 1.2.7']
|
12
|
+
extra_deps << ['nokogiri','>= 1.4']
|
13
|
+
need_tar
|
14
|
+
need_zip
|
15
|
+
|
16
|
+
self.summary = 'Ruby libraries for working with Mechanical Turk'
|
17
|
+
self.email = 'requester@mturk.com'
|
18
|
+
self.urls = ["https://requester.mturk.com/"]
|
19
|
+
end
|
20
|
+
|
21
|
+
task :gitversion do
|
22
|
+
gv = `git describe --dirty`.chomp.gsub(/^v/,'').gsub('-','.')
|
23
|
+
File.open('lib/mturk/version.rb', File::WRONLY | File::CREAT | File::TRUNC ) do |f|
|
24
|
+
f << "# Copyright:: Copyright (c) 2007-2015 Amazon Technologies, Inc.\n"
|
25
|
+
f << "# License:: Apache License, Version 2.0\n"
|
26
|
+
f << "\n"
|
27
|
+
f << "module MTurk\n"
|
28
|
+
f << " VERSION = '#{gv}'.freeze\n"
|
29
|
+
f << "end\n"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# vim: syntax=ruby
|
data/bin/mturk
ADDED
data/lib/amazon/util.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# Copyright:: Copyright (c) 2007 Amazon Technologies, Inc.
|
2
|
+
# License:: Apache License, Version 2.0
|
3
|
+
|
4
|
+
require 'amazon/util/logging'
|
5
|
+
require 'amazon/util/paginated_iterator'
|
6
|
+
require 'amazon/util/lazy_results'
|
7
|
+
require 'amazon/util/proactive_results'
|
8
|
+
require 'amazon/util/binder'
|
9
|
+
require 'amazon/util/data_reader'
|
10
|
+
require 'amazon/util/threadpool'
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# Copyright:: Copyright (c) 2007 Amazon Technologies, Inc.
|
2
|
+
# License:: Apache License, Version 2.0
|
3
|
+
|
4
|
+
require 'erb'
|
5
|
+
|
6
|
+
module Amazon
|
7
|
+
module Util
|
8
|
+
|
9
|
+
# Simple class for holding eval information
|
10
|
+
# useful for feeding to ERB templates and the like
|
11
|
+
class Binder
|
12
|
+
|
13
|
+
def initialize(initial={},&block) # :yields: self
|
14
|
+
initial.each {|k,v| set(k,v) }
|
15
|
+
yield self unless block.nil?
|
16
|
+
end
|
17
|
+
|
18
|
+
def merge(hash)
|
19
|
+
hash.each {|k,v| set(k,v) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def set(k,v)
|
23
|
+
self.instance_variable_set "@#{k.to_s}", "#{v.to_s}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def bind
|
27
|
+
binding
|
28
|
+
end
|
29
|
+
|
30
|
+
# Helper method to simplify ERB evaluation
|
31
|
+
def old_erb_eval( template )
|
32
|
+
buffer = ""
|
33
|
+
c = ERB::Compiler.new("")
|
34
|
+
c.put_cmd = "buffer <<" if c.respond_to? :put_cmd=
|
35
|
+
c.insert_cmd = "buffer <<" if c.respond_to? :insert_cmd=
|
36
|
+
compiled = c.compile template
|
37
|
+
eval compiled
|
38
|
+
return buffer
|
39
|
+
end
|
40
|
+
def erb_eval( template )
|
41
|
+
t = ERB.new template
|
42
|
+
return t.result(binding)
|
43
|
+
end
|
44
|
+
|
45
|
+
end # Binder
|
46
|
+
|
47
|
+
end # Amazon::Util
|
48
|
+
end # Amazon
|
@@ -0,0 +1,169 @@
|
|
1
|
+
# Copyright:: Copyright (c) 2007 Amazon Technologies, Inc.
|
2
|
+
# License:: Apache License, Version 2.0
|
3
|
+
|
4
|
+
require 'yaml'
|
5
|
+
require 'csv'
|
6
|
+
require 'amazon/util/hash_nesting'
|
7
|
+
|
8
|
+
module Amazon
|
9
|
+
module Util
|
10
|
+
|
11
|
+
# DataReader is a class for loading in data files. It is used to support bulk file-based operations.
|
12
|
+
# DataReader supports a number of different formats:
|
13
|
+
# * YAML
|
14
|
+
# * Tabular
|
15
|
+
# * CSV
|
16
|
+
# * Java Properties
|
17
|
+
# By default, DataReader assumes Tabular, but load and save both support your choice of format
|
18
|
+
class DataReader
|
19
|
+
|
20
|
+
attr_accessor :data
|
21
|
+
|
22
|
+
def initialize(data=[])
|
23
|
+
@data = data
|
24
|
+
end
|
25
|
+
|
26
|
+
def [](index)
|
27
|
+
return @data[index]
|
28
|
+
end
|
29
|
+
|
30
|
+
def []=(index)
|
31
|
+
return @data[index]
|
32
|
+
end
|
33
|
+
|
34
|
+
def load( filename, format=:Tabular )
|
35
|
+
return {} unless File.exist? filename
|
36
|
+
raw_data = File.read( filename )
|
37
|
+
case format
|
38
|
+
when :Tabular
|
39
|
+
@data = parse_csv( raw_data, "\t" )
|
40
|
+
when :YAML
|
41
|
+
@data = YAML.load( raw_data ) || {}
|
42
|
+
when :CSV
|
43
|
+
@data = parse_csv( raw_data )
|
44
|
+
when :Properties
|
45
|
+
@data = parse_properties( raw_data )
|
46
|
+
else
|
47
|
+
raise "invalid format. options are :Tabular, :YAML, :CSV, :Properties"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def save( filename, format=:Tabular, force_headers=false )
|
52
|
+
return if @data.nil? or @data.empty?
|
53
|
+
existing = File.exist?( filename ) && File.size( filename ) > 0
|
54
|
+
File.open( filename, 'a+' ) {|f|
|
55
|
+
f << case format
|
56
|
+
when :Tabular
|
57
|
+
generate_csv( @data, force_headers || !existing, "\t" )
|
58
|
+
when :YAML
|
59
|
+
YAML.dump( @data )
|
60
|
+
when :CSV
|
61
|
+
generate_csv( @data, force_headers || !existing )
|
62
|
+
when :Properties
|
63
|
+
generate_properties( @data )
|
64
|
+
end
|
65
|
+
f << "\n" # adding a newline on the end, so appending is happy
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.load( filename, format=:Tabular )
|
70
|
+
reader = DataReader.new()
|
71
|
+
reader.load( filename, format )
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.save( filename, data, format=:Tabular, force_headers=false )
|
75
|
+
reader = DataReader.new( data )
|
76
|
+
reader.save( filename, format, force_headers )
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def parse_csv( raw_data, delim=nil )
|
82
|
+
rows = nil
|
83
|
+
if CSV.const_defined? :Reader
|
84
|
+
rows = CSV.parse( raw_data, delim )
|
85
|
+
else
|
86
|
+
ops = {:row_sep => :auto}
|
87
|
+
ops[:col_sep] = delim unless delim.nil?
|
88
|
+
rows = CSV.parse( raw_data, ops )
|
89
|
+
end
|
90
|
+
return parse_rows( rows )
|
91
|
+
end
|
92
|
+
|
93
|
+
def parse_rows( rows )
|
94
|
+
processed = []
|
95
|
+
headers = rows.shift
|
96
|
+
for row in rows
|
97
|
+
item = {}
|
98
|
+
headers.each_index do |i|
|
99
|
+
item[headers[i].to_sym] = correct_type(row[i]) unless row[i].nil? or row[i].empty?
|
100
|
+
end
|
101
|
+
item.extend HashNesting
|
102
|
+
processed << item.unnest
|
103
|
+
end
|
104
|
+
return processed
|
105
|
+
end
|
106
|
+
|
107
|
+
def split_data( data )
|
108
|
+
data = data.collect {|d| d.extend(HashNesting).nest }
|
109
|
+
headers = data[0].keys.sort
|
110
|
+
rows = data.collect { |item|
|
111
|
+
row = []
|
112
|
+
item.keys.each {|k|
|
113
|
+
headers << k unless headers.include? k
|
114
|
+
index = headers.index k
|
115
|
+
row[index] = item[k].to_s
|
116
|
+
}
|
117
|
+
row
|
118
|
+
}
|
119
|
+
return headers, rows
|
120
|
+
end
|
121
|
+
|
122
|
+
def generate_csv( data, dump_header, delim=nil )
|
123
|
+
return "" if data.nil? or data.empty?
|
124
|
+
headers, rows = split_data( data )
|
125
|
+
return generate_rows( headers, rows, dump_header, delim )
|
126
|
+
end
|
127
|
+
|
128
|
+
def generate_rows( headers, rows, dump_header, record_seperator=nil )
|
129
|
+
rows.unshift headers if dump_header
|
130
|
+
buff = rows.collect { |row|
|
131
|
+
if CSV.const_defined? :Reader
|
132
|
+
CSV.generate_line( row, record_seperator )
|
133
|
+
else
|
134
|
+
ops = {:row_sep => ''}
|
135
|
+
ops[:col_sep] = record_seperator unless record_seperator.nil?
|
136
|
+
CSV.generate_line( row, ops )
|
137
|
+
end
|
138
|
+
}
|
139
|
+
return buff.join("\n")
|
140
|
+
end
|
141
|
+
|
142
|
+
def parse_properties( raw_data )
|
143
|
+
processed = {}
|
144
|
+
for line in raw_data.split(/\n\r?/)
|
145
|
+
next if line =~ /^\W*(#.*)?$/ # ignore lines beginning w/ comments
|
146
|
+
if md = /^([^:=]+)[=:](.*)/.match(line)
|
147
|
+
processed[md[1].strip] = correct_type(md[2].strip)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
processed.extend HashNesting
|
151
|
+
return processed.unnest
|
152
|
+
end
|
153
|
+
|
154
|
+
def generate_properties( raw_data )
|
155
|
+
raw_data.extend HashNesting
|
156
|
+
(raw_data.nest.collect {|k,v| "#{k}:#{v}" }).join("\n")
|
157
|
+
end
|
158
|
+
|
159
|
+
# convert to integer if possible
|
160
|
+
def correct_type( str )
|
161
|
+
return str.to_f if str =~ /^\d+\.\d+$/ unless str =~ /^0\d/
|
162
|
+
return str.to_i if str =~ /^\d+$/ unless str =~ /^0\d/
|
163
|
+
return str
|
164
|
+
end
|
165
|
+
|
166
|
+
end # DataReader
|
167
|
+
|
168
|
+
end # Amazon::Util
|
169
|
+
end # Amazon
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# Copyright:: Copyright (c) 2007 Amazon Technologies, Inc.
|
2
|
+
# License:: Apache License, Version 2.0
|
3
|
+
|
4
|
+
module Amazon
|
5
|
+
module Util
|
6
|
+
|
7
|
+
# A class for managing around style interceptors,
|
8
|
+
# which can be used to implement a decorator design pattern.
|
9
|
+
class FilterChain
|
10
|
+
|
11
|
+
class Filter
|
12
|
+
|
13
|
+
attr_reader :name, :filter_params, :filter_block
|
14
|
+
|
15
|
+
def initialize( name, filter_params, filter_block )
|
16
|
+
@name = name
|
17
|
+
@filter_params = filter_params
|
18
|
+
@filter_block = filter_block
|
19
|
+
end
|
20
|
+
|
21
|
+
def execute( chain, block_params )
|
22
|
+
@filter_block.call( chain, block_params, *@filter_params )
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_reader :filters
|
28
|
+
|
29
|
+
def initialize()
|
30
|
+
@filters = []
|
31
|
+
end
|
32
|
+
|
33
|
+
def execute( *block_params, &block )
|
34
|
+
if @filters.size == 0
|
35
|
+
block.call( *block_params )
|
36
|
+
else
|
37
|
+
create_chain( @filters, 0, block, block_params ).call
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def add( name=nil, *filter_params, &filter_block )
|
42
|
+
add_filter( Filter.new( name, filter_params, filter_block ) )
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_filter( filter )
|
46
|
+
if !filter.name.nil?
|
47
|
+
@filters.each_with_index { |existing_filter,i|
|
48
|
+
if filter.name == existing_filter.name
|
49
|
+
@filters[i] = filter
|
50
|
+
return
|
51
|
+
end
|
52
|
+
}
|
53
|
+
end
|
54
|
+
@filters << filter
|
55
|
+
end
|
56
|
+
|
57
|
+
def remove( name )
|
58
|
+
@filters.delete_if { |filter| name == filter.name }
|
59
|
+
end
|
60
|
+
|
61
|
+
def remove_all()
|
62
|
+
@filters.clear
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def create_chain( filters, pos, block, block_params )
|
68
|
+
if pos >= filters.size
|
69
|
+
return proc{ block.call( *block_params ) }
|
70
|
+
else
|
71
|
+
chain = create_chain( filters, pos+1, block, block_params )
|
72
|
+
return proc { filters[pos].execute( chain, block_params ) }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end # Amazon::Util
|
79
|
+
end # Amazon
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# Copyright:: Copyright (c) 2007 Amazon Technologies, Inc.
|
2
|
+
# License:: Apache License, Version 2.0
|
3
|
+
|
4
|
+
module Amazon
|
5
|
+
module Util
|
6
|
+
|
7
|
+
module HashNesting
|
8
|
+
|
9
|
+
def nest
|
10
|
+
result = {}.extend HashNesting
|
11
|
+
primaryKeys.each { |key| traverse_nest( "#{key}", self[key] ) { |k,v| result[k] = v } }
|
12
|
+
result
|
13
|
+
end
|
14
|
+
|
15
|
+
def nest!
|
16
|
+
keys = primaryKeys
|
17
|
+
tmp = self.dup
|
18
|
+
self.keys.each { |k| self.delete k}
|
19
|
+
keys.each { |key| traverse_nest( "#{key}", tmp[key] ) { |k,v| self[k] = v} }
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
def unnest
|
24
|
+
result = {}.extend HashNesting
|
25
|
+
for key in primaryKeys
|
26
|
+
true_keys = key.to_s.split('.')
|
27
|
+
resolve_nesting( result, self[key], *true_keys)
|
28
|
+
end
|
29
|
+
result
|
30
|
+
end
|
31
|
+
|
32
|
+
def unnest!
|
33
|
+
for key in primaryKeys
|
34
|
+
true_keys = key.to_s.split('.')
|
35
|
+
value = self[key]
|
36
|
+
self.delete key
|
37
|
+
resolve_nesting( self, value, *true_keys)
|
38
|
+
end
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
# if hash has both string and symbol keys, symbol wins
|
45
|
+
def primaryKeys
|
46
|
+
sym_keys = []
|
47
|
+
str_keys = []
|
48
|
+
self.keys.each { |k|
|
49
|
+
case k
|
50
|
+
when Symbol
|
51
|
+
sym_keys << k
|
52
|
+
when String
|
53
|
+
str_keys << k
|
54
|
+
else
|
55
|
+
str_keys << k
|
56
|
+
end
|
57
|
+
}
|
58
|
+
str_keys.delete_if {|k| sym_keys.member? k.to_s.to_sym }
|
59
|
+
sym_keys + str_keys
|
60
|
+
end
|
61
|
+
|
62
|
+
def resolve_nesting( dest, data, *keys )
|
63
|
+
return data if keys.empty?
|
64
|
+
dest ||= {}
|
65
|
+
key = keys.shift.to_sym
|
66
|
+
if keys.first.to_i.to_s == keys.first
|
67
|
+
# array
|
68
|
+
index = keys.shift.to_i - 1
|
69
|
+
raise "illegal index: #{keys.join '.'} index must be >= 1" if index < 0
|
70
|
+
dest[key] ||= []
|
71
|
+
dest[key][index] = resolve_nesting( dest[key][index], data, *keys )
|
72
|
+
else
|
73
|
+
# hash
|
74
|
+
dest[key] = resolve_nesting( dest[key], data, *keys )
|
75
|
+
end
|
76
|
+
dest
|
77
|
+
end
|
78
|
+
|
79
|
+
def traverse_nest( namespace, data, &block )
|
80
|
+
case data.class.to_s
|
81
|
+
when 'Array'
|
82
|
+
data.each_with_index { |v,i| traverse_nest( "#{namespace}.#{i+1}", v, &block ) }
|
83
|
+
when 'Hash'
|
84
|
+
data.each { |k,v| traverse_nest( "#{namespace}.#{k}", v, &block ) }
|
85
|
+
else
|
86
|
+
yield namespace, data.to_s
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end # HashNesting
|
91
|
+
|
92
|
+
end # Amazon::Util
|
93
|
+
end # Amazon
|