shared_workforce 0.1.0 → 0.1.1
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/README.markdown +150 -0
- data/lib/shared_workforce/client.rb +0 -17
- data/lib/shared_workforce/configuration.rb +26 -0
- data/lib/shared_workforce/end_point.rb +5 -1
- data/lib/shared_workforce/frameworks/rails.rb +2 -2
- data/lib/shared_workforce/task.rb +13 -8
- data/lib/shared_workforce/task_request/black_hole.rb +7 -0
- data/lib/shared_workforce/task_request/http.rb +19 -0
- data/lib/shared_workforce/task_request/task_request.rb +29 -0
- data/lib/shared_workforce/version.rb +1 -1
- data/lib/shared_workforce.rb +22 -2
- data/spec/spec_helper.rb +8 -2
- data/spec/support/configuration_helper.rb +17 -0
- data/spec/task_request/black_hole_spec.rb +19 -0
- data/spec/task_request/http_spec.rb +125 -0
- data/spec/task_response_spec.rb +11 -9
- data/spec/task_result_spec.rb +31 -27
- data/spec/task_spec.rb +14 -8
- metadata +13 -6
- data/README.rdoc +0 -0
- data/lib/shared_workforce/task_request.rb +0 -27
- data/spec/task_request_spec.rb +0 -126
data/README.markdown
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
Shared Workforce Client
|
2
|
+
=======================
|
3
|
+
|
4
|
+
Shared Workforce is a platform for managing and completing repetitive tasks that require human intelligence. For example, tagging photos, approving text and answering simple questions.
|
5
|
+
|
6
|
+
It differs from other similar services in the following ways:
|
7
|
+
|
8
|
+
* All tasks should be very simple and typically take no more than 10-15 seconds. (larger tasks can be broken down in to smaller ones).
|
9
|
+
* Tasks are displayed from a selection of pre-defined formats.
|
10
|
+
* Very simple integration via this ruby gem or the straight forward REST API.
|
11
|
+
|
12
|
+
The service is currently in private beta. You can apply for an invitation at [sharedworkforce.com](http://www.sharedworkforce.com).
|
13
|
+
|
14
|
+
Getting started
|
15
|
+
===============
|
16
|
+
|
17
|
+
### Step 1 - get an API key
|
18
|
+
|
19
|
+
Register for a beta invitation at [sharedworkforce.com](http://www.sharedworkforce.com). Once you are invited, you can create your account and retrieve your API key - which will look something like this:
|
20
|
+
|
21
|
+
acdc30b2-14c5-46ee-ba35-11d50edc65ec
|
22
|
+
|
23
|
+
### Step 2 - add the gem
|
24
|
+
|
25
|
+
Add shared_workforce to your Gemfile:
|
26
|
+
|
27
|
+
gem "shared_workforce"
|
28
|
+
|
29
|
+
Or, for Rails 2.x add the gem to your environment.rb
|
30
|
+
|
31
|
+
config.gem "shared_workforce"
|
32
|
+
|
33
|
+
Create config/initializers/shared_workforce.rb
|
34
|
+
|
35
|
+
SharedWorkforce.configure do |config|
|
36
|
+
config.api_key = "your-api-key"
|
37
|
+
config.callback_host = "http://your-website-host"
|
38
|
+
end
|
39
|
+
|
40
|
+
If you're not using Rails, simply require the gem or include it in your Gemfile, set the client configuration settings as above.
|
41
|
+
|
42
|
+
### Step 3 - define tasks
|
43
|
+
|
44
|
+
Create a directory called 'tasks' in the root of your app. This is where you define your tasks - all files in this directory will be loaded.
|
45
|
+
|
46
|
+
If, for example, you would like to have a photo tagged on upload, create your first task in a file called tasks/tag_photo.rb
|
47
|
+
|
48
|
+
SharedWorkforce::Task.define "Tag photo" do |t|
|
49
|
+
|
50
|
+
t.directions = "Look at this photo. Please tick all that apply."
|
51
|
+
t.answer_options = ['Offensive', 'Contains Nudity', 'Blurry or low quality']
|
52
|
+
t.answer_type = :tags
|
53
|
+
t.responses_required = 1
|
54
|
+
t.replace = true
|
55
|
+
|
56
|
+
t.on_completion do |result|
|
57
|
+
photo = Photo.find(result.callback_params['photo_id'])
|
58
|
+
if result.answers.include?('Offensive')
|
59
|
+
photo.hide!
|
60
|
+
photo.add_comment("Photo hidden because it was offensive")
|
61
|
+
elsif result.answers.include?('Contains Nudity')
|
62
|
+
photo.refer!
|
63
|
+
photo.add_comment("Photo referred because it contains nudity")
|
64
|
+
else
|
65
|
+
photo.approve!
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
### Step 4 - request tasks
|
72
|
+
|
73
|
+
Publishing tasks is simply a case of calling SharedWorkforce::Task.request(name, options). If you are using Rails, this could be done in an after save callback on a model:
|
74
|
+
|
75
|
+
class Photo < ActiveRecord::Base
|
76
|
+
|
77
|
+
after_create :request_tags
|
78
|
+
|
79
|
+
def request_tags
|
80
|
+
SharedWorkforce::Task.request "Tag photo", {:image_url => self.url, :callback_params => { :photo_id => self.id} }
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
That's it - once your task is completed the callback you have defined in the task definition will be called. Everything you define in the :callback_params option will be sent back to your callback as shown in the example.
|
86
|
+
|
87
|
+
Advanced definition options
|
88
|
+
----------------------------------------
|
89
|
+
|
90
|
+
### Task types
|
91
|
+
|
92
|
+
SharedWorkforce currently supports 2 types of task. :tags (multiple select) and :choice (single answer from a list of options). Free-form input is planned in the near future.
|
93
|
+
|
94
|
+
SharedWorkforce::Task.define |t|
|
95
|
+
t.answer_type = #:tags or :choice
|
96
|
+
end
|
97
|
+
|
98
|
+
###Multiple responses
|
99
|
+
|
100
|
+
SharedWorkforce supports multiple responses for each task. The callback method provides you with an array of responses from multiple workers. You can create your own logic to decide what to do. This is useful if you want to prevent destructive action unless a number of workers agree.
|
101
|
+
|
102
|
+
SharedWorkforce::Task.define "Tag photo" do |t|
|
103
|
+
|
104
|
+
t.directions = "Look at this photo. Please tick all that apply."
|
105
|
+
t.answer_options = ['Offensive', 'Contains Nudity', 'Blurry or low quality']
|
106
|
+
t.answer_type = :tags
|
107
|
+
t.responses_required = 3
|
108
|
+
|
109
|
+
t.on_completion do |result|
|
110
|
+
photo = Photo.find(result.callback_params['photo_id'])
|
111
|
+
photo.hide! if result.answers.all? { |a| a == 'Contains Nudity' }
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
###Replacing tasks
|
117
|
+
|
118
|
+
The "replace" option allows you to overwrite or update any existing tasks with the same name and callback params. This could be useful in the example to handle the situation where a user re-uploads their photo - you may only care about the latest one.
|
119
|
+
|
120
|
+
SharedWorkforce::Task.define "Tag photo" do |t|
|
121
|
+
...
|
122
|
+
t.replace=true
|
123
|
+
...
|
124
|
+
end
|
125
|
+
|
126
|
+
###Cancelling tasks
|
127
|
+
|
128
|
+
You can cancel tasks when they are no longer relevant.
|
129
|
+
|
130
|
+
class Photo < ActiveRecord::Base
|
131
|
+
after_destroy :cancel_tagging_request
|
132
|
+
|
133
|
+
def cancel_tagging_request
|
134
|
+
SharedWorkforce::Task.cancel "Classify photo", :callback_params=>{:photo_id=>self.id})
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
###Testing and development
|
139
|
+
|
140
|
+
You can black-hole requests to SharedWorkforce for testing and development by adding the following configuration option in your initializer:
|
141
|
+
|
142
|
+
SharedWorkforce.configure do |config|
|
143
|
+
config.request_class = SharedWorkforce::TaskRequest::BlackHole
|
144
|
+
end
|
145
|
+
|
146
|
+
License
|
147
|
+
-----------
|
148
|
+
|
149
|
+
The SharedWorkforce Client is (c) [Pigment](http://www.thinkpigment.com) and released under the MIT license
|
150
|
+
|
@@ -1,15 +1,7 @@
|
|
1
1
|
module SharedWorkforce
|
2
2
|
class Client
|
3
|
-
|
4
|
-
@http_end_point = "http://hci.heroku.com"
|
5
|
-
@load_path = "tasks"
|
6
|
-
|
7
3
|
class << self
|
8
|
-
attr_accessor :api_key
|
9
4
|
attr_accessor :load_path
|
10
|
-
attr_accessor :http_end_point
|
11
|
-
attr_accessor :callback_host
|
12
|
-
attr_accessor :callback_path
|
13
5
|
|
14
6
|
def version
|
15
7
|
SharedWorkforce::VERSION
|
@@ -20,15 +12,6 @@ module SharedWorkforce
|
|
20
12
|
load file
|
21
13
|
end
|
22
14
|
end
|
23
|
-
|
24
|
-
def callback_url
|
25
|
-
callback_host + '/' + callback_path
|
26
|
-
end
|
27
|
-
|
28
|
-
def callback_path
|
29
|
-
"hci_task_result"
|
30
|
-
end
|
31
15
|
end
|
32
|
-
|
33
16
|
end
|
34
17
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module SharedWorkforce
|
2
|
+
class Configuration
|
3
|
+
|
4
|
+
attr_accessor :api_key
|
5
|
+
attr_accessor :load_path
|
6
|
+
attr_accessor :http_end_point
|
7
|
+
attr_accessor :callback_host
|
8
|
+
attr_accessor :request_class
|
9
|
+
attr_writer :callback_path
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@http_end_point = "http://api.sharedworkforce.com"
|
13
|
+
@load_path = "tasks"
|
14
|
+
@request_class = TaskRequest::Http
|
15
|
+
end
|
16
|
+
|
17
|
+
def callback_url
|
18
|
+
callback_host.to_s + '/' + callback_path.to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
def callback_path
|
22
|
+
@callback_path ||= "hci_task_result"
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -6,7 +6,7 @@ module SharedWorkforce
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def call(env)
|
9
|
-
if env["PATH_INFO"] =~ /^\/#{
|
9
|
+
if env["PATH_INFO"] =~ /^\/#{callback_path}/
|
10
10
|
process_request(env)
|
11
11
|
else
|
12
12
|
@app.call(env)
|
@@ -29,6 +29,10 @@ module SharedWorkforce
|
|
29
29
|
[""]
|
30
30
|
]
|
31
31
|
end
|
32
|
+
|
33
|
+
def callback_path
|
34
|
+
SharedWorkforce.configuration.callback_path
|
35
|
+
end
|
32
36
|
|
33
37
|
end
|
34
38
|
end
|
@@ -1,13 +1,13 @@
|
|
1
1
|
if defined?(ActionController::Metal)
|
2
2
|
class Railtie < Rails::Railtie
|
3
3
|
initializer :load_hci do |app|
|
4
|
-
SharedWorkforce::Client.load_path = Rails.root + SharedWorkforce
|
4
|
+
SharedWorkforce::Client.load_path = Rails.root + SharedWorkforce.configuration.load_path
|
5
5
|
SharedWorkforce::Client.load!
|
6
6
|
end
|
7
7
|
end
|
8
8
|
else
|
9
9
|
Rails.configuration.after_initialize do
|
10
|
-
SharedWorkforce::Client.load_path = Rails.root + SharedWorkforce
|
10
|
+
SharedWorkforce::Client.load_path = Rails.root + SharedWorkforce.configuration.load_path
|
11
11
|
SharedWorkforce::Client.load!
|
12
12
|
end
|
13
13
|
end
|
@@ -10,9 +10,6 @@ module SharedWorkforce
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def define(name, &block)
|
13
|
-
raise ConfigurationError, "Please set your SharedWorkforce api key with SharedWorkforce::Client.api_key = 'your-api-key-here'" unless Client.api_key
|
14
|
-
raise ConfigurationError, "Please set your callback URL with SharedWorkforce::Client.callback_host = 'www.your-domain-name.com'" unless Client.callback_host
|
15
|
-
|
16
13
|
task = self.new
|
17
14
|
task.name = name
|
18
15
|
yield task
|
@@ -50,12 +47,12 @@ module SharedWorkforce
|
|
50
47
|
end
|
51
48
|
|
52
49
|
def request(options)
|
53
|
-
task_request =
|
50
|
+
task_request = remote_request(self, options)
|
54
51
|
task_request.create
|
55
52
|
end
|
56
53
|
|
57
54
|
def cancel(options)
|
58
|
-
task_request =
|
55
|
+
task_request = remote_request(self, options)
|
59
56
|
task_request.cancel
|
60
57
|
end
|
61
58
|
|
@@ -67,7 +64,7 @@ module SharedWorkforce
|
|
67
64
|
:answer_options => answer_options,
|
68
65
|
:responses_required => responses_required,
|
69
66
|
:answer_type => answer_type.to_s,
|
70
|
-
:callback_url =>
|
67
|
+
:callback_url => callback_url,
|
71
68
|
:replace => replace
|
72
69
|
}
|
73
70
|
end
|
@@ -89,7 +86,15 @@ module SharedWorkforce
|
|
89
86
|
def fail!(results)
|
90
87
|
@on_failure_proc.call(results) if @on_failure_proc
|
91
88
|
end
|
92
|
-
|
93
|
-
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def callback_url
|
93
|
+
SharedWorkforce.configuration.callback_url
|
94
|
+
end
|
95
|
+
|
96
|
+
def remote_request(*args)
|
97
|
+
SharedWorkforce.configuration.request_class.new(*args)
|
98
|
+
end
|
94
99
|
end
|
95
100
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rest_client'
|
2
|
+
module SharedWorkforce
|
3
|
+
class TaskRequest::Http < TaskRequest
|
4
|
+
|
5
|
+
def initialize(*args)
|
6
|
+
raise ConfigurationError, "Please set your SharedWorkforce api key with SharedWorkforce::Client.api_key = 'your-api-key-here'" unless SharedWorkforce.configuration.api_key
|
7
|
+
raise ConfigurationError, "Please set your callback URL with SharedWorkforce::Client.callback_host = 'www.your-domain-name.com'" unless SharedWorkforce.configuration.callback_host
|
8
|
+
super(*args)
|
9
|
+
end
|
10
|
+
|
11
|
+
def create
|
12
|
+
RestClient.post("#{http_end_point}/tasks", *request_params)
|
13
|
+
end
|
14
|
+
|
15
|
+
def cancel
|
16
|
+
RestClient.post("#{http_end_point}/tasks/cancel", *request_params)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'json'
|
2
|
+
module SharedWorkforce
|
3
|
+
class TaskRequest
|
4
|
+
|
5
|
+
def initialize(task, params)
|
6
|
+
@task = task
|
7
|
+
@params = params
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def request_params
|
13
|
+
[{:task=>@task.to_hash.merge(@params).merge(:callback_url=>callback_url), :api_key=>api_key}.to_json, {:content_type => :json, :accept => :json}]
|
14
|
+
end
|
15
|
+
|
16
|
+
def http_end_point
|
17
|
+
SharedWorkforce.configuration.http_end_point
|
18
|
+
end
|
19
|
+
|
20
|
+
def callback_url
|
21
|
+
SharedWorkforce.configuration.callback_url
|
22
|
+
end
|
23
|
+
|
24
|
+
def api_key
|
25
|
+
SharedWorkforce.configuration.api_key
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
data/lib/shared_workforce.rb
CHANGED
@@ -1,9 +1,29 @@
|
|
1
|
+
require 'shared_workforce/configuration'
|
1
2
|
require 'shared_workforce/exceptions'
|
2
3
|
require 'shared_workforce/client'
|
3
4
|
require 'shared_workforce/version'
|
4
5
|
require 'shared_workforce/task'
|
5
|
-
require 'shared_workforce/task_request'
|
6
|
+
require 'shared_workforce/task_request/task_request'
|
7
|
+
require 'shared_workforce/task_request/black_hole'
|
8
|
+
require 'shared_workforce/task_request/http'
|
6
9
|
require 'shared_workforce/task_result'
|
7
10
|
require 'shared_workforce/task_response'
|
8
11
|
require 'shared_workforce/end_point'
|
9
|
-
require 'shared_workforce/frameworks/rails' if defined?(Rails)
|
12
|
+
require 'shared_workforce/frameworks/rails' if defined?(Rails)
|
13
|
+
|
14
|
+
module SharedWorkforce
|
15
|
+
|
16
|
+
class << self
|
17
|
+
|
18
|
+
attr_writer :configuration
|
19
|
+
|
20
|
+
def configure
|
21
|
+
yield(configuration); nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def configuration
|
25
|
+
@configuration ||= SharedWorkforce::Configuration.new
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -5,6 +5,10 @@ require 'rspec'
|
|
5
5
|
require 'rspec/autorun'
|
6
6
|
require 'webmock/rspec'
|
7
7
|
|
8
|
+
# Requires supporting ruby files with custom matchers and macros, etc,
|
9
|
+
# in spec/support/ and its subdirectories.
|
10
|
+
Dir[File.join(File.expand_path('../support', __FILE__), '**/*.rb')].each {|f| require f}
|
11
|
+
|
8
12
|
RSpec.configure do |config|
|
9
13
|
config.color_enabled = true
|
10
14
|
config.after :each do
|
@@ -12,8 +16,10 @@ RSpec.configure do |config|
|
|
12
16
|
end
|
13
17
|
|
14
18
|
config.before :each do
|
15
|
-
SharedWorkforce
|
16
|
-
|
19
|
+
SharedWorkforce.configure do |config|
|
20
|
+
config.api_key = "test-api-key"
|
21
|
+
config.callback_host = "www.example.com"
|
22
|
+
end
|
17
23
|
end
|
18
24
|
end
|
19
25
|
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ConfigurationHelper
|
2
|
+
def configure(&block)
|
3
|
+
SharedWorkforce.configure(&block)
|
4
|
+
end
|
5
|
+
|
6
|
+
def with_configuration(&block)
|
7
|
+
old_configuration = SharedWorkforce.configuration
|
8
|
+
SharedWorkforce.configuration = SharedWorkforce::Configuration.new
|
9
|
+
yield(SharedWorkforce.configuration)
|
10
|
+
ensure
|
11
|
+
SharedWorkforce.configuration = old_configuration
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
RSpec.configure do |config|
|
16
|
+
config.include ConfigurationHelper
|
17
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe "TaskRequest::BlackHole" do
|
4
|
+
describe "#create" do
|
5
|
+
it "should not make an HTTP request" do
|
6
|
+
task_request = SharedWorkforce::TaskRequest::BlackHole.new(SharedWorkforce::Task.find("Approve photo"), :callback_params=>{:resource_id=>'1234'})
|
7
|
+
task_request.create
|
8
|
+
a_request(:any, "api.sharedworkforce.com/tasks").should_not have_been_made
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#cancel" do
|
13
|
+
it "should not make an HTTP request" do
|
14
|
+
task_request = SharedWorkforce::TaskRequest::BlackHole.new(SharedWorkforce::Task.find("Approve photo"), :callback_params=>{:resource_id=>'1234'})
|
15
|
+
task_request.create
|
16
|
+
a_request(:any, "api.sharedworkforce.com/tasks/cancel").should_not have_been_made
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe "TaskRequest::Http" do
|
4
|
+
describe "#create" do
|
5
|
+
it "should invoke a task request" do
|
6
|
+
|
7
|
+
task = SharedWorkforce::Task.define "Approve photo" do |h|
|
8
|
+
|
9
|
+
h.directions = "Please classify this photo by choosing the appropriate tickboxes."
|
10
|
+
h.image_url = "http://www.google.com/logo.png"
|
11
|
+
h.answer_type = "tags"
|
12
|
+
h.answer_options = ['Obscenity', 'Nudity', 'Blurry', 'Upside down or sideways', 'Contains more than one person in the foreground', 'Has people in the background', 'Contains children']
|
13
|
+
h.responses_required = 3
|
14
|
+
h.replace = false
|
15
|
+
|
16
|
+
h.on_completion do |result|
|
17
|
+
puts "Complete"
|
18
|
+
end
|
19
|
+
|
20
|
+
h.on_failure do |result|
|
21
|
+
puts "Failed"
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
task_request = SharedWorkforce::TaskRequest::Http.new(task, :callback_params=>{:resource_id=>'1234'})
|
27
|
+
|
28
|
+
stub_request(:post, "api.sharedworkforce.com/tasks")
|
29
|
+
task_request.create
|
30
|
+
a_request(:post, "http://api.sharedworkforce.com/tasks").with(:body=>{'task'=>
|
31
|
+
{
|
32
|
+
'name'=>"Approve photo",
|
33
|
+
'directions'=>"Please classify this photo by choosing the appropriate tickboxes.",
|
34
|
+
'image_url'=>"http://www.google.com/logo.png",
|
35
|
+
'answer_options'=> ['Obscenity', 'Nudity', 'Blurry', 'Upside down or sideways', 'Contains more than one person in the foreground', 'Has people in the background', 'Contains children'],
|
36
|
+
'responses_required'=>3,
|
37
|
+
'answer_type'=>'tags',
|
38
|
+
'callback_url'=>"#{SharedWorkforce.configuration.callback_host}/hci_task_result",
|
39
|
+
'callback_params'=>{'resource_id'=>'1234'},
|
40
|
+
'replace'=>false
|
41
|
+
}, :api_key=>'test-api-key'}).should have_been_made.once
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should allow options to be overridden when making the request" do
|
46
|
+
|
47
|
+
task = SharedWorkforce::Task.define "Approve photo" do |h|
|
48
|
+
|
49
|
+
h.directions = "Please classify this photo by choosing the appropriate tickboxes."
|
50
|
+
h.image_url = "http://www.google.com/logo.png"
|
51
|
+
h.answer_type = "tags"
|
52
|
+
h.answer_options = ['Obscenity', 'Nudity', 'Blurry', 'Upside down or sideways', 'Contains more than one person in the foreground', 'Has people in the background', 'Contains children']
|
53
|
+
h.responses_required = 3
|
54
|
+
|
55
|
+
h.on_completion do |result|
|
56
|
+
puts "Complete"
|
57
|
+
end
|
58
|
+
|
59
|
+
h.on_failure do |result|
|
60
|
+
puts "Failed"
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
task_request = SharedWorkforce::TaskRequest::Http.new(task, {:callback_params=>{:resource_id=>'1234'}, :image_url=>"http://www.example.com/image.jpg"})
|
66
|
+
|
67
|
+
stub_request(:post, "api.sharedworkforce.com/tasks")
|
68
|
+
task_request.create
|
69
|
+
a_request(:post, "http://api.sharedworkforce.com/tasks").with(:body=>{'task'=>
|
70
|
+
{
|
71
|
+
'name'=>"Approve photo",
|
72
|
+
'directions'=>"Please classify this photo by choosing the appropriate tickboxes.",
|
73
|
+
'image_url'=>"http://www.example.com/image.jpg",
|
74
|
+
'answer_options'=> ['Obscenity', 'Nudity', 'Blurry', 'Upside down or sideways', 'Contains more than one person in the foreground', 'Has people in the background', 'Contains children'],
|
75
|
+
'responses_required'=>3,
|
76
|
+
'answer_type'=>'tags',
|
77
|
+
'callback_url'=>"#{SharedWorkforce.configuration.callback_host}/hci_task_result",
|
78
|
+
'callback_params'=>{'resource_id'=>'1234'},
|
79
|
+
'replace'=>false,
|
80
|
+
}, :api_key=>'test-api-key'}).should have_been_made.once
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "#cancel" do
|
85
|
+
it "should invoke a task cancellation" do
|
86
|
+
|
87
|
+
task = SharedWorkforce::Task.define "Approve photo" do |h|
|
88
|
+
|
89
|
+
h.directions = "Please classify this photo by choosing the appropriate tickboxes."
|
90
|
+
h.image_url = "http://www.google.com/logo.png"
|
91
|
+
h.answer_type = "tags"
|
92
|
+
h.answer_options = ['Obscenity', 'Nudity', 'Blurry', 'Upside down or sideways', 'Contains more than one person in the foreground', 'Has people in the background', 'Contains children']
|
93
|
+
h.responses_required = 3
|
94
|
+
h.replace = false
|
95
|
+
|
96
|
+
h.on_completion do |result|
|
97
|
+
puts "Complete"
|
98
|
+
end
|
99
|
+
|
100
|
+
h.on_failure do |result|
|
101
|
+
puts "Failed"
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
task_request = SharedWorkforce::TaskRequest::Http.new(task, :callback_params=>{:resource_id=>'1234'})
|
107
|
+
|
108
|
+
stub_request(:post, "api.sharedworkforce.com/tasks/cancel")
|
109
|
+
task_request.cancel
|
110
|
+
a_request(:post, "http://api.sharedworkforce.com/tasks/cancel").with(:body=>{'task'=>
|
111
|
+
{
|
112
|
+
'name'=>"Approve photo",
|
113
|
+
'directions'=>"Please classify this photo by choosing the appropriate tickboxes.",
|
114
|
+
'image_url'=>"http://www.google.com/logo.png",
|
115
|
+
'answer_options'=> ['Obscenity', 'Nudity', 'Blurry', 'Upside down or sideways', 'Contains more than one person in the foreground', 'Has people in the background', 'Contains children'],
|
116
|
+
'responses_required'=>3,
|
117
|
+
'answer_type'=>'tags',
|
118
|
+
'callback_url'=>"#{SharedWorkforce.configuration.callback_host}/hci_task_result",
|
119
|
+
'callback_params'=>{'resource_id'=>'1234'},
|
120
|
+
'replace'=>false
|
121
|
+
}, :api_key=>'test-api-key'}).should have_been_made.once
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
data/spec/task_response_spec.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
|
3
3
|
describe "TaskResponse" do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
describe ".create_collection_from_array" do
|
5
|
+
it "should create responses from an array" do
|
6
|
+
r = SharedWorkforce::TaskResponse.create_collection_from_array([{'answer'=>'answer 1'}, {'answer'=>'answer 2'}])
|
7
|
+
r[0].answer.should == "answer 1"
|
8
|
+
r[1].answer.should == "answer 2"
|
9
|
+
end
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
it "should accept and store a username" do
|
12
|
+
r = SharedWorkforce::TaskResponse.create_collection_from_array([{'answer'=>'answer 1', 'username'=>'bilbo'}, {'answer'=>'answer 2', 'username'=>'frodo'}])
|
13
|
+
r[0].username.should == "bilbo"
|
14
|
+
r[1].username.should == "frodo"
|
15
|
+
end
|
14
16
|
end
|
15
17
|
end
|
data/spec/task_result_spec.rb
CHANGED
@@ -1,40 +1,44 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
|
3
3
|
describe "TaskResult" do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
resources = {}
|
8
|
-
resources['1'] = OpenStruct.new(:approved=>false)
|
9
|
-
resources['2'] = OpenStruct.new(:approved=>false)
|
10
|
-
resources['3'] = OpenStruct.new(:approved=>false)
|
11
|
-
|
12
|
-
task = SharedWorkforce::Task.define("Approve photo") do |h|
|
4
|
+
describe "#process!" do
|
5
|
+
it "should invoke a tasks complete callback" do
|
13
6
|
|
14
|
-
|
15
|
-
|
16
|
-
|
7
|
+
resources = {}
|
8
|
+
resources['1'] = OpenStruct.new(:approved=>false)
|
9
|
+
resources['2'] = OpenStruct.new(:approved=>false)
|
10
|
+
resources['3'] = OpenStruct.new(:approved=>false)
|
11
|
+
|
12
|
+
task = SharedWorkforce::Task.define("Approve photo") do |h|
|
13
|
+
|
14
|
+
h.on_completion do |result|
|
15
|
+
if result.responses.first.answer == 'yes'
|
16
|
+
resources[result.callback_params['resource_id']].approved = true
|
17
|
+
end
|
17
18
|
end
|
19
|
+
|
18
20
|
end
|
19
21
|
|
22
|
+
SharedWorkforce::TaskResult.new({'callback_params'=>{'resource_id' => '2'}, 'responses'=>[{'answer'=>'yes'}], 'name'=>"Approve photo"}).process!
|
23
|
+
|
24
|
+
resources['1'].approved.should == false
|
25
|
+
resources['2'].approved.should == true
|
26
|
+
resources['3'].approved.should == false
|
27
|
+
|
20
28
|
end
|
21
|
-
|
22
|
-
SharedWorkforce::TaskResult.new({'callback_params'=>{'resource_id' => '2'}, 'responses'=>[{'answer'=>'yes'}], 'name'=>"Approve photo"}).process!
|
23
|
-
|
24
|
-
resources['1'].approved.should == false
|
25
|
-
resources['2'].approved.should == true
|
26
|
-
resources['3'].approved.should == false
|
27
|
-
|
28
29
|
end
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
31
|
+
describe "#answers" do
|
32
|
+
it "should collect the answers" do
|
33
|
+
r = SharedWorkforce::TaskResult.new({'callback_params'=>{'resource_id' => '2'}, 'responses'=>[{'answer'=>'yes'}, {'answer'=>'no'}, {'answer'=>'yes'}], 'name'=>"Approve photo"})
|
34
|
+
r.answers.should == ['yes', 'no', 'yes']
|
35
|
+
end
|
33
36
|
end
|
34
37
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
+
describe "#usernames" do
|
39
|
+
it "should collect the usernames" do
|
40
|
+
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"})
|
41
|
+
r.usernames.should == ['bilbo', 'frodo', 'sam']
|
42
|
+
end
|
38
43
|
end
|
39
|
-
|
40
|
-
end
|
44
|
+
end
|
data/spec/task_spec.rb
CHANGED
@@ -91,17 +91,23 @@ describe "Task" do
|
|
91
91
|
end
|
92
92
|
|
93
93
|
it "should raise a ConfigurationError if a callback host is not set" do
|
94
|
-
SharedWorkforce::
|
95
|
-
|
96
|
-
|
97
|
-
|
94
|
+
SharedWorkforce::Task.define("Approve photo") {}
|
95
|
+
with_configuration do |config|
|
96
|
+
config.callback_host = nil
|
97
|
+
lambda {
|
98
|
+
SharedWorkforce::Task.request("Approve photo", {})
|
99
|
+
}.should raise_error SharedWorkforce::ConfigurationError
|
100
|
+
end
|
98
101
|
end
|
99
102
|
|
100
103
|
it "should raise a ConfigurationError if an API key is not set" do
|
101
|
-
SharedWorkforce::
|
102
|
-
|
103
|
-
|
104
|
-
|
104
|
+
SharedWorkforce::Task.define("Approve photo") {}
|
105
|
+
with_configuration do |config|
|
106
|
+
config.api_key = nil
|
107
|
+
lambda {
|
108
|
+
SharedWorkforce::Task.request("Approve photo", {})
|
109
|
+
}.should raise_error SharedWorkforce::ConfigurationError
|
110
|
+
end
|
105
111
|
end
|
106
112
|
end
|
107
113
|
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: shared_workforce
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.1.
|
5
|
+
version: 0.1.1
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Sam Oliver
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-
|
13
|
+
date: 2011-05-10 00:00:00 +01:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -69,15 +69,18 @@ extra_rdoc_files: []
|
|
69
69
|
files:
|
70
70
|
- .gitignore
|
71
71
|
- Gemfile
|
72
|
-
- README.
|
72
|
+
- README.markdown
|
73
73
|
- Rakefile
|
74
74
|
- lib/shared_workforce.rb
|
75
75
|
- lib/shared_workforce/client.rb
|
76
|
+
- lib/shared_workforce/configuration.rb
|
76
77
|
- lib/shared_workforce/end_point.rb
|
77
78
|
- lib/shared_workforce/exceptions.rb
|
78
79
|
- lib/shared_workforce/frameworks/rails.rb
|
79
80
|
- lib/shared_workforce/task.rb
|
80
|
-
- lib/shared_workforce/task_request.rb
|
81
|
+
- lib/shared_workforce/task_request/black_hole.rb
|
82
|
+
- lib/shared_workforce/task_request/http.rb
|
83
|
+
- lib/shared_workforce/task_request/task_request.rb
|
81
84
|
- lib/shared_workforce/task_response.rb
|
82
85
|
- lib/shared_workforce/task_result.rb
|
83
86
|
- lib/shared_workforce/version.rb
|
@@ -85,7 +88,9 @@ files:
|
|
85
88
|
- spec/.rspec
|
86
89
|
- spec/client_spec.rb
|
87
90
|
- spec/spec_helper.rb
|
88
|
-
- spec/
|
91
|
+
- spec/support/configuration_helper.rb
|
92
|
+
- spec/task_request/black_hole_spec.rb
|
93
|
+
- spec/task_request/http_spec.rb
|
89
94
|
- spec/task_response_spec.rb
|
90
95
|
- spec/task_result_spec.rb
|
91
96
|
- spec/task_spec.rb
|
@@ -121,7 +126,9 @@ summary: Shared Workforce Client
|
|
121
126
|
test_files:
|
122
127
|
- spec/client_spec.rb
|
123
128
|
- spec/spec_helper.rb
|
124
|
-
- spec/
|
129
|
+
- spec/support/configuration_helper.rb
|
130
|
+
- spec/task_request/black_hole_spec.rb
|
131
|
+
- spec/task_request/http_spec.rb
|
125
132
|
- spec/task_response_spec.rb
|
126
133
|
- spec/task_result_spec.rb
|
127
134
|
- spec/task_spec.rb
|
data/README.rdoc
DELETED
File without changes
|
@@ -1,27 +0,0 @@
|
|
1
|
-
require 'rest_client'
|
2
|
-
require 'json'
|
3
|
-
module SharedWorkforce
|
4
|
-
class TaskRequest
|
5
|
-
|
6
|
-
def initialize(task, params)
|
7
|
-
@task = task
|
8
|
-
@params = params
|
9
|
-
@http_end_point = Client.http_end_point
|
10
|
-
end
|
11
|
-
|
12
|
-
def create
|
13
|
-
RestClient.post("#{@http_end_point}/tasks", *request_params)
|
14
|
-
end
|
15
|
-
|
16
|
-
def cancel
|
17
|
-
RestClient.post("#{@http_end_point}/tasks/cancel", *request_params)
|
18
|
-
end
|
19
|
-
|
20
|
-
private
|
21
|
-
|
22
|
-
def request_params
|
23
|
-
[{:task=>@task.to_hash.merge(@params).merge(:callback_url=>Client.callback_url), :api_key=>Client.api_key}.to_json, {:content_type => :json, :accept => :json}]
|
24
|
-
end
|
25
|
-
|
26
|
-
end
|
27
|
-
end
|
data/spec/task_request_spec.rb
DELETED
@@ -1,126 +0,0 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
-
|
3
|
-
describe "TaskRequest" do
|
4
|
-
|
5
|
-
it "should invoke a task request" do
|
6
|
-
|
7
|
-
task = SharedWorkforce::Task.define "Approve photo" do |h|
|
8
|
-
|
9
|
-
h.directions = "Please classify this photo by choosing the appropriate tickboxes."
|
10
|
-
h.image_url = "http://www.google.com/logo.png"
|
11
|
-
h.answer_type = "tags"
|
12
|
-
h.answer_options = ['Obscenity', 'Nudity', 'Blurry', 'Upside down or sideways', 'Contains more than one person in the foreground', 'Has people in the background', 'Contains children']
|
13
|
-
h.responses_required = 3
|
14
|
-
h.replace = false
|
15
|
-
|
16
|
-
h.on_completion do |result|
|
17
|
-
puts "Complete"
|
18
|
-
end
|
19
|
-
|
20
|
-
h.on_failure do |result|
|
21
|
-
puts "Failed"
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
task_request = SharedWorkforce::TaskRequest.new(task, :callback_params=>{:resource_id=>'1234'})
|
27
|
-
|
28
|
-
stub_request(:post, "hci.heroku.com/tasks")
|
29
|
-
task_request.create
|
30
|
-
a_request(:post, "http://hci.heroku.com/tasks").with(:body=>{'task'=>
|
31
|
-
{
|
32
|
-
'name'=>"Approve photo",
|
33
|
-
'directions'=>"Please classify this photo by choosing the appropriate tickboxes.",
|
34
|
-
'image_url'=>"http://www.google.com/logo.png",
|
35
|
-
'answer_options'=> ['Obscenity', 'Nudity', 'Blurry', 'Upside down or sideways', 'Contains more than one person in the foreground', 'Has people in the background', 'Contains children'],
|
36
|
-
'responses_required'=>3,
|
37
|
-
'answer_type'=>'tags',
|
38
|
-
'callback_url'=>"#{SharedWorkforce::Client.callback_host}/hci_task_result",
|
39
|
-
'callback_params'=>{'resource_id'=>'1234'},
|
40
|
-
'replace'=>false
|
41
|
-
}, :api_key=>'test-api-key'}).should have_been_made.once
|
42
|
-
|
43
|
-
end
|
44
|
-
|
45
|
-
|
46
|
-
it "should invoke a task cancellation" do
|
47
|
-
|
48
|
-
task = SharedWorkforce::Task.define "Approve photo" do |h|
|
49
|
-
|
50
|
-
h.directions = "Please classify this photo by choosing the appropriate tickboxes."
|
51
|
-
h.image_url = "http://www.google.com/logo.png"
|
52
|
-
h.answer_type = "tags"
|
53
|
-
h.answer_options = ['Obscenity', 'Nudity', 'Blurry', 'Upside down or sideways', 'Contains more than one person in the foreground', 'Has people in the background', 'Contains children']
|
54
|
-
h.responses_required = 3
|
55
|
-
h.replace = false
|
56
|
-
|
57
|
-
h.on_completion do |result|
|
58
|
-
puts "Complete"
|
59
|
-
end
|
60
|
-
|
61
|
-
h.on_failure do |result|
|
62
|
-
puts "Failed"
|
63
|
-
end
|
64
|
-
|
65
|
-
end
|
66
|
-
|
67
|
-
task_request = SharedWorkforce::TaskRequest.new(task, :callback_params=>{:resource_id=>'1234'})
|
68
|
-
|
69
|
-
stub_request(:post, "hci.heroku.com/tasks/cancel")
|
70
|
-
task_request.cancel
|
71
|
-
a_request(:post, "http://hci.heroku.com/tasks/cancel").with(:body=>{'task'=>
|
72
|
-
{
|
73
|
-
'name'=>"Approve photo",
|
74
|
-
'directions'=>"Please classify this photo by choosing the appropriate tickboxes.",
|
75
|
-
'image_url'=>"http://www.google.com/logo.png",
|
76
|
-
'answer_options'=> ['Obscenity', 'Nudity', 'Blurry', 'Upside down or sideways', 'Contains more than one person in the foreground', 'Has people in the background', 'Contains children'],
|
77
|
-
'responses_required'=>3,
|
78
|
-
'answer_type'=>'tags',
|
79
|
-
'callback_url'=>"#{SharedWorkforce::Client.callback_host}/hci_task_result",
|
80
|
-
'callback_params'=>{'resource_id'=>'1234'},
|
81
|
-
'replace'=>false
|
82
|
-
}, :api_key=>'test-api-key'}).should have_been_made.once
|
83
|
-
|
84
|
-
end
|
85
|
-
|
86
|
-
it "should allow options to be overridden when making the request" do
|
87
|
-
|
88
|
-
task = SharedWorkforce::Task.define "Approve photo" do |h|
|
89
|
-
|
90
|
-
h.directions = "Please classify this photo by choosing the appropriate tickboxes."
|
91
|
-
h.image_url = "http://www.google.com/logo.png"
|
92
|
-
h.answer_type = "tags"
|
93
|
-
h.answer_options = ['Obscenity', 'Nudity', 'Blurry', 'Upside down or sideways', 'Contains more than one person in the foreground', 'Has people in the background', 'Contains children']
|
94
|
-
h.responses_required = 3
|
95
|
-
|
96
|
-
h.on_completion do |result|
|
97
|
-
puts "Complete"
|
98
|
-
end
|
99
|
-
|
100
|
-
h.on_failure do |result|
|
101
|
-
puts "Failed"
|
102
|
-
end
|
103
|
-
|
104
|
-
end
|
105
|
-
|
106
|
-
task_request = SharedWorkforce::TaskRequest.new(task, {:callback_params=>{:resource_id=>'1234'}, :image_url=>"http://www.example.com/image.jpg"})
|
107
|
-
|
108
|
-
stub_request(:post, "hci.heroku.com/tasks")
|
109
|
-
task_request.create
|
110
|
-
a_request(:post, "http://hci.heroku.com/tasks").with(:body=>{'task'=>
|
111
|
-
{
|
112
|
-
'name'=>"Approve photo",
|
113
|
-
'directions'=>"Please classify this photo by choosing the appropriate tickboxes.",
|
114
|
-
'image_url'=>"http://www.example.com/image.jpg",
|
115
|
-
'answer_options'=> ['Obscenity', 'Nudity', 'Blurry', 'Upside down or sideways', 'Contains more than one person in the foreground', 'Has people in the background', 'Contains children'],
|
116
|
-
'responses_required'=>3,
|
117
|
-
'answer_type'=>'tags',
|
118
|
-
'callback_url'=>"#{SharedWorkforce::Client.callback_host}/hci_task_result",
|
119
|
-
'callback_params'=>{'resource_id'=>'1234'},
|
120
|
-
'replace'=>false,
|
121
|
-
}, :api_key=>'test-api-key'}).should have_been_made.once
|
122
|
-
end
|
123
|
-
|
124
|
-
end
|
125
|
-
|
126
|
-
|