Merge pull request #3520 from consul/backport-refactor_stats_enabled

Add options to show advanced stats
This commit is contained in:
Javier Martín
2019-05-23 13:07:19 +02:00
committed by GitHub
41 changed files with 549 additions and 255 deletions

View File

@@ -1,5 +1,6 @@
class Admin::BudgetsController < Admin::BaseController
include Translatable
include ReportAttributes
include FeatureFlags
feature_flag :budgets
@@ -62,7 +63,7 @@ class Admin::BudgetsController < Admin::BaseController
def budget_params
descriptions = Budget::Phase::PHASE_KINDS.map{|p| "description_#{p}"}.map(&:to_sym)
valid_attributes = [:phase, :currency_symbol] + descriptions
params.require(:budget).permit(*valid_attributes, translation_params(Budget))
params.require(:budget).permit(*valid_attributes, *report_attributes, translation_params(Budget))
end
end

View File

@@ -1,6 +1,7 @@
class Admin::Poll::PollsController < Admin::Poll::BaseController
include Translatable
include ImageAttributes
include ReportAttributes
load_and_authorize_resource
before_action :load_search, only: [:search_booths, :search_officers]
@@ -75,11 +76,10 @@ class Admin::Poll::PollsController < Admin::Poll::BaseController
end
def poll_params
image_attributes = [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy]
attributes = [:name, :starts_at, :ends_at, :geozone_restricted, :results_enabled,
:stats_enabled, :budget_id, geozone_ids: [],
image_attributes: image_attributes]
params.require(:poll).permit(*attributes, translation_params(Poll))
attributes = [:name, :starts_at, :ends_at, :geozone_restricted, :budget_id,
geozone_ids: [], image_attributes: image_attributes]
params.require(:poll).permit(*attributes, *report_attributes, translation_params(Poll))
end
def search_params

View File

@@ -0,0 +1,9 @@
module ReportAttributes
extend ActiveSupport::Concern
private
def report_attributes
Report::KINDS.map { |kind| :"#{kind}_enabled" }
end
end

View File

@@ -15,8 +15,7 @@ class Dashboard::PollsController < Dashboard::BaseController
def create
authorize! :manage_polls, proposal
@poll = Poll.new(poll_params.merge(author: current_user, related: proposal,
stats_enabled: false))
@poll = Poll.new(poll_params.merge(author: current_user, related: proposal))
if @poll.save
redirect_to proposal_dashboard_polls_path(proposal), notice: t("flash.actions.create.poll")
else
@@ -54,7 +53,7 @@ class Dashboard::PollsController < Dashboard::BaseController
end
def poll_attributes
[:name, :starts_at, :ends_at, :description, :results_enabled, :stats_enabled,
[:name, :starts_at, :ends_at, :description, :results_enabled,
questions_attributes: question_attributes]
end

View File

@@ -20,8 +20,9 @@ module Abilities
can [:read], Budget
can [:read], Budget::Group
can [:read, :print, :json_data], Budget::Investment
can [:read_results, :read_executions], Budget, phase: "finished"
can(:read_stats, Budget) { |budget| budget.valuating_or_later? }
can(:read_results, Budget) { |budget| budget.results_enabled? && budget.finished? }
can(:read_stats, Budget) { |budget| budget.stats_enabled? && budget.valuating_or_later? }
can :read_executions, Budget, phase: "finished"
can :new, DirectMessage
can [:read, :debate, :draft_publication, :allegations, :result_publication,
:proposals, :milestones], Legislation::Process, published: true

View File

@@ -3,6 +3,7 @@ class Budget < ApplicationRecord
include Measurable
include Sluggable
include StatsVersionable
include Reportable
translates :name, touch: true
include Globalizable

View File

@@ -0,0 +1,23 @@
module Reportable
extend ActiveSupport::Concern
included do
has_one :report, as: :process, dependent: :destroy
accepts_nested_attributes_for :report
end
def report
super || build_report
end
Report::KINDS.each do |kind|
define_method "#{kind}_enabled?" do
report.send(kind)
end
alias_method "#{kind}_enabled", "#{kind}_enabled?"
define_method "#{kind}_enabled=" do |enabled|
report.send("#{kind}=", enabled)
end
end
end

View File

@@ -93,6 +93,10 @@ module Statisticable
"v#{resource.find_or_create_stats_version.updated_at.to_i}"
end
def advanced?
resource.advanced_stats_enabled?
end
private
def base_stats_methods

View File

@@ -7,6 +7,7 @@ class Poll < ApplicationRecord
include Notifiable
include Sluggable
include StatsVersionable
include Reportable
translates :name, touch: true
translates :summary, touch: true

5
app/models/report.rb Normal file
View File

@@ -0,0 +1,5 @@
class Report < ApplicationRecord
KINDS = %i[results stats advanced_stats]
belongs_to :process, polymorphic: true
end

View File

@@ -65,6 +65,10 @@
</div>
<% end %>
<div class="small-12 column">
<%= render "admin/shared/show_results_fields", form: f %>
</div>
<div class="small-12 column">
<div class="clear small-12 medium-4 large-3 inline-block">
<%= f.submit nil, class: "button success" %>

View File

@@ -1,11 +1,5 @@
<%= form_for [:admin, @poll], action: "update" do |f| %>
<fieldset class="fieldset">
<legend><%= t("admin.polls.new.show_results_and_stats") %></legend>
<%= f.check_box :results_enabled, checked: @poll.results_enabled?, label: t("admin.polls.new.show_results") %>
<%= f.check_box :stats_enabled, checked: @poll.stats_enabled?, label: t("admin.polls.new.show_stats") %>
<p class="small"><%= t("admin.polls.new.results_and_stats_reminder") %></p>
</fieldset>
<%= render "admin/shared/show_results_fields", form: f %>
<div class="small-12 medium-4 large-2">
<%= f.submit t("admin.polls.#{admin_submit_action(@poll)}.submit_button"),

View File

@@ -0,0 +1,7 @@
<fieldset class="fieldset">
<legend><%= t("admin.shared.show_results_and_stats") %></legend>
<%= form.check_box :results_enabled %>
<%= form.check_box :stats_enabled %>
<%= form.check_box :advanced_stats_enabled %>
<p class="small"><%= t("admin.shared.results_and_stats_reminder") %></p>
</fieldset>

View File

@@ -0,0 +1,83 @@
<div id="advanced_statistics">
<h3 class="section-title"><%= t("stats.advanced") %></h3>
<div id="total_investments" class="stats-group">
<h4><%= t("stats.budgets.total_investments") %></h4>
<%= number_with_info_tags(
stats.total_budget_investments,
t("stats.budgets.total_investments"),
html_class: "total-investments"
) %>
<%= number_with_info_tags(stats.total_unfeasible_investments,
t("stats.budgets.total_unfeasible_investments")) %>
<%= number_with_info_tags(stats.total_selected_investments,
t("stats.budgets.total_selected_investments")) %>
</div>
<div id="stats_by_phase" class="stats-group">
<h4><%= t("stats.budgets.by_phase") %></h4>
<% stats.phases.each do |phase| %>
<%= number_with_info_tags(
stats.send("total_participants_#{phase}_phase"),
t("stats.budgets.participants_#{phase}_phase")
) %>
<% end %>
</div>
<div id="stats_by_heading" class="stats-group">
<h4 class="margin-bottom"><%= t("stats.budgets.by_heading") %></h4>
<table class="stats-districts survey-districts">
<thead>
<tr>
<th scope="col" rowspan="2"><%= t("stats.budgets.heading") %></th>
<th scope="col" rowspan="2"><%= t("stats.budgets.investments_sent_html") %></th>
<% stats.all_phases.each do |phase| %>
<th scope="col" colspan="3">
<%= t("stats.budgets.participants_#{phase}_phase") %>
</th>
<% end %>
</tr>
<tr>
<% stats.all_phases.each do %>
<th scope="col" class="phase-subheader"><%= t("stats.budgets.total") %></th>
<th scope="col" class="phase-subheader"><%= t("stats.budgets.percent_total_participants") %></th>
<th scope="col" class="phase-subheader"><%= t("stats.budgets.percent_heading_census") %></th>
<% end %>
</tr>
</thead>
<tbody id="headings">
<% @headings.each do |heading| %>
<tr id="<%= heading.name.parameterize %>">
<td class="border-left">
<strong><%= heading.name %></strong>
</td>
<td id="total_spending_proposals_heading_<%= heading.id %>"
class="text-center border-left border-right">
<%= stats.headings[heading.id][:total_investments_count] %>
</td>
<% stats.all_phases.each do |phase| %>
<td id="total_participants_<%= phase %>_phase_heading_<%= heading.id %>"
class="border-left text-center">
<%= stats.headings[heading.id]["total_participants_#{phase}_phase".to_sym] %>
</td>
<td id="percentage_participants_<%= phase %>_phase_heading_<%= heading.id %>"
class="border-left border-right text-center">
<%= number_to_stats_percentage(stats.headings[heading.id]["percentage_participants_#{phase}_phase".to_sym]) %>
</td>
<td id="percentage_district_population_<%= phase %>_phase_heading_<%= heading.id %>"
class="text-center border-right">
<%= number_to_stats_percentage(stats.headings[heading.id]["percentage_district_population_#{phase}_phase".to_sym]) %>
</td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>

View File

@@ -0,0 +1,12 @@
<p><strong><%= link_to t("stats.advanced"), "#advanced_statistics" %></strong></p>
<ul class="menu vertical">
<li>
<%= link_to t("stats.budgets.total_investments"), "#total_investments" %>
</li>
<li>
<%= link_to t("stats.budgets.by_phase"), "#stats_by_phase" %>
</li>
<li>
<%= link_to t("stats.budgets.by_heading"), "#stats_by_heading" %>
</li>
</ul>

View File

@@ -41,107 +41,12 @@
<div class="row margin">
<div class="small-12 medium-3 column sidebar">
<%= render "shared/stats/links", stats: @stats %>
<p><strong><%= link_to t("stats.advanced"), "#advanced_statistics" %></strong></p>
<ul class="menu vertical">
<li>
<%= link_to t("stats.budgets.total_investments"), "#total_investments" %>
</li>
<li>
<%= link_to t("stats.budgets.by_phase"), "#stats_by_phase" %>
</li>
<li>
<%= link_to t("stats.budgets.by_heading"), "#stats_by_heading" %>
</li>
</ul>
<%= render "advanced_stats_links" if @stats.advanced? %>
</div>
<div class="small-12 medium-9 column stats-content">
<%= render "shared/stats/participation", stats: @stats %>
<div id="advanced_statistics">
<h3 class="section-title"><%= t("stats.advanced") %></h3>
<div id="total_investments" class="stats-group">
<h4><%= t("stats.budgets.total_investments") %></h4>
<%= number_with_info_tags(
@stats.total_budget_investments,
t("stats.budgets.total_investments"),
html_class: "total-investments"
) %>
<%= number_with_info_tags(@stats.total_unfeasible_investments,
t("stats.budgets.total_unfeasible_investments")) %>
<%= number_with_info_tags(@stats.total_selected_investments,
t("stats.budgets.total_selected_investments")) %>
</div>
<div id="stats_by_phase" class="stats-group">
<h4><%= t("stats.budgets.by_phase") %></h4>
<% @stats.phases.each do |phase| %>
<%= number_with_info_tags(
@stats.send("total_participants_#{phase}_phase"),
t("stats.budgets.participants_#{phase}_phase")
) %>
<% end %>
</div>
<div id="stats_by_heading" class="stats-group">
<h4 class="margin-bottom"><%= t("stats.budgets.by_heading") %></h4>
<table class="stats-districts survey-districts">
<thead>
<tr>
<th scope="col" rowspan="2"><%= t("stats.budgets.heading") %></th>
<th scope="col" rowspan="2"><%= t("stats.budgets.investments_sent_html") %></th>
<% @stats.all_phases.each do |phase| %>
<th scope="col" colspan="3">
<%= t("stats.budgets.participants_#{phase}_phase") %>
</th>
<% end %>
</tr>
<tr>
<% @stats.all_phases.each do %>
<th scope="col" class="phase-subheader"><%= t("stats.budgets.total") %></th>
<th scope="col" class="phase-subheader"><%= t("stats.budgets.percent_total_participants") %></th>
<th scope="col" class="phase-subheader"><%= t("stats.budgets.percent_heading_census") %></th>
<% end %>
</tr>
</thead>
<tbody id="headings">
<% @headings.each do |heading| %>
<tr id="<%= heading.name.parameterize %>">
<td class="border-left">
<strong><%= heading.name %></strong>
</td>
<td id="total_spending_proposals_heading_<%= heading.id %>"
class="text-center border-left border-right">
<%= @stats.headings[heading.id][:total_investments_count] %>
</td>
<% @stats.all_phases.each do |phase| %>
<td id="total_participants_<%= phase %>_phase_heading_<%= heading.id %>"
class="border-left text-center">
<%= @stats.headings[heading.id]["total_participants_#{phase}_phase".to_sym] %>
</td>
<td id="percentage_participants_<%= phase %>_phase_heading_<%= heading.id %>"
class="border-left border-right text-center">
<%= number_to_stats_percentage(@stats.headings[heading.id]["percentage_participants_#{phase}_phase".to_sym]) %>
</td>
<td id="percentage_district_population_<%= phase %>_phase_heading_<%= heading.id %>"
class="text-center border-right">
<%= number_to_stats_percentage(@stats.headings[heading.id]["percentage_district_population_#{phase}_phase".to_sym]) %>
</td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
<%= render "advanced_stats", stats: @stats if @stats.advanced? %>
<div class="row margin">
<div class="small-12 column">

View File

@@ -0,0 +1,92 @@
<div id="advanced_statistics">
<h3 class="section-title"><%= t("stats.advanced") %></h3>
<div id="stats_by_channel" class="stats-group">
<h4><%= t("stats.polls.by_channel") %></h4>
<% stats.channels.each do |channel| %>
<%= number_with_info_tags(
stats.send("total_participants_#{channel}"),
t("stats.polls.#{channel}_percentage",
percentage: number_to_stats_percentage(stats.send(:"total_participants_#{channel}_percentage"))
),
html_class: channel
) %>
<% end %>
</div>
<div id="vote_stats_by_channel" class="stats-group">
<h4><%= t("stats.polls.vote_by_channel") %></h4>
<table class="stack">
<thead>
<tr>
<th scope="col"><%= t("polls.show.stats.votes") %></th>
<% stats.channels.each do |channel| %>
<th scope="col"><%= t("polls.show.stats.#{channel}") %></th>
<% end %>
<th scope="col"><%= t("polls.show.stats.total") %></th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row"><%= t("polls.show.stats.valid") %></th>
<% stats.channels.each do |channel| %>
<td>
<%= stats.send(:"total_#{channel}_valid") %>
<small><em>(<%= stats.send(:"valid_percentage_#{channel}").round(2) %>%)</em></small>
</td>
<% end %>
<td>
<%= stats.total_valid_votes %>
<small><em>(<%= stats.total_valid_percentage.round(2) %>%)</em></small>
</td>
</tr>
<tr>
<th scope="row"><%= t("polls.show.stats.white") %></th>
<% stats.channels.each do |channel| %>
<td>
<%= stats.send(:"total_#{channel}_white") %>
<small><em>(<%= stats.send(:"white_percentage_#{channel}").round(2) %>%)</em></small>
</td>
<% end %>
<td><%= stats.total_white_votes %>
<small><em>(<%= stats.total_white_percentage.round(2) %>%)</em></small>
</td>
</tr>
<tr>
<th scope="row"><%= t("polls.show.stats.null_votes") %></th>
<% stats.channels.each do |channel| %>
<td>
<%= stats.send(:"total_#{channel}_null") %>
<small><em>(<%= stats.send(:"null_percentage_#{channel}").round(2) %>%)</em></small>
</td>
<% end %>
<td>
<%= stats.total_null_votes %>
<small><em>(<%= stats.total_null_percentage.round(2) %>%)</em></small>
</td>
</tr>
<tr>
<th scope="row"><%= t("polls.show.stats.total") %></th>
<% stats.channels.each do |channel| %>
<td>
<%= stats.send(:"total_participants_#{channel}") %>
<small><em>(<%= stats.send(:"total_participants_#{channel}_percentage").round(2) %>%)</em></small>
</td>
<% end %>
<td><%= stats.total_participants %></td>
</tr>
</tbody>
</table>
</div>
</div>

View File

@@ -0,0 +1,9 @@
<p><strong><%= link_to t("stats.advanced"), "#advanced_statistics" %></strong></p>
<ul class="menu vertical">
<li>
<%= link_to t("stats.polls.by_channel"), "#stats_by_channel" %>
</li>
<li>
<%= link_to t("stats.polls.vote_by_channel"), "#vote_stats_by_channel" %>
</li>
</ul>

View File

@@ -8,118 +8,17 @@
<div class="row margin">
<div class="small-12 medium-3 column sidebar">
<%= render "shared/stats/links", stats: @stats %>
<p><strong><%= link_to t("stats.advanced"), "#advanced_statistics" %></strong></p>
<ul class="menu vertical">
<li>
<%= link_to t("stats.polls.by_channel"), "#stats_by_channel" %>
</li>
<li>
<%= link_to t("stats.polls.vote_by_channel"), "#vote_stats_by_channel" %>
</li>
</ul>
<%= render "advanced_stats_links" if @stats.advanced? %>
</div>
<div class="small-12 medium-9 column stats-content">
<%= render "shared/stats/participation", stats: @stats %>
<%= render "advanced_stats", stats: @stats if @stats.advanced? %>
<div id="advanced_statistics">
<h3 class="section-title"><%= t("stats.advanced") %></h3>
<div id="stats_by_channel" class="stats-group">
<h4><%= t("stats.polls.by_channel") %></h4>
<% @stats.channels.each do |channel| %>
<%= number_with_info_tags(
@stats.send("total_participants_#{channel}"),
t("stats.polls.#{channel}_percentage",
percentage: number_to_stats_percentage(@stats.send(:"total_participants_#{channel}_percentage"))
),
html_class: channel
) %>
<% end %>
</div>
<div id="vote_stats_by_channel" class="stats-group">
<h4><%= t("stats.polls.vote_by_channel") %></h4>
<table class="stack">
<thead>
<tr>
<th scope="col"><%= t("polls.show.stats.votes") %></th>
<% @stats.channels.each do |channel| %>
<th scope="col"><%= t("polls.show.stats.#{channel}") %></th>
<% end %>
<th scope="col"><%= t("polls.show.stats.total") %></th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row"><%= t("polls.show.stats.valid") %></th>
<% @stats.channels.each do |channel| %>
<td>
<%= @stats.send(:"total_#{channel}_valid") %>
<small><em>(<%= @stats.send(:"valid_percentage_#{channel}").round(2) %>%)</em></small>
</td>
<% end %>
<td>
<%= @stats.total_valid_votes %>
<small><em>(<%= @stats.total_valid_percentage.round(2) %>%)</em></small>
</td>
</tr>
<tr>
<th scope="row"><%= t("polls.show.stats.white") %></th>
<% @stats.channels.each do |channel| %>
<td>
<%= @stats.send(:"total_#{channel}_white") %>
<small><em>(<%= @stats.send(:"white_percentage_#{channel}").round(2) %>%)</em></small>
</td>
<% end %>
<td><%= @stats.total_white_votes %>
<small><em>(<%= @stats.total_white_percentage.round(2) %>%)</em></small>
</td>
</tr>
<tr>
<th scope="row"><%= t("polls.show.stats.null_votes") %></th>
<% @stats.channels.each do |channel| %>
<td>
<%= @stats.send(:"total_#{channel}_null") %>
<small><em>(<%= @stats.send(:"null_percentage_#{channel}").round(2) %>%)</em></small>
</td>
<% end %>
<td>
<%= @stats.total_null_votes %>
<small><em>(<%= @stats.total_null_percentage.round(2) %>%)</em></small>
</td>
</tr>
<tr>
<th scope="row"><%= t("polls.show.stats.total") %></th>
<% @stats.channels.each do |channel| %>
<td>
<%= @stats.send(:"total_participants_#{channel}") %>
<small><em>(<%= @stats.send(:"total_participants_#{channel}_percentage").round(2) %>%)</em></small>
</td>
<% end %>
<td><%= @stats.total_participants %></td>
</tr>
</tbody>
</table>
</div>
<div id="total_no_demographic_data">
<p class="help-text">
<%= t("stats.no_demographic_data", count: @stats.total_no_demographic_data) %>
</p>
</div>
<div id="total_no_demographic_data">
<p class="help-text">
<%= t("stats.no_demographic_data", count: @stats.total_no_demographic_data) %>
</p>
</div>
</div>
</div>

View File

@@ -127,6 +127,7 @@ ignore_unused:
- "budgets.index.section_header.*"
- "activerecord.*"
- "activemodel.*"
- "attributes.*"
- "date.order"
- "unauthorized.*"
- "admin.officials.level_*"

View File

@@ -1,4 +1,8 @@
en:
attributes:
results_enabled: "Show results"
stats_enabled: "Show stats"
advanced_stats_enabled: "Show advanced stats"
activerecord:
models:
activity:

View File

@@ -1049,10 +1049,6 @@ en:
geozone_restricted: "Restricted to districts"
new:
title: "New poll"
show_results_and_stats: "Show results and stats"
show_results: "Show results"
show_stats: "Show stats"
results_and_stats_reminder: "Marking these checkboxes the results and/or stats of this poll will be publicly available and every user will see them."
submit_button: "Create poll"
edit:
title: "Edit poll"
@@ -1330,6 +1326,8 @@ en:
created_at: Created at
delete: Delete
color_help: Hexadecimal format
show_results_and_stats: "Show results and stats"
results_and_stats_reminder: "Marking these checkboxes the results and/or stats will be publicly available and every user will see them."
spending_proposals:
index:
geozone_filter_all: All zones

View File

@@ -1,4 +1,8 @@
es:
attributes:
results_enabled: "Mostrar resultados"
stats_enabled: "Mostrar estadísticas"
advanced_stats_enabled: "Mostrar estadísticas avanzadas"
activerecord:
models:
activity:

View File

@@ -1048,10 +1048,6 @@ es:
geozone_restricted: "Restringida a los distritos"
new:
title: "Nueva votación"
show_results_and_stats: "Mostrar resultados y estadísticas"
show_results: "Mostrar resultados"
show_stats: "Mostrar estadísticas"
results_and_stats_reminder: "Si marcas estas casillas los resultados y/o estadísticas de esta votación serán públicos y podrán verlos todos los usuarios."
submit_button: "Crear votación"
edit:
title: "Editar votación"
@@ -1329,6 +1325,8 @@ es:
created_at: Fecha de creación
delete: Eliminar
color_help: Formato hexadecimal
show_results_and_stats: "Mostrar resultados y estadísticas"
results_and_stats_reminder: "Si marcas estas casillas los resultados y/o estadísticas serán públicos y podrán verlos todos los usuarios."
spending_proposals:
index:
geozone_filter_all: Todos los ámbitos de actuación

View File

@@ -0,0 +1,11 @@
class CreateReports < ActiveRecord::Migration[5.0]
def change
create_table :reports do |t|
t.boolean :stats
t.boolean :results
t.references :process, polymorphic: true
t.timestamps null: false
end
end
end

View File

@@ -0,0 +1,5 @@
class AddAdvancedStatsToReports < ActiveRecord::Migration[5.0]
def change
add_column :reports, :advanced_stats, :boolean
end
end

View File

@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20190411090023) do
ActiveRecord::Schema.define(version: 20190429125842) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -1234,6 +1234,17 @@ ActiveRecord::Schema.define(version: 20190411090023) do
t.index ["related_content_id"], name: "opposite_related_content", using: :btree
end
create_table "reports", force: :cascade do |t|
t.boolean "stats"
t.boolean "results"
t.string "process_type"
t.integer "process_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "advanced_stats"
t.index ["process_type", "process_id"], name: "index_reports_on_process_type_and_process_id", using: :btree
end
create_table "settings", force: :cascade do |t|
t.string "key"
t.string "value"

29
lib/migrations/reports.rb Normal file
View File

@@ -0,0 +1,29 @@
class Migrations::Reports
def migrate
migrate_polls
migrate_budgets
end
private
def migrate_polls
Poll.find_each do |poll|
next unless poll.report.new_record?
poll.report.update!(
results: poll.read_attribute(:results_enabled),
stats: poll.read_attribute(:stats_enabled),
advanced_stats: poll.read_attribute(:stats_enabled),
)
end
end
def migrate_budgets
Budget.find_each do |budget|
next unless budget.report.new_record?
budget.report.update!(results: true, stats: true, advanced_stats: true)
end
end
end

View File

@@ -0,0 +1,6 @@
namespace :stats_and_results do
desc "Migrates stats_enabled and results_enabled data to enabled reports"
task migrate_to_reports: :environment do
Migrations::Reports.new.migrate
end
end

View File

@@ -61,6 +61,8 @@ FactoryBot.define do
trait :finished do
phase "finished"
results_enabled true
stats_enabled true
end
end

View File

@@ -269,7 +269,7 @@ feature "Admin budgets" do
end
scenario "For a finished Budget" do
budget = create(:budget, phase: "finished")
budget = create(:budget, :finished)
allow_any_instance_of(Budget).to receive(:has_winning_investments?).and_return(true)
visit edit_admin_budget_path(budget)
@@ -279,7 +279,7 @@ feature "Admin budgets" do
end
scenario "Recalculate for a finished Budget" do
budget = create(:budget, phase: "finished")
budget = create(:budget, :finished)
group = create(:budget_group, budget: budget)
heading = create(:budget_heading, group: group)
create(:budget_investment, :winner, heading: heading)

View File

@@ -2,7 +2,7 @@ require "rails_helper"
feature "Executions" do
let(:budget) { create(:budget, phase: "finished") }
let(:budget) { create(:budget, :finished) }
let(:group) { create(:budget_group, budget: budget) }
let(:heading) { create(:budget_heading, group: group) }

View File

@@ -522,7 +522,7 @@ feature "Budget Investments" do
context "Results Phase" do
before { budget.update(phase: "finished") }
before { budget.update(phase: "finished", results_enabled: true) }
scenario "show winners by default" do
investment1 = create(:budget_investment, :winner, heading: heading)

View File

@@ -2,7 +2,7 @@ require "rails_helper"
feature "Results" do
let(:budget) { create(:budget, phase: "finished") }
let(:budget) { create(:budget, :finished) }
let(:group) { create(:budget_group, budget: budget) }
let(:heading) { create(:budget_heading, group: group, price: 1000) }

View File

@@ -7,22 +7,22 @@ feature "Stats" do
let(:heading) { create(:budget_heading, group: group, price: 1000) }
describe "Show" do
describe "advanced stats" do
let(:budget) { create(:budget, :finished) }
it "is not accessible if supports phase is not finished" do
budget.update(phase: "selecting")
scenario "advanced stats enabled" do
budget.update(advanced_stats_enabled: true)
visit budget_stats_path(budget.id)
expect(page).to have_content "You do not have permission to carry out the action "\
"'read_stats' on budget."
visit budget_stats_path(budget)
expect(page).to have_content "Advanced statistics"
end
scenario "advanced stats disabled" do
visit budget_stats_path(budget)
expect(page).not_to have_content "Advanced statistics"
end
end
it "is accessible if supports phase is finished" do
budget.update(phase: "valuating")
visit budget_stats_path(budget.id)
expect(page).to have_content "Stats"
end
end
end

View File

@@ -432,7 +432,18 @@ feature "Polls" do
expect(page).to have_content("Questions")
visit stats_poll_path(poll)
expect(page).to have_content("Participation data")
expect(page).not_to have_content "Advanced statistics"
end
scenario "Advanced stats enabled" do
poll = create(:poll, :expired, stats_enabled: true, advanced_stats_enabled: true)
visit stats_poll_path(poll)
expect(page).to have_content "Participation data"
expect(page).to have_content "Advanced statistics"
end
scenario "Don't show poll results and stats if not enabled" do

View File

@@ -0,0 +1,53 @@
require "rails_helper"
describe Migrations::Reports do
describe "#migrate" do
it "ignores polls with existing reports" do
create(:poll, results_enabled: true, stats_enabled: true) do |poll|
poll.write_attribute(:results_enabled, false)
poll.write_attribute(:stats_enabled, false)
poll.save
end
Migrations::Reports.new.migrate
expect(Poll.last.results_enabled).to be true
expect(Poll.last.stats_enabled).to be true
expect(Poll.last.advanced_stats_enabled).to be nil
end
it "migrates polls with no reports" do
create(:poll) do |poll|
poll.write_attribute(:results_enabled, true)
poll.write_attribute(:stats_enabled, true)
poll.save
end
Migrations::Reports.new.migrate
expect(Poll.last.results_enabled).to be true
expect(Poll.last.stats_enabled).to be true
expect(Poll.last.advanced_stats_enabled).to be true
end
it "ignores budgets with existing reports" do
create(:budget, results_enabled: false, stats_enabled: false, advanced_stats_enabled: false)
Migrations::Reports.new.migrate
expect(Budget.last.results_enabled).to be false
expect(Budget.last.stats_enabled).to be false
expect(Budget.last.advanced_stats_enabled).to be false
end
it "enables results and stats for every budget" do
create(:budget)
Migrations::Reports.new.migrate
expect(Budget.last.results_enabled).to be true
expect(Budget.last.stats_enabled).to be true
expect(Budget.last.advanced_stats_enabled).to be true
end
end
end

View File

@@ -8,9 +8,6 @@ describe Abilities::Everyone do
let(:debate) { create(:debate) }
let(:proposal) { create(:proposal) }
let(:reviewing_ballot_budget) { create(:budget, phase: "reviewing_ballots") }
let(:finished_budget) { create(:budget, phase: "finished") }
it { should be_able_to(:index, Debate) }
it { should be_able_to(:show, debate) }
it { should_not be_able_to(:edit, Debate) }
@@ -32,8 +29,6 @@ describe Abilities::Everyone do
it { should be_able_to(:index, Budget) }
it { should be_able_to(:read_results, finished_budget) }
it { should_not be_able_to(:read_results, reviewing_ballot_budget) }
it { should_not be_able_to(:manage, Dashboard::Action) }
context "when accessing poll results" do
@@ -73,4 +68,44 @@ describe Abilities::Everyone do
it { should_not be_able_to(:stats, poll) }
end
end
context "when accessing budget results" do
context "budget is not finished" do
let(:budget) { create(:budget, phase: "reviewing_ballots", results_enabled: true) }
it { should_not be_able_to(:read_results, budget) }
end
context "budget is finished" do
let(:budget) { create(:budget, :finished) }
it { should be_able_to(:read_results, budget) }
end
context "results disabled" do
let(:budget) { create(:budget, :finished, results_enabled: false) }
it { should_not be_able_to(:read_results, budget) }
end
end
context "when accessing budget stats" do
context "supports phase is not finished" do
let(:budget) { create(:budget, phase: "selecting", stats_enabled: true) }
it { should_not be_able_to(:read_stats, budget) }
end
context "supports phase is finished" do
let(:budget) { create(:budget, phase: "valuating", stats_enabled: true) }
it { should be_able_to(:read_stats, budget) }
end
context "stats disabled" do
let(:budget) { create(:budget, phase: "valuating", stats_enabled: false) }
it { should_not be_able_to(:read_stats, budget) }
end
end
end

View File

@@ -5,6 +5,7 @@ describe Budget do
let(:budget) { create(:budget) }
it_behaves_like "sluggable", updatable_slug_trait: :drafting
it_behaves_like "reportable"
describe "name" do
before do

View File

@@ -0,0 +1,75 @@
shared_examples "reportable" do
let(:reportable) { create(model_name(described_class)) }
describe "#results_enabled" do
it "can write and read the attribute" do
reportable.results_enabled = true
expect(reportable.results_enabled?).to be true
expect(reportable.results_enabled).to be true
reportable.results_enabled = false
expect(reportable.results_enabled?).to be false
expect(reportable.results_enabled).to be false
end
it "can save the value to the database" do
reportable.update(results_enabled: true)
saved_reportable = described_class.last
expect(saved_reportable.results_enabled?).to be true
expect(saved_reportable.results_enabled).to be true
reportable.update(results_enabled: false)
saved_reportable = described_class.last
expect(saved_reportable.results_enabled?).to be false
expect(saved_reportable.results_enabled).to be false
end
it "uses the `has_one` relation instead of the original column" do
skip "there's no original column" unless reportable.has_attribute?(:results_enabled)
reportable.update(results_enabled: true)
expect(reportable.read_attribute(:results_enabled)).to be false
end
end
describe "#stats_enabled" do
it "can write and read the attribute" do
reportable.stats_enabled = true
expect(reportable.stats_enabled?).to be true
expect(reportable.stats_enabled).to be true
reportable.stats_enabled = false
expect(reportable.stats_enabled?).to be false
expect(reportable.stats_enabled).to be false
end
it "can save the attribute to the database" do
reportable.update(stats_enabled: true)
saved_reportable = described_class.last
expect(saved_reportable.stats_enabled?).to be true
expect(saved_reportable.stats_enabled).to be true
reportable.update(stats_enabled: false)
saved_reportable = described_class.last
expect(saved_reportable.stats_enabled?).to be false
expect(saved_reportable.stats_enabled).to be false
end
it "uses the `has_one` relation instead of the original column" do
skip "there's no original column" unless reportable.has_attribute?(:stats_enabled)
reportable.update(stats_enabled: true)
expect(reportable.read_attribute(:stats_enabled)).to be false
end
end
end

View File

@@ -6,6 +6,7 @@ describe Poll do
describe "Concerns" do
it_behaves_like "notifiable"
it_behaves_like "reportable"
end
describe "validations" do