From c01226e12542a8656e25d9e19a53615a9c7e58ef Mon Sep 17 00:00:00 2001 From: Dhruv Kapadia Date: Fri, 2 Apr 2010 01:49:36 -0400 Subject: [PATCH] Appearance model added, tracks when prompts have been shown --- app/controllers/prompts_controller.rb | 17 +++++++++++++---- app/controllers/questions_controller.rb | 20 +++++++++++++++++++- app/models/appearance.rb | 7 +++++++ app/models/user.rb | 12 ++++++++++-- app/models/visitor.rb | 8 ++++++-- app/models/vote.rb | 1 + db/migrate/20100401230916_create_appearances.rb | 21 +++++++++++++++++++++ spec/fixtures/appearances.yml | 0 spec/models/appearance_spec.rb | 17 +++++++++++++++++ 9 files changed, 94 insertions(+), 9 deletions(-) create mode 100644 app/models/appearance.rb create mode 100644 db/migrate/20100401230916_create_appearances.rb create mode 100644 spec/fixtures/appearances.yml create mode 100644 spec/models/appearance_spec.rb diff --git a/app/controllers/prompts_controller.rb b/app/controllers/prompts_controller.rb index 86781eb..6dd4554 100644 --- a/app/controllers/prompts_controller.rb +++ b/app/controllers/prompts_controller.rb @@ -53,12 +53,16 @@ class PromptsController < InheritedResources::Base @prompt = @question.prompts.find(params[:id]) time_viewed = params['params']['time_viewed'] + + visitor_identifier = params['params']['auto'] + + appearance_lookup = params['params']['appearance_lookup'] case direction when :left - successful = c = current_user.record_vote(params['params']['auto'], @prompt, 0, time_viewed) + successful = c = current_user.record_vote(params['params']['auto'], appearance_lookup, @prompt, 0, time_viewed) when :right - successful = c = current_user.record_vote(params['params']['auto'], @prompt, 1, time_viewed) + successful = c = current_user.record_vote(params['params']['auto'], appearance_lookup, @prompt, 1, time_viewed) else raise "need to specify either ':left' or ':right' as a direction" end @@ -74,8 +78,13 @@ class PromptsController < InheritedResources::Base next_prompt = @question.picked_prompt end - format.xml { render :xml => next_prompt.to_xml(:methods => [:left_choice_text, :right_choice_text, :left_choice_id, :right_choice_id]), :status => :ok } - format.json { render :json => next_prompt.to_json(:methods => [:left_choice_text, :right_choice_text, :left_choice_id, :right_choice_id]), :status => :ok } + + @a = current_user.record_appearance(visitor_identifier, next_prompt) + + appearance_id = Proc.new { |options| options[:builder].tag!('appearance_id', @a.lookup) } + + 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 } + 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 } else format.xml { render :xml => c, :status => :unprocessable_entity } format.json { render :json => c, :status => :unprocessable_entity } diff --git a/app/controllers/questions_controller.rb b/app/controllers/questions_controller.rb index 22bbd49..b34cf0e 100644 --- a/app/controllers/questions_controller.rb +++ b/app/controllers/questions_controller.rb @@ -55,6 +55,7 @@ class QuestionsController < InheritedResources::Base def show @question = Question.find(params[:id]) + visitor_identifier = params[:visitor_identifier] unless params[:barebones] if params[:algorithm] && params[:algorithm] == "catchup" logger.info("Question #{@question.id} requested catchup algorithm!") @@ -62,13 +63,30 @@ class QuestionsController < InheritedResources::Base else @p = @question.picked_prompt end + + # we sometimes request a question when no prompt is displayed + # TODO It would be a good idea to find these places and treat them like barebones + if visitor_identifier != "" + @a = current_user.record_appearance(visitor_identifier, @p) + else + @a = nil + end + left_choice_text = Proc.new { |options| options[:builder].tag!('left_choice_text', @p.left_choice.item.data) } right_choice_text = Proc.new { |options| options[:builder].tag!('right_choice_text', @p.right_choice.item.data) } picked_prompt_id = Proc.new { |options| options[:builder].tag!('picked_prompt_id', @p.id) } + appearance_id = Proc.new { |options| options[:builder].tag!('appearance_id', @a.lookup) } + + the_procs = [left_choice_text, right_choice_text, picked_prompt_id] + + if @a + the_procs << appearance_id + end + show! do |format| session['prompts_ids'] ||= [] format.xml { - render :xml => @question.to_xml(:methods => [:item_count], :procs => [left_choice_text, right_choice_text, picked_prompt_id]) + render :xml => @question.to_xml(:methods => [:item_count], :procs => the_procs) } end else diff --git a/app/models/appearance.rb b/app/models/appearance.rb new file mode 100644 index 0000000..3f18b04 --- /dev/null +++ b/app/models/appearance.rb @@ -0,0 +1,7 @@ +class Appearance < ActiveRecord::Base + belongs_to :voter, :class_name => "Visitor", :foreign_key => 'voter_id' + belongs_to :prompt + belongs_to :question + has_one :vote + +end diff --git a/app/models/user.rb b/app/models/user.rb index 5c53cf0..5ce0bce 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -31,13 +31,21 @@ class User < ActiveRecord::Base return choice end - def record_vote(visitor_identifier, prompt, ordinality, time_viewed) + def record_vote(visitor_identifier, appearance_lookup, prompt, ordinality, time_viewed) #@click = Click.new(:what_was_clicked => 'on the API level, inside record_vote' + " with prompt id #{prompt.id}, ordinality #{ordinality.to_s}") #@click.save! visitor = visitors.find_or_create_by_identifier(visitor_identifier) - visitor.vote_for!(prompt, ordinality, time_viewed) + visitor.vote_for!(appearance_lookup, prompt, ordinality, time_viewed) #prompt.choices.each {|c| c.compute_score; c.save!} end + + def record_appearance(visitor_identifier, prompt) + visitor = visitors.find_or_create_by_identifier(visitor_identifier) + + a = Appearance.create(:voter => visitor, :prompt => prompt, :question_id => prompt.question_id, + :lookup => Digest::MD5.hexdigest(rand(10000000000).to_s + visitor.id.to_s + prompt.id.to_s) ) + end + def record_skip(visitor_identifier, prompt) visitor = visitors.find_or_create_by_identifier(visitor_identifier) diff --git a/app/models/visitor.rb b/app/models/visitor.rb index 49ace8d..5d2b4a2 100644 --- a/app/models/visitor.rb +++ b/app/models/visitor.rb @@ -15,7 +15,8 @@ class Visitor < ActiveRecord::Base questions.include? question end - def vote_for!(prompt, ordinality, time_viewed) + def vote_for!(appearance_lookup, prompt, ordinality, time_viewed) + @a = Appearance.find_by_lookup(appearance_lookup) choices = prompt.choices choice = choices[ordinality] #we need to guarantee that the choices are in the right order (by position) other_choices = choices - [choice] @@ -24,12 +25,15 @@ class Visitor < ActiveRecord::Base end loser_choice = other_choices.first - 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) + 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) + # 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 # The updated votes_count object is not saved to the db, so we don't need to worry about double counting # Alternatively, we could just do choice.reload, but that results in another db read choice.votes_count +=1 choice.compute_score! #update score after win + + end def skip!(prompt) diff --git a/app/models/vote.rb b/app/models/vote.rb index 2b69f62..bc4f890 100644 --- a/app/models/vote.rb +++ b/app/models/vote.rb @@ -5,6 +5,7 @@ class Vote < ActiveRecord::Base belongs_to :prompt, :counter_cache => true belongs_to :choice, :counter_cache => true belongs_to :loser_choice, :class_name => "Choice", :foreign_key => "loser_choice_id" + belongs_to :appearance named_scope :recent, lambda { |*args| {:conditions => ["created_at > ?", (args.first || Date.today.beginning_of_day)]} } named_scope :with_question, lambda { |*args| {:conditions => {:question_id => args.first }} } diff --git a/db/migrate/20100401230916_create_appearances.rb b/db/migrate/20100401230916_create_appearances.rb new file mode 100644 index 0000000..bcf06a4 --- /dev/null +++ b/db/migrate/20100401230916_create_appearances.rb @@ -0,0 +1,21 @@ +class CreateAppearances < ActiveRecord::Migration + def self.up + create_table :appearances do |table| + table.integer :voter_id + table.integer :site_id + table.integer :prompt_id + table.integer :question_id + table.string :lookup + table.timestamps + end + + add_column :votes, :appearance_id, :integer + add_index :appearances, :lookup + + end + + def self.down + drop_table :appearances + remove_column :votes, :appearance_id, :integer + end +end diff --git a/spec/fixtures/appearances.yml b/spec/fixtures/appearances.yml new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/spec/fixtures/appearances.yml diff --git a/spec/models/appearance_spec.rb b/spec/models/appearance_spec.rb new file mode 100644 index 0000000..38de0a8 --- /dev/null +++ b/spec/models/appearance_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe Appearance do + before(:each) do + @valid_attributes = { + :voter_id => , + :site_id => , + :prompt_id => , + :question_id => , + :vote_id => 1 + } + end + + it "should create a new instance given valid attributes" do + Appearance.create!(@valid_attributes) + end +end -- libgit2 0.21.2