From 1be015eb4d8fd5d285e87cae79da7af80114d43b Mon Sep 17 00:00:00 2001 From: Dhruv Kapadia Date: Thu, 10 Jun 2010 10:34:54 -0400 Subject: [PATCH] Change catchup to no longer depend on pregenerated prompts --- app/models/choice.rb | 1 - app/models/question.rb | 48 +++++++++++++++++++++++++++++++++--------------- lib/tasks/test_api.rake | 36 ++++++++++++++++++++++++------------ 3 files changed, 57 insertions(+), 28 deletions(-) diff --git a/app/models/choice.rb b/app/models/choice.rb index 30f35ef..e04a059 100644 --- a/app/models/choice.rb +++ b/app/models/choice.rb @@ -54,7 +54,6 @@ class Choice < ActiveRecord::Base votes_count || 0 end - after_create :generate_prompts def before_create #puts "just got inside choice#before_create. is set to active? #{self.active?}" unless item diff --git a/app/models/question.rb b/app/models/question.rb index 417a77f..a31bb53 100644 --- a/app/models/question.rb +++ b/app/models/question.rb @@ -84,16 +84,16 @@ class Question < ActiveRecord::Base until prompt && prompt.active? target = rand - prompt_id = nil + left_choice_id = right_choice_id = nil weighted.each do |item, weight| if target <= weight - prompt_id = item + left_choice_id, right_choice_id = item.split(", ") break end target -= weight end - prompt = Prompt.find(prompt_id, :include => ['left_choice', 'right_choice']) + prompt = prompts.find_or_create_by_left_choice_id_and_right_choice_id(left_choice_id, right_choice_id, :include => [{ :left_choice => :item }, { :right_choice => :item }]) end # check if prompt has two active choices here, maybe we can set this on the prompt level too? prompt @@ -104,16 +104,31 @@ class Question < ActiveRecord::Base def catchup_prompts_weights weights = Hash.new(0) throttle_min = 0.05 - #assuming all prompts exist + sum = 0.0 - #the_prompts = prompts.find(:all, :select => 'id, votes_count') - #We don't really need to instantiate all the objects - the_prompts = ActiveRecord::Base.connection.select_all("SELECT id, votes_count from prompts where question_id =#{self.id}") + prompts.find_each(:select => 'votes_count, left_choice_id, right_choice_id') do |p| + value = [(1.0/ (p.votes.size + 1).to_f).to_f, throttle_min].min + weights["#{p.left_choice_id}, #{p.right_choice_id}"] = value + sum += value + end - the_prompts.each do |p| - weights[p["id"].to_i] = [(1.0/ (p["votes_count"].to_i + 1).to_f).to_f, throttle_min].min + # This will not run once all prompts have been generated, + # but it prevents us from having to pregenerate all possible prompts + if weights.size < choices.size ** 2 - choices.size + choices.each do |l| + choices.each do |r| + if l.id == r.id + next + end + if !weights.has_key?("#{l.id}, #{r.id}") + weights["#{l.id}, #{r.id}"] = throttle_min + sum+=throttle_min + end + end + end end - normalize!(weights) + + normalize!(weights, sum) weights end @@ -148,18 +163,21 @@ class Question < ActiveRecord::Base end - def normalize!(weighted) + #passing precomputed sum saves us a traversal through the array + def normalize!(weighted, sum=nil) if weighted.instance_of?(Hash) - sum = weighted.inject(0) do |sum, item_and_weight| - sum += item_and_weight[1] + if sum.nil? + sum = weighted.inject(0) do |sum, item_and_weight| + sum += item_and_weight[1] + end + sum = sum.to_f end - sum = sum.to_f weighted.each do |item, weight| weighted[item] = weight/sum weighted[item] = 0.0 unless weighted[item].finite? end elsif weighted.instance_of?(Array) - sum = weighted.inject(0) {|sum, item| sum += item} + sum = weighted.inject(0) {|sum, item| sum += item} if sum.nil? weighted.each_with_index do |item, i| weighted[i] = item/sum weighted[i] = 0.0 unless weighted[i].finite? diff --git a/lib/tasks/test_api.rake b/lib/tasks/test_api.rake index 5707842..d96bab3 100644 --- a/lib/tasks/test_api.rake +++ b/lib/tasks/test_api.rake @@ -11,11 +11,14 @@ namespace :test_api do current_user = User.first - 3000.times do - question = Question.find(120) # test question change as needed - @p = Prompt.find(question.catchup_choose_prompt_id) - - current_user.record_vote("test_vote", @p, rand(2)) + 1000.times do |n| + puts "#{n} votes completed" if n % 100 == 0 + question = Question.find(214) # test question change as needed + @prompt = question.catchup_choose_prompt + @appearance = current_user.record_appearance(current_user.default_visitor, @prompt) + + direction = (rand(2) == 0) ? "left" : "right" + current_user.record_vote(:prompt => @prompt, :direction => direction, :appearance_lookup => @appearance.lookup) end end @@ -183,20 +186,29 @@ namespace :test_api do desc "Dump votes of a question by left vs right id" task(:make_csv => :environment) do - q = Question.find(120) + q = Question.find(214) + the_prompts = q.prompts_hash_by_choice_ids #hash_of_choice_ids_from_left_to_right_to_votes the_hash = {} - the_prompts.each do |key, p| - left_id, right_id = key.split(", ") - if not the_hash.has_key?(left_id) - the_hash[left_id] = {} - the_hash[left_id][left_id] = 0 + q.choices.each do |l| + q.choices.each do |r| + next if l.id == r.id + + if not the_hash.has_key?(l.id) + the_hash[l.id] = {} + the_hash[l.id][l.id] = 0 end - the_hash[left_id][right_id] = p.votes.size + p = the_prompts["#{l.id}, #{r.id}"] + if p.nil? + the_hash[l.id][r.id] = 0 + else + the_hash[l.id][r.id] = p.appearances.size + end + end end the_hash.sort.each do |xval, row| -- libgit2 0.21.2