decidim-forms 0.30.3 → 0.31.0.rc1

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.
Files changed (159) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -10
  3. data/app/cells/decidim/forms/matrix_readonly/show.erb +1 -1
  4. data/app/cells/decidim/forms/matrix_readonly_cell.rb +3 -3
  5. data/app/cells/decidim/forms/question_readonly/show.erb +5 -5
  6. data/app/cells/decidim/forms/question_readonly/title_and_description.erb +3 -3
  7. data/app/cells/decidim/forms/response_readonly_cell.rb +9 -0
  8. data/app/cells/decidim/forms/step_navigation/show.erb +3 -3
  9. data/app/cells/decidim/forms/step_navigation_cell.rb +3 -3
  10. data/app/commands/decidim/forms/admin/update_questions.rb +6 -6
  11. data/app/commands/decidim/forms/{answer_questionnaire.rb → response_questionnaire.rb} +35 -35
  12. data/app/controllers/decidim/forms/admin/concerns/has_questionnaire.rb +12 -12
  13. data/app/controllers/decidim/forms/admin/concerns/{has_questionnaire_answers.rb → has_questionnaire_responses.rb} +23 -19
  14. data/app/controllers/decidim/forms/concerns/has_questionnaire.rb +38 -33
  15. data/app/forms/decidim/forms/admin/display_condition_form.rb +14 -14
  16. data/app/forms/decidim/forms/admin/question_form.rb +3 -3
  17. data/app/forms/decidim/forms/admin/{answer_option_form.rb → response_option_form.rb} +3 -3
  18. data/app/forms/decidim/forms/questionnaire_form.rb +8 -9
  19. data/app/forms/decidim/forms/{answer_choice_form.rb → response_choice_form.rb} +5 -5
  20. data/app/forms/decidim/forms/{answer_form.rb → response_form.rb} +9 -9
  21. data/app/helpers/decidim/forms/admin/application_helper.rb +2 -2
  22. data/app/helpers/decidim/forms/admin/concerns/{has_questionnaire_answers_pagination_helper.rb → has_questionnaire_responses_pagination_helper.rb} +4 -4
  23. data/app/helpers/decidim/forms/admin/concerns/{has_questionnaire_answers_url_helper.rb → has_questionnaire_responses_url_helper.rb} +5 -5
  24. data/app/helpers/decidim/forms/admin/questionnaire_responses_helper.rb +32 -0
  25. data/app/helpers/decidim/forms/application_helper.rb +1 -2
  26. data/app/jobs/decidim/forms/{export_questionnaire_answers_job.rb → export_questionnaire_responses_job.rb} +5 -5
  27. data/app/models/decidim/forms/display_condition.rb +20 -20
  28. data/app/models/decidim/forms/question.rb +14 -13
  29. data/app/models/decidim/forms/question_matrix_row.rb +1 -1
  30. data/app/models/decidim/forms/questionnaire.rb +11 -6
  31. data/app/models/decidim/forms/{answer.rb → response.rb} +9 -9
  32. data/app/models/decidim/forms/response_choice.rb +22 -0
  33. data/app/models/decidim/forms/{answer_option.rb → response_option.rb} +5 -5
  34. data/app/packs/entrypoints/decidim_forms_admin.js +3 -3
  35. data/app/packs/src/decidim/forms/admin/collapsible_questions.js +12 -10
  36. data/app/packs/src/decidim/forms/admin/forms.js +35 -35
  37. data/app/packs/src/decidim/forms/admin/{publish_answers_buttons.js → publish_responses_buttons.js} +12 -12
  38. data/app/packs/src/decidim/forms/display_conditions.component.js +12 -12
  39. data/app/packs/src/decidim/forms/forms.js +8 -29
  40. data/app/packs/stylesheets/decidim/forms/forms.scss +2 -2
  41. data/app/presenters/decidim/forms/admin/questionnaire_participant_presenter.rb +13 -13
  42. data/app/presenters/decidim/forms/admin/{questionnaire_answer_presenter.rb → questionnaire_response_presenter.rb} +15 -15
  43. data/app/presenters/decidim/forms/admin_log/question_presenter.rb +3 -3
  44. data/app/presenters/decidim/forms/{answer_option_presenter.rb → response_option_presenter.rb} +2 -2
  45. data/app/queries/decidim/forms/questionnaire_participant.rb +5 -5
  46. data/app/queries/decidim/forms/questionnaire_participants.rb +6 -6
  47. data/app/queries/decidim/forms/questionnaire_user_responses.rb +32 -0
  48. data/app/views/decidim/forms/admin/questionnaires/_display_condition.html.erb +9 -9
  49. data/app/views/decidim/forms/admin/questionnaires/_form.html.erb +2 -2
  50. data/app/views/decidim/forms/admin/questionnaires/_matrix_row.html.erb +3 -3
  51. data/app/views/decidim/forms/admin/questionnaires/_question.html.erb +21 -11
  52. data/app/views/decidim/forms/admin/questionnaires/_questions_form.html.erb +13 -41
  53. data/app/views/decidim/forms/admin/questionnaires/{_answer_option.html.erb → _response_option.html.erb} +7 -7
  54. data/app/views/decidim/forms/admin/questionnaires/_response_option_template.html.erb +7 -0
  55. data/app/views/decidim/forms/admin/questionnaires/_separator.html.erb +11 -1
  56. data/app/views/decidim/forms/admin/questionnaires/_title_and_description.html.erb +12 -2
  57. data/app/views/decidim/forms/admin/questionnaires/edit.html.erb +1 -1
  58. data/app/views/decidim/forms/admin/questionnaires/edit_questions.html.erb +8 -8
  59. data/app/views/decidim/forms/admin/questionnaires/responses/index.html.erb +80 -0
  60. data/app/views/decidim/forms/admin/questionnaires/responses/show.html.erb +43 -0
  61. data/app/views/decidim/forms/questionnaires/_questionnaire.html.erb +22 -26
  62. data/app/views/decidim/forms/questionnaires/_questionnaire_readonly.html.erb +8 -3
  63. data/app/views/decidim/forms/questionnaires/_response.html.erb +61 -0
  64. data/app/views/decidim/forms/questionnaires/edit.html.erb +2 -2
  65. data/app/views/decidim/forms/questionnaires/responses/_files.html.erb +1 -0
  66. data/app/views/decidim/forms/questionnaires/responses/_long_response.html.erb +3 -0
  67. data/app/views/decidim/forms/questionnaires/{answers → responses}/_matrix_multiple.html.erb +16 -15
  68. data/app/views/decidim/forms/questionnaires/{answers → responses}/_matrix_single.html.erb +17 -16
  69. data/app/views/decidim/forms/questionnaires/responses/_multiple_option.html.erb +26 -0
  70. data/app/views/decidim/forms/questionnaires/responses/_separator.html.erb +1 -0
  71. data/app/views/decidim/forms/questionnaires/responses/_short_response.html.erb +3 -0
  72. data/app/views/decidim/forms/questionnaires/responses/_single_option.html.erb +33 -0
  73. data/app/views/decidim/forms/questionnaires/responses/_sorting.html.erb +26 -0
  74. data/app/views/decidim/forms/questionnaires/responses/_title_and_description.html.erb +1 -0
  75. data/app/views/decidim/forms/questionnaires/show.html.erb +10 -10
  76. data/config/assets.rb +2 -2
  77. data/config/locales/ar.yml +2 -22
  78. data/config/locales/bg.yml +10 -36
  79. data/config/locales/ca-IT.yml +80 -74
  80. data/config/locales/ca.yml +80 -74
  81. data/config/locales/cs.yml +79 -76
  82. data/config/locales/de.yml +79 -72
  83. data/config/locales/el.yml +10 -36
  84. data/config/locales/en.yml +90 -84
  85. data/config/locales/es-MX.yml +82 -76
  86. data/config/locales/es-PY.yml +82 -76
  87. data/config/locales/es.yml +80 -74
  88. data/config/locales/eu.yml +78 -72
  89. data/config/locales/fi-plain.yml +78 -72
  90. data/config/locales/fi.yml +76 -70
  91. data/config/locales/fr-CA.yml +77 -68
  92. data/config/locales/fr.yml +77 -68
  93. data/config/locales/ga-IE.yml +4 -7
  94. data/config/locales/gl.yml +4 -22
  95. data/config/locales/hu.yml +4 -22
  96. data/config/locales/id-ID.yml +2 -22
  97. data/config/locales/it.yml +8 -40
  98. data/config/locales/ja.yml +87 -81
  99. data/config/locales/lb.yml +8 -36
  100. data/config/locales/lt.yml +10 -36
  101. data/config/locales/lv.yml +4 -22
  102. data/config/locales/nl.yml +10 -36
  103. data/config/locales/no.yml +10 -36
  104. data/config/locales/pl.yml +10 -36
  105. data/config/locales/pt-BR.yml +10 -36
  106. data/config/locales/pt.yml +8 -36
  107. data/config/locales/ro-RO.yml +29 -51
  108. data/config/locales/ru.yml +2 -9
  109. data/config/locales/sk.yml +2 -22
  110. data/config/locales/sl.yml +0 -5
  111. data/config/locales/sv.yml +89 -55
  112. data/config/locales/tr-TR.yml +8 -36
  113. data/config/locales/val-ES.yml +2 -0
  114. data/config/locales/zh-CN.yml +8 -36
  115. data/config/locales/zh-TW.yml +10 -36
  116. data/db/migrate/20190315203056_add_session_token_to_decidim_forms_answers.rb +1 -1
  117. data/db/migrate/20250314150250_rename_answer_to_response_in_decidim_forms.rb +30 -0
  118. data/db/migrate/20250319130003_change_question_types_in_questions.rb +14 -0
  119. data/lib/decidim/api/question_matrix_row_type.rb +13 -0
  120. data/lib/decidim/api/question_type.rb +4 -3
  121. data/lib/decidim/api/questionnaire_type.rb +1 -0
  122. data/lib/decidim/api/response_option_type.rb +13 -0
  123. data/lib/decidim/exporters/form_pdf.rb +33 -33
  124. data/lib/decidim/forms/api.rb +2 -1
  125. data/lib/decidim/forms/{download_your_data_user_answers_serializer.rb → download_your_data_user_responses_serializer.rb} +3 -3
  126. data/lib/decidim/forms/engine.rb +2 -2
  127. data/lib/decidim/forms/test/factories.rb +24 -24
  128. data/lib/decidim/forms/test/shared_examples/has_questionnaire.rb +176 -176
  129. data/lib/decidim/forms/test/shared_examples/manage_questionnaire_responses.rb +159 -0
  130. data/lib/decidim/forms/test/shared_examples/manage_questionnaires/add_display_conditions.rb +19 -19
  131. data/lib/decidim/forms/test/shared_examples/manage_questionnaires/add_questions.rb +44 -44
  132. data/lib/decidim/forms/test/shared_examples/manage_questionnaires/update_display_conditions.rb +10 -10
  133. data/lib/decidim/forms/test/shared_examples/manage_questionnaires/update_questions.rb +43 -140
  134. data/lib/decidim/forms/test/shared_examples/manage_questionnaires.rb +4 -4
  135. data/lib/decidim/forms/test.rb +1 -1
  136. data/lib/decidim/forms/user_responses_serializer.rb +110 -0
  137. data/lib/decidim/forms/version.rb +1 -1
  138. data/lib/decidim/forms.rb +2 -2
  139. metadata +48 -45
  140. data/app/cells/decidim/forms/answer_readonly_cell.rb +0 -9
  141. data/app/helpers/decidim/forms/admin/questionnaire_answers_helper.rb +0 -30
  142. data/app/models/decidim/forms/answer_choice.rb +0 -22
  143. data/app/queries/decidim/forms/questionnaire_user_answers.rb +0 -32
  144. data/app/views/decidim/forms/admin/questionnaires/_answer_option_template.html.erb +0 -7
  145. data/app/views/decidim/forms/admin/questionnaires/answers/index.html.erb +0 -49
  146. data/app/views/decidim/forms/admin/questionnaires/answers/show.html.erb +0 -43
  147. data/app/views/decidim/forms/questionnaires/_answer.html.erb +0 -61
  148. data/app/views/decidim/forms/questionnaires/answers/_files.html.erb +0 -1
  149. data/app/views/decidim/forms/questionnaires/answers/_long_answer.html.erb +0 -3
  150. data/app/views/decidim/forms/questionnaires/answers/_multiple_option.html.erb +0 -25
  151. data/app/views/decidim/forms/questionnaires/answers/_separator.html.erb +0 -1
  152. data/app/views/decidim/forms/questionnaires/answers/_short_answer.html.erb +0 -3
  153. data/app/views/decidim/forms/questionnaires/answers/_single_option.html.erb +0 -32
  154. data/app/views/decidim/forms/questionnaires/answers/_sorting.html.erb +0 -26
  155. data/app/views/decidim/forms/questionnaires/answers/_title_and_description.html.erb +0 -1
  156. data/lib/decidim/api/answer_option_type.rb +0 -13
  157. data/lib/decidim/forms/test/shared_examples/manage_questionnaire_answers.rb +0 -149
  158. data/lib/decidim/forms/user_answers_serializer.rb +0 -105
  159. /data/app/cells/decidim/forms/{answer_readonly → response_readonly}/show.erb +0 -0
@@ -1,149 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
-
5
- shared_examples_for "manage questionnaire answers" do
6
- let(:first_type) { "short_answer" }
7
- let!(:first) do
8
- create(:questionnaire_question, questionnaire:, position: 1, question_type: first_type)
9
- end
10
- let!(:second) do
11
- create(:questionnaire_question, questionnaire:, position: 2, question_type: "single_option")
12
- end
13
- let!(:third) do
14
- create(:questionnaire_question, questionnaire:, position: 3, question_type: "files")
15
- end
16
- let(:questions) do
17
- [first, second, third]
18
- end
19
-
20
- context "when there are no answers" do
21
- it "do not answer admin link" do
22
- click_on "Manage questions"
23
- expect(page).to have_content("No answers yet")
24
- end
25
- end
26
-
27
- context "when there are answers" do
28
- let!(:answer1) { create(:answer, questionnaire:, question: first) }
29
- let!(:answer2) { create(:answer, body: "second answer", questionnaire:, question: first) }
30
- let!(:answer3) { create(:answer, questionnaire:, question: second) }
31
- let!(:file_answer) { create(:answer, :with_attachments, questionnaire:, question: third, body: nil, user: answer3.user, session_token: answer3.session_token) }
32
-
33
- it "shows the answer admin link" do
34
- click_on "Manage questions"
35
- expect(page).to have_content("Show responses")
36
- end
37
-
38
- context "and managing answers page" do
39
- before do
40
- click_on "Manage questions"
41
- click_on "Show responses"
42
- end
43
-
44
- it "shows the answers page" do
45
- expect(page).to have_content(answer1.body)
46
- expect(page).to have_content(answer1.question.body["en"])
47
- expect(page).to have_content(answer2.body)
48
- expect(page).to have_content(answer2.question.body["en"])
49
- end
50
-
51
- it "shows the percentage" do
52
- expect(page).to have_content("33%")
53
- end
54
-
55
- it "has a detail link" do
56
- expect(page).to have_link("Show answers")
57
- end
58
-
59
- it "has an export link" do
60
- expect(page).to have_link(answer1.body)
61
- expect(page).to have_link(answer2.body)
62
- expect(page).to have_link("Export")
63
- end
64
-
65
- context "when no short answer exist" do
66
- let(:first_type) { "long_answer" }
67
-
68
- it "shows session token" do
69
- expect(page).to have_no_content(answer1.body)
70
- expect(page).to have_content(answer1.session_token)
71
- expect(page).to have_content(answer2.session_token)
72
- expect(page).to have_content(answer3.session_token)
73
- expect(page).to have_content("User identifier")
74
- end
75
- end
76
-
77
- context "when multiple answer choice" do
78
- let(:first_type) { "multiple_option" }
79
- let!(:answer1) { create(:answer, questionnaire:, question: first, body: nil) }
80
- let!(:answer_option) { create(:answer_option, question: first) }
81
- let!(:answer_choice) { create(:answer_choice, answer: answer1, answer_option:, body: translated(answer_option.body, locale: I18n.locale)) }
82
-
83
- it "shows the answers page with custom body" do
84
- new_window = window_opened_by { find_all("a.action-icon.action-icon--eye").first.click }
85
-
86
- page.within_window(new_window) do
87
- within "#answers" do
88
- expect(page).to have_css("dt", text: translated(first.body))
89
- expect(page).to have_css("li", text: translated(answer_option.body))
90
- end
91
- end
92
- end
93
- end
94
- end
95
-
96
- context "and managing individual answer page" do
97
- let!(:answer11) { create(:answer, questionnaire:, body: "", user: answer1.user, question: second) }
98
-
99
- before do
100
- click_on "Manage questions"
101
- click_on "Show responses"
102
- end
103
-
104
- it "shows all the questions and responses" do
105
- click_on answer1.body, match: :first
106
- expect(page).to have_content(first.body["en"])
107
- expect(page).to have_content(second.body["en"])
108
- expect(page).to have_content(answer1.body)
109
- end
110
-
111
- it "first answer has a next link" do
112
- click_on answer1.body, match: :first
113
- expect(page).to have_link("Next ›")
114
- expect(page).to have_no_link("‹ Prev")
115
- end
116
-
117
- it "second answer has prev/next links" do
118
- click_on answer2.body, match: :first
119
- expect(page).to have_link("Next ›")
120
- expect(page).to have_link("‹ Prev")
121
- end
122
-
123
- it "third answer has prev link" do
124
- click_on answer3.session_token, match: :first
125
- expect(page).to have_no_link("Next ›")
126
- expect(page).to have_link("‹ Prev")
127
- end
128
-
129
- it "third answer has download link for the attachments" do
130
- click_on answer3.session_token, match: :first
131
- expect(page).to have_content(translated(file_answer.attachments.first.title))
132
- expect(page).to have_content(translated(file_answer.attachments.second.title))
133
- end
134
-
135
- context "when the file answer does not have a title for the attachment" do
136
- let!(:file_answer) { create(:answer, questionnaire:, question: third, body: nil, user: answer3.user, session_token: answer3.session_token) }
137
-
138
- before do
139
- create(:attachment, :with_image, attached_to: file_answer, title: {}, description: {})
140
- end
141
-
142
- it "third answer has download link for the attachments" do
143
- click_on answer3.session_token, match: :first
144
- expect(page).to have_content("Download attachment")
145
- end
146
- end
147
- end
148
- end
149
- end
@@ -1,105 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Decidim
4
- module Forms
5
- # This class serializes the answers given by a User for questionnaire so can be
6
- # exported to CSV, JSON or other formats.
7
- class UserAnswersSerializer < Decidim::Exporters::Serializer
8
- include Decidim::TranslationsHelper
9
-
10
- # Public: Initializes the serializer with a collection of Answers.
11
- def initialize(answers)
12
- @answers = answers
13
- end
14
-
15
- # Public: Exports a hash with the serialized data for the user answers.
16
- def serialize
17
- answers_hash = hash_for(@answers.first)
18
- answers_hash.merge!(questions_hash)
19
-
20
- @answers.each do |answer|
21
- answers_hash[translated_question_key(answer.question.position, answer.question.body)] = normalize_body(answer)
22
- end
23
-
24
- answers_hash
25
- end
26
-
27
- private
28
-
29
- attr_reader :answers
30
- alias resource answers
31
-
32
- def hash_for(answer)
33
- {
34
- answer_translated_attribute_name(:id) => answer&.session_token,
35
- answer_translated_attribute_name(:created_at) => answer&.created_at,
36
- answer_translated_attribute_name(:ip_hash) => answer&.ip_hash,
37
- answer_translated_attribute_name(:user_status) => answer_translated_attribute_name(answer&.decidim_user_id.present? ? "registered" : "unregistered")
38
- }
39
- end
40
-
41
- def questions_hash
42
- questionnaire_id = @answers.first&.decidim_questionnaire_id
43
- return {} unless questionnaire_id
44
-
45
- questions = Decidim::Forms::Question.where(decidim_questionnaire_id: questionnaire_id).order(:position)
46
- return {} if questions.none?
47
-
48
- questions.each.inject({}) do |serialized, question|
49
- serialized.update(
50
- translated_question_key(question.position, question.body) => ""
51
- )
52
- end
53
- end
54
-
55
- def translated_question_key(idx, body)
56
- "#{idx + 1}. #{translated_attribute(body)}"
57
- end
58
-
59
- def normalize_body(answer)
60
- answer.body ||
61
- normalize_attachments(answer) ||
62
- normalize_choices(answer, answer.choices)
63
- end
64
-
65
- def normalize_attachments(answer)
66
- return if answer.attachments.blank?
67
-
68
- answer.attachments.map(&:url)
69
- end
70
-
71
- def normalize_choices(answer, choices)
72
- if answer.question.matrix?
73
- normalize_matrix_choices(answer, choices)
74
- else
75
- choices.map do |choice|
76
- format_free_text_for choice
77
- end
78
- end
79
- end
80
-
81
- def normalize_matrix_choices(answer, choices)
82
- answer.question.matrix_rows.to_h do |matrix_row|
83
- row_body = translated_attribute(matrix_row.body)
84
-
85
- row_choices = answer.question.answer_options.map do |answer_option|
86
- choice = choices.find_by(matrix_row:, answer_option:)
87
- choice.try(:custom_body) || choice.try(:body)
88
- end
89
-
90
- [row_body, row_choices]
91
- end
92
- end
93
-
94
- def answer_translated_attribute_name(attribute)
95
- I18n.t(attribute.to_sym, scope: "decidim.forms.user_answers_serializer")
96
- end
97
-
98
- def format_free_text_for(choice)
99
- return choice.try(:body) if choice.try(:custom_body).blank?
100
-
101
- "#{choice.try(:body)} (#{choice.try(:custom_body)})"
102
- end
103
- end
104
- end
105
- end