Commit 1be015eb4d8fd5d285e87cae79da7af80114d43b

Authored by Dhruv Kapadia
1 parent 1377962b

Change catchup to no longer depend on pregenerated prompts

app/models/choice.rb
@@ -54,7 +54,6 @@ class Choice < ActiveRecord::Base @@ -54,7 +54,6 @@ class Choice < ActiveRecord::Base
54 votes_count || 0 54 votes_count || 0
55 end 55 end
56 56
57 - after_create :generate_prompts  
58 def before_create 57 def before_create
59 #puts "just got inside choice#before_create. is set to active? #{self.active?}" 58 #puts "just got inside choice#before_create. is set to active? #{self.active?}"
60 unless item 59 unless item
app/models/question.rb
@@ -84,16 +84,16 @@ class Question < ActiveRecord::Base @@ -84,16 +84,16 @@ class Question < ActiveRecord::Base
84 84
85 until prompt && prompt.active? 85 until prompt && prompt.active?
86 target = rand 86 target = rand
87 - prompt_id = nil 87 + left_choice_id = right_choice_id = nil
88 88
89 weighted.each do |item, weight| 89 weighted.each do |item, weight|
90 if target <= weight 90 if target <= weight
91 - prompt_id = item 91 + left_choice_id, right_choice_id = item.split(", ")
92 break 92 break
93 end 93 end
94 target -= weight 94 target -= weight
95 end 95 end
96 - prompt = Prompt.find(prompt_id, :include => ['left_choice', 'right_choice']) 96 + 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 }])
97 end 97 end
98 # check if prompt has two active choices here, maybe we can set this on the prompt level too? 98 # check if prompt has two active choices here, maybe we can set this on the prompt level too?
99 prompt 99 prompt
@@ -104,16 +104,31 @@ class Question &lt; ActiveRecord::Base @@ -104,16 +104,31 @@ class Question &lt; ActiveRecord::Base
104 def catchup_prompts_weights 104 def catchup_prompts_weights
105 weights = Hash.new(0) 105 weights = Hash.new(0)
106 throttle_min = 0.05 106 throttle_min = 0.05
107 - #assuming all prompts exist 107 + sum = 0.0
108 108
109 - #the_prompts = prompts.find(:all, :select => 'id, votes_count')  
110 - #We don't really need to instantiate all the objects  
111 - the_prompts = ActiveRecord::Base.connection.select_all("SELECT id, votes_count from prompts where question_id =#{self.id}") 109 + prompts.find_each(:select => 'votes_count, left_choice_id, right_choice_id') do |p|
  110 + value = [(1.0/ (p.votes.size + 1).to_f).to_f, throttle_min].min
  111 + weights["#{p.left_choice_id}, #{p.right_choice_id}"] = value
  112 + sum += value
  113 + end
112 114
113 - the_prompts.each do |p|  
114 - weights[p["id"].to_i] = [(1.0/ (p["votes_count"].to_i + 1).to_f).to_f, throttle_min].min 115 + # This will not run once all prompts have been generated,
  116 + # but it prevents us from having to pregenerate all possible prompts
  117 + if weights.size < choices.size ** 2 - choices.size
  118 + choices.each do |l|
  119 + choices.each do |r|
  120 + if l.id == r.id
  121 + next
  122 + end
  123 + if !weights.has_key?("#{l.id}, #{r.id}")
  124 + weights["#{l.id}, #{r.id}"] = throttle_min
  125 + sum+=throttle_min
  126 + end
  127 + end
  128 + end
115 end 129 end
116 - normalize!(weights) 130 +
  131 + normalize!(weights, sum)
117 weights 132 weights
118 end 133 end
119 134
@@ -148,18 +163,21 @@ class Question &lt; ActiveRecord::Base @@ -148,18 +163,21 @@ class Question &lt; ActiveRecord::Base
148 163
149 end 164 end
150 165
151 - def normalize!(weighted) 166 + #passing precomputed sum saves us a traversal through the array
  167 + def normalize!(weighted, sum=nil)
152 if weighted.instance_of?(Hash) 168 if weighted.instance_of?(Hash)
153 - sum = weighted.inject(0) do |sum, item_and_weight|  
154 - sum += item_and_weight[1] 169 + if sum.nil?
  170 + sum = weighted.inject(0) do |sum, item_and_weight|
  171 + sum += item_and_weight[1]
  172 + end
  173 + sum = sum.to_f
155 end 174 end
156 - sum = sum.to_f  
157 weighted.each do |item, weight| 175 weighted.each do |item, weight|
158 weighted[item] = weight/sum 176 weighted[item] = weight/sum
159 weighted[item] = 0.0 unless weighted[item].finite? 177 weighted[item] = 0.0 unless weighted[item].finite?
160 end 178 end
161 elsif weighted.instance_of?(Array) 179 elsif weighted.instance_of?(Array)
162 - sum = weighted.inject(0) {|sum, item| sum += item} 180 + sum = weighted.inject(0) {|sum, item| sum += item} if sum.nil?
163 weighted.each_with_index do |item, i| 181 weighted.each_with_index do |item, i|
164 weighted[i] = item/sum 182 weighted[i] = item/sum
165 weighted[i] = 0.0 unless weighted[i].finite? 183 weighted[i] = 0.0 unless weighted[i].finite?
lib/tasks/test_api.rake
@@ -11,11 +11,14 @@ namespace :test_api do @@ -11,11 +11,14 @@ namespace :test_api do
11 11
12 12
13 current_user = User.first 13 current_user = User.first
14 - 3000.times do  
15 - question = Question.find(120) # test question change as needed  
16 - @p = Prompt.find(question.catchup_choose_prompt_id)  
17 -  
18 - current_user.record_vote("test_vote", @p, rand(2)) 14 + 1000.times do |n|
  15 + puts "#{n} votes completed" if n % 100 == 0
  16 + question = Question.find(214) # test question change as needed
  17 + @prompt = question.catchup_choose_prompt
  18 + @appearance = current_user.record_appearance(current_user.default_visitor, @prompt)
  19 +
  20 + direction = (rand(2) == 0) ? "left" : "right"
  21 + current_user.record_vote(:prompt => @prompt, :direction => direction, :appearance_lookup => @appearance.lookup)
19 end 22 end
20 23
21 end 24 end
@@ -183,20 +186,29 @@ namespace :test_api do @@ -183,20 +186,29 @@ namespace :test_api do
183 desc "Dump votes of a question by left vs right id" 186 desc "Dump votes of a question by left vs right id"
184 task(:make_csv => :environment) do 187 task(:make_csv => :environment) do
185 188
186 - q = Question.find(120) 189 + q = Question.find(214)
  190 +
187 191
188 the_prompts = q.prompts_hash_by_choice_ids 192 the_prompts = q.prompts_hash_by_choice_ids
189 193
190 #hash_of_choice_ids_from_left_to_right_to_votes 194 #hash_of_choice_ids_from_left_to_right_to_votes
191 the_hash = {} 195 the_hash = {}
192 - the_prompts.each do |key, p|  
193 - left_id, right_id = key.split(", ")  
194 - if not the_hash.has_key?(left_id)  
195 - the_hash[left_id] = {}  
196 - the_hash[left_id][left_id] = 0 196 + q.choices.each do |l|
  197 + q.choices.each do |r|
  198 + next if l.id == r.id
  199 +
  200 + if not the_hash.has_key?(l.id)
  201 + the_hash[l.id] = {}
  202 + the_hash[l.id][l.id] = 0
197 end 203 end
198 204
199 - the_hash[left_id][right_id] = p.votes.size 205 + p = the_prompts["#{l.id}, #{r.id}"]
  206 + if p.nil?
  207 + the_hash[l.id][r.id] = 0
  208 + else
  209 + the_hash[l.id][r.id] = p.appearances.size
  210 + end
  211 + end
200 end 212 end
201 213
202 the_hash.sort.each do |xval, row| 214 the_hash.sort.each do |xval, row|