rturk 1.0.5 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitmodules +3 -0
- data/.yardoc +0 -0
- data/README.markdown +49 -29
- data/Rakefile +53 -0
- data/TODO.markdown +3 -0
- data/VERSION +1 -1
- data/examples/blank_slate.rb +21 -4
- data/examples/create_hit.rb +19 -0
- data/examples/newtweet.html +0 -1
- data/examples/review_answer.rb +18 -5
- data/lib/rturk.rb +33 -6
- data/lib/rturk/adapters/answers.rb +38 -0
- data/lib/rturk/adapters/assignment.rb +75 -0
- data/lib/rturk/adapters/hit.rb +97 -0
- data/lib/rturk/builders/qualification_builder.rb +68 -0
- data/lib/rturk/builders/qualifications_builder.rb +43 -0
- data/lib/rturk/builders/question_builder.rb +51 -0
- data/lib/rturk/errors.rb +5 -0
- data/lib/rturk/logger.rb +20 -0
- data/lib/rturk/macros.rb +35 -0
- data/lib/rturk/operation.rb +86 -0
- data/lib/rturk/operations/approve_assignment.rb +28 -0
- data/lib/rturk/operations/create_hit.rb +77 -0
- data/lib/rturk/operations/disable_hit.rb +19 -0
- data/lib/rturk/operations/dispose_hit.rb +19 -0
- data/lib/rturk/operations/force_expire_hit.rb +18 -0
- data/lib/rturk/operations/get_account_balance.rb +15 -0
- data/lib/rturk/operations/get_assignments_for_hit.rb +24 -0
- data/lib/rturk/operations/get_hit.rb +21 -0
- data/lib/rturk/operations/get_reviewable_hits.rb +23 -0
- data/lib/rturk/operations/grant_bonus.rb +26 -0
- data/lib/rturk/operations/reject_assignment.rb +23 -0
- data/lib/rturk/requester.rb +58 -76
- data/lib/rturk/response.rb +58 -0
- data/lib/rturk/responses/create_hit_response.rb +33 -0
- data/lib/rturk/responses/get_account_balance_response.rb +11 -0
- data/lib/rturk/responses/get_assignments_for_hit_response.rb +43 -0
- data/lib/rturk/responses/get_hit_response.rb +80 -0
- data/lib/rturk/responses/get_reviewable_hits_response.rb +33 -0
- data/lib/rturk/utilities.rb +19 -1
- data/lib/rturk/xml_utilities.rb +23 -0
- data/rturk.gemspec +143 -0
- data/spec/adapters/answers_spec.rb +27 -0
- data/spec/adapters/assignment_spec.rb +0 -0
- data/spec/adapters/hit_spec.rb +46 -0
- data/spec/builders/qualification_spec.rb +53 -0
- data/spec/builders/qualifications_spec.rb +30 -0
- data/spec/builders/question_spec.rb +46 -0
- data/spec/fake_responses/approve_assignment.xml +5 -0
- data/spec/fake_responses/create_hit.xml +12 -0
- data/spec/fake_responses/disable_hit.xml +5 -0
- data/spec/fake_responses/dispose_hit.xml +5 -0
- data/spec/fake_responses/force_expire_hit.xml +5 -0
- data/spec/fake_responses/get_account_balance.xml +10 -0
- data/spec/fake_responses/get_assignments.xml +35 -0
- data/spec/fake_responses/get_hit.xml +41 -0
- data/spec/fake_responses/get_reviewable_hits.xml +17 -0
- data/spec/fake_responses/grant_bonus.xml +5 -0
- data/spec/fake_responses/invalid_credentials.xml +12 -0
- data/spec/fake_responses/reject_assignment.xml +5 -0
- data/spec/operations/approve_assignment_spec.rb +27 -0
- data/spec/operations/create_hit_spec.rb +66 -0
- data/spec/operations/disable_hit_spec.rb +26 -0
- data/spec/operations/dispose_hit_spec.rb +26 -0
- data/spec/operations/force_expire_hit_spec.rb +26 -0
- data/spec/operations/get_account_balance_spec.rb +15 -0
- data/spec/operations/get_assignments_spec.rb +26 -0
- data/spec/operations/get_hit_spec.rb +18 -0
- data/spec/operations/get_reviewable_hits_spec.rb +0 -0
- data/spec/operations/grant_bonus_spec.rb +32 -0
- data/spec/operations/reject_assignment_spec.rb +26 -0
- data/spec/requester_spec.rb +7 -18
- data/spec/response_spec.rb +48 -0
- data/spec/rturk_spec.rb +27 -0
- data/spec/spec_helper.rb +28 -3
- data/spec/tmp +2 -0
- data/spec/xml_parse_spec.rb +32 -0
- metadata +94 -34
- data/examples/external_page.rb +0 -33
- data/lib/rturk/answer.rb +0 -20
- data/lib/rturk/custom_operations.rb +0 -80
- data/lib/rturk/external_question_builder.rb +0 -22
- data/spec/answer_spec.rb +0 -24
- data/spec/external_question_spec.rb +0 -27
@@ -0,0 +1,19 @@
|
|
1
|
+
# http://docs.amazonwebservices.com/AWSMturkAPI/2008-08-02/ApiReference_DisposeHITOperation.html
|
2
|
+
module RTurk
|
3
|
+
class DisposeHIT < Operation
|
4
|
+
|
5
|
+
operation 'DisposeHIT'
|
6
|
+
require_params :hit_id
|
7
|
+
attr_accessor :hit_id
|
8
|
+
|
9
|
+
def to_params
|
10
|
+
{'HITId' => self.hit_id}
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.DisposeHIT(*args, &blk)
|
16
|
+
RTurk::DisposeHIT.create(*args, &blk)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module RTurk
|
2
|
+
class ForceExpireHIT < Operation
|
3
|
+
|
4
|
+
operation 'ForceExpireHIT'
|
5
|
+
require_params :hit_id
|
6
|
+
attr_accessor :hit_id
|
7
|
+
|
8
|
+
def to_params
|
9
|
+
{'HITId' => self.hit_id}
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.ForceExpireHIT(*args, &blk)
|
15
|
+
RTurk::ForceExpireHIT.create(*args, &blk)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module RTurk
|
2
|
+
class GetAssignmentsForHIT < Operation
|
3
|
+
|
4
|
+
operation 'GetAssignmentsForHIT'
|
5
|
+
require_params :hit_id
|
6
|
+
|
7
|
+
attr_accessor :hit_id, :page_size, :page_number
|
8
|
+
|
9
|
+
def parse(xml)
|
10
|
+
GetAssignmentsForHITResponse.new(xml)
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_params
|
14
|
+
{'HITId' => hit_id,
|
15
|
+
'PageSize' => (page_size || 100),
|
16
|
+
'PageNumber' => page_number}
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
def self.GetAssignmentsForHIT(*args, &blk)
|
21
|
+
RTurk::GetAssignmentsForHIT.create(*args, &blk)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module RTurk
|
2
|
+
class GetHIT < Operation
|
3
|
+
|
4
|
+
operation 'GetHIT'
|
5
|
+
require_params :hit_id
|
6
|
+
attr_accessor :hit_id
|
7
|
+
|
8
|
+
def parse(xml)
|
9
|
+
RTurk::GetHITResponse.new(xml)
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_params
|
13
|
+
{"HITId" => self.hit_id}
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
def self.GetHIT(*args, &blk)
|
18
|
+
RTurk::GetHIT.create(*args, &blk)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module RTurk
|
2
|
+
class GetReviewableHITs < Operation
|
3
|
+
|
4
|
+
operation 'GetReviewableHITs'
|
5
|
+
attr_accessor :page_size, :page_number
|
6
|
+
|
7
|
+
def parse(xml)
|
8
|
+
RTurk::GetReviewableHITsResponse.new(xml)
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_params
|
12
|
+
{
|
13
|
+
'PageSize' => (page_size || 100),
|
14
|
+
'PageNumber' => (page_number || 1)
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
def self.GetReviewableHITs(*args, &blk)
|
20
|
+
RTurk::GetReviewableHITs.create(*args, &blk)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module RTurk
|
2
|
+
|
3
|
+
# == Grant Bonus operation
|
4
|
+
#
|
5
|
+
# Grants a worker a bonus
|
6
|
+
#
|
7
|
+
|
8
|
+
class GrantBonus < Operation
|
9
|
+
|
10
|
+
operation 'GrantBonus'
|
11
|
+
attr_accessor :assignment_id, :feedback, :worker_id, :amount, :currency
|
12
|
+
require_params :assignment_id, :worker_id, :amount, :feedback
|
13
|
+
|
14
|
+
def to_params
|
15
|
+
{'AssignmentId' => self.assignment_id,
|
16
|
+
'BonusAmount.1.Amount' => self.amount,
|
17
|
+
'BonusAmount.1.CurrencyCode' => (self.currency || 'USD'),
|
18
|
+
'RequesterFeedback' => self.feedback}
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
def self.GrantBonus(*args)
|
23
|
+
RTurk::GrantBonus.create(*args)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module RTurk
|
2
|
+
|
3
|
+
# == Reject Assignment
|
4
|
+
#
|
5
|
+
# Operation to reject a workers assignment, requires a reason
|
6
|
+
|
7
|
+
class RejectAssignment < Operation
|
8
|
+
|
9
|
+
operation 'RejectAssignment'
|
10
|
+
attr_accessor :assignment_id, :feedback
|
11
|
+
require_params :assignment_id, :feedback
|
12
|
+
|
13
|
+
def to_params
|
14
|
+
{'AssignmentId' => self.assignment_id,
|
15
|
+
'RequesterFeedback' => self.feedback}
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
def self.RejectAssignment(*args)
|
20
|
+
RTurk::RejectAssignment.create(*args)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/lib/rturk/requester.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'rubygems'
|
2
1
|
require 'cgi'
|
3
2
|
require 'digest/sha1'
|
4
3
|
require 'base64'
|
@@ -6,85 +5,68 @@ require 'time'
|
|
6
5
|
require 'base64'
|
7
6
|
require 'digest/sha1'
|
8
7
|
require 'restclient'
|
9
|
-
require 'xmlsimple'
|
10
8
|
|
11
9
|
module RTurk
|
12
10
|
class Requester
|
13
|
-
include RTurk::Utilities
|
14
|
-
include RTurk::CustomOperations
|
15
11
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
12
|
+
class << self
|
13
|
+
include RTurk::Utilities
|
14
|
+
|
15
|
+
# @param [Hash] params
|
16
|
+
# @option [String] 'Operation' The operation - Required
|
17
|
+
# @option [String] Any Pass any other params and they will be included in the request
|
18
|
+
#
|
19
|
+
def request(params = {})
|
20
|
+
params.delete_if {|k,v| v.nil? }
|
21
|
+
params = stringify_keys(params)
|
22
|
+
base_params = {
|
23
|
+
'Service'=>'AWSMechanicalTurkRequester',
|
24
|
+
'AWSAccessKeyId' => credentials.access_key,
|
25
|
+
'Timestamp' => Time.now.iso8601,
|
26
|
+
'Version' => '2008-08-02'
|
27
|
+
}
|
28
|
+
|
29
|
+
params.merge!(base_params)
|
30
|
+
signature = sign(credentials.secret_key, params['Service'], params['Operation'], params["Timestamp"])
|
31
|
+
params['Signature'] = signature
|
32
|
+
querystring = params.collect { |key, value| [CGI.escape(key.to_s), CGI.escape(value.to_s)].join('=') }.join('&') # order doesn't matter for the actual request
|
33
|
+
RTurk.log.debug "Sending request:\n\t #{credentials.host}?#{querystring}"
|
34
|
+
RestClient.get("#{credentials.host}?#{querystring}")
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def credentials
|
40
|
+
RTurk
|
41
|
+
end
|
42
|
+
|
43
|
+
def sign(secret_key, service,method,time)
|
44
|
+
msg = "#{service}#{method}#{time}"
|
45
|
+
return hmac_sha1(secret_key, msg )
|
46
|
+
end
|
47
|
+
|
48
|
+
def hmac_sha1(key, s)
|
49
|
+
ipad = [].fill(0x36, 0, 64)
|
50
|
+
opad = [].fill(0x5C, 0, 64)
|
51
|
+
key = key.unpack("C*")
|
52
|
+
key += [].fill(0, 0, 64-key.length) if key.length < 64
|
53
|
+
|
54
|
+
inner = []
|
55
|
+
64.times { |i| inner.push(key[i] ^ ipad[i]) }
|
56
|
+
inner += s.unpack("C*")
|
57
|
+
|
58
|
+
outer = []
|
59
|
+
64.times { |i| outer.push(key[i] ^ opad[i]) }
|
60
|
+
outer = outer.pack("c*")
|
61
|
+
outer += Digest::SHA1.digest(inner.pack("c*"))
|
62
|
+
|
63
|
+
return Base64::encode64(Digest::SHA1.digest(outer)).chomp
|
64
|
+
end
|
65
65
|
end
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
def hmac_sha1(key, s)
|
70
|
-
ipad = [].fill(0x36, 0, 64)
|
71
|
-
opad = [].fill(0x5C, 0, 64)
|
72
|
-
key = key.unpack("C*")
|
73
|
-
key += [].fill(0, 0, 64-key.length) if key.length < 64
|
74
|
-
|
75
|
-
inner = []
|
76
|
-
64.times { |i| inner.push(key[i] ^ ipad[i]) }
|
77
|
-
inner += s.unpack("C*")
|
78
|
-
|
79
|
-
outer = []
|
80
|
-
64.times { |i| outer.push(key[i] ^ opad[i]) }
|
81
|
-
outer = outer.pack("c*")
|
82
|
-
outer += Digest::SHA1.digest(inner.pack("c*"))
|
83
|
-
|
84
|
-
return Base64::encode64(Digest::SHA1.digest(outer)).chomp
|
85
|
-
end
|
86
|
-
|
87
|
-
|
88
66
|
end
|
89
|
-
|
67
|
+
|
68
|
+
def self.Request(*args)
|
69
|
+
RTurk::Requester.request(*args)
|
70
|
+
end
|
90
71
|
|
72
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module RTurk
|
4
|
+
class Response
|
5
|
+
include RTurk::XmlUtilities
|
6
|
+
|
7
|
+
#
|
8
|
+
# In some cases we want more than just a hash parsed from the returned
|
9
|
+
# XML. This class is our response object, and it can be extended for more
|
10
|
+
# functionality.
|
11
|
+
#
|
12
|
+
|
13
|
+
attr_reader :xml, :raw_xml
|
14
|
+
|
15
|
+
def initialize(response)
|
16
|
+
@raw_xml = response
|
17
|
+
@xml = Nokogiri::XML(@raw_xml)
|
18
|
+
raise_errors
|
19
|
+
end
|
20
|
+
|
21
|
+
def success?
|
22
|
+
@xml.xpath('//Request/IsValid').inner_text.strip == "True"
|
23
|
+
end
|
24
|
+
|
25
|
+
def errors
|
26
|
+
errors = []
|
27
|
+
@xml.xpath('//Errors').each do |error|
|
28
|
+
errors << {:code => error.xpath('Error/Code').inner_text,
|
29
|
+
:message => error.xpath('Error/Message').inner_text}
|
30
|
+
end
|
31
|
+
errors
|
32
|
+
end
|
33
|
+
|
34
|
+
def humanized_errors
|
35
|
+
string = self.errors.inject('') { |str, error|
|
36
|
+
str + "#{error[:code]}: #{error[:message]}"
|
37
|
+
}
|
38
|
+
string
|
39
|
+
end
|
40
|
+
|
41
|
+
def raise_errors
|
42
|
+
raise InvalidRequest, self.humanized_errors unless self.success?
|
43
|
+
end
|
44
|
+
|
45
|
+
def [](element_name)
|
46
|
+
self.elements[element_name]
|
47
|
+
end
|
48
|
+
|
49
|
+
def xpath(*args)
|
50
|
+
self.xml.xpath(*args)
|
51
|
+
end
|
52
|
+
|
53
|
+
def elements
|
54
|
+
xml_to_hash(@xml)
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Parses out the CreateHIT response
|
2
|
+
#
|
3
|
+
# Example response:
|
4
|
+
# <CreateHITResponse>
|
5
|
+
# <OperationRequest>
|
6
|
+
# <RequestId>ece2785b-6292-4b12-a60e-4c34847a7916</RequestId>
|
7
|
+
# </OperationRequest>
|
8
|
+
# <HIT>
|
9
|
+
# <Request>
|
10
|
+
# <IsValid>True</IsValid>
|
11
|
+
# </Request>
|
12
|
+
# <HITId>GBHZVQX3EHXZ2AYDY2T0</HITId>
|
13
|
+
# <HITTypeId>NYVZTQ1QVKJZXCYZCZVZ</HITTypeId>
|
14
|
+
# </HIT>
|
15
|
+
# </CreateHITResponse>
|
16
|
+
|
17
|
+
module RTurk
|
18
|
+
class CreateHITResponse < Response
|
19
|
+
|
20
|
+
def hit_id
|
21
|
+
@xml.xpath('//HITId').inner_text
|
22
|
+
end
|
23
|
+
|
24
|
+
def hit_type_id
|
25
|
+
@xml.xpath('//HITTypeId').inner_text
|
26
|
+
end
|
27
|
+
|
28
|
+
def hit
|
29
|
+
RTurk::Hit.new(self.hit_id, self.hit_type_id)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# <GetAssignmentsForHITResult>
|
2
|
+
# <Request>
|
3
|
+
# <IsValid>True</IsValid>
|
4
|
+
# </Request>
|
5
|
+
# <NumResults>1</NumResults>
|
6
|
+
# <TotalNumResults>1</TotalNumResults>
|
7
|
+
# <PageNumber>1</PageNumber>
|
8
|
+
# <Assignment>
|
9
|
+
# <AssignmentId>GYFTRHZ5J3DZREY48WNZE38ZR9RR1ZPMXGWE7WE0</AssignmentId>
|
10
|
+
# <WorkerId>AD20WXZZP9XXK</WorkerId>
|
11
|
+
# <HITId>GYFTRHZ5J3DZREY48WNZ</HITId>
|
12
|
+
# <AssignmentStatus>Approved</AssignmentStatus>
|
13
|
+
# <AutoApprovalTime>2009-08-12T19:21:54Z</AutoApprovalTime>
|
14
|
+
# <AcceptTime>2009-07-13T19:21:40Z</AcceptTime>
|
15
|
+
# <SubmitTime>2009-07-13T19:21:54Z</SubmitTime>
|
16
|
+
# <ApprovalTime>2009-07-13T19:27:54Z</ApprovalTime>
|
17
|
+
# <Answer>
|
18
|
+
# <?xml version="1.0" encoding="UTF-8"?>
|
19
|
+
# <QuestionFormAnswers xmlns="http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2005-10-01/QuestionFormAnswers.xsd">
|
20
|
+
# <Answer>
|
21
|
+
# <QuestionIdentifier>Question100</QuestionIdentifier>
|
22
|
+
# <FreeText>Move to X.</FreeText>
|
23
|
+
# </Answer>
|
24
|
+
# </QuestionFormAnswers>
|
25
|
+
# </Answer>
|
26
|
+
# </Assignment>
|
27
|
+
# </GetAssignmentsForHITResult>
|
28
|
+
|
29
|
+
module RTurk
|
30
|
+
|
31
|
+
class GetAssignmentsForHITResponse < Response
|
32
|
+
|
33
|
+
def assignments
|
34
|
+
assignments = []
|
35
|
+
@xml.xpath('//Assignment').each do |assignment_xml|
|
36
|
+
assignments << RTurk::Assignment.new(assignment_xml)
|
37
|
+
end
|
38
|
+
assignments
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|