Skip to content

Commit

Permalink
some of these files are leaving
Browse files Browse the repository at this point in the history
  • Loading branch information
Mark Percival committed Oct 15, 2009
1 parent 88d1c86 commit cf4b266
Show file tree
Hide file tree
Showing 16 changed files with 422 additions and 0 deletions.
3 changes: 3 additions & 0 deletions TODO.markdown
@@ -0,0 +1,3 @@
Fakeweb all requests, should be able to turn off
Better error handling and checking
Operations should include request?
Empty file added lib/rturk/base.rb
Empty file.
98 changes: 98 additions & 0 deletions lib/rturk/builders/qualifications_builder.rb
@@ -0,0 +1,98 @@
module RTurk

class Qualifications

# For more information about qualification requirements see:
# http://docs.amazonwebservices.com/AWSMturkAPI/2008-08-02/index.html?ApiReference_QualificationRequirementDataStructureArticle.html
#

COMPARATORS = {:gt => 'GreaterThan', :lt => 'LessThan', :gte => 'GreaterThanOrEqualTo',
:lte => 'LessThanOrEqualTo', :eql => 'EqualTo', :not => 'NotEqualTo', :exists => 'Exists'}

TYPES = {:approval_rate => '000000000000000000L0', :submission_rate => '00000000000000000000',
:abandoned_rate => '0000000000000000007', :return_rate => '000000000000000000E0',
:rejection_rate => '000000000000000000S0', :hits_approved => '00000000000000000040',
:adult => '00000000000000000060', :country => '00000000000000000071'}

attr_accessor :requirements, :types

def initialize
@requirements = []
@types = {}
end


# Builds the basic requirements for a qualification
# needs at the minimum
# :type_id, :comparator => :value
#
def build (opts)
# If the value is a string, we can assume it's the country since,
# Amazon states that there can be only integer values and countries
operation = opts.reject{|k,v| !COMPARATORS.include?(k)}
comparator = COMPARATORS[operation.keys.first]
value = operation.values.first
params = {}
value = 1 if value == true # For boolean types eg. Adult
if value.to_s.match(/[A-Z]./)
params[:Country] = value
else
params[:IntegerValue] = value
end
params = params.merge({:QualificationTypeId => opts[:type_id],
:Comparator => comparator, :RequiredToPreview => opts[:required_to_preview]})
end

def to_aws_params
params = {}
@requirements.each_with_index do |qualifier, i|
params["QualificationRequirement.#{i+1}.QualificationTypeId"] = qualifier[:QualificationTypeId]
params["QualificationRequirement.#{i+1}.Comparator"] = qualifier[:Comparator]
params["QualificationRequirement.#{i+1}.IntegerValue"] = qualifier[:IntegerValue] if qualifier[:IntegerValue]
params["QualificationRequirement.#{i+1}.LocaleValue.Country"] = qualifier[:Country] if qualifier[:Country]
params["QualificationRequirement.#{i+1}.RequiredToPreview"] = qualifier[:RequiredToPreview] || 'true'
end
params
end

# Can use this to manually add custom requirement types
# Needs a type name(you can reference this later)
# and the operation as a hash: ':gt => 85'
# Example
# qualifications.add('EnglishSkillsRequirement', :gt => 66, :type_id => '1234567890123456789ABC')
#
def add(opts)
@requirements << self.build(opts)
end

# This lets you add a custom named type to the list
# Example
# qualifications.add_type(:custom_requirement, '1234567890123456789ABC')
# qualifications.custom_requirement(:gte => 55)
#
def add_type(name, type_id)
@types[name.to_sym] = type_id
end

def method_missing(method, opts)
if opts == true || opts == false
# allows us to call booleans on a method
# e.g. qualifications.adult(true)
opts = {:eql => opts}
end
if types.include?(method)
opts.merge!({:type_id => types[method]})
self.add(opts)
end
end

def types
TYPES.merge(@types)
end

end


end


50 changes: 50 additions & 0 deletions lib/rturk/builders/question_builder.rb
@@ -0,0 +1,50 @@
require 'cgi'

module RTurk
class Question

attr_accessor :url, :url_params, :frame_height

def initialize(url = nil, opts = {})
@url = url
self.frame_height = opts.delete(:frame_height) || 400
self.url_params = opts
end

def querystring
@url_params.collect { |key, value| [CGI.escape(key.to_s), CGI.escape(value.to_s)].join('=') }.join('&')
end

def url
unless querystring.empty?
# slam the params onto url, if url already has params, add 'em with a &
@url.index('?') ? "#{@url}&#{querystring}" : "#{@url}?#{querystring}"
else
@url
end
end

def params
@url_params
end

def params=(param_set)
@url_params = param_set
end

def to_aws_params
raise MissingURL, "needs a url to build an external question" unless @url
# TODO: update the xmlns schema... maybe
xml = <<-XML
<ExternalQuestion xmlns="http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2006-07-14/ExternalQuestion.xsd">
<ExternalURL>#{url}</ExternalURL>
<FrameHeight>#{frame_height}</FrameHeight>
</ExternalQuestion>
XML
xml
end

end


end
16 changes: 16 additions & 0 deletions lib/rturk/credentials.rb
@@ -0,0 +1,16 @@
module RTurk
module Credentials

SANDBOX = 'http://mechanicalturk.sandbox.amazonaws.com/'
PRODUCTION = 'http://mechanicalturk.amazonaws.com/'

attr_reader :access_key, :secret_key, :host

def initialize(access_key, secret_key, opts ={})
@access_key = access_key
@secret_key = secret_key
@host = opts[:sandbox] ? SANDBOX : PRODUCTION
end

end
end
21 changes: 21 additions & 0 deletions lib/rturk/logging.rb
@@ -0,0 +1,21 @@
require 'logger'

module RTurk
module Logging
def logger=(logger_obj)
@logger = logger_obj
end

def logger
unless @logger
@logger = Logger.new(STDOUT)
@logger.level = Logger::INFO
end
@logger
end

def log_level=(level=Logger::INFO)
logger.level = level
end
end
end
61 changes: 61 additions & 0 deletions lib/rturk/operations/create_hit.rb
@@ -0,0 +1,61 @@
module RTurk
class CreateHit < Operation
#
# We perform the magic here to create a HIT with the minimum amount of fuss.
# You should be able to pass in a hash with all the setting(ala YAML) or
# do all the config in a block.
#

attr_accessor :title, :keywords, :description, :reward, :currency, :assignments
attr_accessor :lifetime, :duration, :auto_approval, :note, :qualifications


def initialize(opts = {})
opts.each_pair do |k,v|
if v.is_a? Hash
obj = self.send k.to_sym
v.each_pair do |key,val|
obj.send key.to_sym, val
end
elsif self.respond_to?("#{k.to_sym}=")
self.send "#{k}=".to_sym, v
elsif self.respond_to?(k.to_sym)
self.send k.to_sym, v
end
end
yield(self) if block_given?
end

def qualification
@qualifications ||= RTurk::Qualifications.new
end

def question
@question ||= RTurk::Question.new
end

def to_aws_params
hit_params.merge(qualification.to_aws_params)
end

def response(xml)
RTurk::CreateHitResponse.parse(xml)
end

private

def hit_params
{'Title'=>self.title,
'MaxAssignments' => self.assignments,
'LifetimeInSeconds'=> self.lifetime,
'Reward.Amount' => self.reward,
'Reward.CurrencyCode' => (self.currency || 'USD'),
'Keywords' => self.keywords,
'Description' => self.description,
'RequesterAnnotation' => note}
end

RTurk::Operation.register('create_hit', self)

end
end
7 changes: 7 additions & 0 deletions lib/rturk/operations/get_answer.rb
@@ -0,0 +1,7 @@
module RTurk
class GetAnswer < Operation
def get_it

end
end
end
20 changes: 20 additions & 0 deletions lib/rturk/parsers/answer_parser.rb
@@ -0,0 +1,20 @@
module RTurk
class AnswerParser

def self.parse(xml)
answer = XmlSimple.xml_in(xml, {'ForceArray' => false})
response = {}
answers = answer['Answer']
answers = Array.new(1) { answers } unless answers.instance_of? Array
answers.each do |a|
question = a['QuestionIdentifier']
a.delete('QuestionIdentifier')
a.each_value do |v|
response[question] = v
end
end
response
end

end
end
20 changes: 20 additions & 0 deletions lib/rturk/responses/basic_response.rb
@@ -0,0 +1,20 @@
module RTurk

class BasicResponse < Response

def parsed_xml
@parsed_xml ||= XmlSimple.xml_in(@xml.to_s, {'ForceArray' => false})
end

def success?
!parsed_xml['HIT']['Request'].include?('Errors')
end

def [](key)
parsed_xml[key]
end


end

end
5 changes: 5 additions & 0 deletions lib/rturk/responses/create_hit_response.rb
@@ -0,0 +1,5 @@
module RTurk
class HitResponse < Response

end
end
12 changes: 12 additions & 0 deletions spec/fake_responses/create_hit.xml
@@ -0,0 +1,12 @@
<CreateHITResponse>
<OperationRequest>
<RequestId>ece2785b-6292-4b12-a60e-4c34847a7916</RequestId>
</OperationRequest>
<HIT>
<Request>
<IsValid>True</IsValid>
</Request>
<HITId>GBHZVQX3EHXZ2AYDY2T0</HITId>
<HITTypeId>NYVZTQ1QVKJZXCYZCZVZ</HITTypeId>
</HIT>
</CreateHITResponse>
41 changes: 41 additions & 0 deletions spec/hit_operation_spec.rb
@@ -0,0 +1,41 @@
require File.dirname(__FILE__) + '/spec_helper'

describe "The creation of a HIT operation" do

before(:all) do
aws = YAML.load(File.open(File.join(SPEC_ROOT, 'mturk.yml')))
@turk = RTurk::Requester.new(aws['AWSAccessKeyId'], aws['AWSAccessKey'], :sandbox => true)
end

it "should allow us to create HIT's in a ruby'esque way" do

hit = RTurk::Hit.new(:assignments => 5) do |hit|
hit.qualification.approval :gt => 90
hit.qualification.country :not => 'PH'
hit.qualification.adult true
hit.title = "Look at some dirty pictures from 4Chan"
hit.question('http://mpercival.com', :chapter => 1)
hit.question.params[:chapter] = 2 #change the parameters
end

hit.assignments.should eql 5
hit.title.should eql "Look at some dirty pictures from 4Chan"
hit.question.url.should == "http://mpercival.com?chapter=2"
end

it "should allow us to create HIT's with a hash from yaml" do

hit = RTurk::Hit.new(:assignments => 5, :title => "Look at some dirty pictures from 4Chan",
:question => 'http://mpercival.com', :assignments => 5,
:qualification =>
{
:approval => {:gt => 90},
:adult => true,
:country => {:not => 'PH'}
},
:description => "Get paid in nickels!")
hit.assignments.should eql 5
hit.title.should eql "Look at some dirty pictures from 4Chan"
end

end
17 changes: 17 additions & 0 deletions spec/hit_spec.yml
@@ -0,0 +1,17 @@
---
:Description: Simply write a twitter update for me
:LifetimeInSeconds: 3600
:RequesterAnnotation: OptionalNote
:Reward:
:Amount: 0.1
:CurrencyCode: USD
:AssignmentDurationInSeconds: 3600
:AutoApprovalDelayInSeconds: 3600
:QualificationRequirement:
- :IntegerValue: 90
:Comparator: GreaterThan
:RequiredToPreview: "false"
:QualificationTypeId: 000000000000000000L0
:Title: Write a twitter update
:Keywords: twitter, blogging, writing, english
:MaxAssignments: 1

0 comments on commit cf4b266

Please sign in to comment.