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
@@ -0,0 +1,123 @@
|
|
1
|
+
# Copyright:: Copyright (c) 2007 Amazon Technologies, Inc.
|
2
|
+
# License:: Apache License, Version 2.0
|
3
|
+
|
4
|
+
require 'amazon/util/logging'
|
5
|
+
require 'amazon/webservices/util/amazon_authentication_relay'
|
6
|
+
require 'amazon/webservices/mturk/mechanical_turk_error_handler'
|
7
|
+
require 'amazon/webservices/util/validation_exception'
|
8
|
+
require 'amazon/webservices/util/rest_transport'
|
9
|
+
require 'amazon/webservices/util/soap_transport'
|
10
|
+
|
11
|
+
module Amazon
|
12
|
+
module WebServices
|
13
|
+
|
14
|
+
class MechanicalTurk
|
15
|
+
include Amazon::Util::Logging
|
16
|
+
|
17
|
+
SOFTWARE_NAME = 'MTurkRubySDK'
|
18
|
+
|
19
|
+
SANDBOX = 'mechanicalturk.sandbox.amazonaws.com'
|
20
|
+
PROD = 'mechanicalturk.amazonaws.com'
|
21
|
+
|
22
|
+
# By default, MechanicalTurk will operate on the MechanicalTurk Sandbox. To run against the main site, pass +:Host => :Production+ into the constructor.
|
23
|
+
def initialize(args={})
|
24
|
+
name = args[:Name] || 'AWSMechanicalTurkRequester'
|
25
|
+
software = args.has_key?(:SoftwareName) ? "#{SOFTWARE_NAME}, #{args[:SoftwareName]}" : "#{SOFTWARE_NAME}"
|
26
|
+
@host = case args[:Host].to_s
|
27
|
+
when /^Prod/i
|
28
|
+
PROD
|
29
|
+
when /^Sandbox/i,""
|
30
|
+
SANDBOX
|
31
|
+
else
|
32
|
+
args[:Host].to_s
|
33
|
+
end
|
34
|
+
ssl = ( args[:UseSSL].nil? ? true : args[:UseSSL] )
|
35
|
+
newargs = args.merge( :Name => name, :SoftwareName => software, :Host => @host, :UseSSL => ssl)
|
36
|
+
if args[:Transport].to_s =~ /^SOAP/i
|
37
|
+
unless Util::SOAPTransport.canSOAP?
|
38
|
+
log "Unable to use SOAP transport. Falling back to REST."
|
39
|
+
args[:Transport] = :REST
|
40
|
+
end
|
41
|
+
end
|
42
|
+
transport = case args[:Transport]
|
43
|
+
when :SOAP,/^SOAP/i
|
44
|
+
getSOAPTransport(newargs)
|
45
|
+
when :REST,/^REST/i
|
46
|
+
getRESTTransport(newargs)
|
47
|
+
else
|
48
|
+
require 'amazon/webservices/util/soap_transport.rb'
|
49
|
+
allowOverride( 'Transport', args[:Transport], newargs ) { |a|
|
50
|
+
if Util::RESTTransport.canPost?
|
51
|
+
getRESTTransport(newargs)
|
52
|
+
elsif Util::SOAPTransport.canSOAP?
|
53
|
+
getSOAPTransport(newargs)
|
54
|
+
else
|
55
|
+
getRESTTransport(newargs)
|
56
|
+
end
|
57
|
+
}
|
58
|
+
end
|
59
|
+
newargs.merge!( :Transport => transport )
|
60
|
+
log "Generating relay with following args: #{newargs.inspect}"
|
61
|
+
relay = allowOverride('Relay',args[:Relay],newargs) { |a| Amazon::WebServices::Util::AmazonAuthenticationRelay.new(a) }
|
62
|
+
newargs.merge!( :Relay => relay )
|
63
|
+
log "Generating error handler with the following args: #{newargs.inspect}"
|
64
|
+
@errorHandler = allowOverride('ErrorHandler',args[:ErrorHandler],newargs) { |a| Amazon::WebServices::MTurk::MechanicalTurkErrorHandler.new(a) }
|
65
|
+
end
|
66
|
+
|
67
|
+
attr_accessor :host
|
68
|
+
|
69
|
+
def method_missing(method,*args)
|
70
|
+
log "Sending request: #{method} #{args.inspect}"
|
71
|
+
@errorHandler.dispatch(method,*args)
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def allowOverride(name,override,args,&default)
|
77
|
+
newargs = args.merge( :DefaultOverride => default )
|
78
|
+
case override
|
79
|
+
when nil
|
80
|
+
yield( args )
|
81
|
+
when String,Symbol,Array,Hash,Integer
|
82
|
+
raise "Invalid #{name}: #{override.inspect}"
|
83
|
+
when Class
|
84
|
+
override.new( newargs )
|
85
|
+
else
|
86
|
+
override.configure( newargs ) if override.respond_to? :configure
|
87
|
+
override
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def getRESTTransport(args)
|
92
|
+
endpoint = findRestEndpoint( args[:Name], args[:Host], args[:UseSSL] )
|
93
|
+
require 'amazon/webservices/util/rest_transport.rb'
|
94
|
+
@transport = Amazon::WebServices::Util::RESTTransport.new( args.merge( :Endpoint => endpoint ) )
|
95
|
+
end
|
96
|
+
|
97
|
+
def getSOAPTransport(args)
|
98
|
+
wsdl = findWSDL( args[:Name], args[:Host], args[:Version], args[:UseSSL] )
|
99
|
+
endpoint = findSOAPEndpoint( args[:Name], args[:Host], args[:UseSSL] )
|
100
|
+
require 'amazon/webservices/util/soap_transport.rb'
|
101
|
+
Amazon::WebServices::Util::SOAPTransport.new( args.merge( :Wsdl => wsdl, :Endpoint => endpoint ) )
|
102
|
+
end
|
103
|
+
|
104
|
+
def findWSDL( name, host, version, ssl )
|
105
|
+
if version.nil?
|
106
|
+
"#{ssl ? 'https' : 'http'}://#{host}/AWSMechanicalTurk/#{name}.wsdl"
|
107
|
+
else
|
108
|
+
"#{ssl ? 'https' : 'http'}://#{host}/AWSMechanicalTurk/#{version}/#{name}.wsdl"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def findSOAPEndpoint( name, host, ssl )
|
113
|
+
"#{ssl ? 'https' : 'http'}://#{host}/onca/soap?Service=#{name}"
|
114
|
+
end
|
115
|
+
|
116
|
+
def findRestEndpoint( name, host, ssl )
|
117
|
+
"#{ssl ? 'https' : 'http'}://#{host}/?Service=#{name}"
|
118
|
+
end
|
119
|
+
|
120
|
+
end # MTurk
|
121
|
+
|
122
|
+
end # Amazon::WebServices
|
123
|
+
end # Amazon
|
@@ -0,0 +1,285 @@
|
|
1
|
+
# Copyright:: Copyright (c) 2007-2014 Amazon Technologies, Inc.
|
2
|
+
# License:: Apache License, Version 2.0
|
3
|
+
|
4
|
+
require 'erb'
|
5
|
+
require 'monitor'
|
6
|
+
require 'amazon/util'
|
7
|
+
require 'amazon/webservices/util/xml_simplifier'
|
8
|
+
require 'amazon/webservices/util/convenience_wrapper'
|
9
|
+
require 'amazon/webservices/mechanical_turk'
|
10
|
+
|
11
|
+
module Amazon
|
12
|
+
module WebServices
|
13
|
+
|
14
|
+
class MechanicalTurkRequester < Amazon::WebServices::Util::ConvenienceWrapper
|
15
|
+
|
16
|
+
WSDL_VERSION = "2014-08-15"
|
17
|
+
|
18
|
+
# Deprecated qualification type IDs
|
19
|
+
ABANDONMENT_RATE_QUALIFICATION_TYPE_ID = "00000000000000000070";
|
20
|
+
APPROVAL_RATE_QUALIFICATION_TYPE_ID = "000000000000000000L0";
|
21
|
+
REJECTION_RATE_QUALIFICATION_TYPE_ID = "000000000000000000S0";
|
22
|
+
RETURN_RATE_QUALIFICATION_TYPE_ID = "000000000000000000E0";
|
23
|
+
SUBMISSION_RATE_QUALIFICATION_TYPE_ID = "00000000000000000000";
|
24
|
+
|
25
|
+
# Current qualification type IDs
|
26
|
+
LOCALE_QUALIFICATION_TYPE_ID = "00000000000000000071";
|
27
|
+
TOTAL_NUMBER_OF_HITS_APPROVED_QUALIFICATION_TYPE_ID = "00000000000000000040";
|
28
|
+
ADULT_QUALIFICATION_TYPE_ID = "00000000000000000060";
|
29
|
+
PHOTO_MODERATION_MASTERS_QUALIFICATION_TYPE_ID = "21VZU98JHSTLZ5BPP4A9NOBJEK3DPG";
|
30
|
+
PHOTO_MODERATION_MASTERS_SANDBOX_QUALIFICATION_TYPE_ID = "2TGBB6BFMFFOM08IBMAFGGESC1UWJX";
|
31
|
+
CATEGORIZATION_MASTERS_QUALIFICATION_TYPE_ID = "2NDP2L92HECWY8NS8H3CK0CP5L9GHO";
|
32
|
+
CATEGORIZATION_MASTERS_SANDBOX_QUALIFICATION_TYPE_ID = "2F1KVCNHMVHV8E9PBUB2A4J79LU20F";
|
33
|
+
MASTERS_QUALIFICATION_TYPE_ID = "2F1QJWKUDD8XADTFD2Q0G6UTO95ALH";
|
34
|
+
MASTERS_SANDBOX_QUALIFICATION_TYPE_ID = "2ARFPLSP75KLA8M8DH1HTEQVJT3SY6";
|
35
|
+
|
36
|
+
DEFAULT_THREADCOUNT = 10
|
37
|
+
|
38
|
+
serviceCall :RegisterHITType, :RegisterHITTypeResult, {
|
39
|
+
:AssignmentDurationInSeconds => 60*60,
|
40
|
+
:AutoApprovalDelayInSeconds => 60*60*24*7
|
41
|
+
}
|
42
|
+
serviceCall :SetHITTypeNotification, :SetHITTypeNotificationResult
|
43
|
+
|
44
|
+
|
45
|
+
serviceCall :CreateHIT, :HIT, { :MaxAssignments => 1,
|
46
|
+
:AssignmentDurationInSeconds => 60*60,
|
47
|
+
:AutoApprovalDelayInSeconds => 60*60*24*7,
|
48
|
+
:LifetimeInSeconds => 60*60*24,
|
49
|
+
}
|
50
|
+
|
51
|
+
serviceCall :DisableHIT, :DisableHITResult
|
52
|
+
serviceCall :DisposeHIT, :DisposeHITResult
|
53
|
+
serviceCall :ExtendHIT, :ExtendHITResult
|
54
|
+
serviceCall :ForceExpireHIT, :ForceExpireHITResult
|
55
|
+
serviceCall :GetHIT, :HIT, { :ResponseGroup => %w( Minimal HITDetail HITQuestion HITAssignmentSummary ) }
|
56
|
+
serviceCall :ChangeHITTypeOfHIT, :ChangeHITTypeOfHITResult
|
57
|
+
|
58
|
+
serviceCall :SearchHITs, :SearchHITsResult
|
59
|
+
serviceCall :GetReviewableHITs, :GetReviewableHITsResult
|
60
|
+
serviceCall :SetHITAsReviewing, :SetHITAsReviewingResult
|
61
|
+
serviceCall :GetAssignmentsForHIT, :GetAssignmentsForHITResult
|
62
|
+
serviceCall :GetReviewResultsForHIT, :GetReviewResultsForHITResult
|
63
|
+
|
64
|
+
paginate :SearchHITs, :HIT
|
65
|
+
paginate :GetReviewableHITs, :HIT
|
66
|
+
paginate :GetAssignmentsForHIT, :Assignment
|
67
|
+
|
68
|
+
serviceCall :GetAssignment, :GetAssignmentResult
|
69
|
+
serviceCall :ApproveAssignment, :ApproveAssignmentResult
|
70
|
+
serviceCall :RejectAssignment, :RejectAssignmentResult
|
71
|
+
serviceCall :ApproveRejectedAssignment, :ApproveRejectedAssignmentResult
|
72
|
+
|
73
|
+
serviceCall :GrantBonus, :GrantBonusResult
|
74
|
+
serviceCall :GetBonusPayments, :GetBonusPaymentsResult
|
75
|
+
|
76
|
+
serviceCall :CreateQualificationType, :QualificationType, { :QualificationTypeStatus => 'Active' }
|
77
|
+
serviceCall :GetQualificationType, :QualificationType
|
78
|
+
serviceCall :SearchQualificationTypes, :SearchQualificationTypesResult, { :MustBeRequestable => true }
|
79
|
+
serviceCall :UpdateQualificationType, :QualificationType
|
80
|
+
serviceCall :GetQualificationsForQualificationType, :GetQualificationsForQualificationTypeResult, { :Status => 'Granted' }
|
81
|
+
serviceCall :GetHITsForQualificationType, :GetHITsForQualificationTypeResult
|
82
|
+
serviceCall :DisposeQualficationType, :DisposeQualificationTypeResult
|
83
|
+
|
84
|
+
paginate :SearchQualificationTypes, :QualificationType
|
85
|
+
paginate :GetQualificationsForQualificationType, :Qualification
|
86
|
+
|
87
|
+
serviceCall :AssignQualification, :AssignQualificationResult
|
88
|
+
serviceCall :GetQualificationRequests, :GetQualificationRequestsResult
|
89
|
+
serviceCall :GrantQualification, :GrantQualificationResult
|
90
|
+
serviceCall :RejectQualificationRequest, :RejectQualificationRequestResult
|
91
|
+
serviceCall :GetQualificationScore, :Qualification
|
92
|
+
serviceCall :UpdateQualificationScore, :UpdateQualificationScoreResult
|
93
|
+
serviceCall :RevokeQualification, :RevokeQualificationResult
|
94
|
+
|
95
|
+
paginate :GetQualificationRequests, :QualificationRequest
|
96
|
+
|
97
|
+
serviceCall :GetBlockedWorkers, :GetBlockedWorkersResult
|
98
|
+
serviceCall :BlockWorker, :BlockWorkerResult
|
99
|
+
serviceCall :UnblockWorker, :UnblockWorkerResult
|
100
|
+
|
101
|
+
paginate :GetBlockedWorkers, :WorkerBlock
|
102
|
+
|
103
|
+
serviceCall :GetFileUploadURL, :GetFileUploadURLResult
|
104
|
+
serviceCall :GetAccountBalance, :GetAccountBalanceResult
|
105
|
+
serviceCall :GetRequesterStatistic, :GetStatisticResult, { :Count => 1 }
|
106
|
+
serviceCall :GetRequesterWorkerStatistic, :GetStatisticResult, { :Count => 1 }
|
107
|
+
|
108
|
+
serviceCall :NotifyWorkers, :NotifyWorkersResult
|
109
|
+
|
110
|
+
def initialize(args={})
|
111
|
+
newargs = args.dup
|
112
|
+
unless args[:Config].nil?
|
113
|
+
if args[:Config] == :Rails
|
114
|
+
rails_config = Amazon::Util::DataReader.load( File.join(::RAILS_ROOT,'config','mturk.yml'), :YAML )
|
115
|
+
newargs = args.merge rails_config[::RAILS_ENV].inject({}) {|a,b| a[b[0].to_sym] = b[1] ; a }
|
116
|
+
else
|
117
|
+
loaded = Amazon::Util::DataReader.load( args[:Config], :YAML )
|
118
|
+
newargs = args.merge loaded.inject({}) {|a,b| a[b[0].to_sym] = b[1] ; a }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
@threadcount = args[:ThreadCount].to_i
|
122
|
+
@threadcount = DEFAULT_THREADCOUNT unless @threadcount >= 1
|
123
|
+
raise "Cannot override WSDL version ( #{WSDL_VERSION} )" unless args[:Version].nil? or args[:Version].equals? WSDL_VERSION
|
124
|
+
super newargs.merge( :Name => :AWSMechanicalTurkRequester,
|
125
|
+
:ServiceClass => Amazon::WebServices::MechanicalTurk,
|
126
|
+
:Version => WSDL_VERSION )
|
127
|
+
end
|
128
|
+
|
129
|
+
# Create a series of similar HITs, sharing common parameters. Utilizes HITType
|
130
|
+
# * hit_template is the array of parameters to pass to createHIT.
|
131
|
+
# * question_template will be passed as a template into ERB to generate the :Question parameter
|
132
|
+
# * the RequesterAnnotation parameter of hit_template will also be passed through ERB
|
133
|
+
# * hit_data_set should consist of an array of hashes defining unique instance variables utilized by question_template
|
134
|
+
def createHITs( hit_template, question_template, hit_data_set )
|
135
|
+
hit_template = hit_template.dup
|
136
|
+
lifetime = hit_template[:LifetimeInSeconds]
|
137
|
+
numassignments_template = hit_template[:MaxAssignments]
|
138
|
+
annotation_template = hit_template[:RequesterAnnotation]
|
139
|
+
hit_template.delete :LifetimeInSeconds
|
140
|
+
hit_template.delete :MaxAssignments
|
141
|
+
hit_template.delete :RequesterAnnotation
|
142
|
+
|
143
|
+
ht = hit_template[:HITTypeId] || registerHITType( hit_template )[:HITTypeId]
|
144
|
+
|
145
|
+
tp = Amazon::Util::ThreadPool.new @threadcount
|
146
|
+
|
147
|
+
created = [].extend(MonitorMixin)
|
148
|
+
failed = [].extend(MonitorMixin)
|
149
|
+
hit_data_set.each do |hd|
|
150
|
+
tp.addWork(hd) do |hit_data|
|
151
|
+
begin
|
152
|
+
b = Amazon::Util::Binder.new( hit_data )
|
153
|
+
annotation = annotation_template.nil? ? nil : b.erb_eval( annotation_template )
|
154
|
+
numassignments = numassignments_template.nil? ? nil : b.erb_eval( numassignments_template.to_s ).to_i
|
155
|
+
question = b.erb_eval( question_template )
|
156
|
+
result = self.createHIT( :HITTypeId => ht,
|
157
|
+
:LifetimeInSeconds => lifetime,
|
158
|
+
:MaxAssignments => ( hit_data[:MaxAssignments] || numassignments || 1 ),
|
159
|
+
:Question => question,
|
160
|
+
:RequesterAnnotation => ( hit_data[:RequesterAnnotation] || annotation || "")
|
161
|
+
)
|
162
|
+
created.synchronize do
|
163
|
+
created << result
|
164
|
+
end
|
165
|
+
rescue => e
|
166
|
+
failed.synchronize do
|
167
|
+
failed << hit_data.merge( :Error => e.message )
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end # tp.addWork
|
171
|
+
end # hit_data_set.each
|
172
|
+
tp.finish
|
173
|
+
|
174
|
+
return :Created => created, :Failed => failed
|
175
|
+
end
|
176
|
+
|
177
|
+
# Update a series of HITs to belong to a new HITType
|
178
|
+
# * hit_template is the array of parameters to pass to registerHITType
|
179
|
+
# * hit_ids is a list of HITIds (strings)
|
180
|
+
def updateHITs( hit_template, hit_ids )
|
181
|
+
hit_template = hit_template.dup
|
182
|
+
hit_template.delete :LifetimeInSeconds
|
183
|
+
hit_template.delete :RequesterAnnotation
|
184
|
+
|
185
|
+
hit_type_id = registerHITType( hit_template )[:HITTypeId]
|
186
|
+
|
187
|
+
tp = Amazon::Util::ThreadPool.new @threadcount
|
188
|
+
|
189
|
+
updated = [].extend(MonitorMixin)
|
190
|
+
failed = [].extend(MonitorMixin)
|
191
|
+
hit_ids.each do |hid|
|
192
|
+
tp.addWork(hid) do |hit_id|
|
193
|
+
begin
|
194
|
+
changeHITTypeOfHIT( :HITId => hit_id, :HITTypeId => hit_type_id )
|
195
|
+
updated.synchronize do
|
196
|
+
updated << hit_id
|
197
|
+
end
|
198
|
+
rescue => e
|
199
|
+
failed.synchronize do
|
200
|
+
failed << { :HITId => hit_id, :Error => e.message }
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end # tp.addWork
|
204
|
+
end # hit_ids.each
|
205
|
+
tp.finish
|
206
|
+
|
207
|
+
return :Updated => updated, :Failed => failed
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
# Update a HIT with new properties.
|
212
|
+
# hit_id:: Id of the HIT to update
|
213
|
+
# hit_template:: hash ( parameter => value ) of parameters to update
|
214
|
+
#
|
215
|
+
# Acceptable attributes:
|
216
|
+
# * Title
|
217
|
+
# * Description
|
218
|
+
# * Keywords
|
219
|
+
# * Reward
|
220
|
+
# * QualificationRequirement
|
221
|
+
# * AutoApprovalDelayInSeconds
|
222
|
+
# * AssignmentDurationInSeconds
|
223
|
+
#
|
224
|
+
# Behind the scenes, this function retrieves the HIT, merges the HITs
|
225
|
+
# current attributes with any you specify, and registers a new HIT
|
226
|
+
# Template. It then uses the new ChangeHITTypeOfHIT function to move
|
227
|
+
# your HIT to the newly-created HIT Template.
|
228
|
+
def updateHIT( hit_id, hit_template )
|
229
|
+
hit_template = hit_template.dup
|
230
|
+
|
231
|
+
hit = getHIT( :HITId => hit_id )
|
232
|
+
|
233
|
+
props = %w( Title Description Keywords Reward QualificationRequirement
|
234
|
+
AutoApprovalDelayInSeconds AssignmentDurationInSeconds
|
235
|
+
).collect {|str| str.to_sym }
|
236
|
+
|
237
|
+
props.each do |p|
|
238
|
+
hit_template[p] = hit[p] if hit_template[p].nil?
|
239
|
+
end
|
240
|
+
|
241
|
+
hit_type_id = registerHITType( hit_template )[:HITTypeId]
|
242
|
+
|
243
|
+
changeHITTypeOfHIT( :HITId => hit_id, :HITTypeId => hit_type_id )
|
244
|
+
end
|
245
|
+
|
246
|
+
|
247
|
+
def getHITResults( list )
|
248
|
+
results = [].extend(MonitorMixin)
|
249
|
+
tp = Amazon::Util::ThreadPool.new @threadcount
|
250
|
+
list.each do |line|
|
251
|
+
tp.addWork(line) do |h|
|
252
|
+
hit = getHIT( :HITId => h[:HITId] )
|
253
|
+
getAssignmentsForHITAll( :HITId => h[:HITId] ).each {|assignment|
|
254
|
+
results.synchronize do
|
255
|
+
results << ( hit.merge( assignment ) )
|
256
|
+
end
|
257
|
+
}
|
258
|
+
end
|
259
|
+
end
|
260
|
+
tp.finish
|
261
|
+
results.flatten
|
262
|
+
end
|
263
|
+
|
264
|
+
# Returns available funds in USD
|
265
|
+
# Calls getAccountBalance and parses out the correct amount
|
266
|
+
def availableFunds
|
267
|
+
return getAccountBalance[:AvailableBalance][:Amount]
|
268
|
+
end
|
269
|
+
|
270
|
+
# helper function to simplify answer XML
|
271
|
+
def simplifyAnswer( answerXML )
|
272
|
+
answerHash = Amazon::WebServices::Util::XMLSimplifier.simplify REXML::Document.new(answerXML)
|
273
|
+
list = [answerHash[:Answer]].flatten
|
274
|
+
list.inject({}) { |answers, answer|
|
275
|
+
id = answer[:QuestionIdentifier]
|
276
|
+
result = answer[:FreeText] || answer[:SelectionIdentifier] || answer[:UploadedFileKey]
|
277
|
+
answers[id] = result
|
278
|
+
answers
|
279
|
+
}
|
280
|
+
end
|
281
|
+
|
282
|
+
end # MechanicalTurkRequester
|
283
|
+
|
284
|
+
end # Amazon::WebServices
|
285
|
+
end # Amazon
|
@@ -0,0 +1,153 @@
|
|
1
|
+
# Copyright:: Copyright (c) 2007-2014 Amazon Technologies, Inc.
|
2
|
+
# License:: Apache License, Version 2.0
|
3
|
+
|
4
|
+
require 'amazon/util/logging'
|
5
|
+
require 'amazon/webservices/util/validation_exception'
|
6
|
+
require 'amazon/webservices/util/unknown_result_exception'
|
7
|
+
|
8
|
+
module Amazon
|
9
|
+
module WebServices
|
10
|
+
module MTurk
|
11
|
+
|
12
|
+
class MechanicalTurkErrorHandler
|
13
|
+
include Amazon::Util::Logging
|
14
|
+
|
15
|
+
REQUIRED_PARAMETERS = [:Relay]
|
16
|
+
|
17
|
+
# Commands with these prefixes can be retried if we are unsure of success
|
18
|
+
RETRY_PRE = %w( search get register update disable assign set dispose )
|
19
|
+
|
20
|
+
# Max number of times to retry a call
|
21
|
+
MAX_RETRY = 6
|
22
|
+
|
23
|
+
# Base used in Exponential Backoff retry delay
|
24
|
+
BACKOFF_BASE = 2
|
25
|
+
# Scale factor for Exponential Backoff retry delay
|
26
|
+
BACKOFF_INITIAL = 0.1
|
27
|
+
|
28
|
+
# Matching pattern to find a 'Results' element in the Response
|
29
|
+
RESULT_PATTERN = /Result/
|
30
|
+
# Additional elements to be considered a 'Result' despite not matching RESULT_PATTERN
|
31
|
+
ACCEPTABLE_RESULTS = %w( HIT Qualification QualificationType QualificationRequest Information )
|
32
|
+
|
33
|
+
def initialize( args )
|
34
|
+
missing_parameters = REQUIRED_PARAMETERS - args.keys
|
35
|
+
raise "Missing paramters: #{missing_parameters.join(',')}" unless missing_parameters.empty?
|
36
|
+
@relay = args[:Relay]
|
37
|
+
end
|
38
|
+
|
39
|
+
def dispatch(method, *args)
|
40
|
+
try = 0
|
41
|
+
begin
|
42
|
+
try += 1
|
43
|
+
log "Dispatching call to #{method} (try #{try})"
|
44
|
+
response = @relay.send(method,*args)
|
45
|
+
validateResponse( response )
|
46
|
+
return response
|
47
|
+
rescue Exception => error
|
48
|
+
case handleError( error,method,args )
|
49
|
+
when :RetryWithBackoff
|
50
|
+
retry if doBackoff( try )
|
51
|
+
when :RetryImmediate
|
52
|
+
retry if canRetry( try )
|
53
|
+
when :Ignore
|
54
|
+
return :IgnoredError => error
|
55
|
+
when :Unknown
|
56
|
+
raise Util::UnknownResultException.new( error, method, args )
|
57
|
+
when :Fail
|
58
|
+
raise error
|
59
|
+
else
|
60
|
+
raise "Unknown error handling method: #{handleError( error,method )}"
|
61
|
+
end
|
62
|
+
raise error
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def methodRetryable( method )
|
67
|
+
RETRY_PRE.each do |pre|
|
68
|
+
return true if method.to_s =~ /^#{pre}/i
|
69
|
+
end
|
70
|
+
return false
|
71
|
+
end
|
72
|
+
|
73
|
+
def handleError( error, method, args )
|
74
|
+
log "Handling error: #{error.inspect}"
|
75
|
+
idempotent = args.none? {|params| params[:UniqueRequestToken].nil? || params[:UniqueRequestToken].empty? }
|
76
|
+
retryable = methodRetryable( method ) || idempotent
|
77
|
+
|
78
|
+
case error.class.to_s
|
79
|
+
when 'Timeout::Error','SOAP::HTTPStreamError','Errno::ECONNRESET','Errno::EPIPE'
|
80
|
+
if retryable
|
81
|
+
return :RetryImmediate
|
82
|
+
else
|
83
|
+
return :Unknown
|
84
|
+
end
|
85
|
+
when 'SOAP::FaultError'
|
86
|
+
case error.faultcode.data
|
87
|
+
when "aws:Server.ServiceUnavailable"
|
88
|
+
return :RetryWithBackoff
|
89
|
+
else
|
90
|
+
return :Unknown
|
91
|
+
end
|
92
|
+
when 'Amazon::WebServices::Util::ValidationException'
|
93
|
+
case error.message
|
94
|
+
when 'AWS.ServiceUnavailable'
|
95
|
+
return :RetryWithBackoff if retryable
|
96
|
+
end
|
97
|
+
return :Fail
|
98
|
+
when 'RuntimeError'
|
99
|
+
case error.message
|
100
|
+
when 'Throttled'
|
101
|
+
return :RetryWithBackoff
|
102
|
+
else
|
103
|
+
return :RetryImmediate
|
104
|
+
end
|
105
|
+
else
|
106
|
+
return :Unknown
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def canRetry( try )
|
111
|
+
try <= MAX_RETRY
|
112
|
+
end
|
113
|
+
|
114
|
+
def doBackoff( try )
|
115
|
+
return false unless canRetry(try)
|
116
|
+
delay = BACKOFF_INITIAL * ( BACKOFF_BASE ** try )
|
117
|
+
sleep delay
|
118
|
+
return true
|
119
|
+
end
|
120
|
+
|
121
|
+
def isResultTag( tag )
|
122
|
+
tag.to_s =~ RESULT_PATTERN or ACCEPTABLE_RESULTS.include?( tag.to_s )
|
123
|
+
end
|
124
|
+
|
125
|
+
def validateResponse(response)
|
126
|
+
log "Validating response: #{response.inspect}"
|
127
|
+
if response[:Errors] and response[:Errors][:Error]
|
128
|
+
if response[:Errors][:Error][:Code] == "ServiceUnavailable"
|
129
|
+
raise 'Throttled'
|
130
|
+
else
|
131
|
+
raise Util::ValidationException.new(response)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
if response[:OperationRequest] and response[:OperationRequest][:Errors]
|
135
|
+
raise Util::ValidationException.new(response)
|
136
|
+
end
|
137
|
+
resultTags = response.keys.find_all {|r| isResultTag( r ) }
|
138
|
+
raise Util::ValidationException.new(response, "Didn't get back an acceptable result tag (got back #{response.keys.join(',')})") if resultTags.empty?
|
139
|
+
resultTags.each do |resultTag|
|
140
|
+
log "using result tag <#{resultTag}>"
|
141
|
+
result = response[resultTag]
|
142
|
+
if result[:Request] and result[:Request][:Errors]
|
143
|
+
raise Util::ValidationException.new(result)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
response
|
147
|
+
end
|
148
|
+
|
149
|
+
end # MechanicalTurkErrorHandler
|
150
|
+
|
151
|
+
end # Amazon::WebServices::MTurk
|
152
|
+
end # Amazon::WebServices
|
153
|
+
end # Amazon
|