Commit e532eb9bdb651d881c8f92dacf177cbc2a737e20
1 parent
6d3412e9
Exists in
master
and in
1 other branch
refactor random prompt picking
when using catchup and no prompt found, use random prompt
Showing
1 changed file
with
34 additions
and
28 deletions
Show diff stats
app/models/question.rb
| @@ -57,7 +57,7 @@ class Question < ActiveRecord::Base | @@ -57,7 +57,7 @@ class Question < ActiveRecord::Base | ||
| 57 | next_prompt = self.pop_prompt_queue | 57 | next_prompt = self.pop_prompt_queue |
| 58 | if next_prompt.nil? | 58 | if next_prompt.nil? |
| 59 | logger.info("DEBUG Catchup prompt cache miss! Nothing in prompt_queue") | 59 | logger.info("DEBUG Catchup prompt cache miss! Nothing in prompt_queue") |
| 60 | - next_prompt = self.catchup_choose_prompt | 60 | + next_prompt = self.picked_prompt |
| 61 | record_prompt_cache_miss | 61 | record_prompt_cache_miss |
| 62 | else | 62 | else |
| 63 | record_prompt_cache_hit | 63 | record_prompt_cache_hit |
| @@ -66,24 +66,18 @@ class Question < ActiveRecord::Base | @@ -66,24 +66,18 @@ class Question < ActiveRecord::Base | ||
| 66 | return next_prompt | 66 | return next_prompt |
| 67 | else | 67 | else |
| 68 | #Standard choose prompt at random | 68 | #Standard choose prompt at random |
| 69 | - next_prompt = self.picked_prompt | ||
| 70 | - return next_prompt | 69 | + return self.picked_prompt |
| 71 | end | 70 | end |
| 72 | 71 | ||
| 73 | end | 72 | end |
| 74 | 73 | ||
| 75 | - #TODO: generalize for prompts of rank > 2 | ||
| 76 | - #TODO: add index for rapid finding | ||
| 77 | - def picked_prompt(rank = 2) | ||
| 78 | - logger.info "inside Question#picked_prompt" | ||
| 79 | - raise NotImplementedError.new("Sorry, we currently only support pairwise prompts. Rank of the prompt must be 2.") unless rank == 2 | ||
| 80 | - begin | ||
| 81 | - choice_id_array = distinct_array_of_choice_ids(rank) | ||
| 82 | - @p = prompts.find_or_create_by_left_choice_id_and_right_choice_id(choice_id_array[0], choice_id_array[1], :include => [:left_choice ,:right_choice ]) | ||
| 83 | - logger.info "#{@p.inspect} is active? #{@p.active?}" | ||
| 84 | - end until @p.active? | ||
| 85 | - return @p | ||
| 86 | - end | 74 | + #TODO: generalize for prompts of rank > 2 |
| 75 | + def picked_prompt(rank = 2) | ||
| 76 | + logger.info "inside Question#picked_prompt" | ||
| 77 | + raise NotImplementedError.new("Sorry, we currently only support pairwise prompts. Rank of the prompt must be 2.") unless rank == 2 | ||
| 78 | + choice_id_array = distinct_array_of_choice_ids(rank, true) | ||
| 79 | + prompts.find_or_create_by_left_choice_id_and_right_choice_id(choice_id_array[0], choice_id_array[1], :include => [:left_choice ,:right_choice ]) | ||
| 80 | + end | ||
| 87 | 81 | ||
| 88 | # adapted from ruby cookbook(2006): section 5-11 | 82 | # adapted from ruby cookbook(2006): section 5-11 |
| 89 | def catchup_choose_prompt | 83 | def catchup_choose_prompt |
| @@ -118,6 +112,11 @@ class Question < ActiveRecord::Base | @@ -118,6 +112,11 @@ class Question < ActiveRecord::Base | ||
| 118 | active_choices = choices.active | 112 | active_choices = choices.active |
| 119 | active_choice_ids = active_choices.map {|c| c.id} | 113 | active_choice_ids = active_choices.map {|c| c.id} |
| 120 | sql = "SELECT votes_count, left_choice_id, right_choice_id FROM prompts WHERE question_id = #{self.id} AND left_choice_id IN (#{active_choice_ids.join(',')}) AND right_choice_id IN (#{active_choice_ids.join(',')})" | 114 | sql = "SELECT votes_count, left_choice_id, right_choice_id FROM prompts WHERE question_id = #{self.id} AND left_choice_id IN (#{active_choice_ids.join(',')}) AND right_choice_id IN (#{active_choice_ids.join(',')})" |
| 115 | + # Warning: lots of memory possibly used here | ||
| 116 | + # We don't want to use Rails find_each or find_in_batches because | ||
| 117 | + # it is too slow for close to a million rows. We don't need ActiveRecord | ||
| 118 | + # objects here. It may be a good idea to update this to grab these in | ||
| 119 | + # batches. | ||
| 121 | ActiveRecord::Base.connection.select_all(sql).each do |p| | 120 | ActiveRecord::Base.connection.select_all(sql).each do |p| |
| 122 | value = [(1.0/ (p['votes_count'].to_i + 1).to_f).to_f, throttle_min].min | 121 | value = [(1.0/ (p['votes_count'].to_i + 1).to_f).to_f, throttle_min].min |
| 123 | weights[p['left_choice_id']+", "+p['right_choice_id']] = value | 122 | weights[p['left_choice_id']+", "+p['right_choice_id']] = value |
| @@ -335,19 +334,26 @@ class Question < ActiveRecord::Base | @@ -335,19 +334,26 @@ class Question < ActiveRecord::Base | ||
| 335 | end | 334 | end |
| 336 | 335 | ||
| 337 | 336 | ||
| 338 | - def distinct_array_of_choice_ids(rank = 2, only_active = true) | ||
| 339 | - @choice_ids = choice_ids | ||
| 340 | - @s = @choice_ids.size | ||
| 341 | - begin | ||
| 342 | - index_list = (0...@s).sort_by{rand} | ||
| 343 | - first_one, second_one = index_list.first, index_list.second | ||
| 344 | - @the_choice_ids = @choice_ids.values_at(first_one, second_one) | ||
| 345 | - # @the_choice_ids << choices.active.first(:order => 'RAND()', :select => 'id').id | ||
| 346 | - # @the_choice_ids << choices.active.last(:order => 'RAND()', :select => 'id').id | ||
| 347 | - end until (@the_choice_ids.size == rank) | ||
| 348 | - logger.info "List populated and looks like #{@the_choice_ids.inspect}" | ||
| 349 | - return @the_choice_ids.to_a | ||
| 350 | - end | 337 | + def distinct_array_of_choice_ids(rank = 2, only_active = true) |
| 338 | + count = (only_active) ? choices.active.count : choices.count | ||
| 339 | + | ||
| 340 | + found_choices = [] | ||
| 341 | + rank.times do | ||
| 342 | + # select only active choices? | ||
| 343 | + conditions = (only_active) ? ['active = ?', true] : [''] | ||
| 344 | + # if we've already found some, make sure we don't find them again | ||
| 345 | + if found_choices.count > 0 | ||
| 346 | + conditions[0] += ' AND id NOT IN (?)' | ||
| 347 | + conditions.push found_choices | ||
| 348 | + end | ||
| 349 | + | ||
| 350 | + found_choices.push choices.find(:first, | ||
| 351 | + :select => 'id', | ||
| 352 | + :conditions => conditions, | ||
| 353 | + :offset => rand(count - found_choices.count)).id | ||
| 354 | + end | ||
| 355 | + return found_choices | ||
| 356 | + end | ||
| 351 | 357 | ||
| 352 | def picked_prompt_id | 358 | def picked_prompt_id |
| 353 | picked_prompt.id | 359 | picked_prompt.id |