qwester 0.0.9 → 0.1.0

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.
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