Create reports

This table will store which reports (stats, results, ...) will be shown
for a certain process (polls, budgets, ...).

Note Rails fails to save a poll and its report when both are new records
if we add a `validate :process, presence: true` rule. Since it caused a
lot of trouble when creating records for tests during factories rule
completely. Instead, I've created the `results_enabled=` and
`stats_enabled=` methods, so tests are easier to set up, while also
automatically creating a report if it doesn't already exist. This also
decouples form structure and database implemenation.

Originally I named this table `enabled_reports` and instead of having
`stats` and `results` columns, it had an `enabled` column and a `kind`
column, which would be set to "stats" or "results". However, although
that table would allow us to add arbitrary reports easily, I found the
way we had to handle the `has_many` relationship was a bit too complex.
This commit is contained in:
Javi Martín
2019-04-29 14:13:46 +02:00
parent 2d29243a9e
commit 354b183e17
13 changed files with 136 additions and 11 deletions

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

@@ -0,0 +1,30 @@
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
def results_enabled?
report&.results?
end
alias_method :results_enabled, :results_enabled?
def stats_enabled?
report&.stats?
end
alias_method :stats_enabled, :stats_enabled?
def results_enabled=(enabled)
report.results = enabled
end
def stats_enabled=(enabled)
report.stats = enabled
end
end

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

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

@@ -0,0 +1,3 @@
class Report < ApplicationRecord
belongs_to :process, polymorphic: true
end

View File

@@ -1,12 +1,11 @@
<%= 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") %>
<%= f.check_box :results_enabled %>
<%= f.check_box :stats_enabled %>
<p class="small"><%= t("admin.polls.new.results_and_stats_reminder") %></p>
</fieldset>
<div class="small-12 medium-4 large-2">
<%= f.submit t("admin.polls.#{admin_submit_action(@poll)}.submit_button"),
class: "button success expanded" %>

View File

@@ -212,6 +212,8 @@ en:
geozone_restricted: "Restricted by geozone"
summary: "Summary"
description: "Description"
results_enabled: "Show results"
stats_enabled: "Show stats"
poll/translation:
name: "Name"
summary: "Summary"

View File

@@ -1049,8 +1049,6 @@ en:
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:

View File

@@ -212,6 +212,8 @@ es:
geozone_restricted: "Restringida por zonas"
summary: "Resumen"
description: "Descripción"
results_enabled: "Mostrar resultados"
stats_enabled: "Mostrar estadísticas"
poll/translation:
name: "Nombre"
summary: "Resumen"

View File

@@ -1048,8 +1048,6 @@ es:
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:

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

@@ -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: 20190424114803) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -1234,6 +1234,16 @@ 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.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"

View File

@@ -0,0 +1,71 @@
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
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
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