dbgrandi-ruby-aws 1.2.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/History.txt +38 -0
- data/LICENSE.txt +202 -0
- data/Manifest.txt +69 -0
- data/NOTICE.txt +4 -0
- data/README.txt +105 -0
- data/Rakefile +20 -0
- data/bin/ruby-aws +9 -0
- data/lib/amazon/util.rb +10 -0
- data/lib/amazon/util/binder.rb +44 -0
- data/lib/amazon/util/data_reader.rb +157 -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 +117 -0
- data/lib/amazon/webservices/mechanical_turk_requester.rb +261 -0
- data/lib/amazon/webservices/mturk/mechanical_turk_error_handler.rb +136 -0
- data/lib/amazon/webservices/mturk/question_generator.rb +58 -0
- data/lib/amazon/webservices/util/amazon_authentication_relay.rb +64 -0
- data/lib/amazon/webservices/util/command_line.rb +156 -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 +108 -0
- data/lib/amazon/webservices/util/soap_simplifier.rb +48 -0
- data/lib/amazon/webservices/util/soap_transport.rb +38 -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/ruby-aws.rb +21 -0
- data/lib/ruby-aws/version.rb +8 -0
- data/samples/mturk/best_image/BestImage.rb +61 -0
- data/samples/mturk/best_image/best_image.properties +39 -0
- data/samples/mturk/best_image/best_image.question +82 -0
- data/samples/mturk/blank_slate/BlankSlate.rb +63 -0
- data/samples/mturk/blank_slate/BlankSlate_multithreaded.rb +67 -0
- data/samples/mturk/helloworld/MTurkHelloWorld.rb +56 -0
- data/samples/mturk/helloworld/mturk.yml +8 -0
- data/samples/mturk/reviewer/Reviewer.rb +103 -0
- data/samples/mturk/reviewer/mturk.yml +8 -0
- data/samples/mturk/simple_survey/SimpleSurvey.rb +90 -0
- data/samples/mturk/simple_survey/simple_survey.question +30 -0
- data/samples/mturk/site_category/SiteCategory.rb +87 -0
- data/samples/mturk/site_category/externalpage.htm +71 -0
- data/samples/mturk/site_category/site_category.input +6 -0
- data/samples/mturk/site_category/site_category.properties +45 -0
- data/samples/mturk/site_category/site_category.question +9 -0
- data/test/mturk/test_changehittypeofhit.rb +130 -0
- data/test/mturk/test_error_handler.rb +135 -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_ruby-aws.rb +22 -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 +93 -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 +54 -0
- data/test/unit/test_threadpool.rb +50 -0
- data/test/unit/test_user_data_store.rb +80 -0
- metadata +158 -0
@@ -0,0 +1,261 @@
|
|
1
|
+
# Copyright:: Copyright (c) 2007 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 = "2007-06-21"
|
17
|
+
|
18
|
+
ABANDONMENT_RATE_QUALIFICATION_TYPE_ID = "00000000000000000070";
|
19
|
+
APPROVAL_RATE_QUALIFICATION_TYPE_ID = "000000000000000000L0";
|
20
|
+
REJECTION_RATE_QUALIFICATION_TYPE_ID = "000000000000000000S0";
|
21
|
+
RETURN_RATE_QUALIFICATION_TYPE_ID = "000000000000000000E0";
|
22
|
+
SUBMISSION_RATE_QUALIFICATION_TYPE_ID = "00000000000000000000";
|
23
|
+
LOCALE_QUALIFICATION_TYPE_ID = "00000000000000000071";
|
24
|
+
|
25
|
+
DEFAULT_THREADCOUNT = 10
|
26
|
+
|
27
|
+
serviceCall :RegisterHITType, :RegisterHITTypeResult, {
|
28
|
+
:AssignmentDurationInSeconds => 60*60,
|
29
|
+
:AutoApprovalDelayInSeconds => 60*60*24*7
|
30
|
+
}
|
31
|
+
|
32
|
+
serviceCall :CreateHIT, :HIT, { :MaxAssignments => 1,
|
33
|
+
:AssignmentDurationInSeconds => 60*60,
|
34
|
+
:AutoApprovalDelayInSeconds => 60*60*24*7,
|
35
|
+
:LifetimeInSeconds => 60*60*24,
|
36
|
+
}
|
37
|
+
|
38
|
+
serviceCall :DisableHIT, :DisableHITResult
|
39
|
+
serviceCall :DisposeHIT, :DisposeHITResult
|
40
|
+
serviceCall :ExtendHIT, :ExtendHITResult
|
41
|
+
serviceCall :ForceExpireHIT, :ForceExpireHITResult
|
42
|
+
serviceCall :GetHIT, :HIT, { :ResponseGroup => %w( Minimal HITDetail HITQuestion HITAssignmentSummary ) }
|
43
|
+
serviceCall :ChangeHITTypeOfHIT, :ChangeHITTypeOfHITResult
|
44
|
+
|
45
|
+
serviceCall :SearchHITs, :SearchHITsResult
|
46
|
+
serviceCall :GetReviewableHITs, :GetReviewableHITsResult
|
47
|
+
serviceCall :SetHITAsReviewing, :SetHITAsReviewingResult
|
48
|
+
serviceCall :GetAssignmentsForHIT, :GetAssignmentsForHITResult
|
49
|
+
serviceCall :ApproveAssignment, :ApproveAssignmentResult
|
50
|
+
serviceCall :RejectAssignment, :RejectAssignmentResult
|
51
|
+
|
52
|
+
paginate :SearchHITs, :HIT
|
53
|
+
paginate :GetReviewableHITs, :HIT
|
54
|
+
paginate :GetAssignmentsForHIT, :Assignment
|
55
|
+
|
56
|
+
serviceCall :GrantBonus, :GrantBonusResult
|
57
|
+
serviceCall :GetBonusPayments, :GetBonusPaymentsResult
|
58
|
+
|
59
|
+
serviceCall :CreateQualificationType, :QualificationType, { :QualificationTypeStatus => 'Active' }
|
60
|
+
serviceCall :GetQualificationType, :QualificationType
|
61
|
+
serviceCall :SearchQualificationTypes, :SearchQualificationTypesResult, { :MustBeRequestable => true }
|
62
|
+
serviceCall :UpdateQualificationType, :QualificationType
|
63
|
+
serviceCall :GetQualificationsForQualificationType, :GetQualificationsForQualificationTypeResult, { :Status => 'Granted' }
|
64
|
+
serviceCall :GetHITsForQualificationType, :GetHITsForQualificationTypeResult
|
65
|
+
|
66
|
+
paginate :SearchQualificationTypes, :QualificationType
|
67
|
+
paginate :GetQualificationsForQualificationType, :Qualification
|
68
|
+
|
69
|
+
serviceCall :AssignQualification, :AssignQualificationResult
|
70
|
+
serviceCall :GetQualificationRequests, :GetQualificationRequestsResult
|
71
|
+
serviceCall :GrantQualification, :GrantQualificationResult
|
72
|
+
serviceCall :RejectQualificationRequest, :RejectQualificationRequestResult
|
73
|
+
serviceCall :GetQualificationScore, :Qualification
|
74
|
+
serviceCall :UpdateQualificationScore, :UpdateQualificationScoreResult
|
75
|
+
serviceCall :RevokeQualification, :RevokeQualificationResult
|
76
|
+
|
77
|
+
paginate :GetQualificationRequests, :QualificationRequest
|
78
|
+
|
79
|
+
serviceCall :SetHITTypeNotification, :SetHITTypeNotificationResult
|
80
|
+
serviceCall :SetWorkerAcceptLimit, :SetWorkerAcceptLimitResult
|
81
|
+
serviceCall :GetWorkerAcceptLimit, :GetWorkerAcceptLimitResult
|
82
|
+
serviceCall :BlockWorker, :BlockWorkerResult
|
83
|
+
serviceCall :UnblockWorker, :BlockWorkerResult
|
84
|
+
|
85
|
+
serviceCall :GetFileUploadURL, :GetFileUploadURLResult
|
86
|
+
serviceCall :GetAccountBalance, :GetAccountBalanceResult
|
87
|
+
serviceCall :GetRequesterStatistic, :GetStatisticResult, { :Count => 1 }
|
88
|
+
|
89
|
+
serviceCall :NotifyWorkers, :NotifyWorkersResult
|
90
|
+
|
91
|
+
def initialize(args={})
|
92
|
+
newargs = args.dup
|
93
|
+
unless args[:Config].nil?
|
94
|
+
loaded = Amazon::Util::DataReader.load( args[:Config], :YAML )
|
95
|
+
newargs = args.merge loaded.inject({}) {|a,b| a[b[0].to_sym] = b[1] ; a }
|
96
|
+
end
|
97
|
+
@threadcount = args[:ThreadCount].to_i
|
98
|
+
@threadcount = DEFAULT_THREADCOUNT unless @threadcount >= 1
|
99
|
+
raise "Cannot override WSDL version ( #{WSDL_VERSION} )" unless args[:Version].nil? or args[:Version].equals? WSDL_VERSION
|
100
|
+
super newargs.merge( :Name => :AWSMechanicalTurkRequester,
|
101
|
+
:ServiceClass => Amazon::WebServices::MechanicalTurk,
|
102
|
+
:Version => WSDL_VERSION )
|
103
|
+
end
|
104
|
+
|
105
|
+
# Create a series of similar HITs, sharing common parameters. Utilizes HITType
|
106
|
+
# * hit_template is the array of parameters to pass to createHIT.
|
107
|
+
# * question_template will be passed as a template into ERB to generate the :Question parameter
|
108
|
+
# * the RequesterAnnotation parameter of hit_template will also be passed through ERB
|
109
|
+
# * hit_data_set should consist of an array of hashes defining unique instance variables utilized by question_template
|
110
|
+
def createHITs( hit_template, question_template, hit_data_set )
|
111
|
+
hit_template = hit_template.dup
|
112
|
+
lifetime = hit_template[:LifetimeInSeconds]
|
113
|
+
numassignments_template = hit_template[:MaxAssignments]
|
114
|
+
annotation_template = hit_template[:RequesterAnnotation]
|
115
|
+
hit_template.delete :LifetimeInSeconds
|
116
|
+
hit_template.delete :MaxAssignments
|
117
|
+
hit_template.delete :RequesterAnnotation
|
118
|
+
|
119
|
+
ht = hit_template[:HITTypeId] || registerHITType( hit_template )[:HITTypeId]
|
120
|
+
|
121
|
+
tp = Amazon::Util::ThreadPool.new @threadcount
|
122
|
+
|
123
|
+
created = [].extend(MonitorMixin)
|
124
|
+
failed = [].extend(MonitorMixin)
|
125
|
+
hit_data_set.each do |hd|
|
126
|
+
tp.addWork(hd) do |hit_data|
|
127
|
+
begin
|
128
|
+
b = Amazon::Util::Binder.new( hit_data )
|
129
|
+
annotation = b.erb_eval( annotation_template )
|
130
|
+
numassignments = b.erb_eval( numassignments_template.to_s ).to_i
|
131
|
+
question = b.erb_eval( question_template )
|
132
|
+
result = self.createHIT( :HITTypeId => ht,
|
133
|
+
:LifetimeInSeconds => lifetime,
|
134
|
+
:MaxAssignments => ( hit_data[:MaxAssignments] || numassignments || 1 ),
|
135
|
+
:Question => question,
|
136
|
+
:RequesterAnnotation => ( hit_data[:RequesterAnnotation] || annotation || "")
|
137
|
+
)
|
138
|
+
created.synchronize do
|
139
|
+
created << result
|
140
|
+
end
|
141
|
+
rescue => e
|
142
|
+
failed.synchronize do
|
143
|
+
failed << hit_data.merge( :Error => e.message, :Description => e.description )
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end # tp.addWork
|
147
|
+
end # hit_data_set.each
|
148
|
+
tp.finish
|
149
|
+
|
150
|
+
return :Created => created, :Failed => failed
|
151
|
+
end
|
152
|
+
|
153
|
+
# Update a series of HITs to belong to a new HITType
|
154
|
+
# * hit_template is the array of parameters to pass to registerHITType
|
155
|
+
# * hit_ids is a list of HITIds (strings)
|
156
|
+
def updateHITs( hit_template, hit_ids )
|
157
|
+
hit_template = hit_template.dup
|
158
|
+
hit_template.delete :LifetimeInSeconds
|
159
|
+
hit_template.delete :RequesterAnnotation
|
160
|
+
|
161
|
+
hit_type_id = registerHITType( hit_template )[:HITTypeId]
|
162
|
+
|
163
|
+
tp = Amazon::Util::ThreadPool.new @threadcount
|
164
|
+
|
165
|
+
updated = [].extend(MonitorMixin)
|
166
|
+
failed = [].extend(MonitorMixin)
|
167
|
+
hit_ids.each do |hid|
|
168
|
+
tp.addWork(hid) do |hit_id|
|
169
|
+
begin
|
170
|
+
changeHITTypeOfHIT( :HITId => hit_id, :HITTypeId => hit_type_id )
|
171
|
+
updated.synchronize do
|
172
|
+
updated << hit_id
|
173
|
+
end
|
174
|
+
rescue => e
|
175
|
+
failed.synchronize do
|
176
|
+
failed << { :HITId => hit_id, :Error => e.message }
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end # tp.addWork
|
180
|
+
end # hit_ids.each
|
181
|
+
tp.finish
|
182
|
+
|
183
|
+
return :Updated => updated, :Failed => failed
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
# Update a HIT with new properties.
|
188
|
+
# hit_id:: Id of the HIT to update
|
189
|
+
# hit_template:: hash ( parameter => value ) of parameters to update
|
190
|
+
#
|
191
|
+
# Acceptable attributes:
|
192
|
+
# * Title
|
193
|
+
# * Description
|
194
|
+
# * Keywords
|
195
|
+
# * Reward
|
196
|
+
# * QualificationRequirement
|
197
|
+
# * AutoApprovalDelayInSeconds
|
198
|
+
# * AssignmentDurationInSeconds
|
199
|
+
#
|
200
|
+
# Behind the scenes, this function retrieves the HIT, merges the HITs
|
201
|
+
# current attributes with any you specify, and registers a new HIT
|
202
|
+
# Template. It then uses the new ChangeHITTypeOfHIT function to move
|
203
|
+
# your HIT to the newly-created HIT Template.
|
204
|
+
def updateHIT( hit_id, hit_template )
|
205
|
+
hit_template = hit_template.dup
|
206
|
+
|
207
|
+
hit = getHIT( :HITId => hit_id )
|
208
|
+
|
209
|
+
props = %w( Title Description Keywords Reward QualificationRequirement
|
210
|
+
AutoApprovalDelayInSeconds AssignmentDurationInSeconds
|
211
|
+
).collect {|str| str.to_sym }
|
212
|
+
|
213
|
+
props.each do |p|
|
214
|
+
hit_template[p] = hit[p] if hit_template[p].nil?
|
215
|
+
end
|
216
|
+
|
217
|
+
hit_type_id = registerHITType( hit_template )[:HITTypeId]
|
218
|
+
|
219
|
+
changeHITTypeOfHIT( :HITId => hit_id, :HITTypeId => hit_type_id )
|
220
|
+
end
|
221
|
+
|
222
|
+
|
223
|
+
def getHITResults( list )
|
224
|
+
results = [].extend(MonitorMixin)
|
225
|
+
tp = Amazon::Util::ThreadPool.new @threadcount
|
226
|
+
list.each do |line|
|
227
|
+
tp.addWork(line) do |h|
|
228
|
+
hit = getHIT( :HITId => h[:HITId] )
|
229
|
+
getAssignmentsForHITAll( :HITId => h[:HITId] ).each {|assignment|
|
230
|
+
results.synchronize do
|
231
|
+
results << ( hit.merge( assignment ) )
|
232
|
+
end
|
233
|
+
}
|
234
|
+
end
|
235
|
+
end
|
236
|
+
tp.finish
|
237
|
+
results.flatten
|
238
|
+
end
|
239
|
+
|
240
|
+
# Returns available funds in USD
|
241
|
+
# Calls getAccountBalance and parses out the correct amount
|
242
|
+
def availableFunds
|
243
|
+
return getAccountBalance[:AvailableBalance][:Amount]
|
244
|
+
end
|
245
|
+
|
246
|
+
# helper function to simplify answer XML
|
247
|
+
def simplifyAnswer( answerXML )
|
248
|
+
answerHash = Amazon::WebServices::Util::XMLSimplifier.simplify REXML::Document.new(answerXML)
|
249
|
+
list = [answerHash[:Answer]].flatten
|
250
|
+
list.inject({}) { |list, answer|
|
251
|
+
id = answer[:QuestionIdentifier]
|
252
|
+
result = answer[:FreeText] || answer[:SelectionIdentifier] || answer[:UploadedFileKey]
|
253
|
+
list[id] = result
|
254
|
+
list
|
255
|
+
}
|
256
|
+
end
|
257
|
+
|
258
|
+
end # MechanicalTurkRequester
|
259
|
+
|
260
|
+
end # Amazon::WebServices
|
261
|
+
end # Amazon
|
@@ -0,0 +1,136 @@
|
|
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/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 )
|
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 )
|
74
|
+
log "Handling error: #{error.inspect}"
|
75
|
+
case error.class.to_s
|
76
|
+
when 'Timeout::Error','SOAP::HTTPStreamError'
|
77
|
+
if methodRetryable( method )
|
78
|
+
return :RetryImmediate
|
79
|
+
else
|
80
|
+
return :Unknown
|
81
|
+
end
|
82
|
+
when 'SOAP::FaultError'
|
83
|
+
case error.faultcode.data
|
84
|
+
when "aws:Server.ServiceUnavailable"
|
85
|
+
return :RetryWithBackoff
|
86
|
+
else
|
87
|
+
return :Unknown
|
88
|
+
end
|
89
|
+
when 'Amazon::WebServices::Util::ValidationException'
|
90
|
+
return :Fail
|
91
|
+
when 'RuntimeError'
|
92
|
+
case error.message
|
93
|
+
when 'Throttled'
|
94
|
+
return :RetryWithBackoff
|
95
|
+
else
|
96
|
+
return :RetryImmediate
|
97
|
+
end
|
98
|
+
else
|
99
|
+
return :Unknown
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def canRetry( try )
|
104
|
+
try <= MAX_RETRY
|
105
|
+
end
|
106
|
+
|
107
|
+
def doBackoff( try )
|
108
|
+
return false unless canRetry(try)
|
109
|
+
delay = BACKOFF_INITIAL * ( BACKOFF_BASE ** try )
|
110
|
+
sleep delay
|
111
|
+
return true
|
112
|
+
end
|
113
|
+
|
114
|
+
def isResultTag( tag )
|
115
|
+
tag.to_s =~ RESULT_PATTERN or ACCEPTABLE_RESULTS.include?( tag.to_s )
|
116
|
+
end
|
117
|
+
|
118
|
+
def validateResponse(response)
|
119
|
+
log "Validating response: #{response.inspect}"
|
120
|
+
raise 'Throttled' if response[:Errors] and response[:Errors][:Error] and response[:Errors][:Error][:Code] == "ServiceUnavailable"
|
121
|
+
raise Util::ValidationException.new(response) unless response[:OperationRequest][:Errors].nil?
|
122
|
+
resultTags = response.keys.find_all {|r| isResultTag( r ) }
|
123
|
+
raise Util::ValidationException.new(response, "Didn't get back an acceptable result tag (got back #{response.keys.join(',')})") if resultTags.empty?
|
124
|
+
resultTags.each do |resultTag|
|
125
|
+
log "using result tag <#{resultTag}>"
|
126
|
+
result = response[resultTag]
|
127
|
+
raise Util::ValidationException.new(response) unless result[:Request][:Errors].nil?
|
128
|
+
end
|
129
|
+
response
|
130
|
+
end
|
131
|
+
|
132
|
+
end # MechanicalTurkErrorHandler
|
133
|
+
|
134
|
+
end # Amazon::WebServices::MTurk
|
135
|
+
end # Amazon::WebServices
|
136
|
+
end # Amazon
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# Copyright:: Copyright (c) 2007 Amazon Technologies, Inc.
|
2
|
+
# License:: Apache License, Version 2.0
|
3
|
+
|
4
|
+
require 'rexml/element'
|
5
|
+
|
6
|
+
module Amazon
|
7
|
+
module WebServices
|
8
|
+
module MTurk
|
9
|
+
|
10
|
+
class QuestionGenerator
|
11
|
+
|
12
|
+
def self.build(type=:Basic)
|
13
|
+
question = self.new(type)
|
14
|
+
yield question
|
15
|
+
return question.to_xml
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(type=:Basic)
|
19
|
+
@overview = nil
|
20
|
+
@questions = []
|
21
|
+
@type = type
|
22
|
+
end
|
23
|
+
|
24
|
+
def ask(*args)
|
25
|
+
case @type
|
26
|
+
when :Basic
|
27
|
+
askBasic( args.join )
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def askBasic(text)
|
32
|
+
id = "BasicQuestion#{@questions.size+1}"
|
33
|
+
question = REXML::Element.new 'Text'
|
34
|
+
question.text = text
|
35
|
+
answerSpec = "<FreeTextAnswer/>"
|
36
|
+
@questions << { :Id => id, :Question => question.to_s, :AnswerSpec => answerSpec }
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_xml
|
40
|
+
components = [PREAMBLE]
|
41
|
+
components << OVERVIEW % @overview unless @overview.nil?
|
42
|
+
for question in @questions
|
43
|
+
components << QUESTION % [ question[:Id], question[:Question], question[:AnswerSpec] ]
|
44
|
+
end
|
45
|
+
components << [TAIL]
|
46
|
+
return components.join
|
47
|
+
end
|
48
|
+
|
49
|
+
PREAMBLE = '<?xml version="1.0" encoding="UTF-8"?>'+"\n"+'<QuestionForm xmlns="http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2005-10-01/QuestionForm.xsd">'
|
50
|
+
OVERVIEW = '<Overview>%s</Overview>'
|
51
|
+
QUESTION = '<Question><QuestionIdentifier>%s</QuestionIdentifier><QuestionContent>%s</QuestionContent><AnswerSpecification>%s</AnswerSpecification></Question>'
|
52
|
+
TAIL = '</QuestionForm>'
|
53
|
+
|
54
|
+
end # QuestionGenerator
|
55
|
+
|
56
|
+
end # Amazon::WebServices::MTurk
|
57
|
+
end # Amazon::WebServices
|
58
|
+
end # Amazon
|