2015-07-20 16:01:08 +00:00
module JamRuby
class Review < ActiveRecord :: Base
2016-01-13 03:01:39 +00:00
include HtmlSanitize
html_sanitize strict : [ :description ]
2015-07-21 19:47:35 +00:00
attr_accessible :target , :rating , :description , :user , :user_id , :target_id , :target_type
2015-07-20 16:01:08 +00:00
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 "
2015-07-22 00:14:18 +00:00
scope :available , - > { where ( " deleted_at iS NULL " ) }
2016-01-13 03:01:39 +00:00
scope :all , - > { select ( " * " ) }
2015-07-22 00:14:18 +00:00
2016-01-13 03:01:39 +00:00
validates :description , length : { maximum : 16000 } , no_profanity : true , :allow_blank = > true
validates :rating , presence : true , numericality : { only_integer : true , minimum : 1 , maximum : 5 }
2015-07-20 16:29:19 +00:00
2016-01-13 03:01:39 +00:00
validates :target , presence : true
validates :user_id , presence : true
2015-07-20 16:29:19 +00:00
validates :target_id , uniqueness : { scope : :user_id , message : " There is already a review for this User and Target. " }
2015-07-20 21:49:22 +00:00
2016-01-13 03:01:39 +00:00
after_save :reduce
2015-07-21 01:33:20 +00:00
2016-04-06 02:23:15 +00:00
def self . create ( params )
review = Review . new
review . target = params [ :target ]
review . user = params [ :user ]
review . rating = params [ :rating ]
review . description = params [ :description ]
review . target_type = params [ :target ] . class . to_s
review . save
review
end
2016-01-13 03:01:39 +00:00
def self . index ( options = { } )
if options . key? ( :include_deleted )
arel = Review . all
else
arel = Review . available
end
if options . key? ( :target_id )
arel = arel . where ( " target_id=? " , options [ :target_id ] )
end
2015-07-21 01:33:20 +00:00
2016-01-13 03:01:39 +00:00
if options . key? ( :user_id )
arel = arel . where ( " user_id=? " , options [ :user_id ] )
2015-07-21 01:33:20 +00:00
end
2016-01-13 03:01:39 +00:00
arel
end
2015-07-20 21:49:22 +00:00
2016-01-13 03:01:39 +00:00
# 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
2015-07-20 21:49:22 +00:00
end
2016-01-13 03:01:39 +00:00
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
2015-07-20 21:49:22 +00:00
2016-01-13 03:01:39 +00:00
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
2015-07-20 16:01:08 +00:00
end
end