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 +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
|