shared_workforce 0.2.12 → 0.2.13
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +117 -70
- data/lib/shared_workforce.rb +1 -0
- data/lib/shared_workforce/configuration.rb +4 -0
- data/lib/shared_workforce/frameworks/rails.rb +10 -4
- data/lib/shared_workforce/version.rb +1 -1
- data/spec/configuration_spec.rb +7 -0
- metadata +12 -12
data/README.markdown
CHANGED
@@ -27,121 +27,168 @@ Register for a beta invitation at [sharedworkforce.com](http://www.sharedworkfor
|
|
27
27
|
|
28
28
|
Add shared_workforce to your Gemfile:
|
29
29
|
|
30
|
-
|
30
|
+
```ruby
|
31
|
+
gem "shared_workforce"
|
32
|
+
```
|
31
33
|
|
32
34
|
Create config/initializers/shared_workforce.rb
|
33
35
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
36
|
+
```ruby
|
37
|
+
SharedWorkforce.configure do |config|
|
38
|
+
config.api_key = "your-api-key"
|
39
|
+
config.callback_host = "http://your-website-host"
|
40
|
+
end
|
41
|
+
```
|
38
42
|
|
39
43
|
If you're not using Rails, simply require the gem or include it in your Gemfile, set the client configuration settings as above.
|
40
44
|
|
41
45
|
### Step 3 - define tasks
|
42
46
|
|
43
|
-
|
44
|
-
|
45
|
-
class ApprovePhotoTask
|
46
|
-
include SharedWorkforce::Task
|
47
|
-
|
48
|
-
title 'Approve Photo'
|
47
|
+
A class defines the content (for example, an image url) and instructions that the human worker will see when they work on your task, and the methods to be run once the task has been completed.
|
49
48
|
|
50
|
-
|
51
|
-
responses_required 1
|
49
|
+
If, for example, you would like to approve a photo on upload, create a task class file in an `app/tasks` directory (or anywhere in the load path).
|
52
50
|
|
53
|
-
answer_options ['Offensive', 'Contains Nudity', 'Blurry or low quality', 'Upside down or sideways']
|
54
|
-
answer_type :tags
|
55
|
-
image_url "http://www.google.com/logo.png"
|
56
51
|
|
57
|
-
|
52
|
+
`app/tasks/tag_photo_task.rb`:
|
58
53
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
54
|
+
```ruby
|
55
|
+
class TagPhotoTask
|
56
|
+
include SharedWorkforce::Task
|
57
|
+
|
58
|
+
title 'Please tag our photo'
|
59
|
+
|
60
|
+
instruction 'Please look at the photo and tick all that apply.'
|
61
|
+
|
62
|
+
answer_type :tags
|
63
|
+
answer_options ['Offensive', 'Contains Nudity', 'Blurry or low quality', 'Upside down or sideways']
|
64
|
+
|
65
|
+
responses_required 1
|
66
|
+
|
67
|
+
on_success :moderate_photo
|
68
|
+
|
69
|
+
def setup(photo)
|
70
|
+
self.image_url = photo.url
|
71
|
+
end
|
72
|
+
|
73
|
+
def moderate_photo(photo, responses)
|
74
|
+
# responses => [{:answer=>['Offensive', 'Contains Nudity']}]
|
75
|
+
if responses.map {|r| r[:answer]}.flatten.include?('Offensive')
|
76
|
+
photo.delete
|
77
|
+
photo.user.quarantine("Uploded offensive photo")
|
67
78
|
end
|
68
|
-
|
79
|
+
end
|
80
|
+
end
|
81
|
+
```
|
82
|
+
_**Note:** the task definition includes a `setup` method which is called automatically whenever the task is initialized. In the example, the task's `image_url` (the image shown to the worker) is set from the photo model's url attribute. Any of the task's attributes can be set this way._
|
83
|
+
|
84
|
+
Once you have created a task definition, you can request real human responses for a model instance by calling its `create` method. This can be done in an `after_create` callback in one of your Active Record models. This will be covered in more detail in the next section.
|
69
85
|
|
70
86
|
### Step 4 - request tasks
|
71
87
|
|
72
|
-
|
88
|
+
When you publish a task it will be queued for a human worker to complete. You can publish a task in an `after_create` callback on a typical Active Record model:
|
73
89
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
90
|
+
```ruby
|
91
|
+
class Photo < ActiveRecord::Base
|
92
|
+
|
93
|
+
after_create :request_tags
|
94
|
+
|
95
|
+
def request_tags
|
96
|
+
TagPhotoTask.create(self)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
```
|
100
|
+
|
101
|
+
_**Note:** In the example, the photo model instance (self) is used an argument to the TagPhotoTask.create method. This argument will be available in the setup method and the callback method as shown in the example of a Task Definition in step 3._
|
102
|
+
|
103
|
+
When the response(s) from the human workers are collected, the method specified in the `on_success` attribute in your task definition will be called. Typically this will take about 15 minutes. You can check the [Shared Workforce web site](http://www.sharedworkforce.com) for an up to date status on the current response time.
|
82
104
|
|
105
|
+
### Unit testing
|
106
|
+
|
107
|
+
You can test your task definition by calling its methods directly.
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
it "should quarantine the user" do
|
111
|
+
photo = Factory(:photo)
|
112
|
+
task = ClassifyPhotoTask.new(photo)
|
113
|
+
task.moderate_photo(photo, [{:answer=>['Offensive']}])
|
114
|
+
photo.user.should be_quarantined
|
115
|
+
end
|
116
|
+
```
|
83
117
|
|
84
118
|
Advanced definition options
|
85
119
|
----------------------------------------
|
86
120
|
|
87
121
|
### Task types
|
88
122
|
|
89
|
-
SharedWorkforce currently supports
|
123
|
+
SharedWorkforce currently supports 5 types of task. Possible options are:
|
124
|
+
|
125
|
+
* `"choice"`: choose one option from a list
|
126
|
+
* `"tags"`: choose any number of options from a list
|
127
|
+
* `"edit"`: edit the text in the 'text' attribute
|
128
|
+
* `"crop"`: crop the photo (see image_crop_ratio)
|
129
|
+
* `"rotate"`: rotate the photo
|
90
130
|
|
91
|
-
###Multiple responses
|
131
|
+
### Multiple responses
|
92
132
|
|
93
133
|
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.
|
94
134
|
|
95
|
-
|
96
|
-
|
135
|
+
```ruby
|
136
|
+
class ApprovePhotoTask
|
137
|
+
include SharedWorkforce::Task
|
97
138
|
|
98
|
-
|
139
|
+
title 'Approve Photo'
|
99
140
|
|
100
|
-
|
101
|
-
|
141
|
+
instruction 'Look at this photo. Please tick all that apply.'
|
142
|
+
responses_required 3
|
102
143
|
|
103
|
-
|
104
|
-
|
144
|
+
answer_options ['Offensive', 'Contains Nudity', 'Blurry or low quality', 'Upside down or sideways']
|
145
|
+
answer_type :tags
|
105
146
|
|
106
|
-
|
147
|
+
on_complete :moderate_photo
|
107
148
|
|
108
|
-
|
109
|
-
|
110
|
-
|
149
|
+
def moderate_photo(photo, responses)
|
150
|
+
photo.hide! if responses.map { |r| r[:answer] }.all? { |a| a.include?('Contains Nudity') }
|
151
|
+
end
|
111
152
|
|
112
|
-
|
113
|
-
|
153
|
+
end
|
154
|
+
```
|
114
155
|
|
115
|
-
###Replacing tasks
|
156
|
+
### Replacing tasks
|
116
157
|
|
117
158
|
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.
|
118
159
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
160
|
+
```ruby
|
161
|
+
class ApprovePhotoTask
|
162
|
+
include SharedWorkforce::Task
|
163
|
+
...
|
164
|
+
replace true
|
165
|
+
...
|
166
|
+
end
|
167
|
+
```
|
125
168
|
|
126
|
-
###Cancelling tasks
|
169
|
+
### Cancelling tasks
|
127
170
|
|
128
171
|
You can cancel tasks when they are no longer relevant.
|
129
|
-
|
130
|
-
class Photo
|
131
|
-
after_destroy :cancel_tagging_request
|
132
172
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
end
|
173
|
+
```ruby
|
174
|
+
class Photo
|
175
|
+
after_destroy :cancel_tagging_request
|
137
176
|
|
138
|
-
|
177
|
+
def cancel_tagging_request
|
178
|
+
ApprovePhotoTask.cancel(self)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
```
|
139
182
|
|
140
|
-
|
183
|
+
### Disabling requests during development
|
141
184
|
|
142
|
-
|
143
|
-
|
144
|
-
|
185
|
+
You can black-hole requests to Shared Workforce for testing and development by adding the following configuration option in your initializer:
|
186
|
+
|
187
|
+
```ruby
|
188
|
+
SharedWorkforce.configure do |config|
|
189
|
+
config.request_class = SharedWorkforce::TaskRequest::BlackHole
|
190
|
+
end
|
191
|
+
```
|
145
192
|
|
146
193
|
License
|
147
194
|
-----------
|
data/lib/shared_workforce.rb
CHANGED
@@ -14,6 +14,7 @@ require 'shared_workforce/response_poller'
|
|
14
14
|
require 'shared_workforce/frameworks/rails' if defined?(Rails)
|
15
15
|
require 'active_support/inflector'
|
16
16
|
require 'active_support/core_ext/hash/indifferent_access'
|
17
|
+
require 'active_support/core_ext/object/blank'
|
17
18
|
|
18
19
|
module SharedWorkforce
|
19
20
|
|
@@ -2,10 +2,16 @@ if defined?(ActionController::Metal)
|
|
2
2
|
class Railtie < Rails::Railtie
|
3
3
|
initializer 'shared_workforce' do |app|
|
4
4
|
app.config.middleware.use SharedWorkforce::EndPoint
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
app.config.after_initialize do
|
6
|
+
if Rails.env.development?
|
7
|
+
if SharedWorkforce.configuration.valid?
|
8
|
+
# Stop log buffering when using Foreman in development
|
9
|
+
$stdout.sync = true
|
10
|
+
SharedWorkforce::ResponsePoller.start
|
11
|
+
else
|
12
|
+
puts 'Shared Workforce: API key not configured.'
|
13
|
+
end
|
14
|
+
end
|
9
15
|
end
|
10
16
|
end
|
11
17
|
end
|
data/spec/configuration_spec.rb
CHANGED
@@ -69,5 +69,12 @@ describe "Configuration" do
|
|
69
69
|
configuration.logger.should == logger_double
|
70
70
|
end
|
71
71
|
end
|
72
|
+
|
73
|
+
describe "#valid" do
|
74
|
+
it "should be false if no api key is set" do
|
75
|
+
configuration.api_key = nil
|
76
|
+
configuration.should_not be_valid
|
77
|
+
end
|
78
|
+
end
|
72
79
|
|
73
80
|
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.13
|
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-08-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rest-client
|
16
|
-
requirement: &
|
16
|
+
requirement: &70323744943380 !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: *70323744943380
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: json
|
27
|
-
requirement: &
|
27
|
+
requirement: &70323744942960 !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: *70323744942960
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: activesupport
|
38
|
-
requirement: &
|
38
|
+
requirement: &70323744942540 !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: *70323744942540
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rspec
|
49
|
-
requirement: &
|
49
|
+
requirement: &70323744942040 !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: *70323744942040
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: webmock
|
60
|
-
requirement: &
|
60
|
+
requirement: &70323744941620 !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: *70323744941620
|
69
69
|
description: Shared Workforce is a service and simple API for human intelligence tasks
|
70
70
|
email:
|
71
71
|
- sam@samoliver.com
|