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 +7 -0
- data/.rspec +1 -0
- data/.travis.yml +1 -2
- data/Gemfile +1 -1
- data/README.markdown +15 -8
- data/examples/create_hit.rb +1 -1
- data/lib/rturk/adapters/assignment.rb +2 -0
- data/lib/rturk/adapters/hit.rb +11 -5
- data/lib/rturk/builders/answer_key_builder.rb +39 -0
- data/lib/rturk/builders/{question_builder.rb → external_question_builder.rb} +14 -14
- data/lib/rturk/builders/qualification_builder.rb +28 -12
- data/lib/rturk/builders/qualifications_builder.rb +3 -1
- data/lib/rturk/builders/question_form_builder.rb +86 -0
- data/lib/rturk/operations/create_hit.rb +16 -8
- data/lib/rturk/operations/register_hit_type.rb +2 -2
- data/lib/rturk/parsers/responses/get_assignments_for_hit_response.rb +21 -5
- data/lib/rturk/parsers/responses/get_hit_response.rb +3 -2
- data/lib/rturk/parsers/responses/get_reviewable_hits_response.rb +20 -5
- data/lib/rturk/parsers/responses/search_hits_response.rb +23 -7
- data/lib/rturk/requester.rb +1 -1
- data/lib/rturk/version.rb +1 -1
- data/rturk.gemspec +3 -2
- data/spec/builders/answer_key_builder_spec.rb +26 -0
- data/spec/builders/{question_spec.rb → external_question_spec.rb} +20 -20
- data/spec/builders/question_form_builder_spec.rb +75 -0
- data/spec/example_question_form.rb +106 -0
- data/spec/example_question_form.xml +64 -0
- data/spec/operations/create_hit_spec.rb +33 -6
- data/spec/requester_spec.rb +8 -0
- data/spec/spec_helper.rb +4 -1
- metadata +114 -121
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
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.
|
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,
|
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
|
|
data/examples/create_hit.rb
CHANGED
@@ -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.
|
9
|
+
hit.max_assignments = 1
|
10
10
|
hit.question("http://mpercival.com.s3.amazonaws.com/newtweet.html")
|
11
11
|
end
|
12
12
|
|
data/lib/rturk/adapters/hit.rb
CHANGED
@@ -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.
|
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
|
-
|
44
|
-
|
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
|
-
|
50
|
-
|
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
|
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
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
41
|
-
|
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
|
@@ -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 \<QuestionForm then it's used as the full QuestionForm value. Otherwise it is wrapped in a \<QuestionForm\> 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, :
|
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::
|
14
|
+
# @return [RTurk::ExternalQuestion] The question if instantiated or nil
|
15
15
|
def question(*args)
|
16
16
|
unless args.empty?
|
17
|
-
@question ||= RTurk::
|
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' => (
|
33
|
+
'HITTypeId' => hit_type_id,
|
34
|
+
'MaxAssignments' => (max_assignments || 1),
|
27
35
|
'Question' => question.to_params,
|
28
36
|
'LifetimeInSeconds' => (lifetime || 3600),
|
29
|
-
'RequesterAnnotation' =>
|
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
|