diff --git a/app/assets/stylesheets/legislation_process.scss b/app/assets/stylesheets/legislation_process.scss
index cb4488004..81c090a5f 100644
--- a/app/assets/stylesheets/legislation_process.scss
+++ b/app/assets/stylesheets/legislation_process.scss
@@ -1006,3 +1006,84 @@
font-weight: bold;
}
}
+
+// 10. Legislation summary
+// -------------------------
+
+.process-summary {
+ > section {
+ @include grid-row;
+ margin-top: $line-height * 1.5;
+ padding: 0 rem-calc(16);
+
+ > header {
+ background: none;
+ border: 0;
+ margin: 0;
+ }
+ }
+
+ h4,
+ p {
+ margin-bottom: 0;
+ }
+
+ > section > header,
+ .question-title,
+ .annotation-title,
+ .comment-summary,
+ .proposal-summary {
+ @include breakpoint(medium) {
+ align-items: center;
+ display: flex;
+
+ > :first-child {
+ $margin: rem-calc(map-get($grid-column-gutter, "medium"));
+
+ margin-right: $margin;
+ width: calc(75% - #{$margin});
+ }
+ }
+ }
+
+ .debate-summary,
+ .proposal-summary,
+ .annotation-summary {
+ @extend %panel;
+ min-height: auto;
+ padding-bottom: rem-calc(16);
+ padding-top: rem-calc(16);
+
+ &:not(:last-child) {
+ margin-bottom: $line-height / 2;
+ }
+ }
+
+ .comments-count {
+ @include has-fa-icon(comments, r);
+ }
+
+ .question-title:not(:only-child) {
+ margin-bottom: $line-height / 2;
+ }
+
+ .annotation-title {
+ margin-bottom: $line-height / 2;
+ margin-top: $line-height / 2;
+ }
+
+ .annotation-quote {
+ border: 1px solid $black;
+ padding: rem-calc(10);
+ }
+
+ .comment-summary {
+ margin-bottom: $line-height / 2;
+
+ > :first-child {
+ background-color: rgba(217, 216, 243, 0.2);
+ border-radius: rem-calc(10);
+ padding: rem-calc(12);
+ }
+ }
+}
diff --git a/app/assets/stylesheets/participation.scss b/app/assets/stylesheets/participation.scss
index 895e4d2cc..a825c77bb 100644
--- a/app/assets/stylesheets/participation.scss
+++ b/app/assets/stylesheets/participation.scss
@@ -612,6 +612,45 @@
}
}
+%panel {
+ background: #fff;
+ border: 1px solid;
+ border-color: #e5e6e9 #dfe0e4 #d0d1d5;
+ border-radius: 0;
+ box-shadow: 0 1px 3px 0 $border;
+ margin-bottom: rem-calc(12);
+ min-height: rem-calc(192);
+ padding: rem-calc(12) rem-calc(12) 0;
+
+ @include breakpoint(medium) {
+ margin-bottom: rem-calc(-1);
+ padding-bottom: rem-calc(12);
+ }
+
+ @include breakpoint(medium) {
+ .divider {
+ display: inline-block;
+ }
+ }
+
+ h3 {
+ font-weight: bold;
+ margin-top: $line-height / 2;
+
+ a {
+ color: $text;
+ }
+ }
+
+ &.past-budgets {
+ min-height: 0;
+
+ .button ~ .button {
+ margin-left: $line-height / 2;
+ }
+ }
+}
+
.debate,
.proposal,
.investment-project,
@@ -621,42 +660,7 @@
margin: $line-height / 4 0;
.panel {
- background: #fff;
- border: 1px solid;
- border-color: #e5e6e9 #dfe0e4 #d0d1d5;
- border-radius: 0;
- box-shadow: 0 1px 3px 0 $border;
- margin-bottom: rem-calc(12);
- min-height: rem-calc(192);
- padding: rem-calc(12) rem-calc(12) 0;
-
- @include breakpoint(medium) {
- margin-bottom: rem-calc(-1);
- padding-bottom: rem-calc(12);
- }
-
- @include breakpoint(medium) {
- .divider {
- display: inline-block;
- }
- }
-
- h3 {
- font-weight: bold;
- margin-top: $line-height / 2;
-
- a {
- color: $text;
- }
- }
-
- &.past-budgets {
- min-height: 0;
-
- .button ~ .button {
- margin-left: $line-height / 2;
- }
- }
+ @extend %panel;
}
.debate-content,
diff --git a/app/controllers/legislation/processes_controller.rb b/app/controllers/legislation/processes_controller.rb
index d6dd6d3e5..2c8f01076 100644
--- a/app/controllers/legislation/processes_controller.rb
+++ b/app/controllers/legislation/processes_controller.rb
@@ -97,6 +97,12 @@ class Legislation::ProcessesController < Legislation::BaseController
@phase = :milestones
end
+ def summary
+ @phase = :summary
+ @proposals = @process.proposals.selected
+ @comments = @process.draft_versions.published.last&.best_comments || Comment.none
+ end
+
def proposals
set_process
@phase = :proposals_phase
diff --git a/app/models/abilities/administrator.rb b/app/models/abilities/administrator.rb
index 25dcb60c7..24b70e453 100644
--- a/app/models/abilities/administrator.rb
+++ b/app/models/abilities/administrator.rb
@@ -90,7 +90,9 @@ module Abilities
can :access, :ckeditor
can :manage, Ckeditor::Picture
- can [:manage], ::Legislation::Process
+ can [:read, :debate, :draft_publication, :allegations, :result_publication,
+ :milestones], Legislation::Process
+ can [:create, :update, :destroy], Legislation::Process
can [:manage], ::Legislation::DraftVersion
can [:manage], ::Legislation::Question
can [:manage], ::Legislation::Proposal
diff --git a/app/models/abilities/everyone.rb b/app/models/abilities/everyone.rb
index 89a29f4b9..432099150 100644
--- a/app/models/abilities/everyone.rb
+++ b/app/models/abilities/everyone.rb
@@ -21,6 +21,8 @@ module Abilities
can :new, DirectMessage
can [:read, :debate, :draft_publication, :allegations, :result_publication,
:proposals, :milestones], Legislation::Process, published: true
+ can :summary, Legislation::Process,
+ id: Legislation::Process.past.published.where(result_publication_enabled: true).ids
can [:read, :changes, :go_to_version], Legislation::DraftVersion
can [:read], Legislation::Question
can [:read, :map, :share], Legislation::Proposal
diff --git a/app/models/comment.rb b/app/models/comment.rb
index 1552ce3a5..7af862a73 100644
--- a/app/models/comment.rb
+++ b/app/models/comment.rb
@@ -49,6 +49,7 @@ class Comment < ApplicationRecord
scope :sort_by_most_voted, -> { order(confidence_score: :desc, created_at: :desc) }
scope :sort_descendants_by_most_voted, -> { order(confidence_score: :desc, created_at: :asc) }
+ scope :sort_by_supports, -> { order("cached_votes_up - cached_votes_down DESC") }
scope :sort_by_newest, -> { order(created_at: :desc) }
scope :sort_descendants_by_newest, -> { order(created_at: :desc) }
@@ -129,6 +130,10 @@ class Comment < ApplicationRecord
cached_votes_up)
end
+ def votes_score
+ cached_votes_up - cached_votes_down
+ end
+
private
def validate_body_length
diff --git a/app/models/legislation/draft_version.rb b/app/models/legislation/draft_version.rb
index 93b65eb8f..9019d6cb8 100644
--- a/app/models/legislation/draft_version.rb
+++ b/app/models/legislation/draft_version.rb
@@ -40,4 +40,8 @@ class Legislation::DraftVersion < ApplicationRecord
def total_comments
annotations.sum(:comments_count)
end
+
+ def best_comments
+ Comment.where(commentable: annotations, ancestry: nil).sort_by_supports.limit(10)
+ end
end
diff --git a/app/models/legislation/question.rb b/app/models/legislation/question.rb
index ff9e0ea94..73f1cc946 100644
--- a/app/models/legislation/question.rb
+++ b/app/models/legislation/question.rb
@@ -44,4 +44,8 @@ class Legislation::Question < ApplicationRecord
def comments_open?
process.debate_phase.open?
end
+
+ def best_comments
+ comments.sort_by_supports.limit(3)
+ end
end
diff --git a/app/views/legislation/processes/_key_dates.html.erb b/app/views/legislation/processes/_key_dates.html.erb
index 4e8ab85c0..7e5842d1a 100644
--- a/app/views/legislation/processes/_key_dates.html.erb
+++ b/app/views/legislation/processes/_key_dates.html.erb
@@ -52,6 +52,15 @@
<% end %>
<% end %>
+
+ <% if can?(:summary, process) %>
+
>
+ <%= link_to summary_legislation_process_path(process) do %>
+ <%= t("legislation.summary.title") %>
+ <%= format_date(process.result_publication_date) %>
+ <% end %>
+
+ <% end %>
diff --git a/app/views/legislation/processes/_summary_allegations.html.erb b/app/views/legislation/processes/_summary_allegations.html.erb
new file mode 100644
index 000000000..345542dc1
--- /dev/null
+++ b/app/views/legislation/processes/_summary_allegations.html.erb
@@ -0,0 +1,32 @@
+
diff --git a/app/views/legislation/processes/_summary_comments.html.erb b/app/views/legislation/processes/_summary_comments.html.erb
new file mode 100644
index 000000000..7e8b61d60
--- /dev/null
+++ b/app/views/legislation/processes/_summary_comments.html.erb
@@ -0,0 +1,10 @@
+<% if comments.any? %>
+ <%= t("legislation.summary.most_voted_comments") %>
+
+ <% comments.each do |comment| %>
+
+ <% end %>
+<% end %>
diff --git a/app/views/legislation/processes/_summary_debate.html.erb b/app/views/legislation/processes/_summary_debate.html.erb
new file mode 100644
index 000000000..8530f192d
--- /dev/null
+++ b/app/views/legislation/processes/_summary_debate.html.erb
@@ -0,0 +1,28 @@
+
+
+ <%= t("legislation.summary.debate_phase") %>
+ <%= t("legislation.summary.debates", count: questions.count) %>
+
+
+ <% if questions.any? %>
+
+ <% questions.each do |question| %>
+
+
+
<%= link_to question.title, polymorphic_path(question) %>
+
+
+
+ <%= render "summary_comments", comments: question.best_comments %>
+
+ <% end %>
+
+ <% else %>
+
+
<%= t("legislation.processes.debate.empty_questions") %>
+
+ <% end %>
+
diff --git a/app/views/legislation/processes/_summary_proposals.html.erb b/app/views/legislation/processes/_summary_proposals.html.erb
new file mode 100644
index 000000000..09ccca777
--- /dev/null
+++ b/app/views/legislation/processes/_summary_proposals.html.erb
@@ -0,0 +1,23 @@
+
+
+ <%= t("legislation.summary.proposals_phase") %>
+ <%= t("legislation.summary.proposals", count: proposals.count) %>
+
+
+ <% if proposals.any? %>
+
+ <% proposals.sort_by_supports.each do |proposal| %>
+
+
+ <%= link_to proposal.title, polymorphic_path(proposal) %>
+
+
<%= t("legislation.summary.votes", count: proposal.votes_score) %>
+
+ <% end %>
+
+ <% else %>
+
+
<%= t("legislation.processes.proposals.empty_proposals") %>
+
+ <% end %>
+
diff --git a/app/views/legislation/processes/summary.html.erb b/app/views/legislation/processes/summary.html.erb
new file mode 100644
index 000000000..97c211d7a
--- /dev/null
+++ b/app/views/legislation/processes/summary.html.erb
@@ -0,0 +1,25 @@
+<% provide(:title) { @process.title } %>
+
+<%= render "legislation/processes/header", process: @process, header: :full %>
+
+<%= render "key_dates", process: @process, phase: @phase %>
+
+<% if @process.debate_phase.enabled? || @process.proposals_phase.enabled? || @process.allegations_phase.enabled? %>
+
+ <% if @process.debate_phase.enabled? %>
+ <%= render "summary_debate", questions: @process.questions %>
+ <% end %>
+
+ <% if @process.proposals_phase.enabled? %>
+ <%= render "summary_proposals", proposals: @proposals %>
+ <% end %>
+
+ <% if @process.allegations_phase.enabled? %>
+ <%= render "summary_allegations", comments: @comments %>
+ <% end %>
+
+<% else %>
+
+
<%= t("legislation.summary.process_empty") %>
+
+<% end %>
diff --git a/config/locales/en/legislation.yml b/config/locales/en/legislation.yml
index 967f6d1d9..665cb35ee 100644
--- a/config/locales/en/legislation.yml
+++ b/config/locales/en/legislation.yml
@@ -118,3 +118,31 @@ en:
tags_label: "Categories"
not_verified: "For vote proposals %{verify_account}."
process_title: Collaborative legislation process
+ summary:
+ title: "Summary"
+ votes:
+ zero: "%{count} votes"
+ one: "%{count} vote"
+ other: "%{count} votes"
+ debate_phase: "Debate phase"
+ proposals_phase: "Proposals phase"
+ allegations_phase: "Comments phase"
+ debates:
+ zero: "No debates"
+ one: "%{count} debate"
+ other: "%{count} debates"
+ proposals:
+ zero: "No proposals"
+ one: "%{count} proposal"
+ other: "%{count} proposals"
+ comments:
+ zero: "No comments"
+ one: "%{count} comment"
+ other: "%{count} comments"
+ top_comments:
+ zero: "No comments"
+ one: "%{count} comment"
+ other: "Top comments"
+ most_voted_comments: "Most voted comments"
+ no_allegation: "There are no comments"
+ process_empty: "This process didn't have any participation phases"
diff --git a/config/locales/es/legislation.yml b/config/locales/es/legislation.yml
index fe723e250..2c88e185b 100644
--- a/config/locales/es/legislation.yml
+++ b/config/locales/es/legislation.yml
@@ -118,3 +118,31 @@ es:
tags_label: "Categorías"
not_verified: "Para votar propuestas %{verify_account}."
process_title: Proceso de legislación colaborativa
+ summary:
+ title: "Resumen"
+ votes:
+ zero: "%{count} votos"
+ one: "%{count} voto"
+ other: "%{count} votos"
+ debate_phase: "Fase de debate"
+ proposals_phase: "Fase de propuestas"
+ allegations_phase: "Fase de comentarios"
+ debates:
+ zero: "No hay debates"
+ one: "%{count} debate"
+ other: "%{count} debates"
+ proposals:
+ zero: "No hay propuestas"
+ one: "%{count} propuesta"
+ other: "%{count} propuestas"
+ comments:
+ zero: "No hay comentarios"
+ one: "%{count} comentario"
+ other: "%{count} comentarios"
+ top_comments:
+ zero: "No hay comentarios"
+ one: "%{count} comentario"
+ other: "Los más votados"
+ most_voted_comments: "Comentarios más votados"
+ no_allegation: "No hay comentarios"
+ process_empty: "Este proceso no ha tenido fases de participación"
diff --git a/config/routes/legislation.rb b/config/routes/legislation.rb
index 2458a2bae..ee92ce26c 100644
--- a/config/routes/legislation.rb
+++ b/config/routes/legislation.rb
@@ -7,6 +7,7 @@ namespace :legislation do
get :result_publication
get :proposals
get :milestones
+ get :summary
end
resources :questions, only: [:show] do
diff --git a/spec/models/abilities/administrator_spec.rb b/spec/models/abilities/administrator_spec.rb
index bbf8f91fe..c87cc889d 100644
--- a/spec/models/abilities/administrator_spec.rb
+++ b/spec/models/abilities/administrator_spec.rb
@@ -18,6 +18,10 @@ describe Abilities::Administrator do
let(:legislation_question) { create(:legislation_question) }
let(:poll_question) { create(:poll_question) }
+ let(:past_process) { create(:legislation_process, :past) }
+ let(:past_draft_process) { create(:legislation_process, :past, :not_published) }
+ let(:open_process) { create(:legislation_process, :open) }
+
let(:proposal_document) { build(:document, documentable: proposal, user: proposal.author) }
let(:budget_investment_document) { build(:document, documentable: budget_investment) }
let(:poll_question_document) { build(:document, documentable: poll_question) }
@@ -67,6 +71,10 @@ describe Abilities::Administrator do
it { should be_able_to(:comment_as_administrator, legislation_question) }
it { should_not be_able_to(:comment_as_moderator, legislation_question) }
+ it { should be_able_to(:summary, past_process) }
+ it { should_not be_able_to(:summary, past_draft_process) }
+ it { should_not be_able_to(:summary, open_process) }
+
it { should be_able_to(:create, Budget) }
it { should be_able_to(:update, Budget) }
it { should be_able_to(:read_results, Budget) }
diff --git a/spec/models/abilities/everyone_spec.rb b/spec/models/abilities/everyone_spec.rb
index cfdbbf932..fdc03c74d 100644
--- a/spec/models/abilities/everyone_spec.rb
+++ b/spec/models/abilities/everyone_spec.rb
@@ -48,4 +48,8 @@ describe Abilities::Everyone do
it { should be_able_to(:read_stats, create(:budget, :valuating, stats_enabled: true)) }
it { should_not be_able_to(:read_stats, create(:budget, :valuating, stats_enabled: false)) }
it { should_not be_able_to(:read_stats, create(:budget, :selecting, stats_enabled: true)) }
+
+ it { should be_able_to(:summary, create(:legislation_process, :past)) }
+ it { should_not be_able_to(:summary, create(:legislation_process, :open)) }
+ it { should_not be_able_to(:summary, create(:legislation_process, :past, :not_published)) }
end
diff --git a/spec/system/legislation/summary_spec.rb b/spec/system/legislation/summary_spec.rb
new file mode 100644
index 000000000..0763c16ba
--- /dev/null
+++ b/spec/system/legislation/summary_spec.rb
@@ -0,0 +1,172 @@
+require "rails_helper"
+
+describe "Legislation" do
+ context "process summary page" do
+ scenario "summary tab is not shown for open processes" do
+ process = create(:legislation_process, :open)
+
+ visit legislation_process_path(process)
+
+ expect(page).not_to have_content "Summary"
+ end
+
+ scenario "summary tab is shown por past processes" do
+ process = create(:legislation_process, :past)
+
+ visit legislation_process_path(process)
+
+ expect(page).to have_content "Summary"
+ end
+ end
+
+ scenario "empty process" do
+ process = create(:legislation_process, :empty,
+ result_publication_enabled: true,
+ end_date: Date.current - 1.day
+ )
+
+ visit summary_legislation_process_path(process)
+
+ expect(page).to have_content "This process didn't have any participation phases"
+ end
+
+ scenario "empty phases" do
+ process = create(:legislation_process, end_date: Date.current - 1.day)
+ visit summary_legislation_process_path(process)
+
+ expect(page).to have_content "Debate phase"
+ expect(page).to have_content "No debates"
+ expect(page).to have_content "There aren't any questions"
+
+ expect(page).to have_content "Proposals phase"
+ expect(page).to have_content "No proposals"
+ expect(page).to have_content "There are no proposals"
+
+ expect(page).to have_content "Comments phase"
+ expect(page).to have_content "No comments"
+ expect(page).to have_content "There are no comments"
+ end
+
+ context "only debates exist" do
+ let(:process) { create(:legislation_process, end_date: Date.current - 1.day) }
+ let(:user) { create(:user, :level_two) }
+
+ before do
+ create(:legislation_question, process: process, title: "Question 1") do |question|
+ create(:comment, user: user, commentable: question, body: "Answer 1")
+ create(:comment, user: user, commentable: question, body: "Answer 2")
+ end
+
+ create(:legislation_question, process: process, title: "Question 2") do |question|
+ create(:comment, user: user, commentable: question, body: "Answer 3")
+ create(:comment, user: user, commentable: question, body: "Answer 4")
+ end
+ end
+
+ scenario "shows debates list" do
+ visit summary_legislation_process_path(process)
+
+ expect(page).to have_content "Debate phase"
+ expect(page).to have_content "2 debates"
+ expect(page).to have_link "Question 1"
+ expect(page).to have_content "Answer 1"
+ expect(page).to have_content "Answer 2"
+ expect(page).to have_link "Question 2"
+ expect(page).to have_content "Answer 3"
+ expect(page).to have_content "Answer 4"
+
+ expect(page).to have_content "Proposals phase"
+ expect(page).to have_content "No proposals"
+ expect(page).to have_content "There are no proposals"
+
+ expect(page).to have_content "Comments phase"
+ expect(page).to have_content "No comments"
+ expect(page).to have_content "There are no comments"
+ end
+ end
+
+ context "only proposals exist" do
+ let(:process) { create(:legislation_process, end_date: Date.current - 1.day) }
+
+ before do
+ create(:legislation_proposal, legislation_process_id: process.id,
+ title: "Legislation proposal 1", selected: true)
+ create(:legislation_proposal, legislation_process_id: process.id,
+ title: "Legislation proposal 2", selected: false)
+ create(:legislation_proposal, legislation_process_id: process.id,
+ title: "Legislation proposal 3", selected: true)
+ create(:legislation_proposal, legislation_process_id: process.id,
+ title: "Legislation proposal 4", selected: false)
+ end
+
+ scenario "shows proposals list" do
+ visit summary_legislation_process_path(process)
+
+ expect(page).to have_content "Debate phase"
+ expect(page).to have_content "No debates"
+ expect(page).to have_content "There aren't any questions"
+
+ expect(page).to have_content "Proposals phase"
+ expect(page).to have_content "2 proposals"
+ expect(page).to have_link "Legislation proposal 1"
+ expect(page).not_to have_content "Legislation proposal 2"
+ expect(page).to have_link "Legislation proposal 3"
+ expect(page).not_to have_content "Legislation proposal 4"
+
+ expect(page).to have_content "Comments phase"
+ expect(page).to have_content "No comments"
+ expect(page).to have_content "There are no comments"
+ end
+ end
+
+ context "only text comments exist" do
+ let(:process) { create(:legislation_process, end_date: Date.current - 1.day) }
+
+ before do
+ user = create(:user, :level_two)
+ draft_version_1 = create(:legislation_draft_version, process: process,
+ title: "Version 1", body: "Body of the first version",
+ status: "published")
+ draft_version_2 = create(:legislation_draft_version, process: process,
+ title: "Version 2", body: "Body of the second version and that's it all of it",
+ status: "published")
+ annotation0 = create(:legislation_annotation,
+ draft_version: draft_version_1, text: "my annotation123",
+ ranges: annotation_ranges(5, 10))
+ annotation1 = create(:legislation_annotation,
+ draft_version: draft_version_2, text: "hola",
+ ranges: annotation_ranges(5, 10))
+ annotation2 = create(:legislation_annotation,
+ draft_version: draft_version_2,
+ ranges: annotation_ranges(12, 19))
+
+ create(:comment, user: user, commentable: annotation0, body: "Comment 0")
+ create(:comment, user: user, commentable: annotation1, body: "Comment 1")
+ create(:comment, user: user, commentable: annotation2, body: "Comment 2")
+ create(:comment, user: user, commentable: annotation2, body: "Comment 3")
+ end
+
+ scenario "shows coments list" do
+ visit summary_legislation_process_path(process)
+
+ expect(page).to have_content "Debate phase"
+ expect(page).to have_content "No debates"
+ expect(page).to have_content "There aren't any questions"
+
+ expect(page).to have_content "Proposals phase"
+ expect(page).to have_content "No proposals"
+ expect(page).to have_content "There are no proposals"
+
+ expect(page).to have_content "Comments phase"
+ expect(page).to have_content "Top comments"
+ expect(page).not_to have_content "Comment 0"
+ expect(page).to have_link "Comment 1"
+ expect(page).to have_link "Comment 2"
+ expect(page).to have_link "Comment 3"
+ end
+ end
+
+ def annotation_ranges(start_offset, end_offset)
+ [{ "start" => "/p[1]", "startOffset" => start_offset, "end" => "/p[1]", "endOffset" => end_offset }]
+ end
+end
<%= t("legislation.summary.allegations_phase") %>
+<%= t("legislation.summary.top_comments", count: comments.count) %>
+<%= t("legislation.summary.no_allegation") %>
+