<%= link_to t("debates.debate.comments", count: debate.comments_count), debate_path(debate, anchor: "comments") %>
+ •
+ <%= l debate.created_at.to_date %>
<%= link_to debate.description, debate %>
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 7a4be1323..7000a0150 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -69,6 +69,7 @@ en:
select_order: Order by
select_order_long: Order debates by
orders:
+ hot_score: most active
created_at: newest
score: best rated
most_commented: most commented
diff --git a/config/locales/es.yml b/config/locales/es.yml
index 10351fe3a..a556757fa 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -69,6 +69,7 @@ es:
select_order: Ordenar por
select_order_long: Estás viendo los debates
orders:
+ hot_score: "más activos"
created_at: "más nuevos"
score: "mejor valorados"
most_commented: "más comentados"
diff --git a/db/dev_seeds.rb b/db/dev_seeds.rb
index 58a1da128..0fd7bbee1 100644
--- a/db/dev_seeds.rb
+++ b/db/dev_seeds.rb
@@ -59,6 +59,7 @@ tags = Faker::Lorem.words(25)
description = "
#{Faker::Lorem.paragraphs.join('
')}"
debate = Debate.create!(author: author,
title: Faker::Lorem.sentence(3),
+ created_at: rand((Time.now - 1.week) .. Time.now),
description: description,
tag_list: tags.sample(3).join(','),
terms_of_service: "1")
@@ -72,6 +73,7 @@ puts "Commenting Debates"
author = User.reorder("RANDOM()").first
debate = Debate.reorder("RANDOM()").first
Comment.create!(user: author,
+ created_at: rand(debate.created_at .. Time.now),
commentable: debate,
body: Faker::Lorem.sentence)
end
@@ -82,7 +84,8 @@ puts "Commenting Comments"
(1..100).each do |i|
author = User.reorder("RANDOM()").first
parent = Comment.reorder("RANDOM()").first
- comment = Comment.create!(user: author,
+ Comment.create!(user: author,
+ created_at: rand(parent.created_at .. Time.now),
commentable_id: parent.commentable_id,
commentable_type: parent.commentable_type,
body: Faker::Lorem.sentence,
diff --git a/db/migrate/20150903171309_add_hot_score_to_debates.rb b/db/migrate/20150903171309_add_hot_score_to_debates.rb
new file mode 100644
index 000000000..7e82d4c13
--- /dev/null
+++ b/db/migrate/20150903171309_add_hot_score_to_debates.rb
@@ -0,0 +1,5 @@
+class AddHotScoreToDebates < ActiveRecord::Migration
+ def change
+ add_column :debates, :hot_score, :bigint, default: 0
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 688f20838..13705eb14 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -101,6 +101,7 @@ ActiveRecord::Schema.define(version: 20150903200440) do
t.datetime "confirmed_hide_at"
t.integer "cached_anonymous_votes_total", default: 0
t.integer "cached_votes_score", default: 0
+ t.integer "hot_score", limit: 8, default: 0
end
add_index "debates", ["cached_votes_down"], name: "index_debates_on_cached_votes_down", using: :btree
diff --git a/lib/tasks/debates.rake b/lib/tasks/debates.rake
new file mode 100644
index 000000000..ce37e2743
--- /dev/null
+++ b/lib/tasks/debates.rake
@@ -0,0 +1,9 @@
+namespace :debates do
+ desc "Updates all debates by recalculating their hot_score"
+ task hot_score: :environment do
+ Debate.find_in_batches do |debates|
+ debates.each(&:save)
+ end
+ end
+
+end
diff --git a/spec/factories.rb b/spec/factories.rb
index 9f4345d4d..e541c1b20 100644
--- a/spec/factories.rb
+++ b/spec/factories.rb
@@ -71,6 +71,10 @@ FactoryGirl.define do
Flag.flag(FactoryGirl.create(:user), debate)
end
end
+
+ trait :with_hot_score do
+ before(:save) { |d| d.calculate_hot_score }
+ end
end
factory :vote do
diff --git a/spec/features/admin/organizations_spec.rb b/spec/features/admin/organizations_spec.rb
index f43c5d4dc..289b5cd1c 100644
--- a/spec/features/admin/organizations_spec.rb
+++ b/spec/features/admin/organizations_spec.rb
@@ -13,7 +13,7 @@ feature 'Admin::Organizations' do
background do
@user = create(:user, email: "marley@humanrights.com", phone_number: "6764440002")
- organization = create(:organization, user: @user, name: "Get up, Stand up")
+ create(:organization, user: @user, name: "Get up, Stand up")
end
scenario "returns no results if search term is empty" do
@@ -70,11 +70,13 @@ feature 'Admin::Organizations' do
organization = create(:organization)
visit admin_organizations_path
- expect(current_path).to eq(admin_organizations_path)
- expect(page).to have_link('Verify')
- expect(page).to have_link('Reject')
+ within("#organization_#{organization.id}") do
+ expect(current_path).to eq(admin_organizations_path)
+ expect(page).to have_link('Verify')
+ expect(page).to have_link('Reject')
- click_on 'Verify'
+ click_on 'Verify'
+ end
expect(current_path).to eq(admin_organizations_path)
expect(page).to have_content ('Verified')
@@ -86,11 +88,13 @@ feature 'Admin::Organizations' do
visit admin_organizations_path
expect(current_path).to eq(admin_organizations_path)
- expect(page).to have_content ('Verified')
- expect(page).to_not have_link('Verify')
- expect(page).to have_link('Reject')
+ within("#organization_#{organization.id}") do
+ expect(page).to have_content ('Verified')
+ expect(page).to_not have_link('Verify')
+ expect(page).to have_link('Reject')
- click_on 'Reject'
+ click_on 'Reject'
+ end
expect(current_path).to eq(admin_organizations_path)
expect(page).to have_content ('Rejected')
@@ -102,10 +106,12 @@ feature 'Admin::Organizations' do
visit admin_organizations_path
expect(current_path).to eq(admin_organizations_path)
- expect(page).to have_link('Verify')
- expect(page).to_not have_link('Reject', exact: true)
+ within("#organization_#{organization.id}") do
+ expect(page).to have_link('Verify')
+ expect(page).to_not have_link('Reject', exact: true)
- click_on 'Verify'
+ click_on 'Verify'
+ end
expect(current_path).to eq(admin_organizations_path)
expect(page).to have_content ('Verified')
diff --git a/spec/features/debates_spec.rb b/spec/features/debates_spec.rb
index 3e2a0ea08..5c808a5ef 100644
--- a/spec/features/debates_spec.rb
+++ b/spec/features/debates_spec.rb
@@ -364,80 +364,87 @@ feature 'Debates' do
expect(Flag.flagged?(user, debate)).to_not be
end
- feature 'Debate index order filters', :js do
+ feature 'Debate index order filters' do
- before do
- @most_commented_debate = create(:debate)
- @most_score_debate = create(:debate)
- @most_recent_debate = create(:debate)
- create_list(:comment, 2, commentable: @most_commented_debate)
- create_list(:vote, 2, votable: @most_score_debate)
- create_list(:vote, 2, votable: @most_recent_debate, vote_flag: false)
- create(:vote, votable: @most_recent_debate)
- create(:comment, commentable: @most_recent_debate)
- end
+ scenario 'Default order is hot_score', :js do
+ create(:debate, title: 'best').update_column(:hot_score, 10)
+ create(:debate, title: 'worst').update_column(:hot_score, 2)
+ create(:debate, title: 'medium').update_column(:hot_score, 5)
- scenario 'Default order is created_at' do
visit debates_path
- expect(page).to have_select('order-selector', selected: 'newest')
- expect(@most_recent_debate.title).to appear_before(@most_score_debate.title)
- expect(@most_score_debate.title).to appear_before(@most_commented_debate.title)
+ expect(page).to have_select('order-selector', selected: 'most active')
+ expect('best').to appear_before('medium')
+ expect('medium').to appear_before('worst')
end
- scenario 'Debates are ordered by best rated' do
- visit debates_path
+ scenario 'Debates are ordered by best rated', :js do
+ create(:debate, title: 'best', cached_votes_score: 10)
+ create(:debate, title: 'medium', cached_votes_score: 5)
+ create(:debate, title: 'worst', cached_votes_score: 2)
+ visit debates_path
select 'best rated', from: 'order-selector'
expect(page).to have_select('order-selector', selected: 'best rated')
- expect(find("#debates .debate", match: :first)).to have_content(@most_score_debate.title)
+
+ within '#debates' do
+ expect('best').to appear_before('medium')
+ expect('medium').to appear_before('worst')
+ end
expect(current_url).to include('order=score')
- expect(@most_score_debate.title).to appear_before(@most_commented_debate.title)
- expect(@most_commented_debate.title).to appear_before(@most_recent_debate.title)
end
- scenario 'Debates are ordered by most commented' do
- visit debates_path
+ scenario 'Debates are ordered by most commented', :js do
+ create(:debate, title: 'best', comments_count: 10)
+ create(:debate, title: 'medium', comments_count: 5)
+ create(:debate, title: 'worst', comments_count: 2)
+ visit debates_path
select 'most commented', from: 'order-selector'
expect(page).to have_select('order-selector', selected: 'most commented')
- expect(find("#debates .debate", match: :first)).to have_content(@most_commented_debate.title)
+
+ within '#debates' do
+ expect('best').to appear_before('medium')
+ expect('medium').to appear_before('worst')
+ end
expect(current_url).to include('order=most_commented')
- expect(@most_commented_debate.title).to appear_before(@most_recent_debate.title)
- expect(@most_recent_debate.title).to appear_before(@most_score_debate.title)
end
- scenario 'Debates are ordered by newest' do
+ scenario 'Debates are ordered by newest', :js do
+ create(:debate, title: 'best', created_at: Time.now)
+ create(:debate, title: 'medium', created_at: Time.now - 1.hour)
+ create(:debate, title: 'worst', created_at: Time.now - 1.day)
+
visit debates_path
-
- select 'best rated', from: 'order-selector'
- expect(find("#debates .debate", match: :first)).to have_content(@most_score_debate.title)
-
select 'newest', from: 'order-selector'
expect(page).to have_select('order-selector', selected: 'newest')
- expect(find("#debates .debate", match: :first)).to have_content(@most_recent_debate.title)
+
+ within '#debates' do
+ expect('best').to appear_before('medium')
+ expect('medium').to appear_before('worst')
+ end
expect(current_url).to include('order=created_at')
- expect(@most_recent_debate.title).to appear_before(@most_score_debate.title)
- expect(@most_score_debate.title).to appear_before(@most_commented_debate.title)
end
- scenario 'Debates are ordered randomly' do
+ scenario 'Debates are ordered randomly', :js do
create_list(:debate, 12)
visit debates_path
select 'random', from: 'order-selector'
expect(page).to have_select('order-selector', selected: 'random')
+ expect(page).to have_selector('#debates')
debates_first_time = find("#debates").text
select 'most commented', from: 'order-selector'
expect(page).to have_select('order-selector', selected: 'most commented')
- expect(find("#debates .debate", match: :first)).to have_content(@most_commented_debate.title)
+ expect(page).to have_selector('#debates')
select 'random', from: 'order-selector'
expect(page).to have_select('order-selector', selected: 'random')
+ expect(page).to have_selector('#debates')
debates_second_time = find("#debates").text
expect(debates_first_time).to_not eq(debates_second_time)
diff --git a/spec/features/votes_spec.rb b/spec/features/votes_spec.rb
index ea9761bde..1116d2985 100644
--- a/spec/features/votes_spec.rb
+++ b/spec/features/votes_spec.rb
@@ -2,17 +2,14 @@ require 'rails_helper'
feature 'Votes' do
+ background do
+ @manuela = create(:user, verified_at: Time.now)
+ @pablo = create(:user)
+
+ login_as(@manuela)
+ end
+
feature 'Debates' do
-
- background do
- @manuela = create(:user, verified_at: Time.now)
- @pablo = create(:user)
- @debate = create(:debate)
-
- login_as(@manuela)
- visit debate_path(@debate)
- end
-
scenario "Home shows user votes on featured debates" do
debate1 = create(:debate)
debate2 = create(:debate)
@@ -109,84 +106,86 @@ feature 'Votes' do
end
end
- scenario 'Show no votes' do
- visit debate_path(@debate)
-
- expect(page).to have_content "No votes"
-
- within('.in-favor') do
- expect(page).to have_content "0%"
- expect(page).to_not have_css("a.voted")
- expect(page).to_not have_css("a.no-voted")
+ feature 'Single debate' do
+ background do
+ @debate = create(:debate)
end
- within('.against') do
- expect(page).to have_content "0%"
- expect(page).to_not have_css("a.voted")
- expect(page).to_not have_css("a.no-voted")
- end
- end
+ scenario 'Show no votes' do
+ visit debate_path(@debate)
- scenario 'Show' do
- create(:vote, voter: @manuela, votable: @debate, vote_flag: true)
- create(:vote, voter: @pablo, votable: @debate, vote_flag: false)
-
- visit debate_path(@debate)
-
- expect(page).to have_content "2 votes"
-
- within('.in-favor') do
- expect(page).to have_content "50%"
- expect(page).to have_css("a.voted")
- end
-
- within('.against') do
- expect(page).to have_content "50%"
- expect(page).to have_css("a.no-voted")
- end
- end
-
- scenario 'Create from debate show', :js do
- find('.in-favor a').click
-
- within('.in-favor') do
- expect(page).to have_content "100%"
- expect(page).to have_css("a.voted")
- end
-
- within('.against') do
- expect(page).to have_content "0%"
- expect(page).to have_css("a.no-voted")
- end
-
- expect(page).to have_content "1 vote"
- end
-
- scenario 'Create from debate featured', :js do
- visit root_path
-
- within("#featured-debates") do
- find('.in-favor a').click
+ expect(page).to have_content "No votes"
within('.in-favor') do
- expect(page).to have_content "100%"
- expect(page).to have_css("a.voted")
+ expect(page).to have_content "0%"
+ expect(page).to_not have_css("a.voted")
+ expect(page).to_not have_css("a.no-voted")
end
within('.against') do
+ expect(page).to have_content "0%"
+ expect(page).to_not have_css("a.voted")
+ expect(page).to_not have_css("a.no-voted")
+ end
+ end
+
+ scenario 'Update', :js do
+ visit debate_path(@debate)
+
+ find('.in-favor a').click
+ find('.against a').click
+
+ within('.in-favor') do
expect(page).to have_content "0%"
expect(page).to have_css("a.no-voted")
end
+ within('.against') do
+ expect(page).to have_content "100%"
+ expect(page).to have_css("a.voted")
+ end
+
expect(page).to have_content "1 vote"
end
- expect(URI.parse(current_url).path).to eq(root_path)
- end
- scenario 'Create from debate index', :js do
- visit debates_path
+ scenario 'Trying to vote multiple times', :js do
+ visit debate_path(@debate)
- within("#debates") do
+ find('.in-favor a').click
+ find('.in-favor a').click
+
+ within('.in-favor') do
+ expect(page).to have_content "100%"
+ end
+
+ within('.against') do
+ expect(page).to have_content "0%"
+ end
+
+ expect(page).to have_content "1 vote"
+ end
+
+ scenario 'Show' do
+ create(:vote, voter: @manuela, votable: @debate, vote_flag: true)
+ create(:vote, voter: @pablo, votable: @debate, vote_flag: false)
+
+ visit debate_path(@debate)
+
+ expect(page).to have_content "2 votes"
+
+ within('.in-favor') do
+ expect(page).to have_content "50%"
+ expect(page).to have_css("a.voted")
+ end
+
+ within('.against') do
+ expect(page).to have_content "50%"
+ expect(page).to have_css("a.no-voted")
+ end
+ end
+
+ scenario 'Create from debate show', :js do
+ visit debate_path(@debate)
find('.in-favor a').click
@@ -202,51 +201,56 @@ feature 'Votes' do
expect(page).to have_content "1 vote"
end
- expect(URI.parse(current_url).path).to eq(debates_path)
- end
- scenario 'Update', :js do
- find('.in-favor a').click
- find('.against a').click
+ scenario 'Create in featured', :js do
+ visit root_path
- within('.in-favor') do
- expect(page).to have_content "0%"
- expect(page).to have_css("a.no-voted")
+ within("#featured-debates") do
+ find('.in-favor a').click
+
+ within('.in-favor') do
+ expect(page).to have_content "100%"
+ expect(page).to have_css("a.voted")
+ end
+
+ within('.against') do
+ expect(page).to have_content "0%"
+ expect(page).to have_css("a.no-voted")
+ end
+
+ expect(page).to have_content "1 vote"
+ end
+ expect(URI.parse(current_url).path).to eq(root_path)
end
- within('.against') do
- expect(page).to have_content "100%"
- expect(page).to have_css("a.voted")
+ scenario 'Create in index', :js do
+ visit debates_path
+
+ within("#debates") do
+
+ find('.in-favor a').click
+
+ within('.in-favor') do
+ expect(page).to have_content "100%"
+ expect(page).to have_css("a.voted")
+ end
+
+ within('.against') do
+ expect(page).to have_content "0%"
+ expect(page).to have_css("a.no-voted")
+ end
+
+ expect(page).to have_content "1 vote"
+ end
+ expect(URI.parse(current_url).path).to eq(debates_path)
end
-
- expect(page).to have_content "1 vote"
- end
-
- scenario 'Trying to vote multiple times', :js do
- find('.in-favor a').click
- find('.in-favor a').click
-
- within('.in-favor') do
- expect(page).to have_content "100%"
- end
-
- within('.against') do
- expect(page).to have_content "0%"
- end
-
- expect(page).to have_content "1 vote"
end
end
feature 'Comments' do
background do
- @manuela = create(:user)
- @pablo = create(:user)
@debate = create(:debate)
@comment = create(:comment, commentable: @debate)
-
- login_as(@manuela)
- visit debate_path(@debate)
end
scenario 'Show' do
@@ -269,6 +273,8 @@ feature 'Votes' do
end
scenario 'Create', :js do
+ visit debate_path(@debate)
+
within("#comment_#{@comment.id}_votes") do
find(".in_favor a").click
@@ -285,6 +291,8 @@ feature 'Votes' do
end
scenario 'Update', :js do
+ visit debate_path(@debate)
+
within("#comment_#{@comment.id}_votes") do
find('.in_favor a').click
find('.against a').click
@@ -302,6 +310,8 @@ feature 'Votes' do
end
scenario 'Trying to vote multiple times', :js do
+ visit debate_path(@debate)
+
within("#comment_#{@comment.id}_votes") do
find('.in_favor a').click
find('.in_favor a').click
@@ -317,6 +327,5 @@ feature 'Votes' do
expect(page).to have_content "1 vote"
end
end
-
end
end
diff --git a/spec/models/debate_spec.rb b/spec/models/debate_spec.rb
index fc4130354..537cc303c 100644
--- a/spec/models/debate_spec.rb
+++ b/spec/models/debate_spec.rb
@@ -174,15 +174,6 @@ describe Debate do
end
end
- describe '#default_order' do
- let!(:economy) { create(:debate) }
- let!(:health) { create(:debate) }
-
- it "returns debates ordered by last one first" do
- expect(Debate.all).to eq([health, economy])
- end
- end
-
describe '#anonymous_votes_ratio' do
it "returns the percentage of anonymous votes of the total votes" do
debate = create(:debate, cached_anonymous_votes_total: 25, cached_votes_total: 100)
@@ -190,4 +181,64 @@ describe Debate do
end
end
+ describe '#hot_score' do
+ let(:now) { Time.now }
+
+ it "increases for newer debates" do
+ old = create(:debate, :with_hot_score, created_at: now - 1.day)
+ new = create(:debate, :with_hot_score, created_at: now)
+ expect(new.hot_score).to be > old.hot_score
+ end
+
+ it "increases for debates with more comments" do
+ more_comments = create(:debate, :with_hot_score, created_at: now, comments_count: 10)
+ less_comments = create(:debate, :with_hot_score, created_at: now, comments_count: 1)
+ expect(more_comments.hot_score).to be > less_comments.hot_score
+ end
+
+ it "increases for debates with more positive votes" do
+ more_likes = create(:debate, :with_hot_score, created_at: now, cached_votes_total: 10, cached_votes_up: 5)
+ less_likes = create(:debate, :with_hot_score, created_at: now, cached_votes_total: 10, cached_votes_up: 1)
+ expect(more_likes.hot_score).to be > less_likes.hot_score
+ end
+
+ it "increases for debates with more confidence" do
+ more_confidence = create(:debate, :with_hot_score, created_at: now, cached_votes_total: 1000, cached_votes_up: 700)
+ less_confidence = create(:debate, :with_hot_score, created_at: now, cached_votes_total: 10, cached_votes_up: 9)
+ expect(more_confidence.hot_score).to be > less_confidence.hot_score
+ end
+
+ it "decays in older debates, even if they have more votes" do
+ older_more_voted = create(:debate, :with_hot_score, created_at: now - 2.days, cached_votes_total: 1000, cached_votes_up: 900)
+ new_less_voted = create(:debate, :with_hot_score, created_at: now, cached_votes_total: 10, cached_votes_up: 9)
+ expect(new_less_voted.hot_score).to be > older_more_voted.hot_score
+ end
+
+ describe 'actions which affect it' do
+ let(:debate) { create(:debate, :with_hot_score) }
+
+ it "increases with likes" do
+ previous = debate.hot_score
+ 5.times { debate.register_vote(create(:user), true) }
+ expect(previous).to be < debate.hot_score
+ end
+
+ it "decreases with dislikes" do
+ debate.register_vote(create(:user), true)
+ previous = debate.hot_score
+ 3.times { debate.register_vote(create(:user), false) }
+ expect(previous).to be > debate.hot_score
+ end
+
+ it "increases with comments" do
+ previous = debate.hot_score
+ Comment.create(user: create(:user), commentable: debate, body: 'foo')
+ expect(previous).to be < debate.hot_score
+ end
+ end
+
+ end
+
+
+
end
diff --git a/spec/support/matchers/appear_before.rb b/spec/support/matchers/appear_before.rb
index bb1d65072..73df35139 100644
--- a/spec/support/matchers/appear_before.rb
+++ b/spec/support/matchers/appear_before.rb
@@ -1,6 +1,7 @@
RSpec::Matchers.define :appear_before do |later_content|
match do |earlier_content|
- page.body.index(earlier_content) < page.body.index(later_content)
+ text = page.text
+ text.index(earlier_content) < text.index(later_content)
end
end