From 8f8afdfd623353a6cc60f979f792d5b732349c24 Mon Sep 17 00:00:00 2001 From: kikito Date: Mon, 14 Sep 2015 20:37:32 +0200 Subject: [PATCH] Proposals hot_score --- app/models/proposal.rb | 19 +++++++++ lib/score_calculator.rb | 12 +++--- spec/models/proposal_spec.rb | 83 +++++++++++++++++++++++++++++++++++- 3 files changed, 107 insertions(+), 7 deletions(-) diff --git a/app/models/proposal.rb b/app/models/proposal.rb index 9e1b1daa6..24045e8f7 100644 --- a/app/models/proposal.rb +++ b/app/models/proposal.rb @@ -27,6 +27,8 @@ class Proposal < ActiveRecord::Base before_validation :sanitize_tag_list before_validation :set_responsible_name + before_save :calculate_hot_score, :calculate_confidence_score + scope :for_render, -> { includes(:tags) } scope :sort_by_hot_score , -> { order(hot_score: :desc) } scope :sort_by_confidence_score , -> { order(confidence_score: :desc) } @@ -87,6 +89,22 @@ class Proposal < ActiveRecord::Base "#{Setting.value_for("proposal_code_prefix")}-#{created_at.strftime('%Y-%M')}-#{id}" end + def after_commented + save # updates the hot_score because there is a before_save + end + + def calculate_hot_score + self.hot_score = ScoreCalculator.hot_score(created_at, + cached_votes_up, + cached_votes_up, + comments_count) + end + + def calculate_confidence_score + self.confidence_score = ScoreCalculator.confidence_score(cached_votes_up, + cached_votes_up) + end + def self.title_max_length @@title_max_length ||= self.columns.find { |c| c.name == 'title' }.limit || 80 end @@ -126,6 +144,7 @@ class Proposal < ActiveRecord::Base self.responsible_name = author.document_number end end + private def validate_description_length diff --git a/lib/score_calculator.rb b/lib/score_calculator.rb index 66738445c..7d77e82d6 100644 --- a/lib/score_calculator.rb +++ b/lib/score_calculator.rb @@ -4,16 +4,16 @@ module ScoreCalculator COMMENT_WEIGHT = 1.0/20 # 1 positive vote / x comments TIME_UNIT = 12.hours.to_f - def self.hot_score(date, votes_total, votes_up, comments_count) - total = votes_total + COMMENT_WEIGHT * comments_count - ups = votes_up + COMMENT_WEIGHT * comments_count + def self.hot_score(date, votes_total, votes_up, comments_count) + total = (votes_total + COMMENT_WEIGHT * comments_count).to_f + ups = (votes_up + COMMENT_WEIGHT * comments_count).to_f downs = total - ups score = ups - downs - order = Math.log([score.abs, 1].max, 10) - sign = (score <=> 0).to_f + offset = Math.log([score.abs, 1].max, 10) * (ups / [total, 1].max) + sign = score <=> 0 seconds = ((date || Time.now) - EPOC).to_f - (((order * sign) + (seconds/TIME_UNIT)) * 10000000).round + (((offset * sign) + (seconds/TIME_UNIT)) * 10000000).round end def self.confidence_score(votes_total, votes_up) diff --git a/spec/models/proposal_spec.rb b/spec/models/proposal_spec.rb index fe2422173..f713f3ce5 100644 --- a/spec/models/proposal_spec.rb +++ b/spec/models/proposal_spec.rb @@ -142,4 +142,85 @@ describe Proposal do end end end -end \ No newline at end of file + + describe '#hot_score' do + let(:now) { Time.now } + + it "increases for newer proposals" do + old = create(:proposal, :with_hot_score, created_at: now - 1.day) + new = create(:proposal, :with_hot_score, created_at: now) + expect(new.hot_score).to be > old.hot_score + end + + it "increases for proposals with more comments" do + more_comments = create(:proposal, :with_hot_score, created_at: now, comments_count: 25) + less_comments = create(:proposal, :with_hot_score, created_at: now, comments_count: 1) + expect(more_comments.hot_score).to be > less_comments.hot_score + end + + it "increases for proposals with more positive votes" do + more_likes = create(:proposal, :with_hot_score, created_at: now, cached_votes_up: 5) + less_likes = create(:proposal, :with_hot_score, created_at: now, cached_votes_up: 1) + expect(more_likes.hot_score).to be > less_likes.hot_score + end + + it "increases for proposals with more confidence" do + more_confidence = create(:proposal, :with_hot_score, created_at: now, cached_votes_up: 700) + less_confidence = create(:proposal, :with_hot_score, created_at: now, cached_votes_up: 9) + expect(more_confidence.hot_score).to be > less_confidence.hot_score + end + + it "decays in older proposals, even if they have more votes" do + older_more_voted = create(:proposal, :with_hot_score, created_at: now - 2.days, cached_votes_up: 900) + new_less_voted = create(:proposal, :with_hot_score, created_at: now, 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(:proposal) { create(:proposal, :with_hot_score) } + + it "increases with votes" do + previous = proposal.hot_score + 5.times { proposal.register_vote(create(:user, verified_at: Time.now), true) } + expect(previous).to be < proposal.reload.hot_score + end + + it "increases with comments" do + previous = proposal.hot_score + 25.times{ Comment.create(user: create(:user), commentable: proposal, body: 'foobarbaz') } + expect(previous).to be < proposal.reload.hot_score + end + end + end + + describe "#confidence_score" do + + it "takes into account votes" do + proposal = create(:proposal, :with_confidence_score, cached_votes_up: 100) + expect(proposal.confidence_score).to eq(10000) + + proposal = create(:proposal, :with_confidence_score, cached_votes_up: 0) + expect(proposal.confidence_score).to eq(0) + + proposal = create(:proposal, :with_confidence_score, cached_votes_up: 75) + expect(proposal.confidence_score).to eq(7500) + + proposal = create(:proposal, :with_confidence_score, cached_votes_up: 750) + expect(proposal.confidence_score).to eq(75000) + + proposal = create(:proposal, :with_confidence_score, cached_votes_up: 10) + expect(proposal.confidence_score).to eq(1000) + end + + describe 'actions which affect it' do + let(:proposal) { create(:proposal, :with_confidence_score) } + + it "increases with like" do + previous = proposal.confidence_score + 5.times { proposal.register_vote(create(:user, verified_at: Time.now), true) } + expect(previous).to be < proposal.confidence_score + end + end + + end +end