rturk 1.0.5 → 2.0.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/.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
|