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