kempelen 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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