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