Skip to content

Commit

Permalink
add conf var for sorting
Browse files Browse the repository at this point in the history
  • Loading branch information
microstudi committed Oct 30, 2023
1 parent 3f5f889 commit fa619d6
Show file tree
Hide file tree
Showing 6 changed files with 225 additions and 11 deletions.
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,33 @@ Results can be filtered by role and by time range and also exported as CSV or ot

![Admin accountability](examples/admin_accountability.png)

#### 16. Weighted voting
#### 16. Additional proposal sortings

This feature allows you to add additional sorting options to the proposals component. By default 4 additional sortings are included:

- `supported_first`: Sort proposals supported by me first.
- `supported_last`: Sort proposals supported by me last.
- `az`: Sort proposals alphabetically.
- `za`: Sort proposals alphabetically (reverse).

By enabling this feature the user choosed sorting method will be stored in the browser's session. This means that if the user changes the sorting method and then navigates to another page, the same sorting will be applied.

You can disable or configure this feature setting the variable `additional_proposal_sortings` configuration in an initializer:

```ruby
# config/initializers/awesome_defaults.rb
Decidim::DecidimAwesome.configure do |config|
config.additional_proposal_sortings = :disabled
end

# Or, to disable alphabetical sorting:

Decidim::DecidimAwesome.configure do |config|
config.additional_proposal_sortings = [:supported_first, :supported_last]
end
```

#### 17. Weighted voting

This feature allows you to configure a proposals component to use a weighted voting system. This means that each vote can have a different weight and the result of the vote is calculated as the sum of all the weights.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,21 @@ module OrderableOverride
extend ActiveSupport::Concern

included do
before_action only: [:index] do
session[:order] = params[:order] if params[:order].present?
end

private

# read order from session if available
def order
@order ||= detect_order(session[:order]) || default_order
end

def possible_orders
@possible_orders ||= begin
possible_orders = %w(random recent)
possible_orders << "supported_first" if supported_order_available?
possible_orders << "supported_last" if supported_order_available?
possible_orders += awesome_additional_sortings
possible_orders << "most_voted" if most_voted_order_available?
possible_orders << "most_endorsed" if current_settings.endorsements_enabled?
possible_orders << "most_commented" if component_settings.comments_enabled?
Expand All @@ -25,6 +33,10 @@ def possible_orders
# rubocop:disable Metrics/CyclomaticComplexity
def reorder(proposals)
case order
when "az"
proposals.order(Arel.sql("decidim_proposals_proposals.title->>'#{I18n.locale}' ASC"))
when "za"
proposals.order(Arel.sql("decidim_proposals_proposals.title->>'#{I18n.locale}' DESC"))
when "supported_first"
proposals.joins(my_votes_join).group(:id).order(Arel.sql("COUNT(decidim_proposals_proposal_votes.id) DESC"))
when "supported_last"
Expand Down Expand Up @@ -62,6 +74,17 @@ def my_votes_join
def supported_order_available?
most_voted_order_available? && current_user
end

def awesome_additional_sortings
return [] unless DecidimAwesome.additional_proposal_sortings.is_a?(Array)

DecidimAwesome.additional_proposal_sortings.filter_map do |sort|
next unless sort.to_sym.in?([:az, :za, :supported_first, :supported_last])
next if sort.to_sym.in?([:supported_first, :supported_last]) && !supported_order_available?

sort.to_s
end
end
end
end
end
Expand Down
13 changes: 13 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ en:
awesome_voting_manifest_options:
default: Simple vote (default)
three_flags: Voting using coloured cards
default_sort_order_options:
az: A-Z (Alphabetical)
supported_first: Supported first
supported_last: Supported last
za: Z-A (Reverse alphabetical)
three_flags_box_title: Title for the voting box
three_flags_box_title_help: Default is "Vote on this proposal". Use "-"
to hide it.
Expand All @@ -139,6 +144,12 @@ en:
three_flags_show_abstain: Add an abstention option
three_flags_show_modal_help: Show instructions/help modal when support
is clicked
step:
default_sort_order_options:
az: A-Z (Alphabetical)
supported_first: Supported first
supported_last: Supported last
za: Z-A (Reverse alphabetical)
decidim_awesome:
admin:
admin_accountability:
Expand Down Expand Up @@ -529,8 +540,10 @@ en:
proposals:
proposals:
orders:
az: A-Z (Alphabetical)
supported_first: Supported first
supported_last: Supported last
za: Z-A (Reverse alphabetical)
layouts:
decidim:
admin:
Expand Down
11 changes: 11 additions & 0 deletions lib/decidim/decidim_awesome/awesome.rb
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,17 @@ module DecidimAwesome
true
end

# Additional sorting methods for proposals
# this setting also stores the selected sorting method in the user's session
config_accessor :additional_proposal_sortings do
[
:supported_first,
:supported_last,
:az,
:za
]
end

# allows admins to created specific CSS snippets affecting only some specific parts
# Valid values differ a little from the previous convention:
# :disabled => false and non available, hidden from admins
Expand Down
18 changes: 17 additions & 1 deletion lib/decidim/decidim_awesome/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,31 @@ class Engine < ::Rails::Engine

if DecidimAwesome.enabled?(:weighted_proposal_voting)
Decidim::Proposals::ProposalVotesController.include(Decidim::DecidimAwesome::Proposals::ProposalVotesControllerOverride)
Decidim::Proposals::ProposalsController.include(Decidim::DecidimAwesome::Proposals::OrderableOverride)
end

Decidim::Proposals::ProposalsController.include(Decidim::DecidimAwesome::Proposals::OrderableOverride) if DecidimAwesome.enabled?(:additional_proposal_sortings)
end
end

initializer "decidim_decidim_awesome.middleware" do |app|
app.config.middleware.insert_after Decidim::Middleware::CurrentOrganization, Decidim::DecidimAwesome::CurrentConfig
end

initializer "decidim_decidim_awesome.additional_proposal_sortings" do |_app|
if DecidimAwesome.enabled?(:additional_proposal_sortings) && DecidimAwesome.additional_proposal_sortings.is_a?(Array)
possible_orders = %w(default random recent most_endorsed most_voted most_commented most_followed
with_more_authors) + DecidimAwesome.additional_proposal_sortings.map(&:to_s)
Decidim.component_registry.find(:proposals).tap do |component|
component.settings(:global) do |settings|
settings.attribute :default_sort_order, type: :select, default: "default", choices: -> { possible_orders }
end
component.settings(:step) do |settings|
settings.attribute :default_sort_order, type: :select, include_blank: true, choices: -> { possible_orders }
end
end
end
end

initializer "decidim_decidim_awesome.weighted_proposal_voting" do |_app|
if DecidimAwesome.enabled?(:weighted_proposal_voting)
# register available processors
Expand Down
139 changes: 132 additions & 7 deletions spec/controllers/proposals_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ module Decidim::Proposals
describe ProposalsController, type: :controller do
routes { Decidim::Proposals::Engine.routes }

let(:component) { create(:proposal_component, :with_votes_enabled) }
let!(:proposal) { create(:proposal, component: component) }
let!(:proposal2) { create(:proposal, component: component) }
let!(:proposal3) { create(:proposal, component: component) }
let(:component) { create(:proposal_component, :with_votes_enabled, settings: settings) }
let!(:proposal) { create(:proposal, title: { en: "m middle" }, component: component) }
let!(:proposal2) { create(:proposal, title: { en: "z last" }, component: component) }
let!(:proposal3) { create(:proposal, title: { en: "a first" }, component: component) }
let(:user) { create(:user, :confirmed, organization: component.organization) }
let!(:vote1) { create(:proposal_vote, proposal: proposal2, author: user) }
let!(:vote2) { create(:proposal_vote, proposal: proposal3, author: user) }
Expand All @@ -20,8 +20,18 @@ module Decidim::Proposals
component_id: component.id
}
end
let(:settings) do
{
default_sort_order: default_order
}
end

let(:default_order) { "default" }

let(:additional_sortings) { [:supported_first, :supported_last, :az, :za] }

before do
allow(Decidim::DecidimAwesome).to receive(:additional_proposal_sortings).and_return(additional_sortings)
request.env["decidim.current_organization"] = component.organization
request.env["decidim.current_participatory_space"] = component.participatory_space
request.env["decidim.current_component"] = component
Expand All @@ -33,7 +43,74 @@ module Decidim::Proposals
get :index, params: params

expect(response).to have_http_status(:ok)
expect(controller.helpers.available_orders).to eq(%w(random recent supported_first supported_last most_voted most_endorsed most_commented most_followed with_more_authors))
expect(controller.helpers.available_orders).to eq(%w(random recent supported_first supported_last az za most_voted most_endorsed most_commented most_followed with_more_authors))
end

context "when no additional_sortings" do
let(:additional_sortings) { :disabled }

it "has standard order filters" do
get :index, params: params

expect(response).to have_http_status(:ok)
expect(controller.helpers.available_orders).to eq(%w(random recent most_voted most_endorsed most_commented most_followed with_more_authors))
end
end

context "when some additional_sortings" do
let(:additional_sortings) { [:az, :supported_last] }

it "has standard order filters" do
get :index, params: params

expect(response).to have_http_status(:ok)
expect(controller.helpers.available_orders).to eq(%w(random recent az supported_last most_voted most_endorsed most_commented most_followed with_more_authors))
end
end

context "when strange additional_sortings" do
let(:additional_sortings) { [:baz, :az] }

it "has standard order filters" do
get :index, params: params

expect(response).to have_http_status(:ok)
expect(controller.helpers.available_orders).to eq(%w(random recent az most_voted most_endorsed most_commented most_followed with_more_authors))
end
end

context "when az order" do
let(:params) do
{
proposal_id: proposal.id,
component_id: component.id,
order: "az"
}
end

it "orders by az" do
get :index, params: params

expect(response).to have_http_status(:ok)
expect(assigns(:proposals).to_a).to eq([proposal3, proposal, proposal2])
end
end

context "when za order" do
let(:params) do
{
proposal_id: proposal.id,
component_id: component.id,
order: "za"
}
end

it "orders by za" do
get :index, params: params

expect(response).to have_http_status(:ok)
expect(assigns(:proposals).to_a).to eq([proposal2, proposal, proposal3])
end
end

context "when supported_first order" do
Expand Down Expand Up @@ -77,7 +154,7 @@ module Decidim::Proposals
get :index, params: params

expect(response).to have_http_status(:ok)
expect(controller.helpers.available_orders).to eq(%w(random recent most_endorsed most_commented most_followed with_more_authors))
expect(controller.helpers.available_orders).to eq(%w(random recent az za most_endorsed most_commented most_followed with_more_authors))
end
end

Expand All @@ -90,7 +167,55 @@ module Decidim::Proposals
get :index, params: params

expect(response).to have_http_status(:ok)
expect(controller.helpers.available_orders).to eq(%w(random recent most_voted most_endorsed most_commented most_followed with_more_authors))
expect(controller.helpers.available_orders).to eq(%w(random recent az za most_voted most_endorsed most_commented most_followed with_more_authors))
end
end

context "when order in session" do
before do
session[:order] = "supported_first"
end

it "orders by supported_first" do
get :index, params: params

expect(response).to have_http_status(:ok)
expect(assigns(:proposals).to_a).to eq([proposal2, proposal3, proposal])
end

context "when order is nonsense" do
let(:default_order) { "az" }

before do
session[:order] = "nonsense"
end

it "orders by default" do
get :index, params: params

expect(response).to have_http_status(:ok)
expect(assigns(:proposals).to_a).to eq([proposal3, proposal, proposal2])
end
end
end

context "when order in params" do
let(:params) do
{
proposal_id: proposal.id,
component_id: component.id,
order: "supported_last"
}
end

it "orders by supported_last" do
get :index, params: params

expect(response).to have_http_status(:ok)
expect(assigns(:proposals).to_a).to eq([proposal, proposal2, proposal3])

get :index, params: params.except(:order)
expect(assigns(:proposals).to_a).to eq([proposal, proposal2, proposal3])
end
end
end
Expand Down

0 comments on commit fa619d6

Please sign in to comment.