qwester 0.0.9 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/README.rdoc +51 -0
  2. data/app/controllers/qwester/application_controller.rb +1 -2
  3. data/app/controllers/qwester/questionnaires_controller.rb +1 -2
  4. data/app/models/qwester/presentation.rb +45 -0
  5. data/app/models/qwester/presentation_questionnaire.rb +8 -0
  6. data/app/models/qwester/questionnaire.rb +7 -0
  7. data/app/models/qwester/rule_set.rb +2 -2
  8. data/app/views/qwester/questionnaires/index.html.erb +3 -3
  9. data/db/migrate/20130315112847_create_qwester_presentations.rb +11 -0
  10. data/db/migrate/20130315113027_create_qwester_presentation_questionnaires.rb +10 -0
  11. data/db/migrate/20130318102537_add_presentation_to_rule_sets.rb +6 -0
  12. data/lib/active_admin/admin/answers.rb +1 -1
  13. data/lib/active_admin/admin/presentations.rb +51 -0
  14. data/lib/active_admin/admin/questionnaires.rb +2 -0
  15. data/lib/active_admin/admin/questions.rb +2 -0
  16. data/lib/active_admin/admin/rule_sets.rb +35 -0
  17. data/lib/qwester/version.rb +7 -1
  18. data/lib/rails/actionpack/lib/action_controller/base.rb +36 -3
  19. data/test/dummy/db/development.sqlite3 +0 -0
  20. data/test/dummy/db/migrate/20130315113346_create_qwester_presentations.qwester.rb +12 -0
  21. data/test/dummy/db/migrate/20130315113347_create_qwester_presentation_questionnaires.qwester.rb +11 -0
  22. data/test/dummy/db/migrate/20130318123540_add_presentation_to_rule_sets.qwester.rb +7 -0
  23. data/test/dummy/db/schema.rb +20 -3
  24. data/test/dummy/db/test.sqlite3 +0 -0
  25. data/test/dummy/log/development.log +9021 -0
  26. data/test/dummy/log/test.log +146429 -0
  27. data/test/dummy/public/system/qwester/questionnaires/button_images/000/000/001/link/commend_button.png +0 -0
  28. data/test/dummy/public/system/qwester/questionnaires/button_images/000/000/001/original/commend_button.png +0 -0
  29. data/test/dummy/public/system/qwester/questionnaires/button_images/000/000/001/thumbnail/commend_button.png +0 -0
  30. data/test/dummy/public/system/qwester/questionnaires/button_images/000/000/002/link/commend_button.gif +0 -0
  31. data/test/dummy/public/system/qwester/questionnaires/button_images/000/000/002/original/commend_button.gif +0 -0
  32. data/test/dummy/public/system/qwester/questionnaires/button_images/000/000/002/thumbnail/commend_button.gif +0 -0
  33. data/test/dummy/test/fixtures/qwester/presentation_questionnaires.yml +3 -0
  34. data/test/dummy/test/fixtures/qwester/presentations.yml +13 -0
  35. data/test/dummy/test/functional/qwester/questionnaires_controller_test.rb +68 -3
  36. data/test/unit/qwester/presentation_test.rb +61 -0
  37. data/test/unit/qwester/questionnaire_test.rb +5 -1
  38. data/test/unit/qwester/rule_set_test.rb +12 -0
  39. metadata +32 -2
data/README.rdoc CHANGED
@@ -133,6 +133,57 @@ to a resource either within or outside the app.
133
133
  RuleSet uses array_logic[http://github.com/reggieb/array_logic] to manage
134
134
  the logic used to compare each rule set with the array of answers.
135
135
 
136
+ == Presentation
137
+
138
+ Questionnaires can be grouped into Presentations, and these are used to
139
+ control which questionnaires are displayed (presented to the user) at
140
+ any time.
141
+
142
+ All questionnaires are display at the engine root unless:
143
+
144
+ * A presentation is set as 'default'
145
+
146
+ * An existing presentation's name is added to session[:presentations]
147
+ (an array)
148
+
149
+ === Restricting the questionnaires displayed
150
+
151
+ If you do not want to display all the available questionnaires, create
152
+ a presentation, set it as 'default' and add to it the questionnaires you
153
+ wish to display.
154
+
155
+ === Using a questionnaire to control access to other questionnaires
156
+
157
+ Say you have three presentations of questionnaires, and you want to display
158
+ one initially. Then you want to use the answers submitted from that
159
+ questionnaire, to control which of the other two sets of questionnaires are
160
+ displayed next.
161
+
162
+ To do this set the initial presentation as default. Then create two rule
163
+ sets, one for each of the other presentations. Update each rule set to
164
+ match the answer pattern that should be submitted for the associated
165
+ pattern to be displayed, and set rule_set#presentation to the name of
166
+ the presentation you wish displayed when the rule is matched.
167
+
168
+ Then when the first set of questionnaires are submitted, the rules will be
169
+ checked, and if one of the two rules sets matches the submitted answers,
170
+ the associated presentation's questionnaires will be displayed.
171
+
172
+ === Simple rule matching for presentations
173
+
174
+ The rule matching is simple and if two presentation rules match, the system
175
+ will not try to work out which one it should display. It will just show the
176
+ last one it finds. So some care is required when setting up presentations
177
+ with matching rule sets to avoid overlaps and clashes.
178
+
179
+ === Once only match
180
+
181
+ Once a presentation has been matched, it cannot be returned to later. That
182
+ is if there are two presentations: 'one' and 'two', you cannot have a work
183
+ flow that goes from one to two and then back to one, unless you reset or
184
+ otherwise manipulate session[:presentations]. Instead you should clone 'one'
185
+ as a new presentation e.g. 'three', and then go from one to two to three.
186
+
136
187
  == Dummy
137
188
 
138
189
  A test app is present within this engine, and provides an example of how
@@ -1,6 +1,5 @@
1
1
  module Qwester
2
2
  class ApplicationController < ActionController::Base
3
-
4
-
3
+
5
4
  end
6
5
  end
@@ -2,8 +2,7 @@ module Qwester
2
2
  class QuestionnairesController < ApplicationController
3
3
 
4
4
  def index
5
- @questionnaires = Qwester::Questionnaire.all
6
- @rule_sets = matching_rule_sets
5
+ @questionnaires = current_questionnaires
7
6
  end
8
7
 
9
8
  def show
@@ -0,0 +1,45 @@
1
+ module Qwester
2
+ class Presentation < ActiveRecord::Base
3
+ attr_accessible :description, :name, :title, :questionnaire_ids, :default
4
+
5
+ has_many :presentation_questionnaires
6
+
7
+ has_many(
8
+ :questionnaires,
9
+ :through => :presentation_questionnaires
10
+ )
11
+ accepts_nested_attributes_for :questionnaires
12
+
13
+ before_save :before_save_tasks
14
+ after_save :after_save_tasks
15
+
16
+ validates(
17
+ :name,
18
+ :format => {
19
+ :with => /^\w+$/,
20
+ :message => "must comprise letters or numbers with underscores separating words"
21
+ },
22
+ :presence => true,
23
+ :uniqueness => true
24
+ )
25
+
26
+ private
27
+ def before_save_tasks
28
+ update_title_from_name
29
+ end
30
+
31
+ def after_save_tasks
32
+ undefault_others if self.default?
33
+ end
34
+
35
+ def undefault_others
36
+ current_defaults = self.class.find_all_by_default(true)
37
+ current_defaults.each{|p| p.update_attribute(:default, false) unless p == self}
38
+ end
39
+
40
+ def update_title_from_name
41
+ self.title = self.name.humanize unless self.title.present?
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,8 @@
1
+ module Qwester
2
+ class PresentationQuestionnaire < ActiveRecord::Base
3
+ attr_accessible :presentation_id, :questionnaire_id
4
+
5
+ belongs_to :presentation
6
+ belongs_to :questionnaire
7
+ end
8
+ end
@@ -34,6 +34,13 @@ module Qwester
34
34
  :join_table => :qwester_answer_stores_questionnaires,
35
35
  :uniq => true
36
36
  )
37
+
38
+ has_many :presentation_questionnaires
39
+
40
+ has_many(
41
+ :presentations,
42
+ :through => :presentation_questionnaires
43
+ )
37
44
 
38
45
  validates :title, :presence => true
39
46
 
@@ -1,7 +1,7 @@
1
1
  require 'array_logic'
2
2
  module Qwester
3
3
  class RuleSet < ActiveRecord::Base
4
- attr_accessible :title, :description, :answers, :url, :rule, :answer_ids, :link_text, :warning_id
4
+ attr_accessible :title, :description, :answers, :url, :rule, :answer_ids, :link_text, :warning_id, :presentation
5
5
 
6
6
  before_save :keep_answers_in_step_with_rule
7
7
 
@@ -23,7 +23,7 @@ module Qwester
23
23
  validate :check_rule_is_valid
24
24
 
25
25
  validates :title, :presence => true
26
- validates :url, :presence => true
26
+ validates :url, :presence => {:unless => :presentation?}
27
27
 
28
28
 
29
29
  def self.matching(answers)
@@ -19,10 +19,10 @@
19
19
  <% end %>
20
20
  </div>
21
21
 
22
- <% if @rule_sets.present? %>
22
+ <% if @qwester_rule_sets.present? %>
23
23
  <h2>Matching rule sets</h2>
24
24
  <ul>
25
- <% @rule_sets.each do |rule_set| %>
25
+ <% @qwester_rule_sets.each do |rule_set| %>
26
26
  <li>
27
27
  <%= rule_set.title %>:
28
28
  <%= link_to(rule_set.link_text, rule_set.url) %>
@@ -35,5 +35,5 @@
35
35
  content_tag(
36
36
  'p',
37
37
  link_to('Clear answers and start again', reset_questionnaires_path)
38
- ) if @rule_sets.present?
38
+ ) if @qwester_rule_sets.present?
39
39
  %>
@@ -0,0 +1,11 @@
1
+ class CreateQwesterPresentations < ActiveRecord::Migration
2
+ def change
3
+ create_table :qwester_presentations do |t|
4
+ t.string :name
5
+ t.string :title
6
+ t.text :description
7
+
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ class CreateQwesterPresentationQuestionnaires < ActiveRecord::Migration
2
+ def change
3
+ create_table :qwester_presentation_questionnaires do |t|
4
+ t.integer :questionnaire_id
5
+ t.integer :presentation_id
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,6 @@
1
+ class AddPresentationToRuleSets < ActiveRecord::Migration
2
+ def change
3
+ add_column :qwester_rule_sets, :presentation, :string
4
+ add_column :qwester_presentations, :default, :boolean
5
+ end
6
+ end
@@ -7,6 +7,7 @@ module Qwester
7
7
  menu :parent => Qwester.active_admin_menu, :label => menu_label
8
8
 
9
9
  actions :all, :except => [:edit]
10
+ config.batch_actions = false
10
11
 
11
12
  index do
12
13
  column :id
@@ -15,7 +16,6 @@ module Qwester
15
16
  link_to(answer.question.title, edit_admin_qwester_question_path(answer.question)) if answer.question
16
17
  end
17
18
  column :position
18
- column :cope_index
19
19
  default_actions
20
20
  end
21
21
 
@@ -0,0 +1,51 @@
1
+ module Qwester
2
+ ActiveAdmin.register Presentation do
3
+
4
+ menu_label = 'Presentations'
5
+ menu_label = "Qwester #{menu_label}" unless Qwester.active_admin_menu
6
+ menu :parent => Qwester.active_admin_menu, :label => menu_label
7
+
8
+ config.batch_actions = false
9
+
10
+ index do
11
+ column :name do |presentation|
12
+ link_to presentation.name, admin_qwester_presentation_path(presentation)
13
+ end
14
+ column :title
15
+ column :default do |presentation|
16
+ 'default' if presentation.default?
17
+ end
18
+ column :questionnaires do |presentation|
19
+ presentation.questionnaires.collect(&:title).join(', ')
20
+ end
21
+ default_actions
22
+ end
23
+
24
+ show do
25
+ h2 qwester_presentation.title
26
+ qwester_presentation.questionnaires.each do |questionnaire|
27
+ div(:style => 'display:inline-block;margin-right:20px;') do
28
+ para image_tag(questionnaire.button_image.url(:thumbnail))
29
+ para questionnaire.title
30
+ end
31
+ end
32
+ para "Default: Will be inital presentation of quesitonnaires" if qwester_presentation.default?
33
+ end
34
+
35
+ form do |f|
36
+ f.inputs "Details" do
37
+ f.input :name
38
+ f.input :title, :label => "Title (or create from name)"
39
+ f.input :default, :label => 'Set as default (that is, the first presentation displayed to a user). If no default, all questionnaires will be displayed'
40
+ if defined?(Ckeditor)
41
+ f.input :description, :as => :ckeditor, :input_html => { :height => 100, :toolbar => 'Basic' }
42
+ else
43
+ f.input :description, :input_html => { :rows => 3}
44
+ end
45
+ f.input :questionnaires, :as => :check_boxes, :collection => Questionnaire.all
46
+ end
47
+ f.buttons
48
+ end
49
+
50
+ end
51
+ end
@@ -6,6 +6,8 @@ module Qwester
6
6
  menu_label = "Qwester #{menu_label}" unless Qwester.active_admin_menu
7
7
  menu :parent => Qwester.active_admin_menu, :label => menu_label
8
8
 
9
+ config.batch_actions = false
10
+
9
11
  index do
10
12
  column :image do |questionnaire|
11
13
  image_tag(questionnaire.button_image.url(:thumbnail))
@@ -6,6 +6,8 @@ module Qwester
6
6
  menu_label = "Qwester #{menu_label}" unless Qwester.active_admin_menu
7
7
  menu :parent => Qwester.active_admin_menu, :label => menu_label
8
8
 
9
+ config.batch_actions = false
10
+
9
11
  index do
10
12
  column :ref
11
13
  column :title do |qwester_question|
@@ -6,8 +6,11 @@ module Qwester
6
6
  menu_label = "Qwester #{menu_label}" unless Qwester.active_admin_menu
7
7
  menu :parent => Qwester.active_admin_menu, :label => menu_label
8
8
 
9
+ config.batch_actions = false
10
+
9
11
  index do
10
12
  column :title
13
+ column :presentation
11
14
  column :answers do |rule_set|
12
15
  rule_set.answers.count
13
16
  end
@@ -57,6 +60,25 @@ module Qwester
57
60
 
58
61
  end
59
62
 
63
+ sidebar :presentations do
64
+ para <<EOF
65
+ If a rule is assigned a presentation, that presentation's questionnaires will
66
+ be displayed when the rule is matched.
67
+ EOF
68
+
69
+ para <<EOF
70
+ There is no mechanism to handle multiple rules with presentations, being trigged
71
+ at the same time. The app will just display the questionnaires assigned to the
72
+ last rule_set.presentation it finds. So some care should be taken to avoid
73
+ clashes.
74
+ EOF
75
+
76
+ para <<EOF
77
+ A rule set with a presentation, will change the display. It will not display
78
+ an output tab, any url associated with the rule will be ignored.
79
+ EOF
80
+ end
81
+
60
82
  form do |f|
61
83
  f.inputs "Details" do
62
84
  f.input :title
@@ -72,6 +94,10 @@ module Qwester
72
94
  f.input :link_text
73
95
  end
74
96
 
97
+ f.inputs "Change Presentation of Questionnaires" do
98
+ f.input :presentation, :as => :select, :collection => Presentation.all.collect(&:name), :include_blank => 'No effect on presentation'
99
+ end
100
+
75
101
  f.inputs "Logic" do
76
102
  f.input :rule, :input_html => { :rows => 3}
77
103
  end
@@ -123,6 +149,15 @@ module Qwester
123
149
  para qwester_rule_set.link_text? ? qwester_rule_set.link_text : 'No link text specified'
124
150
  end
125
151
 
152
+ div do
153
+ h3 'Presentation'
154
+ if qwester_rule_set.presentation?
155
+ para "The presentations of questionnaires should change to: #{qwester_rule_set.presentation}"
156
+ else
157
+ para "The presentations of questionnaires should be unaffected by this rule"
158
+ end
159
+ end
160
+
126
161
  div do
127
162
  h3 'The rule'
128
163
  para qwester_rule_set.rule
@@ -1,10 +1,16 @@
1
1
  module Qwester
2
- VERSION = "0.0.9"
2
+ VERSION = "0.1.0"
3
3
  end
4
4
 
5
5
  # History
6
6
  # =======
7
7
  #
8
+ # 0.1.0 - Add presentations as a way of controlling the display of questionnaires
9
+ # Allows admin to define groups of questionnaires as presentation views
10
+ # and rule sets that will display a presentation when triggered. In that way
11
+ # the list of questionnaires being displayed can change as questionnaires are
12
+ # submitted.
13
+ #
8
14
  # 0.0.9 - maintenance update
9
15
  # Removes cope_index from migrations.
10
16
  # Ensures answer_store#session_id is unique
@@ -33,13 +33,46 @@ module ActionController
33
33
  @qwester_answer_store
34
34
  end
35
35
 
36
- def matching_rule_sets
36
+ def match_rule_sets
37
37
  if get_qwester_answer_store
38
- Qwester::RuleSet.matching(@qwester_answer_store.answers)
38
+ @qwester_rule_sets = Qwester::RuleSet.matching(@qwester_answer_store.answers)
39
+ get_presentation_from_rule_sets
40
+ return @qwester_rule_sets
41
+ end
42
+ end
43
+ alias_method :matching_rule_sets, :match_rule_sets
44
+
45
+ def current_questionnaires
46
+ match_rule_sets
47
+ presentation_questionnaires || default_presentation_questionnaires || Qwester::Questionnaire.all
48
+ end
49
+
50
+ def presentation_questionnaires
51
+ presentation = Qwester::Presentation.find_by_name(session[:presentations].last) if session[:presentations]
52
+ presentation.questionnaires if presentation
53
+ end
54
+
55
+ def default_presentation_questionnaires
56
+ presentation = Qwester::Presentation.find_by_default(true)
57
+ presentation.questionnaires if presentation
58
+ end
59
+
60
+ def get_presentation_from_rule_sets
61
+ @qwester_rule_sets.clone.each do |rule_set|
62
+ next unless rule_set.presentation?
63
+ add_presentation_to_session rule_set.presentation
64
+ @qwester_rule_sets.delete(rule_set)
65
+ end
66
+ end
67
+
68
+ def add_presentation_to_session(presentation)
69
+ session_presentations = session[:presentations] || []
70
+ unless session_presentations.include? presentation
71
+ session_presentations << presentation
72
+ session[:presentations] = session_presentations
39
73
  end
40
74
  end
41
75
 
42
- # private
43
76
  def add_answers_to_qwester_answer_store
44
77
  answers = params[:question_id].values.collect do |question_values|
45
78
  question_values[:answer_ids].collect{|id| Qwester::Answer.find(id)}
Binary file
@@ -0,0 +1,12 @@
1
+ # This migration comes from qwester (originally 20130315112847)
2
+ class CreateQwesterPresentations < ActiveRecord::Migration
3
+ def change
4
+ create_table :qwester_presentations do |t|
5
+ t.string :name
6
+ t.string :title
7
+ t.text :description
8
+
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ # This migration comes from qwester (originally 20130315113027)
2
+ class CreateQwesterPresentationQuestionnaires < ActiveRecord::Migration
3
+ def change
4
+ create_table :qwester_presentation_questionnaires do |t|
5
+ t.integer :questionnaire_id
6
+ t.integer :presentation_id
7
+
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ # This migration comes from qwester (originally 20130318102537)
2
+ class AddPresentationToRuleSets < ActiveRecord::Migration
3
+ def change
4
+ add_column :qwester_rule_sets, :presentation, :string
5
+ add_column :qwester_presentations, :default, :boolean
6
+ end
7
+ end
@@ -11,7 +11,7 @@
11
11
  #
12
12
  # It's strongly recommended to check this file into your version control system.
13
13
 
14
- ActiveRecord::Schema.define(:version => 20130314103662) do
14
+ ActiveRecord::Schema.define(:version => 20130318123540) do
15
15
 
16
16
  create_table "active_admin_comments", :force => true do |t|
17
17
  t.string "resource_id", :null => false
@@ -91,6 +91,22 @@ ActiveRecord::Schema.define(:version => 20130314103662) do
91
91
  add_index "qwester_ckeditor_assets", ["assetable_type", "assetable_id"], :name => "qwester_idx_ckeditor_assetable"
92
92
  add_index "qwester_ckeditor_assets", ["assetable_type", "type", "assetable_id"], :name => "qwester_idx_ckeditor_assetable_type"
93
93
 
94
+ create_table "qwester_presentation_questionnaires", :force => true do |t|
95
+ t.integer "questionnaire_id"
96
+ t.integer "presentation_id"
97
+ t.datetime "created_at", :null => false
98
+ t.datetime "updated_at", :null => false
99
+ end
100
+
101
+ create_table "qwester_presentations", :force => true do |t|
102
+ t.string "name"
103
+ t.string "title"
104
+ t.text "description"
105
+ t.datetime "created_at", :null => false
106
+ t.datetime "updated_at", :null => false
107
+ t.boolean "default"
108
+ end
109
+
94
110
  create_table "qwester_questionnaires", :force => true do |t|
95
111
  t.string "title"
96
112
  t.text "description"
@@ -127,10 +143,11 @@ ActiveRecord::Schema.define(:version => 20130314103662) do
127
143
  t.string "title"
128
144
  t.text "description"
129
145
  t.string "url"
130
- t.datetime "created_at", :null => false
131
- t.datetime "updated_at", :null => false
146
+ t.datetime "created_at", :null => false
147
+ t.datetime "updated_at", :null => false
132
148
  t.text "rule"
133
149
  t.string "link_text"
150
+ t.string "presentation"
134
151
  end
135
152
 
136
153
  end
Binary file