Merge pull request #4065 from consul/legislation_summary
Add collaborative legislation summary
This commit is contained in:
2
Gemfile
2
Gemfile
@@ -10,6 +10,8 @@ gem "ancestry", "~> 3.0.7"
|
|||||||
gem "audited", "~> 4.9.0"
|
gem "audited", "~> 4.9.0"
|
||||||
gem "autoprefixer-rails", "~> 8.2.0"
|
gem "autoprefixer-rails", "~> 8.2.0"
|
||||||
gem "cancancan", "~> 2.3.0"
|
gem "cancancan", "~> 2.3.0"
|
||||||
|
gem "caxlsx", "~> 3.0.2"
|
||||||
|
gem "caxlsx_rails", "~> 0.6.2"
|
||||||
gem "ckeditor", "~> 4.3.0"
|
gem "ckeditor", "~> 4.3.0"
|
||||||
gem "cocoon", "~> 1.2.14"
|
gem "cocoon", "~> 1.2.14"
|
||||||
gem "daemons", "~> 1.3.1"
|
gem "daemons", "~> 1.3.1"
|
||||||
|
|||||||
10
Gemfile.lock
10
Gemfile.lock
@@ -131,6 +131,14 @@ GEM
|
|||||||
rack (>= 1.4)
|
rack (>= 1.4)
|
||||||
rack-proxy (>= 0.6.0)
|
rack-proxy (>= 0.6.0)
|
||||||
selenium-webdriver (~> 3.0)
|
selenium-webdriver (~> 3.0)
|
||||||
|
caxlsx (3.0.2)
|
||||||
|
htmlentities (~> 4.3, >= 4.3.4)
|
||||||
|
mimemagic (~> 0.3)
|
||||||
|
nokogiri (~> 1.10, >= 1.10.4)
|
||||||
|
rubyzip (>= 1.3.0, < 3)
|
||||||
|
caxlsx_rails (0.6.2)
|
||||||
|
actionpack (>= 3.1)
|
||||||
|
caxlsx (>= 3.0)
|
||||||
chef-utils (16.4.41)
|
chef-utils (16.4.41)
|
||||||
childprocess (0.9.0)
|
childprocess (0.9.0)
|
||||||
ffi (~> 1.0, >= 1.0.11)
|
ffi (~> 1.0, >= 1.0.11)
|
||||||
@@ -638,6 +646,8 @@ DEPENDENCIES
|
|||||||
capistrano3-puma (~> 4.0.0)
|
capistrano3-puma (~> 4.0.0)
|
||||||
capybara (~> 3.29.0)
|
capybara (~> 3.29.0)
|
||||||
capybara-webmock (~> 0.5.5)
|
capybara-webmock (~> 0.5.5)
|
||||||
|
caxlsx (~> 3.0.2)
|
||||||
|
caxlsx_rails (~> 0.6.2)
|
||||||
ckeditor (~> 4.3.0)
|
ckeditor (~> 4.3.0)
|
||||||
cocoon (~> 1.2.14)
|
cocoon (~> 1.2.14)
|
||||||
coveralls (~> 0.8.22)
|
coveralls (~> 0.8.22)
|
||||||
|
|||||||
@@ -2045,10 +2045,6 @@ table {
|
|||||||
padding: $line-height / 4;
|
padding: $line-height / 4;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.relative {
|
|
||||||
padding-left: rem-calc(18);
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider {
|
.divider {
|
||||||
color: $text-light;
|
color: $text-light;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -2060,24 +2056,23 @@ table {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.responses-count {
|
.responses-count {
|
||||||
.far {
|
|
||||||
@extend .fa-minus-square;
|
|
||||||
font-size: $small-font-size;
|
|
||||||
left: 0;
|
|
||||||
position: absolute;
|
|
||||||
text-decoration: none;
|
|
||||||
top: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.show-children {
|
.show-children {
|
||||||
|
@include has-fa-icon(plus-square, r);
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.collapsed {
|
.collapse-children {
|
||||||
.far {
|
@include has-fa-icon(minus-square, r);
|
||||||
@extend .fa-plus-square;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.show-children::before,
|
||||||
|
.collapse-children::before {
|
||||||
|
margin-right: rem-calc(6);
|
||||||
|
transform: translateY(1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.collapsed {
|
||||||
|
|
||||||
.collapse-children {
|
.collapse-children {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1006,3 +1006,91 @@
|
|||||||
font-weight: bold;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-button {
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-left: 50%;
|
||||||
|
margin-top: $line-height;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -148,3 +148,18 @@
|
|||||||
transition: none;
|
transition: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mixin has-fa-icon($icon, $style) {
|
||||||
|
@extend .fa-#{$icon};
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
@extend %fa-icon;
|
||||||
|
font-family: "Font Awesome 5 Free";
|
||||||
|
|
||||||
|
@if $style == "r" {
|
||||||
|
font-weight: normal;
|
||||||
|
} @else {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -612,15 +612,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.debate,
|
%panel {
|
||||||
.proposal,
|
|
||||||
.investment-project,
|
|
||||||
.budget-investment,
|
|
||||||
.legislation,
|
|
||||||
.communities-show {
|
|
||||||
margin: $line-height / 4 0;
|
|
||||||
|
|
||||||
.panel {
|
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border: 1px solid;
|
border: 1px solid;
|
||||||
border-color: #e5e6e9 #dfe0e4 #d0d1d5;
|
border-color: #e5e6e9 #dfe0e4 #d0d1d5;
|
||||||
@@ -657,6 +649,18 @@
|
|||||||
margin-left: $line-height / 2;
|
margin-left: $line-height / 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.debate,
|
||||||
|
.proposal,
|
||||||
|
.investment-project,
|
||||||
|
.budget-investment,
|
||||||
|
.legislation,
|
||||||
|
.communities-show {
|
||||||
|
margin: $line-height / 4 0;
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
@extend %panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
.debate-content,
|
.debate-content,
|
||||||
|
|||||||
@@ -97,6 +97,17 @@ class Legislation::ProcessesController < Legislation::BaseController
|
|||||||
@phase = :milestones
|
@phase = :milestones
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def summary
|
||||||
|
@phase = :summary
|
||||||
|
@proposals = @process.proposals.selected
|
||||||
|
@comments = @process.draft_versions.published.last&.best_comments || Comment.none
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html
|
||||||
|
format.xlsx { render xlsx: "summary", filename: "summary-#{Date.current}.xlsx" }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def proposals
|
def proposals
|
||||||
set_process
|
set_process
|
||||||
@phase = :proposals_phase
|
@phase = :proposals_phase
|
||||||
|
|||||||
@@ -90,7 +90,9 @@ module Abilities
|
|||||||
can :access, :ckeditor
|
can :access, :ckeditor
|
||||||
can :manage, Ckeditor::Picture
|
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::DraftVersion
|
||||||
can [:manage], ::Legislation::Question
|
can [:manage], ::Legislation::Question
|
||||||
can [:manage], ::Legislation::Proposal
|
can [:manage], ::Legislation::Proposal
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ module Abilities
|
|||||||
can :new, DirectMessage
|
can :new, DirectMessage
|
||||||
can [:read, :debate, :draft_publication, :allegations, :result_publication,
|
can [:read, :debate, :draft_publication, :allegations, :result_publication,
|
||||||
:proposals, :milestones], Legislation::Process, published: true
|
: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, :changes, :go_to_version], Legislation::DraftVersion
|
||||||
can [:read], Legislation::Question
|
can [:read], Legislation::Question
|
||||||
can [:read, :map, :share], Legislation::Proposal
|
can [:read, :map, :share], Legislation::Proposal
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ class Comment < ApplicationRecord
|
|||||||
|
|
||||||
scope :sort_by_most_voted, -> { order(confidence_score: :desc, created_at: :desc) }
|
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_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_by_newest, -> { order(created_at: :desc) }
|
||||||
scope :sort_descendants_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)
|
cached_votes_up)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def votes_score
|
||||||
|
cached_votes_up - cached_votes_down
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def validate_body_length
|
def validate_body_length
|
||||||
|
|||||||
@@ -40,4 +40,8 @@ class Legislation::DraftVersion < ApplicationRecord
|
|||||||
def total_comments
|
def total_comments
|
||||||
annotations.sum(:comments_count)
|
annotations.sum(:comments_count)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def best_comments
|
||||||
|
Comment.where(commentable: annotations, ancestry: nil).sort_by_supports.limit(10)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -44,4 +44,8 @@ class Legislation::Question < ApplicationRecord
|
|||||||
def comments_open?
|
def comments_open?
|
||||||
process.debate_phase.open?
|
process.debate_phase.open?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def best_comments
|
||||||
|
comments.sort_by_supports.limit(3)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<% if count > 0 %>
|
<% if count > 0 %>
|
||||||
<%= link_to "", class: "js-toggle-children relative" do %>
|
<%= link_to "", class: "js-toggle-children" do %>
|
||||||
<span class="far"></span>
|
|
||||||
<span class="show-children"><%= t("comments.comment.responses_show", count: count) %></span>
|
<span class="show-children"><%= t("comments.comment.responses_show", count: count) %></span>
|
||||||
<span class="collapse-children"><%= t("comments.comment.responses_collapse", count: count) %></span>
|
<span class="collapse-children"><%= t("comments.comment.responses_collapse", count: count) %></span>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -52,6 +52,15 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
</li>
|
</li>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
<% if can?(:summary, process) %>
|
||||||
|
<li <%= "class=is-active" if phase == :summary %>>
|
||||||
|
<%= link_to summary_legislation_process_path(process) do %>
|
||||||
|
<h4><%= t("legislation.summary.title") %></h4>
|
||||||
|
<span><%= format_date(process.result_publication_date) %></span>
|
||||||
|
<% end %>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<section class="comments-phase">
|
||||||
|
<header>
|
||||||
|
<h3><%= t("legislation.summary.allegations_phase") %></h3>
|
||||||
|
<h3><%= t("legislation.summary.top_comments", count: comments.count) %></h3>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<% if comments.any? %>
|
||||||
|
<div>
|
||||||
|
<% comments.group_by(&:commentable).each do |annotation, annotation_comments| %>
|
||||||
|
<div class="annotation-summary">
|
||||||
|
<span><%= t("legislation.annotations.index.comments_about") %></span>
|
||||||
|
<div class="annotation-title">
|
||||||
|
<div class="annotation-quote">
|
||||||
|
<%= annotation.quote %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span class="comments-count">
|
||||||
|
<%= link_to t("legislation.summary.comments", count: annotation.comments.count),
|
||||||
|
polymorphic_path(annotation, anchor: "comments") %>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%= render "summary_comments", comments: annotation_comments %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% else %>
|
||||||
|
<div class="callout primary">
|
||||||
|
<p><%= t("legislation.summary.no_allegation") %></p>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</section>
|
||||||
10
app/views/legislation/processes/_summary_comments.html.erb
Normal file
10
app/views/legislation/processes/_summary_comments.html.erb
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<% if comments.any? %>
|
||||||
|
<span><%= t("legislation.summary.most_voted_comments") %></span>
|
||||||
|
|
||||||
|
<% comments.each do |comment| %>
|
||||||
|
<div class="comment-summary">
|
||||||
|
<%= link_to simple_format(sanitize(comment.body, tags: [])), comment_path(comment) %>
|
||||||
|
<p><%= t("legislation.summary.votes", count: comment.votes_score) %></p>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
28
app/views/legislation/processes/_summary_debate.html.erb
Normal file
28
app/views/legislation/processes/_summary_debate.html.erb
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<section class="debate-phase">
|
||||||
|
<header>
|
||||||
|
<h3><%= t("legislation.summary.debate_phase") %></h3>
|
||||||
|
<h3><%= t("legislation.summary.debates", count: questions.count) %></h3>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<% if questions.any? %>
|
||||||
|
<div>
|
||||||
|
<% questions.each do |question| %>
|
||||||
|
<div class="debate-summary">
|
||||||
|
<div class="question-title">
|
||||||
|
<h4><%= link_to question.title, polymorphic_path(question) %></h4>
|
||||||
|
<span class="comments-count">
|
||||||
|
<%= link_to t("legislation.summary.comments", count: question.comments.count),
|
||||||
|
polymorphic_path(question, anchor: "comments") %>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%= render "summary_comments", comments: question.best_comments %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% else %>
|
||||||
|
<div class="callout primary">
|
||||||
|
<p><%= t("legislation.processes.debate.empty_questions") %></p>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</section>
|
||||||
23
app/views/legislation/processes/_summary_proposals.html.erb
Normal file
23
app/views/legislation/processes/_summary_proposals.html.erb
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<section class="proposals-phase">
|
||||||
|
<header>
|
||||||
|
<h3><%= t("legislation.summary.proposals_phase") %></h3>
|
||||||
|
<h3><%= t("legislation.summary.proposals", count: proposals.count) %></h3>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<% if proposals.any? %>
|
||||||
|
<div>
|
||||||
|
<% proposals.sort_by_supports.each do |proposal| %>
|
||||||
|
<div class="proposal-summary">
|
||||||
|
<h4>
|
||||||
|
<%= link_to proposal.title, polymorphic_path(proposal) %>
|
||||||
|
</h4>
|
||||||
|
<p><%= t("legislation.summary.votes", count: proposal.votes_score) %></p>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% else %>
|
||||||
|
<div class="callout primary">
|
||||||
|
<p><%= t("legislation.processes.proposals.empty_proposals") %></p>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</section>
|
||||||
29
app/views/legislation/processes/summary.html.erb
Normal file
29
app/views/legislation/processes/summary.html.erb
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<% 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? %>
|
||||||
|
<section class="process-summary">
|
||||||
|
<%= link_to t("legislation.summary.download"),
|
||||||
|
summary_legislation_process_path(@process, format: :xlsx),
|
||||||
|
class: "button hollow download-button" %>
|
||||||
|
|
||||||
|
<% 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 %>
|
||||||
|
</section>
|
||||||
|
<% else %>
|
||||||
|
<div class="callout primary">
|
||||||
|
<p><%= t("legislation.summary.process_empty") %></p>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
50
app/views/legislation/processes/summary.xlsx.axlsx
Normal file
50
app/views/legislation/processes/summary.xlsx.axlsx
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
xlsx_package.workbook.add_worksheet(name: "Summary") do |sheet|
|
||||||
|
|
||||||
|
styles = xlsx_package.workbook.styles
|
||||||
|
title = styles.add_style(b:true)
|
||||||
|
link = styles.add_style(fg_color: "0000FF", u: true)
|
||||||
|
|
||||||
|
if @process.debate_phase.enabled? && @process.questions.any?
|
||||||
|
sheet.add_row [t("legislation.summary.debate_phase"), t("legislation.summary.debates", count: @process.questions.count)], style: title
|
||||||
|
@process.questions.each do |question|
|
||||||
|
sheet.add_row [question.title, t("legislation.summary.comments", count: question.comments.count)], style: link
|
||||||
|
sheet.add_hyperlink location: legislation_process_question_url(question.process, question), ref: sheet.rows.last.cells.first
|
||||||
|
sheet.add_hyperlink location: polymorphic_url(question, anchor: "comments"), ref: sheet.rows.last.cells.last
|
||||||
|
sheet.add_row [t("legislation.summary.most_voted_comments")] if question.best_comments.any?
|
||||||
|
question.best_comments.each do |comment|
|
||||||
|
sheet.add_row [comment.body, t("legislation.summary.votes", count: comment.votes_score)]
|
||||||
|
sheet.add_hyperlink location: comment_url(comment), ref: sheet.rows.last.cells.first
|
||||||
|
sheet.rows.last.cells.first.style = link
|
||||||
|
end
|
||||||
|
sheet.add_row ["", ""]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if @process.proposals_phase.enabled? && @proposals.any?
|
||||||
|
sheet.add_row [t("legislation.summary.proposals_phase"), t("legislation.summary.proposals", count: @proposals.count)], style: title
|
||||||
|
@proposals.sort_by_supports.each do |proposal|
|
||||||
|
sheet.add_row [proposal.title, t("legislation.summary.votes", count: proposal.votes_score)]
|
||||||
|
sheet.add_hyperlink location: legislation_process_proposal_url(proposal.legislation_process_id, proposal), ref: sheet.rows.last.cells.first
|
||||||
|
sheet.rows.last.cells.first.style = link
|
||||||
|
end
|
||||||
|
sheet.add_row ["", ""]
|
||||||
|
end
|
||||||
|
|
||||||
|
if @process.allegations_phase.enabled? && @comments.any?
|
||||||
|
sheet.add_row [t("legislation.summary.allegations_phase"),
|
||||||
|
t("legislation.summary.top_comments", count: @comments.count)], style: title
|
||||||
|
@comments.group_by(&:commentable).each do |annotation, annotation_comments|
|
||||||
|
sheet.add_row [t("legislation.annotations.index.comments_about")]
|
||||||
|
sheet.add_row [annotation.quote, t("legislation.summary.comments", count: annotation.comments.count)]
|
||||||
|
sheet.add_hyperlink location: polymorphic_url(annotation, anchor: "comments"), ref: sheet.rows.last.cells.last
|
||||||
|
sheet.rows.last.cells.last.style = link
|
||||||
|
|
||||||
|
annotation_comments.each do |comment|
|
||||||
|
sheet.add_row [comment.body, t("legislation.summary.votes", count: comment.votes_score)]
|
||||||
|
sheet.add_hyperlink location: comment_url(comment), ref: sheet.rows.last.cells.first
|
||||||
|
sheet.rows.last.cells.first.style = link
|
||||||
|
end
|
||||||
|
sheet.add_row ["", ""]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -118,3 +118,32 @@ en:
|
|||||||
tags_label: "Categories"
|
tags_label: "Categories"
|
||||||
not_verified: "For vote proposals %{verify_account}."
|
not_verified: "For vote proposals %{verify_account}."
|
||||||
process_title: Collaborative legislation process
|
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"
|
||||||
|
download: "Download summary"
|
||||||
|
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"
|
||||||
|
|||||||
@@ -118,3 +118,32 @@ es:
|
|||||||
tags_label: "Categorías"
|
tags_label: "Categorías"
|
||||||
not_verified: "Para votar propuestas %{verify_account}."
|
not_verified: "Para votar propuestas %{verify_account}."
|
||||||
process_title: Proceso de legislación colaborativa
|
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"
|
||||||
|
download: "Descargar resumen"
|
||||||
|
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"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ namespace :legislation do
|
|||||||
get :result_publication
|
get :result_publication
|
||||||
get :proposals
|
get :proposals
|
||||||
get :milestones
|
get :milestones
|
||||||
|
get :summary
|
||||||
end
|
end
|
||||||
|
|
||||||
resources :questions, only: [:show] do
|
resources :questions, only: [:show] do
|
||||||
|
|||||||
13
spec/controllers/legislation/processes_controller_spec.rb
Normal file
13
spec/controllers/legislation/processes_controller_spec.rb
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
require "rails_helper"
|
||||||
|
|
||||||
|
describe Legislation::ProcessesController do
|
||||||
|
let(:legislation_process) { create(:legislation_process, end_date: Date.current - 1.day) }
|
||||||
|
|
||||||
|
it "download excel file test" do
|
||||||
|
create(:legislation_question, process: legislation_process, title: "Question 1")
|
||||||
|
|
||||||
|
get :summary, params: { id: legislation_process, format: :xlsx }
|
||||||
|
|
||||||
|
expect(response).to be_success
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -18,6 +18,10 @@ describe Abilities::Administrator do
|
|||||||
let(:legislation_question) { create(:legislation_question) }
|
let(:legislation_question) { create(:legislation_question) }
|
||||||
let(:poll_question) { create(:poll_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(:proposal_document) { build(:document, documentable: proposal, user: proposal.author) }
|
||||||
let(:budget_investment_document) { build(:document, documentable: budget_investment) }
|
let(:budget_investment_document) { build(:document, documentable: budget_investment) }
|
||||||
let(:poll_question_document) { build(:document, documentable: poll_question) }
|
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 be_able_to(:comment_as_administrator, legislation_question) }
|
||||||
it { should_not be_able_to(:comment_as_moderator, 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(:create, Budget) }
|
||||||
it { should be_able_to(:update, Budget) }
|
it { should be_able_to(:update, Budget) }
|
||||||
it { should be_able_to(:read_results, Budget) }
|
it { should be_able_to(:read_results, Budget) }
|
||||||
|
|||||||
@@ -48,4 +48,8 @@ describe Abilities::Everyone do
|
|||||||
it { should be_able_to(:read_stats, create(:budget, :valuating, stats_enabled: true)) }
|
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, :valuating, stats_enabled: false)) }
|
||||||
it { should_not be_able_to(:read_stats, create(:budget, :selecting, stats_enabled: true)) }
|
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
|
end
|
||||||
|
|||||||
179
spec/system/legislation/summary_spec.rb
Normal file
179
spec/system/legislation/summary_spec.rb
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
scenario "excel download" do
|
||||||
|
visit summary_legislation_process_path(process)
|
||||||
|
click_link "Download summary"
|
||||||
|
|
||||||
|
expect(page.response_headers["Content-Type"]).to match(/officedocument.spreadsheetml/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def annotation_ranges(start_offset, end_offset)
|
||||||
|
[{ "start" => "/p[1]", "startOffset" => start_offset, "end" => "/p[1]", "endOffset" => end_offset }]
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user