diff --git a/app/models/race_score.rb b/app/models/race_score.rb deleted file mode 100644 index a10e34fb..00000000 --- a/app/models/race_score.rb +++ /dev/null @@ -1,6 +0,0 @@ -class RaceScore < ApplicationRecord - belongs_to :measure - belongs_to :school - belongs_to :academic_year - belongs_to :race -end diff --git a/app/presenters/analyze/graph/column/grouped_bar_column_presenter.rb b/app/presenters/analyze/graph/column/grouped_bar_column_presenter.rb index 97fc65ac..e43c677f 100644 --- a/app/presenters/analyze/graph/column/grouped_bar_column_presenter.rb +++ b/app/presenters/analyze/graph/column/grouped_bar_column_presenter.rb @@ -20,6 +20,10 @@ module Analyze @number_of_columns = number_of_columns end + def academic_year_for_year_index(year_index) + academic_years[year_index] + end + def score(year_index) measure.score(school:, academic_year: academic_years[year_index]) || 0 end diff --git a/app/presenters/analyze/graph/column/score_for_race.rb b/app/presenters/analyze/graph/column/score_for_race.rb index 207e198f..36bee8a5 100644 --- a/app/presenters/analyze/graph/column/score_for_race.rb +++ b/app/presenters/analyze/graph/column/score_for_race.rb @@ -3,16 +3,64 @@ module Analyze module Column module ScoreForRace def score(year_index) - s = ::RaceScore.find_by(measure:, school:, academic_year: academic_years[year_index], race:) - average = s.average.round(2) unless s.nil? - average ||= 0 - meets_student_threshold = s.meets_student_threshold? unless s.nil? - meets_student_threshold ||= false - Score.new(average:, - meets_teacher_threshold: false, - meets_student_threshold:, + academic_year = academic_year_for_year_index(year_index) + rate = response_rate(school:, academic_year:, measure:) + return Score::NIL_SCORE unless rate.meets_student_threshold + + survey_items = measure.student_survey_items + + averages = grouped_responses(school:, academic_year:, survey_items:, race:) + meets_student_threshold = sufficient_responses(school:, academic_year:, race:) + scorify(responses: averages, meets_student_threshold:, measure:) + end + + def grouped_responses(school:, academic_year:, survey_items:, race:) + @grouped_responses ||= Hash.new do |memo, (school, academic_year, survey_items, race)| + memo[[school, academic_year, survey_items, race]] = + SurveyItemResponse.joins("JOIN student_races on survey_item_responses.student_id = student_races.student_id JOIN students on students.id = student_races.student_id").where( + school:, academic_year:, grade: school.grades(academic_year:) + ).where("student_races.race_id": race.id).group(:survey_item_id).having("count(*) >= 10").average(:likert_score) + end + + @grouped_responses[[school, academic_year, survey_items, race]] + end + + def response_rate(school:, academic_year:, measure:) + subcategory = measure.subcategory + @response_rate ||= Hash.new do |memo, (school, academic_year, subcategory)| + memo[[school, academic_year, subcategory]] = subcategory.response_rate(school:, academic_year:) + end + + @response_rate[[school, academic_year, subcategory]] + end + + def scorify(responses:, meets_student_threshold:, measure:) + averages = bubble_up_averages(responses:, measure:) + average = averages.average.round(2) + + average = 0 unless meets_student_threshold + + Score.new(average:, meets_teacher_threshold: false, meets_student_threshold:, meets_admin_data_threshold: false) end + + def sufficient_responses(school:, academic_year:, race:) + @sufficient_responses ||= Hash.new do |memo, (school, academic_year, race)| + number_of_students_for_a_racial_group = SurveyItemResponse.joins("JOIN student_races on survey_item_responses.student_id = student_races.student_id JOIN students on students.id = student_races.student_id").where( + school:, academic_year: + ).where("student_races.race_id": race.id).distinct.pluck(:student_id).count + memo[[school, academic_year, race]] = number_of_students_for_a_racial_group >= 10 + end + @sufficient_responses[[school, academic_year, race]] + end + + def bubble_up_averages(responses:, measure:) + 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 diff --git a/app/presenters/analyze/presenter.rb b/app/presenters/analyze/presenter.rb index 9e5492d0..391b69a1 100644 --- a/app/presenters/analyze/presenter.rb +++ b/app/presenters/analyze/presenter.rb @@ -137,12 +137,6 @@ module Analyze end.keys end - def race_score_timestamp - score = RaceScore.where(school: @school, - academic_year: @academic_year).order(updated_at: :DESC).first || Today.new - score.updated_at - end - def incomes @incomes ||= Income.all end diff --git a/app/services/race_score_loader.rb b/app/services/race_score_loader.rb deleted file mode 100644 index 7f23b382..00000000 --- a/app/services/race_score_loader.rb +++ /dev/null @@ -1,114 +0,0 @@ -class RaceScoreLoader - def self.reset(schools: School.all, academic_years: AcademicYear.all, measures: Measure.all, races: Race.all, fast_processing: true) - RaceScore.where(school: schools, academic_year: academic_years, measure: measures, race: races).delete_all - measures.each do |measure| - if fast_processing - large_memory_use(measure:, schools:, academic_years:, races:) - else - slow_loading_time(measure:, schools:, academic_years:, races:) - end - end - end - - private - - def self.large_memory_use(measure:, schools:, academic_years:, races:) - loadable_race_scores = schools.map do |school| - academic_years.map do |academic_year| - races.map do |race| - process_score(measure:, school:, academic_year:, race:) - end - end - end - RaceScore.import(loadable_race_scores.flatten.compact, batch_size: 1_000, on_duplicate_key_update: :all) - end - - def self.slow_loading_time(measure:, schools:, academic_years:, races:) - schools.each do |school| - loadable_race_scores = academic_years.map do |academic_year| - races.map do |race| - process_score(measure:, school:, academic_year:, race:) - end - end - RaceScore.import(loadable_race_scores.flatten.compact, batch_size: 1_000, on_duplicate_key_update: :all) - @grouped_responses = nil - @response_rate = nil - @sufficient_responses = nil - end - end - - def self.process_score(measure:, school:, academic_year:, race:) - score = race_score(measure:, school:, academic_year:, race:) - { measure_id: measure.id, school_id: school.id, academic_year_id: academic_year.id, race_id: race.id, average: score.average, - meets_student_threshold: score.meets_student_threshold? } - end - - def self.race_score(measure:, school:, academic_year:, race:) - rate = response_rate(school:, academic_year:, measure:) - unless rate.meets_student_threshold - return Score.new(average: 0, meets_teacher_threshold: false, meets_student_threshold: false, - meets_admin_data_threshold: false) - end - - survey_items = measure.student_survey_items - - averages = grouped_responses(school:, academic_year:, survey_items:, race:) - meets_student_threshold = sufficient_responses(school:, academic_year:, race:) - scorify(responses: averages, meets_student_threshold:, measure:) - end - - def self.grouped_responses(school:, academic_year:, survey_items:, race:) - @grouped_responses ||= Hash.new do |memo, (school, academic_year, survey_items, race)| - memo[[school, academic_year, survey_items, race]] = - SurveyItemResponse.joins("JOIN student_races on survey_item_responses.student_id = student_races.student_id JOIN students on students.id = student_races.student_id").where( - school:, academic_year:, grade: school.grades(academic_year:) - ).where("student_races.race_id": race.id).group(:survey_item_id).having("count(*) >= 10").average(:likert_score) - end - - @grouped_responses[[school, academic_year, survey_items, race]] - end - - def self.response_rate(school:, academic_year:, measure:) - subcategory = measure.subcategory - @response_rate ||= Hash.new do |memo, (school, academic_year, subcategory)| - memo[[school, academic_year, subcategory]] = subcategory.response_rate(school:, academic_year:) - end - - @response_rate[[school, academic_year, subcategory]] - end - - def self.scorify(responses:, meets_student_threshold:, measure:) - averages = bubble_up_averages(responses:, measure:) - average = averages.average - - average = 0 unless meets_student_threshold - - Score.new(average:, meets_teacher_threshold: false, meets_student_threshold:, meets_admin_data_threshold: false) - end - - def self.sufficient_responses(school:, academic_year:, race:) - @sufficient_responses ||= Hash.new do |memo, (school, academic_year, race)| - number_of_students_for_a_racial_group = SurveyItemResponse.joins("JOIN student_races on survey_item_responses.student_id = student_races.student_id JOIN students on students.id = student_races.student_id").where( - school:, academic_year: - ).where("student_races.race_id": race.id).distinct.pluck(:student_id).count - memo[[school, academic_year, race]] = number_of_students_for_a_racial_group >= 10 - end - @sufficient_responses[[school, academic_year, race]] - end - - def self.bubble_up_averages(responses:, measure:) - 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 - - private_class_method :process_score - private_class_method :race_score - private_class_method :grouped_responses - private_class_method :response_rate - private_class_method :scorify - private_class_method :sufficient_responses - private_class_method :bubble_up_averages -end diff --git a/app/views/analyze/index.html.erb b/app/views/analyze/index.html.erb index 63ec8a1f..d7704a5b 100644 --- a/app/views/analyze/index.html.erb +++ b/app/views/analyze/index.html.erb @@ -13,7 +13,7 @@ <%= render partial: "school_years", locals: {available_academic_years: @presenter.academic_years, selected_academic_years: @presenter.selected_academic_years, district: @district, school: @school, academic_year: @academic_year, category: @presenter.category, subcategory: @presenter.subcategory, measures: @presenter.measures, graph: @presenter.graph} %> <%= render partial: "data_filters", locals: {district: @district, school: @school, academic_year: @academic_year, category: @presenter.category, subcategory: @presenter.subcategory} %> - <% cache [@presenter.subcategory, @school, @presenter.selected_academic_years, @presenter.graph, @presenter.selected_races, @presenter.race_score_timestamp, @presenter.selected_grades, @presenter.grades, @presenter.selected_genders, @presenter.genders] do %> + <% cache [@presenter.subcategory, @school, @presenter.selected_academic_years, @presenter.graph, @presenter.selected_races, @presenter.selected_grades, @presenter.grades, @presenter.selected_genders, @presenter.genders] do %>