From cab0a3955e7f3bd21b5c303489cca53254474d7a Mon Sep 17 00:00:00 2001 From: rebuilt Date: Wed, 25 Jun 2025 17:12:20 -0700 Subject: [PATCH] finish up adding socio economic disag filter --- app/models/benefit.rb | 2 +- app/models/education.rb | 2 +- app/models/employment.rb | 14 +++---- app/models/survey_item_response.rb | 4 ++ .../column/parent/socio_economic_status.rb | 39 +++++++++++++++++++ .../graph/parents_by_socio_economic_status.rb | 38 ++++++++++++++++++ app/presenters/analyze/presenter.rb | 7 +++- app/services/survey_responses_data_loader.rb | 9 +++++ ...425_add_socio_economic_status_to_parent.rb | 5 +++ db/schema.rb | 3 +- .../survey_responses_data_loader_spec.rb | 28 +++++++++++++ 11 files changed, 139 insertions(+), 12 deletions(-) create mode 100644 app/presenters/analyze/graph/column/parent/socio_economic_status.rb create mode 100644 app/presenters/analyze/graph/parents_by_socio_economic_status.rb create mode 100644 db/migrate/20250625201425_add_socio_economic_status_to_parent.rb diff --git a/app/models/benefit.rb b/app/models/benefit.rb index 1125eb5a..36273a25 100644 --- a/app/models/benefit.rb +++ b/app/models/benefit.rb @@ -17,7 +17,7 @@ class Benefit < ApplicationRecord puts "************************************" puts "******** ERROR **********" puts "" - puts "Error parsing Income column. '#{benefits}' is not a known value. Halting execution" + puts "Error parsing benefits column. '#{benefits}' is not a known value. Halting execution" puts "" puts "************************************" exit diff --git a/app/models/education.rb b/app/models/education.rb index 91f31981..e4275686 100644 --- a/app/models/education.rb +++ b/app/models/education.rb @@ -27,7 +27,7 @@ class Education < ApplicationRecord puts "************************************" puts "******** ERROR **********" puts "" - puts "Error parsing Income column. '#{education}' is not a known value. Halting execution" + puts "Error parsing Education column. '#{education}' is not a known value. Halting execution" puts "" puts "************************************" exit diff --git a/app/models/employment.rb b/app/models/employment.rb index 27aa7212..fb05a3f0 100644 --- a/app/models/employment.rb +++ b/app/models/employment.rb @@ -5,23 +5,23 @@ class Employment < ApplicationRecord return "Unknown" if employment.blank? or employment.nil? case employment - in /^1$/i + in /^1$|^1100$/i "Two adults with full-time employment" - in /^2$/i + in /^2$|^2100$/i "One adult with full-time employment" - in /^3$/i + in /^3$|^3100$/i "Two adults with part-time employment" - in /^4$/i + in /^4$|^4100$/i "One adult with part-time employment" - in /^5$/i + in /^5$|^5100$/i "No full-time or part-time employment" - in /^99$|^100$/i + in /^99$|^100$|^99100$/i "Unknown" else puts "************************************" puts "******** ERROR **********" puts "" - puts "Error parsing Income column. '#{employment}' is not a known value. Halting execution" + puts "Error parsing Employment column. '#{employment}' is not a known value. Halting execution" puts "" puts "************************************" exit diff --git a/app/models/survey_item_response.rb b/app/models/survey_item_response.rb index 278bbe2b..9293cb3f 100644 --- a/app/models/survey_item_response.rb +++ b/app/models/survey_item_response.rb @@ -75,6 +75,10 @@ class SurveyItemResponse < ActiveRecord::Base SurveyItemResponse.joins([parent: :languages]).where(languages: { designation: designations }, survey_item: survey_items, school:, academic_year:).group(:survey_item).average(:likert_score) } + scope :averages_for_socio_economic_status, lambda { |survey_items, school, academic_year, socio_economic_status| + SurveyItemResponse.joins(:parent).where(parent: { socio_economic_status: }, survey_item: survey_items, school:, academic_year:).group(:survey_item).average(:likert_score) + } + def self.grouped_responses(school:, academic_year:) @grouped_responses ||= Hash.new do |memo, (school, academic_year)| memo[[school, academic_year]] = diff --git a/app/presenters/analyze/graph/column/parent/socio_economic_status.rb b/app/presenters/analyze/graph/column/parent/socio_economic_status.rb new file mode 100644 index 00000000..abe29f1b --- /dev/null +++ b/app/presenters/analyze/graph/column/parent/socio_economic_status.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module Analyze + module Graph + module Column + module Parent + class SocioEconomicStatus < Base + attr_reader :socio_economic_status, :label + + def initialize(socio_economic_status:, label:, show_irrelevancy_message:) + @socio_economic_status = socio_economic_status + @label = label + @show_irrelevancy_message = show_irrelevancy_message + end + + def n_size(construct:, school:, academic_year:) + SurveyItemResponse.joins(:parent).where(parent: { socio_economic_status: }, survey_item: construct.parent_survey_items, school:, academic_year:).select(:parent_id).distinct.count + end + + def score(construct:, school:, academic_year:) + return Score::NIL_SCORE if n_size(construct:, school:, academic_year:) < 10 + + averages = SurveyItemResponse.averages_for_socio_economic_status(construct.parent_survey_items, school, academic_year, + socio_economic_status) + average = bubble_up_averages(construct:, averages:).round(2) + Score.new(average:, + meets_teacher_threshold: false, + meets_student_threshold: true, + meets_admin_data_threshold: false) + end + + def designations + language.map(&:designation) + end + end + end + end + end +end diff --git a/app/presenters/analyze/graph/parents_by_socio_economic_status.rb b/app/presenters/analyze/graph/parents_by_socio_economic_status.rb new file mode 100644 index 00000000..760ffba1 --- /dev/null +++ b/app/presenters/analyze/graph/parents_by_socio_economic_status.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module Analyze + module Graph + class ParentsBySocioEconomicStatus + def to_s + "Parents by Socio-Economic Status" + end + + def slug + "parents-by-socio-economic-status" + end + + def columns + [].tap do |array| + array << Analyze::Graph::Column::Parent::SocioEconomicStatus.new(socio_economic_status: 0, label: ["No Advantage"], show_irrelevancy_message: false) + array << Analyze::Graph::Column::Parent::SocioEconomicStatus.new(socio_economic_status: 1, label: ["Low Advantage"], show_irrelevancy_message: false) + array << Analyze::Graph::Column::Parent::SocioEconomicStatus.new(socio_economic_status: 2, label: ["Mediumn Advantage"], show_irrelevancy_message: false) + array << Analyze::Graph::Column::Parent::SocioEconomicStatus.new(socio_economic_status: 3, label: ["High Advantage"], show_irrelevancy_message: false) + + array << Analyze::Graph::Column::Parent::SocioEconomicStatus.new(socio_economic_status: [0, 1, 2, 3, nil], label: ["All Students"], show_irrelevancy_message: true) + end + end + + def source + Analyze::Source::SurveyData.new(slices: nil, graph: self) + end + + def slice + Analyze::Slice::ParentsByGroup.new(graph: self) + end + + def group + Analyze::Group::Base.new(name: "Parents By Socio-Economic Status", slug: "parents-by-socio-economic-status", graph: self) + end + end + end +end diff --git a/app/presenters/analyze/presenter.rb b/app/presenters/analyze/presenter.rb index f573596b..5f4e79aa 100644 --- a/app/presenters/analyze/presenter.rb +++ b/app/presenters/analyze/presenter.rb @@ -167,7 +167,8 @@ module Analyze "students-by-ell" => Analyze::Graph::StudentsByEll.new(ells: selected_ells), "parents-by-race" => Analyze::Graph::ParentsByRace.new, "parents-by-language" => Analyze::Graph::ParentsByLanguage.new, - "parents-by-gender" => Analyze::Graph::ParentsByGender.new } + "parents-by-gender" => Analyze::Graph::ParentsByGender.new, + "parents-by-socio-economic-status" => Analyze::Graph::ParentsBySocioEconomicStatus.new } end # The last item will per slice type will be selected as the default slice @@ -185,7 +186,9 @@ module Analyze "students-by-ell" => nil, "parents-by-race" => Analyze::Graph::ParentsByRace.new, "parents-by-language" => Analyze::Graph::ParentsByLanguage.new, - "parents-by-gender" => Analyze::Graph::ParentsByGender.new } + "parents-by-gender" => Analyze::Graph::ParentsByGender.new, + + "parents-by-socio-economic-status" => Analyze::Graph::ParentsBySocioEconomicStatus.new } end def graphs diff --git a/app/services/survey_responses_data_loader.rb b/app/services/survey_responses_data_loader.rb index ea34c9f1..93ae531a 100644 --- a/app/services/survey_responses_data_loader.rb +++ b/app/services/survey_responses_data_loader.rb @@ -52,6 +52,13 @@ class SurveyResponsesDataLoader SurveyItemResponse.import(survey_item_responses.compact.flatten, batch_size:, on_duplicate_key_update: :all) end + def socio_economic_score(education, benefits, employment) + employment_points = employment.map(&:points).sum.clamp(0, 1) + ed_points = education&.points || 0 + benefits_points = benefits&.points || 0 + ed_points + benefits_points + employment_points + end + private def schools @@ -141,7 +148,9 @@ class SurveyResponsesDataLoader tmp_employments = row.employments.map { |employment| employments[employment] }.reject(&:nil?) parent.employments.concat(tmp_employments) + parent.socio_economic_status = socio_economic_score(educations[row.education], benefits[row.benefits], tmp_employments) parent.housing = housings[row.housing] if row.housing.present? + parent.save end diff --git a/db/migrate/20250625201425_add_socio_economic_status_to_parent.rb b/db/migrate/20250625201425_add_socio_economic_status_to_parent.rb new file mode 100644 index 00000000..4f9ba9df --- /dev/null +++ b/db/migrate/20250625201425_add_socio_economic_status_to_parent.rb @@ -0,0 +1,5 @@ +class AddSocioEconomicStatusToParent < ActiveRecord::Migration[8.0] + def change + add_column :parents, :socio_economic_status, :integer, default: nil + end +end diff --git a/db/schema.rb b/db/schema.rb index 095073e0..330d6ea3 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[8.0].define(version: 2025_06_24_230522) do +ActiveRecord::Schema[8.0].define(version: 2025_06_25_201425) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" @@ -399,6 +399,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_06_24_230522) do t.bigint "housing_id" t.bigint "education_id" t.bigint "benefits_id" + t.integer "socio_economic_status" t.index ["benefits_id"], name: "index_parents_on_benefits_id" t.index ["education_id"], name: "index_parents_on_education_id" t.index ["housing_id"], name: "index_parents_on_housing_id" diff --git a/spec/services/survey_responses_data_loader_spec.rb b/spec/services/survey_responses_data_loader_spec.rb index 656f2a2d..fb2be6de 100644 --- a/spec/services/survey_responses_data_loader_spec.rb +++ b/spec/services/survey_responses_data_loader_spec.rb @@ -341,6 +341,34 @@ describe SurveyResponsesDataLoader do end end end + + describe ".socio_economic_score" do + it "returns 0 when none of the rubrics meet the standard for higher advantage" do + score = SurveyResponsesDataLoader.new.socio_economic_score(Education.new(designation: "No formal schooling completed"), Benefit.new(designation: "No"), [Employment.new(designation: "No full-time or part-time employment")]) + expect(score).to eq 0 + + score = SurveyResponsesDataLoader.new.socio_economic_score(Education.new(designation: "Unknown"), Benefit.new(designation: "Unknown"), [Employment.new(designation: "Unknown")]) + expect(score).to eq 0 + end + + it "returns 1 when one of the rubrics meets the standard for higher advantage" do + score = SurveyResponsesDataLoader.new.socio_economic_score(Education.new(designation: "Associates Degree"), Benefit.new(designation: "No"), [Employment.new(designation: "No full-time or part-time employment")]) + expect(score).to eq 1 + end + + it "returns 2 when two of the rubrics meet the standard for higher advantage" do + score = SurveyResponsesDataLoader.new.socio_economic_score(Education.new(designation: "Associates Degree"), Benefit.new(designation: "Yes"), [Employment.new(designation: "No full-time or part-time employment")]) + expect(score).to eq 2 + end + + it "returns 3 when all three of the rubrics meet the standard for higher advantage" do + score = SurveyResponsesDataLoader.new.socio_economic_score(Education.new(designation: "Associates Degree"), Benefit.new(designation: "Yes"), [Employment.new(designation: "Two adults with full-time employment")]) + expect(score).to eq 3 + + score = SurveyResponsesDataLoader.new.socio_economic_score(Education.new(designation: "Associates Degree"), Benefit.new(designation: "Yes"), [Employment.new(designation: "One adult with full-time employment"), Employment.new(designation: "Two adults with full-time employment")]) + expect(score).to eq 3 + end + end end def assigns_academic_year_to_survey_item_responses