feat: Round up response rate. Survey response rates above 24.5 will now meet sufficiency

rpp-main
rebuilt 2 years ago
parent 8cf9b4eeb8
commit e58ea3f1af

@ -18,7 +18,7 @@ class ResponseRateCalculator
return 0 unless total_possible_responses.positive? return 0 unless total_possible_responses.positive?
cap_at_one_hundred(raw_response_rate) cap_at_one_hundred(raw_response_rate).round
end end
def meets_student_threshold? def meets_student_threshold?

@ -1,4 +1,4 @@
require 'rails_helper' require "rails_helper"
describe ResponseRateCalculator, type: :model do describe ResponseRateCalculator, type: :model do
let(:school) { create(:school) } let(:school) { create(:school) }
@ -17,65 +17,65 @@ describe ResponseRateCalculator, type: :model do
let(:sufficient_student_survey_item_2) { create(:student_survey_item, scale: sufficient_scale_2) } let(:sufficient_student_survey_item_2) { create(:student_survey_item, scale: sufficient_scale_2) }
let(:sufficient_student_survey_item_3) { create(:student_survey_item, scale: sufficient_scale_2) } let(:sufficient_student_survey_item_3) { create(:student_survey_item, scale: sufficient_scale_2) }
context '.raw_response_rate' do context ".raw_response_rate" do
context 'when no survey item responses exist' do context "when no survey item responses exist" do
before do before do
create(:respondent, school:, academic_year:, pk: 20) create(:respondent, school:, academic_year:, pk: 20)
end end
it 'returns an average of the response rates for all grades' do it "returns an average of the response rates for all grades" do
expect(StudentResponseRateCalculator.new(subcategory:, school:, academic_year:).rate).to eq 0 expect(StudentResponseRateCalculator.new(subcategory:, school:, academic_year:).rate).to eq 0
end end
context 'or when the count of survey items does not meet the minimum threshold' do context "or when the count of survey items does not meet the minimum threshold" do
before do before do
create_list(:survey_item_response, 9, survey_item: sufficient_student_survey_item_1, academic_year:, create_list(:survey_item_response, 9, survey_item: sufficient_student_survey_item_1, academic_year:,
school:, grade: 1) school:, grade: 1)
end end
it 'returns an average of the response rates for all grades' do it "returns an average of the response rates for all grades" do
expect(StudentResponseRateCalculator.new(subcategory:, school:, academic_year:).rate).to eq 0 expect(StudentResponseRateCalculator.new(subcategory:, school:, academic_year:).rate).to eq 0
end end
end end
end end
context 'when at least one survey item has sufficient responses' do context "when at least one survey item has sufficient responses" do
before do before do
create(:respondent, school:, academic_year:, total_students: 20, one: 20) create(:respondent, school:, academic_year:, total_students: 20, one: 20)
create_list(:survey_item_response, 10, survey_item: sufficient_student_survey_item_1, academic_year:, create_list(:survey_item_response, 10, survey_item: sufficient_student_survey_item_1, academic_year:,
school:, grade: 1) school:, grade: 1)
end end
context 'and half of students responded' do context "and half of students responded" do
it 'reports a response rate of fifty percent' do it "reports a response rate of fifty percent" do
expect(StudentResponseRateCalculator.new(subcategory:, school:, expect(StudentResponseRateCalculator.new(subcategory:, school:,
academic_year:).rate).to eq 50 academic_year:).rate).to eq 50
end end
end end
context 'and another unrelated subcategory has responses' do context "and another unrelated subcategory has responses" do
before do before do
create_list(:survey_item_response, 10, create_list(:survey_item_response, 10,
survey_item: second_subcategory.measures.first.scales.first.survey_items.first, academic_year:, school:, grade: 1) survey_item: second_subcategory.measures.first.scales.first.survey_items.first, academic_year:, school:, grade: 1)
end end
it 'does not count the responses for the unrelated subcategory' do it "does not count the responses for the unrelated subcategory" do
expect(StudentResponseRateCalculator.new(subcategory:, school:, expect(StudentResponseRateCalculator.new(subcategory:, school:,
academic_year:).rate).to eq 50 academic_year:).rate).to eq 50
end end
end end
context 'there are responses for another survey item but not enough to meet the minimum threshold' do context "there are responses for another survey item but not enough to meet the minimum threshold" do
before do before do
less_than_a_quarter_of_respondents_for_first_grade = 4 less_than_a_quarter_of_respondents_for_first_grade = 4
create_list(:survey_item_response, less_than_a_quarter_of_respondents_for_first_grade, survey_item: insufficient_student_survey_item_1, academic_year:, create_list(:survey_item_response, less_than_a_quarter_of_respondents_for_first_grade, survey_item: insufficient_student_survey_item_1, academic_year:,
school:, grade: 1) school:, grade: 1)
end end
it 'returns an average of the response rates for all grades' do it "returns an average of the response rates for all grades" do
expect(StudentResponseRateCalculator.new(subcategory:, school:, academic_year:).rate).to eq 50 expect(StudentResponseRateCalculator.new(subcategory:, school:, academic_year:).rate).to eq 50
end end
end end
end end
context 'when two survey items have sufficient responses' do context "when two survey items have sufficient responses" do
before do before do
create(:respondent, school:, academic_year:, total_students: 20, one: 20) create(:respondent, school:, academic_year:, total_students: 20, one: 20)
create_list(:survey_item_response, 10, survey_item: sufficient_student_survey_item_1, academic_year:, create_list(:survey_item_response, 10, survey_item: sufficient_student_survey_item_1, academic_year:,
@ -84,25 +84,37 @@ describe ResponseRateCalculator, type: :model do
school:, grade: 1) school:, grade: 1)
end end
context 'one one question got half the students to respond and the other got all the students to respond' do context "and the response rate is a decimal number" do
it 'reports a response rate that averages fifty and 100' do before do
create_list(:survey_item_response, 1, survey_item: sufficient_student_survey_item_1, academic_year:,
school:, grade: 1)
end
it "rounds the response rate to the nearest whole number" do
expect(StudentResponseRateCalculator.new(subcategory:, school:,
academic_year:).rate).to eq 78
end
end
context "one one question got half the students to respond and the other got all the students to respond" do
it "reports a response rate that averages fifty and 100" do
expect(StudentResponseRateCalculator.new(subcategory:, school:, academic_year:).rate).to eq 75 expect(StudentResponseRateCalculator.new(subcategory:, school:, academic_year:).rate).to eq 75
end end
end end
context 'and another unrelated subcategory has responses' do context "and another unrelated subcategory has responses" do
before do before do
create_list(:survey_item_response, 10, create_list(:survey_item_response, 10,
survey_item: second_subcategory.measures.first.scales.first.survey_items.first, academic_year:, school:, grade: 1) survey_item: second_subcategory.measures.first.scales.first.survey_items.first, academic_year:, school:, grade: 1)
end end
it 'does not count the responses for the unrelated subcategory' do it "does not count the responses for the unrelated subcategory" do
expect(StudentResponseRateCalculator.new(subcategory:, school:, academic_year:).rate).to eq 75 expect(StudentResponseRateCalculator.new(subcategory:, school:, academic_year:).rate).to eq 75
end end
end end
end end
context 'when there survey items between two scales' do context "when there survey items between two scales" do
before do before do
create(:respondent, school:, academic_year:, total_students: 20, one: 20) create(:respondent, school:, academic_year:, total_students: 20, one: 20)
create_list(:survey_item_response, 20, survey_item: sufficient_student_survey_item_1, academic_year:, create_list(:survey_item_response, 20, survey_item: sufficient_student_survey_item_1, academic_year:,
@ -113,26 +125,26 @@ describe ResponseRateCalculator, type: :model do
school:, grade: 1) school:, grade: 1)
end end
context 'one scale got all students to respond and another scale got an average response rate of fifty percent' do context "one scale got all students to respond and another scale got an average response rate of fifty percent" do
it 'computes the response rate by dividing the actual responses over possible responses' do it "computes the response rate by dividing the actual responses over possible responses" do
# (20 + 15 + 10) / (20 + 20 + 20) * 100 = 75% # (20 + 15 + 10) / (20 + 20 + 20) * 100 = 75%
expect(StudentResponseRateCalculator.new(subcategory:, school:, academic_year:).rate).to eq 75 expect(StudentResponseRateCalculator.new(subcategory:, school:, academic_year:).rate).to eq 75
end end
end end
context 'and another unrelated subcategory has responses' do context "and another unrelated subcategory has responses" do
before do before do
create_list(:survey_item_response, 10, create_list(:survey_item_response, 10,
survey_item: second_subcategory.measures.first.scales.first.survey_items.first, academic_year:, school:, grade: 1) survey_item: second_subcategory.measures.first.scales.first.survey_items.first, academic_year:, school:, grade: 1)
end end
it 'does not count the responses for the unrelated subcategory' do it "does not count the responses for the unrelated subcategory" do
expect(StudentResponseRateCalculator.new(subcategory:, school:, academic_year:).rate).to eq 75 expect(StudentResponseRateCalculator.new(subcategory:, school:, academic_year:).rate).to eq 75
end end
end end
end end
context 'when two grades have sufficient responses' do context "when two grades have sufficient responses" do
context 'and half of one grade responded and all of the other grade responded' do context "and half of one grade responded and all of the other grade responded" do
before do before do
create(:respondent, school:, academic_year:, total_students: 20, one: 20, two: 20) create(:respondent, school:, academic_year:, total_students: 20, one: 20, two: 20)
create_list(:survey_item_response, 10, survey_item: sufficient_student_survey_item_1, academic_year:, create_list(:survey_item_response, 10, survey_item: sufficient_student_survey_item_1, academic_year:,
@ -140,12 +152,12 @@ describe ResponseRateCalculator, type: :model do
create_list(:survey_item_response, 20, survey_item: sufficient_student_survey_item_1, academic_year:, create_list(:survey_item_response, 20, survey_item: sufficient_student_survey_item_1, academic_year:,
school:, grade: 2) school:, grade: 2)
end end
it 'reports a response rate that averages fifty and 100' do it "reports a response rate that averages fifty and 100" do
expect(StudentResponseRateCalculator.new(subcategory:, school:, academic_year:).rate).to eq 75 expect(StudentResponseRateCalculator.new(subcategory:, school:, academic_year:).rate).to eq 75
end end
end end
context 'and two grades responded to different questions at different rates' do context "and two grades responded to different questions at different rates" do
before do before do
create(:respondent, school:, academic_year:, total_students: 20, one: 20, two: 20) create(:respondent, school:, academic_year:, total_students: 20, one: 20, two: 20)
create_list(:survey_item_response, 10, survey_item: sufficient_student_survey_item_1, academic_year:, create_list(:survey_item_response, 10, survey_item: sufficient_student_survey_item_1, academic_year:,
@ -153,13 +165,13 @@ describe ResponseRateCalculator, type: :model do
create_list(:survey_item_response, 20, survey_item: sufficient_student_survey_item_2, academic_year:, create_list(:survey_item_response, 20, survey_item: sufficient_student_survey_item_2, academic_year:,
school:, grade: 2) school:, grade: 2)
end end
it 'reports a response rate that averages fifty and 100' do it "reports a response rate that averages fifty and 100" do
expect(StudentResponseRateCalculator.new(subcategory:, school:, academic_year:).rate).to eq 75 expect(StudentResponseRateCalculator.new(subcategory:, school:, academic_year:).rate).to eq 75
end end
end end
end end
context 'when two grades have different numbers of students' do context "when two grades have different numbers of students" do
before do before do
create(:respondent, school:, academic_year:, total_students: 60, one: 40, two: 20) create(:respondent, school:, academic_year:, total_students: 60, one: 40, two: 20)
create_list(:survey_item_response, 20, survey_item: sufficient_student_survey_item_1, academic_year:, create_list(:survey_item_response, 20, survey_item: sufficient_student_survey_item_1, academic_year:,
@ -167,13 +179,13 @@ describe ResponseRateCalculator, type: :model do
create_list(:survey_item_response, 15, survey_item: sufficient_student_survey_item_2, academic_year:, create_list(:survey_item_response, 15, survey_item: sufficient_student_survey_item_2, academic_year:,
school:, grade: 2) # 75% school:, grade: 2) # 75%
end end
it 'weights the average response rate by the number of students in each grade' do it "weights the average response rate by the number of students in each grade" do
expect(StudentResponseRateCalculator.new(subcategory:, school:, expect(StudentResponseRateCalculator.new(subcategory:, school:,
academic_year:).rate).to be_within(0.01).of(58.333333) academic_year:).rate).to be_within(0.01).of(58)
end end
end end
context 'when three grades have different numbers of students' do context "when three grades have different numbers of students" do
before do before do
create(:respondent, school:, academic_year:, total_students: 120, one: 40, two: 20, three: 60) create(:respondent, school:, academic_year:, total_students: 120, one: 40, two: 20, three: 60)
create_list(:survey_item_response, 20, survey_item: sufficient_student_survey_item_1, academic_year:, create_list(:survey_item_response, 20, survey_item: sufficient_student_survey_item_1, academic_year:,
@ -183,38 +195,38 @@ describe ResponseRateCalculator, type: :model do
create_list(:survey_item_response, 15, survey_item: sufficient_student_survey_item_2, academic_year:, create_list(:survey_item_response, 15, survey_item: sufficient_student_survey_item_2, academic_year:,
school:, grade: 3) # 25% school:, grade: 3) # 25%
end end
it 'weights the average response rate by the number of students in each grade' do it "weights the average response rate by the number of students in each grade" do
expect(StudentResponseRateCalculator.new(subcategory:, school:, expect(StudentResponseRateCalculator.new(subcategory:, school:,
academic_year:).rate).to be_within(0.01).of(41.6666) academic_year:).rate).to be_within(0.01).of(42)
end end
end end
context 'when one grade gets surveyed but another does not, the grade that does not get surveyed is not counted' do context "when one grade gets surveyed but another does not, the grade that does not get surveyed is not counted" do
before do before do
create(:respondent, school:, academic_year:, total_students: 20, one: 20, two: 20) create(:respondent, school:, academic_year:, total_students: 20, one: 20, two: 20)
create_list(:survey_item_response, 10, survey_item: sufficient_student_survey_item_1, academic_year:, create_list(:survey_item_response, 10, survey_item: sufficient_student_survey_item_1, academic_year:,
school:, grade: 1) school:, grade: 1)
end end
it 'reports a response rate that averages fifty and 100' do it "reports a response rate that averages fifty and 100" do
expect(StudentResponseRateCalculator.new(subcategory:, school:, academic_year:).rate).to eq 50 expect(StudentResponseRateCalculator.new(subcategory:, school:, academic_year:).rate).to eq 50
end end
end end
end end
context 'when the average number of student responses is greater than the total possible responses' do context "when the average number of student responses is greater than the total possible responses" do
before do before do
create(:respondent, school:, academic_year:, total_students: 20, one: 20, two: 20) create(:respondent, school:, academic_year:, total_students: 20, one: 20, two: 20)
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD * 11, survey_item: sufficient_student_survey_item_2, create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD * 11, survey_item: sufficient_student_survey_item_2,
academic_year:, school:, likert_score: 1, grade: 1) academic_year:, school:, likert_score: 1, grade: 1)
end end
it 'returns 100 percent' do it "returns 100 percent" do
expect(StudentResponseRateCalculator.new(subcategory:, school:, expect(StudentResponseRateCalculator.new(subcategory:, school:,
academic_year:).rate).to eq 100 academic_year:).rate).to eq 100
end end
end end
context 'when no survey information exists for that school or year' do context "when no survey information exists for that school or year" do
it 'returns 100 percent' do it "returns 100 percent" do
expect(StudentResponseRateCalculator.new(subcategory:, school:, academic_year:).rate).to eq 100 expect(StudentResponseRateCalculator.new(subcategory:, school:, academic_year:).rate).to eq 100
end end
end end
@ -242,54 +254,54 @@ describe ResponseRateCalculator, type: :model do
academic_year:, school:, likert_score: 4) academic_year:, school:, likert_score: 4)
end end
context 'when the average number of teacher responses per question in a subcategory is at the threshold' do context "when the average number of teacher responses per question in a subcategory is at the threshold" do
before :each do before :each do
respondent respondent
end end
it 'returns 25 percent' do it "returns 25 percent" do
expect(TeacherResponseRateCalculator.new(subcategory:, school:, expect(TeacherResponseRateCalculator.new(subcategory:, school:,
academic_year:).rate).to eq 25 academic_year:).rate).to eq 25
end end
end end
context 'when the teacher response rate is not a whole number. eg 29.166%' do context "when the teacher response rate is not a whole number. eg 29.166%" do
before do before do
respondent respondent
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD + 1, survey_item: sufficient_teacher_survey_item_3, create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD + 1, survey_item: sufficient_teacher_survey_item_3,
academic_year:, school:, likert_score: 1) academic_year:, school:, likert_score: 1)
end end
it 'it will return the nearest whole number' do it "it will return the nearest whole number" do
expect(TeacherResponseRateCalculator.new(subcategory:, school:, expect(TeacherResponseRateCalculator.new(subcategory:, school:,
academic_year:).rate).to eq 29 academic_year:).rate).to eq 29
end end
end end
context 'when the average number of teacher responses is greater than the total possible responses' do context "when the average number of teacher responses is greater than the total possible responses" do
before do before do
respondent respondent
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD * 11, survey_item: sufficient_teacher_survey_item_3, create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD * 11, survey_item: sufficient_teacher_survey_item_3,
academic_year:, school:, likert_score: 1) academic_year:, school:, likert_score: 1)
end end
it 'returns 100 percent' do it "returns 100 percent" do
expect(TeacherResponseRateCalculator.new(subcategory:, school:, expect(TeacherResponseRateCalculator.new(subcategory:, school:,
academic_year:).rate).to eq 100 academic_year:).rate).to eq 100
end end
end end
context 'when no survey information exists for that school and academic_year' do context "when no survey information exists for that school and academic_year" do
it 'returns 100 percent' do it "returns 100 percent" do
expect(TeacherResponseRateCalculator.new(subcategory:, school:, expect(TeacherResponseRateCalculator.new(subcategory:, school:,
academic_year:).rate).to eq 100 academic_year:).rate).to eq 100
end end
end end
context 'when there is an imbalance in the response rate of the teacher items' do context "when there is an imbalance in the response rate of the teacher items" do
context 'and one of the teacher items has no associated survey item responses' do context "and one of the teacher items has no associated survey item responses" do
before do before do
respondent respondent
insufficient_teacher_survey_item_4 insufficient_teacher_survey_item_4
end end
it 'ignores the empty survey item and returns only the average response rate of teacher survey items with responses' do it "ignores the empty survey item and returns only the average response rate of teacher survey items with responses" do
expect(TeacherResponseRateCalculator.new(subcategory:, school:, expect(TeacherResponseRateCalculator.new(subcategory:, school:,
academic_year:).rate).to eq 25 academic_year:).rate).to eq 25
end end

Loading…
Cancel
Save