diff --git a/CHANGELOG.md b/CHANGELOG.md index 76dc2131..4d877101 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,9 +56,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add page caching - Add counter caches - Detect the latest year for which a school has data and direct a user to that year when routing from welcome page +- Modify behavior of insufficient data indicators for admin data items. Now we show indicators in line with the admin data item descriptions to indicate which items are missing data +- Add student demographic data to the database + + `bundle exec rake data:load_students` +- Add students by group graph on analyze page +- Precalculate race scores for analyze page + + `bundle exec rake one_off:reset_race_scores` : limit which years/schools/measures/races are processed + + `bundle exec rake data:reset_race_scores` : reset all race scores ## [Unreleased] ### Added -- Modify behavior of insufficient data indicators for admin data items. Now we show indicators in line with the admin data item descriptions to indicate which items are missing data diff --git a/app/models/measure.rb b/app/models/measure.rb index 1b92437b..6b7d2bd2 100644 --- a/app/models/measure.rb +++ b/app/models/measure.rb @@ -147,7 +147,7 @@ class Measure < ActiveRecord::Base def collect_survey_item_average(survey_items:, school:, academic_year:) @collect_survey_item_average ||= Hash.new do |memo, (survey_items, school, academic_year)| averages = survey_items.map do |survey_item| - grouped_responses(school:, academic_year:)[survey_item] + grouped_responses(school:, academic_year:)[survey_item.id] end.remove_blanks memo[[survey_items, school, academic_year]] = averages.average || 0 end @@ -169,7 +169,7 @@ class Measure < ActiveRecord::Base def grouped_responses(school:, academic_year:) @grouped_responses ||= Hash.new do |memo, (school, academic_year)| memo[[school, academic_year]] = - SurveyItemResponse.where(school:, academic_year:).group(:survey_item).average(:likert_score) + SurveyItemResponse.where(school:, academic_year:).group(:survey_item_id).average(:likert_score) end @grouped_responses[[school, academic_year]] end diff --git a/app/models/race_score.rb b/app/models/race_score.rb new file mode 100644 index 00000000..a10e34fb --- /dev/null +++ b/app/models/race_score.rb @@ -0,0 +1,6 @@ +class RaceScore < ApplicationRecord + belongs_to :measure + belongs_to :school + belongs_to :academic_year + belongs_to :race +end diff --git a/app/models/race_score_calculator.rb b/app/models/race_score_calculator.rb new file mode 100644 index 00000000..0d914cb9 --- /dev/null +++ b/app/models/race_score_calculator.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class RaceScoreCalculator + include Analyze::Graph::Column::RacialScore + attr_reader :measure, :school, :academic_year, :race + + def initialize(measure:, school:, academic_year:, race:) + @measure = measure + @school = school + @academic_year = academic_year + @race = race + end + + def score + race_score(measure:, school:, academic_year:, race:) + end +end diff --git a/app/models/sample.rb b/app/models/sample.rb new file mode 100644 index 00000000..2f3a1c2c --- /dev/null +++ b/app/models/sample.rb @@ -0,0 +1,12 @@ +class Sample + attr_reader :school, :academic_year, :category, :measure, :race + + def initialize + @school = School.find_by_slug 'milford-high-school' + @academic_year = AcademicYear.last + @category = Category.find_by_category_id '1' + @subcategory = Subcategory.find_by_subcategory_id '1A' + @measure = Measure.find_by_measure_id '1A-ii' + @race = Race.find_by_qualtrics_code 1 + end +end diff --git a/app/models/student.rb b/app/models/student.rb index 2063c533..3ad482b1 100644 --- a/app/models/student.rb +++ b/app/models/student.rb @@ -1,7 +1,7 @@ class Student < ApplicationRecord has_many :survey_item_responses has_many :student_races - has_many :races, through: :student_races + has_and_belongs_to_many :races, join_table: :student_races encrypts :lasid, deterministic: true end diff --git a/app/presenters/analyze/graph/column/american_indian.rb b/app/presenters/analyze/graph/column/american_indian.rb index 2037cd53..6994c12f 100644 --- a/app/presenters/analyze/graph/column/american_indian.rb +++ b/app/presenters/analyze/graph/column/american_indian.rb @@ -4,9 +4,8 @@ module Analyze module Graph module Column class AmericanIndian < GroupedBarColumnPresenter - include Analyze::Graph::Column::RaceScore + include Analyze::Graph::Column::ScoreForRace def label - # TODO: offset labels so they don't overlap 'American Indian' end @@ -15,20 +14,13 @@ module Analyze end def show_irrelevancy_message? - # !measure.includes_student_survey_items? false end def show_insufficient_data_message? - # TODO: implement this logic. Resize messages so they are bound to their column false end - def score(year_index) - # TODO: make sure the score calculation bubble up instead of just average - race_score(measure:, school:, academic_year: academic_years[year_index], race:) - end - def race Race.find_by_qualtrics_code 1 end diff --git a/app/presenters/analyze/graph/column/asian.rb b/app/presenters/analyze/graph/column/asian.rb index 329b7646..d09a5383 100644 --- a/app/presenters/analyze/graph/column/asian.rb +++ b/app/presenters/analyze/graph/column/asian.rb @@ -4,7 +4,7 @@ module Analyze module Graph module Column class Asian < GroupedBarColumnPresenter - include Analyze::Graph::Column::RaceScore + include Analyze::Graph::Column::ScoreForRace def label 'Asian' end @@ -14,7 +14,6 @@ module Analyze end def show_irrelevancy_message? - # !measure.includes_student_survey_items? false end @@ -22,10 +21,6 @@ module Analyze false end - def score(year_index) - race_score(measure:, school:, academic_year: academic_years[year_index], race:) - end - def race Race.find_by_qualtrics_code 2 end diff --git a/app/presenters/analyze/graph/column/black.rb b/app/presenters/analyze/graph/column/black.rb index 09b68f01..742f4144 100644 --- a/app/presenters/analyze/graph/column/black.rb +++ b/app/presenters/analyze/graph/column/black.rb @@ -4,7 +4,7 @@ module Analyze module Graph module Column class Black < GroupedBarColumnPresenter - include Analyze::Graph::Column::RaceScore + include Analyze::Graph::Column::ScoreForRace def label 'Black' end @@ -14,7 +14,6 @@ module Analyze end def show_irrelevancy_message? - # !measure.includes_student_survey_items? false end @@ -22,10 +21,6 @@ module Analyze false end - def score(year_index) - race_score(measure:, school:, academic_year: academic_years[year_index], race:) - end - def race Race.find_by_qualtrics_code 3 end diff --git a/app/presenters/analyze/graph/column/hispanic.rb b/app/presenters/analyze/graph/column/hispanic.rb index c47e2821..75e98f43 100644 --- a/app/presenters/analyze/graph/column/hispanic.rb +++ b/app/presenters/analyze/graph/column/hispanic.rb @@ -4,7 +4,7 @@ module Analyze module Graph module Column class Hispanic < GroupedBarColumnPresenter - include Analyze::Graph::Column::RaceScore + include Analyze::Graph::Column::ScoreForRace def label 'Hispanic' end @@ -14,7 +14,6 @@ module Analyze end def show_irrelevancy_message? - # !measure.includes_student_survey_items? false end @@ -22,10 +21,6 @@ module Analyze false end - def score(year_index) - race_score(measure:, school:, academic_year: academic_years[year_index], race:) - end - def race Race.find_by_qualtrics_code 4 end diff --git a/app/presenters/analyze/graph/column/middle_eastern.rb b/app/presenters/analyze/graph/column/middle_eastern.rb index 2ddc0fa0..fd196afb 100644 --- a/app/presenters/analyze/graph/column/middle_eastern.rb +++ b/app/presenters/analyze/graph/column/middle_eastern.rb @@ -4,7 +4,8 @@ module Analyze module Graph module Column class MiddleEastern < GroupedBarColumnPresenter - include Analyze::Graph::Column::RaceScore + include Analyze::Graph::Column::ScoreForRace + def label 'Middle Eastern' end @@ -14,7 +15,6 @@ module Analyze end def show_irrelevancy_message? - # !measure.includes_student_survey_items? false end @@ -22,10 +22,6 @@ module Analyze false end - def score(year_index) - race_score(measure:, school:, academic_year: academic_years[year_index], race:) - end - def race Race.find_by_qualtrics_code 8 end diff --git a/app/presenters/analyze/graph/column/multiracial.rb b/app/presenters/analyze/graph/column/multiracial.rb index a423062a..d1c0e398 100644 --- a/app/presenters/analyze/graph/column/multiracial.rb +++ b/app/presenters/analyze/graph/column/multiracial.rb @@ -4,9 +4,8 @@ module Analyze module Graph module Column class Multiracial < GroupedBarColumnPresenter - include Analyze::Graph::Column::RaceScore + include Analyze::Graph::Column::ScoreForRace def label - # TODO: offset labels so they don't overlap 'Multiracial' end @@ -15,20 +14,13 @@ module Analyze end def show_irrelevancy_message? - # !measure.includes_student_survey_items? false end def show_insufficient_data_message? - # TODO: implement this logic. Resize messages so they are bound to their column false end - def score(year_index) - # TODO: make sure the score calculation bubble up instead of just average - race_score(measure:, school:, academic_year: academic_years[year_index], race:) - end - def race Race.find_by_qualtrics_code 100 end diff --git a/app/presenters/analyze/graph/column/race_score.rb b/app/presenters/analyze/graph/column/race_score.rb deleted file mode 100644 index 949f2f0b..00000000 --- a/app/presenters/analyze/graph/column/race_score.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -module Analyze - module Graph - module Column - module RaceScore - def race_score(measure:, school:, academic_year:, race:) - survey_items = measure.student_survey_items - students = StudentRace.where(race:).pluck(:student_id) - average = SurveyItemResponse.where(school:, - academic_year:, - survey_item: survey_items, - student: students) - .average(:likert_score) - Score.new(average, true, true, true) - end - end - end - end -end diff --git a/app/presenters/analyze/graph/column/racial_score.rb b/app/presenters/analyze/graph/column/racial_score.rb new file mode 100644 index 00000000..c40826f7 --- /dev/null +++ b/app/presenters/analyze/graph/column/racial_score.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +module Analyze + module Graph + module Column + module RacialScore + def race_score(measure:, school:, academic_year:, race:) + rate = response_rate(school:, academic_year:, measure:) + return Score.new(0, false, false, false) unless rate.meets_student_threshold + + survey_items = measure.student_survey_items + + students = StudentRace.where(race:).pluck(:student_id).uniq + averages = grouped_responses(school:, academic_year:, survey_items:, students:) + number_of_responses = total_responses(school:, academic_year:, students:, survey_items:) + scorify(responses: averages, number_of_responses:) + end + + private + + def grouped_responses(school:, academic_year:, survey_items:, students:) + SurveyItemResponse.where(school:, + academic_year:, + student: students, + survey_item: survey_items) + .group(:survey_item_id) + .average(:likert_score) + end + + def total_responses(school:, academic_year:, students:, survey_items:) + @total_responses ||= SurveyItemResponse.where(school:, + academic_year:, + student: students, + survey_item: survey_items).count + end + + def response_rate(school:, academic_year:, measure:) + @response_rate ||= Hash.new do |memo, (school, academic_year)| + memo[[school, academic_year]] = + ResponseRate.find_by(subcategory: measure.subcategory, school:, academic_year:) + end + + @response_rate[[school, academic_year]] + end + + def scorify(responses:, number_of_responses:) + averages = bubble_up_averages(responses:) + average = averages.average + + meets_student_threshold = sufficient_responses(averages:, number_of_responses:) + average = 0 unless meets_student_threshold + + Score.new(average, false, meets_student_threshold, false) + end + + def sufficient_responses(averages:, number_of_responses:) + total_questions = averages.count + average_num_of_responses = number_of_responses.to_f / total_questions + meets_student_threshold = average_num_of_responses >= 10 + end + + def bubble_up_averages(responses:) + measure.student_scales.map do |scale| + scale.survey_items.map do |survey_item| + responses[survey_item.id] + end.remove_blanks.average + end.remove_blanks + end + end + end + end +end diff --git a/app/presenters/analyze/graph/column/score_for_race.rb b/app/presenters/analyze/graph/column/score_for_race.rb new file mode 100644 index 00000000..c4cbb0c0 --- /dev/null +++ b/app/presenters/analyze/graph/column/score_for_race.rb @@ -0,0 +1,16 @@ +module Analyze + module Graph + module Column + module ScoreForRace + def score(year_index) + s = ::RaceScore.find_by(measure:, school:, academic_year: academic_years[year_index], race:) + average = s.average unless s.nil? + average ||= 0 + meets_student_threshold = s.meets_student_threshold? unless s.nil? + meets_student_threshold ||= false + Score.new(average, false, meets_student_threshold, false) + end + end + end + end +end diff --git a/app/presenters/analyze/graph/column/unknown.rb b/app/presenters/analyze/graph/column/unknown.rb index 9900a95b..4e1003f4 100644 --- a/app/presenters/analyze/graph/column/unknown.rb +++ b/app/presenters/analyze/graph/column/unknown.rb @@ -4,7 +4,7 @@ module Analyze module Graph module Column class Unknown < GroupedBarColumnPresenter - include Analyze::Graph::Column::RaceScore + include Analyze::Graph::Column::ScoreForRace def label 'Unknown' end @@ -14,7 +14,6 @@ module Analyze end def show_irrelevancy_message? - # !measure.includes_student_survey_items? false end @@ -22,10 +21,6 @@ module Analyze false end - def score(year_index) - race_score(measure:, school:, academic_year: academic_years[year_index], race:) - end - def race Race.find_by_qualtrics_code 99 end diff --git a/app/presenters/analyze/graph/column/white.rb b/app/presenters/analyze/graph/column/white.rb index c2d75cbf..ba9e8cfd 100644 --- a/app/presenters/analyze/graph/column/white.rb +++ b/app/presenters/analyze/graph/column/white.rb @@ -4,7 +4,7 @@ module Analyze module Graph module Column class White < GroupedBarColumnPresenter - include Analyze::Graph::Column::RaceScore + include Analyze::Graph::Column::ScoreForRace def label 'White' end @@ -14,7 +14,6 @@ module Analyze end def show_irrelevancy_message? - # !measure.includes_student_survey_items? false end @@ -22,10 +21,6 @@ module Analyze false end - def score(year_index) - race_score(measure:, school:, academic_year: academic_years[year_index], race:) - end - def race Race.find_by_qualtrics_code 5 end diff --git a/app/services/race_score_loader.rb b/app/services/race_score_loader.rb new file mode 100644 index 00000000..b2d0af37 --- /dev/null +++ b/app/services/race_score_loader.rb @@ -0,0 +1,29 @@ +class RaceScoreLoader + def self.reset(schools: School.all, academic_years: AcademicYear.all, measures: Measure.all, races: Race.all) + RaceScore.where(school: schools, academic_year: academic_years, measure: measures, race: races).delete_all + scores = [] + measures.each do |measure| + schools.each do |school| + academic_years.each do |academic_year| + races.each do |race| + scores << process_score(measure:, school:, academic_year:, race:) + end + end + end + end + RaceScore.import scores, batch_size: 1000 + end + + private + + def self.process_score(measure:, school:, academic_year:, race:) + score = RaceScoreCalculator.new(measure:, school:, academic_year:, race:).score + rs = RaceScore.find_by(measure:, school:, academic_year:, race:) + rs ||= RaceScore.new(measure:, school:, academic_year:, race:) + rs.average = score.average + rs.meets_student_threshold = score.meets_student_threshold? + rs + end + + private_class_method :process_score +end diff --git a/db/migrate/20220809213959_create_race_scores.rb b/db/migrate/20220809213959_create_race_scores.rb new file mode 100644 index 00000000..48f06990 --- /dev/null +++ b/db/migrate/20220809213959_create_race_scores.rb @@ -0,0 +1,14 @@ +class CreateRaceScores < ActiveRecord::Migration[7.0] + def change + create_table :race_scores do |t| + t.references :measure, null: false, foreign_key: true + t.references :school, null: false, foreign_key: true + t.references :academic_year, null: false, foreign_key: true + t.references :race, null: false, foreign_key: true + t.float :average + t.boolean :meets_student_threshold + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 66e147de..e4a876c1 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2022_07_28_232445) do +ActiveRecord::Schema[7.0].define(version: 2022_08_09_213959) do # These are extensions that must be enabled in order to support this database enable_extension "pg_stat_statements" enable_extension "plpgsql" @@ -290,6 +290,21 @@ ActiveRecord::Schema[7.0].define(version: 2022_07_28_232445) do t.index ["subcategory_id"], name: "index_measures_on_subcategory_id" end + create_table "race_scores", force: :cascade do |t| + t.bigint "measure_id", null: false + t.bigint "school_id", null: false + t.bigint "academic_year_id", null: false + t.bigint "race_id", null: false + t.float "average" + t.boolean "meets_student_threshold" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["academic_year_id"], name: "index_race_scores_on_academic_year_id" + t.index ["measure_id"], name: "index_race_scores_on_measure_id" + t.index ["race_id"], name: "index_race_scores_on_race_id" + t.index ["school_id"], name: "index_race_scores_on_school_id" + end + create_table "races", force: :cascade do |t| t.string "designation" t.integer "qualtrics_code" @@ -428,6 +443,10 @@ ActiveRecord::Schema[7.0].define(version: 2022_07_28_232445) do add_foreign_key "legacy_school_categories", "legacy_categories", column: "category_id" add_foreign_key "legacy_school_categories", "legacy_schools", column: "school_id" add_foreign_key "measures", "subcategories" + add_foreign_key "race_scores", "academic_years" + add_foreign_key "race_scores", "measures" + add_foreign_key "race_scores", "races" + add_foreign_key "race_scores", "schools" add_foreign_key "respondents", "academic_years" add_foreign_key "respondents", "schools" add_foreign_key "response_rates", "academic_years" diff --git a/lib/tasks/data.rake b/lib/tasks/data.rake index 159f7559..a5c07cc9 100644 --- a/lib/tasks/data.rake +++ b/lib/tasks/data.rake @@ -57,6 +57,14 @@ namespace :data do puts "=====================> Completed loading #{ResponseRate.count} survey responses" end + desc 'reset race score calculations' + task reset_race_scores: :environment do + puts 'Resetting race scores' + RaceScoreLoader.reset + Rails.cache.clear + puts "=====================> Completed loading #{RaceScore.count} survey responses" + end + desc 'load admin_data' task load_admin_data: :environment do Dir.glob(Rails.root.join('data', 'admin_data', '*.csv')).each do |filepath| diff --git a/lib/tasks/one_off.rake b/lib/tasks/one_off.rake index e1627eac..1ae8864b 100644 --- a/lib/tasks/one_off.rake +++ b/lib/tasks/one_off.rake @@ -106,6 +106,14 @@ namespace :one_off do puts "=====================> Completed recalculating #{ResponseRate.count} response rates" end + desc 'reset race score calculations' + task reset_race_scores: :environment do + puts 'Resetting race scores' + RaceScoreLoader.reset(academic_years: [AcademicYear.find_by_range('2021-22')]) + Rails.cache.clear + puts "=====================> Completed loading #{RaceScore.count} survey responses" + end + desc 'list scales that have no survey responses' task list_scales_that_lack_survey_responses: :environment do output = AcademicYear.all.map do |academic_year| diff --git a/spec/factories.rb b/spec/factories.rb index f8fe292e..71cf2155 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -1,4 +1,18 @@ FactoryBot.define do + factory :race_score do + measure { nil } + school { nil } + academic_year { nil } + race { nil } + average { 1.5 } + meets_student_threshold { false } + end + + factory :student do + response_id { "ID#{rand}" } + lasid { "Lasid#{rand}" } + end + factory :student_race do student { nil } race { nil } @@ -81,11 +95,13 @@ FactoryBot.define do measure_id { rand.to_s } name { 'A Measure' } subcategory - # trait :with_student_survey_items do - # after(:create) do |measure| - # measure.survey_items << build_list(:student_survey_item, 2) - # end - # end + trait :with_student_survey_items do + after(:create) do |measure| + create(:student_scale, measure:) do |scale| + create_list(:student_survey_item, 2, scale:) + end + end + end end factory :scale do diff --git a/spec/models/race_score_calulator_spec.rb b/spec/models/race_score_calulator_spec.rb new file mode 100644 index 00000000..180ee0f1 --- /dev/null +++ b/spec/models/race_score_calulator_spec.rb @@ -0,0 +1,41 @@ +require 'rails_helper' + +describe RaceScoreCalculator do + let(:measure) { create(:measure, :with_student_survey_items) } + let(:school) { create(:school) } + let(:academic_year) { create(:academic_year) } + let(:race) { create(:race) } + let(:student) do + s = create(:student) + s.races << race + s.save + s + end + let(:survey_item_1) { measure.survey_items[0] } + let(:survey_item_2) { measure.survey_items[1] } + let(:survey_item_3) { measure.survey_items[2] } + let(:response_rate) do + create(:response_rate, school:, academic_year:, subcategory: measure.subcategory, meets_student_threshold: true) + end + + context 'when survey item responses exist' do + before :each do + response_rate + create(:survey_item_response, school:, academic_year:, likert_score: 1, survey_item: survey_item_1, student:) + create_list(:survey_item_response, 8, school:, academic_year:, likert_score: 2, survey_item: survey_item_1, + student:) + create(:survey_item_response, school:, academic_year:, likert_score: 3, survey_item: survey_item_1, student:) + + create(:survey_item_response, school:, academic_year:, likert_score: 2, survey_item: survey_item_2, student:) + create_list(:survey_item_response, 8, school:, academic_year:, likert_score: 3, survey_item: survey_item_2, + student:) + create(:survey_item_response, school:, academic_year:, likert_score: 4, survey_item: survey_item_2, student:) + end + + it 'returns a list of averages' do + expect(measure.student_survey_items.count).to eq 2 + american_indian_score = RaceScoreCalculator.new(measure:, school:, academic_year:, race:).score + expect(american_indian_score).to eq Score.new(2.5, false, true, false) + end + end +end diff --git a/spec/presenters/analyze/graph/racial_score_spec.rb b/spec/presenters/analyze/graph/racial_score_spec.rb new file mode 100644 index 00000000..9d3d9a25 --- /dev/null +++ b/spec/presenters/analyze/graph/racial_score_spec.rb @@ -0,0 +1,46 @@ +require 'rails_helper' + +include Analyze::Graph::Column + +# RacialScore is a module used in the RaceScoreCalculator class +describe RacialScore do + let(:measure) { create(:measure, :with_student_survey_items) } + let(:school) { create(:school) } + let(:academic_year) { create(:academic_year) } + let(:race) { create(:race) } + let(:student) do + s = create(:student) + s.races << race + s.save + s + end + let(:survey_item_1) { measure.survey_items[0] } + let(:survey_item_2) { measure.survey_items[1] } + let(:survey_item_3) { measure.survey_items[2] } + let(:response_rate) do + create(:response_rate, school:, academic_year:, subcategory: measure.subcategory, meets_student_threshold: true) + end + + context 'when survey item responses exist' do + before :each do + response_rate + create(:survey_item_response, school:, academic_year:, likert_score: 1, survey_item: survey_item_1, student:) + create_list(:survey_item_response, 8, school:, academic_year:, likert_score: 2, survey_item: survey_item_1, + student:) + create(:survey_item_response, school:, academic_year:, likert_score: 3, survey_item: survey_item_1, student:) + + create(:survey_item_response, school:, academic_year:, likert_score: 2, survey_item: survey_item_2, student:) + create_list(:survey_item_response, 8, school:, academic_year:, likert_score: 3, survey_item: survey_item_2, + student:) + create(:survey_item_response, school:, academic_year:, likert_score: 4, survey_item: survey_item_2, student:) + end + + it 'returns a list of averages' do + expect(measure.student_survey_items.count).to eq 2 + students = StudentRace.where(race:).pluck(:student_id) + + american_indian_score = RaceScoreCalculator.new(measure:, school:, academic_year:, race:).score + expect(american_indian_score).to eq Score.new(2.5, false, true, false) + end + end +end diff --git a/spec/services/race_score_loader_spec.rb b/spec/services/race_score_loader_spec.rb new file mode 100644 index 00000000..f55edb2a --- /dev/null +++ b/spec/services/race_score_loader_spec.rb @@ -0,0 +1,50 @@ +require 'rails_helper' + +describe RaceScoreLoader do + let(:measure) { create(:measure, :with_student_survey_items) } + let(:school) { create(:school) } + let(:academic_year) { create(:academic_year) } + let(:race) { create(:race) } + let(:student) do + s = create(:student) + s.races << race + s.save + s + end + let(:survey_item_1) { measure.survey_items[0] } + let(:survey_item_2) { measure.survey_items[1] } + let(:survey_item_3) { measure.survey_items[2] } + let(:response_rate) do + create(:response_rate, school:, academic_year:, subcategory: measure.subcategory, meets_student_threshold: true) + end + + context 'when survey item responses exist' do + before :each do + response_rate + create(:survey_item_response, school:, academic_year:, likert_score: 1, survey_item: survey_item_1, student:) + create_list(:survey_item_response, 8, school:, academic_year:, likert_score: 2, survey_item: survey_item_1, + student:) + create(:survey_item_response, school:, academic_year:, likert_score: 3, survey_item: survey_item_1, student:) + + create(:survey_item_response, school:, academic_year:, likert_score: 2, survey_item: survey_item_2, student:) + create_list(:survey_item_response, 8, school:, academic_year:, likert_score: 3, survey_item: survey_item_2, + student:) + create(:survey_item_response, school:, academic_year:, likert_score: 4, survey_item: survey_item_2, student:) + RaceScoreLoader.reset + end + + it 'returns a list of averages' do + expect(measure.student_survey_items.count).to eq 2 + american_indian_score = RaceScore.find_by(measure:, school:, academic_year:, race:) + expect(american_indian_score.average).to eq 2.5 + expect(american_indian_score.meets_student_threshold).to eq true + end + + it 'is idempotent' do + original_count = RaceScore.count + RaceScoreLoader.reset + new_count = RaceScore.count + expect(original_count).to eq new_count + end + end +end