decidim-demographics 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.
- checksums.yaml +7 -0
- data/README.md +26 -0
- data/Rakefile +3 -0
- data/app/commands/decidim/demographics/admin/update_demographics_settings.rb +44 -0
- data/app/commands/decidim/demographics/delete_demographic_data.rb +24 -0
- data/app/controllers/decidim/demographics/admin/application_controller.rb +42 -0
- data/app/controllers/decidim/demographics/admin/publish_responses_controller.rb +24 -0
- data/app/controllers/decidim/demographics/admin/questions_controller.rb +33 -0
- data/app/controllers/decidim/demographics/admin/responses_controller.rb +36 -0
- data/app/controllers/decidim/demographics/admin/settings_controller.rb +33 -0
- data/app/controllers/decidim/demographics/application_controller.rb +17 -0
- data/app/controllers/decidim/demographics/demographics_controller.rb +70 -0
- data/app/forms/decidim/demographics/admin/demographics_settings_form.rb +11 -0
- data/app/helpers/decidim/demographics/publish_responses_helper.rb +104 -0
- data/app/models/decidim/demographics/application_record.rb +10 -0
- data/app/models/decidim/demographics/demographic.rb +9 -0
- data/app/packs/entrypoints/decidim_demographics.js +1 -0
- data/app/packs/stylesheets/decidim/demographics/demographics.scss +37 -0
- data/app/permissions/decidim/demographics/admin/permissions.rb +22 -0
- data/app/permissions/decidim/demographics/permissions.rb +23 -0
- data/app/views/decidim/demographics/admin/demographics/_tabs_menu.html.erb +15 -0
- data/app/views/decidim/demographics/admin/publish_responses/index.html.erb +36 -0
- data/app/views/decidim/demographics/admin/questions/_questions_form.html.erb +74 -0
- data/app/views/decidim/demographics/admin/questions/edit.html.erb +13 -0
- data/app/views/decidim/demographics/admin/responses/index.html.erb +98 -0
- data/app/views/decidim/demographics/admin/settings/show.html.erb +23 -0
- data/app/views/decidim/demographics/demographics/_questionnaire.html.erb +88 -0
- data/app/views/decidim/demographics/demographics/_response.html.erb +41 -0
- data/app/views/decidim/demographics/demographics/responses/_single_option.html.erb +19 -0
- data/app/views/decidim/demographics/demographics/show.html.erb +25 -0
- data/config/assets.rb +8 -0
- data/config/locales/am-ET.yml +1 -0
- data/config/locales/ar.yml +1 -0
- data/config/locales/bg.yml +1 -0
- data/config/locales/bn-BD.yml +1 -0
- data/config/locales/bs-BA.yml +1 -0
- data/config/locales/ca-IT.yml +79 -0
- data/config/locales/ca.yml +79 -0
- data/config/locales/cs.yml +55 -0
- data/config/locales/da.yml +1 -0
- data/config/locales/de.yml +79 -0
- data/config/locales/el.yml +1 -0
- data/config/locales/en.yml +79 -0
- data/config/locales/eo.yml +1 -0
- data/config/locales/es-MX.yml +79 -0
- data/config/locales/es-PY.yml +79 -0
- data/config/locales/es.yml +79 -0
- data/config/locales/et.yml +1 -0
- data/config/locales/eu.yml +79 -0
- data/config/locales/fa-IR.yml +1 -0
- data/config/locales/fi-plain.yml +79 -0
- data/config/locales/fi.yml +79 -0
- data/config/locales/fr-CA.yml +79 -0
- data/config/locales/fr.yml +79 -0
- data/config/locales/ga-IE.yml +1 -0
- data/config/locales/gl.yml +1 -0
- data/config/locales/gn-PY.yml +1 -0
- data/config/locales/he-IL.yml +1 -0
- data/config/locales/hr.yml +1 -0
- data/config/locales/hu.yml +1 -0
- data/config/locales/id-ID.yml +1 -0
- data/config/locales/is-IS.yml +1 -0
- data/config/locales/it.yml +1 -0
- data/config/locales/ja.yml +79 -0
- data/config/locales/ka-GE.yml +1 -0
- data/config/locales/kaa.yml +1 -0
- data/config/locales/ko.yml +1 -0
- data/config/locales/lb.yml +1 -0
- data/config/locales/lo-LA.yml +1 -0
- data/config/locales/lt.yml +1 -0
- data/config/locales/lv.yml +1 -0
- data/config/locales/mt.yml +1 -0
- data/config/locales/nl.yml +1 -0
- data/config/locales/no.yml +8 -0
- data/config/locales/oc-FR.yml +1 -0
- data/config/locales/om-ET.yml +1 -0
- data/config/locales/pl.yml +1 -0
- data/config/locales/pt-BR.yml +1 -0
- data/config/locales/pt.yml +1 -0
- data/config/locales/ro-RO.yml +1 -0
- data/config/locales/ru.yml +1 -0
- data/config/locales/si-LK.yml +1 -0
- data/config/locales/sk.yml +1 -0
- data/config/locales/sl.yml +1 -0
- data/config/locales/so-SO.yml +1 -0
- data/config/locales/sq-AL.yml +1 -0
- data/config/locales/sr-CS.yml +1 -0
- data/config/locales/sv.yml +79 -0
- data/config/locales/sw-KE.yml +1 -0
- data/config/locales/th-TH.yml +1 -0
- data/config/locales/ti-ER.yml +1 -0
- data/config/locales/tr-TR.yml +1 -0
- data/config/locales/uk.yml +1 -0
- data/config/locales/val-ES.yml +1 -0
- data/config/locales/vi.yml +1 -0
- data/config/locales/zh-CN.yml +1 -0
- data/config/locales/zh-TW.yml +1 -0
- data/db/migrate/20250305200253_create_decidim_demographics_demographics.rb +12 -0
- data/decidim-demographics.gemspec +37 -0
- data/lib/decidim/demographics/admin.rb +10 -0
- data/lib/decidim/demographics/admin_engine.rb +83 -0
- data/lib/decidim/demographics/engine.rb +41 -0
- data/lib/decidim/demographics/test/factories.rb +16 -0
- data/lib/decidim/demographics/version.rb +10 -0
- data/lib/decidim/demographics.rb +52 -0
- data/lib/tasks/decidim_demographics.rake +14 -0
- metadata +195 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 2572bda270734d954964af2fc083221aa83af70d7455b5527a865a969b71f7e7
|
|
4
|
+
data.tar.gz: 604ec62cd5b07aa7ac8539f9a9d21e988eb3e7e4d49a194c0decae258038a93f
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 917f79eef27f12df2f0962e76d1dc8d54ac9fce118279b276dbf287f505da9172c560e0ae245360715055be6e7eccaf5a58cb05b089bae601811e51063be400a
|
|
7
|
+
data.tar.gz: 83f708b0437594f145095e68a8bbffc2c5b31a17f307953661f197e5ab97351c152b6d48ca70122ed21ab7099deda62030a11012cbd8a2000adc0451c0533dea
|
data/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Decidim::Demographics
|
|
2
|
+
|
|
3
|
+
The demographic data module allows participants to voluntarily donate their demographic information for statistical purposes. The collected data will be anonymized, aggregated, and visualized in compliance with GDPR regulations. This feature aims to enhance the understanding of participant demographics while ensuring transparency and data privacy.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add this line to your application's Gemfile:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
gem "decidim-demographics"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
And then execute:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
bundle
|
|
17
|
+
bin/rails decidim_demographics:install:migrations
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Contributing
|
|
21
|
+
|
|
22
|
+
See [Decidim](https://github.com/decidim/decidim).
|
|
23
|
+
|
|
24
|
+
## License
|
|
25
|
+
|
|
26
|
+
This engine is distributed under the GNU AFFERO GENERAL PUBLIC LICENSE.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Demographics
|
|
5
|
+
module Admin
|
|
6
|
+
class UpdateDemographicsSettings < Decidim::Command
|
|
7
|
+
# Initializes an UpdateDemographicsSettings Command.
|
|
8
|
+
#
|
|
9
|
+
# @param form - The form from which to get the data.
|
|
10
|
+
def initialize(form)
|
|
11
|
+
@form = form
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
attr_reader :form
|
|
15
|
+
|
|
16
|
+
delegate :current_organization, to: :form
|
|
17
|
+
delegate :current_user, to: :form
|
|
18
|
+
|
|
19
|
+
# Updates the demographic data if valid.
|
|
20
|
+
#
|
|
21
|
+
# Broadcasts :ok if successful, :invalid otherwise.
|
|
22
|
+
def call
|
|
23
|
+
Decidim.traceability.perform_action!("update", demographic, current_user) do
|
|
24
|
+
update_demographic_settings
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
broadcast(:ok)
|
|
28
|
+
rescue ActiveRecord::RecordInvalid
|
|
29
|
+
broadcast(:invalid)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def demographic
|
|
35
|
+
@demographic ||= Decidim::Demographics::Demographic.where(organization: current_organization).first_or_create!
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def update_demographic_settings
|
|
39
|
+
demographic.update!(collect_data: form.collect_data)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Demographics
|
|
5
|
+
class DeleteDemographicData < Decidim::Command
|
|
6
|
+
# Initializes a DeleteDemographicData Command.
|
|
7
|
+
#
|
|
8
|
+
# @param questionnaire [Decidim::Forms::Questionnaire] The questionnaire containing demographic data
|
|
9
|
+
# @param user [Decidim::User] The user whose demographic data should be deleted
|
|
10
|
+
def initialize(questionnaire, user)
|
|
11
|
+
@questionnaire = questionnaire
|
|
12
|
+
@user = user
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def call
|
|
16
|
+
@questionnaire.responses.where(user: @user).destroy_all
|
|
17
|
+
|
|
18
|
+
broadcast(:ok)
|
|
19
|
+
rescue ActiveRecord::RecordInvalid
|
|
20
|
+
broadcast(:invalid)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Demographics
|
|
5
|
+
module Admin
|
|
6
|
+
# This controller is the abstract class from which all other controllers of
|
|
7
|
+
# this engine inherit.
|
|
8
|
+
#
|
|
9
|
+
# Note that it inherits from `Decidim::Admin::ApplicationController`, which
|
|
10
|
+
# overrides its layout and provides all kinds of useful methods.
|
|
11
|
+
class ApplicationController < Decidim::Admin::ApplicationController
|
|
12
|
+
register_permissions(::Decidim::Demographics::Admin::ApplicationController,
|
|
13
|
+
::Decidim::Demographics::Admin::Permissions,
|
|
14
|
+
::Decidim::Admin::Permissions)
|
|
15
|
+
|
|
16
|
+
layout "decidim/admin/insights"
|
|
17
|
+
|
|
18
|
+
add_breadcrumb_item_from_menu :admin_settings_menu
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def questionnaire
|
|
23
|
+
@questionnaire ||= Decidim::Forms::Questionnaire.where(questionnaire_for:).first_or_create
|
|
24
|
+
@questionnaire.override_edit!
|
|
25
|
+
@questionnaire
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def questionnaire_for
|
|
29
|
+
demographic
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def permission_class_chain
|
|
33
|
+
::Decidim.permissions_registry.chain_for(::Decidim::Demographics::Admin::ApplicationController)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def demographic
|
|
37
|
+
@demographic ||= Decidim::Demographics::Demographic.where(organization: current_organization).first_or_create!
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Demographics
|
|
5
|
+
module Admin
|
|
6
|
+
# Mind that even though the name of the controller, currently we do not allow the publication.
|
|
7
|
+
# This is only for showing to the admin the visualization of the responses
|
|
8
|
+
class PublishResponsesController < Admin::ApplicationController
|
|
9
|
+
include Decidim::Forms::Admin::Concerns::HasQuestionnaireResponsesUrlHelper
|
|
10
|
+
|
|
11
|
+
helper PublishResponsesHelper
|
|
12
|
+
helper_method :questionnaire_for, :questionnaire
|
|
13
|
+
|
|
14
|
+
def index
|
|
15
|
+
enforce_permission_to(:index, :demographics)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def questionnaire_url
|
|
19
|
+
responses_path
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Demographics
|
|
5
|
+
module Admin
|
|
6
|
+
class QuestionsController < Admin::ApplicationController
|
|
7
|
+
include Decidim::Forms::Admin::Concerns::HasQuestionnaire
|
|
8
|
+
include Decidim::Forms::Admin::Concerns::HasQuestionnaireResponsesUrlHelper
|
|
9
|
+
|
|
10
|
+
def edit_questions_template = "decidim/demographics/admin/questions/edit"
|
|
11
|
+
|
|
12
|
+
def after_update_url = edit_questions_questions_path
|
|
13
|
+
|
|
14
|
+
def questionnaire_participants_url = decidim_admin_demographics.responses_path
|
|
15
|
+
|
|
16
|
+
def update_url = update_questions_questions_path
|
|
17
|
+
|
|
18
|
+
def response_options_url(_params) = decidim_admin_demographics.responses_path
|
|
19
|
+
|
|
20
|
+
def questionnaire
|
|
21
|
+
@questionnaire ||= Decidim::Forms::Questionnaire.where(questionnaire_for:).first_or_initialize
|
|
22
|
+
@questionnaire.override_edit!
|
|
23
|
+
Decidim::Demographics.create_default_questionnaire!(@questionnaire)
|
|
24
|
+
@questionnaire
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def questionnaire_for = demographic
|
|
28
|
+
|
|
29
|
+
def edit_questionnaire_title = t(:title, scope: "decidim.demographics.admin.questions.edit")
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Demographics
|
|
5
|
+
module Admin
|
|
6
|
+
class ResponsesController < Admin::ApplicationController
|
|
7
|
+
include Decidim::Forms::Admin::Concerns::HasQuestionnaireResponses
|
|
8
|
+
helper_method :questionnaire_for, :questionnaire
|
|
9
|
+
|
|
10
|
+
def index
|
|
11
|
+
enforce_permission_to :index, permission_subject
|
|
12
|
+
|
|
13
|
+
@query = paginate(collection)
|
|
14
|
+
@participants = participants(@query)
|
|
15
|
+
@total = questionnaire.count_participants
|
|
16
|
+
@survey = questionnaire_for
|
|
17
|
+
|
|
18
|
+
render template: "decidim/demographics/admin/responses/index"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def permission_subject = :demographics_responses
|
|
22
|
+
|
|
23
|
+
def questionnaire_for = demographic
|
|
24
|
+
|
|
25
|
+
def questionnaire_export_response_url(id) = export_response_response_path(id:)
|
|
26
|
+
|
|
27
|
+
def questionnaire_url = edit_questions_questions_path
|
|
28
|
+
|
|
29
|
+
# Specify where to redirect after exporting a user response
|
|
30
|
+
def questionnaire_participant_responses_url(id) = response_path(id:)
|
|
31
|
+
|
|
32
|
+
def questionnaire_participants_url = decidim_admin_demographics.responses_path
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Demographics
|
|
5
|
+
module Admin
|
|
6
|
+
class SettingsController < Decidim::Demographics::Admin::ApplicationController
|
|
7
|
+
def show
|
|
8
|
+
enforce_permission_to(:update, :demographics)
|
|
9
|
+
|
|
10
|
+
@form = form(Admin::DemographicsSettingsForm).from_model(demographic)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def update
|
|
14
|
+
enforce_permission_to(:update, :demographics)
|
|
15
|
+
|
|
16
|
+
@form = form(Admin::DemographicsSettingsForm).from_params(params)
|
|
17
|
+
|
|
18
|
+
Admin::UpdateDemographicsSettings.call(@form) do
|
|
19
|
+
on(:ok) do
|
|
20
|
+
flash[:notice] = I18n.t("update.success", scope: "decidim.demographics.admin.settings")
|
|
21
|
+
redirect_to decidim_admin_demographics.settings_path
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
on(:invalid) do
|
|
25
|
+
flash.now[:alert] = I18n.t("update.invalid", scope: "decidim.demographics.admin.settings")
|
|
26
|
+
render action: "edit", status: :unprocessable_entity
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Demographics
|
|
5
|
+
class ApplicationController < Decidim::ApplicationController
|
|
6
|
+
include FormFactory
|
|
7
|
+
register_permissions(::Decidim::Demographics::ApplicationController,
|
|
8
|
+
::Decidim::Demographics::Permissions,
|
|
9
|
+
::Decidim::Admin::Permissions,
|
|
10
|
+
::Decidim::Permissions)
|
|
11
|
+
|
|
12
|
+
def permission_class_chain
|
|
13
|
+
::Decidim.permissions_registry.chain_for(::Decidim::Demographics::ApplicationController)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Demographics
|
|
5
|
+
class DemographicsController < Decidim::Demographics::ApplicationController
|
|
6
|
+
include Decidim::Forms::Concerns::HasQuestionnaire
|
|
7
|
+
include Decidim::UserProfile
|
|
8
|
+
|
|
9
|
+
helper_method :allow_editing_responses?, :demographic
|
|
10
|
+
|
|
11
|
+
def show
|
|
12
|
+
@form = form(Decidim::Forms::QuestionnaireForm).from_model(questionnaire)
|
|
13
|
+
@form.add_responses!(questionnaire:, session_token:, ip_hash:)
|
|
14
|
+
@form.allow_editing_responses = true
|
|
15
|
+
|
|
16
|
+
render template:
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def destroy
|
|
20
|
+
DeleteDemographicData.call(questionnaire, current_user) do
|
|
21
|
+
on(:ok) do
|
|
22
|
+
flash[:notice] = I18n.t("destroy.success", scope: "decidim.demographics")
|
|
23
|
+
redirect_to demographics_engine.demographics_path
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
on(:invalid) do
|
|
27
|
+
flash.now[:alert] = I18n.t("destroy.error", scope: "decidim.demographics")
|
|
28
|
+
redirect_to demographics_engine.demographics_path
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def template
|
|
34
|
+
"decidim/demographics/demographics/show"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def after_response_path = demographics_path
|
|
40
|
+
|
|
41
|
+
def form_path = demographics_path
|
|
42
|
+
|
|
43
|
+
def update_url = respond_demographics_path
|
|
44
|
+
|
|
45
|
+
def allow_responses?
|
|
46
|
+
demographic.collect_data?
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def allow_editing_responses?
|
|
50
|
+
demographic.collect_data?
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def questionnaire_for = demographic
|
|
54
|
+
|
|
55
|
+
def demographic
|
|
56
|
+
@demographic ||= Decidim::Demographics::Demographic.where(organization: current_organization).first_or_create!
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def enforce_permission_to_respond_questionnaire
|
|
60
|
+
enforce_permission_to :respond, :demographics
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def questionnaire
|
|
64
|
+
@questionnaire ||= Decidim::Forms::Questionnaire.where(questionnaire_for:).first_or_initialize
|
|
65
|
+
Decidim::Demographics.create_default_questionnaire!(@questionnaire)
|
|
66
|
+
@questionnaire
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Demographics
|
|
5
|
+
module PublishResponsesHelper
|
|
6
|
+
def question_response_is_publicable(question_type)
|
|
7
|
+
ignored_question_types = %w(short_response long_response separator files).freeze
|
|
8
|
+
|
|
9
|
+
ignored_question_types.exclude?(question_type)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Renders the chart for the given question.
|
|
13
|
+
# Uses chartkick to render the chart.
|
|
14
|
+
#
|
|
15
|
+
# @param question_id [Integer] the question id for Decidim:
|
|
16
|
+
def chart_for_question(question_id)
|
|
17
|
+
question = Decidim::Forms::Question.includes(responses: { choices: [:response_option, :matrix_row] }).find(question_id)
|
|
18
|
+
|
|
19
|
+
Chartkick.options = {
|
|
20
|
+
library: { animation: { easing: "easeOutQuart" } },
|
|
21
|
+
colors: colors_list
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
case question.question_type
|
|
25
|
+
when "single_option", "multiple_option"
|
|
26
|
+
options_column_chart_wrapper(question)
|
|
27
|
+
when "matrix_single", "matrix_multiple"
|
|
28
|
+
matrix_stack_chart_wrapper(question)
|
|
29
|
+
when "sorting"
|
|
30
|
+
sorting_stack_chart_wrapper(question)
|
|
31
|
+
else
|
|
32
|
+
"Unknown question type"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def options_column_chart_wrapper(question)
|
|
37
|
+
tally = question.responses.map { |response| response.choices.map { |choice| translated_attribute(choice.response_option.body) } }.tally
|
|
38
|
+
|
|
39
|
+
column_chart(tally, download: true)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def colors_list
|
|
43
|
+
%w(
|
|
44
|
+
#3366CC
|
|
45
|
+
#DC3912
|
|
46
|
+
#FF9900
|
|
47
|
+
#109618
|
|
48
|
+
#3B3EAC
|
|
49
|
+
#0099C6
|
|
50
|
+
#DD4477
|
|
51
|
+
#66AA00
|
|
52
|
+
#B82E2E
|
|
53
|
+
#316395
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def matrix_stack_chart_wrapper(question)
|
|
60
|
+
counts = Hash.new { |hash, key| hash[key] = Hash.new(0) }
|
|
61
|
+
|
|
62
|
+
question.responses.each do |response|
|
|
63
|
+
response.choices.each do |choice|
|
|
64
|
+
name = translated_attribute(choice.response_option.body)
|
|
65
|
+
row = translated_attribute(choice.matrix_row.body)
|
|
66
|
+
|
|
67
|
+
counts[name][row] += 1
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
tally = counts.map do |name, row_data|
|
|
72
|
+
{
|
|
73
|
+
name:,
|
|
74
|
+
data: row_data.map { |row, count| [row, count] }
|
|
75
|
+
}
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
column_chart(tally, stacked: true, legend: :right, download: true)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def sorting_stack_chart_wrapper(question)
|
|
82
|
+
counts = Hash.new { |hash, key| hash[key] = Hash.new(0) }
|
|
83
|
+
|
|
84
|
+
question.responses.each do |response|
|
|
85
|
+
response.choices.each do |choice|
|
|
86
|
+
name = translated_attribute(choice.response_option.body)
|
|
87
|
+
row = choice.position + 1
|
|
88
|
+
|
|
89
|
+
counts[row][name] += 1
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
tally = counts.map do |name, row_data|
|
|
94
|
+
{
|
|
95
|
+
name:,
|
|
96
|
+
data: row_data.map { |row, count| [row, count] }
|
|
97
|
+
}
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
bar_chart(tally.sort_by { |data| data[:name] }, stacked: true, download: true)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "stylesheets/decidim/demographics/demographics.scss"
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
.demographic {
|
|
2
|
+
.response-questionnaire {
|
|
3
|
+
@apply border-t-0 border-background pt-8;
|
|
4
|
+
|
|
5
|
+
&__question-label {
|
|
6
|
+
@apply before:hidden;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
&__step {
|
|
10
|
+
@apply space-y-6;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
&__footer {
|
|
14
|
+
@apply flex flex-col gap-6 pt-6 border-t-2 border-background text-gray-2 only:border-t-0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* text-like inputs */
|
|
18
|
+
input[type="date"],
|
|
19
|
+
input[type="datetime-local"],
|
|
20
|
+
input[type="email"],
|
|
21
|
+
input[type="month"],
|
|
22
|
+
input[type="number"],
|
|
23
|
+
input[type="password"],
|
|
24
|
+
input[type="search"],
|
|
25
|
+
input[type="tel"],
|
|
26
|
+
input[type="text"],
|
|
27
|
+
input[type="time"],
|
|
28
|
+
input[type="url"],
|
|
29
|
+
input[type="week"],
|
|
30
|
+
select,
|
|
31
|
+
textarea {
|
|
32
|
+
&:not(.reset-defaults) {
|
|
33
|
+
@apply border border-gray;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Demographics
|
|
5
|
+
module Admin
|
|
6
|
+
class Permissions < Decidim::DefaultPermissions
|
|
7
|
+
def permissions
|
|
8
|
+
return permission_action unless user
|
|
9
|
+
return permission_action unless permission_action.scope == :admin
|
|
10
|
+
|
|
11
|
+
toggle_allow(user.admin?) if permission_action.subject == :demographics && permission_action.action == :index
|
|
12
|
+
toggle_allow(user.admin?) if permission_action.subject == :demographics && permission_action.action == :update
|
|
13
|
+
toggle_allow(user.admin?) if permission_action.subject == :demographics_responses && permission_action.action == :index
|
|
14
|
+
toggle_allow(user.admin?) if permission_action.subject == :demographics_responses && permission_action.action == :show
|
|
15
|
+
toggle_allow(user.admin?) if permission_action.subject == :demographics_responses && permission_action.action == :export_response
|
|
16
|
+
|
|
17
|
+
permission_action
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Demographics
|
|
5
|
+
class Permissions < Decidim::DefaultPermissions
|
|
6
|
+
def permissions
|
|
7
|
+
return permission_action unless user
|
|
8
|
+
return permission_action unless permission_action.scope == :public
|
|
9
|
+
return permission_action unless permission_action.subject == :demographics
|
|
10
|
+
|
|
11
|
+
toggle_allow(demographic.collect_data?) if permission_action.action == :respond
|
|
12
|
+
|
|
13
|
+
permission_action
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def demographic
|
|
19
|
+
@demographic ||= Decidim::Demographics::Demographic.where(organization: user.organization).first_or_initialize
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<% add_decidim_page_title(t("decidim.demographics.admin.demographics.tabs_menu.title")) %>
|
|
2
|
+
|
|
3
|
+
<div class="item_show__header">
|
|
4
|
+
<h1 class="item_show__header-title">
|
|
5
|
+
<%= t(".title") %>
|
|
6
|
+
</h1>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<div class="flex justify-between">
|
|
10
|
+
<div class="w-2/3">
|
|
11
|
+
<%= admin_tabs(:admin_demographics_menu).render %>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<%= yield if block_given? %>
|
|
15
|
+
</div>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<% add_decidim_page_title(t(".title")) %>
|
|
2
|
+
|
|
3
|
+
<%= render "decidim/demographics/admin/demographics/tabs_menu" do %>
|
|
4
|
+
<%= link_to t("actions.back", scope: "decidim.forms.admin.questionnaires"), questionnaire_url, class: "main-tabs-menu__cta-button" %>
|
|
5
|
+
<% end %>
|
|
6
|
+
|
|
7
|
+
<h1 class="item_show__header-title">
|
|
8
|
+
<%= t(".description") %>
|
|
9
|
+
</h1>
|
|
10
|
+
|
|
11
|
+
<hr class="border-b border-t-0 border-gray mb-8 pb-4">
|
|
12
|
+
|
|
13
|
+
<div class="item__edit item__edit-1col">
|
|
14
|
+
<div class="item__edit-form">
|
|
15
|
+
<ol class="list-decimal list-inside">
|
|
16
|
+
<% questionnaire.questions.each do |question| %>
|
|
17
|
+
<% next if question.question_type == "separator" %>
|
|
18
|
+
<% next if question.question_type == "title_and_description" %>
|
|
19
|
+
|
|
20
|
+
<li class="<%= "text-gray" unless question_response_is_publicable(question.question_type) %> mb-6">
|
|
21
|
+
<div class="inline">
|
|
22
|
+
<span><%= translated_attribute question.body %> (<%= I18n.t("decidim.surveys.admin.publish_responses.index.responses", count: question.responses.count) %>)</span>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<% if question_response_is_publicable(question.question_type) %>
|
|
26
|
+
<div class="m-4">
|
|
27
|
+
<%= chart_for_question(question.id) %>
|
|
28
|
+
</div>
|
|
29
|
+
<% end %>
|
|
30
|
+
</li>
|
|
31
|
+
<% end %>
|
|
32
|
+
</ol>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<%= append_javascript_pack_tag "decidim_forms_admin" %>
|