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.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.travis.yml +4 -0
  4. data/CODE_OF_CONDUCT.md +22 -0
  5. data/Gemfile +5 -0
  6. data/Guardfile +42 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +59 -0
  9. data/Rakefile +27 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +7 -0
  12. data/examples/approve_assignment.rb +16 -0
  13. data/examples/create_hit.rb +20 -0
  14. data/examples/force_expire_hit.rb +16 -0
  15. data/examples/get_account_balance.rb +16 -0
  16. data/examples/get_assignments_for_hit.rb +16 -0
  17. data/examples/get_hit.rb +16 -0
  18. data/examples/get_reviewable_hits.rb +16 -0
  19. data/examples/reject_assignment.rb +16 -0
  20. data/examples/set_hit_as_reviewing.rb +16 -0
  21. data/kempelen.gemspec +35 -0
  22. data/lib/kempelen.rb +35 -0
  23. data/lib/kempelen/API/client.rb +45 -0
  24. data/lib/kempelen/API/common/answers.rb +34 -0
  25. data/lib/kempelen/API/common/assignment.rb +37 -0
  26. data/lib/kempelen/API/common/price.rb +26 -0
  27. data/lib/kempelen/API/common/question_form_answers.rb +57 -0
  28. data/lib/kempelen/API/operations/approve_assignment.rb +17 -0
  29. data/lib/kempelen/API/operations/assignment_operation.rb +36 -0
  30. data/lib/kempelen/API/operations/base.rb +71 -0
  31. data/lib/kempelen/API/operations/create_hit.rb +55 -0
  32. data/lib/kempelen/API/operations/force_expire_hit.rb +25 -0
  33. data/lib/kempelen/API/operations/get_account_balance.rb +30 -0
  34. data/lib/kempelen/API/operations/get_assignments_for_hit.rb +42 -0
  35. data/lib/kempelen/API/operations/get_hit.rb +25 -0
  36. data/lib/kempelen/API/operations/get_reviewable_hits.rb +64 -0
  37. data/lib/kempelen/API/operations/hit_operation.rb +23 -0
  38. data/lib/kempelen/API/operations/parameters.rb +40 -0
  39. data/lib/kempelen/API/operations/register_hit_type.rb +47 -0
  40. data/lib/kempelen/API/operations/reject_assignment.rb +17 -0
  41. data/lib/kempelen/API/operations/set_hit_as_reviewing.rb +34 -0
  42. data/lib/kempelen/API/responses/account_balance_response.rb +27 -0
  43. data/lib/kempelen/API/responses/base.rb +32 -0
  44. data/lib/kempelen/API/responses/empty_response.rb +17 -0
  45. data/lib/kempelen/API/responses/error_response.rb +32 -0
  46. data/lib/kempelen/API/responses/get_assignments_response.rb +31 -0
  47. data/lib/kempelen/API/responses/hit_response.rb +66 -0
  48. data/lib/kempelen/API/responses/reviewable_hits_response.rb +38 -0
  49. data/lib/kempelen/version.rb +3 -0
  50. 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