turkee 1.2.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/.document +5 -0
  2. data/.gitignore +27 -0
  3. data/Gemfile.lock +124 -0
  4. data/Guardfile +12 -0
  5. data/README.rdoc +53 -21
  6. data/VERSION +1 -0
  7. data/lib/generators/turkee/templates/create_turkee_study.rb.erb +18 -0
  8. data/lib/generators/turkee/turkee_generator.rb +4 -0
  9. data/lib/helpers/turkee_forms_helper.rb +68 -0
  10. data/lib/models/turkee_imported_assignment.rb +15 -0
  11. data/lib/models/turkee_study.rb +11 -0
  12. data/lib/models/turkee_task.rb +257 -0
  13. data/lib/tasks/turkee.rb +13 -1
  14. data/lib/turkee.rb +4 -317
  15. data/spec/dummy/Rakefile +7 -0
  16. data/spec/dummy/app/assets/javascripts/application.js +5 -0
  17. data/spec/dummy/app/assets/stylesheets/application.css +7 -0
  18. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  19. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  20. data/spec/dummy/app/mailers/.gitkeep +0 -0
  21. data/spec/dummy/app/models/.gitkeep +0 -0
  22. data/spec/dummy/app/views/layouts/application.html.erb +19 -0
  23. data/spec/dummy/config.ru +4 -0
  24. data/spec/dummy/config/application.rb +41 -0
  25. data/spec/dummy/config/boot.rb +10 -0
  26. data/spec/dummy/config/database.yml +25 -0
  27. data/spec/dummy/config/environment.rb +5 -0
  28. data/spec/dummy/config/environments/development.rb +27 -0
  29. data/spec/dummy/config/environments/production.rb +54 -0
  30. data/spec/dummy/config/environments/test.rb +39 -0
  31. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  32. data/spec/dummy/config/initializers/inflections.rb +10 -0
  33. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  34. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  35. data/spec/dummy/config/initializers/session_store.rb +8 -0
  36. data/spec/dummy/config/initializers/wrap_parameters.rb +12 -0
  37. data/spec/dummy/config/locales/en.yml +5 -0
  38. data/spec/dummy/config/routes.rb +62 -0
  39. data/spec/dummy/db/migrate/20110710143903_initial_tables.rb +23 -0
  40. data/spec/dummy/db/migrate/20120819164528_add_association_with_class_name.rb +12 -0
  41. data/spec/dummy/db/migrate/20130203095901_create_company.rb +14 -0
  42. data/spec/dummy/db/schema.rb +45 -0
  43. data/spec/dummy/public/404.html +26 -0
  44. data/spec/dummy/public/422.html +26 -0
  45. data/spec/dummy/public/500.html +26 -0
  46. data/spec/dummy/public/favicon.ico +0 -0
  47. data/spec/dummy/script/rails +6 -0
  48. data/spec/dummy/tmp/cache/.gitkeep +0 -0
  49. data/spec/factories/turkee_task_factory.rb +21 -0
  50. data/spec/helpers/turkee_forms_helper_spec.rb +44 -0
  51. data/spec/models/turkee_study_spec.rb +19 -0
  52. data/spec/{turkee_spec.rb → models/turkee_task_spec.rb} +0 -0
  53. data/spec/spec_helper.rb +21 -6
  54. data/turkee.gemspec +59 -0
  55. metadata +69 -21
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,27 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ *.gem
5
+ build.sh
6
+
7
+ ## TEXTMATE
8
+ *.tmproj
9
+ tmtags
10
+ *.log
11
+
12
+ ## EMACS
13
+ *~
14
+ \#*
15
+ .\#*
16
+
17
+ ## VIM
18
+ *.swp
19
+
20
+ ## PROJECT::GENERAL
21
+ coverage
22
+ rdoc
23
+ pkg
24
+
25
+ .idea
26
+
27
+ ## PROJECT::SPECIFIC
data/Gemfile.lock ADDED
@@ -0,0 +1,124 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ turkee (2.0.0)
5
+ lockfile
6
+ rails (>= 3.1.1)
7
+ rturk (>= 2.4.0)
8
+
9
+ GEM
10
+ remote: http://rubygems.org/
11
+ specs:
12
+ actionmailer (3.2.3)
13
+ actionpack (= 3.2.3)
14
+ mail (~> 2.4.4)
15
+ actionpack (3.2.3)
16
+ activemodel (= 3.2.3)
17
+ activesupport (= 3.2.3)
18
+ builder (~> 3.0.0)
19
+ erubis (~> 2.7.0)
20
+ journey (~> 1.0.1)
21
+ rack (~> 1.4.0)
22
+ rack-cache (~> 1.2)
23
+ rack-test (~> 0.6.1)
24
+ sprockets (~> 2.1.2)
25
+ activemodel (3.2.3)
26
+ activesupport (= 3.2.3)
27
+ builder (~> 3.0.0)
28
+ activerecord (3.2.3)
29
+ activemodel (= 3.2.3)
30
+ activesupport (= 3.2.3)
31
+ arel (~> 3.0.2)
32
+ tzinfo (~> 0.3.29)
33
+ activeresource (3.2.3)
34
+ activemodel (= 3.2.3)
35
+ activesupport (= 3.2.3)
36
+ activesupport (3.2.3)
37
+ i18n (~> 0.6)
38
+ multi_json (~> 1.0)
39
+ arel (3.0.2)
40
+ builder (3.0.4)
41
+ diff-lcs (1.2.3)
42
+ erubis (2.7.0)
43
+ factory_girl (3.2.0)
44
+ activesupport (>= 3.0.0)
45
+ hike (1.2.2)
46
+ i18n (0.6.0)
47
+ journey (1.0.4)
48
+ json (1.7.7)
49
+ lockfile (2.1.0)
50
+ mail (2.4.4)
51
+ i18n (>= 0.4.0)
52
+ mime-types (~> 1.16)
53
+ treetop (~> 1.4.8)
54
+ metaclass (0.0.1)
55
+ mime-types (1.22)
56
+ mocha (0.11.4)
57
+ metaclass (~> 0.0.1)
58
+ multi_json (1.3.4)
59
+ nokogiri (1.5.9)
60
+ polyglot (0.3.3)
61
+ rack (1.4.5)
62
+ rack-cache (1.2)
63
+ rack (>= 0.4)
64
+ rack-ssl (1.3.3)
65
+ rack
66
+ rack-test (0.6.2)
67
+ rack (>= 1.0)
68
+ rails (3.2.3)
69
+ actionmailer (= 3.2.3)
70
+ actionpack (= 3.2.3)
71
+ activerecord (= 3.2.3)
72
+ activeresource (= 3.2.3)
73
+ activesupport (= 3.2.3)
74
+ bundler (~> 1.0)
75
+ railties (= 3.2.3)
76
+ railties (3.2.3)
77
+ actionpack (= 3.2.3)
78
+ activesupport (= 3.2.3)
79
+ rack-ssl (~> 1.3.2)
80
+ rake (>= 0.8.7)
81
+ rdoc (~> 3.4)
82
+ thor (~> 0.14.6)
83
+ rake (10.0.4)
84
+ rdoc (3.12.2)
85
+ json (~> 1.4)
86
+ rest-client (1.6.7)
87
+ mime-types (>= 1.16)
88
+ rspec-core (2.13.1)
89
+ rspec-expectations (2.13.0)
90
+ diff-lcs (>= 1.1.3, < 2.0)
91
+ rspec-mocks (2.13.1)
92
+ rspec-rails (2.13.0)
93
+ actionpack (>= 3.0)
94
+ activesupport (>= 3.0)
95
+ railties (>= 3.0)
96
+ rspec-core (~> 2.13.0)
97
+ rspec-expectations (~> 2.13.0)
98
+ rspec-mocks (~> 2.13.0)
99
+ rturk (2.10.1)
100
+ nokogiri (>= 1.4.1)
101
+ rest-client (>= 1.4.0)
102
+ spork (0.9.2)
103
+ sprockets (2.1.3)
104
+ hike (~> 1.2)
105
+ rack (~> 1.0)
106
+ tilt (~> 1.1, != 1.3.0)
107
+ sqlite3 (1.3.6)
108
+ thor (0.14.6)
109
+ tilt (1.3.7)
110
+ treetop (1.4.12)
111
+ polyglot
112
+ polyglot (>= 0.3.1)
113
+ tzinfo (0.3.37)
114
+
115
+ PLATFORMS
116
+ ruby
117
+
118
+ DEPENDENCIES
119
+ factory_girl (>= 1.3.2)
120
+ mocha
121
+ rspec-rails (~> 2.6)
122
+ spork
123
+ sqlite3
124
+ turkee!
data/Guardfile ADDED
@@ -0,0 +1,12 @@
1
+ guard 'spork', :rspec_env => { 'RAILS_ENV' => 'test' } do
2
+ watch('spec/spec_helper.rb')
3
+ end
4
+
5
+
6
+ guard 'rspec', :version => 2, :cli => "--drb" do
7
+ watch(%r{^spec/.+_spec\.rb$})
8
+
9
+ # lib/
10
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
11
+ end
12
+
data/README.rdoc CHANGED
@@ -1,18 +1,21 @@
1
- == Turkee Description
1
+ == WHAT TURKEE CAN DO
2
2
 
3
- Seamlessly convert your Rails forms for use on Mechanical Turk. Then, easily import the data posted by the Mechanical Turk workers back into your data models.
3
+ * Perform user feedback studies easily.
4
+ http://i.imgur.com/wIU40BAl.png
5
+
6
+ * Seamlessly convert your Rails forms for use on Mechanical Turk.
7
+ * Easily import the data posted by the Mechanical Turk workers back into your data models.
4
8
 
5
9
  External forms are created using a simple form helper. HITs are created by issuing a rake command. Retrieving submitted response data and importing that data into your model(s) requires just one more rake command.
6
10
 
7
11
 
8
- == Install/Upgrade
12
+ == INSTALL/UPGRADE
9
13
 
10
14
  Add turkee to your Gemfile as a gem dependency, then do a 'bundle install':
11
15
 
12
16
  gem 'turkee'
13
17
 
14
18
 
15
-
16
19
  If you're upgrading Turkee (1.1.1 and prior) or installing for the first time, run:
17
20
 
18
21
  rails g turkee --skip
@@ -29,9 +32,9 @@ If you haven't created a Mechanical Turk account, surf on over to {Amazon's Web
29
32
  Once you have your account created, you can access your AWS access key and secret access key from {here.}[https://aws-portal.amazon.com/gp/aws/developer/account/index.html?action=access-key]
30
33
 
31
34
 
32
- == Configuration
35
+ == CONFIGURATION
33
36
 
34
- Inside the config/initializers directory, you'll see the file turkee.rb. Edit that file with your Amazon credenti.
37
+ Inside the config/initializers directory, you'll see the file turkee.rb. Edit the file with your Amazon credentials.
35
38
 
36
39
  AWSACCESSKEYID = 'XXXXXXXXXXXXXXXXXX'
37
40
  AWSSECRETACCESSKEY = 'YYYYYYYYYYYYYYYYYYYYYYYYYYYY'
@@ -39,14 +42,13 @@ Inside the config/initializers directory, you'll see the file turkee.rb. Edit t
39
42
  RTurk::logger.level = Logger::DEBUG
40
43
  RTurk.setup(AWSACCESSKEYID, AWSSECRETACCESSKEY, :sandbox => (Rails.env == 'production' ? false : true))
41
44
 
42
-
43
- == Use
44
-
45
- 1) Run your migrations :
45
+ Run the migrations :
46
46
 
47
47
  rake db:migrate
48
48
 
49
- 2) You should disable form controls if the Turker hasn't accepted the HIT. You can determine this from your controller:
49
+ == BASIC FORMS
50
+
51
+ 1) You should disable form controls if the Turker hasn't accepted the HIT. You can determine this from your controller:
50
52
  class SurveysController < ApplicationController
51
53
 
52
54
  def new
@@ -60,7 +62,7 @@ Inside the config/initializers directory, you'll see the file turkee.rb. Edit t
60
62
  @survey = Survey.new
61
63
  end
62
64
 
63
- 3) Change your forms to use the form helper.
65
+ 2) Change your forms to use the form helper.
64
66
 
65
67
  <%= turkee_form_for(@survey, params) do |f| %>
66
68
  <p><%= f.text_area :value, :disabled => @disabled %></p>
@@ -69,7 +71,8 @@ Inside the config/initializers directory, you'll see the file turkee.rb. Edit t
69
71
 
70
72
  Using the turkee_form_for helper will post the form to the Mechanical Turk sandbox if you're in development/test mode, otherwise it will be posted to Mechanical Turk production/live site.
71
73
 
72
- 4) Run the following rake task to post to Mechanical Turk :
74
+
75
+ 3) Run the following rake task to post to Mechanical Turk :
73
76
  # Host URL of your application
74
77
  # Title of your HIT
75
78
  # Description of your HIT
@@ -88,9 +91,28 @@ This will insert a row for the requested HIT into the turkee_tasks table. The t
88
91
 
89
92
  When a Turk worker views your HIT, the HIT will display your form within an iFrame. With the above example, Mechanical Turk will open an iFrame for the HIT assignment displaying the form http://www.yourapp.com/surveys/new
90
93
 
91
- 5) Allow some time for the Mechanical Turk workers ("Turkers") to respond to your HIT.
94
+ == USER FEEDBACK STUDIES
95
+
96
+ 1) Add the following method call to your app/views/layouts/application.html.haml file :
97
+
98
+ <%= turkee_study %>
99
+
100
+ 2) Call the create_study rake task :
101
+
102
+ rake turkee:create_study[<Application Page URL>, <HIT Title>, <HIT Description>, <Number of Assignments>, <Reward>, <Lifetime of HIT>]
103
+
104
+ # 30 Assignments at a quarter each requesting feedback on my site
105
+ RAILS_ENV=production rake turkee:create_study['https://yoursite.com/page-to-be-test',"Can you help me test my website?","Give feedback below on what you didn't like about my site. Click submit below when you're finished.", 30,0.25, 20]
92
106
 
93
- 6) Run the rake task that retrieves the values from Mechanical Turk and stores the user entered values into your model.
107
+ 3) The Turker will see a feedback form overlayed over your website. Within this textarea, they can provide feedback.
108
+
109
+ Screenshot:
110
+
111
+ http://i.imgur.com/wIU40BA.png
112
+
113
+ == RETRIEVING RESULTS
114
+
115
+ 1) Run the rake task that retrieves the values from Mechanical Turk and stores the user entered values into your model.
94
116
  rake turkee:get_all_results
95
117
 
96
118
  Rerun this task periodically to retrieve newly entered form values. You can setup this task as a cronjob to automate this.
@@ -104,7 +126,10 @@ Or you can directly call :
104
126
 
105
127
  Turkee::TurkeeTask.process_hits
106
128
 
107
- 7) When a response is retrieved from Mechanical Turk, Turkee attempts to create a data row for the model specified using the corresponding retrieved data. If the row cannot be created (input failed model validations), the assignment is rejected.
129
+
130
+ == DATA RETRIEVAL; THE INS AND OUTS
131
+
132
+ 1) When a response is retrieved from Mechanical Turk, Turkee attempts to create a data row for the model specified using the corresponding retrieved data. If the row cannot be created (input failed model validations), the assignment is rejected.
108
133
  As for Mechanical Turk approval, if the row is created and you haven't specified your own custom approve? method for the model, the assignment will automatically be approved. If you'd like to add your own custom approval method, add the approve? instance method to your model. E.g. :
109
134
  class Survey < ActiveRecord::Base
110
135
  def approve?
@@ -112,14 +137,14 @@ As for Mechanical Turk approval, if the row is created and you haven't specified
112
137
  end
113
138
  end
114
139
 
115
- 8) When all specified assignments for a HIT have been completed, Turkee will attempt to call the optional hit_complete class method for the model. This can be used for any cleanup logic. E.g. :
140
+ 2) When all specified assignments for a HIT have been completed, Turkee will attempt to call the optional hit_complete class method for the model. This can be used for any cleanup logic. E.g. :
116
141
  class Survey < ActiveRecord::Base
117
142
  def self.hit_complete(turkee_task)
118
143
  #do something
119
144
  end
120
145
  end
121
146
 
122
- 9) If all of specified assignments for a HIT have not been completed by the end of the HITs lifetime, Turkee will attempt to call the optional hit_expired class method for the model. This can be used for any cleanup logic. E.g. :
147
+ 3) If all of specified assignments for a HIT have not been completed by the end of the HITs lifetime, Turkee will attempt to call the optional hit_expired class method for the model. This can be used for any cleanup logic. E.g. :
123
148
  class Survey < ActiveRecord::Base
124
149
  def self.hit_expired(turkee_task)
125
150
  #do something
@@ -132,16 +157,23 @@ As for Mechanical Turk approval, if the row is created and you haven't specified
132
157
  1) You can use the params hash to pass object IDs to your forms. E.g. if you wanted to setup a form of questions about a given URL (let's call the model UrlSurvey), your code would look something like :
133
158
 
134
159
  URL.all do |url|
135
- Turkee::TurkeeTask.create_hit(host, hit_title, hit_description, UrlSurvey, num_assignments, reward,
160
+ Turkee::TurkeeTask.create_hit(host, "Enter the address displayed for the given url.",
161
+ hit_description, UrlSurvey, num_assignments, reward,
136
162
  lifetime, duration, {}, {:url_id => url.id}, {})
137
163
  end
138
164
 
139
165
  Then when displaying your form, you can find the URL object via the params[:url_id] value.
140
166
 
167
+ 2) You can pass in qualifications to allow only certain Turkers to work on your HIT.
141
168
 
142
- 2) Turkee assumes that the form url will be the new action for the class passed to create_hit. If you have a more complex form url which would be the case for nested resources, you can use the :form_url
143
- option to designate a form url different from the default.
169
+ E.g. qualifications = {:approval_rate => {:gt => 70}, :country => {:eql => 'US'}}
170
+
171
+ Turkee::TurkeeTask.create_hit(host, hit_title, hit_description, UrlSurvey, num_assignments, reward, lifetime, duration, qualifications, {}, {})
144
172
 
173
+ * The rake task does not support the passing of qualifications.
174
+
175
+ 3) Turkee assumes that the form url will be the new action for the class passed to create_hit. If you have a more complex form url which would be the case for nested resources, you can use the :form_url
176
+ option to designate a form url different from the default.
145
177
 
146
178
  form_url = Rails.application.routes.url_helpers.send("new_user_survey_path",@my_survey)
147
179
 
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 2.0.0
@@ -0,0 +1,18 @@
1
+ class CreateTurkeeStudy < ActiveRecord::Migration
2
+
3
+ def self.up
4
+ create_table "turkee_studies" do |t|
5
+ t.integer "turkee_task_id"
6
+ t.text "feedback"
7
+ t.string "gold_response"
8
+ t.timestamps
9
+ end
10
+
11
+ add_index :turkee_studies, :turkee_task_id
12
+ end
13
+
14
+ def self.down
15
+ drop_table :turkee_studies
16
+ end
17
+
18
+ end
@@ -38,6 +38,10 @@ class TurkeeGenerator < Rails::Generators::Base
38
38
 
39
39
  migration_template "add_expired.rb.erb", "db/migrate/add_expired.rb"
40
40
 
41
+ sleep 1
42
+
43
+ migration_template "create_turkee_study.rb.erb", "db/migrate/create_turkee_study.rb"
44
+
41
45
  end
42
46
 
43
47
  def create_initializer
@@ -0,0 +1,68 @@
1
+ module Turkee
2
+
3
+ module TurkeeFormHelper
4
+ def turkee_form_for(record, params, options = {}, &proc)
5
+ raise ArgumentError, "turkee_form_for now requires that you pass in the entire params hash, instead of just the assignmentId value. " unless params.is_a?(Hash)
6
+
7
+ options.merge!({:url => mturk_url})
8
+
9
+ buffer = ''
10
+ form_for record, options do |f|
11
+ params.each do |k,v|
12
+ unless ['action','controller'].include?(k) || !v.is_a?(String)
13
+ buffer << hidden_field_tag(k, v)
14
+ cookies[k] = v
15
+ end
16
+ end
17
+
18
+ ['assignmentId', 'workerId', 'hitId'].each do |k|
19
+ buffer << hidden_field_tag(k, cookies[k]) if !params.has_key?(k) && cookies.has_key?(k)
20
+ end
21
+
22
+ buffer << yield(f)
23
+ buffer.html_safe
24
+ end
25
+ end
26
+
27
+ def turkee_study(id = nil)
28
+ task = id.nil? ? Turkee::TurkeeTask.last : Turkee::TurkeeTask.find(id)
29
+ study = Turkee::TurkeeStudy.new
30
+ disabled = Turkee::TurkeeFormHelper::disable_form_fields?(params[:assignmentId])
31
+
32
+ if task.present?
33
+ style = "position: fixed; top: 120px; right: 30px; color: #FFF;"
34
+ style << "width: 400px; height: 375px; z-index: 100; padding: 10px;"
35
+ style << "background-color: rgba(0,0,0, 0.5); border: 1px solid #000;"
36
+ div_for(task, :style => style) do
37
+ content = content_tag(:h3, "DIRECTIONS", :style => 'text-align: right; color:#FF0000;')
38
+ content << task.hit_description.html_safe
39
+ content << '<hr/>'.html_safe
40
+ content << turkee_form_for(study, params) do |f|
41
+ buffer = f.label(:feedback, "Feedback?:")
42
+ buffer << f.text_area(:feedback, :rows => 3, :disabled => disabled)
43
+ buffer << f.label(:gold_response, "Enter the fourth word from your above feedback :")
44
+ buffer << f.text_field(:gold_response, :disabled => disabled)
45
+ buffer << f.hidden_field(:turkee_task_id, :value => task.id)
46
+ buffer << '<br/>'.html_safe
47
+ buffer << f.submit('Submit', :disabled => disabled)
48
+ buffer
49
+ end.html_safe
50
+ content
51
+ end
52
+ end
53
+ end
54
+
55
+ # Returns the external Mechanical Turk url used to post form data based on whether RTurk is cofigured
56
+ # for sandbox use or not.
57
+ def mturk_url
58
+ RTurk.sandbox? ? "https://workersandbox.mturk.com/mturk/externalSubmit" : "https://www.mturk.com/mturk/externalSubmit"
59
+ end
60
+
61
+ # Returns whether the form fields should be disabled or not (based on the assignment_id)
62
+ def self.disable_form_fields?(assignment)
63
+ assignment_id = assignment.is_a?(Hash) ? assignment[:assignmentId] : assignment
64
+ (assignment_id.nil? || assignment_id == 'ASSIGNMENT_ID_NOT_AVAILABLE')
65
+ end
66
+ end
67
+
68
+ end
@@ -0,0 +1,15 @@
1
+ require 'active_record'
2
+
3
+ module Turkee
4
+ class TurkeeImportedAssignment < ActiveRecord::Base
5
+ attr_accessible :assignment_id, :turkee_task_id, :worker_id, :result_id
6
+
7
+ def self.record_imported_assignment(assignment, result, turk)
8
+ TurkeeImportedAssignment.create!(:assignment_id => assignment.id,
9
+ :turkee_task_id => turk.id,
10
+ :worker_id => assignment.worker_id,
11
+ :result_id => result.id)
12
+ end
13
+
14
+ end
15
+ end