kempelen 1.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.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +22 -0
- data/Gemfile +5 -0
- data/Guardfile +42 -0
- data/LICENSE.txt +21 -0
- data/README.md +59 -0
- data/Rakefile +27 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/examples/approve_assignment.rb +16 -0
- data/examples/create_hit.rb +20 -0
- data/examples/force_expire_hit.rb +16 -0
- data/examples/get_account_balance.rb +16 -0
- data/examples/get_assignments_for_hit.rb +16 -0
- data/examples/get_hit.rb +16 -0
- data/examples/get_reviewable_hits.rb +16 -0
- data/examples/reject_assignment.rb +16 -0
- data/examples/set_hit_as_reviewing.rb +16 -0
- data/kempelen.gemspec +35 -0
- data/lib/kempelen.rb +35 -0
- data/lib/kempelen/API/client.rb +45 -0
- data/lib/kempelen/API/common/answers.rb +34 -0
- data/lib/kempelen/API/common/assignment.rb +37 -0
- data/lib/kempelen/API/common/price.rb +26 -0
- data/lib/kempelen/API/common/question_form_answers.rb +57 -0
- data/lib/kempelen/API/operations/approve_assignment.rb +17 -0
- data/lib/kempelen/API/operations/assignment_operation.rb +36 -0
- data/lib/kempelen/API/operations/base.rb +71 -0
- data/lib/kempelen/API/operations/create_hit.rb +55 -0
- data/lib/kempelen/API/operations/force_expire_hit.rb +25 -0
- data/lib/kempelen/API/operations/get_account_balance.rb +30 -0
- data/lib/kempelen/API/operations/get_assignments_for_hit.rb +42 -0
- data/lib/kempelen/API/operations/get_hit.rb +25 -0
- data/lib/kempelen/API/operations/get_reviewable_hits.rb +64 -0
- data/lib/kempelen/API/operations/hit_operation.rb +23 -0
- data/lib/kempelen/API/operations/parameters.rb +40 -0
- data/lib/kempelen/API/operations/register_hit_type.rb +47 -0
- data/lib/kempelen/API/operations/reject_assignment.rb +17 -0
- data/lib/kempelen/API/operations/set_hit_as_reviewing.rb +34 -0
- data/lib/kempelen/API/responses/account_balance_response.rb +27 -0
- data/lib/kempelen/API/responses/base.rb +32 -0
- data/lib/kempelen/API/responses/empty_response.rb +17 -0
- data/lib/kempelen/API/responses/error_response.rb +32 -0
- data/lib/kempelen/API/responses/get_assignments_response.rb +31 -0
- data/lib/kempelen/API/responses/hit_response.rb +66 -0
- data/lib/kempelen/API/responses/reviewable_hits_response.rb +38 -0
- data/lib/kempelen/version.rb +3 -0
- metadata +260 -0
data/lib/kempelen.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require "kempelen/version"
|
2
|
+
|
3
|
+
require "kempelen/API/client"
|
4
|
+
|
5
|
+
require "kempelen/API/common/price"
|
6
|
+
require "kempelen/API/common/assignment"
|
7
|
+
require "kempelen/API/common/answers"
|
8
|
+
require "kempelen/API/common/question_form_answers"
|
9
|
+
|
10
|
+
require "kempelen/API/responses/base"
|
11
|
+
require "kempelen/API/responses/error_response"
|
12
|
+
require "kempelen/API/responses/empty_response"
|
13
|
+
require "kempelen/API/responses/hit_response"
|
14
|
+
require "kempelen/API/responses/account_balance_response"
|
15
|
+
require "kempelen/API/responses/reviewable_hits_response"
|
16
|
+
require "kempelen/API/responses/get_assignments_response"
|
17
|
+
|
18
|
+
require "kempelen/API/operations/base"
|
19
|
+
require "kempelen/API/operations/parameters"
|
20
|
+
require "kempelen/API/operations/hit_operation"
|
21
|
+
require "kempelen/API/operations/assignment_operation"
|
22
|
+
require "kempelen/API/operations/create_hit"
|
23
|
+
require "kempelen/API/operations/get_hit"
|
24
|
+
require "kempelen/API/operations/get_reviewable_hits"
|
25
|
+
require "kempelen/API/operations/get_account_balance"
|
26
|
+
require "kempelen/API/operations/force_expire_hit"
|
27
|
+
require "kempelen/API/operations/approve_assignment"
|
28
|
+
require "kempelen/API/operations/reject_assignment"
|
29
|
+
require "kempelen/API/operations/get_assignments_for_hit"
|
30
|
+
require "kempelen/API/operations/set_hit_as_reviewing"
|
31
|
+
require "kempelen/API/operations/register_hit_type"
|
32
|
+
|
33
|
+
module Kempelen
|
34
|
+
# Your code goes here...
|
35
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Kempelen
|
2
|
+
module API
|
3
|
+
class Client
|
4
|
+
# Amazon AWS access key.
|
5
|
+
attr_reader :access_key
|
6
|
+
|
7
|
+
# Secret key for AWS access key. Used to sign the requests.
|
8
|
+
attr_reader :secret_key
|
9
|
+
|
10
|
+
# Mechanical Turk environment to make API calls to - defaults to :production.
|
11
|
+
attr_reader :environment
|
12
|
+
|
13
|
+
# URLs for Mechanical Turk APIs, indexed by environment.
|
14
|
+
SERVICE_URLS = {
|
15
|
+
sandbox: 'https://mechanicalturk.sandbox.amazonaws.com'.freeze,
|
16
|
+
production: 'https://mechanicalturk.amazonaws.com'.freeze
|
17
|
+
}
|
18
|
+
|
19
|
+
# Service name of the Mechanical Turk API.
|
20
|
+
SERVICE_NAME = "AWSMechanicalTurkRequester".freeze
|
21
|
+
|
22
|
+
def initialize(access_key, secret_key, environment = :production)
|
23
|
+
@access_key = access_key
|
24
|
+
@secret_key = secret_key
|
25
|
+
|
26
|
+
@environment = environment.to_sym
|
27
|
+
|
28
|
+
raise ArgumentError.new("Unknown environment".freeze) if SERVICE_URLS[@environment].nil?
|
29
|
+
end
|
30
|
+
|
31
|
+
def service_name
|
32
|
+
SERVICE_NAME
|
33
|
+
end
|
34
|
+
|
35
|
+
def service_url
|
36
|
+
SERVICE_URLS[@environment]
|
37
|
+
end
|
38
|
+
|
39
|
+
def perform_request(query_string, response_object)
|
40
|
+
response = HTTParty.get(query_string)
|
41
|
+
response.parsed_response[response_object]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Kempelen
|
2
|
+
module API
|
3
|
+
module Common
|
4
|
+
class Answer
|
5
|
+
attr_accessor :question_identifier
|
6
|
+
|
7
|
+
def initialize(question_identifier)
|
8
|
+
@question_identifier = question_identifier
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class FreeTextAnswer < Answer
|
13
|
+
attr_accessor :free_text
|
14
|
+
|
15
|
+
def initialize(question_identifier, free_text)
|
16
|
+
super(question_identifier)
|
17
|
+
|
18
|
+
@free_text = free_text
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class MultipleChoiceAnswer < Answer
|
23
|
+
attr_accessor :selection_identifiers
|
24
|
+
attr_accessor :other_selection_text
|
25
|
+
|
26
|
+
def initialize(question_identifier)
|
27
|
+
super(question_identifier)
|
28
|
+
|
29
|
+
@selection_identifiers = []
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Kempelen
|
2
|
+
module API
|
3
|
+
module Common
|
4
|
+
class Assignment
|
5
|
+
attr_accessor :assignment_id
|
6
|
+
attr_accessor :worker_id
|
7
|
+
attr_accessor :hit_id
|
8
|
+
attr_accessor :status
|
9
|
+
attr_accessor :auto_approval_time
|
10
|
+
attr_accessor :accept_time
|
11
|
+
attr_accessor :submit_time
|
12
|
+
attr_accessor :answer
|
13
|
+
|
14
|
+
def self.create_from_response(response)
|
15
|
+
return nil if response.nil?
|
16
|
+
|
17
|
+
assignment = Assignment.new
|
18
|
+
|
19
|
+
assignment.assignment_id = response["AssignmentId"]
|
20
|
+
assignment.worker_id = response["WorkerId"]
|
21
|
+
assignment.hit_id = response["HITId"]
|
22
|
+
assignment.status = response["AssignmentStatus"]
|
23
|
+
assignment.auto_approval_time = DateTime.parse(response["AutoApprovalTime"]) rescue nil
|
24
|
+
assignment.accept_time = DateTime.parse(response["AcceptTime"]) rescue nil
|
25
|
+
assignment.submit_time = DateTime.parse(response["SubmitTime"]) rescue nil
|
26
|
+
assignment.answer = QuestionFormAnswers.new(response["Answer"])
|
27
|
+
|
28
|
+
assignment
|
29
|
+
end
|
30
|
+
|
31
|
+
def answer_question(question_identifier, index = 0)
|
32
|
+
@answer.get_answer(question_identifier, index)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Kempelen
|
2
|
+
module API
|
3
|
+
module Common
|
4
|
+
class Price
|
5
|
+
attr_accessor :amount
|
6
|
+
attr_accessor :currency_code
|
7
|
+
|
8
|
+
def initialize(amount = 0.0, currency_code = "USD")
|
9
|
+
@amount = amount
|
10
|
+
@currency_code = currency_code
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.create_from_response(response)
|
14
|
+
return nil if response.nil?
|
15
|
+
|
16
|
+
price = Price.new
|
17
|
+
|
18
|
+
price.amount = response["Amount"].to_f
|
19
|
+
price.currency_code = response["CurrencyCode"]
|
20
|
+
|
21
|
+
price
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'nori'
|
2
|
+
|
3
|
+
module Kempelen
|
4
|
+
module API
|
5
|
+
module Common
|
6
|
+
class QuestionFormAnswers
|
7
|
+
attr_accessor :answers
|
8
|
+
|
9
|
+
def initialize(answer = nil)
|
10
|
+
@answers = []
|
11
|
+
|
12
|
+
load_from_answer(answer) unless answer.nil?
|
13
|
+
end
|
14
|
+
|
15
|
+
# <?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
16
|
+
#<QuestionFormAnswers xmlns="http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2005-10-01/QuestionFormAnswers.xsd">
|
17
|
+
# <Answer>
|
18
|
+
# <QuestionIdentifier>TranscriptionTexts</QuestionIdentifier>
|
19
|
+
# <FreeText>This is the second test.</FreeText>
|
20
|
+
# </Answer>
|
21
|
+
#</QuestionFormAnswers>
|
22
|
+
|
23
|
+
def load_from_answer(answer)
|
24
|
+
parser = Nori.new(:parser => :rexml)
|
25
|
+
parsed_data = parser.parse(answer)
|
26
|
+
answer_list = parsed_data["QuestionFormAnswers"]["Answer"]
|
27
|
+
|
28
|
+
unless answer_list.is_a?(Array)
|
29
|
+
answer_list = [answer_list]
|
30
|
+
end
|
31
|
+
|
32
|
+
answer_list.each do |answer_data|
|
33
|
+
if answer_data.has_key?("FreeText")
|
34
|
+
@answers << FreeTextAnswer.new(answer_data["QuestionIdentifier"].to_s, answer_data["FreeText"].to_s)
|
35
|
+
elsif answer_data.has_key?("SelectionIdentifier") or answer_data.has_key?("OtherSelectionText")
|
36
|
+
new_answer = MultipleChoiceAnswer.new(answer_data["QuestionIdentifier"].to_s)
|
37
|
+
|
38
|
+
selection = answer_data["SelectionIdentifier"]
|
39
|
+
if selection.is_a?(Array)
|
40
|
+
new_answer.selection_identifiers += selection
|
41
|
+
else
|
42
|
+
new_answer.selection_identifiers << selection
|
43
|
+
end
|
44
|
+
new_answer.other_selection_text = answer_data["OtherSelectionText"]
|
45
|
+
|
46
|
+
@answers << new_answer
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def get_answer(question_identifier, index = 0)
|
52
|
+
@answers.select {|a| a.question_identifier == question_identifier}[index] rescue nil
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Kempelen
|
2
|
+
module API
|
3
|
+
module Operations
|
4
|
+
class ApproveAssignment < AssignmentOperation
|
5
|
+
AWS_OPERATION_NAME = "ApproveAssignment".freeze
|
6
|
+
AWS_RESPONSE_OBJECT = "ApproveAssignmentResponse".freeze
|
7
|
+
|
8
|
+
def initialize(client, assignment_id, requester_feedback = nil)
|
9
|
+
super(client, assignment_id, requester_feedback)
|
10
|
+
|
11
|
+
@operation_name = AWS_OPERATION_NAME
|
12
|
+
@response_object = AWS_RESPONSE_OBJECT
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Kempelen
|
2
|
+
module API
|
3
|
+
module Operations
|
4
|
+
class AssignmentOperation < Base
|
5
|
+
attr_accessor :assignment_id
|
6
|
+
attr_accessor :requester_feedback
|
7
|
+
attr_accessor :operation_name
|
8
|
+
|
9
|
+
FEEDBACK_MAX_SIZE = 1024
|
10
|
+
|
11
|
+
def initialize(client, assignment_id, requester_feedback = nil)
|
12
|
+
super(client)
|
13
|
+
|
14
|
+
@assignment_id = assignment_id
|
15
|
+
@requester_feedback = requester_feedback
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_parameters
|
19
|
+
@parameters[:operation] = @operation_name
|
20
|
+
@parameters[:assignment_id] = @assignment_id
|
21
|
+
@parameters[:requester_feedback] = @requester_feedback.slice(0, FEEDBACK_MAX_SIZE) unless @requester_feedback.nil?
|
22
|
+
|
23
|
+
super
|
24
|
+
end
|
25
|
+
|
26
|
+
def perform_operation
|
27
|
+
create_request_string
|
28
|
+
|
29
|
+
super
|
30
|
+
|
31
|
+
Kempelen::API::Responses::EmptyResponse.new(@raw_response)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'hmac-sha1'
|
3
|
+
require 'digest/sha1'
|
4
|
+
require 'base64'
|
5
|
+
|
6
|
+
module Kempelen
|
7
|
+
module API
|
8
|
+
module Operations
|
9
|
+
class Base
|
10
|
+
attr_accessor :client
|
11
|
+
attr_accessor :parameters
|
12
|
+
attr_accessor :timestamp
|
13
|
+
attr_accessor :response_object
|
14
|
+
attr_accessor :query_string
|
15
|
+
attr_accessor :raw_response
|
16
|
+
|
17
|
+
def initialize(client)
|
18
|
+
@client = client
|
19
|
+
@parameters = {}
|
20
|
+
end
|
21
|
+
|
22
|
+
def create_parameters
|
23
|
+
@parameters[:service] = client.service_name
|
24
|
+
@parameters[:access_key] = client.access_key
|
25
|
+
@parameters[:timestamp] = DateTime.now
|
26
|
+
|
27
|
+
string_to_sign = [@parameters[:service], @parameters[:operation], @parameters[:timestamp]].join
|
28
|
+
@parameters[:signature] = (Base64.encode64 HMAC::SHA1.digest(client.secret_key, string_to_sign)).strip
|
29
|
+
end
|
30
|
+
|
31
|
+
# TODO: Reduce the complexity of this method.
|
32
|
+
def create_request_string
|
33
|
+
create_parameters
|
34
|
+
|
35
|
+
request_str = ""
|
36
|
+
|
37
|
+
@parameters.each do |name, value|
|
38
|
+
aws_name = Kempelen::API::Operations::PARAMETERS[name]
|
39
|
+
if !aws_name.nil?
|
40
|
+
unless value.nil?
|
41
|
+
request_str += "&" unless request_str.empty?
|
42
|
+
request_str += "#{aws_name}=#{CGI.escape(value.to_s)}"
|
43
|
+
end
|
44
|
+
else
|
45
|
+
aws_name = Kempelen::API::Operations::ARRAY_PARAMETERS[name]
|
46
|
+
unless aws_name.nil?
|
47
|
+
value.each_index do |idx|
|
48
|
+
hash_value = value[idx]
|
49
|
+
|
50
|
+
if hash_value.respond_to?(:each)
|
51
|
+
hash_value.each do |k, v|
|
52
|
+
request_str += "&" unless request_str.empty?
|
53
|
+
request_str += "#{aws_name}.#{idx + 1}.#{k}=#{CGI.escape(v.to_s)}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
@query_string = "#{client.service_url}?#{request_str}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def perform_operation
|
65
|
+
@raw_response = @client.perform_request(@query_string, @response_object)
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Kempelen
|
2
|
+
module API
|
3
|
+
module Operations
|
4
|
+
class CreateHit < Base
|
5
|
+
attr_accessor :hit_type_id
|
6
|
+
attr_accessor :layout_id
|
7
|
+
attr_accessor :layout_parameters
|
8
|
+
attr_accessor :lifetime_in_seconds
|
9
|
+
attr_accessor :max_assignments
|
10
|
+
attr_accessor :requester_annotation
|
11
|
+
attr_accessor :unique_request_token
|
12
|
+
|
13
|
+
AWS_OPERATION_NAME = "CreateHIT".freeze
|
14
|
+
AWS_RESPONSE_OBJECT = "CreateHITResponse".freeze
|
15
|
+
|
16
|
+
def initialize(client, hit_type_id, lifetime_in_seconds, &block)
|
17
|
+
super(client)
|
18
|
+
|
19
|
+
@hit_type_id = hit_type_id
|
20
|
+
@lifetime_in_seconds = lifetime_in_seconds.to_i
|
21
|
+
@layout_parameters = []
|
22
|
+
@max_assignments = 1
|
23
|
+
@response_object = AWS_RESPONSE_OBJECT
|
24
|
+
|
25
|
+
yield self unless block == nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def add_layout_parameter(name, value)
|
29
|
+
@layout_parameters << {Name: name, Value: value}
|
30
|
+
end
|
31
|
+
|
32
|
+
def create_parameters
|
33
|
+
@parameters[:operation] = AWS_OPERATION_NAME
|
34
|
+
@parameters[:hit_type_id] = @hit_type_id
|
35
|
+
@parameters[:lifetime_in_seconds] = @lifetime_in_seconds
|
36
|
+
@parameters[:max_assignments] = @max_assignments
|
37
|
+
@parameters[:layout_id] = @layout_id unless @layout_id.nil?
|
38
|
+
@parameters[:layout_parameters] = @layout_parameters unless @layout_parameters.empty?
|
39
|
+
@parameters[:requester_annotation] = @requester_annotation unless @requester_annotation.nil?
|
40
|
+
@parameters[:unique_request_token] = @unique_request_token unless @unique_request_token.nil?
|
41
|
+
|
42
|
+
super
|
43
|
+
end
|
44
|
+
|
45
|
+
def perform_operation
|
46
|
+
create_request_string
|
47
|
+
|
48
|
+
super
|
49
|
+
|
50
|
+
Kempelen::API::Responses::HitResponse.new(@raw_response)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Kempelen
|
2
|
+
module API
|
3
|
+
module Operations
|
4
|
+
class ForceExpireHit < HitOperation
|
5
|
+
AWS_OPERATION_NAME = "ForceExpireHIT".freeze
|
6
|
+
AWS_RESPONSE_OBJECT = "ForceExpireHITResponse".freeze
|
7
|
+
|
8
|
+
def initialize(client, hit_id)
|
9
|
+
super(client, hit_id)
|
10
|
+
|
11
|
+
@operation_name = AWS_OPERATION_NAME
|
12
|
+
@response_object = AWS_RESPONSE_OBJECT
|
13
|
+
end
|
14
|
+
|
15
|
+
def perform_operation
|
16
|
+
create_request_string
|
17
|
+
|
18
|
+
super
|
19
|
+
|
20
|
+
Kempelen::API::Responses::EmptyResponse.new(@raw_response)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|