From 61aad20cb2219f58b60879bde455f719c534f54b Mon Sep 17 00:00:00 2001 From: rebuilt Date: Tue, 14 Jun 2022 15:28:20 -0700 Subject: [PATCH] Create response rate model --- .../20220614210108_create_response_rates.rb | 17 ++ db/schema.rb | 23 ++- spec/factories.rb | 10 + spec/models/response_rate_spec.rb | 182 +----------------- 4 files changed, 50 insertions(+), 182 deletions(-) create mode 100644 db/migrate/20220614210108_create_response_rates.rb diff --git a/db/migrate/20220614210108_create_response_rates.rb b/db/migrate/20220614210108_create_response_rates.rb new file mode 100644 index 00000000..06859c82 --- /dev/null +++ b/db/migrate/20220614210108_create_response_rates.rb @@ -0,0 +1,17 @@ +class CreateResponseRates < ActiveRecord::Migration[7.0] + def change + create_table :response_rates do |t| + t.references :subcategory, null: false, foreign_key: true + t.references :school, null: false, foreign_key: true + t.references :academic_year, null: false, foreign_key: true + t.float :student_response_rate + t.float :teacher_response_rate + t.boolean :meets_student_threshold + t.boolean :meets_teacher_threshold + + t.timestamps + end + + add_index :response_rates, %i[school_id subcategory_id] + end +end diff --git a/db/schema.rb b/db/schema.rb index 9d13733a..8636cde0 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,9 +10,8 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2022_05_10_232626) do +ActiveRecord::Schema[7.0].define(version: 2022_06_14_211616) do # These are extensions that must be enabled in order to support this database - enable_extension "pg_stat_statements" enable_extension "plpgsql" create_table "academic_years", id: :serial, force: :cascade do |t| @@ -299,6 +298,22 @@ ActiveRecord::Schema[7.0].define(version: 2022_05_10_232626) do t.index ["school_id"], name: "index_respondents_on_school_id" end + create_table "response_rates", force: :cascade do |t| + t.bigint "subcategory_id", null: false + t.bigint "school_id", null: false + t.bigint "academic_year_id", null: false + t.float "student_response_rate" + t.float "teacher_response_rate" + t.boolean "meets_student_threshold" + t.boolean "meets_teacher_threshold" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["academic_year_id"], name: "index_response_rates_on_academic_year_id" + t.index ["school_id", "subcategory_id"], name: "index_response_rates_on_school_id_and_subcategory_id" + t.index ["school_id"], name: "index_response_rates_on_school_id" + t.index ["subcategory_id"], name: "index_response_rates_on_subcategory_id" + end + create_table "scales", force: :cascade do |t| t.string "scale_id", null: false t.bigint "measure_id", null: false @@ -340,6 +355,7 @@ ActiveRecord::Schema[7.0].define(version: 2022_05_10_232626) do t.datetime "updated_at", null: false t.index ["academic_year_id"], name: "index_survey_item_responses_on_academic_year_id" t.index ["response_id"], name: "index_survey_item_responses_on_response_id" + t.index ["school_id", "academic_year_id"], name: "index_survey_item_responses_on_school_id_and_academic_year_id" t.index ["school_id"], name: "index_survey_item_responses_on_school_id" t.index ["survey_item_id"], name: "index_survey_item_responses_on_survey_item_id" end @@ -380,6 +396,9 @@ ActiveRecord::Schema[7.0].define(version: 2022_05_10_232626) do add_foreign_key "measures", "subcategories" add_foreign_key "respondents", "academic_years" add_foreign_key "respondents", "schools" + add_foreign_key "response_rates", "academic_years" + add_foreign_key "response_rates", "schools" + add_foreign_key "response_rates", "subcategories" add_foreign_key "scales", "measures" add_foreign_key "schools", "districts" add_foreign_key "subcategories", "categories" diff --git a/spec/factories.rb b/spec/factories.rb index ab540c09..796eba0e 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -1,4 +1,14 @@ FactoryBot.define do + factory :response_rate do + subcategory { nil } + school { nil } + academic_year { nil } + student_response_rate { 1.5 } + teacher_response_rate { 1.5 } + meets_student_threshold { false } + meets_teacher_threshold { false } + end + factory :admin_data_value do likert_score { 1.5 } school diff --git a/spec/models/response_rate_spec.rb b/spec/models/response_rate_spec.rb index 85dada30..860c32fb 100644 --- a/spec/models/response_rate_spec.rb +++ b/spec/models/response_rate_spec.rb @@ -1,183 +1,5 @@ require 'rails_helper' -describe ResponseRate, type: :model do - let(:school) { create(:school) } - let(:academic_year) { create(:academic_year) } - - describe StudentResponseRate do - let(:subcategory) { create(:subcategory) } - let(:sufficient_measure_1) { create(:measure, subcategory:) } - let(:sufficient_scale_1) { create(:scale, measure: sufficient_measure_1) } - let(:sufficient_measure_2) { create(:measure, subcategory:) } - let(:sufficient_scale_2) { create(:scale, measure: sufficient_measure_2) } - let(:sufficient_teacher_survey_item) { create(:teacher_survey_item, scale: sufficient_scale_1) } - let(:sufficient_student_survey_item_1) { create(:student_survey_item, scale: sufficient_scale_1) } - let(:insufficient_student_survey_item_1) { create(:student_survey_item, scale: sufficient_scale_1) } - let(:sufficient_student_survey_item_2) { create(:student_survey_item, scale: sufficient_scale_2) } - - before :each do - create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD, survey_item: sufficient_teacher_survey_item, - academic_year:, school:, likert_score: 1) - create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD, survey_item: sufficient_student_survey_item_1, - academic_year:, school:, likert_score: 4) - create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD, survey_item: sufficient_student_survey_item_2, - academic_year:, school:, likert_score: 4) - end - context 'when a students take a regular survey' do - before :each do - create(:respondent, school:, academic_year:) - create(:survey, school:, academic_year:) - end - - context 'when the average number of student responses per question in a subcategory is equal to the student response threshold' do - it 'returns a response rate equal to the response threshold' do - expect(StudentResponseRate.new(subcategory:, school:, - academic_year:).rate).to eq 25 - end - end - end - - context 'when students take the short form survey' do - before :each do - create(:respondent, school:, academic_year:) - create(:survey, form: :short, school:, academic_year:) - end - - context 'when the average number of student responses per question in a subcategory is equal to the student response threshold' do - before :each do - sufficient_student_survey_item_1.update! on_short_form: true - sufficient_student_survey_item_2.update! on_short_form: true - end - - it 'returns 100 percent' do - expect(StudentResponseRate.new(subcategory:, school:, - academic_year:).rate).to eq 25 - end - - context 'for the same number of responses, if only one of the questions is a short form question, the response rate will be half' do - before do - sufficient_student_survey_item_2.update! on_short_form: false - end - - it 'returns 100 percent' do - expect(StudentResponseRate.new(subcategory:, school:, - academic_year:).rate).to eq 50 - end - end - end - end - - context 'when the average number of teacher responses is greater than the total possible responses' do - before do - create(:respondent, school:, academic_year:) - create(:survey, school:, academic_year:) - create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD * 11, survey_item: sufficient_student_survey_item_2, - academic_year:, school:, likert_score: 1) - end - it 'returns 100 percent' do - expect(StudentResponseRate.new(subcategory:, school:, - academic_year:).rate).to eq 100 - end - end - - context 'when no survey information exists for that school or year' do - it 'returns 100 percent' do - expect(StudentResponseRate.new(subcategory:, school:, academic_year:).rate).to eq 100 - end - end - - context 'when there is an imbalance in the response rate of the student items' do - context 'and one of the student items has no associated survey item responses' do - before do - create(:respondent, school:, academic_year:) - create(:survey, school:, academic_year:) - insufficient_student_survey_item_1 - end - it 'ignores the empty survey item and returns only the average response rate of student survey items with responses' do - expect(StudentResponseRate.new(subcategory:, school:, - academic_year:).rate).to eq 25 - end - end - end - end - - describe TeacherResponseRate do - let(:subcategory) { create(:subcategory) } - let(:sufficient_measure_1) { create(:measure, subcategory:) } - let(:sufficient_scale_1) { create(:scale, measure: sufficient_measure_1) } - let(:sufficient_measure_2) { create(:measure, subcategory:) } - let(:sufficient_scale_2) { create(:scale, measure: sufficient_measure_2) } - let(:sufficient_teacher_survey_item_1) { create(:teacher_survey_item, scale: sufficient_scale_1) } - let(:sufficient_teacher_survey_item_2) { create(:teacher_survey_item, scale: sufficient_scale_1) } - let(:sufficient_teacher_survey_item_3) { create(:teacher_survey_item, scale: sufficient_scale_1) } - let(:insufficient_teacher_survey_item_4) { create(:teacher_survey_item, scale: sufficient_scale_1) } - let(:sufficient_student_survey_item_1) { create(:student_survey_item, scale: sufficient_scale_1) } - - before :each do - create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD, survey_item: sufficient_teacher_survey_item_1, - academic_year:, school:, likert_score: 1) - create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD, survey_item: sufficient_teacher_survey_item_2, - academic_year:, school:, likert_score: 1) - create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD, survey_item: sufficient_student_survey_item_1, - academic_year:, school:, likert_score: 4) - end - - context 'when the average number of teacher responses per question in a subcategory is at the threshold' do - before :each do - create(:respondent, school:, academic_year:) - create(:survey, school:, academic_year:) - end - it 'returns 25 percent' do - expect(TeacherResponseRate.new(subcategory:, school:, - academic_year:).rate).to eq 25 - end - end - - context 'when the teacher response rate is not a whole number. eg 29.166%' do - before do - create(:respondent, school:, academic_year:) - create(:survey, school:, academic_year:) - create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD + 1, survey_item: sufficient_teacher_survey_item_3, - academic_year:, school:, likert_score: 1) - end - it 'it will return the nearest whole number' do - expect(TeacherResponseRate.new(subcategory:, school:, - academic_year:).rate).to eq 29 - end - end - - context 'when the average number of teacher responses is greater than the total possible responses' do - before do - create(:respondent, school:, academic_year:) - create(:survey, school:, academic_year:) - create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD * 11, survey_item: sufficient_teacher_survey_item_3, - academic_year:, school:, likert_score: 1) - end - it 'returns 100 percent' do - expect(TeacherResponseRate.new(subcategory:, school:, - academic_year:).rate).to eq 100 - end - end - - context 'when no survey information exists for that school and academic_year' do - it 'returns 100 percent' do - expect(TeacherResponseRate.new(subcategory:, school:, - academic_year:).rate).to eq 100 - end - end - - 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 - before do - create(:respondent, school:, academic_year:) - create(:survey, school:, academic_year:) - insufficient_teacher_survey_item_4 - end - it 'ignores the empty survey item and returns only the average response rate of teacher survey items with responses' do - expect(TeacherResponseRate.new(subcategory:, school:, - academic_year:).rate).to eq 25 - end - end - end - end +RSpec.describe ResponseRate, type: :model do + pending "add some examples to (or delete) #{__FILE__}" end