Commit c01226e12542a8656e25d9e19a53615a9c7e58ef
1 parent
3537faab
Exists in
master
and in
1 other branch
Appearance model added, tracks when prompts have been shown
Showing
9 changed files
with
94 additions
and
9 deletions
Show diff stats
app/controllers/prompts_controller.rb
| ... | ... | @@ -53,12 +53,16 @@ class PromptsController < InheritedResources::Base |
| 53 | 53 | @prompt = @question.prompts.find(params[:id]) |
| 54 | 54 | |
| 55 | 55 | time_viewed = params['params']['time_viewed'] |
| 56 | + | |
| 57 | + visitor_identifier = params['params']['auto'] | |
| 58 | + | |
| 59 | + appearance_lookup = params['params']['appearance_lookup'] | |
| 56 | 60 | |
| 57 | 61 | case direction |
| 58 | 62 | when :left |
| 59 | - successful = c = current_user.record_vote(params['params']['auto'], @prompt, 0, time_viewed) | |
| 63 | + successful = c = current_user.record_vote(params['params']['auto'], appearance_lookup, @prompt, 0, time_viewed) | |
| 60 | 64 | when :right |
| 61 | - successful = c = current_user.record_vote(params['params']['auto'], @prompt, 1, time_viewed) | |
| 65 | + successful = c = current_user.record_vote(params['params']['auto'], appearance_lookup, @prompt, 1, time_viewed) | |
| 62 | 66 | else |
| 63 | 67 | raise "need to specify either ':left' or ':right' as a direction" |
| 64 | 68 | end |
| ... | ... | @@ -74,8 +78,13 @@ class PromptsController < InheritedResources::Base |
| 74 | 78 | next_prompt = @question.picked_prompt |
| 75 | 79 | end |
| 76 | 80 | |
| 77 | - format.xml { render :xml => next_prompt.to_xml(:methods => [:left_choice_text, :right_choice_text, :left_choice_id, :right_choice_id]), :status => :ok } | |
| 78 | - format.json { render :json => next_prompt.to_json(:methods => [:left_choice_text, :right_choice_text, :left_choice_id, :right_choice_id]), :status => :ok } | |
| 81 | + | |
| 82 | + @a = current_user.record_appearance(visitor_identifier, next_prompt) | |
| 83 | + | |
| 84 | + appearance_id = Proc.new { |options| options[:builder].tag!('appearance_id', @a.lookup) } | |
| 85 | + | |
| 86 | + format.xml { render :xml => next_prompt.to_xml(:procs => [appearance_id], :methods => [:left_choice_text, :right_choice_text, :left_choice_id, :right_choice_id]), :status => :ok } | |
| 87 | + format.json { render :json => next_prompt.to_json(:procs => [appearance_id], :methods => [:left_choice_text, :right_choice_text, :left_choice_id, :right_choice_id]), :status => :ok } | |
| 79 | 88 | else |
| 80 | 89 | format.xml { render :xml => c, :status => :unprocessable_entity } |
| 81 | 90 | format.json { render :json => c, :status => :unprocessable_entity } | ... | ... |
app/controllers/questions_controller.rb
| ... | ... | @@ -55,6 +55,7 @@ class QuestionsController < InheritedResources::Base |
| 55 | 55 | |
| 56 | 56 | def show |
| 57 | 57 | @question = Question.find(params[:id]) |
| 58 | + visitor_identifier = params[:visitor_identifier] | |
| 58 | 59 | unless params[:barebones] |
| 59 | 60 | if params[:algorithm] && params[:algorithm] == "catchup" |
| 60 | 61 | logger.info("Question #{@question.id} requested catchup algorithm!") |
| ... | ... | @@ -62,13 +63,30 @@ class QuestionsController < InheritedResources::Base |
| 62 | 63 | else |
| 63 | 64 | @p = @question.picked_prompt |
| 64 | 65 | end |
| 66 | + | |
| 67 | + # we sometimes request a question when no prompt is displayed | |
| 68 | + # TODO It would be a good idea to find these places and treat them like barebones | |
| 69 | + if visitor_identifier != "" | |
| 70 | + @a = current_user.record_appearance(visitor_identifier, @p) | |
| 71 | + else | |
| 72 | + @a = nil | |
| 73 | + end | |
| 74 | + | |
| 65 | 75 | left_choice_text = Proc.new { |options| options[:builder].tag!('left_choice_text', @p.left_choice.item.data) } |
| 66 | 76 | right_choice_text = Proc.new { |options| options[:builder].tag!('right_choice_text', @p.right_choice.item.data) } |
| 67 | 77 | picked_prompt_id = Proc.new { |options| options[:builder].tag!('picked_prompt_id', @p.id) } |
| 78 | + appearance_id = Proc.new { |options| options[:builder].tag!('appearance_id', @a.lookup) } | |
| 79 | + | |
| 80 | + the_procs = [left_choice_text, right_choice_text, picked_prompt_id] | |
| 81 | + | |
| 82 | + if @a | |
| 83 | + the_procs << appearance_id | |
| 84 | + end | |
| 85 | + | |
| 68 | 86 | show! do |format| |
| 69 | 87 | session['prompts_ids'] ||= [] |
| 70 | 88 | format.xml { |
| 71 | - render :xml => @question.to_xml(:methods => [:item_count], :procs => [left_choice_text, right_choice_text, picked_prompt_id]) | |
| 89 | + render :xml => @question.to_xml(:methods => [:item_count], :procs => the_procs) | |
| 72 | 90 | } |
| 73 | 91 | end |
| 74 | 92 | else | ... | ... |
app/models/user.rb
| ... | ... | @@ -31,13 +31,21 @@ class User < ActiveRecord::Base |
| 31 | 31 | return choice |
| 32 | 32 | end |
| 33 | 33 | |
| 34 | - def record_vote(visitor_identifier, prompt, ordinality, time_viewed) | |
| 34 | + def record_vote(visitor_identifier, appearance_lookup, prompt, ordinality, time_viewed) | |
| 35 | 35 | #@click = Click.new(:what_was_clicked => 'on the API level, inside record_vote' + " with prompt id #{prompt.id}, ordinality #{ordinality.to_s}") |
| 36 | 36 | #@click.save! |
| 37 | 37 | visitor = visitors.find_or_create_by_identifier(visitor_identifier) |
| 38 | - visitor.vote_for!(prompt, ordinality, time_viewed) | |
| 38 | + visitor.vote_for!(appearance_lookup, prompt, ordinality, time_viewed) | |
| 39 | 39 | #prompt.choices.each {|c| c.compute_score; c.save!} |
| 40 | 40 | end |
| 41 | + | |
| 42 | + def record_appearance(visitor_identifier, prompt) | |
| 43 | + visitor = visitors.find_or_create_by_identifier(visitor_identifier) | |
| 44 | + | |
| 45 | + a = Appearance.create(:voter => visitor, :prompt => prompt, :question_id => prompt.question_id, | |
| 46 | + :lookup => Digest::MD5.hexdigest(rand(10000000000).to_s + visitor.id.to_s + prompt.id.to_s) ) | |
| 47 | + end | |
| 48 | + | |
| 41 | 49 | |
| 42 | 50 | def record_skip(visitor_identifier, prompt) |
| 43 | 51 | visitor = visitors.find_or_create_by_identifier(visitor_identifier) | ... | ... |
app/models/visitor.rb
| ... | ... | @@ -15,7 +15,8 @@ class Visitor < ActiveRecord::Base |
| 15 | 15 | questions.include? question |
| 16 | 16 | end |
| 17 | 17 | |
| 18 | - def vote_for!(prompt, ordinality, time_viewed) | |
| 18 | + def vote_for!(appearance_lookup, prompt, ordinality, time_viewed) | |
| 19 | + @a = Appearance.find_by_lookup(appearance_lookup) | |
| 19 | 20 | choices = prompt.choices |
| 20 | 21 | choice = choices[ordinality] #we need to guarantee that the choices are in the right order (by position) |
| 21 | 22 | other_choices = choices - [choice] |
| ... | ... | @@ -24,12 +25,15 @@ class Visitor < ActiveRecord::Base |
| 24 | 25 | end |
| 25 | 26 | |
| 26 | 27 | loser_choice = other_choices.first |
| 27 | - votes.create!(:question_id => prompt.question_id, :prompt_id => prompt.id, :voter_id=> self.id, :choice_id => choice.id, :loser_choice_id => loser_choice.id, :time_viewed => time_viewed) | |
| 28 | + v = votes.create!(:question_id => prompt.question_id, :prompt_id => prompt.id, :voter_id=> self.id, :choice_id => choice.id, :loser_choice_id => loser_choice.id, :time_viewed => time_viewed, :appearance_id => @a.id) | |
| 29 | + | |
| 28 | 30 | # Votes count is a cached value, creating the vote above will increment it in the db, but to get the proper score, we need to increment it in the current object |
| 29 | 31 | # The updated votes_count object is not saved to the db, so we don't need to worry about double counting |
| 30 | 32 | # Alternatively, we could just do choice.reload, but that results in another db read |
| 31 | 33 | choice.votes_count +=1 |
| 32 | 34 | choice.compute_score! #update score after win |
| 35 | + | |
| 36 | + | |
| 33 | 37 | end |
| 34 | 38 | |
| 35 | 39 | def skip!(prompt) | ... | ... |
app/models/vote.rb
| ... | ... | @@ -5,6 +5,7 @@ class Vote < ActiveRecord::Base |
| 5 | 5 | belongs_to :prompt, :counter_cache => true |
| 6 | 6 | belongs_to :choice, :counter_cache => true |
| 7 | 7 | belongs_to :loser_choice, :class_name => "Choice", :foreign_key => "loser_choice_id" |
| 8 | + belongs_to :appearance | |
| 8 | 9 | |
| 9 | 10 | named_scope :recent, lambda { |*args| {:conditions => ["created_at > ?", (args.first || Date.today.beginning_of_day)]} } |
| 10 | 11 | named_scope :with_question, lambda { |*args| {:conditions => {:question_id => args.first }} } | ... | ... |
| ... | ... | @@ -0,0 +1,21 @@ |
| 1 | +class CreateAppearances < ActiveRecord::Migration | |
| 2 | + def self.up | |
| 3 | + create_table :appearances do |table| | |
| 4 | + table.integer :voter_id | |
| 5 | + table.integer :site_id | |
| 6 | + table.integer :prompt_id | |
| 7 | + table.integer :question_id | |
| 8 | + table.string :lookup | |
| 9 | + table.timestamps | |
| 10 | + end | |
| 11 | + | |
| 12 | + add_column :votes, :appearance_id, :integer | |
| 13 | + add_index :appearances, :lookup | |
| 14 | + | |
| 15 | + end | |
| 16 | + | |
| 17 | + def self.down | |
| 18 | + drop_table :appearances | |
| 19 | + remove_column :votes, :appearance_id, :integer | |
| 20 | + end | |
| 21 | +end | ... | ... |
| ... | ... | @@ -0,0 +1,17 @@ |
| 1 | +require 'spec_helper' | |
| 2 | + | |
| 3 | +describe Appearance do | |
| 4 | + before(:each) do | |
| 5 | + @valid_attributes = { | |
| 6 | + :voter_id => , | |
| 7 | + :site_id => , | |
| 8 | + :prompt_id => , | |
| 9 | + :question_id => , | |
| 10 | + :vote_id => 1 | |
| 11 | + } | |
| 12 | + end | |
| 13 | + | |
| 14 | + it "should create a new instance given valid attributes" do | |
| 15 | + Appearance.create!(@valid_attributes) | |
| 16 | + end | |
| 17 | +end | ... | ... |