Merge pull request #3520 from consul/backport-refactor_stats_enabled
Add options to show advanced stats
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
9
app/controllers/concerns/report_attributes.rb
Normal file
9
app/controllers/concerns/report_attributes.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
module ReportAttributes
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
private
|
||||
|
||||
def report_attributes
|
||||
Report::KINDS.map { |kind| :"#{kind}_enabled" }
|
||||
end
|
||||
end
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -3,6 +3,7 @@ class Budget < ApplicationRecord
|
||||
include Measurable
|
||||
include Sluggable
|
||||
include StatsVersionable
|
||||
include Reportable
|
||||
|
||||
translates :name, touch: true
|
||||
include Globalizable
|
||||
|
||||
23
app/models/concerns/reportable.rb
Normal file
23
app/models/concerns/reportable.rb
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
5
app/models/report.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
class Report < ApplicationRecord
|
||||
KINDS = %i[results stats advanced_stats]
|
||||
|
||||
belongs_to :process, polymorphic: true
|
||||
end
|
||||
@@ -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" %>
|
||||
|
||||
@@ -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"),
|
||||
|
||||
7
app/views/admin/shared/_show_results_fields.html.erb
Normal file
7
app/views/admin/shared/_show_results_fields.html.erb
Normal 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>
|
||||
83
app/views/budgets/stats/_advanced_stats.html.erb
Normal file
83
app/views/budgets/stats/_advanced_stats.html.erb
Normal 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>
|
||||
12
app/views/budgets/stats/_advanced_stats_links.html.erb
Normal file
12
app/views/budgets/stats/_advanced_stats_links.html.erb
Normal 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>
|
||||
@@ -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">
|
||||
|
||||
92
app/views/polls/_advanced_stats.html.erb
Normal file
92
app/views/polls/_advanced_stats.html.erb
Normal 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>
|
||||
9
app/views/polls/_advanced_stats_links.html.erb
Normal file
9
app/views/polls/_advanced_stats_links.html.erb
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
@@ -127,6 +127,7 @@ ignore_unused:
|
||||
- "budgets.index.section_header.*"
|
||||
- "activerecord.*"
|
||||
- "activemodel.*"
|
||||
- "attributes.*"
|
||||
- "date.order"
|
||||
- "unauthorized.*"
|
||||
- "admin.officials.level_*"
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
en:
|
||||
attributes:
|
||||
results_enabled: "Show results"
|
||||
stats_enabled: "Show stats"
|
||||
advanced_stats_enabled: "Show advanced stats"
|
||||
activerecord:
|
||||
models:
|
||||
activity:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
11
db/migrate/20190424114803_create_reports.rb
Normal file
11
db/migrate/20190424114803_create_reports.rb
Normal 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
|
||||
@@ -0,0 +1,5 @@
|
||||
class AddAdvancedStatsToReports < ActiveRecord::Migration[5.0]
|
||||
def change
|
||||
add_column :reports, :advanced_stats, :boolean
|
||||
end
|
||||
end
|
||||
13
db/schema.rb
13
db/schema.rb
@@ -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
29
lib/migrations/reports.rb
Normal 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
|
||||
6
lib/tasks/stats_and_results.rake
Normal file
6
lib/tasks/stats_and_results.rake
Normal 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
|
||||
@@ -61,6 +61,8 @@ FactoryBot.define do
|
||||
|
||||
trait :finished do
|
||||
phase "finished"
|
||||
results_enabled true
|
||||
stats_enabled true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) }
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) }
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
53
spec/lib/migrations/reports_spec.rb
Normal file
53
spec/lib/migrations/reports_spec.rb
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
75
spec/models/concerns/reportable.rb
Normal file
75
spec/models/concerns/reportable.rb
Normal 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
|
||||
@@ -6,6 +6,7 @@ describe Poll do
|
||||
|
||||
describe "Concerns" do
|
||||
it_behaves_like "notifiable"
|
||||
it_behaves_like "reportable"
|
||||
end
|
||||
|
||||
describe "validations" do
|
||||
|
||||
Reference in New Issue
Block a user