shared_workforce 0.2.2 → 0.2.3
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.
- data/lib/shared_workforce/end_point.rb +16 -10
- data/lib/shared_workforce/exceptions.rb +1 -0
- data/lib/shared_workforce/task.rb +1 -0
- data/lib/shared_workforce/task_response.rb +3 -2
- data/lib/shared_workforce/task_result.rb +14 -3
- data/lib/shared_workforce/version.rb +1 -1
- data/spec/end_point_spec.rb +25 -0
- data/spec/task_request/http_spec.rb +2 -1
- data/spec/task_response_spec.rb +17 -0
- data/spec/task_result_spec.rb +42 -12
- data/spec/task_spec.rb +7 -4
- metadata +14 -12
@@ -1,26 +1,26 @@
|
|
1
1
|
module SharedWorkforce
|
2
2
|
class EndPoint
|
3
3
|
|
4
|
-
def initialize(app)
|
4
|
+
def initialize(app = nil)
|
5
5
|
@app = app
|
6
6
|
end
|
7
7
|
|
8
8
|
def call(env)
|
9
9
|
if env["PATH_INFO"] =~ /^\/#{callback_path}/
|
10
|
-
|
10
|
+
Rack::Request.new(env)
|
11
|
+
process_response(req.body.read)
|
11
12
|
else
|
12
|
-
@app.call(env)
|
13
|
+
@app.call(env) if @app.respond_to? :call
|
13
14
|
end
|
14
|
-
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
req = Rack::Request.new(env)
|
21
|
-
body = JSON.parse(req.body.read)
|
22
|
-
puts "processing task callback"
|
17
|
+
def process_response(body)
|
18
|
+
body = JSON.parse(body)
|
19
|
+
puts "Processing Shared Workforce task callback"
|
23
20
|
puts body.inspect
|
21
|
+
|
22
|
+
raise SecurityTransgression unless valid_api_key?(body['api_key'])
|
23
|
+
|
24
24
|
SharedWorkforce::TaskResult.new(body).process!
|
25
25
|
|
26
26
|
[ 200,
|
@@ -34,5 +34,11 @@ module SharedWorkforce
|
|
34
34
|
SharedWorkforce.configuration.callback_path
|
35
35
|
end
|
36
36
|
|
37
|
+
private
|
38
|
+
|
39
|
+
def valid_api_key?(key)
|
40
|
+
key == SharedWorkforce.configuration.api_key
|
41
|
+
end
|
42
|
+
|
37
43
|
end
|
38
44
|
end
|
@@ -6,15 +6,16 @@ module SharedWorkforce
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def to_hash
|
9
|
-
{:answer=>answer, :answered_by=>username}
|
9
|
+
{:answer=>answer, :answered_by=>username, :new_image_url=>new_image_url}.reject {|k,v| v.nil? }.with_indifferent_access
|
10
10
|
end
|
11
11
|
|
12
|
-
attr_accessor :answer, :callback_params, :username
|
12
|
+
attr_accessor :answer, :callback_params, :username, :new_image_url
|
13
13
|
|
14
14
|
def initialize(params)
|
15
15
|
@answer = params['answer']
|
16
16
|
@callback_params = params['callback_params']
|
17
17
|
@username = params['username']
|
18
|
+
@new_image_url = params['new_image_url']
|
18
19
|
end
|
19
20
|
|
20
21
|
end
|
@@ -1,17 +1,28 @@
|
|
1
1
|
module SharedWorkforce
|
2
2
|
class TaskResult
|
3
3
|
|
4
|
-
|
4
|
+
attr_reader :callback_params
|
5
5
|
attr_accessor :responses
|
6
6
|
attr_accessor :name
|
7
7
|
attr_accessor :status
|
8
|
+
attr_accessor :task_hash
|
8
9
|
|
9
10
|
def initialize(params)
|
10
11
|
params = params.with_indifferent_access
|
11
|
-
|
12
|
-
|
12
|
+
|
13
|
+
self.task_hash = if params[:task]
|
14
|
+
params[:task]
|
15
|
+
else
|
16
|
+
params
|
17
|
+
end
|
18
|
+
|
19
|
+
@responses = TaskResponse.create_collection_from_array(task_hash[:responses]) if task_hash[:responses]
|
13
20
|
self.name = callback_params[:_task][:class_name] if callback_params && callback_params[:_task] && callback_params[:_task][:class_name]
|
14
21
|
end
|
22
|
+
|
23
|
+
def callback_params
|
24
|
+
task_hash[:callback_params]
|
25
|
+
end
|
15
26
|
|
16
27
|
def responses
|
17
28
|
@responses.map(&:to_hash)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require 'rack'
|
3
|
+
describe "EndPoint" do
|
4
|
+
|
5
|
+
describe "#process_request" do
|
6
|
+
it "should not raise a security transgression with a valid api key" do
|
7
|
+
lambda {
|
8
|
+
SharedWorkforce::EndPoint.new.process_response({:api_key => SharedWorkforce.configuration.api_key}.to_json)
|
9
|
+
}.should_not raise_error(SharedWorkforce::SecurityTransgression)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should raise a security transgression with an invalid api key" do
|
13
|
+
lambda {
|
14
|
+
SharedWorkforce::EndPoint.new.process_response({:api_key=>'GIBBERISH'}.to_json)
|
15
|
+
}.should raise_error(SharedWorkforce::SecurityTransgression)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should raise a security transgression with a missing api key" do
|
19
|
+
lambda {
|
20
|
+
SharedWorkforce::EndPoint.new.process_response({}.to_json)
|
21
|
+
}.should raise_error(SharedWorkforce::SecurityTransgression)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -5,7 +5,7 @@ describe "TaskRequest::Http" do
|
|
5
5
|
it "should invoke a task request" do
|
6
6
|
|
7
7
|
task = ApprovePhotoTask.new
|
8
|
-
task_request = SharedWorkforce::TaskRequest::Http.new(task, :image_url=>"http://www.google.com/logo.png", :callback_params=>{:resource_id=>'1234'})
|
8
|
+
task_request = SharedWorkforce::TaskRequest::Http.new(task, :image_url=>"http://www.google.com/logo.png", :image_crop_ratio=>1.7, :callback_params=>{:resource_id=>'1234'})
|
9
9
|
|
10
10
|
stub_request(:post, "api.sharedworkforce.com/tasks")
|
11
11
|
task_request.create
|
@@ -14,6 +14,7 @@ describe "TaskRequest::Http" do
|
|
14
14
|
'title'=>"Approve Photo",
|
15
15
|
'instruction'=>"Please classify this photo by choosing the appropriate tickboxes.",
|
16
16
|
'image_url'=>"http://www.google.com/logo.png",
|
17
|
+
'image_crop_ratio'=>1.7,
|
17
18
|
'answer_options'=> ['Obscenity', 'Nudity', 'Blurry', 'Upside down or sideways', 'Contains more than one person in the foreground', 'Has people in the background', 'Contains children'],
|
18
19
|
'responses_required'=>3,
|
19
20
|
'answer_type'=>'tags',
|
data/spec/task_response_spec.rb
CHANGED
@@ -14,4 +14,21 @@ describe "TaskResponse" do
|
|
14
14
|
r[1].username.should == "frodo"
|
15
15
|
end
|
16
16
|
end
|
17
|
+
|
18
|
+
describe "#to_hash" do
|
19
|
+
it "should include answered_by" do
|
20
|
+
t = SharedWorkforce::TaskResponse.new('username'=>'bilbo')
|
21
|
+
t.to_hash[:answered_by].should == 'bilbo'
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should include new_image_url" do
|
25
|
+
t = SharedWorkforce::TaskResponse.new('new_image_url'=>'http://www.example.com/image.jpg')
|
26
|
+
t.to_hash[:new_image_url].should == 'http://www.example.com/image.jpg'
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should have indifferent access" do
|
30
|
+
t = SharedWorkforce::TaskResponse.new('new_image_url'=>'http://www.example.com/image.jpg')
|
31
|
+
t.to_hash['new_image_url'].should == 'http://www.example.com/image.jpg'
|
32
|
+
end
|
33
|
+
end
|
17
34
|
end
|
data/spec/task_result_spec.rb
CHANGED
@@ -1,37 +1,67 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
|
3
3
|
describe "TaskResult" do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@task_result_hash = {
|
7
|
+
'task' => {
|
8
|
+
'name'=>"Approve photo please",
|
9
|
+
'callback_params' =>
|
10
|
+
{
|
11
|
+
'_task'=>{'class_name'=>'ApprovePhotoTask'},
|
12
|
+
'resource_id' => '2',
|
13
|
+
},
|
14
|
+
'responses' => [{'answer' => 'yes'}]
|
15
|
+
}
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
4
19
|
describe "#process!" do
|
5
20
|
it "should invoke Task#process_result" do
|
6
21
|
ApprovePhotoTask.any_instance.should_receive(:process_result).once
|
7
|
-
SharedWorkforce::TaskResult.new(
|
22
|
+
SharedWorkforce::TaskResult.new(@task_result_hash).process!
|
8
23
|
end
|
9
24
|
end
|
10
25
|
|
11
26
|
describe "#responses" do
|
12
27
|
it "should convert the result to a hash" do
|
13
28
|
r = SharedWorkforce::TaskResult.new(
|
14
|
-
{'
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
29
|
+
{'task' =>
|
30
|
+
{
|
31
|
+
'name'=>"Approve photo",
|
32
|
+
'callback_params'=>{'resource_id' => '2'},
|
33
|
+
'responses'=>[
|
34
|
+
{'answer'=>'yes', 'username'=>'bilbo'},
|
35
|
+
{'answer'=>'no', 'username'=>'frodo'},
|
36
|
+
{'answer'=>'yes', 'username'=>'sam'}
|
37
|
+
]
|
38
|
+
}
|
21
39
|
}
|
22
40
|
)
|
23
41
|
|
24
42
|
r.responses.should == [
|
25
|
-
{
|
26
|
-
{
|
27
|
-
{
|
43
|
+
{'answer'=>'yes', 'answered_by'=>'bilbo'},
|
44
|
+
{'answer'=>'no', 'answered_by'=>'frodo'},
|
45
|
+
{'answer'=>'yes', 'answered_by'=>'sam'}
|
28
46
|
]
|
29
47
|
end
|
30
48
|
end
|
49
|
+
|
50
|
+
describe "#task_attributes" do
|
51
|
+
it "should assign the task hash" do
|
52
|
+
task = SharedWorkforce::TaskResult.new(@task_result_hash)
|
53
|
+
task.task_hash.should == @task_result_hash['task']
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should accept a task that is not nested in a :task key (backwards compatibility)" do
|
57
|
+
task = SharedWorkforce::TaskResult.new(@task_result_hash['task'])
|
58
|
+
task.task_hash.should == @task_result_hash['task']
|
59
|
+
end
|
60
|
+
end
|
31
61
|
|
32
62
|
describe "#usernames" do
|
33
63
|
it "should collect the usernames" do
|
34
|
-
r = SharedWorkforce::TaskResult.new({'callback_params'=>{'resource_id' => '2'}, 'responses'=>[{'answer'=>'yes', 'username'=>'bilbo'}, {'answer'=>'no', 'username'=>'frodo'}, {'answer'=>'yes', 'username'=>'sam'}], 'name'=>"Approve photo"})
|
64
|
+
r = SharedWorkforce::TaskResult.new('task'=>{'callback_params'=>{'resource_id' => '2'}, 'responses'=>[{'answer'=>'yes', 'username'=>'bilbo'}, {'answer'=>'no', 'username'=>'frodo'}, {'answer'=>'yes', 'username'=>'sam'}], 'name'=>"Approve photo"})
|
35
65
|
r.usernames.should == ['bilbo', 'frodo', 'sam']
|
36
66
|
end
|
37
67
|
end
|
data/spec/task_spec.rb
CHANGED
@@ -15,6 +15,8 @@ describe "Task" do
|
|
15
15
|
instruction 'Please classify this photo by choosing the appropriate tickboxes.'
|
16
16
|
responses_required 3
|
17
17
|
image_url "http://www.google.com/logo.png"
|
18
|
+
answer_type :crop
|
19
|
+
image_crop_ratio 1.7
|
18
20
|
|
19
21
|
answer_options ['Obscenity', 'Nudity', 'Blurry', 'Upside down or sideways']
|
20
22
|
|
@@ -35,6 +37,7 @@ describe "Task" do
|
|
35
37
|
task.title.should == "Approve Photo"
|
36
38
|
task.responses_required.should == 3
|
37
39
|
task.image_url.should == "http://www.google.com/logo.png"
|
40
|
+
task.image_crop_ratio.should == 1.7
|
38
41
|
task.answer_options.should == ['Obscenity', 'Nudity', 'Blurry', 'Upside down or sideways']
|
39
42
|
end
|
40
43
|
|
@@ -85,7 +88,7 @@ describe "Task" do
|
|
85
88
|
task_class = Class.new { include SharedWorkforce::Task; on_success :do_work; def do_work(*args); end; def setup(*args); end }
|
86
89
|
task_class.any_instance.should_receive(:success!)
|
87
90
|
task_class.any_instance.should_receive(:complete!)
|
88
|
-
task = task_class.new(SharedWorkforce::TaskResult.new({'callback_params'=>{'key'=>'value'}}))
|
91
|
+
task = task_class.new(SharedWorkforce::TaskResult.new({'task'=>{'callback_params'=>{'key'=>'value'}}}))
|
89
92
|
task.attributes[:key].should == 'value'
|
90
93
|
end
|
91
94
|
end
|
@@ -152,7 +155,7 @@ describe "Task" do
|
|
152
155
|
end
|
153
156
|
|
154
157
|
it "should pass the resource to the callback method as the first argument and the result as the second argument" do
|
155
|
-
result = SharedWorkforce::TaskResult.new(
|
158
|
+
result = SharedWorkforce::TaskResult.new({'task'=>{'callback_params'=>@task.attributes}})
|
156
159
|
@task.should_receive(:do_work).with(@resource, @result.responses)
|
157
160
|
@task.success!(@result)
|
158
161
|
end
|
@@ -290,13 +293,13 @@ describe "Task" do
|
|
290
293
|
it "should return the resource from the callback params" do
|
291
294
|
class ResourceFinder; def self.find(id); return "#{id}ABCD"; end; end
|
292
295
|
task_class = Class.new { include SharedWorkforce::Task }
|
293
|
-
task = task_class.new(SharedWorkforce::TaskResult.new({'callback_params'=>{'_task'=>{'resource'=>{'class_name'=>'ResourceFinder', 'id' => '2'}}}}))
|
296
|
+
task = task_class.new(SharedWorkforce::TaskResult.new({'api_key'=>SharedWorkforce.configuration.api_key, 'task'=>{'callback_params'=>{'_task'=>{'resource'=>{'class_name'=>'ResourceFinder', 'id' => '2'}}}}}))
|
294
297
|
task.resource.should == "2ABCD"
|
295
298
|
end
|
296
299
|
|
297
300
|
it "should return nil if the callback params do not specify a resource" do
|
298
301
|
task_class = Class.new { include SharedWorkforce::Task }
|
299
|
-
task = task_class.new(SharedWorkforce::TaskResult.new({'callback_params'=>{}}))
|
302
|
+
task = task_class.new(SharedWorkforce::TaskResult.new({'api_key'=>SharedWorkforce.configuration.api_key, 'task'=>{'callback_params'=>{}}}))
|
300
303
|
task.resource.should == nil
|
301
304
|
end
|
302
305
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shared_workforce
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-04-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rest-client
|
16
|
-
requirement: &
|
16
|
+
requirement: &70269574726440 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70269574726440
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: json
|
27
|
-
requirement: &
|
27
|
+
requirement: &70269574725980 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70269574725980
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: activesupport
|
38
|
-
requirement: &
|
38
|
+
requirement: &70269574725520 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70269574725520
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rspec
|
49
|
-
requirement: &
|
49
|
+
requirement: &70269574724960 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: 1.2.9
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70269574724960
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: webmock
|
60
|
-
requirement: &
|
60
|
+
requirement: &70269574724540 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,7 +65,7 @@ dependencies:
|
|
65
65
|
version: '0'
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70269574724540
|
69
69
|
description: Shared Workforce is a service and simple API for human intelligence tasks
|
70
70
|
email:
|
71
71
|
- sam@samoliver.com
|
@@ -96,6 +96,7 @@ files:
|
|
96
96
|
- shared_workforce.gemspec
|
97
97
|
- spec/.rspec
|
98
98
|
- spec/client_spec.rb
|
99
|
+
- spec/end_point_spec.rb
|
99
100
|
- spec/spec_helper.rb
|
100
101
|
- spec/support/configuration_helper.rb
|
101
102
|
- spec/task_request/black_hole_spec.rb
|
@@ -130,6 +131,7 @@ specification_version: 3
|
|
130
131
|
summary: Shared Workforce Client
|
131
132
|
test_files:
|
132
133
|
- spec/client_spec.rb
|
134
|
+
- spec/end_point_spec.rb
|
133
135
|
- spec/spec_helper.rb
|
134
136
|
- spec/support/configuration_helper.rb
|
135
137
|
- spec/task_request/black_hole_spec.rb
|