Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Upstream] Budgets approval voting #1843

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions app/assets/javascripts/forms.js.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,22 @@ App.Forms =

$("[name='progress_bar[kind]']").change()

toggleSelect: ->
$('.js-toggle-select').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
7 changes: 6 additions & 1 deletion app/controllers/admin/budget_groups_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
10 changes: 9 additions & 1 deletion app/helpers/ballots_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,12 @@ def city_wide_amount_spent(ballot)
ballot.amount_spent('all')
end

end
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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lint/Syntax: unexpected token $end

4 changes: 4 additions & 0 deletions app/helpers/budgets_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions app/models/budget/group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -24,6 +28,10 @@ def single_heading_group?
headings.count == 1
end

def approval_voting?
voting_style == "approval"
end

private

def generate_slug?
Expand Down
1 change: 1 addition & 0 deletions app/models/budget/investment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Metrics/LineLength: Line is too long. [149/100] (https://github.com/bbatsov/ruby-style-guide#80-character-limits)

return :not_enough_money_html if ballot.present? && !enough_money?(ballot)
return :casted_offline if ballot.casted_offline?
end
Expand Down
1 change: 1 addition & 0 deletions app/models/vote.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
class Vote < ActsAsVotable::Vote
KINDS = %w(knapsack approval)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style/PercentLiteralDelimiters: %w-literals should be delimited by [ and ]. (https://github.com/bbatsov/ruby-style-guide#percent-literal-braces)

end
16 changes: 16 additions & 0 deletions app/views/admin/budget_groups/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,26 @@
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") %>

<div id="number_votes_per_heading_wrapper" class="<%= 'hide' unless @group.approval_voting? %>">
<%= 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")
%>
</div>
<% end %>

<%= f.submit t("admin.budget_groups.form.#{action}"), class: "button success" %>
Expand Down
15 changes: 9 additions & 6 deletions app/views/budgets/ballot/_ballot.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,19 @@
<h3>
<%= group.name %> - <%= @ballot.heading_for_group(group).name %>
</h3>
<%= 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: remaining_votes(@ballot, group)),
budget_group_path(@budget, group) %>
</div>
<% if @ballot.has_lines_in_group?(group) %>
<h4 class="amount-spent text-right">
<%= t("budgets.ballots.show.amount_spent") %>
<span>
<%= @ballot.formatted_amount_spent(@ballot.heading_for_group(group)) %>
</span>
<% 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") %>
<span>
<%= @ballot.formatted_amount_spent(@ballot.heading_for_group(group)) %>
</span>
<%end %>
</h4>
<% else %>
<p>
Expand Down
58 changes: 35 additions & 23 deletions app/views/budgets/ballot/_progress_bar.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,40 @@
<%= @budget.formatted_heading_price(@heading) %>
</span>

<div class="progress" role="progressbar" tabindex="0"
id="progress"
aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
<div class="progress-meter"
style="width:
<%= progress_bar_width(@budget.heading_price(@heading),
@ballot.amount_spent(@heading)) %>">
</div>
</div>
<% if @heading.group.approval_voting? %>
<div class="panel clear">
<p>
<%= t('budgets.progress_bar.remaining_votes_html',
casted_votes: @ballot.investments.count,
votes_available: @heading.group.number_votes_per_heading ) %>

<div class="progress spent-amount-progress" role="progressbar" tabindex="0"
aria-valuenow="20" aria-valuemin="0" aria-valuetext="25 percent" aria-valuemax="100">
<span class="progress-meter spent-amount-meter"
style="width:
<%= progress_bar_width(@budget.heading_price(@heading),
@ballot.amount_spent(@heading)) %>">
<p id="amount-spent" class="progress-meter-text spent-amount-text">
<small><%= t("budgets.progress_bar.assigned") %></small><%= @ballot.formatted_amount_spent(@heading) %>
<span id="amount-available" class="amount-available">
<small><%= t("budgets.progress_bar.available") %></small>
<span><%= @ballot.formatted_amount_available(@heading) %></span>
</span>
</p>
</span>
</div>
</div>
<% else %>
<div class="progress" role="progressbar" tabindex="0"
id="progress"
aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
<div class="progress-meter"
style="width:
<%= progress_bar_width(@budget.heading_price(@heading),
@ballot.amount_spent(@heading)) %>">
</div>
</div>

<div class="progress spent-amount-progress" role="progressbar" tabindex="0"
aria-valuenow="20" aria-valuemin="0" aria-valuetext="25 percent" aria-valuemax="100">
<span class="progress-meter spent-amount-meter"
style="width:
<%= progress_bar_width(@budget.heading_price(@heading),
@ballot.amount_spent(@heading)) %>">
<p id="amount-spent" class="progress-meter-text spent-amount-text">
<small><%= t("budgets.progress_bar.assigned") %></small><%= @ballot.formatted_amount_spent(@heading) %>
<span id="amount-available" class="amount-available">
<small><%= t("budgets.progress_bar.available") %></small>
<span><%= @ballot.formatted_amount_available(@heading) %></span>
</span>
</p>
</span>
</div>
<% end %>

2 changes: 1 addition & 1 deletion app/views/budgets/investments/_header.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<h2>
<%= t("budgets.investments.index.by_heading", heading: @heading.name) %>
</h2>
<div id="progress_bar" class="progress-bar-amount">
<div id="progress_bar" class="no-margin-top">
<%= render 'budgets/ballot/progress_bar' %>
</div>
</div>
Expand Down
6 changes: 3 additions & 3 deletions app/views/budgets/investments/_sidebar.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@

<% if @heading && can?(:show, @ballot) %>
<p class="callout">
<%= 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)))
%>
</p>
<% end %>

Expand Down
1 change: 1 addition & 0 deletions config/i18n-tasks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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}.*'
Expand Down
2 changes: 2 additions & 0 deletions config/locales/en/admin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 a user can cast under each heading"
index:
back: "Go back to budgets"
budget_headings:
Expand Down
14 changes: 12 additions & 2 deletions config/locales/en/budgets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ en:
show:
title: Your ballot
amount_spent: Amount spent
remaining: "You still have <span>%{amount}</span> to invest."
votes_cast_html: "Votes cast: <span>%{amount}</span>"
remaining:
knapsack_html: "You still have <span>%{amount}</span> to invest."
approval_html: "You can still cast <span>%{amount}</span> votes."
no_balloted_group_yet: "You have not voted on this group yet, go vote!"
remove: Remove vote
voted_html:
Expand All @@ -20,6 +23,7 @@ en:
not_enough_money_html: "You have already assigned the available budget.<br><small>Remember you can %{change_ballot} at any time</small>"
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:
Expand All @@ -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.
Expand Down Expand Up @@ -87,7 +94,9 @@ en:
voted_html:
one: "<strong>You voted one proposal with a cost of %{amount_spent}</strong>"
other: "<strong>You voted %{count} proposals with a cost of %{amount_spent}</strong>"
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."
Expand Down Expand Up @@ -152,6 +161,7 @@ en:
progress_bar:
assigned: "You have assigned: "
available: "Available budget: "
remaining_votes_html: "You have selected <strong>%{casted_votes}</strong> of <strong>%{votes_available}</strong> projects."
show:
group: Group
phase: Actual phase
Expand Down
2 changes: 2 additions & 0 deletions config/locales/es/admin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
16 changes: 13 additions & 3 deletions config/locales/es/budgets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ es:
show:
title: Mis votos
amount_spent: Coste total
remaining: "Te quedan <span>%{amount}</span> para invertir"
remaining:
knapsack_html: "Te quedan <span>%{amount}</span> para invertir"
approval_html: "Te quedan <span>%{amount}</span> votos disponibles"
no_balloted_group_yet: "Todavía no has votado proyectos de este grupo, ¡vota!"
remove: Quitar voto
votes_cast_html: "Votos: <span>%{amount}</span>"
voted_html:
one: "Has votado <span>un</span> proyecto."
other: "Has votado <span>%{count}</span> proyectos."
Expand All @@ -20,6 +23,7 @@ es:
not_enough_money_html: "Ya has asignado el presupuesto disponible.<br><small>Recuerda que puedes %{change_ballot} en cualquier momento</small>"
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:
Expand All @@ -40,6 +44,9 @@ es:
balloting: Votación final
reviewing_ballots: Votación finalizada
finished: Resultados
voting_style:
knapsack: Bolsa de dinero
approval: Por aprovación
index:
title: Presupuestos participativos
empty_budgets: No hay presupuestos participativos.
Expand Down Expand Up @@ -86,8 +93,10 @@ es:
my_ballot: Mis votos
voted_html:
one: "<strong>Has votado un proyecto por un valor de %{amount_spent}</strong>"
other: "<strong>Has votado %{count} proyectos por un valor de %{amount_spent}</strong>"
voted_info: Puedes %{link} en cualquier momento hasta el cierre de esta fase. No hace falta que gastes todo el dinero disponible.
other: "<strong>Has votado %{count} propuestas por un valor de %{amount_spent}</strong>"
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."
Expand Down Expand Up @@ -152,6 +161,7 @@ es:
progress_bar:
assigned: "Has asignado: "
available: "Presupuesto disponible: "
remaining_votes_html: "Has seleccionado <strong>%{casted_votes}</strong> projectos de <strong>%{votes_available}</strong>."
show:
group: Grupo
phase: Fase actual
Expand Down
5 changes: 4 additions & 1 deletion config/locales/fr/budgets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ fr:
show:
title: Votre vote
amount_spent: Montant dépensé
remaining: "Il vous reste <span>%{amount}</span> à investir."
remaining:
knapsack_html: "Il vous reste <span>%{amount}</span> à investir."
approval_html: "Vous pouvez toujours lancer des votes <span>%{amount}</span>."
no_balloted_group_yet: "Vous n'avez pas encore voté dans ce groupe, allez voter !"
remove: Supprimer le vote
votes_cast_html: "Votes: <span>%{amount}</span>"
voted_html:
one: "Vous avez voté <span>un</span> projet d'investissement."
other: "Vous avez voté <span>%{count}</span> projets d'investissement."
Expand Down
5 changes: 4 additions & 1 deletion config/locales/nl/budgets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ nl:
show:
title: Jouw stem
amount_spent: Uitgegeven
remaining: "Je hebt nog <span>%{amount}</span< te spenderen."
remaining:
knapsack_html: "U heeft nog <span>%{amount}</span> te spenderen."
approval_html: "Je kunt nog steeds <span>%{amount}</span> stemmen plaatsen."
no_balloted_group_yet: "U hebt nog niet gestemd op deze groep, stem nu!"
remove: Verwijder keuze
votes_cast_html: "Uitgebrachte stemmen: <span>%{amount}</span>"
voted_html:
one: "U heeft op <span>één</span> voorstel gestemd."
other: "U heeft op <span>%{count}</span> voorstellen gestemd."
Expand Down
Loading