Commit 387cad08f262945185d4c9de181b177bfe42cb7d
1 parent
fdc546eb
Exists in
master
and in
1 other branch
Catchup algorithm
Showing
5 changed files
with
179 additions
and
13 deletions
Show diff stats
app/models/choice.rb
| ... | ... | @@ -44,7 +44,7 @@ class Choice < ActiveRecord::Base |
| 44 | 44 | votes_count || 0 |
| 45 | 45 | end |
| 46 | 46 | |
| 47 | - #after_create :generate_prompts | |
| 47 | + after_create :generate_prompts | |
| 48 | 48 | def before_create |
| 49 | 49 | puts "just got inside choice#before_create. is set to active? #{self.active?}" |
| 50 | 50 | unless item |
| ... | ... | @@ -90,9 +90,31 @@ class Choice < ActiveRecord::Base |
| 90 | 90 | #do this in a new process (via delayed jobs) |
| 91 | 91 | previous_choices = (self.question.choices - [self]) |
| 92 | 92 | return if previous_choices.empty? |
| 93 | - previous_choices.each { |c| | |
| 94 | - question.prompts.create!(:left_choice => c, :right_choice => self) | |
| 95 | - question.prompts.create!(:left_choice => self, :right_choice => c) | |
| 96 | - } | |
| 93 | + inserts = [] | |
| 94 | + | |
| 95 | + timestring = Time.now.to_s(:db) #isn't rails awesome? | |
| 96 | + | |
| 97 | + #add prompts with this choice on the left | |
| 98 | + previous_choices.each do |r| | |
| 99 | + inserts.push("(NULL, #{self.question_id}, NULL, #{self.id}, '#{timestring}', '#{timestring}', NULL, 0, #{r.id}, NULL, NULL)") | |
| 100 | + end | |
| 101 | + #add prompts with this choice on the right | |
| 102 | + previous_choices.each do |l| | |
| 103 | + inserts.push("(NULL, #{self.question_id}, NULL, #{l.id}, '#{timestring}', '#{timestring}', NULL, 0, #{self.id}, NULL, NULL)") | |
| 104 | + end | |
| 105 | + sql = "INSERT INTO `prompts` (`algorithm_id`, `question_id`, `voter_id`, `left_choice_id`, `created_at`, `updated_at`, `tracking`, `votes_count`, `right_choice_id`, `active`, `randomkey`) VALUES #{inserts.join(', ')}" | |
| 106 | + | |
| 107 | + Question.update_counters(self.question_id, :prompts_count => 2*previous_choices.size) | |
| 108 | + | |
| 109 | + logger.info("The sql is:::: #{sql}") | |
| 110 | + | |
| 111 | + ActiveRecord::Base.connection.execute(sql) | |
| 112 | + | |
| 113 | +#VALUES (NULL, 108, NULL, 1892, '2010-03-16 11:12:37', '2010-03-16 11:12:37', NULL, 0, 1893, NULL, NULL) | |
| 114 | +# INSERT INTO `prompts` (`algorithm_id`, `question_id`, `voter_id`, `left_choice_id`, `created_at`, `updated_at`, `tracking`, `votes_count`, `right_choice_id`, `active`, `randomkey`) VALUES(NULL, 108, NULL, 1892, '2010-03-16 11:12:37', '2010-03-16 11:12:37', NULL, 0, 1893, NULL, NULL) | |
| 115 | + #previous_choices.each { |c| | |
| 116 | + # question.prompts.create!(:left_choice => c, :right_choice => self) | |
| 117 | + # question.prompts.create!(:left_choice => self, :right_choice => c) | |
| 118 | + #} | |
| 97 | 119 | end |
| 98 | 120 | end | ... | ... |
app/models/prompt.rb
| ... | ... | @@ -38,14 +38,6 @@ class Prompt < ActiveRecord::Base |
| 38 | 38 | left_choice.item.data |
| 39 | 39 | end |
| 40 | 40 | |
| 41 | - def left_choice_id | |
| 42 | - left_choice.id | |
| 43 | - end | |
| 44 | - | |
| 45 | - def right_choice_id | |
| 46 | - right_choice.id | |
| 47 | - end | |
| 48 | - | |
| 49 | 41 | def active? |
| 50 | 42 | left_choice.active? and right_choice.active? |
| 51 | 43 | end | ... | ... |
app/models/question.rb
| ... | ... | @@ -37,6 +37,44 @@ class Question < ActiveRecord::Base |
| 37 | 37 | end until @p.active? |
| 38 | 38 | return @p |
| 39 | 39 | end |
| 40 | + | |
| 41 | + # adapted from ruby cookbook(2006): section 5-11 | |
| 42 | + def catchup_choose_prompt_id | |
| 43 | + weighted = catchup_prompts_weights | |
| 44 | + # Rand returns a number from 0 - 1, so weighted needs to be normalized | |
| 45 | + target = rand | |
| 46 | + weighted.each do |item, weight| | |
| 47 | + return item if target <= weight | |
| 48 | + target -= weight | |
| 49 | + end | |
| 50 | + # check if prompt has two active choices here, maybe we can set this on the prompt level too? | |
| 51 | + end | |
| 52 | + | |
| 53 | + | |
| 54 | + # TODO Add index for question id on prompts table | |
| 55 | + def catchup_prompts_weights | |
| 56 | + weights = Hash.new(0) | |
| 57 | + throttle_min = 0.05 | |
| 58 | + #assuming all prompts exist | |
| 59 | + prompts.each do |p| | |
| 60 | + weights[p.id] = [(1.0/ (p.votes.size + 1).to_f).to_f, throttle_min].min | |
| 61 | + end | |
| 62 | + normalize!(weights) | |
| 63 | + weights | |
| 64 | + end | |
| 65 | + | |
| 66 | + def normalize!(weighted) | |
| 67 | + if weighted.instance_of?(Hash) | |
| 68 | + sum = weighted.inject(0) do |sum, item_and_weight| | |
| 69 | + sum += item_and_weight[1] | |
| 70 | + end | |
| 71 | + sum = sum.to_f | |
| 72 | + weighted.each { |item, weight| weighted[item] = weight/sum } | |
| 73 | + elsif weighted.instance_of?(Array) | |
| 74 | + sum = weighted.inject(0) {|sum, item| sum += item} | |
| 75 | + weighted.each_with_index {|item, i| weighted[i] = item/sum} | |
| 76 | + end | |
| 77 | + end | |
| 40 | 78 | |
| 41 | 79 | def distinct_array_of_choice_ids(rank = 2, only_active = true) |
| 42 | 80 | @choice_ids = choice_ids | ... | ... |
db/migrate/20100317161212_add_question_index_to_prompts.rb
0 → 100644
lib/tasks/test_api.rake
| 1 | +require 'fastercsv' | |
| 1 | 2 | namespace :test_api do |
| 2 | 3 | |
| 3 | 4 | task :all => [:question_vote_consistency] |
| 4 | 5 | |
| 6 | + desc "Don't run unless you know what you are doing" | |
| 7 | + task(:generate_lots_of_votes => :environment) do | |
| 8 | + if Rails.env.production? | |
| 9 | + print "You probably don't want to run this in production as it will falsify a bunch of random votes" | |
| 10 | + end | |
| 11 | + | |
| 12 | + | |
| 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)) | |
| 19 | + end | |
| 20 | + | |
| 21 | + end | |
| 22 | + | |
| 23 | + desc "Should only need to be run once" | |
| 24 | + task(:generate_all_possible_prompts => :environment) do | |
| 25 | + inserts = [] | |
| 26 | + Question.find(:all).each do |q| | |
| 27 | + choices = q.choices | |
| 28 | + if q.prompts.size > choices.size**2 - choices.size | |
| 29 | + print "ERROR: #{q.id}\n" | |
| 30 | + next | |
| 31 | + elsif q.prompts.size == choices.size**2 - choices.size | |
| 32 | + print "#{q.id} has enough prompts, skipping...\n" | |
| 33 | + next | |
| 34 | + else | |
| 35 | + print "#{q.id} should add #{(choices.size ** 2 - choices.size) - q.prompts.size}\n" | |
| 36 | + | |
| 37 | + end | |
| 38 | + timestring = Time.now.to_s(:db) #isn't rails awesome? | |
| 39 | + promptscount=0 | |
| 40 | + the_prompts = Prompt.find(:all, :select => 'id, left_choice_id, right_choice_id', :conditions => {:question_id => q.id}) | |
| 41 | + | |
| 42 | + the_prompts_hash = {} | |
| 43 | + the_prompts.each do |p| | |
| 44 | + the_prompts_hash["#{p.left_choice_id},#{p.right_choice_id}"] = 1 | |
| 45 | + end | |
| 46 | + | |
| 47 | + choices.each do |l| | |
| 48 | + choices.each do |r| | |
| 49 | + if l.id == r.id | |
| 50 | + next | |
| 51 | + else | |
| 52 | + #p = the_prompts.find{|o| o.left_choice_id == l.id && o.right_choice_id == r.id} | |
| 53 | + keystring = "#{l.id},#{r.id}" | |
| 54 | + p = the_prompts_hash[keystring] | |
| 55 | + if p.nil? | |
| 56 | + print "." | |
| 57 | + inserts.push("(NULL, #{q.id}, NULL, #{l.id}, '#{timestring}', '#{timestring}', NULL, 0, #{r.id}, NULL, NULL)") | |
| 58 | + promptscount+=1 | |
| 59 | + end | |
| 60 | + | |
| 61 | + end | |
| 62 | + | |
| 63 | + end | |
| 64 | + end | |
| 65 | + | |
| 66 | + print "Added #{promptscount} to #{q.id}\n" | |
| 67 | + | |
| 68 | + Question.update_counters(q.id, :prompts_count => promptscount) | |
| 69 | + | |
| 70 | + end | |
| 71 | + | |
| 72 | + sql = "INSERT INTO `prompts` (`algorithm_id`, `question_id`, `voter_id`, `left_choice_id`, `created_at`, `updated_at`, `tracking`, `votes_count`, `right_choice_id`, `active`, `randomkey`) VALUES #{inserts.join(', ')}" | |
| 73 | + | |
| 74 | + unless inserts.empty? | |
| 75 | + ActiveRecord::Base.connection.execute(sql) | |
| 76 | + end | |
| 77 | + | |
| 78 | + end | |
| 79 | + | |
| 80 | + | |
| 81 | + desc "Dump votes of a question by left vs right id" | |
| 82 | + task(:make_csv => :environment) do | |
| 83 | + | |
| 84 | + q = Question.find(120) | |
| 85 | + | |
| 86 | + the_prompts = q.prompts_hash_by_choice_ids | |
| 87 | + | |
| 88 | + #hash_of_choice_ids_from_left_to_right_to_votes | |
| 89 | + the_hash = {} | |
| 90 | + the_prompts.each do |key, p| | |
| 91 | + left_id, right_id = key.split(", ") | |
| 92 | + if not the_hash.has_key?(left_id) | |
| 93 | + the_hash[left_id] = {} | |
| 94 | + the_hash[left_id][left_id] = 0 | |
| 95 | + end | |
| 96 | + | |
| 97 | + the_hash[left_id][right_id] = p.votes.size | |
| 98 | + end | |
| 99 | + | |
| 100 | + the_hash.sort.each do |xval, row| | |
| 101 | + rowarray = [] | |
| 102 | + row.sort.each do |yval, cell| | |
| 103 | + rowarray << cell | |
| 104 | + end | |
| 105 | + puts rowarray.join(", ") | |
| 106 | + end | |
| 107 | + end | |
| 108 | + | |
| 109 | + | |
| 5 | 110 | desc "Description here" |
| 6 | 111 | task(:question_vote_consistency => :environment) do |
| 7 | 112 | questions = Question.find(:all) | ... | ... |