decidim-stratified_sortitions 0.0.1
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/LICENSE-AGPLv3.txt +661 -0
- data/README.md +91 -0
- data/Rakefile +42 -0
- data/app/cells/decidim/stratified_sortitions/stratified_sortition_cell.rb +21 -0
- data/app/cells/decidim/stratified_sortitions/stratified_sortition_l_cell.rb +22 -0
- data/app/cells/decidim/stratified_sortitions/stratified_sortition_metadata_cell.rb +63 -0
- data/app/commands/decidim/stratified_sortitions/admin/create_stratified_sortition.rb +84 -0
- data/app/commands/decidim/stratified_sortitions/admin/destroy_stratified_sortition.rb +49 -0
- data/app/commands/decidim/stratified_sortitions/admin/duplicate_stratified_sortition.rb +74 -0
- data/app/commands/decidim/stratified_sortitions/admin/import_sample.rb +40 -0
- data/app/commands/decidim/stratified_sortitions/admin/remove_uploaded_samples.rb +53 -0
- data/app/commands/decidim/stratified_sortitions/admin/update_stratified_sortition.rb +174 -0
- data/app/controllers/concerns/decidim/stratified_sortitions/orderable_stratified_sortitions.rb +37 -0
- data/app/controllers/concerns/decidim/stratified_sortitions/strata_charts_data.rb +91 -0
- data/app/controllers/decidim/stratified_sortitions/admin/application_controller.rb +19 -0
- data/app/controllers/decidim/stratified_sortitions/admin/samples_controller.rb +143 -0
- data/app/controllers/decidim/stratified_sortitions/admin/stratified_sortitions_controller.rb +235 -0
- data/app/controllers/decidim/stratified_sortitions/application_controller.rb +13 -0
- data/app/controllers/decidim/stratified_sortitions/charts_pdf_controller_helper.rb +17 -0
- data/app/controllers/decidim/stratified_sortitions/stratified_sortitions_controller.rb +61 -0
- data/app/forms/decidim/stratified_sortitions/admin/sample_upload_form.rb +15 -0
- data/app/forms/decidim/stratified_sortitions/admin/stratified_sortitions_form.rb +155 -0
- data/app/forms/decidim/stratified_sortitions/admin/stratum_form.rb +69 -0
- data/app/forms/decidim/stratified_sortitions/admin/substratum_form.rb +26 -0
- data/app/helpers/decidim/stratified_sortitions/admin/stratified_sortitions_helper.rb +18 -0
- data/app/helpers/decidim/stratified_sortitions/application_helper.rb +85 -0
- data/app/helpers/decidim/stratified_sortitions/charts_pdf_helper.rb +73 -0
- data/app/helpers/decidim/stratified_sortitions/stratified_sortition_cells_helper.rb +56 -0
- data/app/javascript/channels/sample_import_progress.js +12 -0
- data/app/jobs/decidim/stratified_sortitions/admin/execute_sortition_job.rb +21 -0
- data/app/jobs/decidim/stratified_sortitions/admin/import_sample_job.rb +170 -0
- data/app/jobs/decidim/stratified_sortitions/admin/remove_samples_job.rb +19 -0
- data/app/jobs/decidim/stratified_sortitions/admin/sortition_results_export_job.rb +28 -0
- data/app/mailers/decidim/stratified_sortitions/admin/import_mailer.rb +26 -0
- data/app/models/decidim/stratified_sortitions/application_record.rb +9 -0
- data/app/models/decidim/stratified_sortitions/panel_portfolio.rb +165 -0
- data/app/models/decidim/stratified_sortitions/sample_import.rb +14 -0
- data/app/models/decidim/stratified_sortitions/sample_participant.rb +12 -0
- data/app/models/decidim/stratified_sortitions/sample_participant_stratum.rb +11 -0
- data/app/models/decidim/stratified_sortitions/stratified_sortition.rb +74 -0
- data/app/models/decidim/stratified_sortitions/stratum.rb +22 -0
- data/app/models/decidim/stratified_sortitions/substratum.rb +19 -0
- data/app/packs/entrypoints/decidim_stratified_sortitions.js +10 -0
- data/app/packs/entrypoints/decidim_stratified_sortitions_admin.js +9 -0
- data/app/packs/entrypoints/decidim_stratified_sortitions_admin.scss +1 -0
- data/app/packs/images/decidim/stratified_sortitions/icon.svg +1 -0
- data/app/packs/src/decidim/stratified_sortitions/application.js +3 -0
- data/app/packs/src/decidim/stratified_sortitions/results_tabs.js +59 -0
- data/app/packs/src/decidim/stratified_sortitions/stratum_fields.js +302 -0
- data/app/packs/src/decidim/stratified_sortitions/substratum_fields.js +165 -0
- data/app/packs/src/decidim/stratified_sortitions/upload_sample.js +16 -0
- data/app/packs/stylesheets/decidim/stratified_sortitions/admin/stratified_sortitions.scss +210 -0
- data/app/packs/stylesheets/decidim/stratified_sortitions/charts_pdf.scss +148 -0
- data/app/packs/stylesheets/stratified_sortitions.scss +56 -0
- data/app/permissions/decidim/stratified_sortitions/admin/permissions.rb +74 -0
- data/app/permissions/decidim/stratified_sortitions/permissions.rb +23 -0
- data/app/presenters/decidim/stratified_sortitions/admin_log/stratified_sortition_presenter.rb +35 -0
- data/app/queries/decidim/stratified_sortitions/filtered_stratified_sortitions.rb +39 -0
- data/app/serializers/decidim/stratified_sortitions/sortition_result_serializer.rb +116 -0
- data/app/services/decidim/stratified_sortitions/charts_pdf_generator.rb +96 -0
- data/app/services/decidim/stratified_sortitions/fair_sortition_service.rb +247 -0
- data/app/services/decidim/stratified_sortitions/leximin/constraint_builder.rb +180 -0
- data/app/services/decidim/stratified_sortitions/leximin/distribution_solver.rb +217 -0
- data/app/services/decidim/stratified_sortitions/leximin/feasibility_checker.rb +222 -0
- data/app/services/decidim/stratified_sortitions/leximin/panel_generator.rb +149 -0
- data/app/services/decidim/stratified_sortitions/leximin/panel_sampler.rb +125 -0
- data/app/services/decidim/stratified_sortitions/leximin_selector.rb +136 -0
- data/app/services/decidim/stratified_sortitions/sortition_results_exporter.rb +161 -0
- data/app/views/decidim/admin/components/index.html.erb +54 -0
- data/app/views/decidim/stratified_sortitions/admin/import_mailer/import.html.erb +22 -0
- data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/_confirm_execute_modal.html.erb +19 -0
- data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/_confirm_participants_modal.html.erb +20 -0
- data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/_form.html.erb +55 -0
- data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/_navigation_menu.html.erb +10 -0
- data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/_strata.html.erb +22 -0
- data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/_stratum.html.erb +75 -0
- data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/_substrata.html.erb +34 -0
- data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/_substratum.html.erb +54 -0
- data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/edit.html.erb +21 -0
- data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/execute.html.erb +155 -0
- data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/export_charts_pdf.html.erb +38 -0
- data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/index.html.erb +75 -0
- data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/new.html.erb +21 -0
- data/app/views/decidim/stratified_sortitions/admin/stratified_sortitions/upload_sample.html.erb +117 -0
- data/app/views/decidim/stratified_sortitions/shared/_strata_charts.html.erb +21 -0
- data/app/views/decidim/stratified_sortitions/stratified_sortitions/_results_count.html.erb +15 -0
- data/app/views/decidim/stratified_sortitions/stratified_sortitions/_stratified_sortition.html.erb +1 -0
- data/app/views/decidim/stratified_sortitions/stratified_sortitions/_stratified_sortitions.html.erb +13 -0
- data/app/views/decidim/stratified_sortitions/stratified_sortitions/_tags.html.erb +9 -0
- data/app/views/decidim/stratified_sortitions/stratified_sortitions/index.html.erb +22 -0
- data/app/views/decidim/stratified_sortitions/stratified_sortitions/index.js.erb +5 -0
- data/app/views/decidim/stratified_sortitions/stratified_sortitions/show.html.erb +142 -0
- data/app/views/layouts/decidim/admin/_sidebar_menu.html.erb +45 -0
- data/app/views/layouts/decidim/stratified_sortitions/charts_pdf.html.erb +14 -0
- data/config/assets.rb +24 -0
- data/config/locales/ca.yml +361 -0
- data/config/locales/en.yml +360 -0
- data/config/locales/es.yml +360 -0
- data/config/locales/oc.yml +2 -0
- data/config/routes.rb +1 -0
- data/db/migrate/20251023103900_create_decidim_stratified_sortitions_stratified_sortitions.rb +18 -0
- data/db/migrate/20251104120000_create_stratified_sortitions_strata_and_substrata.decidim_stratified_sortitions.rb +27 -0
- data/db/migrate/20251219120000_create_sample_imports.rb +17 -0
- data/db/migrate/20251219120100_create_sample_participants.rb +19 -0
- data/db/migrate/20251219120200_create_sample_participant_strata.rb +16 -0
- data/db/migrate/20251230130000_change_value_to_text_in_substrata.rb +11 -0
- data/db/migrate/20260102114744_add_status_to_stratified_sortitions.rb +11 -0
- data/db/migrate/20260109105643_allow_null_value_in_substrata.rb +10 -0
- data/db/migrate/20260113121726_add_position_to_strata_and_substrata.rb +8 -0
- data/db/migrate/20260127100000_rename_weighing_to_max_quota_percentage.rb +12 -0
- data/db/migrate/20260127120000_create_panel_portfolios.rb +46 -0
- data/db/migrate/20260518000000_add_foreign_key_to_stratified_sortitions_component.rb +10 -0
- data/db/migrate/20260521000000_add_cascade_to_stratified_sortitions_foreign_keys.rb +64 -0
- data/db/migrate/20260616000000_add_execution_error_to_stratified_sortitions.rb +11 -0
- data/lib/decidim/stratified_sortitions/admin.rb +10 -0
- data/lib/decidim/stratified_sortitions/admin_engine.rb +49 -0
- data/lib/decidim/stratified_sortitions/component.rb +41 -0
- data/lib/decidim/stratified_sortitions/engine.rb +34 -0
- data/lib/decidim/stratified_sortitions/seeds.rb +24 -0
- data/lib/decidim/stratified_sortitions/test/factories.rb +80 -0
- data/lib/decidim/stratified_sortitions/version.rb +13 -0
- data/lib/decidim/stratified_sortitions.rb +29 -0
- metadata +222 -0
data/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Decidim::StratifiedSortitions
|
|
2
|
+
|
|
3
|
+
## Usage
|
|
4
|
+
|
|
5
|
+
Simply include it in your Decidim instance.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Add this line to your application's Gemfile:
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
gem 'decidim-stratified_sortitions'
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
And then execute:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
bundle
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### System Dependencies
|
|
22
|
+
|
|
23
|
+
This module uses the COIN-OR CBC solver for the LEXIMIN fair selection algorithm. You need to install CBC on your system:
|
|
24
|
+
|
|
25
|
+
**Ubuntu/Debian:**
|
|
26
|
+
```bash
|
|
27
|
+
sudo apt install coinor-cbc coinor-libcbc-dev
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**macOS (Homebrew):**
|
|
31
|
+
```bash
|
|
32
|
+
brew install cbc
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Fedora/RHEL:**
|
|
36
|
+
```bash
|
|
37
|
+
sudo dnf install coin-or-Cbc coin-or-Cbc-devel
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Import migrations
|
|
41
|
+
|
|
42
|
+
After installing the gem you must import and execute the migrations bundled with the gem:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
bundle exec rails decidim_stratified_sortitions:install:migrations
|
|
46
|
+
bundle exec rails db:migrate
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Usage
|
|
50
|
+
|
|
51
|
+
Decidim::StratifiedSortitions allows fair sortitions in two ways.
|
|
52
|
+
|
|
53
|
+
1. Generate all the panels and extract the samples at once.
|
|
54
|
+
2. Execute the sortition in two phases
|
|
55
|
+
|
|
56
|
+
See `Decidim::StratifiedSortitions::FairSortitionService` for more details on how to each workflow works.
|
|
57
|
+
|
|
58
|
+
## Running tests
|
|
59
|
+
|
|
60
|
+
Create a dummy app in your application (if not present):
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
bundle exec rake test_app
|
|
64
|
+
cd spec/decidim_dummy_app/
|
|
65
|
+
bundle exec rails decidim_stratified_sortitions:install:migrations
|
|
66
|
+
RAILS_ENV=test bundle exec rails db:migrate
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
And run tests:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Run all tests
|
|
73
|
+
bundle exec rspec spec
|
|
74
|
+
|
|
75
|
+
# Run only performance tests
|
|
76
|
+
bundle exec rspec --tag performance
|
|
77
|
+
|
|
78
|
+
# Run all tests except performance (default)
|
|
79
|
+
bundle exec rspec --tag ~performance
|
|
80
|
+
|
|
81
|
+
# Run also slow tests
|
|
82
|
+
bundle exec rspec --tag performance --tag slow
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Contributing
|
|
86
|
+
|
|
87
|
+
See [Decidim](https://github.com/decidim/decidim).
|
|
88
|
+
|
|
89
|
+
## License
|
|
90
|
+
|
|
91
|
+
See [Decidim](https://github.com/decidim/decidim).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "decidim/dev/common_rake"
|
|
4
|
+
|
|
5
|
+
def install_module(path)
|
|
6
|
+
Dir.chdir(path) do
|
|
7
|
+
system("bundle exec rake decidim_stratified_sortitions:install:migrations")
|
|
8
|
+
system("bundle exec rake db:migrate")
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def seed_db(path)
|
|
13
|
+
Dir.chdir(path) do
|
|
14
|
+
system("bundle exec rake db:seed")
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
desc "Generates a dummy app for testing"
|
|
19
|
+
task test_app: "decidim:generate_external_test_app" do
|
|
20
|
+
ENV["RAILS_ENV"] = "test"
|
|
21
|
+
install_module("spec/decidim_dummy_app")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# rubocop disable:Rails/RakeEnvironment
|
|
25
|
+
desc "Generates a development app."
|
|
26
|
+
task development_app: :environment do
|
|
27
|
+
Bundler.with_original_env do
|
|
28
|
+
generate_decidim_app(
|
|
29
|
+
"development_app",
|
|
30
|
+
"--app_name",
|
|
31
|
+
"#{base_app_name}_development_app",
|
|
32
|
+
"--path",
|
|
33
|
+
"..",
|
|
34
|
+
"--recreate_db",
|
|
35
|
+
"--demo"
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
install_module("development_app")
|
|
40
|
+
seed_db("development_app")
|
|
41
|
+
end
|
|
42
|
+
# rubocop enable:Rails/RakeEnvironment
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cell/partial"
|
|
4
|
+
|
|
5
|
+
module Decidim
|
|
6
|
+
module StratifiedSortitions
|
|
7
|
+
# This cell renders the card for an instance of a StratifiedSortition
|
|
8
|
+
# the default size is the Large Card (:l)
|
|
9
|
+
class StratifiedSortitionCell < Decidim::ViewModel
|
|
10
|
+
def show
|
|
11
|
+
cell card_size, model, @options
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def card_size
|
|
17
|
+
"decidim/stratified_sortitions/stratified_sortition_l"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cell/partial"
|
|
4
|
+
|
|
5
|
+
module Decidim
|
|
6
|
+
module StratifiedSortitions
|
|
7
|
+
# This cell renders a stratified sortition with its L-size card.
|
|
8
|
+
class StratifiedSortitionLCell < Decidim::CardLCell
|
|
9
|
+
include Decidim::StratifiedSortitions::Engine.routes.url_helpers
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def has_state?
|
|
14
|
+
true
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def metadata_cell
|
|
18
|
+
"decidim/stratified_sortitions/stratified_sortition_metadata"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module StratifiedSortitions
|
|
5
|
+
# This cell renders the stratified sortition metadata for l card
|
|
6
|
+
class StratifiedSortitionMetadataCell < Decidim::CardMetadataCell
|
|
7
|
+
include Decidim::StratifiedSortitions::ApplicationHelper
|
|
8
|
+
|
|
9
|
+
def initialize(*)
|
|
10
|
+
super
|
|
11
|
+
|
|
12
|
+
@items.prepend(*stratified_sortition_items)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def stratified_sortition_items
|
|
16
|
+
[candidates_item, status_item]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def candidates_item
|
|
20
|
+
{
|
|
21
|
+
text: data_with_text(model.num_candidates.to_s, t("decidim.stratified_sortitions.stratified_sortitions.stratified_sortition.num_candidates")),
|
|
22
|
+
icon: "group-line",
|
|
23
|
+
}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def status_item
|
|
27
|
+
{
|
|
28
|
+
text: content_tag(:span, class: "label #{status_classes}") { status_name },
|
|
29
|
+
}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def strata_item
|
|
33
|
+
{
|
|
34
|
+
text: data_with_text(model.strata.count.to_s, t("decidim.stratified_sortitions.stratified_sortitions.stratified_sortition.strata_count")),
|
|
35
|
+
icon: "bar-chart-box-line",
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def has_badge?
|
|
40
|
+
false
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def status_name
|
|
44
|
+
t(model.status, scope: "decidim.stratified_sortitions.stratified_sortitions.show.statuses", default: model.status)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def data_with_text(data, text)
|
|
48
|
+
"#{content_tag(:strong) { data }}#{content_tag(:span) { text }}".html_safe
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def status_classes
|
|
52
|
+
case model.status
|
|
53
|
+
when "executed"
|
|
54
|
+
"success"
|
|
55
|
+
when "pending"
|
|
56
|
+
"warning"
|
|
57
|
+
else
|
|
58
|
+
"muted"
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module StratifiedSortitions
|
|
5
|
+
module Admin
|
|
6
|
+
# Command that creates a stratified sortition
|
|
7
|
+
class CreateStratifiedSortition < Decidim::Command
|
|
8
|
+
# Public: Initializes the command.
|
|
9
|
+
#
|
|
10
|
+
# form - A form object with the params.
|
|
11
|
+
def initialize(form)
|
|
12
|
+
super()
|
|
13
|
+
@form = form
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Executes the command. Broadcasts these events:
|
|
17
|
+
#
|
|
18
|
+
# - :ok when everything is valid.
|
|
19
|
+
# - :invalid if the form was not valid and we could not proceed.
|
|
20
|
+
#
|
|
21
|
+
# Returns nothing.
|
|
22
|
+
def call
|
|
23
|
+
return broadcast(:invalid) if form.invalid?
|
|
24
|
+
|
|
25
|
+
transaction do
|
|
26
|
+
create_stratified_sortition!
|
|
27
|
+
create_strata(stratified_sortition)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
broadcast(:ok, stratified_sortition)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
attr_reader :form, :stratified_sortition
|
|
36
|
+
|
|
37
|
+
def create_stratified_sortition!
|
|
38
|
+
parsed_title = Decidim::ContentProcessor.parse_with_processor(:hashtag, form.title, current_organization: form.current_organization).rewrite
|
|
39
|
+
parsed_description = Decidim::ContentProcessor.parse_with_processor(:hashtag, form.description, current_organization: form.current_organization).rewrite
|
|
40
|
+
parsed_selection_criteria = Decidim::ContentProcessor.parse_with_processor(:hashtag, form.selection_criteria, current_organization: form.current_organization).rewrite
|
|
41
|
+
parsed_selected_profiles_description = Decidim::ContentProcessor.parse_with_processor(:hashtag, form.selected_profiles_description,
|
|
42
|
+
current_organization: form.current_organization).rewrite
|
|
43
|
+
params = {
|
|
44
|
+
title: parsed_title,
|
|
45
|
+
description: parsed_description,
|
|
46
|
+
selection_criteria: parsed_selection_criteria,
|
|
47
|
+
selected_profiles_description: parsed_selected_profiles_description,
|
|
48
|
+
component: form.current_component,
|
|
49
|
+
num_candidates: form.num_candidates,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@stratified_sortition = Decidim.traceability.create!(
|
|
53
|
+
Decidim::StratifiedSortitions::StratifiedSortition,
|
|
54
|
+
form.current_user,
|
|
55
|
+
params,
|
|
56
|
+
visibility: "all"
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def create_strata(stratified_sortition)
|
|
61
|
+
form.strata_to_persist.each do |stratum_form|
|
|
62
|
+
stratum = Decidim::StratifiedSortitions::Stratum.create!(
|
|
63
|
+
stratified_sortition:,
|
|
64
|
+
name: stratum_form.name,
|
|
65
|
+
kind: stratum_form.kind,
|
|
66
|
+
position: stratum_form.position
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
stratum_form.substrata_to_persist.each do |substratum_form|
|
|
70
|
+
Decidim::StratifiedSortitions::Substratum.create!(
|
|
71
|
+
stratum:,
|
|
72
|
+
name: substratum_form.name,
|
|
73
|
+
value: substratum_form.value,
|
|
74
|
+
range: substratum_form.range,
|
|
75
|
+
max_quota_percentage: substratum_form.max_quota_percentage,
|
|
76
|
+
position: substratum_form.position
|
|
77
|
+
)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module StratifiedSortitions
|
|
5
|
+
module Admin
|
|
6
|
+
# This command deals with destroying a StratifiedSortition from the admin panel.
|
|
7
|
+
class DestroyStratifiedSortition < Decidim::Command
|
|
8
|
+
# Public: Initializes the command.
|
|
9
|
+
#
|
|
10
|
+
# page - The StratifiedSortition to be destroyed.
|
|
11
|
+
def initialize(stratified_sortition, current_user)
|
|
12
|
+
super()
|
|
13
|
+
@stratified_sortition = stratified_sortition
|
|
14
|
+
@current_user = current_user
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Public: Executes the command.
|
|
18
|
+
#
|
|
19
|
+
# Broadcasts :ok if it got destroyed
|
|
20
|
+
# Broadcasts :has_problems if not destroyed 'cause dependent
|
|
21
|
+
# Broadcasts :invalid if it not destroyed
|
|
22
|
+
def call
|
|
23
|
+
destroy_stratified_sortition
|
|
24
|
+
broadcast(:ok)
|
|
25
|
+
rescue ActiveRecord::DeleteRestrictionError
|
|
26
|
+
broadcast(:has_problems)
|
|
27
|
+
rescue ActiveRecord::RecordNotDestroyed
|
|
28
|
+
broadcast(:invalid)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
attr_reader :stratified_sortition, :current_user
|
|
34
|
+
|
|
35
|
+
def destroy_stratified_sortition
|
|
36
|
+
transaction do
|
|
37
|
+
Decidim.traceability.perform_action!(
|
|
38
|
+
"delete",
|
|
39
|
+
stratified_sortition,
|
|
40
|
+
current_user
|
|
41
|
+
) do
|
|
42
|
+
stratified_sortition.destroy!
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module StratifiedSortitions
|
|
5
|
+
module Admin
|
|
6
|
+
# A command with all the business logic when a user duplicates a stratified sortition.
|
|
7
|
+
class DuplicateStratifiedSortition < Decidim::Command
|
|
8
|
+
include ::Decidim::AttachmentAttributesMethods
|
|
9
|
+
|
|
10
|
+
# Public: Initializes the command.
|
|
11
|
+
#
|
|
12
|
+
# stratified_sortition - the stratified sortition to update.
|
|
13
|
+
# current_user - the user performing the duplication.
|
|
14
|
+
def initialize(stratified_sortition, current_user)
|
|
15
|
+
super()
|
|
16
|
+
@stratified_sortition = stratified_sortition
|
|
17
|
+
@current_user = current_user
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Executes the command. Broadcasts these events:
|
|
21
|
+
#
|
|
22
|
+
# - :ok when everything is valid, together with the duplicated_stratified_sortition.
|
|
23
|
+
#
|
|
24
|
+
# Returns nothing.
|
|
25
|
+
def call
|
|
26
|
+
transaction do
|
|
27
|
+
duplicate_stratified_sortition
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
broadcast(:ok)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
attr_reader :stratified_sortition, :current_user
|
|
36
|
+
|
|
37
|
+
def duplicate_stratified_sortition
|
|
38
|
+
@stratified_sortition = Decidim.traceability.perform_action!(
|
|
39
|
+
:duplicate,
|
|
40
|
+
stratified_sortition,
|
|
41
|
+
current_user,
|
|
42
|
+
) do
|
|
43
|
+
@duplicated_stratified_sortition = stratified_sortition.dup
|
|
44
|
+
@duplicated_stratified_sortition.status = :pending
|
|
45
|
+
if @duplicated_stratified_sortition.save!
|
|
46
|
+
duplicate_strata
|
|
47
|
+
@duplicated_stratified_sortition
|
|
48
|
+
else
|
|
49
|
+
broadcast(:invalid)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def duplicate_strata
|
|
55
|
+
stratified_sortition.strata.order(:position).each do |stratum|
|
|
56
|
+
new_stratum = stratum.dup
|
|
57
|
+
new_stratum.decidim_stratified_sortition_id = @duplicated_stratified_sortition.id
|
|
58
|
+
new_stratum.save!
|
|
59
|
+
|
|
60
|
+
duplicate_substrata(stratum, new_stratum)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def duplicate_substrata(stratum, new_stratum)
|
|
65
|
+
stratum.substrata.order(:position).each do |substratum|
|
|
66
|
+
new_substratum = substratum.dup
|
|
67
|
+
new_substratum.decidim_stratified_sortitions_stratum_id = new_stratum.id
|
|
68
|
+
new_substratum.save!
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module StratifiedSortitions
|
|
5
|
+
module Admin
|
|
6
|
+
# Command that import samples of a stratified sortition
|
|
7
|
+
class ImportSample < Decidim::Command
|
|
8
|
+
# Public: Initializes the command.
|
|
9
|
+
#
|
|
10
|
+
# form - A SampleUploadForm with the file blob
|
|
11
|
+
# stratified_sortition - The stratified sortition to import samples to
|
|
12
|
+
# user - The user performing the import
|
|
13
|
+
def initialize(form, stratified_sortition, user)
|
|
14
|
+
super()
|
|
15
|
+
@form = form
|
|
16
|
+
@stratified_sortition = stratified_sortition
|
|
17
|
+
@user = user
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Executes the command. Broadcasts these events:
|
|
21
|
+
#
|
|
22
|
+
# - :ok when everything is valid.
|
|
23
|
+
# - :invalid if the form was not valid and we could not proceed.
|
|
24
|
+
#
|
|
25
|
+
# Returns nothing.
|
|
26
|
+
def call
|
|
27
|
+
return broadcast(:invalid) unless @form.valid?
|
|
28
|
+
|
|
29
|
+
blob = @form.file
|
|
30
|
+
file_content = blob.download
|
|
31
|
+
filename = blob.filename.to_s
|
|
32
|
+
|
|
33
|
+
Decidim::StratifiedSortitions::Admin::ImportSampleJob
|
|
34
|
+
.perform_later(file_content, filename, @stratified_sortition, @user)
|
|
35
|
+
broadcast(:ok)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module StratifiedSortitions
|
|
5
|
+
module Admin
|
|
6
|
+
# Command that removes uploaded samples of a stratified sortition
|
|
7
|
+
class RemoveUploadedSamples < Decidim::Command
|
|
8
|
+
# Public: Initializes the command.
|
|
9
|
+
#
|
|
10
|
+
# stratified_sortition - The stratified sortition to import samples to
|
|
11
|
+
# user - The user performing the import
|
|
12
|
+
def initialize(stratified_sortition)
|
|
13
|
+
super()
|
|
14
|
+
@stratified_sortition = stratified_sortition
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Executes the command. Broadcasts these events:
|
|
18
|
+
#
|
|
19
|
+
# - :ok when everything is valid.
|
|
20
|
+
# - :invalid if the form was not valid and we could not proceed.
|
|
21
|
+
#
|
|
22
|
+
# Returns nothing.
|
|
23
|
+
def call
|
|
24
|
+
remove_participants
|
|
25
|
+
remove_samples
|
|
26
|
+
broadcast(:ok)
|
|
27
|
+
rescue StandardError
|
|
28
|
+
broadcast(:invalid)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def remove_participants
|
|
34
|
+
participant_scope = Decidim::StratifiedSortitions::SampleParticipant.where(
|
|
35
|
+
decidim_stratified_sortition: @stratified_sortition
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
Decidim::StratifiedSortitions::SampleParticipantStratum.where(
|
|
39
|
+
decidim_stratified_sortitions_sample_participant_id: participant_scope.select(:id)
|
|
40
|
+
).delete_all
|
|
41
|
+
|
|
42
|
+
participant_scope.delete_all
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def remove_samples
|
|
46
|
+
Decidim::StratifiedSortitions::SampleImport.where(
|
|
47
|
+
stratified_sortition: @stratified_sortition
|
|
48
|
+
).delete_all
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|