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
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 < 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 < 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| | ... | ... |