Commit 1a096cc027beab65e912dbf6dfc84022de95d6b7
1 parent
bfb34f2d
Exists in
master
and in
1 other branch
add API call for median votes per session
Showing
6 changed files
with
50 additions
and
1 deletions
Show diff stats
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 < ActiveRecord::Base | @@ -50,7 +51,17 @@ class Question < 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, |
@@ -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 "Questions" do | @@ -10,6 +10,15 @@ describe "Questions" 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) |