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 54 votes_count || 0
55 55 end
56 56  
57   - after_create :generate_prompts
58 57 def before_create
59 58 #puts "just got inside choice#before_create. is set to active? #{self.active?}"
60 59 unless item
... ...
app/models/question.rb
... ... @@ -84,16 +84,16 @@ class Question < ActiveRecord::Base
84 84  
85 85 until prompt && prompt.active?
86 86 target = rand
87   - prompt_id = nil
  87 + left_choice_id = right_choice_id = nil
88 88  
89 89 weighted.each do |item, weight|
90 90 if target <= weight
91   - prompt_id = item
  91 + left_choice_id, right_choice_id = item.split(", ")
92 92 break
93 93 end
94 94 target -= weight
95 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 97 end
98 98 # check if prompt has two active choices here, maybe we can set this on the prompt level too?
99 99 prompt
... ... @@ -104,16 +104,31 @@ class Question &lt; ActiveRecord::Base
104 104 def catchup_prompts_weights
105 105 weights = Hash.new(0)
106 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 129 end
116   - normalize!(weights)
  130 +
  131 + normalize!(weights, sum)
117 132 weights
118 133 end
119 134  
... ... @@ -148,18 +163,21 @@ class Question &lt; ActiveRecord::Base
148 163  
149 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 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 174 end
156   - sum = sum.to_f
157 175 weighted.each do |item, weight|
158 176 weighted[item] = weight/sum
159 177 weighted[item] = 0.0 unless weighted[item].finite?
160 178 end
161 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 181 weighted.each_with_index do |item, i|
164 182 weighted[i] = item/sum
165 183 weighted[i] = 0.0 unless weighted[i].finite?
... ...
lib/tasks/test_api.rake
... ... @@ -11,11 +11,14 @@ namespace :test_api do
11 11  
12 12  
13 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 22 end
20 23  
21 24 end
... ... @@ -183,20 +186,29 @@ namespace :test_api do
183 186 desc "Dump votes of a question by left vs right id"
184 187 task(:make_csv => :environment) do
185 188  
186   - q = Question.find(120)
  189 + q = Question.find(214)
  190 +
187 191  
188 192 the_prompts = q.prompts_hash_by_choice_ids
189 193  
190 194 #hash_of_choice_ids_from_left_to_right_to_votes
191 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 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 212 end
201 213  
202 214 the_hash.sort.each do |xval, row|
... ...