Merge pull request #898 from consul/proposals-summary

Proposals summary
This commit is contained in:
Enrique García
2016-02-16 16:55:24 +01:00
14 changed files with 232 additions and 18 deletions

View File

@@ -794,6 +794,13 @@
}
}
.proposals-summary {
.panel {
min-height: 0;
}
}
// 05. Featured
// - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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" %>

View File

@@ -56,6 +56,7 @@
<%= render "shared/tag_cloud", taggable: 'proposal' %>
<%= render 'categories' %>
<%= render 'geozones' %>
<%= render 'popular' %>
</aside>
</div>

View 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>

View File

@@ -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"

View File

@@ -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:

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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