From abd621ab9f2695794acbaac06038fe581efaae06 Mon Sep 17 00:00:00 2001 From: Dhruv Kapadia Date: Tue, 11 May 2010 13:42:09 -0400 Subject: [PATCH] Non-votes files, tweaks to other csv files, better tests for csvs --- app/models/appearance.rb | 4 ++++ app/models/question.rb | 50 +++++++++++++++++++++++++++++++++----------------- app/models/visitor.rb | 1 + spec/models/question_spec.rb | 40 ++++++++++++++++++++++++++++++++-------- 4 files changed, 70 insertions(+), 25 deletions(-) diff --git a/app/models/appearance.rb b/app/models/appearance.rb index 7e88166..c84af0b 100644 --- a/app/models/appearance.rb +++ b/app/models/appearance.rb @@ -2,5 +2,9 @@ class Appearance < ActiveRecord::Base belongs_to :voter, :class_name => "Visitor", :foreign_key => 'voter_id' belongs_to :prompt belongs_to :question + + #technically, an appearance should either one vote or one skip, not one of both objects, but these declarations provide some useful helper methods + # we could refactor this to use rails polymorphism, but currently the foreign key is stored in the vote and skip object has_one :vote + has_one :skip end diff --git a/app/models/question.rb b/app/models/question.rb index c3369ec..6c476b8 100644 --- a/app/models/question.rb +++ b/app/models/question.rb @@ -350,7 +350,7 @@ class Question < ActiveRecord::Base outfile = "ideamarketplace_#{self.id}_votes.csv" headers = ['Vote ID', 'Session ID', 'Question ID','Winner ID', 'Winner Text', 'Loser ID', 'Loser Text', - 'Prompt ID', 'Left Choice ID', 'Right Choice ID', 'Created at', 'Updated at', + 'Prompt ID', 'Left Choice ID', 'Right Choice ID', 'Created at', 'Updated at', 'Appearance ID', 'Response Time (s)', 'Session Identifier'] when 'ideas' @@ -358,11 +358,11 @@ class Question < ActiveRecord::Base headers = ['Ideamarketplace ID','Idea ID', 'Idea Text', 'Wins', 'Losses', 'Times involved in Cant Decide', 'Score', 'User Submitted', 'Session ID', 'Created at', 'Last Activity', 'Active', 'Appearances on Left', 'Appearances on Right'] - when 'skips' - outfile = "ideamarketplace_#{self.id}_skips.csv" - headers = ['Skip ID', 'Session ID', 'Question ID','Left Choice ID', 'Left Choice Text', + when 'non_votes' + outfile = "ideamarketplace_#{self.id}_non_votes.csv" + headers = ['Record Type', 'Record ID', 'Session ID', 'Question ID','Left Choice ID', 'Left Choice Text', 'Right Choice ID', 'Right Choice Text', 'Prompt ID', 'Appearance ID', 'Reason', - 'Created at', 'Updated at', 'Response Time (ms)'] + 'Created at', 'Updated at', 'Response Time (s)', 'Session Identifier'] else raise "Unsupported export type: #{type}" end @@ -377,15 +377,15 @@ class Question < ActiveRecord::Base case type when 'votes' - self.votes.find_each(:include => [:prompt, :choice, :loser_choice]) do |v| + self.votes.find_each(:include => [:prompt, :choice, :loser_choice, :voter]) do |v| prompt = v.prompt # these may not exist loser_data = v.loser_choice.nil? ? "" : "'#{v.loser_choice.data.strip}'" left_id = v.prompt.nil? ? "" : v.prompt.left_choice_id right_id = v.prompt.nil? ? "" : v.prompt.right_choice_id - csv << [ v.id, v.voter_id, v.question_id, v.choice_id, "\'#{v.choice.data.strip}'", v.loser_choice_id, loser_data, - v.prompt_id, left_id, right_id, v.created_at, v.updated_at, + csv << [ v.id, v.voter_id, v.question_id, v.choice_id, "'#{v.choice.data.strip}'", v.loser_choice_id, loser_data, + v.prompt_id, left_id, right_id, v.created_at, v.updated_at, v.appearance_id, v.time_viewed.to_f / 1000.0 , v.voter.identifier] end @@ -404,14 +404,30 @@ class Question < ActiveRecord::Base user_submitted , c.item.creator_id, c.created_at, c.updated_at, c.active, left_appearances, right_appearances] end - when 'skips' + when 'non_votes' - self.skips.find_each(:include => :prompt) do |s| - prompt = s.prompt - csv << [ s.id, s.skipper_id, s.question_id, s.prompt.left_choice.id, s.prompt.left_choice.data.strip, - s.prompt.right_choice.id, s.prompt.right_choice.data.strip, s.prompt_id, s.appearance_id, s.skip_reason, - s.created_at, s.updated_at, s.time_viewed] - end + self.appearances.find_each(:include => [:skip, :vote, :voter]) do |a| + # we only display skips and orphaned appearances in this csv file + unless a.vote.nil? + next + end + + #If no skip and no vote, this is an orphaned appearance + if a.skip.nil? + prompt = a.prompt + csv << [ "Orphaned Appearance", a.id, a.voter_id, a.question_id, a.prompt.left_choice.id, a.prompt.left_choice.data.strip, + a.prompt.right_choice.id, a.prompt.right_choice.data.strip, a.prompt_id, 'N/A', 'N/A', + a.created_at, a.updated_at, 'N/A', a.voter.identifier] + + else + #If this appearance belongs to a skip, show information on the skip instead + s = a.skip + prompt = s.prompt + csv << [ "Skip", s.id, s.skipper_id, s.question_id, s.prompt.left_choice.id, s.prompt.left_choice.data.strip, + s.prompt.right_choice.id, s.prompt.right_choice.data.strip, s.prompt_id, s.appearance_id, s.skip_reason, + s.created_at, s.updated_at, s.time_viewed.to_f / 1000.0 , s.skipper.identifier] + end + end end end @@ -421,9 +437,9 @@ class Question < ActiveRecord::Base if options[:redis_key].nil? raise "No :redis_key specified" end - + #The client should use blpop to listen for a key + #The client is responsible for deleting the redis key (auto expiration results failure in testing) $redis.lpush(options[:redis_key], filename) - $redis.expire(options[:redis_key], 24*60*60 * 3) #Expire in three days #TODO implement response_type == 'email' for use by customers of the API (not local) end diff --git a/app/models/visitor.rb b/app/models/visitor.rb index 742b8a3..5275344 100644 --- a/app/models/visitor.rb +++ b/app/models/visitor.rb @@ -43,6 +43,7 @@ class Visitor < ActiveRecord::Base skip_create_options = { :question_id => prompt.question_id, :prompt_id => prompt.id, :skipper_id=> self.id, :time_viewed => time_viewed, :appearance_id => @a.id} + #the most common optional reason is 'skip_reason', probably want to refactor to make time viewed an optional parameter prompt_skip = skips.create!(skip_create_options.merge(options)) end diff --git a/spec/models/question_spec.rb b/spec/models/question_spec.rb index f461c27..cc71320 100644 --- a/spec/models/question_spec.rb +++ b/spec/models/question_spec.rb @@ -62,7 +62,7 @@ describe Question do @catchup_q.save! 100.times.each do |num| - user.create_choice("visitor identifier", @catchup_q, {:data => num, :local_identifier => "exmaple"}) + user.create_choice("visitor identifier", @catchup_q, {:data => num.to_s, :local_identifier => "exmaple"}) end end it "should choose an active prompt using catchup algorithm on a large number of choices" do @@ -131,10 +131,32 @@ describe Question do end context "exporting data" do - before(:each) do + before(:all) do user = Factory.create(:user) @question = Factory.create(:aoi_question, :site => user, :creator => user.default_visitor) + @question.it_should_autoactivate_ideas = true + @question.save! + + visitor = user.visitors.find_or_create_by_identifier('visitor identifier') + 100.times.each do |num| + user.create_choice(visitor.identifier, @question, {:data => num.to_s, :local_identifier => "example creator"}) + end + + 200.times.each do |num| + @p = @question.picked_prompt + @a = user.record_appearance(visitor, @p) + + choice = rand(3) + case choice + when 0 + user.record_vote(visitor.identifier, @a.lookup, @p, rand(2), rand(1000)) + when 1 + user.record_skip(visitor.identifier, @a.lookup, @p, rand(1000)) + when 2 + #this is an orphaned appearance, so do nothing + end + end end @@ -162,27 +184,28 @@ describe Question do filename.should match /.*ideamarketplace_#{@question.id}_votes[.]csv$/ File.exists?(filename).should be_true $redis.lpop(redis_key).should == filename - - # Not specifying exact file syntax, it's likely to change frequently $redis.del(redis_key) # clean up + File.delete(filename).should be_true end it "should email question owner after completing an export, if email option set" do #TODO end - it "should export skip data to a csv file" do - filename = @question.export('skips') + it "should export non vote data to a csv file" do + filename = @question.export('non_votes') filename.should_not be nil - filename.should match /.*ideamarketplace_#{@question.id}_skips[.]csv$/ + filename.should match /.*ideamarketplace_#{@question.id}_non_votes[.]csv$/ File.exists?(filename).should be_true # Not specifying exact file syntax, it's likely to change frequently # rows = FasterCSV.read(filename) - rows.first.should include("Skip ID") + rows.first.should include("Record ID") + rows.first.should include("Record Type") rows.first.should_not include("Idea ID") + puts filename File.delete(filename).should_not be_nil @@ -199,6 +222,7 @@ describe Question do rows = FasterCSV.read(filename) rows.first.should include("Idea ID") rows.first.should_not include("Skip ID") + puts filename File.delete(filename).should_not be_nil end -- libgit2 0.21.2