diff --git a/ruby/lib/jam_ruby/models/review.rb b/ruby/lib/jam_ruby/models/review.rb index bdddb6d73..4e7726acc 100644 --- a/ruby/lib/jam_ruby/models/review.rb +++ b/ruby/lib/jam_ruby/models/review.rb @@ -10,11 +10,35 @@ module JamRuby validates :target, presence:true validates :user, presence:true validates :target_id, uniqueness: {scope: :user_id, message: "There is already a review for this User and Target."} - - # # @options - can contain values: - # # * target_id (optional) - # def reduce(options) - # arel = Review.where("deleted_at=?", nil) - # end + + class << self + # 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").group("target_type, target_id") + .each do |r| + #puts "Reducing reviews: #{r.inspect} #{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: ci_lower_bound(r.pos_count, r.review_count), + 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) + end + + end # self end end \ No newline at end of file diff --git a/ruby/lib/jam_ruby/models/review_summary.rb b/ruby/lib/jam_ruby/models/review_summary.rb index 6e0ef6b11..94a21e901 100644 --- a/ruby/lib/jam_ruby/models/review_summary.rb +++ b/ruby/lib/jam_ruby/models/review_summary.rb @@ -1,12 +1,13 @@ module JamRuby class ReviewSummary < ActiveRecord::Base - attr_accessible :target, :target_type, :avg_rating, :wilson_score, :review_count + attr_accessible :target, :target_id, :target_type, :avg_rating, :wilson_score, :review_count belongs_to :target, polymorphic: true validates :avg_rating, presence:true, numericality: true validates :review_count, presence:true, numericality: {only_integer: true} validates :wilson_score, presence:true, numericality: {greater_than:0, less_than:1} - validates :target, presence:true - validates :target_id, uniqueness:true + validates :target_id, presence:true, uniqueness:true + + end end \ No newline at end of file diff --git a/ruby/spec/factories.rb b/ruby/spec/factories.rb index 90cd48c44..893a09c17 100644 --- a/ruby/spec/factories.rb +++ b/ruby/spec/factories.rb @@ -139,6 +139,17 @@ FactoryGirl.define do end end + factory :review, :class => JamRuby::Review do + sequence(:name) { |n| "Band" } + biography "My Biography" + city "Apex" + state "NC" + country "US" + before(:create) { |review| + review.genres << Genre.first + } + end + factory :music_session, :class => JamRuby::MusicSession do sequence(:name) { |n| "Music Session #{n}" } sequence(:description) { |n| "Music Session Description #{n}" } diff --git a/ruby/spec/jam_ruby/models/review_spec.rb b/ruby/spec/jam_ruby/models/review_spec.rb index e9fe640f9..dcdced01b 100644 --- a/ruby/spec/jam_ruby/models/review_spec.rb +++ b/ruby/spec/jam_ruby/models/review_spec.rb @@ -46,8 +46,33 @@ describe Review do review2 = Review.create(target:target, rating:3, user:@user) review2.valid?.should be_false end - end + it "reduces" do + review = Review.create(target:target, rating:3, user:@user) + review.valid?.should be_true + + review2 = Review.create(target:target, rating:5, user:FactoryGirl.create(:user)) + review2.valid?.should be_true + Review.count.should eq(2) + ReviewSummary.count.should eq(0) + Review.reduce() + ReviewSummary.count.should eq(1) + ReviewSummary.first.avg_rating.should eq(4.0) + + puts "ORIG: #{ReviewSummary.all.inspect}" + ws_orig = ReviewSummary.first.wilson_score + avg_orig = ReviewSummary.first.avg_rating + + 5.times {Review.create(target:target, rating:5, user:FactoryGirl.create(:user))} + Review.reduce() + + ReviewSummary.first.wilson_score.should > ws_orig + ReviewSummary.first.avg_rating.should > avg_orig + + puts "ALL: #{ReviewSummary.all.inspect}" + end + end # context + context "validates review summary" do it "blank target" do review_summary = ReviewSummary.create() @@ -99,4 +124,5 @@ describe Review do it_behaves_like :review, @jam_track, "jam_track" end + end \ No newline at end of file