Commit 1a096cc027beab65e912dbf6dfc84022de95d6b7

Authored by Luke Baker
1 parent bfb34f2d

add API call for median votes per session

app/controllers/questions_controller.rb
@@ -84,6 +84,13 @@ class QuestionsController < InheritedResources::Base @@ -84,6 +84,13 @@ class QuestionsController < InheritedResources::Base
84 # export_format = params[:export_format] #CSV always now, could expand to xml later 84 # export_format = params[:export_format] #CSV always now, could expand to xml later
85 end 85 end
86 86
  87 + def median_votes_per_session
  88 + @question = current_user.questions.find(params[:id])
  89 + respond_to do |format|
  90 + format.xml{ render :xml => {:median => @question.median_votes_per_session}.to_xml and return}
  91 + end
  92 + end
  93 +
87 def object_info_by_visitor_id 94 def object_info_by_visitor_id
88 95
89 object_type = params[:object_type] 96 object_type = params[:object_type]
app/models/question.rb
1 class Question < ActiveRecord::Base 1 class Question < ActiveRecord::Base
2 require 'set' 2 require 'set'
  3 + include Utility
3 extend ActiveSupport::Memoizable 4 extend ActiveSupport::Memoizable
4 5
5 belongs_to :creator, :class_name => "Visitor", :foreign_key => "creator_id" 6 belongs_to :creator, :class_name => "Visitor", :foreign_key => "creator_id"
@@ -50,7 +51,17 @@ class Question &lt; ActiveRecord::Base @@ -50,7 +51,17 @@ class Question &lt; ActiveRecord::Base
50 def item_count 51 def item_count
51 choices.size 52 choices.size
52 end 53 end
53 - 54 +
  55 + # returns array of hashes where each has has voter_id and total keys
  56 + def votes_per_session
  57 + self.votes.find(:all, :select => 'voter_id, count(*) as total', :group => :voter_id).map { |v| {:voter_id => v.voter_id, :total => v.total.to_i} }
  58 + end
  59 +
  60 + def median_votes_per_session
  61 + totals = self.votes_per_session.map { |v| v[:total] }
  62 + return median(totals)
  63 + end
  64 +
54 def choose_prompt(options = {}) 65 def choose_prompt(options = {})
55 66
56 # if there is one or fewer active choices, we won't be able to find a prompt 67 # if there is one or fewer active choices, we won't be able to find a prompt
config/routes.rb
@@ -6,6 +6,7 @@ ActionController::Routing::Routes.draw do |map| @@ -6,6 +6,7 @@ ActionController::Routing::Routes.draw do |map|
6 map.resources :questions, :except => [:edit, :destroy], 6 map.resources :questions, :except => [:edit, :destroy],
7 :member => {:object_info_totals_by_date => :get, 7 :member => {:object_info_totals_by_date => :get,
8 :object_info_by_visitor_id => :get, 8 :object_info_by_visitor_id => :get,
  9 + :median_votes_per_session => :get,
9 :export => :post} , 10 :export => :post} ,
10 :collection => {:all_num_votes_by_visitor_id => :get, 11 :collection => {:all_num_votes_by_visitor_id => :get,
11 :all_object_info_totals_by_date => :get, 12 :all_object_info_totals_by_date => :get,
lib/utility.rb 0 → 100644
@@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
  1 +module Utility
  2 + def mean(array)
  3 + array.inject(0) { |sum, x| sum += x } / array.size.to_f
  4 + end
  5 +
  6 + def median(array, already_sorted=false)
  7 + return nil if array.empty?
  8 + array = array.sort unless already_sorted
  9 + m_pos = array.size / 2
  10 + return array.size % 2 == 1 ? array[m_pos] : mean(array[m_pos-1..m_pos])
  11 + end
  12 +end
spec/integration/questions_spec.rb
@@ -10,6 +10,15 @@ describe &quot;Questions&quot; do @@ -10,6 +10,15 @@ describe &quot;Questions&quot; do
10 end 10 end
11 end 11 end
12 12
  13 + describe "GET 'median_votes_per_session'" do
  14 + it "should return the median" do
  15 + Factory.create(:vote, :question => @questions.first)
  16 + get_auth median_votes_per_session_question_path(@questions.first, :format => 'xml')
  17 + response.should be_success
  18 + response.body.should have_tag("median", :text => "1")
  19 + end
  20 + end
  21 +
13 describe "GET 'index'" do 22 describe "GET 'index'" do
14 it "should return an array of questions" do 23 it "should return an array of questions" do
15 get_auth questions_path(:format => 'xml') 24 get_auth questions_path(:format => 'xml')
spec/models/question_spec.rb
@@ -22,6 +22,15 @@ describe Question do @@ -22,6 +22,15 @@ describe Question do
22 @question.choices.active.reload.size.should == 2 22 @question.choices.active.reload.size.should == 2
23 end 23 end
24 24
  25 + it "should report median votes per session" do
  26 + aoiquestion = Factory.create(:aoi_question)
  27 + prompt = aoiquestion.prompts.first
  28 + Factory.create(:vote, :question => aoiquestion, :prompt => prompt)
  29 + Factory.create(:vote, :question => aoiquestion, :prompt => prompt)
  30 + aoiquestion.votes_per_session.should == [{:voter_id => aoiquestion.creator.id, :total => 2}]
  31 + aoiquestion.median_votes_per_session.should == 2
  32 + end
  33 +
25 it "should create a new instance given valid attributes" do 34 it "should create a new instance given valid attributes" do
26 # Factory.attributes_for does not return associations, this is a good enough substitute 35 # Factory.attributes_for does not return associations, this is a good enough substitute
27 Question.create!(Factory.build(:question).attributes.symbolize_keys) 36 Question.create!(Factory.build(:question).attributes.symbolize_keys)