Merge pull request #898 from consul/proposals-summary
Proposals summary
This commit is contained in:
@@ -794,6 +794,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
.proposals-summary {
|
||||
|
||||
.panel {
|
||||
min-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 05. Featured
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@ class ProposalsController < ApplicationController
|
||||
before_action :parse_advanced_search_terms, only: :index
|
||||
before_action :parse_tag_filter, only: :index
|
||||
before_action :set_search_order, only: :index
|
||||
before_action :load_categories, only: [:index, :new, :edit, :map]
|
||||
before_action :load_geozones, only: [:edit, :map]
|
||||
before_action :authenticate_user!, except: [:index, :show, :map]
|
||||
before_action :load_categories, only: [:index, :new, :edit, :map, :summary]
|
||||
before_action :load_geozones, only: [:edit, :map, :summary]
|
||||
before_action :authenticate_user!, except: [:index, :show, :map, :summary]
|
||||
|
||||
has_orders %w{hot_score confidence_score created_at relevance}, only: :index
|
||||
has_orders %w{most_voted newest oldest}, only: :show
|
||||
@@ -35,6 +35,11 @@ class ProposalsController < ApplicationController
|
||||
set_featured_proposal_votes(@proposal)
|
||||
end
|
||||
|
||||
def summary
|
||||
@proposals = Proposal.for_summary
|
||||
@tag_cloud = tag_cloud
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def proposal_params
|
||||
|
||||
@@ -4,7 +4,7 @@ module Abilities
|
||||
|
||||
def initialize(user)
|
||||
can [:read, :map], Debate
|
||||
can [:read, :map], Proposal
|
||||
can [:read, :map, :summary], Proposal
|
||||
can :read, Comment
|
||||
can :read, SpendingProposal
|
||||
can :read, Legislation
|
||||
|
||||
@@ -41,7 +41,8 @@ class Proposal < ActiveRecord::Base
|
||||
scope :sort_by_random, -> { reorder("RANDOM()") }
|
||||
scope :sort_by_relevance , -> { all }
|
||||
scope :sort_by_flags, -> { order(flags_count: :desc, updated_at: :desc) }
|
||||
scope :last_week, -> { where("created_at >= ?", 7.days.ago)}
|
||||
scope :last_week, -> { where("proposals.created_at >= ?", 7.days.ago)}
|
||||
scope :in_categories, -> { where("lower(tags.name) IN (?)", ActsAsTaggableOn::Tag.category_names) }
|
||||
|
||||
def searchable_values
|
||||
{ title => 'A',
|
||||
@@ -60,13 +61,24 @@ class Proposal < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def self.search_by_code(terms)
|
||||
if code_match = /\A#{Setting["proposal_code_prefix"]}-\d\d\d\d-\d\d-(\d*)\z/.match(terms)
|
||||
results = where(id: code_match[1])
|
||||
end
|
||||
|
||||
matched_code = self.match_code(terms)
|
||||
results = where(id: matched_code[1]) if matched_code
|
||||
return results if (results.present? && results.first.code == terms)
|
||||
end
|
||||
|
||||
def self.match_code(terms)
|
||||
/\A#{Setting["proposal_code_prefix"]}-\d\d\d\d-\d\d-(\d*)\z/.match(terms)
|
||||
end
|
||||
|
||||
def self.for_summary
|
||||
last_week.
|
||||
sort_by_confidence_score.
|
||||
in_categories.
|
||||
joins(:tags).
|
||||
select('proposals.*, tags.name as tag_name').
|
||||
group_by(&:tag_name)
|
||||
end
|
||||
|
||||
def description
|
||||
super.try :html_safe
|
||||
end
|
||||
|
||||
@@ -16,7 +16,7 @@ class TagCloud
|
||||
end
|
||||
|
||||
def category_names
|
||||
ActsAsTaggableOn::Tag.where("kind = 'category'").map {|tag| tag.name.downcase }
|
||||
ActsAsTaggableOn::Tag.category_names
|
||||
end
|
||||
|
||||
def geozone_names
|
||||
|
||||
4
app/views/proposals/_popular.html.erb
Normal file
4
app/views/proposals/_popular.html.erb
Normal file
@@ -0,0 +1,4 @@
|
||||
<div class="sidebar-divider"></div>
|
||||
<h3 class="sidebar-title"><%= t("proposals.index.top") %></h3>
|
||||
<br>
|
||||
<%= link_to t("proposals.index.top_link"), "/proposals/summary", class: "small" %>
|
||||
@@ -56,6 +56,7 @@
|
||||
<%= render "shared/tag_cloud", taggable: 'proposal' %>
|
||||
<%= render 'categories' %>
|
||||
<%= render 'geozones' %>
|
||||
<%= render 'popular' %>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
|
||||
58
app/views/proposals/summary.html.erb
Normal file
58
app/views/proposals/summary.html.erb
Normal file
@@ -0,0 +1,58 @@
|
||||
<section role="main">
|
||||
<div class="wrap row proposals-summary">
|
||||
<div id="proposals" class="proposals-list small-12 medium-9 column margin-top">
|
||||
|
||||
<%= link_to :back, class: 'back left' do %>
|
||||
<i class="icon-angle-left left"></i>
|
||||
<%= t("proposals.show.back_link") %>
|
||||
<% end %>
|
||||
|
||||
<% @proposals.each do |group_name, proposals| %>
|
||||
<div id="<%= group_name.parameterize %>">
|
||||
<h2 class="margin-top"><%= group_name %></h2>
|
||||
|
||||
<% proposals[0..2].each do |proposal| %>
|
||||
<div class="proposal clear">
|
||||
<div class="panel">
|
||||
<div class="row">
|
||||
<div class="small-12 medium-9 column">
|
||||
<div class="proposal-contenta">
|
||||
<h3><%= link_to proposal.title, namespaced_proposal_path(proposal) %></h3>
|
||||
|
||||
<p class="proposal-info">
|
||||
<% if proposal.author.hidden? || proposal.author.erased? %>
|
||||
<span class="author"><%= t("proposals.show.author_deleted") %></span>
|
||||
<% else %>
|
||||
<span class="author"><%= proposal.author.name %></span>
|
||||
<% if proposal.author.official? %>
|
||||
<span class="label round level-<%= proposal.author.official_level %>">
|
||||
<%= proposal.author.official_position %>
|
||||
</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</p>
|
||||
|
||||
<div class="proposal-description">
|
||||
<%= link_to proposal.description, namespaced_proposal_path(proposal) %>
|
||||
<div class="truncate"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="small-12 medium-3 column">
|
||||
<aside class="sidebar" role="complementary">
|
||||
<%= link_to t("proposals.index.start_proposal"), new_proposal_path, class: 'button radius expand' %>
|
||||
<%= render "shared/tag_cloud", taggable: 'proposal' %>
|
||||
<%= render 'categories' %>
|
||||
<%= render 'geozones' %>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -34,6 +34,10 @@ module ActsAsTaggableOn
|
||||
update(custom_counter_field_name_for(taggable_type) => visible_taggables.count)
|
||||
end
|
||||
|
||||
def self.category_names
|
||||
Tag.where("kind = 'category'").map {|tag| tag.name.downcase }
|
||||
end
|
||||
|
||||
private
|
||||
def custom_counter_field_name_for(taggable_type)
|
||||
"#{taggable_type.underscore.pluralize}_count"
|
||||
|
||||
@@ -108,7 +108,7 @@ en:
|
||||
select_order: Order by
|
||||
start_debate: Start a debate
|
||||
title: Debates
|
||||
share_debate: "Share a link"
|
||||
share_debate: "Share a link"
|
||||
new:
|
||||
back_link: Go back
|
||||
form:
|
||||
@@ -142,7 +142,7 @@ en:
|
||||
debate_link: "Link"
|
||||
new:
|
||||
start_new: "Share a link"
|
||||
recommendations_title: "Recommendations for sharing a link"
|
||||
recommendations_title: "Recommendations for sharing a link"
|
||||
form:
|
||||
submit_button: "Share link"
|
||||
create:
|
||||
@@ -301,6 +301,8 @@ en:
|
||||
select_order_long: 'You are viewing proposals according to:'
|
||||
start_proposal: Create a proposal
|
||||
title: Proposals
|
||||
top: Weekly selection
|
||||
top_link: See the best proposals
|
||||
new:
|
||||
back_link: Go back
|
||||
form:
|
||||
|
||||
@@ -143,7 +143,7 @@ es:
|
||||
debate_link: "Enlace"
|
||||
new:
|
||||
start_new: "Compartir un enlace"
|
||||
recommendations_title: "Recomendaciones para compartir un enlace"
|
||||
recommendations_title: "Recomendaciones para compartir un enlace"
|
||||
form:
|
||||
submit_button: "Comparte un enlace"
|
||||
create:
|
||||
@@ -302,6 +302,8 @@ es:
|
||||
select_order_long: Estas viendo las propuestas
|
||||
start_proposal: Crea una propuesta
|
||||
title: Propuestas ciudadanas
|
||||
top: Selección semanal
|
||||
top_link: Ver las mejores propuestas
|
||||
new:
|
||||
back_link: Volver
|
||||
form:
|
||||
|
||||
@@ -43,9 +43,9 @@ Rails.application.routes.draw do
|
||||
get :suggest
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
get 'debate_links/new' => 'debate_links#new'
|
||||
post 'debate_links' => 'debate_links#create'
|
||||
post 'debate_links' => 'debate_links#create'
|
||||
|
||||
resources :proposals do
|
||||
member do
|
||||
@@ -57,6 +57,7 @@ Rails.application.routes.draw do
|
||||
collection do
|
||||
get :map
|
||||
get :suggest
|
||||
get :summary
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1162,4 +1162,70 @@ feature 'Proposals' do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "Summary" do
|
||||
|
||||
scenario "Displays proposals grouped by category" do
|
||||
create(:tag, kind: 'category', name: 'Culture')
|
||||
create(:tag, kind: 'category', name: 'Social Services')
|
||||
|
||||
3.times { create(:proposal, tag_list: 'Culture') }
|
||||
3.times { create(:proposal, tag_list: 'Social Services') }
|
||||
|
||||
create(:proposal, tag_list: 'Random')
|
||||
|
||||
visit proposals_path
|
||||
click_link "See the best proposals"
|
||||
|
||||
within("#culture") do
|
||||
expect(page).to have_content("Culture")
|
||||
expect(page).to have_css(".proposal", count: 3)
|
||||
end
|
||||
|
||||
within("#social-services") do
|
||||
expect(page).to have_content("Social Services")
|
||||
expect(page).to have_css(".proposal", count: 3)
|
||||
end
|
||||
end
|
||||
|
||||
scenario "Displays a maximum of 3 proposals per category" do
|
||||
create(:tag, kind: 'category', name: 'Culture')
|
||||
4.times { create(:proposal, tag_list: 'Culture') }
|
||||
|
||||
visit summary_proposals_path
|
||||
|
||||
expect(page).to have_css(".proposal", count: 3)
|
||||
end
|
||||
|
||||
scenario "Orders proposals by votes" do
|
||||
create(:tag, kind: 'category', name: 'Culture')
|
||||
create(:proposal, title: 'Best', tag_list: 'Culture').update_column(:confidence_score, 10)
|
||||
create(:proposal, title: 'Worst', tag_list: 'Culture').update_column(:confidence_score, 2)
|
||||
create(:proposal, title: 'Medium', tag_list: 'Culture').update_column(:confidence_score, 5)
|
||||
|
||||
visit summary_proposals_path
|
||||
|
||||
expect('Best').to appear_before('Medium')
|
||||
expect('Medium').to appear_before('Worst')
|
||||
end
|
||||
|
||||
scenario "Displays proposals from last week" do
|
||||
create(:tag, kind: 'category', name: 'Culture')
|
||||
proposal1 = create(:proposal, tag_list: 'Culture', created_at: 1.day.ago)
|
||||
proposal2 = create(:proposal, tag_list: 'Culture', created_at: 5.days.ago)
|
||||
proposal3 = create(:proposal, tag_list: 'Culture', created_at: 8.days.ago)
|
||||
|
||||
visit summary_proposals_path
|
||||
|
||||
within("#proposals") do
|
||||
expect(page).to have_css('.proposal', count: 2)
|
||||
|
||||
expect(page).to have_content(proposal1.title)
|
||||
expect(page).to have_content(proposal2.title)
|
||||
expect(page).to_not have_content(proposal3.title)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -633,12 +633,64 @@ describe Proposal do
|
||||
describe "#last_week" do
|
||||
it "should return proposals created this week" do
|
||||
proposal = create(:proposal)
|
||||
expect(Proposal.last_week.all).to include (proposal)
|
||||
expect(Proposal.last_week).to include(proposal)
|
||||
end
|
||||
|
||||
it "should not show proposals created more than a week ago" do
|
||||
it "should not return proposals created more than a week ago" do
|
||||
proposal = create(:proposal, created_at: 8.days.ago)
|
||||
expect(Proposal.last_week.all).to_not include (proposal)
|
||||
expect(Proposal.last_week).to_not include(proposal)
|
||||
end
|
||||
end
|
||||
|
||||
describe "for_summary" do
|
||||
it "should return proposals tagged with a category" do
|
||||
create(:tag, kind: 'category', name: 'Culture')
|
||||
proposal = create(:proposal, tag_list: 'Culture')
|
||||
|
||||
expect(Proposal.for_summary.values.flatten).to include(proposal)
|
||||
end
|
||||
|
||||
it "should not return proposals tagged without a category" do
|
||||
create(:tag, kind: 'category', name: 'Culture')
|
||||
proposal = create(:proposal, tag_list: 'Parks')
|
||||
|
||||
expect(Proposal.for_summary.values.flatten).to_not include(proposal)
|
||||
end
|
||||
|
||||
it "should return proposals created this week" do
|
||||
create(:tag, kind: 'category', name: 'Culture')
|
||||
proposal = create(:proposal, tag_list: 'Culture')
|
||||
expect(Proposal.for_summary.values.flatten).to include(proposal)
|
||||
end
|
||||
|
||||
it "should not return proposals created more than a week ago" do
|
||||
create(:tag, kind: 'category', name: 'Culture')
|
||||
proposal = create(:proposal, tag_list: 'Culture', created_at: 8.days.ago)
|
||||
expect(Proposal.for_summary.values.flatten).to_not include(proposal)
|
||||
end
|
||||
|
||||
it "should order by votes" do
|
||||
create(:tag, kind: 'category', name: 'Culture')
|
||||
create(:proposal, tag_list: 'Culture').update_column(:confidence_score, 2)
|
||||
create(:proposal, tag_list: 'Culture').update_column(:confidence_score, 10)
|
||||
create(:proposal, tag_list: 'Culture').update_column(:confidence_score, 5)
|
||||
|
||||
results = Proposal.for_summary.values.flatten
|
||||
|
||||
expect(results.first.confidence_score).to be(10)
|
||||
expect(results.second.confidence_score).to be(5)
|
||||
expect(results.third.confidence_score).to be(2)
|
||||
end
|
||||
|
||||
it "should return proposals grouped by tag" do
|
||||
create(:tag, kind: 'category', name: 'Culture')
|
||||
create(:tag, kind: 'category', name: 'Health')
|
||||
|
||||
proposal1 = create(:proposal, tag_list: 'Culture')
|
||||
proposal2 = create(:proposal, tag_list: 'Culture')
|
||||
proposal3 = create(:proposal, tag_list: 'Health')
|
||||
|
||||
expect(Proposal.for_summary).to include('Culture' => [proposal2, proposal1], 'Health' => [proposal3])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user