rturk 2.10.3 → 2.11.0

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