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,7 +44,7 @@ class Choice < ActiveRecord::Base | ||
| 44 | votes_count || 0 | 44 | votes_count || 0 |
| 45 | end | 45 | end |
| 46 | 46 | ||
| 47 | - #after_create :generate_prompts | 47 | + after_create :generate_prompts |
| 48 | def before_create | 48 | def before_create |
| 49 | puts "just got inside choice#before_create. is set to active? #{self.active?}" | 49 | puts "just got inside choice#before_create. is set to active? #{self.active?}" |
| 50 | unless item | 50 | unless item |
| @@ -90,9 +90,31 @@ class Choice < ActiveRecord::Base | @@ -90,9 +90,31 @@ class Choice < ActiveRecord::Base | ||
| 90 | #do this in a new process (via delayed jobs) | 90 | #do this in a new process (via delayed jobs) |
| 91 | previous_choices = (self.question.choices - [self]) | 91 | previous_choices = (self.question.choices - [self]) |
| 92 | return if previous_choices.empty? | 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 | end | 119 | end |
| 98 | end | 120 | end |
app/models/prompt.rb
| @@ -38,14 +38,6 @@ class Prompt < ActiveRecord::Base | @@ -38,14 +38,6 @@ class Prompt < ActiveRecord::Base | ||
| 38 | left_choice.item.data | 38 | left_choice.item.data |
| 39 | end | 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 | def active? | 41 | def active? |
| 50 | left_choice.active? and right_choice.active? | 42 | left_choice.active? and right_choice.active? |
| 51 | end | 43 | end |
app/models/question.rb
| @@ -37,6 +37,44 @@ class Question < ActiveRecord::Base | @@ -37,6 +37,44 @@ class Question < ActiveRecord::Base | ||
| 37 | end until @p.active? | 37 | end until @p.active? |
| 38 | return @p | 38 | return @p |
| 39 | end | 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 | def distinct_array_of_choice_ids(rank = 2, only_active = true) | 79 | def distinct_array_of_choice_ids(rank = 2, only_active = true) |
| 42 | @choice_ids = choice_ids | 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 | namespace :test_api do | 2 | namespace :test_api do |
| 2 | 3 | ||
| 3 | task :all => [:question_vote_consistency] | 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 | desc "Description here" | 110 | desc "Description here" |
| 6 | task(:question_vote_consistency => :environment) do | 111 | task(:question_vote_consistency => :environment) do |
| 7 | questions = Question.find(:all) | 112 | questions = Question.find(:all) |