rturk 2.10.3 → 2.11.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1e315bf746bf3734838ccc4b111232cb716d31ca
4
+ data.tar.gz: 0c168e252589bc94ec2c052ed9daad5f2a372cfb
5
+ SHA512:
6
+ metadata.gz: 73b53fbc9af91652ed7bc54d8a30e8e3f3b0617d16909e2e79bb371dba1916c2be25236afeb3de1e724f21c19281d37e1ed46ffb513b18834038ae5eb6bba548
7
+ data.tar.gz: f639e6d9564ed50418a7f57a9e6b1172f85dbcbf49220d4efcbe3af44647c3ae90ae7d311f63b377bb4bac5103bd6616ffa7c4b69da7dc94e464183b5876b49a
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.travis.yml CHANGED
@@ -1,5 +1,4 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - 1.9.3
4
- - 1.8.7
5
- - ree
4
+ - 2.0.0
data/Gemfile CHANGED
@@ -1,2 +1,2 @@
1
1
  source "http://rubygems.org"
2
- gemspec
2
+ gemspec
data/README.markdown CHANGED
@@ -25,7 +25,7 @@ Let's say you have a form at "http://myapp.com/turkers/add_tags" where Turkers c
25
25
 
26
26
  RTurk.setup(YourAWSAccessKeyId, YourAWSAccessKey, :sandbox => true)
27
27
  hit = RTurk::Hit.create(:title => "Add some tags to a photo") do |hit|
28
- hit.assignments = 2
28
+ hit.max_assignments = 2
29
29
  hit.description = 'blah'
30
30
  hit.question("http://myapp.com/turkers/add_tags",
31
31
  :frame_height => 1000) # pixels for iframe
@@ -82,6 +82,10 @@ Here's a quick peak at what happens on the Mechanical Turk side.
82
82
 
83
83
  A worker takes a look at your hit. The page will contain an iframe with your question URL loaded inside of it.
84
84
 
85
+ If you want to use an Amazon-hosted [QuestionForm](http://docs.amazonwebservices.com/AWSMechTurk/2008-08-02/AWSMturkAPI/ApiReference_QuestionFormDataStructureArticle.html), do
86
+
87
+ hit.question_form "<Question>What color is the sky?</Question>" # not the real format
88
+
85
89
  Amazon will append the AssignmentID parameter to the URL for your own information. In preview mode this will look like
86
90
 
87
91
  http://myapp.com/turkers/add_tags?item_id=1234&AssignmentId=ASSIGNMENT_ID_NOT_AVAILABLE
@@ -101,14 +105,17 @@ Anything submitted in this form will be sent to Amazon and saved for your review
101
105
 
102
106
  ## More information
103
107
 
104
- Take a look at the [Amazon MTurk developer docs](http://docs.amazonwebservices.com/AWSMechTurk/latest/AWSMechanicalTurkRequester/) for more information. They have a complete list of API operations, all of which can be called with this library.
108
+ Take a look at the [Amazon MTurk developer docs](http://docs.amazonwebservices.com/AWSMechTurk/latest/AWSMechanicalTurkRequester/) for more information. They have a complete list of API operations, many of which can be called with this library.
109
+
110
+ Mark gave a [presentation about RTurk at the Atlanta Ruby User Group](http://www.atlruby.org/markpercival/posts/109-Mechanical-Turk-Ruby-Gem) that got recorded as a 20-minute screencast video.
105
111
 
106
112
  ## Contributors
107
113
 
108
- [Zach Hale](http://github.com/zachhale)
109
- [David Balatero](http://github.com/dbalatero)
110
- [Rob Hanlon](http://github.com/ohwillie)
111
- [Haris Amin](http://github.com/hamin)
112
- [Tyler](http://github.com/tkieft)
113
- [David Dai](http://github.com/newtonsapple)
114
+ [Zach Hale](http://github.com/zachhale)
115
+ [David Balatero](http://github.com/dbalatero)
116
+ [Rob Hanlon](http://github.com/ohwillie)
117
+ [Haris Amin](http://github.com/hamin)
118
+ [Tyler](http://github.com/tkieft)
119
+ [David Dai](http://github.com/newtonsapple)
120
+ [Alex Chaffee](http://github.com/alexch)
114
121
 
@@ -6,7 +6,7 @@ RTurk::setup(aws['AWSAccessKeyId'], aws['AWSAccessKey'], :sandbox => true)
6
6
  hit = RTurk::Hit.create(:title => 'Write a tweet for me') do |hit|
7
7
  hit.description = 'Simply write a witty twitter update for my account'
8
8
  hit.reward = 0.05
9
- hit.assignments = 1
9
+ hit.max_assignments = 1
10
10
  hit.question("http://mpercival.com.s3.amazonaws.com/newtweet.html")
11
11
  end
12
12
 
@@ -47,6 +47,8 @@ module RTurk
47
47
  def method_missing(method, *args)
48
48
  if self.source.respond_to?(method)
49
49
  self.source.send(method, *args)
50
+ else
51
+ super
50
52
  end
51
53
  end
52
54
  end
@@ -10,7 +10,7 @@ module RTurk
10
10
  #
11
11
  # RTurk.setup(YourAWSAccessKeyId, YourAWSAccessKey, :sandbox => true)
12
12
  # hit = RTurk::Hit.create(:title => "Add some tags to a photo") do |hit|
13
- # hit.assignments = 2
13
+ # hit.max_assignments = 2
14
14
  # hit.question("http://myapp.com/turkers/add_tags")
15
15
  # hit.reward = 0.05
16
16
  # hit.qualifications.approval_rate, {:gt => 80}
@@ -40,14 +40,18 @@ module RTurk
40
40
  end
41
41
 
42
42
  def all_reviewable
43
- RTurk.GetReviewableHITs.hit_ids.inject([]) do |arr, hit_id|
44
- arr << new(hit_id); arr
43
+ [].tap do |hits|
44
+ RTurk.GetReviewableHITs.hit_ids.each do |hit_id|
45
+ hits << new(hit_id)
46
+ end
45
47
  end
46
48
  end
47
49
 
48
50
  def all
49
- RTurk.SearchHITs.hits.inject([]) do |arr, hit|
50
- arr << new(hit.id, hit); arr;
51
+ [].tap do |hits|
52
+ RTurk.SearchHITs.hits.each do |hit|
53
+ hits << new(hit.id, hit)
54
+ end
51
55
  end
52
56
  end
53
57
 
@@ -119,6 +123,8 @@ module RTurk
119
123
  @source.send(method, *args)
120
124
  elsif self.details.respond_to?(method)
121
125
  self.details.send(method)
126
+ else
127
+ super
122
128
  end
123
129
  end
124
130
  end
@@ -0,0 +1,39 @@
1
+ require "erector/xml_widget"
2
+
3
+ # see http://docs.amazonwebservices.com/AWSMechTurk/2008-08-02/AWSMturkAPI/index.html?ApiReference_CreateQualificationTypeOperation.html
4
+ class RTurk::AnswerKey < Erector::XMLWidget
5
+
6
+ XMLNS = "http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2005-10-01/AnswerKey.xsd"
7
+
8
+ %w{
9
+ AnswerKey
10
+ Question
11
+ QuestionIdentifier
12
+ AnswerOption
13
+ SelectionIdentifier
14
+ AnswerScore
15
+ DefaultScore
16
+ QualificationValueMapping
17
+ PercentageMapping
18
+ MaximumSummedScore
19
+ ScaleMapping
20
+ SummedScoreMultiplier
21
+ RangeMapping
22
+ SummedScoreRange
23
+ InclusiveLowerBound
24
+ InclusiveUpperBound
25
+ QualificationValue
26
+ OutOfRangeQualificationValue
27
+ }.uniq.each do |element_name|
28
+ tag element_name
29
+ tag element_name, :snake_case
30
+ end
31
+
32
+ def content
33
+ answer_key :xmlns => XMLNS do
34
+ answer_key_content
35
+ end
36
+ end
37
+
38
+ end
39
+
@@ -2,51 +2,51 @@ require 'cgi'
2
2
  require 'uri'
3
3
 
4
4
  module RTurk
5
- class Question
6
-
5
+ class ExternalQuestion
6
+
7
7
  attr_accessor :url, :url_params, :frame_height
8
-
8
+
9
9
  def initialize(url, opts = {})
10
10
  @url = url
11
11
  self.frame_height = opts.delete(:frame_height) || 400
12
12
  self.url_params = opts
13
13
  end
14
-
14
+
15
15
  def querystring
16
16
  @url_params.collect { |key, value| [CGI.escape(key.to_s), CGI.escape(value.to_s)].join('=') }.join('&')
17
17
  end
18
-
18
+
19
19
  def url
20
20
  unless querystring.empty?
21
21
  # slam the params onto url, if url already has params, add 'em with a &
22
- u = @url.index('?') ? "#{@url}&#{querystring}" : "#{@url}?#{querystring}"
22
+ u = @url.index('?') ? "#{@url}&#{querystring}" : "#{@url}?#{querystring}"
23
23
  else
24
24
  u = @url
25
25
  end
26
26
  CGI.escapeHTML(u) #URL should be XML/HTML escaped
27
27
  end
28
-
28
+
29
29
  def params
30
30
  @url_params
31
31
  end
32
-
32
+
33
33
  def params=(param_set)
34
34
  @url_params = param_set
35
35
  end
36
-
36
+
37
37
  def to_params
38
38
  raise RTurk::MissingParameters, "needs a url to build an external question" unless @url
39
39
  # TODO: update the xmlns schema... maybe
40
40
  xml = <<-XML
41
41
  <ExternalQuestion xmlns="http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2006-07-14/ExternalQuestion.xsd">
42
- <ExternalURL>#{url}</ExternalURL>
42
+ <ExternalURL>#{url}</ExternalURL>
43
43
  <FrameHeight>#{frame_height}</FrameHeight>
44
44
  </ExternalQuestion>
45
45
  XML
46
46
  xml
47
47
  end
48
-
48
+
49
49
  end
50
-
51
-
52
- end
50
+
51
+
52
+ end
@@ -9,10 +9,28 @@ module RTurk
9
9
  COMPARATORS = {:gt => 'GreaterThan', :lt => 'LessThan', :gte => 'GreaterThanOrEqualTo',
10
10
  :lte => 'LessThanOrEqualTo', :eql => 'EqualTo', :not => 'NotEqualTo', :exists => 'Exists'}
11
11
 
12
- TYPES = {:approval_rate => '000000000000000000L0', :submission_rate => '00000000000000000000',
13
- :abandoned_rate => '00000000000000000070', :return_rate => '000000000000000000E0',
14
- :rejection_rate => '000000000000000000S0', :hits_approved => '00000000000000000040',
15
- :adult => '00000000000000000060', :country => '00000000000000000071'}
12
+ def self.types
13
+ system_qualification_types ||= {
14
+ :approval_rate => '000000000000000000L0', :submission_rate => '00000000000000000000',
15
+ :abandoned_rate => '00000000000000000070', :return_rate => '000000000000000000E0',
16
+ :rejection_rate => '000000000000000000S0', :hits_approved => '00000000000000000040',
17
+ :adult => '00000000000000000060', :country => '00000000000000000071',
18
+ }
19
+
20
+ # Amazon Master qualification ids vary between sandbox and real environments - see https://forums.aws.amazon.com/thread.jspa?threadID=70812
21
+ system_qualification_types.merge(if RTurk.sandbox?
22
+ {
23
+ :categorization_masters => '2F1KVCNHMVHV8E9PBUB2A4J79LU20F',
24
+ :photo_moderation_masters => '2TGBB6BFMFFOM08IBMAFGGESC1UWJX',
25
+ }
26
+ else
27
+ {
28
+ :categorization_masters => '2NDP2L92HECWY8NS8H3CK0CP5L9GHO',
29
+ :photo_moderation_masters => '21VZU98JHSTLZ5BPP4A9NOBJEK3DPG',
30
+ }
31
+ end)
32
+ end
33
+
16
34
 
17
35
  attr_accessor :qualifier
18
36
 
@@ -31,15 +49,17 @@ module RTurk
31
49
  if type.is_a?(String)
32
50
  qualifier[:QualificationTypeId] = type
33
51
  elsif type.is_a?(Symbol)
34
- qualifier[:QualificationTypeId] = types[type]
52
+ qualifier[:QualificationTypeId] = Qualification.types[type]
35
53
  end
54
+
36
55
  if opts.is_a?(Hash)
37
56
  qualifier[:RequiredToPreview] = opts['RequiredToPreview'].to_s unless opts['RequiredToPreview'].nil?
38
57
  qualifier.merge!(build_comparator(opts))
39
58
  elsif opts == true || opts == false
40
- qualifier[:IntegerValue] = opts == true ? 1 : 0
41
- qualifier[:Comparator] = COMPARATORS[:eql]
59
+ qualifier[:IntegerValue] = opts == true ? 1 : 0
60
+ qualifier[:Comparator] = COMPARATORS[:eql]
42
61
  end
62
+
43
63
  qualifier
44
64
  end
45
65
 
@@ -53,11 +73,6 @@ module RTurk
53
73
  params
54
74
  end
55
75
 
56
- def types
57
- # Could use this later to add other TYPES programatically
58
- TYPES
59
- end
60
-
61
76
  private
62
77
 
63
78
  def build_comparator(opts)
@@ -65,6 +80,7 @@ module RTurk
65
80
  opts.each do |k,v|
66
81
  if COMPARATORS.has_key?(k)
67
82
  qualifier[:Comparator] = COMPARATORS[k]
83
+ next if v.nil? # to allow :exists => nil
68
84
  if v.to_s.match(/[A-Z]./)
69
85
  qualifier[:Country] = v
70
86
  else
@@ -32,8 +32,10 @@ module RTurk
32
32
 
33
33
 
34
34
  def method_missing(method, *args)
35
- if RTurk::Qualification::TYPES.include?(method)
35
+ if RTurk::Qualification.types.include?(method)
36
36
  self.add(method, *args)
37
+ else
38
+ super
37
39
  end
38
40
  end
39
41
 
@@ -0,0 +1,86 @@
1
+ require 'cgi'
2
+ require 'uri'
3
+ require "erector/xml_widget"
4
+
5
+ module RTurk
6
+ # see http://docs.amazonwebservices.com/AWSMechTurk/2008-08-02/AWSMturkAPI/ApiReference_QuestionFormDataStructureArticle.html
7
+ # @param options hash (optional)
8
+ # ::xml:: raw xml (optional). If it starts with \&lt;QuestionForm then it's used as the full QuestionForm value. Otherwise it is wrapped in a \&lt;QuestionForm\&gt; element. If it is nil then we use Erector to call its subclass' #question_form_content method.
9
+ class QuestionForm < Erector::XMLWidget
10
+
11
+ XMLNS = "http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2005-10-01/QuestionForm.xsd"
12
+
13
+ needs :xml => nil
14
+
15
+ def to_params
16
+ to_xml # create a new output string and call 'content' via Erector
17
+ end
18
+
19
+ # todo: fill out Application subelements
20
+ # todo: fill out EmbeddedBinary subelements
21
+ %w{
22
+ QuestionForm
23
+ Overview
24
+ Title
25
+ List
26
+ Binary
27
+ MimeType
28
+ Type
29
+ SubType
30
+ DataURL
31
+ AltText
32
+ Application
33
+ EmbeddedBinary
34
+ FormattedContent
35
+
36
+ Question
37
+ QuestionIdentifier
38
+ DisplayName
39
+ IsRequired
40
+ QuestionContent
41
+ AnswerSpecification
42
+ FreeTextAnswer
43
+ Constraints
44
+ IsNumeric
45
+ Length
46
+ AnswerFormatRegex
47
+ DefaultText
48
+ NumberOfLinesSuggestion
49
+ SelectionAnswer
50
+ MinSelectionCount
51
+ MaxSelectionCount
52
+ StyleSuggestion
53
+ Selections
54
+ Selection
55
+ SelectionIdentifier
56
+ Text
57
+ FormattedContent
58
+ Binary
59
+ OtherSelection
60
+ FileUploadAnswer
61
+ MaxFileSizeInBytes
62
+ MinFileSizeInBytes
63
+ }.uniq.each do |element_name|
64
+ tag element_name
65
+ tag element_name, :snake_case unless element_name == 'Text'
66
+ end
67
+
68
+ tag "Text", "text_element" # very sticky since 'text' is a core Erector method
69
+
70
+ def content
71
+ if @xml and @xml.strip =~ /^<QuestionForm/
72
+ rawtext @xml
73
+ else
74
+ question_form :xmlns => XMLNS do
75
+ question_form_content
76
+ end
77
+ end
78
+ end
79
+
80
+ def question_form_content
81
+ rawtext @xml # by default, use the parameter
82
+ end
83
+
84
+ end
85
+
86
+ end
@@ -2,8 +2,8 @@ require File.join(File.dirname(__FILE__), 'register_hit_type')
2
2
 
3
3
  module RTurk
4
4
  class CreateHIT < RegisterHITType
5
- attr_accessor :hit_type_id, :assignments, :lifetime, :note
6
-
5
+ attr_accessor :hit_type_id, :max_assignments, :lifetime, :annotation
6
+
7
7
  def parse(response)
8
8
  RTurk::CreateHITResponse.new(response)
9
9
  end
@@ -11,22 +11,30 @@ module RTurk
11
11
  # Gives us access to a question builder attached to this HIT
12
12
  #
13
13
  # @param [String, Hash] URL Params, if none is passed, simply returns the question
14
- # @return [RTurk::Question] The question if instantiated or nil
14
+ # @return [RTurk::ExternalQuestion] The question if instantiated or nil
15
15
  def question(*args)
16
16
  unless args.empty?
17
- @question ||= RTurk::Question.new(*args)
17
+ @question ||= RTurk::ExternalQuestion.new(*args)
18
18
  else
19
19
  @question
20
20
  end
21
21
  end
22
22
 
23
+ def question_form(text_or_widget)
24
+ if text_or_widget.is_a? Erector::XMLWidget
25
+ @question = text_or_widget
26
+ else
27
+ @question = RTurk::QuestionForm.new(:xml => text)
28
+ end
29
+ end
30
+
23
31
  def to_params
24
32
  super.merge(
25
- 'HITTypeId' => hit_type_id,
26
- 'MaxAssignments' => (assignments || 1),
33
+ 'HITTypeId' => hit_type_id,
34
+ 'MaxAssignments' => (max_assignments || 1),
27
35
  'Question' => question.to_params,
28
36
  'LifetimeInSeconds' => (lifetime || 3600),
29
- 'RequesterAnnotation' => note
37
+ 'RequesterAnnotation' => annotation
30
38
  )
31
39
  end
32
40
 
@@ -41,7 +49,7 @@ module RTurk
41
49
  super # validate as RegisterHitType
42
50
  end
43
51
  end
44
-
52
+
45
53
  def required_fields
46
54
  super << :question
47
55
  end