decidim-forms 0.22.0 → 0.23.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/assets/config/admin/decidim_forms_manifest.css +3 -0
- data/app/assets/config/decidim_forms_manifest.css +1 -0
- data/app/assets/javascripts/decidim/forms/admin/auto_select_options_from_url.component.js.es6 +40 -0
- data/app/assets/javascripts/decidim/forms/admin/forms.js.es6 +181 -25
- data/app/assets/javascripts/decidim/forms/autosortable_checkboxes.component.js.es6 +54 -34
- data/app/assets/javascripts/decidim/forms/display_conditions.component.js.es6 +204 -0
- data/app/assets/javascripts/decidim/forms/forms.js.es6 +8 -1
- data/app/assets/stylesheets/decidim/forms/questionnaire-answers-pdf.scss +69 -0
- data/app/commands/decidim/forms/admin/update_questionnaire.rb +25 -1
- data/app/commands/decidim/forms/answer_questionnaire.rb +1 -1
- data/app/controllers/decidim/forms/admin/concerns/has_questionnaire.rb +47 -1
- data/app/controllers/decidim/forms/admin/concerns/has_questionnaire_answers.rb +97 -0
- data/app/forms/decidim/forms/admin/display_condition_form.rb +100 -0
- data/app/forms/decidim/forms/admin/question_form.rb +1 -0
- data/app/forms/decidim/forms/answer_form.rb +15 -0
- data/app/helpers/decidim/forms/admin/application_helper.rb +21 -0
- data/app/helpers/decidim/forms/admin/concerns/has_questionnaire_answers_pagination_helper.rb +49 -0
- data/app/helpers/decidim/forms/admin/concerns/has_questionnaire_answers_url_helper.rb +40 -0
- data/app/helpers/decidim/forms/admin/questionnaire_answers_helper.rb +27 -0
- data/app/jobs/decidim/forms/export_questionnaire_answers_job.rb +19 -0
- data/app/models/decidim/forms/answer.rb +0 -3
- data/app/models/decidim/forms/answer_option.rb +14 -0
- data/app/models/decidim/forms/display_condition.rb +51 -0
- data/app/models/decidim/forms/question.rb +34 -0
- data/app/models/decidim/forms/question_matrix_row.rb +3 -0
- data/app/models/decidim/forms/questionnaire.rb +11 -1
- data/app/presenters/decidim/forms/admin/questionnaire_answer_presenter.rb +43 -0
- data/app/presenters/decidim/forms/admin/questionnaire_participant_presenter.rb +60 -0
- data/app/presenters/decidim/forms/answer_option_presenter.rb +20 -0
- data/app/presenters/decidim/forms/question_presenter.rb +16 -0
- data/app/queries/decidim/forms/questionnaire_participant.rb +35 -0
- data/app/queries/decidim/forms/questionnaire_participants.rb +43 -0
- data/app/views/decidim/forms/admin/questionnaires/_display_condition.html.erb +88 -0
- data/app/views/decidim/forms/admin/questionnaires/_display_condition_template.html.erb +7 -0
- data/app/views/decidim/forms/admin/questionnaires/_form.html.erb +30 -4
- data/app/views/decidim/forms/admin/questionnaires/_question.html.erb +19 -3
- data/app/views/decidim/forms/admin/questionnaires/answers/export/_answer.html.erb +31 -0
- data/app/views/decidim/forms/admin/questionnaires/answers/export/pdf.html.erb +13 -0
- data/app/views/decidim/forms/admin/questionnaires/answers/index.html.erb +53 -0
- data/app/views/decidim/forms/admin/questionnaires/answers/show.html.erb +48 -0
- data/app/views/decidim/forms/admin/questionnaires/edit.html.erb +9 -5
- data/app/views/decidim/forms/questionnaires/_answer.html.erb +1 -1
- data/app/views/decidim/forms/questionnaires/show.html.erb +22 -2
- data/app/views/layouts/decidim/forms/admin/questionnaires/questionnaire_answers.html.erb +12 -0
- data/config/initializers/wicked_pdf.rb +25 -0
- data/config/locales/am-ET.yml +1 -0
- data/config/locales/ar.yml +0 -1
- data/config/locales/bg-BG.yml +0 -2
- data/config/locales/bg.yml +14 -0
- data/config/locales/ca.yml +56 -4
- data/config/locales/cs.yml +54 -2
- data/config/locales/da.yml +1 -0
- data/config/locales/de.yml +51 -1
- data/config/locales/el.yml +0 -2
- data/config/locales/en.yml +54 -2
- data/config/locales/eo.yml +1 -0
- data/config/locales/es-MX.yml +53 -1
- data/config/locales/es-PY.yml +53 -1
- data/config/locales/es.yml +54 -2
- data/config/locales/et.yml +1 -0
- data/config/locales/eu.yml +0 -1
- data/config/locales/fi-plain.yml +53 -1
- data/config/locales/fi.yml +53 -1
- data/config/locales/fr-CA.yml +54 -2
- data/config/locales/fr.yml +54 -2
- data/config/locales/gl.yml +0 -1
- data/config/locales/hr.yml +1 -0
- data/config/locales/hu.yml +0 -2
- data/config/locales/id-ID.yml +0 -1
- data/config/locales/is.yml +1 -0
- data/config/locales/it.yml +53 -1
- data/config/locales/ja-JP.yml +51 -1
- data/config/locales/ja.yml +170 -0
- data/config/locales/ko-KR.yml +1 -0
- data/config/locales/ko.yml +1 -0
- data/config/locales/lt.yml +1 -0
- data/config/locales/{lv-LV.yml → lv.yml} +0 -1
- data/config/locales/mt.yml +1 -0
- data/config/locales/nl.yml +59 -7
- data/config/locales/no.yml +47 -2
- data/config/locales/om-ET.yml +1 -0
- data/config/locales/pl.yml +52 -3
- data/config/locales/pt-BR.yml +0 -1
- data/config/locales/pt.yml +51 -2
- data/config/locales/ro-RO.yml +51 -2
- data/config/locales/ru.yml +0 -1
- data/config/locales/sk.yml +0 -2
- data/config/locales/sl.yml +7 -0
- data/config/locales/so-SO.yml +1 -0
- data/config/locales/sv.yml +51 -2
- data/config/locales/ti-ER.yml +1 -0
- data/config/locales/tr-TR.yml +0 -1
- data/config/locales/vi-VN.yml +1 -0
- data/config/locales/vi.yml +1 -0
- data/config/locales/zh-CN.yml +172 -0
- data/config/locales/zh-TW.yml +1 -0
- data/db/migrate/20200130194123_create_decidim_forms_display_conditions.rb +20 -0
- data/lib/decidim/exporters/form_pdf.rb +33 -0
- data/lib/decidim/exporters/form_pdf_controller_helper.rb +11 -0
- data/lib/decidim/forms.rb +5 -0
- data/lib/decidim/forms/admin_engine.rb +1 -1
- data/lib/decidim/forms/test.rb +6 -0
- data/lib/decidim/forms/test/factories.rb +30 -0
- data/lib/decidim/forms/test/shared_examples/has_questionnaire.rb +619 -38
- data/lib/decidim/forms/test/shared_examples/manage_questionnaire_answers.rb +108 -0
- data/lib/decidim/forms/test/shared_examples/manage_questionnaires.rb +21 -894
- data/lib/decidim/forms/test/shared_examples/manage_questionnaires/add_display_conditions.rb +179 -0
- data/lib/decidim/forms/test/shared_examples/manage_questionnaires/add_questions.rb +463 -0
- data/lib/decidim/forms/test/shared_examples/manage_questionnaires/update_display_conditions.rb +93 -0
- data/lib/decidim/forms/test/shared_examples/manage_questionnaires/update_questions.rb +461 -0
- data/lib/decidim/forms/version.rb +1 -1
- metadata +91 -11
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Forms
|
|
5
|
+
module Admin
|
|
6
|
+
# This class holds a Form to update questionnaire questions from Decidim's admin panel.
|
|
7
|
+
class DisplayConditionForm < Decidim::Form
|
|
8
|
+
include TranslatableAttributes
|
|
9
|
+
|
|
10
|
+
attribute :decidim_question_id, Integer
|
|
11
|
+
attribute :decidim_condition_question_id, Integer
|
|
12
|
+
attribute :decidim_answer_option_id, Integer
|
|
13
|
+
attribute :condition_type, String
|
|
14
|
+
attribute :mandatory, Boolean, default: false
|
|
15
|
+
attribute :deleted, Boolean, default: false
|
|
16
|
+
|
|
17
|
+
translatable_attribute :condition_value, String
|
|
18
|
+
|
|
19
|
+
validates :question, presence: true, unless: :deleted
|
|
20
|
+
validates :condition_question, presence: true, unless: :deleted
|
|
21
|
+
validates :answer_option, presence: true, if: :answer_option_mandatory?
|
|
22
|
+
|
|
23
|
+
validates :condition_value, translatable_presence: true, if: :condition_value_mandatory?
|
|
24
|
+
validates :condition_type, presence: true, unless: :deleted
|
|
25
|
+
|
|
26
|
+
validate :condition_question_position, unless: :deleted
|
|
27
|
+
validate :valid_answer_option?, unless: :deleted
|
|
28
|
+
|
|
29
|
+
def to_param
|
|
30
|
+
return id if id.present?
|
|
31
|
+
|
|
32
|
+
"questionnaire-display-condition-id"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def answer_options
|
|
36
|
+
return [] if condition_question.blank?
|
|
37
|
+
|
|
38
|
+
condition_question.answer_options
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def questions_for_select(questionnaire, id)
|
|
42
|
+
questionnaire.questions.map do |question|
|
|
43
|
+
[
|
|
44
|
+
question.translated_body,
|
|
45
|
+
question.id,
|
|
46
|
+
{
|
|
47
|
+
"disabled" => (question.question_type == "sorting" || question.id == id),
|
|
48
|
+
"data-type" => question.question_type
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Finds the Question from the given decidim_question_id
|
|
55
|
+
#
|
|
56
|
+
# Returns a Decidim::Forms::Question
|
|
57
|
+
def question
|
|
58
|
+
@question ||= Question.find_by(id: @decidim_question_id)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Finds the Condition Question from the given decidim_condition_question_id
|
|
62
|
+
#
|
|
63
|
+
# Returns a Decidim::Forms::Question
|
|
64
|
+
def condition_question
|
|
65
|
+
@condition_question ||= Question.find_by(id: @decidim_condition_question_id)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Finds the Answer Option from the given decidim_answer_option_id
|
|
69
|
+
#
|
|
70
|
+
# Returns a Decidim::Forms::AnswerOption
|
|
71
|
+
def answer_option
|
|
72
|
+
@answer_option ||= AnswerOption.find_by(id: @decidim_answer_option_id)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
def condition_value_mandatory?
|
|
78
|
+
!deleted && condition_type == "match"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def answer_option_mandatory?
|
|
82
|
+
!deleted && %w(equal not_equal).include?(condition_type)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def valid_answer_option?
|
|
86
|
+
return unless answer_option_mandatory?
|
|
87
|
+
return if answer_option.blank?
|
|
88
|
+
|
|
89
|
+
errors.add(:decidim_answer_option_id, :invalid) if answer_option.question.id != decidim_condition_question_id
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def condition_question_position
|
|
93
|
+
return if decidim_question_id.blank?
|
|
94
|
+
|
|
95
|
+
errors.add(:decidim_question_id, :invalid) unless question.position&.positive?
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -11,6 +11,7 @@ module Decidim
|
|
|
11
11
|
attribute :mandatory, Boolean, default: false
|
|
12
12
|
attribute :question_type, String
|
|
13
13
|
attribute :answer_options, Array[AnswerOptionForm]
|
|
14
|
+
attribute :display_conditions, Array[DisplayConditionForm]
|
|
14
15
|
attribute :matrix_rows, Array[QuestionMatrixRowForm]
|
|
15
16
|
attribute :max_choices, Integer
|
|
16
17
|
attribute :deleted, Boolean, default: false
|
|
@@ -49,8 +49,23 @@ module Decidim
|
|
|
49
49
|
choices.select(&:body)
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
+
def display_conditions_fulfilled?
|
|
53
|
+
question.display_conditions.all? do |condition|
|
|
54
|
+
answer = question.questionnaire.answers.find_by(question: condition.condition_question)
|
|
55
|
+
condition.fulfilled?(answer)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
52
59
|
private
|
|
53
60
|
|
|
61
|
+
def mandatory_body?
|
|
62
|
+
question.mandatory_body? if display_conditions_fulfilled?
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def mandatory_choices?
|
|
66
|
+
question.mandatory_choices? if display_conditions_fulfilled?
|
|
67
|
+
end
|
|
68
|
+
|
|
54
69
|
def grouped_choices
|
|
55
70
|
selected_choices.group_by(&:matrix_row_id).values
|
|
56
71
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# i18n-tasks-use t('decidim.templates.admin.questionnaire_templates.form.title')
|
|
4
|
+
|
|
3
5
|
module Decidim
|
|
4
6
|
module Forms
|
|
5
7
|
module Admin
|
|
@@ -14,6 +16,10 @@ module Decidim
|
|
|
14
16
|
"questionnaire_question_#{question.to_param}_answer_option_#{answer_option.to_param}"
|
|
15
17
|
end
|
|
16
18
|
|
|
19
|
+
def tabs_id_for_question_display_condition(question, display_condition)
|
|
20
|
+
"questionnaire_question_#{question.to_param}_display_condition_#{display_condition.to_param}"
|
|
21
|
+
end
|
|
22
|
+
|
|
17
23
|
def tabs_id_for_question_matrix_row(question, matrix_row)
|
|
18
24
|
"questionnaire_question_#{question.to_param}_matrix_row_#{matrix_row.to_param}"
|
|
19
25
|
end
|
|
@@ -29,6 +35,21 @@ module Decidim
|
|
|
29
35
|
truncate translated_attribute(title), length: options[:max_length], omission: options[:omission]
|
|
30
36
|
end
|
|
31
37
|
end
|
|
38
|
+
|
|
39
|
+
def template?(questionnaire_for)
|
|
40
|
+
return unless defined? Decidim::Templates::Template
|
|
41
|
+
|
|
42
|
+
questionnaire_for.is_a? Decidim::Templates::Template
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def templates_defined?
|
|
46
|
+
defined? Decidim::Templates::Admin::Concerns::Templatable
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def title_for_questionnaire
|
|
50
|
+
scope = templates_defined? ? "decidim.templates.admin.questionnaire_templates" : "decidim.forms.admin.questionnaires"
|
|
51
|
+
t("form.title", scope: scope)
|
|
52
|
+
end
|
|
32
53
|
end
|
|
33
54
|
end
|
|
34
55
|
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Forms
|
|
5
|
+
module Admin
|
|
6
|
+
module Concerns
|
|
7
|
+
# Pagination helper for HasQuestionnaireAnswers controller concern
|
|
8
|
+
#
|
|
9
|
+
module HasQuestionnaireAnswersPaginationHelper
|
|
10
|
+
def self.included(base)
|
|
11
|
+
base.helper_method :prev_url, :next_url, :first?, :last?, :current_idx
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def prev_url
|
|
15
|
+
return if first?
|
|
16
|
+
|
|
17
|
+
token = participant_ids[current_idx - 1]
|
|
18
|
+
questionnaire_participant_answers_url(token)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def next_url
|
|
22
|
+
return if last?
|
|
23
|
+
|
|
24
|
+
token = participant_ids[current_idx + 1]
|
|
25
|
+
questionnaire_participant_answers_url(token)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def first?
|
|
29
|
+
current_idx.zero?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def last?
|
|
33
|
+
current_idx == participant_ids.count - 1
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def participant_ids
|
|
39
|
+
@participant_ids ||= collection.pluck(:session_token)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def current_idx
|
|
43
|
+
participant_ids.index(params[:session_token])
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Forms
|
|
5
|
+
module Admin
|
|
6
|
+
module Concerns
|
|
7
|
+
# Url helper for HasQuestionnaireAnswers controller concern
|
|
8
|
+
#
|
|
9
|
+
module HasQuestionnaireAnswersUrlHelper
|
|
10
|
+
def self.included(base)
|
|
11
|
+
base.helper_method :questionnaire_url, :questionnaire_participants_url,
|
|
12
|
+
:questionnaire_participant_answers_url, :questionnaire_export_response_url
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# You can implement this method in your controller to change the URL
|
|
16
|
+
# where the questionnaire can be edited.
|
|
17
|
+
def questionnaire_url
|
|
18
|
+
url_for(questionnaire.questionnaire_for)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# You can implement this method in your controller to change the URL
|
|
22
|
+
# where the questionnaire participants' info will be shown.
|
|
23
|
+
def questionnaire_participants_url
|
|
24
|
+
url_for([:index, questionnaire.questionnaire_for, format: nil])
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# You can implement this method in your controller to change the URL
|
|
28
|
+
# where the user's questionnaire answers will be shown.
|
|
29
|
+
def questionnaire_participant_answers_url(session_token)
|
|
30
|
+
url_for([:show, questionnaire.questionnaire_for, session_token: session_token])
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def questionnaire_export_response_url(session_token)
|
|
34
|
+
url_for([:export_response, questionnaire.questionnaire_for, session_token: session_token, format: "pdf"])
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Forms
|
|
5
|
+
module Admin
|
|
6
|
+
# Custom helpers for questionnaire answers
|
|
7
|
+
#
|
|
8
|
+
module QuestionnaireAnswersHelper
|
|
9
|
+
def first_table_th(answer)
|
|
10
|
+
return translated_attribute answer.first_short_answer.question.body if answer.first_short_answer
|
|
11
|
+
|
|
12
|
+
t("session_token", scope: "decidim.forms.user_answers_serializer")
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def first_table_td(answer)
|
|
16
|
+
return answer.first_short_answer.body if answer.first_short_answer
|
|
17
|
+
|
|
18
|
+
answer.session_token
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def display_percentage(number)
|
|
22
|
+
number_to_percentage(number, precision: 0, strip_insignificant_zeros: true, locale: I18n.locale)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Forms
|
|
5
|
+
class ExportQuestionnaireAnswersJob < ApplicationJob
|
|
6
|
+
queue_as :default
|
|
7
|
+
|
|
8
|
+
def perform(user, title, answers)
|
|
9
|
+
return if user&.email.blank?
|
|
10
|
+
return if answers&.blank?
|
|
11
|
+
|
|
12
|
+
serializer = Decidim::Forms::UserAnswersSerializer
|
|
13
|
+
export_data = Decidim::Exporters::FormPDF.new(answers, serializer).export
|
|
14
|
+
|
|
15
|
+
ExportMailer.export(user, title, export_data).deliver_now
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -17,9 +17,6 @@ module Decidim
|
|
|
17
17
|
dependent: :destroy,
|
|
18
18
|
inverse_of: :answer
|
|
19
19
|
|
|
20
|
-
validates :body, presence: true, if: -> { question.mandatory_body? }
|
|
21
|
-
validates :choices, presence: true, if: -> { question.mandatory_choices? }
|
|
22
|
-
|
|
23
20
|
validate :user_questionnaire_same_organization
|
|
24
21
|
validate :question_belongs_to_questionnaire
|
|
25
22
|
|
|
@@ -3,9 +3,23 @@
|
|
|
3
3
|
module Decidim
|
|
4
4
|
module Forms
|
|
5
5
|
class AnswerOption < Forms::ApplicationRecord
|
|
6
|
+
include Decidim::TranslatableResource
|
|
7
|
+
|
|
6
8
|
default_scope { order(arel_table[:id].asc) }
|
|
7
9
|
|
|
10
|
+
translatable_fields :body
|
|
11
|
+
|
|
8
12
|
belongs_to :question, class_name: "Question", foreign_key: "decidim_question_id"
|
|
13
|
+
|
|
14
|
+
has_many :display_conditions,
|
|
15
|
+
class_name: "DisplayCondition",
|
|
16
|
+
foreign_key: "decidim_answer_option_id",
|
|
17
|
+
dependent: :nullify,
|
|
18
|
+
inverse_of: :answer_option
|
|
19
|
+
|
|
20
|
+
def translated_body
|
|
21
|
+
Decidim::Forms::AnswerOptionPresenter.new(self).translated_body
|
|
22
|
+
end
|
|
9
23
|
end
|
|
10
24
|
end
|
|
11
25
|
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Forms
|
|
5
|
+
# The data store for a DisplayCondition in the Decidim::Forms component.
|
|
6
|
+
# A display condition is associated to two questions. :question is the question
|
|
7
|
+
# that we want to display or hide based on some conditions, and :condition_question
|
|
8
|
+
# is the question the answers of which are checked against the conditions.
|
|
9
|
+
# Conditions can be whether the question is answered ("answered") or it is not ("not_answered"),
|
|
10
|
+
# if the selected answer option is ("equal") or is not ("not_equal") a given one, or whethe
|
|
11
|
+
# the text value of the answer matches a string ("match").
|
|
12
|
+
class DisplayCondition < Forms::ApplicationRecord
|
|
13
|
+
enum condition_type: [:answered, :not_answered, :equal, :not_equal, :match], _prefix: true
|
|
14
|
+
|
|
15
|
+
# Question which will be displayed or hidden
|
|
16
|
+
belongs_to :question, class_name: "Question", foreign_key: "decidim_question_id", inverse_of: :display_conditions
|
|
17
|
+
|
|
18
|
+
# Question the answers of which are checked against conditions
|
|
19
|
+
belongs_to :condition_question, class_name: "Question", foreign_key: "decidim_condition_question_id", inverse_of: :display_conditions_for_other_questions
|
|
20
|
+
|
|
21
|
+
# Answer option provided to check for "equal" or "not_equal" (optional)
|
|
22
|
+
belongs_to :answer_option, class_name: "AnswerOption", foreign_key: "decidim_answer_option_id", optional: true
|
|
23
|
+
|
|
24
|
+
def fulfilled?(answer)
|
|
25
|
+
case condition_type
|
|
26
|
+
when "answered"
|
|
27
|
+
answer.present?
|
|
28
|
+
when "not_answered"
|
|
29
|
+
answer.blank?
|
|
30
|
+
when "equal"
|
|
31
|
+
answer.present? ? answer.choices.pluck(:decidim_answer_option_id).include?(answer_option.id) : false
|
|
32
|
+
when "not_equal"
|
|
33
|
+
answer.present? ? !answer.choices.pluck(:decidim_answer_option_id).include?(answer_option.id) : true
|
|
34
|
+
when "match"
|
|
35
|
+
answer.present? ? condition_value.values.any? { |value| answer.body.match?(Regexp.new(value, Regexp::IGNORECASE)) } : false
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def to_html_data
|
|
40
|
+
{
|
|
41
|
+
id: id,
|
|
42
|
+
type: condition_type,
|
|
43
|
+
condition: decidim_condition_question_id,
|
|
44
|
+
option: decidim_answer_option_id,
|
|
45
|
+
mandatory: mandatory,
|
|
46
|
+
value: condition_value&.dig(I18n.locale.to_s)
|
|
47
|
+
}.compact
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -4,10 +4,14 @@ module Decidim
|
|
|
4
4
|
module Forms
|
|
5
5
|
# The data store for a Question in the Decidim::Forms component.
|
|
6
6
|
class Question < Forms::ApplicationRecord
|
|
7
|
+
include Decidim::TranslatableResource
|
|
8
|
+
|
|
7
9
|
QUESTION_TYPES = %w(short_answer long_answer single_option multiple_option sorting matrix_single matrix_multiple).freeze
|
|
8
10
|
SEPARATOR_TYPE = "separator"
|
|
9
11
|
TYPES = (QUESTION_TYPES + [SEPARATOR_TYPE]).freeze
|
|
10
12
|
|
|
13
|
+
translatable_fields :body, :description
|
|
14
|
+
|
|
11
15
|
belongs_to :questionnaire, class_name: "Questionnaire", foreign_key: "decidim_questionnaire_id"
|
|
12
16
|
|
|
13
17
|
has_many :matrix_rows,
|
|
@@ -22,8 +26,34 @@ module Decidim
|
|
|
22
26
|
dependent: :destroy,
|
|
23
27
|
inverse_of: :question
|
|
24
28
|
|
|
29
|
+
# Conditions to display this question in questionnaire
|
|
30
|
+
has_many :display_conditions,
|
|
31
|
+
class_name: "DisplayCondition",
|
|
32
|
+
foreign_key: "decidim_question_id",
|
|
33
|
+
dependent: :destroy,
|
|
34
|
+
inverse_of: :question
|
|
35
|
+
|
|
36
|
+
# Conditions to display other questions based on the value of this question's answer
|
|
37
|
+
has_many :display_conditions_for_other_questions,
|
|
38
|
+
class_name: "DisplayCondition",
|
|
39
|
+
foreign_key: "decidim_condition_question_id",
|
|
40
|
+
dependent: :destroy,
|
|
41
|
+
inverse_of: :question
|
|
42
|
+
|
|
43
|
+
# Questions which have display conditions based on the value of this question's answer
|
|
44
|
+
has_many :conditioned_questions,
|
|
45
|
+
through: :display_conditions_for_other_questions,
|
|
46
|
+
foreign_key: "decidim_condition_question_id",
|
|
47
|
+
class_name: "Question"
|
|
48
|
+
|
|
25
49
|
validates :question_type, inclusion: { in: TYPES }
|
|
26
50
|
|
|
51
|
+
scope :with_body, -> { where(question_type: %w(short_answer long_answer)) }
|
|
52
|
+
scope :with_choices, -> { where.not(question_type: %w(short_answer long_answer)) }
|
|
53
|
+
|
|
54
|
+
scope :conditioned, -> { includes(:display_conditions).where.not(decidim_forms_display_conditions: { id: nil }) }
|
|
55
|
+
scope :not_conditioned, -> { includes(:display_conditions).where(decidim_forms_display_conditions: { id: nil }) }
|
|
56
|
+
|
|
27
57
|
def matrix?
|
|
28
58
|
%w(matrix_single matrix_multiple).include?(question_type)
|
|
29
59
|
end
|
|
@@ -44,6 +74,10 @@ module Decidim
|
|
|
44
74
|
answer_options.size
|
|
45
75
|
end
|
|
46
76
|
|
|
77
|
+
def translated_body
|
|
78
|
+
Decidim::Forms::QuestionPresenter.new(self).translated_body
|
|
79
|
+
end
|
|
80
|
+
|
|
47
81
|
def separator?
|
|
48
82
|
question_type.to_s == SEPARATOR_TYPE
|
|
49
83
|
end
|