From a1b580048b8df3803470780d434e2566df390911 Mon Sep 17 00:00:00 2001 From: rebuilt Date: Mon, 7 Aug 2023 16:02:59 -0700 Subject: [PATCH] chore: remove precalculated race scores. Calculate race scores on every reload --- app/models/race_score.rb | 6 - .../column/grouped_bar_column_presenter.rb | 4 + app/services/race_score_loader.rb | 114 ------------------ db/migrate/20230807222503_drop_race_scores.rb | 18 +++ 4 files changed, 22 insertions(+), 120 deletions(-) delete mode 100644 app/models/race_score.rb delete mode 100644 app/services/race_score_loader.rb create mode 100644 db/migrate/20230807222503_drop_race_scores.rb 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 e796af97..e59a9257 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/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/db/migrate/20230807222503_drop_race_scores.rb b/db/migrate/20230807222503_drop_race_scores.rb new file mode 100644 index 00000000..50ba33d2 --- /dev/null +++ b/db/migrate/20230807222503_drop_race_scores.rb @@ -0,0 +1,18 @@ +class DropRaceScores < ActiveRecord::Migration[7.0] + def up + drop_table :race_scores + end + + def down + 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