sprout-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.
Files changed (70) hide show
  1. data/History.txt +38 -0
  2. data/LICENSE.txt +202 -0
  3. data/Manifest.txt +69 -0
  4. data/NOTICE.txt +4 -0
  5. data/README.txt +105 -0
  6. data/Rakefile +20 -0
  7. data/bin/ruby-aws +9 -0
  8. data/lib/amazon/util.rb +10 -0
  9. data/lib/amazon/util/binder.rb +44 -0
  10. data/lib/amazon/util/data_reader.rb +157 -0
  11. data/lib/amazon/util/filter_chain.rb +79 -0
  12. data/lib/amazon/util/hash_nesting.rb +93 -0
  13. data/lib/amazon/util/lazy_results.rb +59 -0
  14. data/lib/amazon/util/logging.rb +23 -0
  15. data/lib/amazon/util/paginated_iterator.rb +70 -0
  16. data/lib/amazon/util/proactive_results.rb +116 -0
  17. data/lib/amazon/util/threadpool.rb +129 -0
  18. data/lib/amazon/util/user_data_store.rb +100 -0
  19. data/lib/amazon/webservices/mechanical_turk.rb +117 -0
  20. data/lib/amazon/webservices/mechanical_turk_requester.rb +340 -0
  21. data/lib/amazon/webservices/mturk/mechanical_turk_error_handler.rb +136 -0
  22. data/lib/amazon/webservices/mturk/question_generator.rb +58 -0
  23. data/lib/amazon/webservices/util/amazon_authentication_relay.rb +64 -0
  24. data/lib/amazon/webservices/util/command_line.rb +156 -0
  25. data/lib/amazon/webservices/util/convenience_wrapper.rb +90 -0
  26. data/lib/amazon/webservices/util/filter_proxy.rb +45 -0
  27. data/lib/amazon/webservices/util/mock_transport.rb +70 -0
  28. data/lib/amazon/webservices/util/request_signer.rb +42 -0
  29. data/lib/amazon/webservices/util/rest_transport.rb +108 -0
  30. data/lib/amazon/webservices/util/soap_simplifier.rb +48 -0
  31. data/lib/amazon/webservices/util/soap_transport.rb +38 -0
  32. data/lib/amazon/webservices/util/soap_transport_header_handler.rb +27 -0
  33. data/lib/amazon/webservices/util/unknown_result_exception.rb +27 -0
  34. data/lib/amazon/webservices/util/validation_exception.rb +55 -0
  35. data/lib/amazon/webservices/util/xml_simplifier.rb +61 -0
  36. data/lib/ruby-aws.rb +21 -0
  37. data/lib/ruby-aws/version.rb +8 -0
  38. data/samples/mturk/best_image/BestImage.rb +61 -0
  39. data/samples/mturk/best_image/best_image.properties +39 -0
  40. data/samples/mturk/best_image/best_image.question +82 -0
  41. data/samples/mturk/blank_slate/BlankSlate.rb +63 -0
  42. data/samples/mturk/blank_slate/BlankSlate_multithreaded.rb +67 -0
  43. data/samples/mturk/helloworld/MTurkHelloWorld.rb +56 -0
  44. data/samples/mturk/helloworld/mturk.yml +8 -0
  45. data/samples/mturk/reviewer/Reviewer.rb +103 -0
  46. data/samples/mturk/reviewer/mturk.yml +8 -0
  47. data/samples/mturk/simple_survey/SimpleSurvey.rb +90 -0
  48. data/samples/mturk/simple_survey/simple_survey.question +30 -0
  49. data/samples/mturk/site_category/SiteCategory.rb +87 -0
  50. data/samples/mturk/site_category/externalpage.htm +71 -0
  51. data/samples/mturk/site_category/site_category.input +6 -0
  52. data/samples/mturk/site_category/site_category.properties +45 -0
  53. data/samples/mturk/site_category/site_category.question +9 -0
  54. data/test/mturk/test_changehittypeofhit.rb +130 -0
  55. data/test/mturk/test_error_handler.rb +135 -0
  56. data/test/mturk/test_mechanical_turk_requester.rb +178 -0
  57. data/test/mturk/test_mock_mechanical_turk_requester.rb +205 -0
  58. data/test/test_ruby-aws.rb +22 -0
  59. data/test/unit/test_binder.rb +89 -0
  60. data/test/unit/test_data_reader.rb +135 -0
  61. data/test/unit/test_exceptions.rb +32 -0
  62. data/test/unit/test_hash_nesting.rb +93 -0
  63. data/test/unit/test_lazy_results.rb +89 -0
  64. data/test/unit/test_mock_transport.rb +132 -0
  65. data/test/unit/test_paginated_iterator.rb +58 -0
  66. data/test/unit/test_proactive_results.rb +108 -0
  67. data/test/unit/test_question_generator.rb +54 -0
  68. data/test/unit/test_threadpool.rb +50 -0
  69. data/test/unit/test_user_data_store.rb +80 -0
  70. metadata +177 -0
@@ -0,0 +1,340 @@
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
+ :ResponseGroup => %w( HITQuestion Minimal)
37
+ }
38
+
39
+ serviceCall :DisableHIT, :DisableHITResult
40
+ serviceCall :DisposeHIT, :DisposeHITResult
41
+ serviceCall :ExtendHIT, :ExtendHITResult
42
+ serviceCall :ForceExpireHIT, :ForceExpireHITResult
43
+ serviceCall :GetHIT, :HIT, { :ResponseGroup => %w( Minimal HITDetail HITQuestion HITAssignmentSummary ) }
44
+ serviceCall :ChangeHITTypeOfHIT, :ChangeHITTypeOfHITResult
45
+
46
+ serviceCall :SearchHITs, :SearchHITsResult
47
+ serviceCall :GetReviewableHITs, :GetReviewableHITsResult
48
+ serviceCall :SetHITAsReviewing, :SetHITAsReviewingResult
49
+ serviceCall :GetAssignmentsForHIT, :GetAssignmentsForHITResult
50
+ serviceCall :ApproveAssignment, :ApproveAssignmentResult
51
+ serviceCall :RejectAssignment, :RejectAssignmentResult
52
+
53
+ paginate :SearchHITs, :HIT
54
+ paginate :GetReviewableHITs, :HIT
55
+ paginate :GetAssignmentsForHIT, :Assignment
56
+
57
+ serviceCall :GrantBonus, :GrantBonusResult
58
+ serviceCall :GetBonusPayments, :GetBonusPaymentsResult
59
+
60
+ serviceCall :CreateQualificationType, :QualificationType, { :QualificationTypeStatus => 'Active' }
61
+ serviceCall :GetQualificationType, :QualificationType
62
+ serviceCall :SearchQualificationTypes, :SearchQualificationTypesResult, { :MustBeRequestable => true }
63
+ serviceCall :UpdateQualificationType, :QualificationType
64
+ serviceCall :GetQualificationsForQualificationType, :GetQualificationsForQualificationTypeResult, { :Status => 'Granted' }
65
+ serviceCall :GetHITsForQualificationType, :GetHITsForQualificationTypeResult
66
+
67
+ paginate :SearchQualificationTypes, :QualificationType
68
+ paginate :GetQualificationsForQualificationType, :Qualification
69
+
70
+ serviceCall :AssignQualification, :AssignQualificationResult
71
+ serviceCall :GetQualificationRequests, :GetQualificationRequestsResult
72
+ serviceCall :GrantQualification, :GrantQualificationResult
73
+ serviceCall :RejectQualificationRequest, :RejectQualificationRequestResult
74
+ serviceCall :GetQualificationScore, :Qualification
75
+ serviceCall :UpdateQualificationScore, :UpdateQualificationScoreResult
76
+ serviceCall :RevokeQualification, :RevokeQualificationResult
77
+
78
+ paginate :GetQualificationRequests, :QualificationRequest
79
+
80
+ serviceCall :SetHITTypeNotification, :SetHITTypeNotificationResult
81
+ serviceCall :SetWorkerAcceptLimit, :SetWorkerAcceptLimitResult
82
+ serviceCall :GetWorkerAcceptLimit, :GetWorkerAcceptLimitResult
83
+ serviceCall :BlockWorker, :BlockWorkerResult
84
+ serviceCall :UnblockWorker, :BlockWorkerResult
85
+
86
+ serviceCall :GetFileUploadURL, :GetFileUploadURLResult
87
+ serviceCall :GetAccountBalance, :GetAccountBalanceResult
88
+ serviceCall :GetRequesterStatistic, :GetStatisticResult, { :Count => 1 }
89
+
90
+ serviceCall :NotifyWorkers, :NotifyWorkersResult
91
+
92
+ def initialize(args={})
93
+ newargs = args.dup
94
+ unless args[:Config].nil?
95
+ loaded = Amazon::Util::DataReader.load( args[:Config], :YAML )
96
+ newargs = args.merge loaded.inject({}) {|a,b| a[b[0].to_sym] = b[1] ; a }
97
+ end
98
+ @threadcount = args[:ThreadCount].to_i
99
+ @threadcount = DEFAULT_THREADCOUNT unless @threadcount >= 1
100
+ raise "Cannot override WSDL version ( #{WSDL_VERSION} )" unless args[:Version].nil? or args[:Version].equals? WSDL_VERSION
101
+ super newargs.merge( :Name => :AWSMechanicalTurkRequester,
102
+ :ServiceClass => Amazon::WebServices::MechanicalTurk,
103
+ :Version => WSDL_VERSION )
104
+ end
105
+
106
+ # Create a series of similar HITs, sharing common parameters. Utilizes HITType
107
+ # * hit_template is the array of parameters to pass to createHIT.
108
+ # * question_template will be passed as a template into ERB to generate the :Question parameter
109
+ # * the RequesterAnnotation parameter of hit_template will also be passed through ERB
110
+ # * hit_data_set should consist of an array of hashes defining unique instance variables utilized by question_template
111
+ def createHITs( hit_template, question_template, hit_data_set )
112
+ hit_template = hit_template.dup
113
+ lifetime = hit_template[:LifetimeInSeconds]
114
+ numassignments_template = hit_template[:MaxAssignments]
115
+ annotation_template = hit_template[:RequesterAnnotation]
116
+ hit_template.delete :LifetimeInSeconds
117
+ hit_template.delete :MaxAssignments
118
+ hit_template.delete :RequesterAnnotation
119
+
120
+ ht = hit_template[:HITTypeId] || registerHITType( hit_template )[:HITTypeId]
121
+
122
+ tp = Amazon::Util::ThreadPool.new @threadcount
123
+
124
+ created = [].extend(MonitorMixin)
125
+ failed = [].extend(MonitorMixin)
126
+ hit_data_set.each do |hd|
127
+ tp.addWork(hd) do |hit_data|
128
+ begin
129
+ b = Amazon::Util::Binder.new( hit_data )
130
+ annotation = b.erb_eval( annotation_template )
131
+ numassignments = b.erb_eval( numassignments_template.to_s ).to_i
132
+ question = b.erb_eval( question_template )
133
+ result = self.createHIT( :HITTypeId => ht,
134
+ :LifetimeInSeconds => lifetime,
135
+ :MaxAssignments => ( hit_data[:MaxAssignments] || numassignments || 1 ),
136
+ :Question => question,
137
+ :RequesterAnnotation => ( hit_data[:RequesterAnnotation] || annotation || "")
138
+ )
139
+ created.synchronize do
140
+ created << result
141
+ end
142
+ rescue => e
143
+ failed.synchronize do
144
+ failed << hit_data.merge( :Error => e.message, :Description => e.description )
145
+ end
146
+ end
147
+ end # tp.addWork
148
+ end # hit_data_set.each
149
+ tp.finish
150
+
151
+ return :Created => created, :Failed => failed
152
+ end
153
+
154
+ # Update a series of HITs to belong to a new HITType
155
+ # * hit_template is the array of parameters to pass to registerHITType
156
+ # * hit_ids is a list of HITIds (strings)
157
+ def updateHITs( hit_template, hit_ids )
158
+ hit_template = hit_template.dup
159
+ hit_template.delete :LifetimeInSeconds
160
+ hit_template.delete :RequesterAnnotation
161
+
162
+ hit_type_id = registerHITType( hit_template )[:HITTypeId]
163
+
164
+ tp = Amazon::Util::ThreadPool.new @threadcount
165
+
166
+ updated = [].extend(MonitorMixin)
167
+ failed = [].extend(MonitorMixin)
168
+ hit_ids.each do |hid|
169
+ tp.addWork(hid) do |hit_id|
170
+ begin
171
+ changeHITTypeOfHIT( :HITId => hit_id, :HITTypeId => hit_type_id )
172
+ updated.synchronize do
173
+ updated << hit_id
174
+ end
175
+ rescue => e
176
+ failed.synchronize do
177
+ failed << { :HITId => hit_id, :Error => e.message }
178
+ end
179
+ end
180
+ end # tp.addWork
181
+ end # hit_ids.each
182
+ tp.finish
183
+
184
+ return :Updated => updated, :Failed => failed
185
+ end
186
+
187
+ # Forces expiration of hits provided. Useful for pausing HITs
188
+ # * hit_ids is an array of HITIds
189
+ def forceExpireHITs(hit_ids)
190
+ tp = Amazon::Util::ThreadPool.new @threadcount
191
+
192
+ expired = [].extend(MonitorMixin)
193
+ failed = [].extend(MonitorMixin)
194
+ hit_ids.each do |hid|
195
+ tp.addWork(hid) do |hit_id|
196
+ begin
197
+ forceExpireHIT( :HITId => hit_id )
198
+ expired.synchronize do
199
+ expired << hit_id
200
+ end
201
+ rescue => e
202
+ failed.synchronize do
203
+ failed << { :HITId => hit_id, :Error => e.message }
204
+ end
205
+ end
206
+ end # tp.addWork
207
+ end # hit_ids.each
208
+ tp.finish
209
+
210
+ return :Expired => expired, :Failed => failed
211
+ end
212
+
213
+ # Adds time for given hits. Useful for resuming paused/expired HITs
214
+ # * hit_ids is an array of HITIds
215
+ # * time is time in second we want to add
216
+ def extendHITs(hit_ids, time)
217
+ tp = Amazon::Util::ThreadPool.new @threadcount
218
+
219
+ extended = [].extend(MonitorMixin)
220
+ failed = [].extend(MonitorMixin)
221
+ hit_ids.each do |hid|
222
+ tp.addWork(hid) do |hit_id|
223
+ begin
224
+ extendHIT( :HITId => hit_id, :ExpirationIncrementInSeconds => time )
225
+ extended.synchronize do
226
+ extended << hit_id
227
+ end
228
+ rescue => e
229
+ failed.synchronize do
230
+ failed << { :HITId => hit_id, :Error => e.message }
231
+ end
232
+ end
233
+ end # tp.addWork
234
+ end # hit_ids.each
235
+ tp.finish
236
+
237
+ return :Extended => extended, :Failed => failed
238
+ end
239
+
240
+ # Deletes given hits from mturk
241
+ # * hit_ids is an array of HITIds
242
+ def deleteHITs(hit_ids)
243
+ tp = Amazon::Util::ThreadPool.new @threadcount
244
+
245
+ deleted = [].extend(MonitorMixin)
246
+ failed = [].extend(MonitorMixin)
247
+ hit_ids.each do |hid|
248
+ tp.addWork(hid) do |hit_id|
249
+ begin
250
+ disableHIT( :HITId => hit_id )
251
+ deleted.synchronize do
252
+ deleted << hit_id
253
+ end
254
+ rescue => e
255
+ failed.synchronize do
256
+ failed << { :HITId => hit_id, :Error => e.message }
257
+ end
258
+ end
259
+ end # tp.addWork
260
+ end # hit_ids.each
261
+ tp.finish
262
+
263
+ return :Deleted => deleted, :Failed => failed
264
+ end
265
+
266
+ # Update a HIT with new properties.
267
+ # hit_id:: Id of the HIT to update
268
+ # hit_template:: hash ( parameter => value ) of parameters to update
269
+ #
270
+ # Acceptable attributes:
271
+ # * Title
272
+ # * Description
273
+ # * Keywords
274
+ # * Reward
275
+ # * QualificationRequirement
276
+ # * AutoApprovalDelayInSeconds
277
+ # * AssignmentDurationInSeconds
278
+ #
279
+ # Behind the scenes, this function retrieves the HIT, merges the HITs
280
+ # current attributes with any you specify, and registers a new HIT
281
+ # Template. It then uses the new ChangeHITTypeOfHIT function to move
282
+ # your HIT to the newly-created HIT Template.
283
+ def updateHIT( hit_id, hit_template )
284
+ hit_template = hit_template.dup
285
+
286
+ hit = getHIT( :HITId => hit_id )
287
+
288
+ props = %w( Title Description Keywords Reward QualificationRequirement
289
+ AutoApprovalDelayInSeconds AssignmentDurationInSeconds
290
+ ).collect {|str| str.to_sym }
291
+
292
+ props.each do |p|
293
+ hit_template[p] = hit[p] if hit_template[p].nil?
294
+ end
295
+
296
+ hit_type_id = registerHITType( hit_template )[:HITTypeId]
297
+
298
+ changeHITTypeOfHIT( :HITId => hit_id, :HITTypeId => hit_type_id )
299
+ end
300
+
301
+
302
+ def getHITResults( list )
303
+ results = [].extend(MonitorMixin)
304
+ tp = Amazon::Util::ThreadPool.new @threadcount
305
+ list.each do |line|
306
+ tp.addWork(line) do |h|
307
+ hit = getHIT( :HITId => h[:HITId] )
308
+ getAssignmentsForHITAll( :HITId => h[:HITId] ).each {|assignment|
309
+ results.synchronize do
310
+ results << ( hit.merge( assignment ) )
311
+ end
312
+ }
313
+ end
314
+ end
315
+ tp.finish
316
+ results.flatten
317
+ end
318
+
319
+ # Returns available funds in USD
320
+ # Calls getAccountBalance and parses out the correct amount
321
+ def availableFunds
322
+ return getAccountBalance[:AvailableBalance][:Amount]
323
+ end
324
+
325
+ # helper function to simplify answer XML
326
+ def simplifyAnswer( answerXML )
327
+ answerHash = Amazon::WebServices::Util::XMLSimplifier.simplify REXML::Document.new(answerXML)
328
+ list = [answerHash[:Answer]].flatten
329
+ list.inject({}) { |list, answer|
330
+ id = answer[:QuestionIdentifier]
331
+ result = answer[:FreeText] || answer[:SelectionIdentifier] || answer[:UploadedFileKey]
332
+ list[id] = result
333
+ list
334
+ }
335
+ end
336
+
337
+ end # MechanicalTurkRequester
338
+
339
+ end # Amazon::WebServices
340
+ 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