decidim-accountability 0.29.2 → 0.30.0.rc2
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/cells/decidim/accountability/project/show.erb +6 -0
- data/app/cells/decidim/accountability/project_cell.rb +23 -42
- data/app/cells/decidim/accountability/result_history_cell.rb +57 -0
- data/app/cells/decidim/accountability/result_metadata_cell.rb +17 -17
- data/app/cells/decidim/accountability/status_cell.rb +9 -13
- data/app/commands/decidim/accountability/admin/create_imported_result.rb +1 -1
- data/app/commands/decidim/accountability/admin/create_result.rb +3 -2
- data/app/commands/decidim/accountability/admin/import_component_to_accountability.rb +47 -0
- data/app/commands/decidim/accountability/admin/update_imported_result.rb +1 -1
- data/app/commands/decidim/accountability/admin/update_result.rb +3 -2
- data/app/commands/decidim/accountability/admin/update_result_dates.rb +56 -0
- data/app/commands/decidim/accountability/admin/update_result_status.rb +61 -0
- data/app/commands/decidim/accountability/admin/update_result_taxonomies.rb +18 -0
- data/app/controllers/concerns/decidim/accountability/admin/filterable.rb +8 -6
- data/app/controllers/decidim/accountability/admin/import_components_controller.rb +36 -0
- data/app/controllers/decidim/accountability/admin/results_bulk_actions_controller.rb +102 -0
- data/app/controllers/decidim/accountability/admin/results_controller.rb +21 -11
- data/app/controllers/decidim/accountability/results_controller.rb +10 -7
- data/app/forms/decidim/accountability/admin/import_component_form.rb +98 -0
- data/app/forms/decidim/accountability/admin/result_bulk_actions_form.rb +18 -0
- data/app/forms/decidim/accountability/admin/result_form.rb +17 -23
- data/app/helpers/decidim/accountability/admin/application_helper.rb +0 -1
- data/app/helpers/decidim/accountability/application_helper.rb +1 -32
- data/app/helpers/decidim/accountability/breadcrumb_helper.rb +10 -14
- data/app/jobs/decidim/accountability/admin/import_projects_job.rb +5 -3
- data/app/jobs/decidim/accountability/admin/import_proposals_job.rb +70 -0
- data/app/mailers/decidim/accountability/import_proposals_mailer.rb +28 -0
- data/app/models/decidim/accountability/result.rb +23 -1
- data/app/models/decidim/accountability/status.rb +4 -0
- data/app/packs/entrypoints/decidim_accountability_admin_form.js +1 -0
- data/app/packs/src/decidim/accountability/admin/imports.js +37 -14
- data/app/packs/src/decidim/accountability/admin/index/action_button.js +42 -0
- data/app/packs/src/decidim/accountability/admin/index/action_form.js +46 -0
- data/app/packs/src/decidim/accountability/admin/index/action_selector.js +37 -0
- data/app/packs/src/decidim/accountability/admin/index/counter.js +40 -0
- data/app/packs/src/decidim/accountability/admin/index/select_all.js +29 -0
- data/app/packs/src/decidim/accountability/admin/index.js +17 -7
- data/app/packs/src/decidim/accountability/admin/result_form.js +21 -0
- data/app/packs/stylesheets/accountability.scss +5 -0
- data/app/permissions/decidim/accountability/admin/permissions.rb +3 -2
- data/app/presenters/decidim/accountability/admin_log/result_presenter.rb +1 -1
- data/app/presenters/decidim/accountability/result_presenter.rb +30 -0
- data/app/queries/decidim/accountability/metrics/results_metric_manage.rb +6 -6
- data/app/services/decidim/accountability/results_calculator.rb +4 -6
- data/app/services/decidim/accountability/results_csv_importer.rb +1 -1
- data/app/views/decidim/accountability/admin/import_components/_filters.html.erb +26 -0
- data/app/views/decidim/accountability/admin/import_components/_form.html.erb +26 -0
- data/app/views/decidim/accountability/admin/import_components/new.html.erb +14 -0
- data/app/views/decidim/accountability/admin/results/_actions.html.erb +29 -0
- data/app/views/decidim/accountability/admin/results/_form.html.erb +15 -8
- data/app/views/decidim/accountability/admin/results/_result-tr.html.erb +32 -0
- data/app/views/decidim/accountability/admin/results/_results-thead.html.erb +26 -0
- data/app/views/decidim/accountability/admin/results/bulk_actions/_dates_form.html.erb +19 -0
- data/app/views/decidim/accountability/admin/results/bulk_actions/_dropdown.html.erb +40 -0
- data/app/views/decidim/accountability/admin/results/bulk_actions/_status_form.html.erb +16 -0
- data/app/views/decidim/accountability/admin/results/bulk_actions/_submit_buttons.html.erb +4 -0
- data/app/views/decidim/accountability/admin/results/bulk_actions/_taxonomies_form.html.erb +18 -0
- data/app/views/decidim/accountability/admin/results/edit.html.erb +2 -2
- data/app/views/decidim/accountability/admin/results/index.html.erb +50 -111
- data/app/views/decidim/accountability/admin/results/manage_trash.html.erb +26 -0
- data/app/views/decidim/accountability/admin/results/new.html.erb +2 -2
- data/app/views/decidim/accountability/import_proposals_mailer/import.html.erb +2 -0
- data/app/views/decidim/accountability/results/{_home_categories.html.erb → _home_taxonomies.html.erb} +6 -8
- data/app/views/decidim/accountability/results/_project.html.erb +1 -3
- data/app/views/decidim/accountability/results/_projects_aside.html.erb +9 -7
- data/app/views/decidim/accountability/results/home.html.erb +18 -6
- data/app/views/decidim/accountability/results/index.html.erb +8 -2
- data/app/views/decidim/accountability/results/show.html.erb +5 -5
- data/config/assets.rb +3 -2
- data/config/locales/ar.yml +97 -55
- data/config/locales/bg.yml +0 -50
- data/config/locales/bs-BA.yml +0 -16
- data/config/locales/ca.yml +128 -40
- data/config/locales/cs.yml +147 -56
- data/config/locales/da.yml +0 -20
- data/config/locales/de.yml +126 -39
- data/config/locales/el.yml +0 -48
- data/config/locales/en.yml +128 -40
- data/config/locales/es-MX.yml +129 -41
- data/config/locales/es-PY.yml +129 -41
- data/config/locales/es.yml +129 -41
- data/config/locales/eu.yml +140 -53
- data/config/locales/fi-plain.yml +136 -49
- data/config/locales/fi.yml +136 -49
- data/config/locales/fr-CA.yml +111 -54
- data/config/locales/fr.yml +111 -54
- data/config/locales/ga-IE.yml +0 -13
- data/config/locales/gl.yml +0 -17
- data/config/locales/gn-PY.yml +0 -11
- data/config/locales/he-IL.yml +45 -54
- data/config/locales/hu.yml +0 -50
- data/config/locales/id-ID.yml +0 -16
- data/config/locales/is-IS.yml +0 -17
- data/config/locales/it.yml +0 -18
- data/config/locales/ja.yml +132 -47
- data/config/locales/kaa.yml +0 -8
- data/config/locales/ko.yml +0 -47
- data/config/locales/lb.yml +0 -17
- data/config/locales/lt.yml +0 -50
- data/config/locales/lv.yml +0 -19
- data/config/locales/nl.yml +0 -17
- data/config/locales/no.yml +0 -17
- data/config/locales/pl.yml +0 -47
- data/config/locales/pt-BR.yml +6 -50
- data/config/locales/pt.yml +35 -23
- data/config/locales/ro-RO.yml +0 -29
- data/config/locales/ru.yml +0 -16
- data/config/locales/si-LK.yml +0 -11
- data/config/locales/sk.yml +0 -19
- data/config/locales/sl.yml +0 -17
- data/config/locales/sq-AL.yml +0 -50
- data/config/locales/sr-CS.yml +0 -16
- data/config/locales/sv.yml +124 -36
- data/config/locales/th-TH.yml +0 -50
- data/config/locales/tr-TR.yml +0 -47
- data/config/locales/uk.yml +0 -16
- data/config/locales/zh-CN.yml +0 -20
- data/config/locales/zh-TW.yml +0 -47
- data/db/migrate/20200827154103_add_commentable_counter_cache_to_results.rb +1 -1
- data/db/migrate/20240828103202_add_deleted_at_to_decidim_accountability_results.rb +8 -0
- data/db/migrate/20240916112128_add_geolocation_fields_to_results.rb +9 -0
- data/decidim-accountability.gemspec +1 -1
- data/lib/decidim/accountability/admin_engine.rb +16 -2
- data/lib/decidim/accountability/component.rb +3 -3
- data/lib/decidim/accountability/result_serializer.rb +9 -13
- data/lib/decidim/accountability/seeds.rb +28 -21
- data/lib/decidim/accountability/test/factories.rb +6 -4
- data/lib/decidim/accountability/version.rb +1 -1
- data/lib/decidim/api/accountability_type.rb +4 -5
- data/lib/decidim/api/result_type.rb +10 -13
- data/lib/decidim/api/status_type.rb +3 -4
- data/lib/decidim/api/timeline_entry_type.rb +5 -6
- metadata +58 -30
- data/app/commands/decidim/accountability/admin/import_projects_to_accountability.rb +0 -37
- data/app/controllers/decidim/accountability/admin/projects_import_controller.rb +0 -31
- data/app/forms/decidim/accountability/admin/result_import_projects_form.rb +0 -51
- data/app/views/decidim/accountability/admin/projects_import/new.html.erb +0 -45
- data/app/views/decidim/accountability/results/_nav_breadcrumb.html.erb +0 -37
- data/app/views/decidim/accountability/results/_scope_filters.html.erb +0 -31
- /data/app/packs/entrypoints/{decidim_accountability_admin.js → decidim_accountability_admin_index.js} +0 -0
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Accountability
|
5
|
+
module Admin
|
6
|
+
class ResultsBulkActionsController < Admin::ApplicationController
|
7
|
+
include Decidim::ApplicationHelper
|
8
|
+
include Decidim::SanitizeHelper
|
9
|
+
include Decidim::Admin::ComponentTaxonomiesHelper
|
10
|
+
|
11
|
+
def update_taxonomies
|
12
|
+
enforce_permission_to :create, :bulk_update
|
13
|
+
|
14
|
+
Admin::UpdateResultTaxonomies.call(result_params[:taxonomies], result_ids, current_organization) do
|
15
|
+
on(:invalid_taxonomies) do
|
16
|
+
flash[:alert] = I18n.t(
|
17
|
+
"results.update_taxonomies.select_a_taxonomy",
|
18
|
+
scope: "decidim.accountability.admin"
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
on(:invalid_resources) do
|
23
|
+
flash[:alert] = I18n.t(
|
24
|
+
"results.update_taxonomies.select_a_result",
|
25
|
+
scope: "decidim.accountability.admin"
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
on(:update_resources_taxonomies) do |response|
|
30
|
+
if response[:successful].any?
|
31
|
+
flash[:notice] = t(
|
32
|
+
"results.update_taxonomies.success",
|
33
|
+
taxonomies: response[:taxonomies].map { |taxonomy| decidim_escape_translated(taxonomy.name) }.to_sentence,
|
34
|
+
results: response[:successful].map { |resource| decidim_escape_translated(resource.title) }.to_sentence,
|
35
|
+
scope: "decidim.accountability.admin"
|
36
|
+
)
|
37
|
+
end
|
38
|
+
if response[:errored].any?
|
39
|
+
flash[:alert] = t(
|
40
|
+
"results.update_taxonomies.invalid",
|
41
|
+
taxonomies: response[:taxonomies].map { |taxonomy| decidim_escape_translated(taxonomy.name) }.to_sentence,
|
42
|
+
results: response[:errored].map { |resource| decidim_escape_translated(resource.title) }.to_sentence,
|
43
|
+
scope: "decidim.accountability.admin"
|
44
|
+
)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
redirect_to results_path
|
50
|
+
end
|
51
|
+
|
52
|
+
def update_status
|
53
|
+
enforce_permission_to :create, :bulk_update
|
54
|
+
|
55
|
+
UpdateResultStatus.call(result_params[:decidim_accountability_status_id], result_ids, current_user) do
|
56
|
+
on(:ok) do
|
57
|
+
flash[:notice] = I18n.t("results.update_status.success", scope: "decidim.accountability.admin")
|
58
|
+
redirect_to results_path
|
59
|
+
end
|
60
|
+
|
61
|
+
on(:invalid) do
|
62
|
+
flash[:alert] = I18n.t("results.update_status.invalid", scope: "decidim.accountability.admin")
|
63
|
+
redirect_to results_path
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def update_dates
|
69
|
+
enforce_permission_to :create, :bulk_update
|
70
|
+
|
71
|
+
UpdateResultDates.call(result_params[:start_date], result_params[:end_date], result_ids, current_user) do
|
72
|
+
on(:ok) do
|
73
|
+
flash[:notice] = I18n.t("results.update_dates.success", scope: "decidim.accountability.admin")
|
74
|
+
redirect_to results_path
|
75
|
+
end
|
76
|
+
|
77
|
+
on(:invalid) do
|
78
|
+
flash[:alert] = I18n.t("results.update_dates.invalid", scope: "decidim.accountability.admin")
|
79
|
+
redirect_to results_path
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def result_ids
|
87
|
+
result_params[:result_ids].map { |ids| ids.split(",") }.flatten.map(&:to_i)
|
88
|
+
end
|
89
|
+
|
90
|
+
def result_params
|
91
|
+
@result_params ||= params.require(:result_bulk_actions).permit(
|
92
|
+
:decidim_accountability_status_id,
|
93
|
+
:start_date,
|
94
|
+
:end_date,
|
95
|
+
result_ids: [],
|
96
|
+
taxonomies: []
|
97
|
+
)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -7,13 +7,15 @@ module Decidim
|
|
7
7
|
class ResultsController < Admin::ApplicationController
|
8
8
|
include Decidim::ApplicationHelper
|
9
9
|
include Decidim::SanitizeHelper
|
10
|
+
include Decidim::Admin::ComponentTaxonomiesHelper
|
10
11
|
include Decidim::Accountability::Admin::Filterable
|
12
|
+
include Decidim::Admin::HasTrashableResources
|
11
13
|
|
12
|
-
helper_method :results, :parent_result, :parent_results, :statuses, :present
|
14
|
+
helper_method :results, :parent_result, :parent_results, :statuses, :present, :bulk_actions_form
|
13
15
|
|
14
16
|
def collection
|
15
17
|
parent_id = params[:parent_id].presence
|
16
|
-
@collection ||= Result.where(component: current_component, parent_id:).page(params[:page]).per(15)
|
18
|
+
@collection ||= Result.where(component: current_component, parent_id:).page(params[:page]).per(15).order(created_at: :asc)
|
17
19
|
end
|
18
20
|
|
19
21
|
def new
|
@@ -65,19 +67,23 @@ module Decidim
|
|
65
67
|
end
|
66
68
|
end
|
67
69
|
|
68
|
-
|
69
|
-
enforce_permission_to(:destroy, :result, result:)
|
70
|
+
private
|
70
71
|
|
71
|
-
|
72
|
-
|
73
|
-
|
72
|
+
def trashable_deleted_resource_type
|
73
|
+
:result
|
74
|
+
end
|
74
75
|
|
75
|
-
|
76
|
-
|
77
|
-
end
|
76
|
+
def trashable_deleted_resource
|
77
|
+
@trashable_deleted_resource ||= Result.with_deleted.where(component: current_component).find_by(id: params[:id])
|
78
78
|
end
|
79
79
|
|
80
|
-
|
80
|
+
def trashable_deleted_collection
|
81
|
+
@trashable_deleted_collection = filtered_collection.only_deleted.deleted_at_desc
|
82
|
+
end
|
83
|
+
|
84
|
+
def find_parent_resource
|
85
|
+
parent_result
|
86
|
+
end
|
81
87
|
|
82
88
|
def results
|
83
89
|
@results ||= filtered_collection
|
@@ -98,6 +104,10 @@ module Decidim
|
|
98
104
|
def statuses
|
99
105
|
@statuses ||= Status.where(component: current_component)
|
100
106
|
end
|
107
|
+
|
108
|
+
def bulk_actions_form
|
109
|
+
@bulk_actions_form ||= ResultBulkActionsForm.new(result_ids: [])
|
110
|
+
end
|
101
111
|
end
|
102
112
|
end
|
103
113
|
end
|
@@ -8,7 +8,7 @@ module Decidim
|
|
8
8
|
helper Decidim::TraceabilityHelper
|
9
9
|
helper Decidim::Accountability::BreadcrumbHelper
|
10
10
|
|
11
|
-
helper_method :results, :result, :
|
11
|
+
helper_method :results, :result, :first_class_taxonomies, :count_calculator
|
12
12
|
|
13
13
|
before_action :set_controller_breadcrumb
|
14
14
|
|
@@ -16,6 +16,10 @@ module Decidim
|
|
16
16
|
raise ActionController::RoutingError, "Not Found" unless result
|
17
17
|
end
|
18
18
|
|
19
|
+
def home
|
20
|
+
@all_geocoded_results = results.geocoded
|
21
|
+
end
|
22
|
+
|
19
23
|
private
|
20
24
|
|
21
25
|
def results
|
@@ -38,17 +42,16 @@ module Decidim
|
|
38
42
|
def default_filter_params
|
39
43
|
{
|
40
44
|
search_text_cont: "",
|
41
|
-
|
42
|
-
with_category: ""
|
45
|
+
taxonomies_part_of_contains: ""
|
43
46
|
}
|
44
47
|
end
|
45
48
|
|
46
|
-
def
|
47
|
-
@
|
49
|
+
def first_class_taxonomies
|
50
|
+
@first_class_taxonomies ||= current_organization.taxonomies.where(parent_id: current_component.available_root_taxonomies, id: current_component.available_taxonomy_ids)
|
48
51
|
end
|
49
52
|
|
50
|
-
def count_calculator(
|
51
|
-
Decidim::Accountability::ResultsCalculator.new(current_component,
|
53
|
+
def count_calculator(taxonomy_id)
|
54
|
+
Decidim::Accountability::ResultsCalculator.new(current_component, taxonomy_id).count
|
52
55
|
end
|
53
56
|
|
54
57
|
def controller_breadcrumb_items
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Accountability
|
5
|
+
module Admin
|
6
|
+
# A form object to be used when admin users want to import results from
|
7
|
+
# another component into Accountability component.
|
8
|
+
class ImportComponentForm < Decidim::Form
|
9
|
+
include Decidim::TranslatableAttributes
|
10
|
+
include Decidim::HasTaxonomyFormAttributes
|
11
|
+
|
12
|
+
attribute :origin_component_id, Integer
|
13
|
+
attribute :proposal_state_id, Integer
|
14
|
+
|
15
|
+
validates :origin_component_id, presence: true
|
16
|
+
validates :filtered_items_count, numericality: { greater_than: 0 }, if: ->(form) { form.origin_component_id }
|
17
|
+
|
18
|
+
def origin_component
|
19
|
+
@origin_component ||= origin_components.find_by(id: origin_component_id)
|
20
|
+
end
|
21
|
+
|
22
|
+
def origin_components
|
23
|
+
@origin_components ||= current_participatory_space.components.where(manifest_name: %w(budgets proposals))
|
24
|
+
end
|
25
|
+
|
26
|
+
def origin_components_collection
|
27
|
+
origin_components.map do |component|
|
28
|
+
[component.name[I18n.locale.to_s], component.id]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def proposal_states_collection
|
33
|
+
Decidim::Proposals::ProposalState.where(component: origin_component).map do |state|
|
34
|
+
[translated_attribute(state.title), state.id]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
delegate :count, to: :filtered_items, prefix: true
|
39
|
+
|
40
|
+
def filtered_items
|
41
|
+
if origin_component.manifest_name == "budgets"
|
42
|
+
filtered_budget_projects
|
43
|
+
elsif origin_component.manifest_name == "proposals"
|
44
|
+
filtered_proposals
|
45
|
+
else
|
46
|
+
raise "Invalid component"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def project_already_copied?(original_project)
|
51
|
+
original_project.linked_resources(:results, "included_projects").any? do |result|
|
52
|
+
result.component == current_component
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def proposal_already_copied?(original_proposal)
|
57
|
+
original_proposal.linked_resources(:results, "included_proposals").any? do |result|
|
58
|
+
result.component == current_component
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def filtered_budget_projects
|
65
|
+
scope = Decidim::Budgets::Project.joins(:budget).selected.where(budget: { component: origin_component })
|
66
|
+
scope = filter_taxonomies(scope)
|
67
|
+
scope.reject { |project| project_already_copied?(project) }
|
68
|
+
end
|
69
|
+
|
70
|
+
def filtered_proposals
|
71
|
+
scope = Decidim::Proposals::Proposal.where(component: origin_component).not_hidden
|
72
|
+
scope = scope.where(decidim_proposals_proposal_state_id: proposal_state_id) if proposal_state_id
|
73
|
+
scope = filter_taxonomies(scope)
|
74
|
+
scope.reject { |proposal| proposal_already_copied?(proposal) }
|
75
|
+
end
|
76
|
+
|
77
|
+
def filter_taxonomies(scope)
|
78
|
+
if taxonomy_sets.any?
|
79
|
+
scope.with_any_taxonomies(*taxonomy_sets)
|
80
|
+
else
|
81
|
+
scope
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def taxonomy_sets
|
86
|
+
@taxonomy_sets ||= taxonomies.compact.map do |taxonomy_id|
|
87
|
+
taxonomy = Decidim::Taxonomy.find(taxonomy_id)
|
88
|
+
root_taxonomy_id = taxonomy.root_taxonomy.id
|
89
|
+
|
90
|
+
return nil if !root_taxonomy_id || !taxonomy_id
|
91
|
+
|
92
|
+
[root_taxonomy_id, [taxonomy_id]]
|
93
|
+
end.compact
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Accountability
|
5
|
+
module Admin
|
6
|
+
# This class holds a Form to create/update results from Decidim's admin panel.
|
7
|
+
class ResultBulkActionsForm < Decidim::Form
|
8
|
+
include Decidim::TranslationsHelper
|
9
|
+
include Decidim::HasTaxonomyFormAttributes
|
10
|
+
|
11
|
+
attribute :result_ids, Array[Integer]
|
12
|
+
attribute :start_date, Decidim::Attributes::LocalizedDate
|
13
|
+
attribute :end_date, Decidim::Attributes::LocalizedDate
|
14
|
+
attribute :decidim_accountability_status_id, Integer
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -5,14 +5,13 @@ module Decidim
|
|
5
5
|
module Admin
|
6
6
|
# This class holds a Form to create/update results from Decidim's admin panel.
|
7
7
|
class ResultForm < Decidim::Form
|
8
|
-
include TranslatableAttributes
|
9
|
-
include TranslationsHelper
|
8
|
+
include Decidim::TranslatableAttributes
|
9
|
+
include Decidim::TranslationsHelper
|
10
|
+
include Decidim::HasTaxonomyFormAttributes
|
10
11
|
|
11
12
|
translatable_attribute :title, String
|
12
13
|
translatable_attribute :description, Decidim::Attributes::RichText
|
13
14
|
|
14
|
-
attribute :decidim_scope_id, Integer
|
15
|
-
attribute :decidim_category_id, Integer
|
16
15
|
attribute :proposal_ids, Array[Integer]
|
17
16
|
attribute :project_ids, Array[Integer]
|
18
17
|
attribute :start_date, Decidim::Attributes::LocalizedDate
|
@@ -22,25 +21,26 @@ module Decidim
|
|
22
21
|
attribute :parent_id, Integer
|
23
22
|
attribute :external_id, String
|
24
23
|
attribute :weight, Float
|
24
|
+
attribute :address, String
|
25
|
+
attribute :latitude, Float
|
26
|
+
attribute :longitude, Float
|
25
27
|
|
26
28
|
validates :title, translatable_presence: true
|
27
29
|
|
28
30
|
validates :progress, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 100 }, if: ->(form) { form.progress.present? }
|
29
31
|
|
30
|
-
validates :scope, presence: true, if: ->(form) { form.decidim_scope_id.present? }
|
31
|
-
validates :decidim_scope_id, scope_belongs_to_component: true, if: ->(form) { form.decidim_scope_id.present? }
|
32
|
-
|
33
|
-
validates :category, presence: true, if: ->(form) { form.decidim_category_id.present? }
|
34
|
-
|
35
32
|
validates :parent, presence: true, if: ->(form) { form.parent_id.present? }
|
36
33
|
validates :status, presence: true, if: ->(form) { form.decidim_accountability_status_id.present? }
|
37
34
|
|
38
|
-
|
35
|
+
validates :address, geocoding: true, if: ->(form) { form.has_address? && !form.geocoded? }
|
39
36
|
|
40
37
|
def map_model(model)
|
41
38
|
self.proposal_ids = model.linked_resources(:proposals, "included_proposals").pluck(:id)
|
42
39
|
self.project_ids = model.linked_resources(:projects, "included_projects").pluck(:id)
|
43
|
-
|
40
|
+
end
|
41
|
+
|
42
|
+
def participatory_space_manifest
|
43
|
+
@participatory_space_manifest ||= current_component.participatory_space.manifest.name
|
44
44
|
end
|
45
45
|
|
46
46
|
def proposals
|
@@ -55,22 +55,16 @@ module Decidim
|
|
55
55
|
&.select(:title, :id)&.map { |a| [a.title[I18n.locale.to_s], a.id] }
|
56
56
|
end
|
57
57
|
|
58
|
-
|
59
|
-
|
60
|
-
# Returns a Decidim::Scope
|
61
|
-
def scope
|
62
|
-
@scope ||= @attributes["decidim_scope_id"].value ? current_component.scopes.find_by(id: @attributes["decidim_scope_id"].value) : current_component.scope
|
58
|
+
def geocoding_enabled?
|
59
|
+
Decidim::Map.available?(:geocoding) && current_component.settings.geocoding_enabled?
|
63
60
|
end
|
64
61
|
|
65
|
-
|
66
|
-
|
67
|
-
# Returns the scope identifier related to the result
|
68
|
-
def decidim_scope_id
|
69
|
-
super || scope&.id
|
62
|
+
def has_address?
|
63
|
+
geocoding_enabled? && address.present?
|
70
64
|
end
|
71
65
|
|
72
|
-
def
|
73
|
-
|
66
|
+
def geocoded?
|
67
|
+
latitude.present? && longitude.present?
|
74
68
|
end
|
75
69
|
|
76
70
|
def parent
|
@@ -7,6 +7,7 @@ module Decidim
|
|
7
7
|
module ApplicationHelper
|
8
8
|
include PaginateHelper
|
9
9
|
include Decidim::Comments::CommentsHelper
|
10
|
+
include Decidim::MapHelper
|
10
11
|
|
11
12
|
def display_percentage(number)
|
12
13
|
return if number.blank?
|
@@ -18,38 +19,6 @@ module Decidim
|
|
18
19
|
(defined?(current_component) && translated_attribute(current_component&.name).presence) || t("decidim.components.accountability.name")
|
19
20
|
end
|
20
21
|
|
21
|
-
def filter_items_for(participatory_space:, category:)
|
22
|
-
[
|
23
|
-
{
|
24
|
-
url: url_for(filter: { with_category: category.try(:id) }),
|
25
|
-
text: t("results.filters.all", scope: "decidim.accountability"),
|
26
|
-
icon: "apps-2-line",
|
27
|
-
active: current_scope.blank?,
|
28
|
-
sr_text: Decidim::Scope.model_name.human(count: 2)
|
29
|
-
},
|
30
|
-
*(
|
31
|
-
if participatory_space.scope
|
32
|
-
[{
|
33
|
-
url: url_for(filter: { with_scope: participatory_space.scope.id, with_category: category.try(:id) }),
|
34
|
-
text: translated_attribute(participatory_space.scope.name),
|
35
|
-
icon: resource_type_icon_key(participatory_space.scope.class),
|
36
|
-
active: participatory_space.scope.id.to_s == current_scope.to_s,
|
37
|
-
sr_text: Decidim::Scope.model_name.human(count: 1)
|
38
|
-
}]
|
39
|
-
end
|
40
|
-
),
|
41
|
-
*participatory_space.subscopes.map do |scope|
|
42
|
-
{
|
43
|
-
url: url_for(filter: { with_scope: scope.id, with_category: category.try(:id) }),
|
44
|
-
text: translated_attribute(scope.name),
|
45
|
-
icon: resource_type_icon_key(scope.class),
|
46
|
-
active: scope.id.to_s == current_scope.to_s,
|
47
|
-
sr_text: Decidim::Scope.model_name.human(count: 1)
|
48
|
-
}
|
49
|
-
end
|
50
|
-
]
|
51
|
-
end
|
52
|
-
|
53
22
|
def apply_accountability_pack_tags
|
54
23
|
append_stylesheet_pack_tag("decidim_accountability", media: "all")
|
55
24
|
append_javascript_pack_tag("decidim_accountability")
|
@@ -5,28 +5,24 @@ module Decidim
|
|
5
5
|
# Helpers needed to render the navigation breadcrumbs in results.
|
6
6
|
#
|
7
7
|
module BreadcrumbHelper
|
8
|
-
def
|
9
|
-
|
8
|
+
def progress_calculator(taxonomy_id)
|
9
|
+
Decidim::Accountability::ResultsCalculator.new(current_component, taxonomy_id).progress
|
10
10
|
end
|
11
11
|
|
12
|
-
def
|
13
|
-
|
14
|
-
end
|
15
|
-
|
16
|
-
def category
|
17
|
-
return if (category_id = params.dig(:filter, :with_category)).blank?
|
12
|
+
def taxonomy
|
13
|
+
return if (taxonomy_id = params.dig(:filter, :taxonomies_part_of_contains)).blank?
|
18
14
|
|
19
|
-
@
|
15
|
+
@taxonomy ||= current_organization.taxonomies.find(taxonomy_id.is_a?(Array) ? taxonomy_id.first : taxonomy_id)
|
20
16
|
end
|
21
17
|
|
22
|
-
def
|
23
|
-
return [] if
|
18
|
+
def parent_taxonomies(taxonomy)
|
19
|
+
return [] if taxonomy&.parent.blank? || taxonomy&.parent&.root?
|
24
20
|
|
25
|
-
[*
|
21
|
+
[*parent_taxonomies(taxonomy.parent), taxonomy.parent]
|
26
22
|
end
|
27
23
|
|
28
|
-
def
|
29
|
-
|
24
|
+
def taxonomies_hierarchy
|
25
|
+
parent_taxonomies(taxonomy)
|
30
26
|
end
|
31
27
|
end
|
32
28
|
end
|
@@ -28,11 +28,13 @@ module Decidim
|
|
28
28
|
params = {
|
29
29
|
title: project.title,
|
30
30
|
description: project.description,
|
31
|
-
|
32
|
-
scope: project.scope || project.budget.scope,
|
31
|
+
taxonomies: project.taxonomies,
|
33
32
|
component:,
|
34
33
|
status:,
|
35
|
-
progress: status&.progress || 0
|
34
|
+
progress: status&.progress || 0,
|
35
|
+
address: project.address,
|
36
|
+
latitude: project.latitude,
|
37
|
+
longitude: project.longitude
|
36
38
|
}
|
37
39
|
@result = Decidim.traceability.create!(
|
38
40
|
Result,
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Accountability
|
5
|
+
module Admin
|
6
|
+
class ImportProposalsJob < ApplicationJob
|
7
|
+
queue_as :default
|
8
|
+
|
9
|
+
def perform(proposals, component, user)
|
10
|
+
proposals.map do |id|
|
11
|
+
original_proposal = Decidim::Proposals::Proposal.find_by(id:)
|
12
|
+
|
13
|
+
new_result = create_result_from_proposal!(original_proposal, statuses(component).first, component, user)
|
14
|
+
new_result.link_resources([original_proposal], "included_proposals")
|
15
|
+
|
16
|
+
copy_attachments(original_proposal, new_result)
|
17
|
+
end.compact
|
18
|
+
Decidim::Accountability::ImportProposalsMailer.import(user, component, proposals.count).deliver_now
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def create_result_from_proposal!(proposal, status, component, user)
|
24
|
+
params = {
|
25
|
+
title: proposal.title,
|
26
|
+
description: proposal.body,
|
27
|
+
taxonomies: proposal.taxonomies,
|
28
|
+
component:,
|
29
|
+
status:,
|
30
|
+
progress: status&.progress || 0,
|
31
|
+
address: proposal.address,
|
32
|
+
latitude: proposal.latitude,
|
33
|
+
longitude: proposal.longitude
|
34
|
+
}
|
35
|
+
@result = Decidim.traceability.create!(
|
36
|
+
Result,
|
37
|
+
user,
|
38
|
+
params,
|
39
|
+
visibility: "all"
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
def copy_attachments(proposal, result)
|
44
|
+
proposal.attachments.each do |attachment|
|
45
|
+
new_attachment = Decidim::Attachment.new(
|
46
|
+
{
|
47
|
+
# Attached to needs to be always defined before the file is set
|
48
|
+
attached_to: result
|
49
|
+
}.merge(
|
50
|
+
attachment.attributes.slice("content_type", "description", "file_size", "title", "weight")
|
51
|
+
)
|
52
|
+
)
|
53
|
+
|
54
|
+
if attachment.file.attached?
|
55
|
+
new_attachment.file = attachment.file.blob
|
56
|
+
else
|
57
|
+
new_attachment.attached_uploader(:file).remote_url = attachment.attached_uploader(:file)
|
58
|
+
end
|
59
|
+
|
60
|
+
new_attachment.save!
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def statuses(component)
|
65
|
+
Decidim::Accountability::Status.where(component:).order(:progress)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Accountability
|
5
|
+
# This mailer sends a notification email containing the result of importing
|
6
|
+
# proposals to the results.
|
7
|
+
class ImportProposalsMailer < Decidim::ApplicationMailer
|
8
|
+
include Decidim::TranslatableAttributes
|
9
|
+
helper Decidim::TranslationsHelper
|
10
|
+
|
11
|
+
# Public: Sends a notification email with the result of proposals import selected proposals to Accountability
|
12
|
+
#
|
13
|
+
# user - The user to be notified.
|
14
|
+
#
|
15
|
+
# Returns nothing.
|
16
|
+
def import(user, component, proposals)
|
17
|
+
@user = user
|
18
|
+
@organization = user.organization
|
19
|
+
@component = component
|
20
|
+
@proposals = proposals
|
21
|
+
|
22
|
+
with_user(user) do
|
23
|
+
mail(to: "#{user.name} <#{user.email}>", subject: I18n.t("decidim.accountability.import_proposals_mailer.import.subject"))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -6,6 +6,7 @@ module Decidim
|
|
6
6
|
# title, description and any other useful information to render a custom result.
|
7
7
|
class Result < Accountability::ApplicationRecord
|
8
8
|
include Decidim::Resourceable
|
9
|
+
include Decidim::Taxonomizable
|
9
10
|
include Decidim::HasAttachments
|
10
11
|
include Decidim::HasAttachmentCollections
|
11
12
|
include Decidim::HasComponent
|
@@ -20,6 +21,7 @@ module Decidim
|
|
20
21
|
include Decidim::Searchable
|
21
22
|
include Decidim::TranslatableResource
|
22
23
|
include Decidim::FilterableResource
|
24
|
+
include Decidim::SoftDeletable
|
23
25
|
|
24
26
|
component_manifest_name "accountability"
|
25
27
|
|
@@ -45,10 +47,16 @@ module Decidim
|
|
45
47
|
datetime: :start_date
|
46
48
|
)
|
47
49
|
|
50
|
+
geocoded_by :address
|
51
|
+
|
48
52
|
def self.log_presenter_class_for(_log)
|
49
53
|
Decidim::Accountability::AdminLog::ResultPresenter
|
50
54
|
end
|
51
55
|
|
56
|
+
def presenter
|
57
|
+
Decidim::Accountability::ResultPresenter.new(self)
|
58
|
+
end
|
59
|
+
|
52
60
|
def update_parent_progress
|
53
61
|
return if parent.blank?
|
54
62
|
|
@@ -83,7 +91,7 @@ module Decidim
|
|
83
91
|
end
|
84
92
|
|
85
93
|
def self.ransackable_scopes(_auth_object = nil)
|
86
|
-
[:
|
94
|
+
[:with_any_taxonomies]
|
87
95
|
end
|
88
96
|
|
89
97
|
ransacker :id_string do
|
@@ -94,6 +102,20 @@ module Decidim
|
|
94
102
|
# Create the :search_text ransacker alias for searching from both of these.
|
95
103
|
ransacker_i18n_multi :search_text, [:title, :description]
|
96
104
|
|
105
|
+
def self.ransackable_attributes(auth_object = nil)
|
106
|
+
base = %w(search_text title description)
|
107
|
+
|
108
|
+
return base unless auth_object&.admin?
|
109
|
+
|
110
|
+
base + %w(id_string created_at id progress)
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.ransackable_associations(auth_object = nil)
|
114
|
+
return [] unless auth_object&.admin?
|
115
|
+
|
116
|
+
%w(taxonomies status)
|
117
|
+
end
|
118
|
+
|
97
119
|
private
|
98
120
|
|
99
121
|
# Private: When a row uses weight 1 and there is more than one, weight should not be considered
|