From 0f8dd9976e8a5f77beb91ed72fcc40ff38abc9a1 Mon Sep 17 00:00:00 2001 From: Juan Fernandez Date: Fri, 10 Aug 2018 16:52:19 -0400 Subject: [PATCH 1/3] Added approval voting to PB groups --- app/assets/javascripts/forms.js.coffee | 12 ++++++ .../admin/budget_groups_controller.rb | 7 +++- app/helpers/budgets_helper.rb | 4 ++ app/models/budget.rb | 1 + app/models/budget/group.rb | 8 ++++ app/models/budget/heading.rb | 1 + app/models/budget/investment.rb | 1 + app/models/vote.rb | 1 + app/views/admin/budget_groups/_form.html.erb | 17 +++++++++ app/views/budgets/ballot/_ballot.html.erb | 18 ++++++--- .../budgets/ballot/_progress_bar.html.erb | 38 ++++++++++++------- .../budgets/investments/_header.html.erb | 4 +- .../budgets/investments/_sidebar.html.erb | 6 +-- app/views/budgets/investments/index.html.erb | 1 - config/locales/en/admin.yml | 2 + config/locales/en/budgets.yml | 14 ++++++- config/locales/es/admin.yml | 2 + config/locales/es/budgets.yml | 16 ++++++-- config/locales/fr/budgets.yml | 5 ++- config/locales/nl/budgets.yml | 5 ++- config/locales/val/budgets.yml | 6 ++- ..._and_number_votes_per_heading_to_groups.rb | 6 +++ db/schema.rb | 31 +++++++++++++++ spec/features/budgets/ballots_spec.rb | 12 +++--- spec/models/budget/group_spec.rb | 18 +++++++++ 25 files changed, 197 insertions(+), 39 deletions(-) create mode 100644 db/migrate/20180810211514_add_voting_style_and_number_votes_per_heading_to_groups.rb diff --git a/app/assets/javascripts/forms.js.coffee b/app/assets/javascripts/forms.js.coffee index 65fe1d7194a..c4022652a64 100644 --- a/app/assets/javascripts/forms.js.coffee +++ b/app/assets/javascripts/forms.js.coffee @@ -44,10 +44,22 @@ App.Forms = $("[name='progress_bar[kind]']").change() + toggleSelect: -> + $('.js-toggle-select').unbind('change').on('change', -> + dropdown = $(this) + target = $(dropdown.data('toggle-selector')) + + if dropdown.val() in dropdown.data('hide-on').split(',') + target.addClass('hide') + else + target.removeClass('hide') + ) + initialize: -> App.Forms.disableEnter() App.Forms.submitOnChange() App.Forms.toggleLink() App.Forms.synchronizeInputs() App.Forms.hideOrShowFieldsAfterSelection() + App.Forms.toggleSelect() false diff --git a/app/controllers/admin/budget_groups_controller.rb b/app/controllers/admin/budget_groups_controller.rb index 1e452934017..bda96e27bd0 100644 --- a/app/controllers/admin/budget_groups_controller.rb +++ b/app/controllers/admin/budget_groups_controller.rb @@ -56,7 +56,12 @@ def groups_index end def budget_group_params - params.require(:budget_group).permit(:name, :max_votable_headings, :max_supportable_headings) + params.require(:budget_group).permit( + :name, + :max_votable_headings, + :voting_style, + :number_votes_per_heading + ) end end diff --git a/app/helpers/budgets_helper.rb b/app/helpers/budgets_helper.rb index 63e2debef92..1d6484b13a4 100644 --- a/app/helpers/budgets_helper.rb +++ b/app/helpers/budgets_helper.rb @@ -21,6 +21,10 @@ def budget_phases_select_options Budget::Phase::PHASE_KINDS.map { |ph| [ t("budgets.phase.#{ph}"), ph ] } end + def budget_voting_style_select_options + Budget::Vote::KINDS.map { |vk| [ t("budgets.voting_style.#{vk}"), vk] } + end + def budget_currency_symbol_select_options Budget::CURRENCY_SYMBOLS.map { |cs| [ cs, cs ] } end diff --git a/app/models/budget.rb b/app/models/budget.rb index 5abf9744c90..185772baa18 100644 --- a/app/models/budget.rb +++ b/app/models/budget.rb @@ -205,4 +205,5 @@ def generate_phases def generate_slug? slug.nil? || drafting? end + end diff --git a/app/models/budget/group.rb b/app/models/budget/group.rb index a65b940d9f6..2016d2f42fd 100644 --- a/app/models/budget/group.rb +++ b/app/models/budget/group.rb @@ -9,9 +9,13 @@ class Group < ActiveRecord::Base validates :budget_id, presence: true validates :name, presence: true, uniqueness: { scope: :budget } validates :slug, presence: true, format: /\A[a-z0-9\-_]+\z/ + validates :max_votable_headings, numericality: { only_integer: true, greater_than_or_equal_to: 1 } validates :max_supportable_headings, numericality: { only_integer: true, greater_than_or_equal_to: 1 } + validates :voting_style, inclusion: { in: Vote::KINDS } + validates :number_votes_per_heading, :numericality => { greater_than_or_equal_to: 1 } + scope :by_slug, ->(slug) { where(slug: slug) } before_save :strip_name @@ -24,6 +28,10 @@ def single_heading_group? headings.count == 1 end + def approval_voting? + voting_style == "approval" + end + private def generate_slug? diff --git a/app/models/budget/heading.rb b/app/models/budget/heading.rb index 900ca029592..cf23875d10f 100644 --- a/app/models/budget/heading.rb +++ b/app/models/budget/heading.rb @@ -20,6 +20,7 @@ class Heading < ActiveRecord::Base format: /\A(-|\+)?((?:1[0-7]|[1-9])?\d(?:\.\d{1,})?|180(?:\.0{1,})?)\z/ delegate :budget, :budget_id, to: :group, allow_nil: true + delegate :voting_style, :approval_voting?, :number_votes_per_heading, to: :group, prefix: true scope :order_by_group_name, -> do includes(:group).order('budget_groups.name DESC', 'budget_headings.name') diff --git a/app/models/budget/investment.rb b/app/models/budget/investment.rb index 528297f35b8..5887d17346f 100644 --- a/app/models/budget/investment.rb +++ b/app/models/budget/investment.rb @@ -237,6 +237,7 @@ def reason_for_not_being_ballotable_by(user, ballot) return :not_selected unless selected? return :no_ballots_allowed unless budget.balloting? return :different_heading_assigned_html unless ballot.valid_heading?(heading) + return :not_enough_available_votes_html if heading.group_approval_voting? && ballot.investments.count == heading.group.number_votes_per_heading return :not_enough_money_html if ballot.present? && !enough_money?(ballot) return :casted_offline if ballot.casted_offline? end diff --git a/app/models/vote.rb b/app/models/vote.rb index 626d7187946..78457050b06 100644 --- a/app/models/vote.rb +++ b/app/models/vote.rb @@ -1,2 +1,3 @@ class Vote < ActsAsVotable::Vote + KINDS = %w(knapsack approval) end diff --git a/app/views/admin/budget_groups/_form.html.erb b/app/views/admin/budget_groups/_form.html.erb index 5da9493d80b..a6cab63a7d0 100644 --- a/app/views/admin/budget_groups/_form.html.erb +++ b/app/views/admin/budget_groups/_form.html.erb @@ -7,10 +7,27 @@ placeholder: t("admin.budget_groups.form.name") %> <% if @group.persisted? %> + <%= f.select :voting_style, + budget_voting_style_select_options, + { label: t("admin.budget_groups.form.voting_style") }, + { + class: 'js-toggle-select', + data: { "toggle-selector": "#number_votes_per_heading_wrapper", "hide-on": "knapsack" } + } + %> + <%= f.select :max_votable_headings, (1..@group.headings.count), label: t("admin.budget_groups.max_votable_headings"), placeholder: t("admin.budget_groups.max_votable_headings") %> + +
+ <%= f.select :number_votes_per_heading, + (1..@group.headings.count), + label: t("admin.budget_groups.form.number_votes_per_heading"), + placeholder: t("admin.budget_groups.form.number_votes_per_heading") + %> +
<% end %> <%= f.submit t("admin.budget_groups.form.#{action}"), class: "button success" %> diff --git a/app/views/budgets/ballot/_ballot.html.erb b/app/views/budgets/ballot/_ballot.html.erb index 51f2750e9f4..e64106bd403 100644 --- a/app/views/budgets/ballot/_ballot.html.erb +++ b/app/views/budgets/ballot/_ballot.html.erb @@ -27,16 +27,22 @@

<%= group.name %> - <%= @ballot.heading_for_group(group).name %>

- <%= link_to t("budgets.ballots.show.remaining", - amount: @ballot.formatted_amount_available(@ballot.heading_for_group(group))).html_safe, + <%= link_to t("budgets.ballots.show.remaining.#{group.voting_style}_html", + amount: group.approval_voting? ? + group.number_votes_per_heading - @ballot.investments.by_group(group.id).count : + @ballot.formatted_amount_available(@ballot.heading_for_group(group))), budget_group_path(@budget, group) %> <% if @ballot.has_lines_in_group?(group) %>

- <%= t("budgets.ballots.show.amount_spent") %> - - <%= @ballot.formatted_amount_spent(@ballot.heading_for_group(group)) %> - + <% if group.approval_voting? %> + <%= t('budgets.ballots.show.votes_cast_html', amount: @ballot.investments.by_group(group.id).count) %> + <% else %> + <%= t("budgets.ballots.show.amount_spent") %> + + <%= @ballot.formatted_amount_spent(@ballot.heading_for_group(group)) %> + + <%end %>

<% else %>

diff --git a/app/views/budgets/ballot/_progress_bar.html.erb b/app/views/budgets/ballot/_progress_bar.html.erb index 4c7035b411f..22e2b6a2fd1 100644 --- a/app/views/budgets/ballot/_progress_bar.html.erb +++ b/app/views/budgets/ballot/_progress_bar.html.erb @@ -2,22 +2,32 @@ <%= @budget.formatted_heading_price(@heading) %> -

-
+<% if @heading.group_approval_voting? %> +
+

+ <%= t('budgets.progress_bar.remaining_votes_html', + casted_votes: @ballot.investments.count, + votes_available: @heading.group_number_votes_per_heading ) %> + +

+
+<% else %> +
+
+
-
-
+
+ <%= progress_bar_width(@budget.heading_price(@heading), + @ballot.amount_spent(@heading)) %>">

<%= t("budgets.progress_bar.assigned") %><%= @ballot.formatted_amount_spent(@heading) %> @@ -26,4 +36,6 @@

-
+
+<% end %> + diff --git a/app/views/budgets/investments/_header.html.erb b/app/views/budgets/investments/_header.html.erb index e287f5e9660..0ec49cf4c30 100644 --- a/app/views/budgets/investments/_header.html.erb +++ b/app/views/budgets/investments/_header.html.erb @@ -26,9 +26,10 @@

<%= t("budgets.investments.index.by_heading", heading: @heading.name) %>

-
+
<%= render 'budgets/ballot/progress_bar' %>
+
<% else %>

@@ -58,6 +59,7 @@ <% end %>

+ <% else %> diff --git a/app/views/budgets/investments/_sidebar.html.erb b/app/views/budgets/investments/_sidebar.html.erb index f215e3cd914..d1cd6456ca6 100644 --- a/app/views/budgets/investments/_sidebar.html.erb +++ b/app/views/budgets/investments/_sidebar.html.erb @@ -17,9 +17,9 @@ <% if @heading && can?(:show, @ballot) %>

- <%= t("budgets.investments.index.sidebar.voted_info", - link: link_to(t("budgets.investments.index.sidebar.voted_info_link"), - budget_ballot_path(@budget))).html_safe %> + <%= t("budgets.investments.index.sidebar.voted_info.#{@heading.group_voting_style}", + link: link_to(t("budgets.investments.index.sidebar.voted_info_link"), budget_ballot_path(@budget))) + %>

<% end %> diff --git a/app/views/budgets/investments/index.html.erb b/app/views/budgets/investments/index.html.erb index 95810530685..32d9f8d2d68 100644 --- a/app/views/budgets/investments/index.html.erb +++ b/app/views/budgets/investments/index.html.erb @@ -16,7 +16,6 @@ <% end %>
- <% unless params[:search].present? %> <%= render '/budgets/investments/header' %> <% end %> diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index 468cf8859b7..65d46152741 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -135,6 +135,8 @@ en: edit: "Edit group" name: "Group name" submit: "Save group" + voting_style: "Voting style" + number_votes_per_heading: "Number of votes user can cast under each heading" index: back: "Go back to budgets" budget_headings: diff --git a/config/locales/en/budgets.yml b/config/locales/en/budgets.yml index 39e8dde2799..61cc7f09957 100644 --- a/config/locales/en/budgets.yml +++ b/config/locales/en/budgets.yml @@ -4,7 +4,10 @@ en: show: title: Your ballot amount_spent: Amount spent - remaining: "You still have %{amount} to invest." + votes_cast_html: "Votes cast: %{amount}" + remaining: + knapsack_html: "You still have %{amount} to invest." + approval_html: "You can still cast %{amount} votes." no_balloted_group_yet: "You have not voted on this group yet, go vote!" remove: Remove vote voted_html: @@ -20,6 +23,7 @@ en: not_enough_money_html: "You have already assigned the available budget.
Remember you can %{change_ballot} at any time" no_ballots_allowed: Selecting phase is closed different_heading_assigned_html: "You have already voted a different heading: %{heading_link}" + not_enough_available_votes_html: "You have reached the maximum number of votes allowed" change_ballot: change your votes casted_offline: You have already participated offline groups: @@ -40,6 +44,9 @@ en: balloting: Voting projects reviewing_ballots: Reviewing voting finished: Finished budget + voting_style: + knapsack: Knapsack + approval: Approval index: title: Participatory budgets empty_budgets: There are no budgets. @@ -87,7 +94,9 @@ en: voted_html: one: "You voted one proposal with a cost of %{amount_spent}" other: "You voted %{count} proposals with a cost of %{amount_spent}" - voted_info: You can %{link} at any time until the close of this phase. No need to spend all the money available. + voted_info: + knapsack_html: You can %{link} at any time until the close of this phase. No need to spend all the money available. + approval_html: You can %{link} at any time until the close of this phase. voted_info_link: change your vote different_heading_assigned_html: "You have active votes in another heading: %{heading_link}" change_ballot: "If your change your mind you can remove your votes in %{check_ballot} and start again." @@ -152,6 +161,7 @@ en: progress_bar: assigned: "You have assigned: " available: "Available budget: " + remaining_votes_html: "You have selected %{casted_votes} of %{votes_available} projects." show: group: Group phase: Actual phase diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index f534332639d..c0a03f91a2b 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -135,6 +135,8 @@ es: edit: "Editar grupo" name: "Nombre del grupo" submit: "Guardar grupo" + voting_style: "Estilo de votación" + number_votes_per_heading: "Número de votos que se pueden realizar por partida" index: back: "Volver a presupuestos" budget_headings: diff --git a/config/locales/es/budgets.yml b/config/locales/es/budgets.yml index 3f03867c9ea..b302a935a39 100644 --- a/config/locales/es/budgets.yml +++ b/config/locales/es/budgets.yml @@ -4,9 +4,12 @@ es: show: title: Mis votos amount_spent: Coste total - remaining: "Te quedan %{amount} para invertir" + remaining: + knapsack_html: "Te quedan %{amount} para invertir" + approval_html: "Te quedan %{amount} votos disponibles" no_balloted_group_yet: "Todavía no has votado proyectos de este grupo, ¡vota!" remove: Quitar voto + votes_cast_html: "Votos: %{amount}" voted_html: one: "Has votado un proyecto." other: "Has votado %{count} proyectos." @@ -20,6 +23,7 @@ es: not_enough_money_html: "Ya has asignado el presupuesto disponible.
Recuerda que puedes %{change_ballot} en cualquier momento" no_ballots_allowed: El periodo de votación está cerrado. different_heading_assigned_html: "Ya has votado proyectos de otra partida: %{heading_link}" + not_enough_available_votes_html: "No tines mas votos disponibles" change_ballot: cambiar tus votos casted_offline: Ya has participado presencialmente groups: @@ -40,6 +44,9 @@ es: balloting: Votación final reviewing_ballots: Votación finalizada finished: Resultados + voting_style: + knapsack: Knapsack + approval: Por aprovación index: title: Presupuestos participativos empty_budgets: No hay presupuestos participativos. @@ -86,8 +93,10 @@ es: my_ballot: Mis votos voted_html: one: "Has votado un proyecto por un valor de %{amount_spent}" - other: "Has votado %{count} proyectos por un valor de %{amount_spent}" - voted_info: Puedes %{link} en cualquier momento hasta el cierre de esta fase. No hace falta que gastes todo el dinero disponible. + other: "Has votado %{count} propuestas por un valor de %{amount_spent}" + voted_info: + knapsack_html: Puedes %{link} en cualquier momento hasta el cierre de esta fase. No hace falta que gastes todo el dinero disponible. + approval_html: Puedes %{link} en cualquier momento hasta el cierre de esta fase. voted_info_link: cambiar tus votos different_heading_assigned_html: "Ya apoyaste proyectos de otra sección del presupuesto: %{heading_link}" change_ballot: "Si cambias de opinión puedes borrar tus votos en %{check_ballot} y volver a empezar." @@ -152,6 +161,7 @@ es: progress_bar: assigned: "Has asignado: " available: "Presupuesto disponible: " + remaining_votes_html: "Has seleccionado %{casted_votes} projectos de %{votes_available}." show: group: Grupo phase: Fase actual diff --git a/config/locales/fr/budgets.yml b/config/locales/fr/budgets.yml index 379a44127c3..c4893d96e84 100644 --- a/config/locales/fr/budgets.yml +++ b/config/locales/fr/budgets.yml @@ -4,9 +4,12 @@ fr: show: title: Votre vote amount_spent: Montant dépensé - remaining: "Il vous reste %{amount} à investir." + remaining: + knapsack_html: "Il vous reste %{amount} à investir." + approval_html: "Vous pouvez toujours lancer des votes %{amount}." no_balloted_group_yet: "Vous n'avez pas encore voté dans ce groupe, allez voter !" remove: Supprimer le vote + votes_cast_html: "Votes: %{amount}" voted_html: one: "Vous avez voté un projet d'investissement." other: "Vous avez voté %{count} projets d'investissement." diff --git a/config/locales/nl/budgets.yml b/config/locales/nl/budgets.yml index 45c4b3e8214..504790f60a1 100644 --- a/config/locales/nl/budgets.yml +++ b/config/locales/nl/budgets.yml @@ -4,9 +4,12 @@ nl: show: title: Jouw stem amount_spent: Uitgegeven - remaining: "Je hebt nog %{amount}%{amount} te spenderen." + approval_html: "Je kunt nog steeds %{amount} stemmen plaatsen." no_balloted_group_yet: "U hebt nog niet gestemd op deze groep, stem nu!" remove: Verwijder keuze + votes_cast_html: "Uitgebrachte stemmen: %{amount}" voted_html: one: "U heeft op één voorstel gestemd." other: "U heeft op %{count} voorstellen gestemd." diff --git a/config/locales/val/budgets.yml b/config/locales/val/budgets.yml index 19e853a4c86..738d0628a1a 100644 --- a/config/locales/val/budgets.yml +++ b/config/locales/val/budgets.yml @@ -4,9 +4,13 @@ val: show: title: Els meus vots amount_spent: Cost total - remaining: "Et queden %{amount} per a invertir." + remaining: + knapsack__html: "Et queden %{amount} per a invertir." + approval_html: "Et queden %{amount} voti" + no_balloted_group_yet: "Encara no has votat projectes d'aquest grup, ¡vota!" remove: Llevar vot + votes_cast_html: "Repartiment de vots: %{amount}" voted_html: one: "Has votat una proposta." other: "Has votat %{count} propostes." diff --git a/db/migrate/20180810211514_add_voting_style_and_number_votes_per_heading_to_groups.rb b/db/migrate/20180810211514_add_voting_style_and_number_votes_per_heading_to_groups.rb new file mode 100644 index 00000000000..35de81b7fe8 --- /dev/null +++ b/db/migrate/20180810211514_add_voting_style_and_number_votes_per_heading_to_groups.rb @@ -0,0 +1,6 @@ +class AddVotingStyleAndNumberVotesPerHeadingToGroups < ActiveRecord::Migration + def change + add_column :budget_groups, :voting_style, :string, default: 'knapsack' + add_column :budget_groups, :number_votes_per_heading, :int, default: 1 + end +end diff --git a/db/schema.rb b/db/schema.rb index d41a5cad10b..6923902343b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -184,12 +184,22 @@ add_index "budget_content_blocks", ["heading_id"], name: "index_budget_content_blocks_on_heading_id", using: :btree + create_table "budget_delegates", force: :cascade do |t| + t.integer "user_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + add_index "budget_delegates", ["user_id"], name: "index_budget_delegates_on_user_id", using: :btree + create_table "budget_groups", force: :cascade do |t| t.integer "budget_id" t.string "name", limit: 50 t.string "slug" t.integer "max_votable_headings", default: 1 t.integer "max_supportable_headings", default: 1 + t.string "voting_style", default: "knapsack" + t.integer "number_votes_per_heading", default: 1 end add_index "budget_groups", ["budget_id"], name: "index_budget_groups_on_budget_id", using: :btree @@ -283,6 +293,8 @@ t.datetime "confirmed_hide_at" t.datetime "ignored_flag_at" t.integer "flags_count", default: 0 + t.integer "kind", default: 0 + t.boolean "published", default: true end add_index "budget_investments", ["administrator_id"], name: "index_budget_investments_on_administrator_id", using: :btree @@ -300,6 +312,7 @@ t.datetime "starts_at" t.datetime "ends_at" t.boolean "enabled", default: true + t.string "title" end add_index "budget_phases", ["ends_at"], name: "index_budget_phases_on_ends_at", using: :btree @@ -367,6 +380,14 @@ t.text "description_drafting" t.text "description_publishing_prices" t.text "description_informing" + t.text "description_ideas_posting" + t.text "description_project_forming" + t.string "post_idea_uri" + t.string "commitee_list_uri" + t.string "volunteer_form_uri" + t.string "delegate_form_uri" + t.boolean "guest_ideas", default: false + t.boolean "budget_delegate_only", default: false end create_table "campaigns", force: :cascade do |t| @@ -1729,6 +1750,14 @@ t.boolean "monday_20_morning" end + create_table "volunteers", force: :cascade do |t| + t.integer "user_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + add_index "volunteers", ["user_id"], name: "index_volunteers_on_user_id", using: :btree + create_table "votes", force: :cascade do |t| t.integer "votable_id" t.string "votable_type" @@ -1787,6 +1816,7 @@ add_foreign_key "administrators", "users" add_foreign_key "annotations", "legacy_legislations" add_foreign_key "annotations", "users" + add_foreign_key "budget_delegates", "users" add_foreign_key "budget_investments", "communities" add_foreign_key "budget_recommendations", "users" add_foreign_key "documents", "users" @@ -1831,4 +1861,5 @@ add_foreign_key "related_content_scores", "users" add_foreign_key "users", "geozones" add_foreign_key "valuators", "users" + add_foreign_key "volunteers", "users" end diff --git a/spec/features/budgets/ballots_spec.rb b/spec/features/budgets/ballots_spec.rb index 01e91edfb8e..e0473ba9e31 100644 --- a/spec/features/budgets/ballots_spec.rb +++ b/spec/features/budgets/ballots_spec.rb @@ -4,7 +4,7 @@ let!(:user) { create(:user, :level_two) } let!(:budget) { create(:budget, phase: "balloting") } - let!(:states) { create(:budget_group, budget: budget, name: "States") } + let!(:states) { create(:budget_group, budget: budget, name: "States", voting_style: "knapsack") } let!(:california) { create(:budget_heading, group: states, name: "California", price: 1000) } let!(:new_york) { create(:budget_heading, group: states, name: "New York", price: 1000000) } @@ -15,8 +15,8 @@ visit budget_path(budget) end - let!(:city) { create(:budget_group, budget: budget, name: "City") } - let!(:districts) { create(:budget_group, budget: budget, name: "Districts") } + let!(:city) { create(:budget_group, budget: budget, name: "City", voting_style: "knapsack") } + let!(:districts) { create(:budget_group, budget: budget, name: "Districts", voting_style: "knapsack") } context "Group and Heading Navigation" do @@ -332,7 +332,7 @@ context 'Showing the ballot' do scenario "Do not display heading name if there is only one heading in the group (example: group city)" do - group = create(:budget_group, budget: budget) + group = create(:budget_group, budget: budget, voting_style: "knapsack") heading = create(:budget_heading, group: group) visit budget_path(budget) click_link group.name @@ -343,8 +343,8 @@ end scenario 'Displaying the correct group, heading, count & amount' do - group1 = create(:budget_group, budget: budget) - group2 = create(:budget_group, budget: budget) + group1 = create(:budget_group, budget: budget, voting_style: "knapsack") + group2 = create(:budget_group, budget: budget, voting_style: "knapsack") create(:budget_heading, name: "District A", group: group1, price: 100) heading1 = create(:budget_heading, name: "District 1", group: group1, price: 100) diff --git a/spec/models/budget/group_spec.rb b/spec/models/budget/group_spec.rb index 0663e896699..71a47758a33 100644 --- a/spec/models/budget/group_spec.rb +++ b/spec/models/budget/group_spec.rb @@ -36,4 +36,22 @@ end end end + + describe "voting_style" do + it "must be of one of a valid type" do + Budget::Vote::KINDS.each do |vk| + expect(build(:budget_group, voting_style: vk)).to be_valid + end + expect(build(:budget_group, voting_style: 'something else')).not_to be_valid + end + end + + describe "number_votes_per_heading" do + it "must be at least 1" do + expect(build(:budget_group, number_votes_per_heading: 10)).to be_valid + expect(build(:budget_group, number_votes_per_heading: -1)).not_to be_valid + expect(build(:budget_group, number_votes_per_heading: 0)).not_to be_valid + end + end + end From e9cc9621af0d36eb6a735e86e6193f95e2142f19 Mon Sep 17 00:00:00 2001 From: Ziyan Junaideen Date: Thu, 18 Oct 2018 16:56:18 +0530 Subject: [PATCH 2/3] PR Comments update --- app/assets/javascripts/forms.js.coffee | 2 +- app/helpers/ballots_helper.rb | 9 +++++- app/models/budget.rb | 1 - app/models/budget/heading.rb | 1 - app/models/budget/investment.rb | 2 +- app/views/admin/budget_groups/_form.html.erb | 3 +- app/views/budgets/ballot/_ballot.html.erb | 5 +-- .../budgets/ballot/_progress_bar.html.erb | 32 +++++++++---------- .../budgets/investments/_header.html.erb | 2 -- .../budgets/investments/_sidebar.html.erb | 2 +- app/views/budgets/investments/index.html.erb | 1 + config/i18n-tasks.yml | 1 + config/locales/en/admin.yml | 2 +- config/locales/es/budgets.yml | 2 +- db/schema.rb | 21 ------------ spec/factories/budgets.rb | 8 +++++ spec/features/budgets/votes_spec.rb | 25 +++++++++++++++ 17 files changed, 66 insertions(+), 53 deletions(-) diff --git a/app/assets/javascripts/forms.js.coffee b/app/assets/javascripts/forms.js.coffee index c4022652a64..64cf3d80066 100644 --- a/app/assets/javascripts/forms.js.coffee +++ b/app/assets/javascripts/forms.js.coffee @@ -45,7 +45,7 @@ App.Forms = $("[name='progress_bar[kind]']").change() toggleSelect: -> - $('.js-toggle-select').unbind('change').on('change', -> + $('.js-toggle-select').on('change', -> dropdown = $(this) target = $(dropdown.data('toggle-selector')) diff --git a/app/helpers/ballots_helper.rb b/app/helpers/ballots_helper.rb index 6055d340fea..7307d79be8a 100644 --- a/app/helpers/ballots_helper.rb +++ b/app/helpers/ballots_helper.rb @@ -19,6 +19,13 @@ def district_wide_amount_spent(ballot) def city_wide_amount_spent(ballot) ballot.amount_spent('all') + + def remaining_votes(ballot, group) + if group.approval_voting? + group.number_votes_per_heading - ballot.investments.by_group(group.id).count + else + ballot.formatted_amount_available(ballot.heading_for_group(group)) + end end -end \ No newline at end of file +end diff --git a/app/models/budget.rb b/app/models/budget.rb index 185772baa18..5abf9744c90 100644 --- a/app/models/budget.rb +++ b/app/models/budget.rb @@ -205,5 +205,4 @@ def generate_phases def generate_slug? slug.nil? || drafting? end - end diff --git a/app/models/budget/heading.rb b/app/models/budget/heading.rb index cf23875d10f..900ca029592 100644 --- a/app/models/budget/heading.rb +++ b/app/models/budget/heading.rb @@ -20,7 +20,6 @@ class Heading < ActiveRecord::Base format: /\A(-|\+)?((?:1[0-7]|[1-9])?\d(?:\.\d{1,})?|180(?:\.0{1,})?)\z/ delegate :budget, :budget_id, to: :group, allow_nil: true - delegate :voting_style, :approval_voting?, :number_votes_per_heading, to: :group, prefix: true scope :order_by_group_name, -> do includes(:group).order('budget_groups.name DESC', 'budget_headings.name') diff --git a/app/models/budget/investment.rb b/app/models/budget/investment.rb index 5887d17346f..64e85b2da96 100644 --- a/app/models/budget/investment.rb +++ b/app/models/budget/investment.rb @@ -237,7 +237,7 @@ def reason_for_not_being_ballotable_by(user, ballot) return :not_selected unless selected? return :no_ballots_allowed unless budget.balloting? return :different_heading_assigned_html unless ballot.valid_heading?(heading) - return :not_enough_available_votes_html if heading.group_approval_voting? && ballot.investments.count == heading.group.number_votes_per_heading + return :not_enough_available_votes_html if heading.group.approval_voting? && ballot.investments.count == heading.group.number_votes_per_heading return :not_enough_money_html if ballot.present? && !enough_money?(ballot) return :casted_offline if ballot.casted_offline? end diff --git a/app/views/admin/budget_groups/_form.html.erb b/app/views/admin/budget_groups/_form.html.erb index a6cab63a7d0..53c70e7810d 100644 --- a/app/views/admin/budget_groups/_form.html.erb +++ b/app/views/admin/budget_groups/_form.html.erb @@ -22,8 +22,7 @@ placeholder: t("admin.budget_groups.max_votable_headings") %>
- <%= f.select :number_votes_per_heading, - (1..@group.headings.count), + <%= f.number_field :number_votes_per_heading, label: t("admin.budget_groups.form.number_votes_per_heading"), placeholder: t("admin.budget_groups.form.number_votes_per_heading") %> diff --git a/app/views/budgets/ballot/_ballot.html.erb b/app/views/budgets/ballot/_ballot.html.erb index e64106bd403..a01386e2e4f 100644 --- a/app/views/budgets/ballot/_ballot.html.erb +++ b/app/views/budgets/ballot/_ballot.html.erb @@ -27,10 +27,7 @@

<%= group.name %> - <%= @ballot.heading_for_group(group).name %>

- <%= link_to t("budgets.ballots.show.remaining.#{group.voting_style}_html", - amount: group.approval_voting? ? - group.number_votes_per_heading - @ballot.investments.by_group(group.id).count : - @ballot.formatted_amount_available(@ballot.heading_for_group(group))), + <%= link_to t("budgets.ballots.show.remaining.#{group.voting_style}_html", amount: remaining_votes(@ballot, group)), budget_group_path(@budget, group) %>
<% if @ballot.has_lines_in_group?(group) %> diff --git a/app/views/budgets/ballot/_progress_bar.html.erb b/app/views/budgets/ballot/_progress_bar.html.erb index 22e2b6a2fd1..723d030330c 100644 --- a/app/views/budgets/ballot/_progress_bar.html.erb +++ b/app/views/budgets/ballot/_progress_bar.html.erb @@ -2,12 +2,12 @@ <%= @budget.formatted_heading_price(@heading) %> -<% if @heading.group_approval_voting? %> -
-

+<% if @heading.group.approval_voting? %> +

+

<%= t('budgets.progress_bar.remaining_votes_html', casted_votes: @ballot.investments.count, - votes_available: @heading.group_number_votes_per_heading ) %> + votes_available: @heading.group.number_votes_per_heading ) %>

@@ -24,18 +24,18 @@
- -

- <%= t("budgets.progress_bar.assigned") %><%= @ballot.formatted_amount_spent(@heading) %> - - <%= t("budgets.progress_bar.available") %> - <%= @ballot.formatted_amount_available(@heading) %> - -

-
+ +

+ <%= t("budgets.progress_bar.assigned") %><%= @ballot.formatted_amount_spent(@heading) %> + + <%= t("budgets.progress_bar.available") %> + <%= @ballot.formatted_amount_available(@heading) %> + +

+
<% end %> diff --git a/app/views/budgets/investments/_header.html.erb b/app/views/budgets/investments/_header.html.erb index 0ec49cf4c30..e1da1ef5fa4 100644 --- a/app/views/budgets/investments/_header.html.erb +++ b/app/views/budgets/investments/_header.html.erb @@ -29,7 +29,6 @@
<%= render 'budgets/ballot/progress_bar' %>
-
<% else %>

@@ -59,7 +58,6 @@ <% end %> - <% else %> diff --git a/app/views/budgets/investments/_sidebar.html.erb b/app/views/budgets/investments/_sidebar.html.erb index d1cd6456ca6..c8fcde1602e 100644 --- a/app/views/budgets/investments/_sidebar.html.erb +++ b/app/views/budgets/investments/_sidebar.html.erb @@ -17,7 +17,7 @@ <% if @heading && can?(:show, @ballot) %>

- <%= t("budgets.investments.index.sidebar.voted_info.#{@heading.group_voting_style}", + <%= t("budgets.investments.index.sidebar.voted_info.#{@heading.group.voting_style}", link: link_to(t("budgets.investments.index.sidebar.voted_info_link"), budget_ballot_path(@budget))) %>

diff --git a/app/views/budgets/investments/index.html.erb b/app/views/budgets/investments/index.html.erb index 32d9f8d2d68..95810530685 100644 --- a/app/views/budgets/investments/index.html.erb +++ b/app/views/budgets/investments/index.html.erb @@ -16,6 +16,7 @@ <% end %>
+ <% unless params[:search].present? %> <%= render '/budgets/investments/header' %> <% end %> diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index bc2cef2d3d0..1cd2cae49a8 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -204,6 +204,7 @@ ignore_unused: - '*.form.map_skip_checkbox' - 'votes.budget_investments.different_heading_assigned*' - '*.form.map_skip_checkbox' + - 'budgets.ballots.show.remaining.*' # - '{devise,kaminari,will_paginate}.*' # - 'simple_form.{yes,no}' # - 'simple_form.{placeholders,hints,labels}.*' diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index 65d46152741..47cec43e97f 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -136,7 +136,7 @@ en: name: "Group name" submit: "Save group" voting_style: "Voting style" - number_votes_per_heading: "Number of votes user can cast under each heading" + number_votes_per_heading: "Number of votes a user can cast under each heading" index: back: "Go back to budgets" budget_headings: diff --git a/config/locales/es/budgets.yml b/config/locales/es/budgets.yml index b302a935a39..99398cb8e12 100644 --- a/config/locales/es/budgets.yml +++ b/config/locales/es/budgets.yml @@ -45,7 +45,7 @@ es: reviewing_ballots: Votación finalizada finished: Resultados voting_style: - knapsack: Knapsack + knapsack: Bolsa de dinero approval: Por aprovación index: title: Presupuestos participativos diff --git a/db/schema.rb b/db/schema.rb index 6923902343b..1ae52aa0542 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -184,14 +184,6 @@ add_index "budget_content_blocks", ["heading_id"], name: "index_budget_content_blocks_on_heading_id", using: :btree - create_table "budget_delegates", force: :cascade do |t| - t.integer "user_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - end - - add_index "budget_delegates", ["user_id"], name: "index_budget_delegates_on_user_id", using: :btree - create_table "budget_groups", force: :cascade do |t| t.integer "budget_id" t.string "name", limit: 50 @@ -293,8 +285,6 @@ t.datetime "confirmed_hide_at" t.datetime "ignored_flag_at" t.integer "flags_count", default: 0 - t.integer "kind", default: 0 - t.boolean "published", default: true end add_index "budget_investments", ["administrator_id"], name: "index_budget_investments_on_administrator_id", using: :btree @@ -312,7 +302,6 @@ t.datetime "starts_at" t.datetime "ends_at" t.boolean "enabled", default: true - t.string "title" end add_index "budget_phases", ["ends_at"], name: "index_budget_phases_on_ends_at", using: :btree @@ -380,14 +369,6 @@ t.text "description_drafting" t.text "description_publishing_prices" t.text "description_informing" - t.text "description_ideas_posting" - t.text "description_project_forming" - t.string "post_idea_uri" - t.string "commitee_list_uri" - t.string "volunteer_form_uri" - t.string "delegate_form_uri" - t.boolean "guest_ideas", default: false - t.boolean "budget_delegate_only", default: false end create_table "campaigns", force: :cascade do |t| @@ -1816,7 +1797,6 @@ add_foreign_key "administrators", "users" add_foreign_key "annotations", "legacy_legislations" add_foreign_key "annotations", "users" - add_foreign_key "budget_delegates", "users" add_foreign_key "budget_investments", "communities" add_foreign_key "budget_recommendations", "users" add_foreign_key "documents", "users" @@ -1861,5 +1841,4 @@ add_foreign_key "related_content_scores", "users" add_foreign_key "users", "geozones" add_foreign_key "valuators", "users" - add_foreign_key "volunteers", "users" end diff --git a/spec/factories/budgets.rb b/spec/factories/budgets.rb index ec9821b75da..b950b4ee1c1 100644 --- a/spec/factories/budgets.rb +++ b/spec/factories/budgets.rb @@ -87,6 +87,14 @@ trait :drafting_budget do association :budget, factory: [:budget, :drafting] end + + trait :knapsack do + voting_style 'knapsack' + end + + trait :approval do + voting_style 'approval' + end end factory :budget_heading, class: 'Budget::Heading' do diff --git a/spec/features/budgets/votes_spec.rb b/spec/features/budgets/votes_spec.rb index 102802a32fa..db41fa9040e 100644 --- a/spec/features/budgets/votes_spec.rb +++ b/spec/features/budgets/votes_spec.rb @@ -173,4 +173,29 @@ end end + + + feature 'Approval Voting' do + let(:budget) { create(:budget, :balloting) } + let(:group) { create(:budget_group, :approval, budget: budget) } + let(:heading) { create(:budget_heading, group: group) } + + background { login_as(@manuela) } + + feature 'Index' do + scenario 'Progress bar', :js do + investment1 = create(:budget_investment, heading: heading) + investment2 = create(:budget_investment, heading: heading) + investment3 = create(:budget_investment, heading: heading) + + create(:vote, voter: @manuela, votable: investment1, vote_flag: true) + + visit budget_investments_path(budget, heading_id: heading.id) + + within(".progress-bar-nav") do + expect(page).to have_content("You have selected 1 of 1 projects") + end + end + end + end end From 8798555d79add0797c3f1e8058205ed400b60a66 Mon Sep 17 00:00:00 2001 From: voodoorai2000 Date: Tue, 22 Jan 2019 18:50:36 +0100 Subject: [PATCH 3/3] Fix conflicts with upstream --- app/helpers/ballots_helper.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/helpers/ballots_helper.rb b/app/helpers/ballots_helper.rb index 7307d79be8a..29896ca2d64 100644 --- a/app/helpers/ballots_helper.rb +++ b/app/helpers/ballots_helper.rb @@ -19,6 +19,7 @@ def district_wide_amount_spent(ballot) def city_wide_amount_spent(ballot) ballot.amount_spent('all') + end def remaining_votes(ballot, group) if group.approval_voting?