effective_questions 0.0.2 → 0.0.3
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.
- checksums.yaml +4 -4
- data/app/datatables/admin/effective_questions_datatable.rb +4 -1
- data/app/models/effective/question.rb +64 -2
- data/app/models/effective/question_answer.rb +204 -0
- data/app/models/effective/question_option.rb +2 -0
- data/app/models/effective/response.rb +11 -4
- data/app/views/admin/questions/_fields_category.html.haml +76 -0
- data/app/views/admin/questions/_fields_scored.html.haml +131 -0
- data/app/views/admin/questions/_form.html.haml +4 -86
- data/app/views/effective/responsable/_results.html.haml +1 -1
- data/app/views/effective/responses/fields/_decimal.html.haml +2 -0
- data/app/views/effective/responses/fields/_percentage.html.haml +2 -0
- data/app/views/effective/responses/fields/_price.html.haml +2 -0
- data/app/views/effective/responses/responses/_decimal.html.haml +1 -0
- data/app/views/effective/responses/responses/_percentage.html.haml +5 -0
- data/app/views/effective/responses/responses/_price.html.haml +1 -0
- data/db/migrate/101_create_effective_questions.rb +3 -0
- data/lib/effective_questions/version.rb +1 -1
- metadata +11 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 816f974b650f646dd7d6028380c57a6471e696aee74c272c051bc6b336c42663
|
|
4
|
+
data.tar.gz: c23ff2b377c70a697f0d5ed1b79d53ce4ba9dd1f8514e9e8fa72dcfdf82967f4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b68de6ae22c33bf0eef91f13f32d9af7ddf5fb76753cb4dfb1006d3eaf10d24e7cb0eec9861c2b039b2520620774f85e1fccf5843d6fe5587ded03bef74d8446
|
|
7
|
+
data.tar.gz: 386055e8ab8f4f66f4a6824fcccf0bda99d833f58f8d74c466abc37040c3667d80671bf07c8c1d294f8ceff00a23d0dc591bb9f3838f15a53b06796376722c7e
|
|
@@ -12,7 +12,7 @@ class Admin::EffectiveQuestionsDatatable < Effective::Datatable
|
|
|
12
12
|
col :questionable
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
col :position, visible:
|
|
15
|
+
col :position, visible: attributes[:follow_up].blank? do |question|
|
|
16
16
|
question.position.to_i + 1
|
|
17
17
|
end
|
|
18
18
|
|
|
@@ -27,6 +27,9 @@ class Admin::EffectiveQuestionsDatatable < Effective::Datatable
|
|
|
27
27
|
col :follow_up_questions, action: false, label: 'Follow up questions'
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
col :scored
|
|
31
|
+
col :answer_to_s, label: 'Answer'
|
|
32
|
+
|
|
30
33
|
actions_col
|
|
31
34
|
end
|
|
32
35
|
|
|
@@ -4,11 +4,13 @@ module Effective
|
|
|
4
4
|
|
|
5
5
|
belongs_to :question, optional: true # Present when I'm a follow up question
|
|
6
6
|
belongs_to :question_option, optional: true # Might be present when I'm a follow up question
|
|
7
|
+
has_many :follow_up_questions, -> { order(:position) }, class_name: 'Effective::Question', foreign_key: :question_id, dependent: :destroy
|
|
7
8
|
|
|
8
9
|
has_many :question_options, -> { order(:position) }, inverse_of: :question, dependent: :delete_all
|
|
9
|
-
accepts_nested_attributes_for :question_options, reject_if:
|
|
10
|
+
accepts_nested_attributes_for :question_options, reject_if: -> (atts) { atts['title'].blank? }, allow_destroy: true
|
|
10
11
|
|
|
11
|
-
has_many :
|
|
12
|
+
has_many :question_answers, dependent: :delete_all
|
|
13
|
+
accepts_nested_attributes_for :question_answers, reject_if: :all_blank, allow_destroy: true
|
|
12
14
|
|
|
13
15
|
has_rich_text :body
|
|
14
16
|
log_changes(to: :questionable) if respond_to?(:log_changes)
|
|
@@ -24,8 +26,11 @@ module Effective
|
|
|
24
26
|
'Select up to 4',
|
|
25
27
|
'Select up to 5',
|
|
26
28
|
'Date', # Date Field
|
|
29
|
+
'Decimal', # Decimal Field
|
|
27
30
|
'Email', # Email Field
|
|
28
31
|
'Number', # Numeric Field
|
|
32
|
+
'Percentage', # Percentage Field
|
|
33
|
+
'Price', # Price Field
|
|
29
34
|
'Long Answer', # Text Area
|
|
30
35
|
'Short Answer', # Text Field
|
|
31
36
|
'Upload File' # File field
|
|
@@ -53,6 +58,8 @@ module Effective
|
|
|
53
58
|
follow_up :boolean
|
|
54
59
|
follow_up_value :string
|
|
55
60
|
|
|
61
|
+
scored :boolean
|
|
62
|
+
|
|
56
63
|
timestamps
|
|
57
64
|
end
|
|
58
65
|
|
|
@@ -81,6 +88,30 @@ module Effective
|
|
|
81
88
|
validates :question_option, presence: true, if: -> { follow_up? && question.try(:question_option?) }
|
|
82
89
|
validates :follow_up_value, presence: true, if: -> { follow_up? && !question.try(:question_option?) }
|
|
83
90
|
|
|
91
|
+
validates :scored, inclusion: { in: [false], message: 'is not supported' }, if: -> { category == 'Upload File' }
|
|
92
|
+
|
|
93
|
+
validates :question_answer, presence: true, if: -> { scored? }
|
|
94
|
+
|
|
95
|
+
validate(if: -> { scored? && (choose_one? || select_up_to_1? || select_all_that_apply?) }) do
|
|
96
|
+
errors.add(:base, 'must have at least one correct answer option') if answer_options.count < 1
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
validate(if: -> { scored? && select_up_to_2? }) do
|
|
100
|
+
errors.add(:base, 'must have two or more correct answer options') if answer_options.count < 2
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
validate(if: -> { scored? && select_up_to_3? }) do
|
|
104
|
+
errors.add(:base, 'must have three or more correct answer options') if answer_options.count < 3
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
validate(if: -> { scored? && select_up_to_4? }) do
|
|
108
|
+
errors.add(:base, 'must have four or more correct answer options') if answer_options.count < 4
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
validate(if: -> { scored? && select_up_to_5? }) do
|
|
112
|
+
errors.add(:base, 'must have five or more correct answer options') if answer_options.count < 5
|
|
113
|
+
end
|
|
114
|
+
|
|
84
115
|
# Create choose_one? and select_all_that_apply? methods for each category
|
|
85
116
|
CATEGORIES.each do |category|
|
|
86
117
|
define_method(category.parameterize.underscore + '?') { self.category == category }
|
|
@@ -95,8 +126,11 @@ module Effective
|
|
|
95
126
|
|
|
96
127
|
case category
|
|
97
128
|
when 'Date' then :date
|
|
129
|
+
when 'Decimal' then :decimal
|
|
98
130
|
when 'Email' then :email
|
|
99
131
|
when 'Number' then :number
|
|
132
|
+
when 'Percentage' then :percentage
|
|
133
|
+
when 'Price' then :price
|
|
100
134
|
when 'Long Answer' then :long_answer
|
|
101
135
|
when 'Short Answer' then :short_answer
|
|
102
136
|
when 'Upload File' then :upload_file
|
|
@@ -104,6 +138,26 @@ module Effective
|
|
|
104
138
|
end
|
|
105
139
|
end
|
|
106
140
|
|
|
141
|
+
def answer
|
|
142
|
+
return unless scored?
|
|
143
|
+
question_option? ? answer_options : question_answer.try(:answer)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def answer_to_s
|
|
147
|
+
return '' unless scored?
|
|
148
|
+
|
|
149
|
+
case category
|
|
150
|
+
when 'Choose one' then "One of: #{answer_options.join(', ')}"
|
|
151
|
+
when 'Select all that apply' then "All of: #{answer_options.join(', ')}"
|
|
152
|
+
when 'Select up to 1' then "One of: #{answer_options.join(', ')}"
|
|
153
|
+
when 'Select up to 2' then "Two of: #{answer_options.join(', ')}"
|
|
154
|
+
when 'Select up to 3' then "Three of: #{answer_options.join(', ')}"
|
|
155
|
+
when 'Select up to 4' then "Four of: #{answer_options.join(', ')}"
|
|
156
|
+
when 'Select up to 5' then "Five of: #{answer_options.join(', ')}"
|
|
157
|
+
else question_answer.try(:to_s)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
107
161
|
def show_if_value
|
|
108
162
|
question.try(:question_option?) ? question_option_id : follow_up_value
|
|
109
163
|
end
|
|
@@ -116,9 +170,17 @@ module Effective
|
|
|
116
170
|
WITH_OPTIONS_CATEGORIES.include?(category)
|
|
117
171
|
end
|
|
118
172
|
|
|
173
|
+
def answer_options
|
|
174
|
+
question_options.reject(&:marked_for_destruction?).select(&:answer?)
|
|
175
|
+
end
|
|
176
|
+
|
|
119
177
|
def category_partial
|
|
120
178
|
category.to_s.parameterize.underscore
|
|
121
179
|
end
|
|
122
180
|
|
|
181
|
+
def question_answer
|
|
182
|
+
question_answers.first || question_answers.build()
|
|
183
|
+
end
|
|
184
|
+
|
|
123
185
|
end
|
|
124
186
|
end
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
module Effective
|
|
2
|
+
class QuestionAnswer < ActiveRecord::Base
|
|
3
|
+
belongs_to :question
|
|
4
|
+
|
|
5
|
+
OPERATIONS = [
|
|
6
|
+
'Equal to',
|
|
7
|
+
'Within range',
|
|
8
|
+
'Less than',
|
|
9
|
+
'Less than or equal to',
|
|
10
|
+
'Greater than',
|
|
11
|
+
'Greater than or equal to',
|
|
12
|
+
'Contains',
|
|
13
|
+
'Does not contain'
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
effective_resource do
|
|
17
|
+
operation :string
|
|
18
|
+
|
|
19
|
+
# The answer
|
|
20
|
+
date :date
|
|
21
|
+
date_begin :date
|
|
22
|
+
date_end :date
|
|
23
|
+
|
|
24
|
+
decimal :decimal
|
|
25
|
+
decimal_begin :decimal
|
|
26
|
+
decimal_end :decimal
|
|
27
|
+
|
|
28
|
+
email :string
|
|
29
|
+
|
|
30
|
+
number :integer
|
|
31
|
+
number_begin :integer
|
|
32
|
+
number_end :integer
|
|
33
|
+
|
|
34
|
+
percentage :integer
|
|
35
|
+
percentage_begin :integer
|
|
36
|
+
percentage_end :integer
|
|
37
|
+
|
|
38
|
+
price :integer
|
|
39
|
+
price_begin :integer
|
|
40
|
+
price_end :integer
|
|
41
|
+
|
|
42
|
+
long_answer :text
|
|
43
|
+
short_answer :text
|
|
44
|
+
|
|
45
|
+
timestamps
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
scope :deep, -> { includes(:question) }
|
|
49
|
+
|
|
50
|
+
with_options(unless: -> { question.blank? }) do
|
|
51
|
+
validate(if: -> { question.category.present? }) do
|
|
52
|
+
supported = operations(question.category)
|
|
53
|
+
errors.add(:operation, 'is not supported for this question') if supported.present? && supported.exclude?(operation)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
validates :date, presence: true, if: -> { question.date? && (equals? || gteq? || lteq? || lt? || gt?) }
|
|
57
|
+
validates :date_begin, presence: true, if: -> { question.date? && within_range? }
|
|
58
|
+
validates :date_end, presence: true, if: -> { question.date? && within_range? }
|
|
59
|
+
|
|
60
|
+
validates :decimal, presence: true, if: -> { question.decimal? && (equals? || gteq? || lteq? || lt? || gt?) }
|
|
61
|
+
validates :decimal_begin, presence: true, if: -> { question.decimal? && within_range? }
|
|
62
|
+
validates :decimal_end, presence: true, if: -> { question.decimal? && within_range? }
|
|
63
|
+
|
|
64
|
+
validates :email, presence: true, if: -> { question.email? && (equals? || contains? || does_not_contain?) }
|
|
65
|
+
validates :email, email: true, if: -> { question.email? && equals? }
|
|
66
|
+
|
|
67
|
+
validates :number, presence: true, if: -> { question.number? && (equals? || gteq? || lteq? || lt? || gt?) }
|
|
68
|
+
validates :number_begin, presence: true, if: -> { question.number? && within_range? }
|
|
69
|
+
validates :number_end, presence: true, if: -> { question.number? && within_range? }
|
|
70
|
+
|
|
71
|
+
validates :percentage, presence: true, if: -> { question.percentage? && (equals? || gteq? || lteq? || lt? || gt?) }
|
|
72
|
+
validates :percentage_begin, presence: true, if: -> { question.percentage? && within_range? }
|
|
73
|
+
validates :percentage_end, presence: true, if: -> { question.percentage? && within_range? }
|
|
74
|
+
|
|
75
|
+
validates :price, presence: true, if: -> { question.price? && (equals? || gteq? || lteq? || lt? || gt?) }
|
|
76
|
+
validates :price_begin, presence: true, if: -> { question.price? && within_range? }
|
|
77
|
+
validates :price_end, presence: true, if: -> { question.price? && within_range? }
|
|
78
|
+
|
|
79
|
+
validates :long_answer, presence: true, if: -> { question.long_answer? }
|
|
80
|
+
validates :short_answer, presence: true, if: -> { question.short_answer? }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def to_s
|
|
84
|
+
return '' unless question.present?
|
|
85
|
+
return '-' unless operations(question.category).present?
|
|
86
|
+
|
|
87
|
+
case operation
|
|
88
|
+
when 'Equal to'
|
|
89
|
+
"Equal to #{format_value(answer)}"
|
|
90
|
+
when 'Within range'
|
|
91
|
+
"Between #{format_value(answer.begin)} and #{format_value(answer.end)}"
|
|
92
|
+
when 'Less than'
|
|
93
|
+
"Less than #{format_value(answer)}"
|
|
94
|
+
when 'Less than or equal to'
|
|
95
|
+
"Less than or equal to #{format_value(answer)}"
|
|
96
|
+
when 'Greater than'
|
|
97
|
+
"Greater than #{format_value(answer)}"
|
|
98
|
+
when 'Greater than or equal to'
|
|
99
|
+
"Greater than or equal to #{format_value(answer)}"
|
|
100
|
+
when 'Contains'
|
|
101
|
+
"Contains #{format_value(answer)}"
|
|
102
|
+
when 'Does not contain'
|
|
103
|
+
"Does not contain #{format_value(answer)}"
|
|
104
|
+
else
|
|
105
|
+
raise("unknown operation: #{operation}")
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def operations(category)
|
|
110
|
+
case category
|
|
111
|
+
when 'Choose one' then nil
|
|
112
|
+
when 'Select all that apply' then nil
|
|
113
|
+
when 'Select up to 1' then nil
|
|
114
|
+
when 'Select up to 2' then nil
|
|
115
|
+
when 'Select up to 3' then nil
|
|
116
|
+
when 'Select up to 4' then nil
|
|
117
|
+
when 'Select up to 5' then nil
|
|
118
|
+
when 'Upload File' then nil
|
|
119
|
+
when 'Date' then OPERATIONS - ['Contains', 'Does not contain']
|
|
120
|
+
when 'Decimal' then OPERATIONS - ['Contains', 'Does not contain']
|
|
121
|
+
when 'Number' then OPERATIONS - ['Contains', 'Does not contain']
|
|
122
|
+
when 'Percentage' then OPERATIONS - ['Contains', 'Does not contain']
|
|
123
|
+
when 'Price' then OPERATIONS - ['Contains', 'Does not contain']
|
|
124
|
+
when 'Email' then ['Equal to', 'Contains', 'Does not contain']
|
|
125
|
+
when 'Long Answer' then ['Equal to', 'Contains', 'Does not contain']
|
|
126
|
+
when 'Short Answer' then ['Equal to', 'Contains', 'Does not contain']
|
|
127
|
+
else
|
|
128
|
+
raise("unknown operations for category: #{category}")
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def answer
|
|
133
|
+
if operation == 'Within range'
|
|
134
|
+
return (date_begin..date_end) if question.date?
|
|
135
|
+
return (decimal_begin..decimal_end) if question.decimal?
|
|
136
|
+
return (number_begin..number_end) if question.number?
|
|
137
|
+
return (percentage_begin..percentage_end) if question.percentage?
|
|
138
|
+
return (price_begin..price_end) if question.price?
|
|
139
|
+
else
|
|
140
|
+
return date if question.date?
|
|
141
|
+
return decimal if question.decimal?
|
|
142
|
+
return email if question.email?
|
|
143
|
+
return number if question.number?
|
|
144
|
+
return percentage if question.percentage?
|
|
145
|
+
return price if question.price?
|
|
146
|
+
return long_answer if question.long_answer?
|
|
147
|
+
return short_answer if question.short_answer?
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
raise('unknown operation: #{operation}')
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def equals?
|
|
154
|
+
operation == 'Equals'
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def contains?
|
|
158
|
+
operation == 'Contains'
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def does_not_contain?
|
|
162
|
+
operation == 'Does not contain'
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def within_range?
|
|
166
|
+
operation == 'Within Range'
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def lt?
|
|
170
|
+
operation == 'Less than'
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def gt?
|
|
174
|
+
operation == 'Greater than'
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def lteq?
|
|
178
|
+
operation == 'Less than or equal to'
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def gteq?
|
|
182
|
+
operation == 'Greater than or equal to'
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
private
|
|
186
|
+
|
|
187
|
+
def format_value(val)
|
|
188
|
+
return '' if val.blank?
|
|
189
|
+
|
|
190
|
+
if question.price?
|
|
191
|
+
"$#{'%0.2f' % (val / 100.0)}"
|
|
192
|
+
elsif question.percentage?
|
|
193
|
+
precision = (val % 1000).zero? ? 0 : 3
|
|
194
|
+
"#{format("%.#{precision}f", val.to_f / 1000)}%"
|
|
195
|
+
elsif question.date?
|
|
196
|
+
val.strftime('%Y-%m-%d')
|
|
197
|
+
elsif val.is_a?(Numeric)
|
|
198
|
+
val.to_s
|
|
199
|
+
else
|
|
200
|
+
"\"#{val}\""
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
@@ -12,8 +12,11 @@ module Effective
|
|
|
12
12
|
effective_resource do
|
|
13
13
|
# The response
|
|
14
14
|
date :date
|
|
15
|
+
decimal :decimal
|
|
15
16
|
email :string
|
|
16
17
|
number :integer
|
|
18
|
+
percentage :integer
|
|
19
|
+
price :integer
|
|
17
20
|
long_answer :text
|
|
18
21
|
short_answer :text
|
|
19
22
|
|
|
@@ -27,6 +30,9 @@ module Effective
|
|
|
27
30
|
end
|
|
28
31
|
|
|
29
32
|
validates :date, presence: true, if: -> { question&.required? && question.date? }
|
|
33
|
+
validates :decimal, presence: true, if: -> { question&.required? && question.decimal? }
|
|
34
|
+
validates :percentage, presence: true, if: -> { question&.required? && question.percentage? }
|
|
35
|
+
validates :price, presence: true, if: -> { question&.required? && question.price? }
|
|
30
36
|
validates :email, presence: true, email: true, if: -> { question&.required? && question.email? }
|
|
31
37
|
validates :number, presence: true, if: -> { question&.required? && question.number? }
|
|
32
38
|
validates :long_answer, presence: true, if: -> { question&.required? && question.long_answer? }
|
|
@@ -53,8 +59,7 @@ module Effective
|
|
|
53
59
|
length: { maximum: 5, message: 'please select 5 options or fewer' }
|
|
54
60
|
|
|
55
61
|
def to_s
|
|
56
|
-
|
|
57
|
-
response.to_s
|
|
62
|
+
model_name.human || 'Response'
|
|
58
63
|
end
|
|
59
64
|
|
|
60
65
|
def response
|
|
@@ -63,6 +68,9 @@ module Effective
|
|
|
63
68
|
return date if question.date?
|
|
64
69
|
return email if question.email?
|
|
65
70
|
return number if question.number?
|
|
71
|
+
return percentage if question.percentage?
|
|
72
|
+
return price if question.price?
|
|
73
|
+
return decimal if question.decimal?
|
|
66
74
|
return long_answer if question.long_answer?
|
|
67
75
|
return short_answer if question.short_answer?
|
|
68
76
|
return upload_file if question.upload_file?
|
|
@@ -79,8 +87,7 @@ module Effective
|
|
|
79
87
|
end
|
|
80
88
|
|
|
81
89
|
def completed?
|
|
82
|
-
|
|
83
|
-
responsable.completed?
|
|
90
|
+
responsable.try(:completed?) == true
|
|
84
91
|
end
|
|
85
92
|
|
|
86
93
|
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
= f.show_if :category, 'Choose one' do
|
|
2
|
+
= card('Choose one') do
|
|
3
|
+
%p Display radio buttons to select one option
|
|
4
|
+
|
|
5
|
+
= f.show_if :category, 'Select all that apply' do
|
|
6
|
+
= card('Select all that apply') do
|
|
7
|
+
%p Display checkboxes to select all options that apply
|
|
8
|
+
|
|
9
|
+
= f.show_if :category, 'Select up to 1' do
|
|
10
|
+
= card('Select up to 1 (one)') do
|
|
11
|
+
%p Display checkboxes to select up to 1 option
|
|
12
|
+
|
|
13
|
+
= f.show_if :category, 'Select up to 2' do
|
|
14
|
+
= card('Select up to 2 (two)') do
|
|
15
|
+
%p Display checkboxes to select up to 2 options
|
|
16
|
+
|
|
17
|
+
= f.show_if :category, 'Select up to 3' do
|
|
18
|
+
= card('Select up to 3 (three)') do
|
|
19
|
+
%p Display checkboxes to select up to 3 options
|
|
20
|
+
|
|
21
|
+
= f.show_if :category, 'Select up to 4' do
|
|
22
|
+
= card('Select up to 4 (four)') do
|
|
23
|
+
%p Display checkboxes to select up to 4 options
|
|
24
|
+
|
|
25
|
+
= f.show_if :category, 'Select up to 5' do
|
|
26
|
+
= card('Select up to 5 (five)') do
|
|
27
|
+
%p Display checkboxes to select up to 5 options
|
|
28
|
+
|
|
29
|
+
= f.show_if :category, 'Short Answer' do
|
|
30
|
+
= card('Short Answer') do
|
|
31
|
+
%p Display a text field to enter a short text answer
|
|
32
|
+
|
|
33
|
+
= f.show_if :category, 'Long Answer' do
|
|
34
|
+
= card('Long Answer') do
|
|
35
|
+
%p Display a textarea to enter a long text answer
|
|
36
|
+
|
|
37
|
+
= f.show_if :category, 'Date' do
|
|
38
|
+
= card('Date') do
|
|
39
|
+
%p Display a date field to enter a date
|
|
40
|
+
|
|
41
|
+
= f.show_if :category, 'Decimal' do
|
|
42
|
+
= card('Decimal') do
|
|
43
|
+
%p Display a decimal field to enter a decimal number
|
|
44
|
+
|
|
45
|
+
= f.show_if :category, 'Email' do
|
|
46
|
+
= card('Email') do
|
|
47
|
+
%p Display an email field to enter an email
|
|
48
|
+
|
|
49
|
+
= f.show_if :category, 'Number' do
|
|
50
|
+
= card('Number') do
|
|
51
|
+
%p Display a number field to enter an integer number
|
|
52
|
+
|
|
53
|
+
= f.show_if :category, 'Percentage' do
|
|
54
|
+
= card('Percentage') do
|
|
55
|
+
%p Display a percentage field to enter a percentage
|
|
56
|
+
|
|
57
|
+
= f.show_if :category, 'Price' do
|
|
58
|
+
= card('Price') do
|
|
59
|
+
%p Display a price field to enter a price
|
|
60
|
+
|
|
61
|
+
= f.show_if :category, 'Upload File' do
|
|
62
|
+
= card('Upload File') do
|
|
63
|
+
%p Display a file field to upload a file
|
|
64
|
+
|
|
65
|
+
= f.show_if_any :category, Effective::Question::WITH_OPTIONS_CATEGORIES do
|
|
66
|
+
.mt-3
|
|
67
|
+
= card do
|
|
68
|
+
%h5 Options
|
|
69
|
+
%p Display the following options:
|
|
70
|
+
|
|
71
|
+
= f.has_many :question_options, class: 'tight' do |fa|
|
|
72
|
+
.row
|
|
73
|
+
.col= fa.text_field :title, label: false
|
|
74
|
+
= f.show_if(:scored, true) do
|
|
75
|
+
.col.mr-3= fa.check_box :answer, label: "Correct answer"
|
|
76
|
+
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
= f.show_if(:scored, true) do
|
|
2
|
+
= f.fields_for :question_answers, f.object.question_answer do |fa|
|
|
3
|
+
= card('Answer') do
|
|
4
|
+
%p The answer to this question is:
|
|
5
|
+
|
|
6
|
+
= f.show_if :category, 'Choose one' do
|
|
7
|
+
%ul
|
|
8
|
+
%li One correct answer option as selected above.
|
|
9
|
+
|
|
10
|
+
= f.show_if :category, 'Select up to 1' do
|
|
11
|
+
%ul
|
|
12
|
+
%li One correct answer option as selected above.
|
|
13
|
+
|
|
14
|
+
= f.show_if :category, 'Select up to 2' do
|
|
15
|
+
%ul
|
|
16
|
+
%li Two correct answer options as selected above.
|
|
17
|
+
|
|
18
|
+
= f.show_if :category, 'Select up to 3' do
|
|
19
|
+
%ul
|
|
20
|
+
%li Three correct answer options as selected above.
|
|
21
|
+
|
|
22
|
+
= f.show_if :category, 'Select up to 4' do
|
|
23
|
+
%ul
|
|
24
|
+
%li Four correct answer options as selected above.
|
|
25
|
+
|
|
26
|
+
= f.show_if :category, 'Select up to 5' do
|
|
27
|
+
%ul
|
|
28
|
+
%li Five correct answer options as selected above.
|
|
29
|
+
|
|
30
|
+
= f.show_if :category, 'Select all that apply' do
|
|
31
|
+
%ul
|
|
32
|
+
%li All correct answer options as selected above.
|
|
33
|
+
|
|
34
|
+
= f.show_if :category, 'Short Answer' do
|
|
35
|
+
- operations = fa.object.operations('Short Answer')
|
|
36
|
+
= fa.radios :operation, operations, buttons: true, label: false
|
|
37
|
+
|
|
38
|
+
= fa.show_if(:operation, 'Equal to') do
|
|
39
|
+
= fa.text_field :short_answer
|
|
40
|
+
|
|
41
|
+
= fa.show_if_any(:operation, operations - ['Equal to']) do
|
|
42
|
+
= fa.text_field :short_answer, label: 'Matched text', hint: 'Does not support wildcards'
|
|
43
|
+
|
|
44
|
+
= f.show_if :category, 'Long Answer' do
|
|
45
|
+
- operations = fa.object.operations('Long Answer')
|
|
46
|
+
= fa.radios :operation, operations, buttons: true, label: false
|
|
47
|
+
|
|
48
|
+
= fa.show_if(:operation, 'Equal to') do
|
|
49
|
+
= fa.text_field :long_answer
|
|
50
|
+
|
|
51
|
+
= fa.show_if_any(:operation, operations - ['Equal to']) do
|
|
52
|
+
= fa.text_field :long_answer, label: 'Matched text', hint: 'Does not support wildcards'
|
|
53
|
+
|
|
54
|
+
= f.show_if :category, 'Date' do
|
|
55
|
+
- operations = fa.object.operations('Date')
|
|
56
|
+
|
|
57
|
+
= fa.radios :operation, operations, buttons: true, label: false
|
|
58
|
+
|
|
59
|
+
= fa.show_if_any(:operation, operations - ['Within range']) do
|
|
60
|
+
.d-flex= fa.date_field :date
|
|
61
|
+
|
|
62
|
+
= fa.show_if(:operation, 'Within range') do
|
|
63
|
+
.d-flex.align-items-center
|
|
64
|
+
= fa.date_field :date_begin
|
|
65
|
+
%span.mx-4 to
|
|
66
|
+
= fa.date_field :date_end
|
|
67
|
+
|
|
68
|
+
= f.show_if :category, 'Decimal' do
|
|
69
|
+
- operations = fa.object.operations('Decimal')
|
|
70
|
+
= fa.radios :operation, operations, buttons: true, label: false
|
|
71
|
+
|
|
72
|
+
= fa.show_if_any(:operation, operations - ['Within range']) do
|
|
73
|
+
.d-flex= fa.float_field :decimal
|
|
74
|
+
|
|
75
|
+
= fa.show_if(:operation, 'Within range') do
|
|
76
|
+
.d-flex.align-items-center
|
|
77
|
+
= fa.float_field :decimal_begin
|
|
78
|
+
%span.mx-4 to
|
|
79
|
+
= fa.float_field :decimal_end
|
|
80
|
+
|
|
81
|
+
= f.show_if :category, 'Email' do
|
|
82
|
+
- operations = fa.object.operations('Email')
|
|
83
|
+
= fa.radios :operation, operations, buttons: true, label: false
|
|
84
|
+
|
|
85
|
+
= fa.show_if(:operation, 'Equal to') do
|
|
86
|
+
.d-flex= fa.email_field :email
|
|
87
|
+
|
|
88
|
+
= fa.show_if_any(:operation, operations - ['Equal to']) do
|
|
89
|
+
.d-flex= fa.text_field :email, label: 'Text to match'
|
|
90
|
+
|
|
91
|
+
= f.show_if :category, 'Number' do
|
|
92
|
+
- operations = fa.object.operations('Number')
|
|
93
|
+
= fa.radios :operation, operations, buttons: true, label: false
|
|
94
|
+
|
|
95
|
+
= fa.show_if_any(:operation, operations - ['Within range']) do
|
|
96
|
+
.d-flex= fa.number_field :number
|
|
97
|
+
|
|
98
|
+
= fa.show_if(:operation, 'Within range') do
|
|
99
|
+
.d-flex.align-items-center
|
|
100
|
+
= fa.number_field :number_begin
|
|
101
|
+
%span.mx-4 to
|
|
102
|
+
= fa.number_field :number_end
|
|
103
|
+
|
|
104
|
+
= f.show_if :category, 'Percentage' do
|
|
105
|
+
- operations = fa.object.operations('Percentage')
|
|
106
|
+
= fa.radios :operation, operations, buttons: true, label: false
|
|
107
|
+
|
|
108
|
+
= fa.show_if_any(:operation, operations - ['Within range']) do
|
|
109
|
+
.d-flex= fa.percent_field :percentage
|
|
110
|
+
|
|
111
|
+
= fa.show_if(:operation, 'Within range') do
|
|
112
|
+
.d-flex.align-items-center
|
|
113
|
+
= fa.percent_field :percentage_begin
|
|
114
|
+
%span.mx-4 to
|
|
115
|
+
= fa.percent_field :percentage_end
|
|
116
|
+
|
|
117
|
+
= f.show_if :category, 'Price' do
|
|
118
|
+
- operations = fa.object.operations('Price')
|
|
119
|
+
= fa.radios :operation, operations, buttons: true, label: false
|
|
120
|
+
|
|
121
|
+
= fa.show_if_any(:operation, operations - ['Within range']) do
|
|
122
|
+
.d-flex= fa.price_field :price
|
|
123
|
+
|
|
124
|
+
= fa.show_if(:operation, 'Within range') do
|
|
125
|
+
.d-flex.align-items-center
|
|
126
|
+
= fa.price_field :price_begin
|
|
127
|
+
%span.mx-4 to
|
|
128
|
+
= fa.price_field :price_end
|
|
129
|
+
|
|
130
|
+
= f.show_if :category, 'Upload File' do
|
|
131
|
+
%p Automatic scoring is not supported for this question type.
|
|
@@ -28,94 +28,12 @@
|
|
|
28
28
|
= f.rich_text_area :body, label: 'Body (optional)'
|
|
29
29
|
|
|
30
30
|
= f.check_box :required, hint: 'A response to this question will be required'
|
|
31
|
-
= f.select :category, Effective::Question::CATEGORIES
|
|
32
|
-
|
|
33
|
-
= f.show_if :category, 'Choose one' do
|
|
34
|
-
.mt-3.card
|
|
35
|
-
.card-body
|
|
36
|
-
%h5 Choose one
|
|
37
|
-
%p Display radio buttons to select one option
|
|
38
|
-
|
|
39
|
-
= f.show_if :category, 'Select all that apply' do
|
|
40
|
-
.card
|
|
41
|
-
.card-body
|
|
42
|
-
%h5 Select all that apply
|
|
43
|
-
%p Display checkboxes to select all options that apply
|
|
44
|
-
|
|
45
|
-
= f.show_if :category, 'Select up to 1' do
|
|
46
|
-
.card
|
|
47
|
-
.card-body
|
|
48
|
-
%h5 Select up to 1 (one)
|
|
49
|
-
%p Display checkboxes to select up to 1 option
|
|
50
|
-
|
|
51
|
-
= f.show_if :category, 'Select up to 2' do
|
|
52
|
-
.card
|
|
53
|
-
.card-body
|
|
54
|
-
%h5 Select up to 2 (two)
|
|
55
|
-
%p Display checkboxes to select up to 2 options
|
|
56
|
-
|
|
57
|
-
= f.show_if :category, 'Select up to 3' do
|
|
58
|
-
.card
|
|
59
|
-
.card-body
|
|
60
|
-
%h5 Select up to 3 (three)
|
|
61
|
-
%p Display checkboxes to select up to 3 options
|
|
62
|
-
|
|
63
|
-
= f.show_if :category, 'Select up to 4' do
|
|
64
|
-
.card
|
|
65
|
-
.card-body
|
|
66
|
-
%h5 Select up to 4 (four)
|
|
67
|
-
%p Display checkboxes to select up to 4 options
|
|
68
|
-
|
|
69
|
-
= f.show_if :category, 'Select up to 5' do
|
|
70
|
-
.card
|
|
71
|
-
.card-body
|
|
72
|
-
%h5 Select up to 5 (five)
|
|
73
|
-
%p Display checkboxes to select up to 5 options
|
|
74
31
|
|
|
75
|
-
= f.
|
|
76
|
-
|
|
77
|
-
.card-body
|
|
78
|
-
%h5 Short Answer
|
|
79
|
-
%p Display a text field to enter a short text answer
|
|
80
|
-
|
|
81
|
-
= f.show_if :category, 'Long Answer' do
|
|
82
|
-
.card
|
|
83
|
-
.card-body
|
|
84
|
-
%h5 Long Answer
|
|
85
|
-
%p Display a textarea to enter a long text answer
|
|
86
|
-
|
|
87
|
-
= f.show_if :category, 'Date' do
|
|
88
|
-
.card
|
|
89
|
-
.card-body
|
|
90
|
-
%h5 Date
|
|
91
|
-
%p Display a date field to enter a date
|
|
92
|
-
|
|
93
|
-
= f.show_if :category, 'Email' do
|
|
94
|
-
.card
|
|
95
|
-
.card-body
|
|
96
|
-
%h5 Email
|
|
97
|
-
%p Display an email field to enter an email
|
|
98
|
-
|
|
99
|
-
= f.show_if :category, 'Number' do
|
|
100
|
-
.card
|
|
101
|
-
.card-body
|
|
102
|
-
%h5 Number
|
|
103
|
-
%p Display a number field to enter an integer number
|
|
104
|
-
|
|
105
|
-
= f.show_if :category, 'Upload File' do
|
|
106
|
-
.card
|
|
107
|
-
.card-body
|
|
108
|
-
%h5 Upload File
|
|
109
|
-
%p Display a file field to upload a file
|
|
110
|
-
|
|
111
|
-
= f.show_if_any :category, Effective::Question::WITH_OPTIONS_CATEGORIES do
|
|
112
|
-
.mt-3.card
|
|
113
|
-
.card-body
|
|
114
|
-
%h5 Options
|
|
115
|
-
%p Display the following options:
|
|
32
|
+
= f.select :category, Effective::Question::CATEGORIES
|
|
33
|
+
.mt-3= render('admin/questions/fields_category', f: f)
|
|
116
34
|
|
|
117
|
-
|
|
118
|
-
|
|
35
|
+
= f.check_box :scored, hint: 'Any responses to this question will be automatically scored'
|
|
36
|
+
.mt-3= render('admin/questions/fields_scored', f: f)
|
|
119
37
|
|
|
120
38
|
= effective_submit(f)
|
|
121
39
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
= response.response.presence || '-'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
= price_to_currency(response.response) || '-'
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: effective_questions
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Code and Effect
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-02-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -183,9 +183,12 @@ files:
|
|
|
183
183
|
- app/models/concerns/acts_as_questionable.rb
|
|
184
184
|
- app/models/concerns/acts_as_responsable.rb
|
|
185
185
|
- app/models/effective/question.rb
|
|
186
|
+
- app/models/effective/question_answer.rb
|
|
186
187
|
- app/models/effective/question_option.rb
|
|
187
188
|
- app/models/effective/response.rb
|
|
188
189
|
- app/models/effective/response_option.rb
|
|
190
|
+
- app/views/admin/questions/_fields_category.html.haml
|
|
191
|
+
- app/views/admin/questions/_fields_scored.html.haml
|
|
189
192
|
- app/views/admin/questions/_form.html.haml
|
|
190
193
|
- app/views/effective/questionable/_result.html.haml
|
|
191
194
|
- app/views/effective/questionable/_results.html.haml
|
|
@@ -210,9 +213,12 @@ files:
|
|
|
210
213
|
- app/views/effective/responses/_response.html.haml
|
|
211
214
|
- app/views/effective/responses/fields/_choose_one.html.haml
|
|
212
215
|
- app/views/effective/responses/fields/_date.html.haml
|
|
216
|
+
- app/views/effective/responses/fields/_decimal.html.haml
|
|
213
217
|
- app/views/effective/responses/fields/_email.html.haml
|
|
214
218
|
- app/views/effective/responses/fields/_long_answer.html.haml
|
|
215
219
|
- app/views/effective/responses/fields/_number.html.haml
|
|
220
|
+
- app/views/effective/responses/fields/_percentage.html.haml
|
|
221
|
+
- app/views/effective/responses/fields/_price.html.haml
|
|
216
222
|
- app/views/effective/responses/fields/_select_all_that_apply.html.haml
|
|
217
223
|
- app/views/effective/responses/fields/_select_up_to_1.html.haml
|
|
218
224
|
- app/views/effective/responses/fields/_select_up_to_2.html.haml
|
|
@@ -223,9 +229,12 @@ files:
|
|
|
223
229
|
- app/views/effective/responses/fields/_upload_file.html.haml
|
|
224
230
|
- app/views/effective/responses/responses/_choose_one.html.haml
|
|
225
231
|
- app/views/effective/responses/responses/_date.html.haml
|
|
232
|
+
- app/views/effective/responses/responses/_decimal.html.haml
|
|
226
233
|
- app/views/effective/responses/responses/_email.html.haml
|
|
227
234
|
- app/views/effective/responses/responses/_long_answer.html.haml
|
|
228
235
|
- app/views/effective/responses/responses/_number.html.haml
|
|
236
|
+
- app/views/effective/responses/responses/_percentage.html.haml
|
|
237
|
+
- app/views/effective/responses/responses/_price.html.haml
|
|
229
238
|
- app/views/effective/responses/responses/_select_all_that_apply.html.haml
|
|
230
239
|
- app/views/effective/responses/responses/_select_up_to_1.html.haml
|
|
231
240
|
- app/views/effective/responses/responses/_select_up_to_2.html.haml
|