effective_polls 0.7.2 → 0.8.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.
- checksums.yaml +4 -4
- data/app/controllers/effective/ballots_controller.rb +1 -1
- data/app/datatables/admin/effective_poll_questions_datatable.rb +21 -4
- data/app/datatables/admin/effective_poll_results_datatable.rb +1 -1
- data/app/models/concerns/effective_polls_user.rb +16 -0
- data/app/models/effective/ballot.rb +1 -1
- data/app/models/effective/poll_question.rb +47 -2
- data/app/views/admin/poll_questions/_form.html.haml +30 -5
- data/app/views/effective/ballot_responses/fields/_upload_file.html.haml +2 -1
- data/app/views/effective/ballots/_ballot.html.haml +9 -1
- data/app/views/effective/ballots/vote.html.haml +12 -1
- data/app/views/effective/poll_results/_results.html.haml +14 -2
- data/db/migrate/101_create_effective_polls.rb +6 -0
- data/lib/effective_polls/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1893c50235d32c084e4edda3e02512dc4d3dda12fe78c0950912bd445b290d95
|
4
|
+
data.tar.gz: 68a4ec6af9aba679d144bceb777fbecc4669216369ad5f1bb25d1f4d9e6284af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 542819abd880bf6f6be5644a6d87339e2aef874883017bb354d19a23bcc7b12b89c24c9af9fc60fd0d8d66a2bd1ef4843cbb6e84adce314d4cd72f0fc610324f
|
7
|
+
data.tar.gz: aecfe705c0a2000edfa42932a9fc8a415086e6c9d95c6ebbf12080958808cf501fc40deb2db477251ee44c4c5d0da0935b1dc003ef231bac586fe20d96830c3d
|
@@ -56,7 +56,7 @@ module Effective
|
|
56
56
|
when :vote
|
57
57
|
params.require(:effective_ballot).permit(:current_step, ballot_responses_attributes: [
|
58
58
|
:id, :poll_id, :poll_question_id,
|
59
|
-
:date, :email, :number, :long_answer, :short_answer, :upload_file,
|
59
|
+
:date, :email, :number, :long_answer, :short_answer, :upload_file, :_destroy,
|
60
60
|
:poll_question_option_ids, poll_question_option_ids: []
|
61
61
|
])
|
62
62
|
when :submit
|
@@ -6,23 +6,40 @@ class Admin::EffectivePollQuestionsDatatable < Effective::Datatable
|
|
6
6
|
col :created_at, visible: false
|
7
7
|
col :id, visible: false
|
8
8
|
|
9
|
-
|
9
|
+
if attributes[:follow_up]
|
10
|
+
col :show_if_value_to_s, label: 'When answered with'
|
11
|
+
else
|
12
|
+
col :poll
|
13
|
+
end
|
10
14
|
|
11
|
-
col :position do |poll_question|
|
15
|
+
col :position, visible: false do |poll_question|
|
12
16
|
poll_question.position.to_i + 1
|
13
17
|
end
|
14
18
|
|
15
19
|
col :title
|
16
|
-
col :body, as: :text
|
20
|
+
col :body, as: :text, visible: !attributes[:follow_up]
|
17
21
|
col :required
|
18
22
|
|
19
23
|
col :category, label: 'Type'
|
20
24
|
col :poll_question_options, label: 'Options'
|
21
25
|
|
26
|
+
unless attributes[:follow_up]
|
27
|
+
col :follow_up_poll_questions, action: false, label: 'Follow up questions'
|
28
|
+
end
|
29
|
+
|
22
30
|
actions_col
|
23
31
|
end
|
24
32
|
|
25
33
|
collection do
|
26
|
-
Effective::PollQuestion.all.deep
|
34
|
+
scope = Effective::PollQuestion.all.deep
|
35
|
+
|
36
|
+
if attributes[:follow_up]
|
37
|
+
scope = scope.where(follow_up: true, poll_question_id: attributes[:poll_question_id])
|
38
|
+
else
|
39
|
+
scope = scope.where(poll_question_id: nil)
|
40
|
+
end
|
41
|
+
|
42
|
+
scope
|
27
43
|
end
|
44
|
+
|
28
45
|
end
|
@@ -5,7 +5,7 @@ class Admin::EffectivePollResultsDatatable < Effective::Datatable
|
|
5
5
|
col :position, visible: false
|
6
6
|
col :category, search: Effective::PollQuestion::CATEGORIES, visible: false
|
7
7
|
|
8
|
-
col :question, search: poll.poll_questions.pluck(:title)
|
8
|
+
col :question, search: poll.poll_questions.pluck(:title).sort
|
9
9
|
col :responses
|
10
10
|
end
|
11
11
|
|
@@ -55,6 +55,16 @@ module EffectivePollsUser
|
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
+
if self.class.try(:effective_mentorships_user?)
|
59
|
+
Effective::MentorshipCycle.sorted.each do |cycle|
|
60
|
+
scopes += [
|
61
|
+
["All grouped participants in #{cycle}", "mentorships_with_groups_id_#{cycle.id}"],
|
62
|
+
["All grouped mentors in #{cycle}", "mentorships_mentors_with_groups_id_#{cycle.id}"],
|
63
|
+
["All grouped mentees in #{cycle}", "mentorships_mentees_with_groups_id_#{cycle.id}"],
|
64
|
+
]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
58
68
|
scopes
|
59
69
|
end
|
60
70
|
|
@@ -72,6 +82,12 @@ module EffectivePollsUser
|
|
72
82
|
return collection.with_committee(Effective::Committee.find_by_id(id))
|
73
83
|
when :with_role
|
74
84
|
return collection.with_role(id)
|
85
|
+
when :mentorships_with_groups
|
86
|
+
return collection.mentorships_with_groups(Effective::MentorshipCycle.find_by_id(id))
|
87
|
+
when :mentorships_mentors_with_groups
|
88
|
+
return collection.mentorships_mentors_with_groups(Effective::MentorshipCycle.find_by_id(id))
|
89
|
+
when :mentorships_mentees_with_groups
|
90
|
+
return collection.mentorships_mentees_with_groups(Effective::MentorshipCycle.find_by_id(id))
|
75
91
|
end
|
76
92
|
|
77
93
|
# Otherwise we don't know what this scope is
|
@@ -2,9 +2,14 @@ module Effective
|
|
2
2
|
class PollQuestion < ActiveRecord::Base
|
3
3
|
belongs_to :poll
|
4
4
|
|
5
|
+
belongs_to :poll_question, optional: true # Present when I'm a follow up question
|
6
|
+
belongs_to :poll_question_option, optional: true # Might be present when I'm a follow up question
|
7
|
+
|
5
8
|
has_many :poll_question_options, -> { order(:position) }, inverse_of: :poll_question, dependent: :delete_all
|
6
9
|
accepts_nested_attributes_for :poll_question_options, reject_if: :all_blank, allow_destroy: true
|
7
10
|
|
11
|
+
has_many :follow_up_poll_questions, -> { order(:position) }, class_name: 'Effective::PollQuestion', foreign_key: :poll_question_id, dependent: :destroy
|
12
|
+
|
8
13
|
has_rich_text :body
|
9
14
|
log_changes(to: :poll) if respond_to?(:log_changes)
|
10
15
|
|
@@ -34,6 +39,8 @@ module Effective
|
|
34
39
|
'Select up to 5'
|
35
40
|
]
|
36
41
|
|
42
|
+
UNSUPPORTED_FOLLOW_UP_QUESTION_CATEGORIES = ['Upload File']
|
43
|
+
|
37
44
|
effective_resource do
|
38
45
|
title :string
|
39
46
|
category :string
|
@@ -41,21 +48,37 @@ module Effective
|
|
41
48
|
|
42
49
|
position :integer
|
43
50
|
|
51
|
+
follow_up :boolean
|
52
|
+
follow_up_value :string
|
53
|
+
|
44
54
|
timestamps
|
45
55
|
end
|
46
56
|
|
47
|
-
before_validation(if: -> {
|
48
|
-
|
57
|
+
before_validation(if: -> { poll_question.present? }) do
|
58
|
+
assign_attributes(poll: poll_question.poll)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Set position
|
62
|
+
before_validation do
|
63
|
+
source = poll_question_option.try(:follow_up_poll_questions) || poll.try(:poll_questions) || []
|
64
|
+
self.position = (source.map { |obj| obj.position }.compact.max || -1) + 1
|
49
65
|
end
|
50
66
|
|
51
67
|
scope :deep, -> { with_rich_text_body_and_embeds.includes(:poll_question_options) }
|
52
68
|
scope :sorted, -> { order(:position) }
|
53
69
|
|
70
|
+
scope :top_level, -> { where(follow_up: false) }
|
71
|
+
scope :follow_up, -> { where(follow_up: true) }
|
72
|
+
|
54
73
|
validates :title, presence: true
|
55
74
|
validates :category, presence: true, inclusion: { in: CATEGORIES }
|
56
75
|
validates :position, presence: true
|
57
76
|
validates :poll_question_options, presence: true, if: -> { poll_question_option? }
|
58
77
|
|
78
|
+
validates :poll_question, presence: true, if: -> { follow_up? }
|
79
|
+
validates :poll_question_option, presence: true, if: -> { follow_up? && poll_question.try(:poll_question_option?) }
|
80
|
+
validates :follow_up_value, presence: true, if: -> { follow_up? && !poll_question.try(:poll_question_option?) }
|
81
|
+
|
59
82
|
# Create choose_one? and select_all_that_apply? methods for each category
|
60
83
|
CATEGORIES.each do |category|
|
61
84
|
define_method(category.parameterize.underscore + '?') { self.category == category }
|
@@ -65,6 +88,28 @@ module Effective
|
|
65
88
|
title.presence || model_name.human
|
66
89
|
end
|
67
90
|
|
91
|
+
def show_if_attribute
|
92
|
+
return :poll_question_option_ids if poll_question_option?
|
93
|
+
|
94
|
+
case category
|
95
|
+
when 'Date' then :date
|
96
|
+
when 'Email' then :email
|
97
|
+
when 'Number' then :number
|
98
|
+
when 'Long Answer' then :long_answer
|
99
|
+
when 'Short Answer' then :short_answer
|
100
|
+
when 'Upload File' then :upload_file
|
101
|
+
else :unknown
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def show_if_value
|
106
|
+
poll_question.try(:poll_question_option?) ? poll_question_option_id : follow_up_value
|
107
|
+
end
|
108
|
+
|
109
|
+
def show_if_value_to_s
|
110
|
+
(poll_question.try(:poll_question_option?) ? poll_question_option : follow_up_value).to_s
|
111
|
+
end
|
112
|
+
|
68
113
|
def poll_question_option?
|
69
114
|
WITH_OPTIONS_CATEGORIES.include?(category)
|
70
115
|
end
|
@@ -4,6 +4,21 @@
|
|
4
4
|
- else
|
5
5
|
= f.select :poll_id, Effective::Poll.all
|
6
6
|
|
7
|
+
-# Follow up
|
8
|
+
- if inline_datatable? && (original = f.object.poll_question).present?
|
9
|
+
= f.hidden_field :follow_up, value: true
|
10
|
+
= f.hidden_field :poll_question_id
|
11
|
+
|
12
|
+
.mb-3.card
|
13
|
+
.card-body
|
14
|
+
%h5 Follow up Question
|
15
|
+
%p.text-muted This question will only appear when they answer the above question with the following value:
|
16
|
+
|
17
|
+
- if original.poll_question_option?
|
18
|
+
= f.select :poll_question_option_id, original.poll_question_options, label: 'Answer required to display this question'
|
19
|
+
- else
|
20
|
+
= 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'
|
21
|
+
|
7
22
|
= f.text_field :title, label: 'Question Title'
|
8
23
|
|
9
24
|
- if defined?(EffectiveArticleEditor)
|
@@ -17,11 +32,8 @@
|
|
17
32
|
= f.show_if :category, 'Choose one' do
|
18
33
|
.mt-3.card
|
19
34
|
.card-body
|
20
|
-
%h5
|
21
|
-
%p Display
|
22
|
-
|
23
|
-
= f.has_many :poll_question_options, build: true do |fa|
|
24
|
-
= fa.text_field :title, label: false
|
35
|
+
%h5 Choose one
|
36
|
+
%p Display radio buttons to select one option
|
25
37
|
|
26
38
|
= f.show_if :category, 'Select all that apply' do
|
27
39
|
.card
|
@@ -105,3 +117,16 @@
|
|
105
117
|
= fa.text_field :title, label: false
|
106
118
|
|
107
119
|
= effective_submit(f)
|
120
|
+
|
121
|
+
- unless poll_question.follow_up?
|
122
|
+
%h2 Follow up questions
|
123
|
+
|
124
|
+
- if Effective::PollQuestion::UNSUPPORTED_FOLLOW_UP_QUESTION_CATEGORIES.include?(poll_question.category)
|
125
|
+
%p.text-muted
|
126
|
+
%em Follow up questions are not supported for the #{poll_question.category} question category
|
127
|
+
- elsif poll_question.new_record?
|
128
|
+
%p.text-muted
|
129
|
+
%em Please save this question to add follow up questions
|
130
|
+
- else
|
131
|
+
%p Display follow up question(s) based on the answer to this question:
|
132
|
+
= render_inline_datatable Admin::EffectivePollQuestionsDatatable.new(follow_up: true, poll_question: poll_question)
|
@@ -1 +1,2 @@
|
|
1
|
-
|
1
|
+
- # Always required false here because we'll handle the logic in the ballot_response model
|
2
|
+
= f.file_field :upload_file, label: false, required: false
|
@@ -6,9 +6,17 @@
|
|
6
6
|
%th Response
|
7
7
|
|
8
8
|
%tbody
|
9
|
-
- ballot.poll.poll_questions.
|
9
|
+
- ballot.poll.poll_questions.top_level.deep.each do |poll_question|
|
10
10
|
- ballot_response = ballot.ballot_response(poll_question)
|
11
11
|
|
12
12
|
%tr
|
13
13
|
%td= poll_question
|
14
14
|
%td= render(ballot_response)
|
15
|
+
|
16
|
+
- poll_question.follow_up_poll_questions.deep.each do |follow_up_poll_question|
|
17
|
+
- ballot_response = ballot.ballot_response(follow_up_poll_question)
|
18
|
+
|
19
|
+
- if ballot_response.persisted?
|
20
|
+
%tr
|
21
|
+
%td= follow_up_poll_question
|
22
|
+
%td= render(ballot_response)
|
@@ -5,10 +5,21 @@
|
|
5
5
|
= effective_form_with(model: resource, url: wizard_path(step), method: :put) do |f|
|
6
6
|
= f.hidden_field :current_step
|
7
7
|
|
8
|
-
- resource.poll.poll_questions.deep.
|
8
|
+
- resource.poll.poll_questions.top_level.deep.each do |poll_question|
|
9
9
|
- ballot_response = resource.ballot_response(poll_question)
|
10
10
|
|
11
11
|
= f.fields_for :ballot_responses, ballot_response do |fbr|
|
12
12
|
= render('/effective/ballot_responses/fields', f: fbr, poll_question: poll_question)
|
13
13
|
|
14
|
+
- poll_question.follow_up_poll_questions.each do |follow_up|
|
15
|
+
- ballot_response = resource.ballot_response(follow_up)
|
16
|
+
|
17
|
+
= fbr.show_if(poll_question.show_if_attribute, follow_up.show_if_value) do
|
18
|
+
= f.fields_for :ballot_responses, ballot_response do |fbr|
|
19
|
+
= render('/effective/ballot_responses/fields', f: fbr, poll_question: follow_up)
|
20
|
+
|
21
|
+
= fbr.hide_if(poll_question.show_if_attribute, follow_up.show_if_value) do
|
22
|
+
= f.fields_for :ballot_responses, ballot_response do |fbr|
|
23
|
+
= fbr.hidden_field :_destroy, value: true
|
24
|
+
|
14
25
|
= f.submit 'Save and Continue', center: true
|
@@ -33,13 +33,25 @@
|
|
33
33
|
%tbody
|
34
34
|
- ballots = poll.ballots
|
35
35
|
|
36
|
-
- poll.poll_questions.each_with_index do |poll_question, index|
|
36
|
+
- poll.poll_questions.top_level.each_with_index do |poll_question, index|
|
37
37
|
- ballot_responses = poll.poll_results(poll_question: poll_question)
|
38
38
|
|
39
39
|
%tr
|
40
40
|
%td
|
41
|
-
#{
|
41
|
+
#{index + 1}. #{poll_question}
|
42
42
|
%br
|
43
43
|
%small.text-muted= poll_question.category
|
44
44
|
|
45
45
|
%td= render('effective/poll_results/poll_result', poll_question: poll_question, ballot_responses: ballot_responses)
|
46
|
+
|
47
|
+
- poll_question.follow_up_poll_questions.each_with_index do |follow_up_poll_question, index|
|
48
|
+
- ballot_responses = poll.poll_results(poll_question: follow_up_poll_question)
|
49
|
+
|
50
|
+
%tr
|
51
|
+
%td
|
52
|
+
.ml-4
|
53
|
+
#{('a'.ord + index).chr}. #{follow_up_poll_question}
|
54
|
+
%br
|
55
|
+
%small.text-muted= follow_up_poll_question.category
|
56
|
+
|
57
|
+
%td= render('effective/poll_results/poll_result', poll_question: follow_up_poll_question, ballot_responses: ballot_responses)
|
@@ -49,6 +49,12 @@ class CreateEffectivePolls < ActiveRecord::Migration[6.0]
|
|
49
49
|
|
50
50
|
t.integer :position
|
51
51
|
|
52
|
+
t.boolean :follow_up, default: false
|
53
|
+
t.string :follow_up_value
|
54
|
+
|
55
|
+
t.integer :poll_question_id
|
56
|
+
t.integer :poll_question_option_id
|
57
|
+
|
52
58
|
t.datetime :updated_at
|
53
59
|
t.datetime :created_at
|
54
60
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: effective_polls
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
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:
|
11
|
+
date: 2025-04-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|