mirror of
https://github.com/edcommonwealth/sqm-dashboards.git
synced 2026-03-08 23:18:18 -07:00
feat: add special education disaggregation
This commit is contained in:
parent
a9b4f97a84
commit
acfdaf5587
23 changed files with 379 additions and 52 deletions
|
|
@ -30,7 +30,9 @@ export default class extends Controller {
|
|||
"&grades=" +
|
||||
this.selected_items("grade").join(",") +
|
||||
"&ells=" +
|
||||
this.selected_items("ell").join(",");
|
||||
this.selected_items("ell").join(",") +
|
||||
"&speds=" +
|
||||
this.selected_items("sped").join(",");
|
||||
|
||||
this.go_to(url);
|
||||
}
|
||||
|
|
@ -124,19 +126,11 @@ export default class extends Controller {
|
|||
return item.id;
|
||||
})[0];
|
||||
|
||||
const groups = new Map([
|
||||
['gender', 'students-by-gender'],
|
||||
['grade', 'students-by-grade'],
|
||||
['income', 'students-by-income'],
|
||||
['race', 'students-by-race'],
|
||||
['ell', 'students-by-ell'],
|
||||
])
|
||||
|
||||
if (target.name === 'slice' || target.name === 'group') {
|
||||
if (selected_slice === 'students-and-teachers') {
|
||||
return 'students-and-teachers';
|
||||
}
|
||||
return groups.get(this.selected_group());
|
||||
return `students-by-${this.selected_group()}`;
|
||||
}
|
||||
|
||||
return window.graph;
|
||||
|
|
|
|||
7
app/models/sped.rb
Normal file
7
app/models/sped.rb
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
class Sped < ApplicationRecord
|
||||
scope :by_designation, -> { all.map { |sped| [sped.designation, sped] }.to_h }
|
||||
|
||||
include FriendlyId
|
||||
|
||||
friendly_id :designation, use: [:slugged]
|
||||
end
|
||||
|
|
@ -11,6 +11,7 @@ class SurveyItemResponse < ActiveRecord::Base
|
|||
belongs_to :gender
|
||||
belongs_to :income
|
||||
belongs_to :ell
|
||||
belongs_to :sped
|
||||
|
||||
has_one :measure, through: :survey_item
|
||||
|
||||
|
|
@ -39,6 +40,11 @@ class SurveyItemResponse < ActiveRecord::Base
|
|||
academic_year:, ell:, grade: school.grades(academic_year:)).group(:survey_item).having("count(*) >= 10").average(:likert_score)
|
||||
}
|
||||
|
||||
scope :averages_for_sped, lambda { |survey_items, school, academic_year, sped|
|
||||
SurveyItemResponse.where(survey_item: survey_items, school:,
|
||||
academic_year:, sped:, grade: school.grades(academic_year:)).group(:survey_item).having("count(*) >= 10").average(:likert_score)
|
||||
}
|
||||
|
||||
scope :averages_for_race, lambda { |school, academic_year, 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:)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ module Analyze
|
|||
include Analyze::Graph::Column::EllColumn::ScoreForEll
|
||||
include Analyze::Graph::Column::EllColumn::EllCount
|
||||
def label
|
||||
%w[Not-ELL]
|
||||
["Not ELL"]
|
||||
end
|
||||
|
||||
def basis
|
||||
|
|
|
|||
34
app/presenters/analyze/graph/column/sped_column/not_sped.rb
Normal file
34
app/presenters/analyze/graph/column/sped_column/not_sped.rb
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module SpedColumn
|
||||
class NotSped < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::SpedColumn::ScoreForSped
|
||||
include Analyze::Graph::Column::SpedColumn::SpedCount
|
||||
|
||||
def label
|
||||
["Not Special", "Education"]
|
||||
end
|
||||
|
||||
def basis
|
||||
"student"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def sped
|
||||
::Sped.find_by_slug "not-special-education"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module SpedColumn
|
||||
module ScoreForSped
|
||||
def score(year_index)
|
||||
academic_year = academic_years[year_index]
|
||||
meets_student_threshold = sufficient_student_responses?(academic_year:)
|
||||
return Score::NIL_SCORE unless meets_student_threshold
|
||||
|
||||
averages = SurveyItemResponse.averages_for_sped(measure.student_survey_items, school, academic_year,
|
||||
sped)
|
||||
average = bubble_up_averages(averages:).round(2)
|
||||
|
||||
Score.new(average:,
|
||||
meets_teacher_threshold: false,
|
||||
meets_student_threshold:,
|
||||
meets_admin_data_threshold: false)
|
||||
end
|
||||
|
||||
def bubble_up_averages(averages:)
|
||||
measure.student_scales.map do |scale|
|
||||
scale.survey_items.map do |survey_item|
|
||||
averages[survey_item]
|
||||
end.remove_blanks.average
|
||||
end.remove_blanks.average
|
||||
end
|
||||
|
||||
def sufficient_student_responses?(academic_year:)
|
||||
return false unless measure.subcategory.response_rate(school:, academic_year:).meets_student_threshold?
|
||||
|
||||
yearly_counts = SurveyItemResponse.where(school:, academic_year:,
|
||||
sped:, survey_item: measure.student_survey_items).group(:sped).select(:response_id).distinct(:response_id).count
|
||||
yearly_counts.any? do |count|
|
||||
count[1] >= 10
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
34
app/presenters/analyze/graph/column/sped_column/sped.rb
Normal file
34
app/presenters/analyze/graph/column/sped_column/sped.rb
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module SpedColumn
|
||||
class Sped < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::SpedColumn::ScoreForSped
|
||||
include Analyze::Graph::Column::SpedColumn::SpedCount
|
||||
|
||||
def label
|
||||
%w[Special Education]
|
||||
end
|
||||
|
||||
def basis
|
||||
"student"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def sped
|
||||
::Sped.find_by_slug "special-education"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module SpedColumn
|
||||
module SpedCount
|
||||
def type
|
||||
:student
|
||||
end
|
||||
|
||||
def n_size(year_index)
|
||||
SurveyItemResponse.where(sped:, survey_item: measure.student_survey_items, school:, grade: grades(year_index),
|
||||
academic_year: academic_years[year_index]).select(:response_id).distinct.count
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
34
app/presenters/analyze/graph/column/sped_column/unknown.rb
Normal file
34
app/presenters/analyze/graph/column/sped_column/unknown.rb
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module SpedColumn
|
||||
class Unknown < GroupedBarColumnPresenter
|
||||
include Analyze::Graph::Column::SpedColumn::ScoreForSped
|
||||
include Analyze::Graph::Column::SpedColumn::SpedCount
|
||||
|
||||
def label
|
||||
%w[Unknown]
|
||||
end
|
||||
|
||||
def basis
|
||||
"student"
|
||||
end
|
||||
|
||||
def show_irrelevancy_message?
|
||||
false
|
||||
end
|
||||
|
||||
def show_insufficient_data_message?
|
||||
false
|
||||
end
|
||||
|
||||
def sped
|
||||
::Sped.find_by_slug "unknown"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
#
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
class StudentsByEll
|
||||
include Analyze::Graph::Column::GenderColumn
|
||||
include Analyze::Graph::Column::EllColumn
|
||||
attr_reader :ells
|
||||
|
||||
def initialize(ells:)
|
||||
|
|
|
|||
43
app/presenters/analyze/graph/students_by_sped.rb
Normal file
43
app/presenters/analyze/graph/students_by_sped.rb
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
module Graph
|
||||
class StudentsBySped
|
||||
include Analyze::Graph::Column::SpedColumn
|
||||
attr_reader :speds
|
||||
|
||||
def initialize(speds:)
|
||||
@speds = speds
|
||||
end
|
||||
|
||||
def to_s
|
||||
"Students by SpEd"
|
||||
end
|
||||
|
||||
def slug
|
||||
"students-by-sped"
|
||||
end
|
||||
|
||||
def columns
|
||||
[].tap do |array|
|
||||
speds.each do |sped|
|
||||
array << column_for_sped_code(code: sped.slug)
|
||||
end
|
||||
array << Analyze::Graph::Column::AllStudent
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def column_for_sped_code(code:)
|
||||
CFR[code]
|
||||
end
|
||||
|
||||
CFR = {
|
||||
"special-education" => Analyze::Graph::Column::SpedColumn::Sped,
|
||||
"not-special-education" => Analyze::Graph::Column::SpedColumn::NotSped,
|
||||
"unknown" => Analyze::Graph::Column::SpedColumn::Unknown
|
||||
}.freeze
|
||||
end
|
||||
end
|
||||
end
|
||||
13
app/presenters/analyze/group/sped.rb
Normal file
13
app/presenters/analyze/group/sped.rb
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
module Analyze
|
||||
module Group
|
||||
class Sped
|
||||
def name
|
||||
"SpEd"
|
||||
end
|
||||
|
||||
def slug
|
||||
"sped"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -67,6 +67,19 @@ module Analyze
|
|||
end
|
||||
end
|
||||
|
||||
def speds
|
||||
@speds ||= Sped.all.order(id: :ASC)
|
||||
end
|
||||
|
||||
def selected_speds
|
||||
@selected_speds ||= begin
|
||||
sped_params = params[:speds]
|
||||
return speds unless sped_params
|
||||
|
||||
sped_params.split(",").map { |sped| Sped.find_by_slug sped }.compact
|
||||
end
|
||||
end
|
||||
|
||||
def graphs
|
||||
@graphs ||= [Analyze::Graph::AllData.new,
|
||||
Analyze::Graph::StudentsAndTeachers.new,
|
||||
|
|
@ -74,7 +87,8 @@ module Analyze
|
|||
Analyze::Graph::StudentsByGrade.new(grades: selected_grades),
|
||||
Analyze::Graph::StudentsByGender.new(genders: selected_genders),
|
||||
Analyze::Graph::StudentsByIncome.new(incomes: selected_incomes),
|
||||
Analyze::Graph::StudentsByEll.new(ells: selected_ells)]
|
||||
Analyze::Graph::StudentsByEll.new(ells: selected_ells),
|
||||
Analyze::Graph::StudentsBySped.new(speds: selected_speds)]
|
||||
end
|
||||
|
||||
def graph
|
||||
|
|
@ -107,7 +121,7 @@ module Analyze
|
|||
|
||||
def groups
|
||||
@groups = [Analyze::Group::Ell.new, Analyze::Group::Gender.new, Analyze::Group::Grade.new, Analyze::Group::Income.new,
|
||||
Analyze::Group::Race.new]
|
||||
Analyze::Group::Race.new, Analyze::Group::Sped.new]
|
||||
end
|
||||
|
||||
def group
|
||||
|
|
|
|||
|
|
@ -7,8 +7,9 @@ class DemographicLoader
|
|||
CSV.parse(File.read(filepath), headers: true) do |row|
|
||||
process_race(row:)
|
||||
process_gender(row:)
|
||||
process_income(row:)
|
||||
process_ell(row:)
|
||||
create_from_column(column: "Income", row:, model: Income)
|
||||
create_from_column(column: "ELL", row:, model: Ell)
|
||||
create_from_column(column: "Special Ed Status", row:, model: Sped)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -33,18 +34,11 @@ class DemographicLoader
|
|||
gender.save
|
||||
end
|
||||
|
||||
def self.process_income(row:)
|
||||
designation = row["Income"]
|
||||
def self.create_from_column(column:, row:, model:)
|
||||
designation = row[column]
|
||||
return unless designation
|
||||
|
||||
Income.find_or_create_by!(designation:)
|
||||
end
|
||||
|
||||
def self.process_ell(row:)
|
||||
designation = row["ELL"]
|
||||
return unless designation
|
||||
|
||||
Ell.find_or_create_by!(designation:)
|
||||
model.find_or_create_by!(designation:)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -62,6 +62,10 @@ class SurveyResponsesDataLoader
|
|||
@ells ||= Ell.by_designation
|
||||
end
|
||||
|
||||
def speds
|
||||
@speds ||= Sped.by_designation
|
||||
end
|
||||
|
||||
def process_row(row:, rules:)
|
||||
return unless row.dese_id?
|
||||
return unless row.school.present?
|
||||
|
|
@ -91,12 +95,14 @@ class SurveyResponsesDataLoader
|
|||
grade = row.grade
|
||||
income = incomes[row.income.parameterize]
|
||||
ell = ells[row.ell]
|
||||
sped = speds[row.sped]
|
||||
if survey_item_response.present?
|
||||
survey_item_response.update!(likert_score:, grade:, gender:, recorded_date: row.recorded_date, income:, ell:)
|
||||
survey_item_response.update!(likert_score:, grade:, gender:, recorded_date: row.recorded_date, income:, ell:,
|
||||
sped:)
|
||||
[]
|
||||
else
|
||||
SurveyItemResponse.new(response_id: row.response_id, academic_year: row.academic_year, school: row.school, survey_item:,
|
||||
likert_score:, grade:, gender:, recorded_date: row.recorded_date, income:, ell:)
|
||||
likert_score:, grade:, gender:, recorded_date: row.recorded_date, income:, ell:, sped:)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -25,3 +25,7 @@
|
|||
<% @presenter.ells.each do |ell| %>
|
||||
<%= render(partial: "checkboxes", locals: {id: "ell-#{ell.slug}", item: ell, selected_items: @presenter.selected_ells, name: "ell", label_text: ell.designation}) %>
|
||||
<% end %>
|
||||
|
||||
<% @presenter.speds.each do |sped| %>
|
||||
<%= render(partial: "checkboxes", locals: {id: "sped-#{sped.slug}", item: sped, selected_items: @presenter.selected_speds, name: "sped", label_text: sped.designation}) %>
|
||||
<% end %>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue