* reviews updated
This commit is contained in:
parent
82dd27754c
commit
69c05a3e0a
|
|
@ -3,7 +3,7 @@ CREATE TABLE reviews (
|
|||
user_id VARCHAR(64) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
target_id VARCHAR(64) NOT NULL,
|
||||
target_type VARCHAR(32) NOT NULL,
|
||||
description VARCHAR(8000),
|
||||
description VARCHAR,
|
||||
rating INT NOT NULL,
|
||||
deleted_by_user_id VARCHAR(64) REFERENCES users(id) ON DELETE SET NULL,
|
||||
deleted_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NULL,
|
||||
|
|
|
|||
|
|
@ -1,67 +1,93 @@
|
|||
module JamRuby
|
||||
class Review < ActiveRecord::Base
|
||||
include HtmlSanitize
|
||||
html_sanitize strict: [:description]
|
||||
|
||||
attr_accessible :target, :rating, :description, :user, :user_id, :target_id, :target_type
|
||||
belongs_to :target, polymorphic: true
|
||||
belongs_to :user, foreign_key: 'user_id', class_name: "JamRuby::User"
|
||||
belongs_to :deleted_by_user, foreign_key: 'deleted_by_user_id', class_name: "JamRuby::User"
|
||||
|
||||
scope :available, -> { where("deleted_at iS NULL") }
|
||||
scope :all, -> { select("*")}
|
||||
scope :all, -> { select("*") }
|
||||
|
||||
validates :rating, presence:true, numericality: {only_integer: true, minimum:1, maximum:5}
|
||||
validates :description, length: {maximum: 16000}, no_profanity: true, :allow_blank => true
|
||||
validates :rating, presence: true, numericality: {only_integer: true, minimum: 1, maximum: 5}
|
||||
|
||||
validates :target, presence:true
|
||||
validates :user_id, presence:true
|
||||
validates :target, presence: true
|
||||
validates :user_id, presence: true
|
||||
validates :target_id, uniqueness: {scope: :user_id, message: "There is already a review for this User and Target."}
|
||||
|
||||
class << self
|
||||
def index(options={})
|
||||
if(options.key?(:include_deleted))
|
||||
arel = Review.all
|
||||
else
|
||||
arel = Review.available
|
||||
end
|
||||
after_save :reduce
|
||||
|
||||
if(options.key?(:target_id))
|
||||
arel = arel.where("target_id=?", options[:target_id])
|
||||
end
|
||||
|
||||
if(options.key?(:user_id))
|
||||
arel = arel.where("user_id=?", options[:user_id])
|
||||
end
|
||||
|
||||
arel
|
||||
def self.index(options={})
|
||||
if options.key?(:include_deleted)
|
||||
arel = Review.all
|
||||
else
|
||||
arel = Review.available
|
||||
end
|
||||
|
||||
# Create review_summary records by grouping reviews
|
||||
def reduce()
|
||||
ReviewSummary.transaction do
|
||||
ReviewSummary.destroy_all
|
||||
Review.select("target_id, target_type AS target_type, AVG(rating) as avg_rating, count(*) as review_count, SUM(CASE WHEN rating>=3.0 THEN 1 ELSE 0 END) AS pos_count")
|
||||
.where("deleted_at IS NULL")
|
||||
.group("target_type, target_id")
|
||||
.each do |r|
|
||||
wilson_score=ci_lower_bound(r.pos_count, r.review_count)
|
||||
ReviewSummary.create!(
|
||||
target_id: r.target_id,
|
||||
target_type: r.target_type,
|
||||
avg_rating: r.avg_rating,
|
||||
wilson_score: wilson_score,
|
||||
review_count: r.review_count
|
||||
)
|
||||
end # each
|
||||
end # transaction
|
||||
end # reduce
|
||||
|
||||
def ci_lower_bound(pos, n, confidence=0.95)
|
||||
pos=pos.to_f
|
||||
n=n.to_f
|
||||
return 0 if n == 0
|
||||
z = 1.96 # Statistics2.pnormaldist(1-(1-confidence)/2)
|
||||
phat = 1.0*pos/n
|
||||
(phat + z*z/(2*n) - z * Math.sqrt((phat*(1-phat)+z*z/(4*n))/n))/(1+z*z/n)
|
||||
if options.key?(:target_id)
|
||||
arel = arel.where("target_id=?", options[:target_id])
|
||||
end
|
||||
|
||||
end # self
|
||||
if options.key?(:user_id)
|
||||
arel = arel.where("user_id=?", options[:user_id])
|
||||
end
|
||||
|
||||
arel
|
||||
end
|
||||
|
||||
# Create review_summary records by grouping reviews
|
||||
def self.reduce_all
|
||||
ReviewSummary.transaction do
|
||||
ReviewSummary.destroy_all
|
||||
Review.select("target_id, target_type AS target_type, AVG(rating) as avg_rating, count(*) as review_count, SUM(CASE WHEN rating>=3.0 THEN 1 ELSE 0 END) AS pos_count")
|
||||
.where("deleted_at IS NULL")
|
||||
.group("target_type, target_id")
|
||||
.each do |r|
|
||||
wilson_score = ci_lower_bound(r.pos_count, r.review_count)
|
||||
ReviewSummary.create!(
|
||||
target_id: r.target_id,
|
||||
target_type: r.target_type,
|
||||
avg_rating: r.avg_rating,
|
||||
wilson_score: wilson_score,
|
||||
review_count: r.review_count
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# http://www.evanmiller.org/how-not-to-sort-by-average-rating.html
|
||||
def self.ci_lower_bound(pos, n, confidence=0.95)
|
||||
pos=pos.to_f
|
||||
n=n.to_f
|
||||
return 0 if n == 0
|
||||
z = 1.96 # Statistics2.pnormaldist(1-(1-confidence)/2)
|
||||
phat = 1.0*pos/n
|
||||
(phat + z*z/(2*n) - z * Math.sqrt((phat*(1-phat)+z*z/(4*n))/n))/(1+z*z/n)
|
||||
end
|
||||
|
||||
def reduce
|
||||
ReviewSummary.transaction do
|
||||
ReviewSummary.where(target_type: target_type, target_id: target_id).destroy_all
|
||||
|
||||
Review.select("target_id, target_type AS target_type, AVG(rating) as avg_rating, count(*) as review_count, SUM(CASE WHEN rating>=3.0 THEN 1 ELSE 0 END) AS pos_count")
|
||||
.where("deleted_at IS NULL")
|
||||
.where(target_type: target_type, target_id: target_id)
|
||||
.group("target_type, target_id")
|
||||
.each do |r|
|
||||
wilson_score = Review.ci_lower_bound(r.pos_count, r.review_count)
|
||||
ReviewSummary.create!(
|
||||
target_id: r.target_id,
|
||||
target_type: r.target_type,
|
||||
avg_rating: r.avg_rating,
|
||||
wilson_score: wilson_score,
|
||||
review_count: r.review_count
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -57,8 +57,6 @@ describe Review do
|
|||
Review.index.should have(2).items
|
||||
|
||||
# Reduce and check:
|
||||
ReviewSummary.should have(0).items
|
||||
Review.reduce()
|
||||
ReviewSummary.should have(1).items
|
||||
ReviewSummary.first.avg_rating.should eq(4.0)
|
||||
|
||||
|
|
@ -68,8 +66,7 @@ describe Review do
|
|||
# Create some more and verify:
|
||||
5.times {Review.create(target:target, rating:5, user:FactoryGirl.create(:user))}
|
||||
Review.index.should have(7).items
|
||||
|
||||
Review.reduce()
|
||||
ReviewSummary.should have(1).items
|
||||
|
||||
ReviewSummary.first.wilson_score.should > ws_orig
|
||||
ReviewSummary.first.avg_rating.should > avg_orig
|
||||
|
|
@ -127,9 +124,6 @@ describe Review do
|
|||
review2.valid?.should be_true
|
||||
Review.should have(2).items
|
||||
|
||||
# Reduce and check:
|
||||
ReviewSummary.should have(0).items
|
||||
Review.reduce()
|
||||
ReviewSummary.should have(1).items
|
||||
ReviewSummary.first.avg_rating.should eq(4.0)
|
||||
|
||||
|
|
@ -139,7 +133,6 @@ describe Review do
|
|||
|
||||
# Create some more and verify:
|
||||
5.times {Review.create(target:target, rating:5, user:FactoryGirl.create(:user))}
|
||||
Review.reduce()
|
||||
ReviewSummary.should have(1).items
|
||||
ReviewSummary.first.wilson_score.should > ws_orig
|
||||
ReviewSummary.first.avg_rating.should > avg_orig
|
||||
|
|
@ -150,7 +143,6 @@ describe Review do
|
|||
5.times {Review.create(target:target2, rating:5, user:FactoryGirl.create(:user))}
|
||||
Review.index.should have(12).items
|
||||
Review.index(target_id: target2).should have(5).items
|
||||
Review.reduce()
|
||||
summaries = ReviewSummary.index()
|
||||
summaries.should have(2).items
|
||||
summaries[0].wilson_score.should > summaries[1].wilson_score
|
||||
|
|
|
|||
Loading…
Reference in New Issue