effective_questions 0.0.3 → 0.0.4

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: 816f974b650f646dd7d6028380c57a6471e696aee74c272c051bc6b336c42663
4
- data.tar.gz: c23ff2b377c70a697f0d5ed1b79d53ce4ba9dd1f8514e9e8fa72dcfdf82967f4
3
+ metadata.gz: 7b1be795579cdb74c263712b86c74f5b5fe34e2e1d87fbbb57fc8cef6fc07510
4
+ data.tar.gz: 1acfbae757e1852f801024ee6679a9d9c1661cd8fd3f5e4c37d24807dad2b0f4
5
5
  SHA512:
6
- metadata.gz: b68de6ae22c33bf0eef91f13f32d9af7ddf5fb76753cb4dfb1006d3eaf10d24e7cb0eec9861c2b039b2520620774f85e1fccf5843d6fe5587ded03bef74d8446
7
- data.tar.gz: 386055e8ab8f4f66f4a6824fcccf0bda99d833f58f8d74c466abc37040c3667d80671bf07c8c1d294f8ceff00a23d0dc591bb9f3838f15a53b06796376722c7e
6
+ metadata.gz: 16c8a5452a08adf9fe820e809c5a5847a460f01040a3356b1dc8448b50f9e268e3ea4e18a747fa842550898c5856cf44081fe86fb19763dc146af3f65bb76cdb
7
+ data.tar.gz: 7cddf83480381e353009cbbf263c3fc7c9c902e0d9c447c3ef7125d61d1dde8d76cc89a27ddd87ba3204726d4ad1fc43873f4c703bc728bef563d5a4dfe6bd93
@@ -0,0 +1,13 @@
1
+ module Admin
2
+ class ResponsesController < ApplicationController
3
+ before_action(:authenticate_user!) if defined?(Devise)
4
+ before_action { EffectiveResources.authorize!(self, :admin, :effective_questions) }
5
+
6
+ include Effective::CrudController
7
+
8
+ def permitted_params
9
+ params.require(:effective_response).permit!
10
+ end
11
+
12
+ end
13
+ end
@@ -17,7 +17,9 @@ class Admin::EffectiveQuestionsDatatable < Effective::Datatable
17
17
  end
18
18
 
19
19
  col :title
20
- col :body, as: :text, visible: !attributes[:follow_up]
20
+ col :task
21
+ col :points
22
+ col :body, as: :text, visible: false
21
23
  col :required
22
24
 
23
25
  col :category, label: 'Type'
@@ -0,0 +1,24 @@
1
+ class Admin::EffectiveResponsesDatatable < Effective::Datatable
2
+ datatable do
3
+ col :updated_at, visible: false
4
+ col :created_at, visible: false
5
+ col :id, visible: false
6
+
7
+ col :responsable
8
+ col :questionable
9
+ col :question
10
+
11
+ col(:response) do |response|
12
+ render('effective/responses/response', response: response)
13
+ end
14
+
15
+ col :correct?, as: :boolean
16
+
17
+ actions_col
18
+ end
19
+
20
+ collection do
21
+ Effective::Response.all.deep
22
+ end
23
+
24
+ end
@@ -27,4 +27,8 @@ module ActsAsQuestionable
27
27
  responses.select { |response| response.completed? && response.question_id == question.id }
28
28
  end
29
29
 
30
+ def questionable_scored?
31
+ questions.any?(&:scored?)
32
+ end
33
+
30
34
  end
@@ -27,4 +27,18 @@ module ActsAsResponsable
27
27
  response ||= responses.build(questionable: question.questionable, question: question)
28
28
  end
29
29
 
30
+ def responsable_completed?
31
+ try(:done?) == true || try(:completed?) == true
32
+ end
33
+
34
+ def scored_responses
35
+ responses.select { |r| r.question&.scored? }
36
+ end
37
+
38
+ def responsable_score
39
+ scored = scored_responses()
40
+ return 0 unless scored.present?
41
+
42
+ ((scored.count(&:correct?).to_f / scored.length) * 100_000).round
43
+ end
30
44
  end
@@ -60,6 +60,9 @@ module Effective
60
60
 
61
61
  scored :boolean
62
62
 
63
+ task :string
64
+ points :decimal
65
+
63
66
  timestamps
64
67
  end
65
68
 
@@ -147,11 +147,11 @@ module Effective
147
147
  return short_answer if question.short_answer?
148
148
  end
149
149
 
150
- raise('unknown operation: #{operation}')
150
+ raise("unknown operation: #{operation}")
151
151
  end
152
152
 
153
153
  def equals?
154
- operation == 'Equals'
154
+ operation == 'Equal to'
155
155
  end
156
156
 
157
157
  def contains?
@@ -163,7 +163,7 @@ module Effective
163
163
  end
164
164
 
165
165
  def within_range?
166
- operation == 'Within Range'
166
+ operation == 'Within range'
167
167
  end
168
168
 
169
169
  def lt?
@@ -59,7 +59,24 @@ module Effective
59
59
  length: { maximum: 5, message: 'please select 5 options or fewer' }
60
60
 
61
61
  def to_s
62
- model_name.human || 'Response'
62
+ return '' unless question.present?
63
+ return '' unless response.present?
64
+
65
+ case question.category
66
+ when 'Date'
67
+ response.strftime('%Y-%m-%d')
68
+ when 'Price'
69
+ "$#{'%0.2f' % (response / 100.0)}"
70
+ when 'Percentage'
71
+ precision = (response % 1000).zero? ? 0 : 3
72
+ "#{format("%.#{precision}f", response.to_f / 1000)}%"
73
+ when 'Upload File'
74
+ response.filename.to_s
75
+ when 'Select all that apply', 'Select up to 2', 'Select up to 3', 'Select up to 4', 'Select up to 5'
76
+ Array(response).map(&:to_s).join(', ')
77
+ else
78
+ response.to_s
79
+ end
63
80
  end
64
81
 
65
82
  def response
@@ -75,9 +92,9 @@ module Effective
75
92
  return short_answer if question.short_answer?
76
93
  return upload_file if question.upload_file?
77
94
 
78
- return question_options.first if question.choose_one?
79
- return question_options.first if question.select_up_to_1?
80
- return question_options if question.question_option?
95
+ return response_options.first if question.choose_one?
96
+ return response_options.first if question.select_up_to_1?
97
+ return response_options if question.question_option?
81
98
 
82
99
  raise('unknown response for unexpected question category')
83
100
  end
@@ -87,7 +104,54 @@ module Effective
87
104
  end
88
105
 
89
106
  def completed?
90
- responsable.try(:completed?) == true
107
+ responsable.responsable_completed?
108
+ end
109
+
110
+ def correct?
111
+ return unless question.present? && question.scored?
112
+ return false unless response.present?
113
+
114
+ if question.question_option?
115
+ # For option-based questions, check if selected options match answer options
116
+ answers = question.answer.map(&:id)
117
+ selected = Array(response).map(&:question_option_id)
118
+
119
+ if question.select_all_that_apply?
120
+ return selected.sort == answers.sort
121
+ else
122
+ # For choose_one or select_up_to_X, all selected must be correct
123
+ return selected.present? && (selected - answers).blank?
124
+ end
125
+ end
126
+
127
+ # For non-option questions, check against question_answer
128
+ answer = question.question_answer
129
+ return false unless answer.present?
130
+
131
+ case answer.operation
132
+ when 'Equal to'
133
+ if response.is_a?(String) && answer.answer.is_a?(String)
134
+ response.downcase.strip == answer.answer.downcase.strip
135
+ else
136
+ response == answer.answer
137
+ end
138
+ when 'Within range'
139
+ answer.answer.cover?(response)
140
+ when 'Less than'
141
+ response < answer.answer
142
+ when 'Less than or equal to'
143
+ response <= answer.answer
144
+ when 'Greater than'
145
+ response > answer.answer
146
+ when 'Greater than or equal to'
147
+ response >= answer.answer
148
+ when 'Contains'
149
+ response.to_s.downcase.include?(answer.answer.to_s.downcase)
150
+ when 'Does not contain'
151
+ !response.to_s.downcase.include?(answer.answer.to_s.downcase)
152
+ else
153
+ false
154
+ end
91
155
  end
92
156
 
93
157
  end
@@ -2,5 +2,10 @@ module Effective
2
2
  class ResponseOption < ActiveRecord::Base
3
3
  belongs_to :response
4
4
  belongs_to :question_option
5
+
6
+ def to_s
7
+ question_option&.to_s
8
+ end
9
+
5
10
  end
6
11
  end
@@ -21,6 +21,9 @@
21
21
  = f.text_field :follow_up_value, label: 'Answer required to display this question', required: true, hint: 'Enter the exact answer value that should trigger this question to appear'
22
22
 
23
23
  = f.text_field :title, label: 'Question Title'
24
+ .row
25
+ .col= f.text_field :task, hint: 'An optional task name or number used in exports'
26
+ .col= f.number_field :points, hint: 'An optional number of points awarded for this question'
24
27
 
25
28
  - if defined?(EffectiveArticleEditor)
26
29
  = f.article_editor :body, label: 'Body (optional)'
@@ -1,6 +1,6 @@
1
1
  - (question.question_options + [nil]).each do |question_option|
2
2
  - selected = responses.select { |br| br.response.blank? } if question_option.blank?
3
- - selected ||= responses.select { |br| Array(br.response).include?(question_option) }
3
+ - selected ||= responses.select { |br| Array(br.response).any? { |ro| ro.question_option == question_option } }
4
4
 
5
5
  - count = selected.length
6
6
  - total = responses.length
@@ -1,10 +1,14 @@
1
1
  - # The individiaual responses for one responsable resource
2
+ - include_scores = (local_assigns[:include_scores] == true)
3
+
2
4
  .responsable-results
3
5
  %table.table.table-hover
4
6
  %thead
5
7
  %tr
6
8
  %th Question
7
9
  %th Response
10
+ - if include_scores
11
+ %th Scored Result
8
12
 
9
13
  %tbody
10
14
  - questionable.questions.top_level.deep.each do |question|
@@ -14,6 +18,17 @@
14
18
  %td= question
15
19
  %td= render('effective/responses/response', response: response)
16
20
 
21
+ - if include_scores
22
+ %td
23
+ - if question.scored?
24
+ %span{title: "Correct answer was #{question.answer_to_s}"}
25
+ - if response.correct?
26
+ = badge('Correct', 'badge-success')
27
+ - else
28
+ = badge('Incorrect', 'badge-danger')
29
+ - else
30
+ = '-'
31
+
17
32
  - question.follow_up_questions.deep.each do |follow_up_question|
18
33
  - response = responsable.response(question: follow_up_question)
19
34
 
@@ -21,3 +36,14 @@
21
36
  %tr
22
37
  %td= follow_up_question
23
38
  %td= render('effective/responses/response', response: response)
39
+
40
+ - if include_scores
41
+ %td
42
+ - if follow_up_question.scored?
43
+ %span{title: "Correct answer was #{follow_up_question.answer_to_s}"}
44
+ - if response.correct?
45
+ = badge('Correct', 'badge-success')
46
+ - else
47
+ = badge('Incorrect', 'badge-danger')
48
+ - else
49
+ = '-'
@@ -1 +1 @@
1
- = response.response.presence || '-'
1
+ = response.response&.question_option || '-'
@@ -1,5 +1,5 @@
1
- - response.response.each do |question_option|
2
- %div= question_option.title
1
+ - response.response.each do |response_option|
2
+ %div= response_option
3
3
 
4
4
  - if response.response.blank?
5
5
  = '-'
@@ -1 +1 @@
1
- = response.response&.title || '-'
1
+ = response.response || '-'
@@ -1,5 +1,5 @@
1
- - response.response.each do |question_option|
2
- %div= question_option.title
1
+ - response.response.each do |response_option|
2
+ %div= response_option
3
3
 
4
4
  - if response.response.blank?
5
5
  = '-'
@@ -1,5 +1,5 @@
1
- - response.response.each do |question_option|
2
- %div= question_option.title
1
+ - response.response.each do |response_option|
2
+ %div= response_option
3
3
 
4
4
  - if response.response.blank?
5
5
  = '-'
@@ -1,5 +1,5 @@
1
- - response.response.each do |question_option|
2
- %div= question_option.title
1
+ - response.response.each do |response_option|
2
+ %div= response_option
3
3
 
4
4
  - if response.response.blank?
5
5
  = '-'
@@ -1,5 +1,5 @@
1
- - response.response.each do |question_option|
2
- %div= question_option.title
1
+ - response.response.each do |response_option|
2
+ %div= response_option
3
3
 
4
4
  - if response.response.blank?
5
5
  = '-'
data/config/routes.rb CHANGED
@@ -8,6 +8,7 @@ EffectiveQuestions::Engine.routes.draw do
8
8
 
9
9
  namespace :admin do
10
10
  resources :questions, except: [:show]
11
+ resources :responses, only: [:index]
11
12
  end
12
13
 
13
14
  end
@@ -16,6 +16,11 @@ class CreateEffectiveQuestions < ActiveRecord::Migration[6.0]
16
16
  t.integer :question_id
17
17
  t.integer :question_option_id
18
18
 
19
+ t.boolean :scored, default: false
20
+
21
+ t.string :task
22
+ t.decimal :points
23
+
19
24
  t.datetime :updated_at
20
25
  t.datetime :created_at
21
26
  end
@@ -26,6 +31,8 @@ class CreateEffectiveQuestions < ActiveRecord::Migration[6.0]
26
31
  t.string :title
27
32
  t.integer :position
28
33
 
34
+ t.boolean :answer, default: false
35
+
29
36
  t.datetime :updated_at
30
37
  t.datetime :created_at
31
38
  end
@@ -59,5 +66,39 @@ class CreateEffectiveQuestions < ActiveRecord::Migration[6.0]
59
66
  t.datetime :updated_at
60
67
  t.datetime :created_at
61
68
  end
69
+
70
+ create_table :question_answers do |t|
71
+ t.integer :question_id
72
+
73
+ t.string :operation
74
+
75
+ t.date :date
76
+ t.date :date_begin
77
+ t.date :date_end
78
+
79
+ t.decimal :decimal
80
+ t.decimal :decimal_begin
81
+ t.decimal :decimal_end
82
+
83
+ t.string :email
84
+
85
+ t.integer :number
86
+ t.integer :number_begin
87
+ t.integer :number_end
88
+
89
+ t.integer :percentage
90
+ t.integer :percentage_begin
91
+ t.integer :percentage_end
92
+
93
+ t.integer :price
94
+ t.integer :price_begin
95
+ t.integer :price_end
96
+
97
+ t.text :long_answer
98
+ t.text :short_answer
99
+
100
+ t.timestamps
101
+ end
102
+
62
103
  end
63
104
  end
@@ -1,3 +1,3 @@
1
1
  module EffectiveQuestions
2
- VERSION = '0.0.3'
2
+ VERSION = '0.0.4'
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.3
4
+ version: 0.0.4
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-02-02 00:00:00.000000000 Z
11
+ date: 2026-02-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -177,8 +177,10 @@ files:
177
177
  - app/assets/config/effective_questions_manifest.js
178
178
  - app/assets/stylesheets/effective_questions.scss
179
179
  - app/controllers/admin/questions_controller.rb
180
+ - app/controllers/admin/responses_controller.rb
180
181
  - app/datatables/admin/effective_questionable_results_datatable.rb
181
182
  - app/datatables/admin/effective_questions_datatable.rb
183
+ - app/datatables/admin/effective_responses_datatable.rb
182
184
  - app/helpers/effective_questions_helper.rb
183
185
  - app/models/concerns/acts_as_questionable.rb
184
186
  - app/models/concerns/acts_as_responsable.rb