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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d948f3c70d98667858558ab90fc1da83bb053636c058c3684dad93f7ccdab507
4
- data.tar.gz: 479a4faf525f5d1f31a5fcc4d2039b0d23783eb736d1cc0338a711316e7cf3bb
3
+ metadata.gz: 816f974b650f646dd7d6028380c57a6471e696aee74c272c051bc6b336c42663
4
+ data.tar.gz: c23ff2b377c70a697f0d5ed1b79d53ce4ba9dd1f8514e9e8fa72dcfdf82967f4
5
5
  SHA512:
6
- metadata.gz: af99111296b23031caa4325bc8fe7a85460d54defc6344efb7627b65d5f9d55e4f60909d9d10cdece24b041c1aad36155e03759f2f77bc6949c5a8624eb6936d
7
- data.tar.gz: 65db1d73024c9a9457da21febc75789578e7ca28a4efd607aabd443692a017536e512e7455fc7ffabf46221d1bff6671c9a76712df78a97800d8ceb97810919f
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: false do |question|
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: :all_blank, allow_destroy: true
10
+ accepts_nested_attributes_for :question_options, reject_if: -> (atts) { atts['title'].blank? }, allow_destroy: true
10
11
 
11
- has_many :follow_up_questions, -> { order(:position) }, class_name: 'Effective::Question', foreign_key: :question_id, dependent: :destroy
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
@@ -6,6 +6,8 @@ module Effective
6
6
  title :text
7
7
  position :integer
8
8
 
9
+ answer :boolean
10
+
9
11
  timestamps
10
12
  end
11
13
 
@@ -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
- #model_name.human
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
- return false if responsable.blank?
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.show_if :category, 'Short Answer' do
76
- .card
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
- = f.has_many :question_options, class: 'tight' do |fa|
118
- = fa.text_field :title, label: false
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
 
@@ -1,4 +1,4 @@
1
- - # The individiaual responses for one resonsable resource
1
+ - # The individiaual responses for one responsable resource
2
2
  .responsable-results
3
3
  %table.table.table-hover
4
4
  %thead
@@ -0,0 +1,2 @@
1
+ .row
2
+ .col-sm-6= f.float_field :decimal, label: false, required: question.required?
@@ -0,0 +1,2 @@
1
+ .row
2
+ .col-sm-6= f.percent_field :percentage, label: false, required: question.required?
@@ -0,0 +1,2 @@
1
+ .row
2
+ .col-sm-6= f.price_field :price, label: false, required: question.required?
@@ -0,0 +1 @@
1
+ = response.response.presence || '-'
@@ -0,0 +1,5 @@
1
+ - if response.response.present?
2
+ - precision = (response.response % 1000).zero? ? 0 : 3
3
+ = number_to_percentage(response.response / 1000.0, precision: precision)
4
+ - else
5
+ = '-'
@@ -0,0 +1 @@
1
+ = price_to_currency(response.response) || '-'
@@ -44,6 +44,9 @@ class CreateEffectiveQuestions < ActiveRecord::Migration[6.0]
44
44
  t.integer :number
45
45
  t.text :long_answer
46
46
  t.text :short_answer
47
+ t.decimal :decimal
48
+ t.integer :percentage
49
+ t.integer :price
47
50
 
48
51
  t.datetime :updated_at
49
52
  t.datetime :created_at
@@ -1,3 +1,3 @@
1
1
  module EffectiveQuestions
2
- VERSION = '0.0.2'
2
+ VERSION = '0.0.3'
3
3
  end
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.2
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-01-29 00:00:00.000000000 Z
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