Commit 1be015eb4d8fd5d285e87cae79da7af80114d43b
1 parent
1377962b
Exists in
master
and in
1 other branch
Change catchup to no longer depend on pregenerated prompts
Showing
3 changed files
with
57 additions
and
28 deletions
Show diff stats
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 < ActiveRecord::Base | @@ -104,16 +104,31 @@ class Question < 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 < ActiveRecord::Base | @@ -148,18 +163,21 @@ class Question < 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| |