adds budget/budget_investment indexes to valuators

This commit is contained in:
Juanjo Bazán
2016-09-08 11:44:07 +02:00
committed by Juanjo Bazán
parent 2d56415048
commit 0fc31b1259
18 changed files with 460 additions and 19 deletions

View File

@@ -384,7 +384,7 @@ body.admin {
}
}
.admin-content .select-geozone {
.admin-content .select-geozone, .admin-content .select-heading {
a {
display: block;

View File

@@ -1,4 +1,6 @@
class Admin::BudgetGroupsController < Admin::BaseController
include FeatureFlags
feature_flag :budgets
def create
@budget = Budget.find params[:budget_id]

View File

@@ -0,0 +1,81 @@
class Valuation::BudgetInvestmentsController < Valuation::BaseController
include FeatureFlags
feature_flag :budgets
before_action :restrict_access_to_assigned_items, only: [:show, :edit, :valuate]
before_action :load_budget
has_filters %w{valuating valuation_finished}, only: :index
load_and_authorize_resource :investment, class: "Budget::Investment"
def index
@heading_filters = heading_filters
if current_user.valuator? && @budget.present?
@investments = @budget.investments.scoped_filter(params_for_current_valuator, @current_filter).order(cached_votes_up: :desc).page(params[:page])
else
@investments = Budget::Investment.none.page(params[:page])
end
end
def valuate
if valid_price_params? && @investment.update(valuation_params)
if @investment.unfeasible_email_pending?
@investment.send_unfeasible_email
end
redirect_to valuation_budget_investment_path(@investment), notice: t('valuation.budget_investments.notice.valuate')
else
render action: :edit
end
end
private
def load_budget
@budget = Budget.find(params[:budget_id])
end
def heading_filters
investments = @budget.investments.by_valuator(current_user.valuator.try(:id)).valuation_open.select(:heading_id).all.to_a
[ { name: t('valuation.budget_investments.index.headings_filter_all'),
id: nil,
pending_count: investments.size
}
] + Budget::Heading.where(id: investments.map(&:heading_id).uniq).order(name: :asc).collect do |h|
{ name: h.name,
id: h.id,
pending_count: investments.count{|x| x.heading_id == h.id}
}
end
end
def params_for_current_valuator
Budget::Investment.filter_params(params).merge({valuator_id: current_user.valuator.id, budget_id: @budget.id})
end
def valuation_params
params[:budget_investment][:feasible] = nil if params[:budget_investment][:feasible] == 'nil'
params.require(:budget_investment).permit(:price, :price_first_year, :price_explanation, :feasible, :feasible_explanation, :duration, :valuation_finished, :internal_comments)
end
def restrict_access_to_assigned_items
raise ActionController::RoutingError.new('Not Found') unless current_user.administrator? || ValuatorAssignment.exists?(investment_id: params[:id], valuator_id: current_user.valuator.id)
end
def valid_price_params?
if /\D/.match params[:budget_investment][:price]
@investment.errors.add(:price, I18n.t('budget.investments.wrong_price_format'))
end
if /\D/.match params[:budget_investment][:price_first_year]
@investment.errors.add(:price_first_year, I18n.t('budget.investments.wrong_price_format'))
end
@investment.errors.empty?
end
end

View File

@@ -0,0 +1,13 @@
class Valuation::BudgetsController < Valuation::BaseController
include FeatureFlags
feature_flag :budgets
has_filters %w{open finished}, only: :index
load_and_authorize_resource
def index
@budgets = Budget.send(@current_filter).order(created_at: :desc).page(params[:page])
end
end

View File

@@ -11,14 +11,14 @@ module ValuationHelper
def assigned_valuators_info(valuators)
case valuators.size
when 0
t("valuation.spending_proposals.index.no_valuators_assigned")
t("valuation.budget_investments.index.no_valuators_assigned")
when 1
"<span title=\"#{t('valuation.spending_proposals.index.valuators_assigned', count: 1)}\">".html_safe +
"<span title=\"#{t('valuation.budget_investments.index.valuators_assigned', count: 1)}\">".html_safe +
valuators.first.name +
"</span>".html_safe
else
"<span title=\"".html_safe + valuators.map(&:name).join(', ') + "\">".html_safe +
t('valuation.spending_proposals.index.valuators_assigned', count: valuators.size) +
t('valuation.budget_investments.index.valuators_assigned', count: valuators.size) +
"</span>".html_safe
end
end

View File

@@ -5,7 +5,7 @@ module Abilities
def initialize(user)
valuator = user.valuator
can [:read, :update, :valuate], SpendingProposal
can [:update, :valuate], Budget::Investment, id: valuator.investment_ids, budget: { valuating: true }
can [:read, :update, :valuate], Budget::Investment, id: valuator.investment_ids, budget: { valuating: true }
end
end
end

View File

@@ -13,10 +13,10 @@ class Budget < ActiveRecord::Base
has_many :ballots, dependent: :destroy
has_many :groups, dependent: :destroy
has_many :headings, through: :groups
has_many :investments, through: :headings
scope :open, -> { where.not(phase: "finished") }
scope :finished, -> { where(phase: "finished") }
scope :valuating, -> { where(valuating: true) }
def on_hold?
phase == "on_hold"

View File

@@ -13,5 +13,14 @@
</li>
<% end %>
<% if feature?(:budgets) %>
<li <%= "class=active" if controller_name == "budget_investments" %>>
<%= link_to valuation_budgets_path do %>
<span class="icon-budget"></span>
<%= t("valuation.menu.budgets") %>
<% end %>
</li>
<% end %>
</ul>
</nav>

View File

@@ -0,0 +1,42 @@
<h2><%= @budget.name %> - <%= t("valuation.budget_investments.index.title") %></h2>
<div class="row collapse">
<% @heading_filters.each_slice(8) do |slice| %>
<div class="small-12 medium-4 column select-heading">
<% slice.each do |filter| %>
<%= link_to valuation_budget_budget_investments_path(budget_id: @budget.id, heading_id: filter[:id]),
class: "#{'active' if params[:heading_id].to_s == filter[:id].to_s}" do %>
<%= filter[:name] %>&nbsp;(<%= filter[:pending_count] %>)
<% end %>
<% end %>
</div>
<% end %>
</div>
<%= render 'shared/filter_subnav', i18n_namespace: "valuation.budget_investments.index" %>
<h3><%= page_entries_info @investments %></h3>
<table>
<% @investments.each do |investment| %>
<tr id="<%= dom_id(investment) %>">
<td>
<strong><%= investment.id %></strong>
</td>
<td>
<%= link_to investment.title, valuation_budget_budget_investment_path(@budget, investment) %>
</td>
<td class="small">
<%= link_to t("valuation.budget_investments.index.edit"), edit_valuation_budget_budget_investment_path(@budget, investment) %>
</td>
<td class="small">
<%= assigned_valuators_info(investment.valuators) %>
</td>
<td class="small">
<%= investment.heading.name %>
</td>
</tr>
<% end %>
</table>
<%= paginate @investments %>

View File

@@ -0,0 +1,17 @@
<h2 class="inline-block"><%= t("valuation.budgets.index.title") %></h2>
<%= render 'shared/filter_subnav', i18n_namespace: "valuation.budgets.index" %>
<h3><%= page_entries_info @budgets %></h3>
<table>
<% @budgets.each do |budget| %>
<tr id="<%= dom_id(budget) %>" class="budget">
<td>
<%= link_to budget.name, valuation_budget_budget_investments_path(budget_id: budget.id) %>
</td>
</tr>
<% end %>
</table>
<%= paginate @budgets %>

View File

@@ -132,6 +132,8 @@ ignore_unused:
- 'moderation.debates.index.filter*'
- 'moderation.debates.index.order*'
- 'valuation.spending_proposals.index.filter*'
- 'valuation.budgets.index.filter*'
- 'valuation.budget_investments.index.filter*'
- 'users.show.filters.*'
- 'debates.index.select_order'
- 'debates.index.orders.*'

View File

@@ -3,8 +3,27 @@ en:
valuation:
menu:
title: Valuation
budgets: Participatory budgets
spending_proposals: Spending proposals
budgets:
index:
title: Participatory budgets
filters:
open: Open
finished: Finished
budget_investments:
index:
headings_filter_all: All headings
filters:
valuation_open: Open
valuating: Under valuation
valuation_finished: Valuation finished
title: Investment projects
edit: Edit
valuators_assigned:
one: Assigned valuator
other: "%{count} valuators assigned"
no_valuators_assigned: No valuators assigned
show:
back: Back
heading: Investment project
@@ -27,6 +46,8 @@ en:
responsibles: Responsibles
assigned_admin: Assigned admin
assigned_valuators: Assigned valuators
notice:
valuate: "Dossier updated"
spending_proposals:
index:
geozone_filter_all: All zones
@@ -36,10 +57,6 @@ en:
valuation_finished: Valuation finished
title: Investment projects for participatory budgeting
edit: Edit
valuators_assigned:
one: Assigned valuator
other: "%{count} valuators assigned"
no_valuators_assigned: No valuators assigned
show:
back: Back
heading: Investment project

View File

@@ -3,8 +3,27 @@ es:
valuation:
menu:
title: Evaluación
budgets: Presupuestos participativos
spending_proposals: Propuestas de inversión
budgets:
index:
title: Presupuestos participativos
filters:
open: Abiertos
finished: Terminados
budget_investments:
index:
headings_filter_all: Todas las partidas
filters:
valuation_open: Abiertas
valuating: En evaluación
valuation_finished: Evaluación finalizada
title: Propuestas de inversión
edit: Editar
valuators_assigned:
one: Evaluador asignado
other: "%{count} evaluadores asignados"
no_valuators_assigned: Sin evaluador
show:
back: Volver
heading: Propuesta de inversión
@@ -27,6 +46,8 @@ es:
responsibles: Responsables
assigned_admin: Administrador asignado
assigned_valuators: Evaluadores asignados
notice:
valuate: "Dossier actualizado"
spending_proposals:
index:
geozone_filter_all: Todos los ámbitos de actuación
@@ -36,10 +57,6 @@ es:
valuation_finished: Evaluación finalizada
title: Propuestas de inversión para presupuestos participativos
edit: Editar
valuators_assigned:
one: Evaluador asignado
other: "%{count} evaluadores asignados"
no_valuators_assigned: Sin evaluador
show:
back: Volver
heading: Propuesta de inversión

View File

@@ -237,11 +237,17 @@ Rails.application.routes.draw do
end
namespace :valuation do
root to: "spending_proposals#index"
root to: "budgets#index"
resources :spending_proposals, only: [:index, :show, :edit] do
patch :valuate, on: :member
end
resources :budgets, only: :index do
resources :budget_investments, only: [:index, :show, :edit] do
patch :valuate, on: :member
end
end
end
namespace :management do

View File

@@ -9,7 +9,7 @@ feature 'Admin budgets' do
context 'Feature flag' do
xscenario 'Disabled with a feature flag' do
scenario 'Disabled with a feature flag' do
Setting['feature.budgets'] = nil
expect{ visit admin_budgets_path }.to raise_exception(FeatureFlags::FeatureDisabled)
end

View File

@@ -0,0 +1,161 @@
require 'rails_helper'
feature 'Valuation budget investments' do
background do
@valuator = create(:valuator, user: create(:user, username: 'Rachel', email: 'rachel@valuators.org'))
login_as(@valuator.user)
@budget = create(:budget, valuating: true)
end
scenario 'Disabled with a feature flag' do
Setting['feature.budgets'] = nil
expect{ visit valuation_budget_budget_investments_path(create(:budget)) }.to raise_exception(FeatureFlags::FeatureDisabled)
end
scenario 'Index shows budget investments assigned to current valuator' do
investment1 = create(:budget_investment, budget: @budget)
investment2 = create(:budget_investment, budget: @budget)
investment1.valuators << @valuator
visit valuation_budget_budget_investments_path(@budget)
expect(page).to have_content(investment1.title)
expect(page).to_not have_content(investment2.title)
end
scenario 'Index shows no budget investment to admins no valuators' do
investment1 = create(:budget_investment, budget: @budget)
investment2 = create(:budget_investment, budget: @budget)
investment1.valuators << @valuator
logout
login_as create(:administrator).user
visit valuation_budget_budget_investments_path(@budget)
expect(page).to_not have_content(investment1.title)
expect(page).to_not have_content(investment2.title)
end
scenario 'Index orders budget investments by votes' do
investment10 = create(:budget_investment, budget: @budget, cached_votes_up: 10)
investment100 = create(:budget_investment, budget: @budget, cached_votes_up: 100)
investment1 = create(:budget_investment, budget: @budget, cached_votes_up: 1)
investment1.valuators << @valuator
investment10.valuators << @valuator
investment100.valuators << @valuator
visit valuation_budget_budget_investments_path(@budget)
expect(investment100.title).to appear_before(investment10.title)
expect(investment10.title).to appear_before(investment1.title)
end
scenario 'Index shows assignments info' do
investment1 = create(:budget_investment, budget: @budget)
investment2 = create(:budget_investment, budget: @budget)
investment3 = create(:budget_investment, budget: @budget)
valuator1 = create(:valuator, user: create(:user))
valuator2 = create(:valuator, user: create(:user))
valuator3 = create(:valuator, user: create(:user))
investment1.valuator_ids = [@valuator.id]
investment2.valuator_ids = [@valuator.id, valuator1.id, valuator2.id]
investment3.valuator_ids = [@valuator.id, valuator3.id]
visit valuation_budget_budget_investments_path(@budget)
within("#budget_investment_#{investment1.id}") do
expect(page).to have_content("Rachel")
end
within("#budget_investment_#{investment2.id}") do
expect(page).to have_content("3 valuators assigned")
end
within("#budget_investment_#{investment3.id}") do
expect(page).to have_content("2 valuators assigned")
end
end
scenario "Index filtering by heading", :js do
group = create(:budget_group, budget: @budget)
heading1 = create(:budget_heading, name: "District 9", group: group)
heading2 = create(:budget_heading, name: "Down to the river", group: group)
investment1 = create(:budget_investment, title: "Realocate visitors", heading: heading1, group: group, budget: @budget)
investment2 = create(:budget_investment, title: "Destroy the city", heading: heading2, group: group, budget: @budget)
investment1.valuators << @valuator
investment2.valuators << @valuator
visit valuation_budget_budget_investments_path(@budget)
expect(page).to have_link("Realocate visitors")
expect(page).to have_link("Destroy the city")
expect(page).to have_content "All headings (2)"
expect(page).to have_content "District 9 (1)"
expect(page).to have_content "Down to the river (1)"
click_link "District 9", exact: false
expect(page).to have_link("Realocate visitors")
expect(page).to_not have_link("Destroy the city")
click_link "Down to the river", exact: false
expect(page).to have_link("Destroy the city")
expect(page).to_not have_link("Realocate visitors")
click_link "All headings", exact: false
expect(page).to have_link("Realocate visitors")
expect(page).to have_link("Destroy the city")
end
scenario "Current filter is properly highlighted" do
filters_links = {'valuating' => 'Under valuation',
'valuation_finished' => 'Valuation finished'}
visit valuation_budget_budget_investments_path(@budget)
expect(page).to_not have_link(filters_links.values.first)
filters_links.keys.drop(1).each { |filter| expect(page).to have_link(filters_links[filter]) }
filters_links.each_pair do |current_filter, link|
visit valuation_budget_budget_investments_path(@budget, filter: current_filter)
expect(page).to_not have_link(link)
(filters_links.keys - [current_filter]).each do |filter|
expect(page).to have_link(filters_links[filter])
end
end
end
scenario "Index filtering by valuation status" do
valuating = create(:budget_investment, budget: @budget, title: "Ongoing valuation")
valuated = create(:budget_investment, budget: @budget, title: "Old idea", valuation_finished: true)
valuating.valuators << @valuator
valuated.valuators << @valuator
visit valuation_budget_budget_investments_path(@budget)
expect(page).to have_content("Ongoing valuation")
expect(page).to_not have_content("Old idea")
visit valuation_budget_budget_investments_path(@budget, filter: 'valuating')
expect(page).to have_content("Ongoing valuation")
expect(page).to_not have_content("Old idea")
visit valuation_budget_budget_investments_path(@budget, filter: 'valuation_finished')
expect(page).to_not have_content("Ongoing valuation")
expect(page).to have_content("Old idea")
end
end

View File

@@ -0,0 +1,74 @@
require 'rails_helper'
feature 'Valuation budgets' do
background do
@valuator = create(:valuator, user: create(:user, username: 'Rachel', email: 'rachel@valuators.org'))
login_as(@valuator.user)
end
scenario 'Disabled with a feature flag' do
Setting['feature.budgets'] = nil
expect{ visit valuation_budgets_path }.to raise_exception(FeatureFlags::FeatureDisabled)
end
context 'Index' do
scenario 'Displaying budgets' do
budget = create(:budget)
visit valuation_budgets_path
expect(page).to have_content(budget.name)
end
scenario 'Filters by phase' do
budget1 = create(:budget)
budget2 = create(:budget, :accepting)
budget3 = create(:budget, :selecting)
budget4 = create(:budget, :balloting)
budget5 = create(:budget, :finished)
visit valuation_budgets_path
expect(page).to have_content(budget1.name)
expect(page).to have_content(budget2.name)
expect(page).to have_content(budget3.name)
expect(page).to have_content(budget4.name)
expect(page).to_not have_content(budget5.name)
click_link 'Finished'
expect(page).to_not have_content(budget1.name)
expect(page).to_not have_content(budget2.name)
expect(page).to_not have_content(budget3.name)
expect(page).to_not have_content(budget4.name)
expect(page).to have_content(budget5.name)
click_link 'Open'
expect(page).to have_content(budget1.name)
expect(page).to have_content(budget2.name)
expect(page).to have_content(budget3.name)
expect(page).to have_content(budget4.name)
expect(page).to_not have_content(budget5.name)
end
scenario 'Current filter is properly highlighted' do
filters_links = {'open' => 'Open', 'finished' => 'Finished'}
visit valuation_budgets_path
expect(page).to_not have_link(filters_links.values.first)
filters_links.keys.drop(1).each { |filter| expect(page).to have_link(filters_links[filter]) }
filters_links.each_pair do |current_filter, link|
visit valuation_budgets_path(filter: current_filter)
expect(page).to_not have_link(link)
(filters_links.keys - [current_filter]).each do |filter|
expect(page).to have_link(filters_links[filter])
end
end
end
end
end