mirror of
https://github.com/edcommonwealth/sqm-dashboards.git
synced 2026-03-07 13:38:18 -08:00
feat: add parent survey gauges
This commit is contained in:
parent
973f59c299
commit
e5e969b968
14 changed files with 307 additions and 11 deletions
|
|
@ -1 +1 @@
|
|||
3.3.4
|
||||
3.3.5
|
||||
|
|
|
|||
2
Gemfile
2
Gemfile
|
|
@ -1,5 +1,5 @@
|
|||
source "https://rubygems.org"
|
||||
ruby "3.3.4"
|
||||
ruby "3.3.5"
|
||||
|
||||
git_source(:github) do |repo_name|
|
||||
repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
|
||||
|
|
|
|||
|
|
@ -515,7 +515,7 @@ DEPENDENCIES
|
|||
watir
|
||||
|
||||
RUBY VERSION
|
||||
ruby 3.3.4p94
|
||||
ruby 3.3.5p100
|
||||
|
||||
BUNDLED WITH
|
||||
2.5.11
|
||||
|
|
|
|||
|
|
@ -24,6 +24,10 @@ class Measure < ActiveRecord::Base
|
|||
@student_survey_items ||= survey_items.student_survey_items
|
||||
end
|
||||
|
||||
def parent_survey_items
|
||||
@parent_survey_items ||= survey_items.parent_survey_items
|
||||
end
|
||||
|
||||
def student_survey_items_with_sufficient_responses(school:, academic_year:)
|
||||
@student_survey_items_with_sufficient_responses ||= Hash.new do |memo, (school, academic_year)|
|
||||
memo[[school, academic_year]] = SurveyItem.where(id: SurveyItem.joins("inner join survey_item_responses on survey_item_responses.survey_item_id = survey_items.id")
|
||||
|
|
@ -59,6 +63,10 @@ class Measure < ActiveRecord::Base
|
|||
@includes_admin_data_items ||= admin_data_items.length.positive?
|
||||
end
|
||||
|
||||
def includes_parent_survey_items?
|
||||
@includes_parent_survey_items ||= parent_survey_items.length.positive?
|
||||
end
|
||||
|
||||
def score(school:, academic_year:)
|
||||
@score ||= Hash.new do |memo, (school, academic_year)|
|
||||
next Score::NIL_SCORE if incalculable_score(school:, academic_year:)
|
||||
|
|
@ -103,6 +111,15 @@ class Measure < ActiveRecord::Base
|
|||
@admin_score[[school, academic_year]]
|
||||
end
|
||||
|
||||
def parent_score(school:, academic_year:)
|
||||
@parent_score ||= Hash.new do |memo, (school, academic_year)|
|
||||
average = parent_averages(school:, academic_year:).average.round(2)
|
||||
memo[[school, academic_year]] = scorify(average:, school:, academic_year:)
|
||||
end
|
||||
|
||||
@parent_score[[school, academic_year]]
|
||||
end
|
||||
|
||||
def warning_low_benchmark
|
||||
1
|
||||
end
|
||||
|
|
@ -222,4 +239,9 @@ class Measure < ActiveRecord::Base
|
|||
def admin_data_averages(school:, academic_year:)
|
||||
AdminDataValue.where(school:, academic_year:, admin_data_item: admin_data_items).pluck(:likert_score)
|
||||
end
|
||||
|
||||
def parent_averages(school:, academic_year:)
|
||||
SurveyItemResponse.where(school:, academic_year:,
|
||||
survey_item_id: parent_survey_items).group(:survey_item).average(:likert_score).values
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ class SurveyItem < ActiveRecord::Base
|
|||
scope :early_education_survey_items, lambda {
|
||||
where("survey_items.survey_item_id LIKE '%-%-es%'")
|
||||
}
|
||||
scope :parent_survey_items, lambda {
|
||||
where("survey_items.survey_item_id LIKE 'p-%'")
|
||||
}
|
||||
|
||||
scope :survey_items_for_grade, lambda { |school, academic_year, grade|
|
||||
includes(:survey_item_responses)
|
||||
|
|
@ -75,6 +78,8 @@ class SurveyItem < ActiveRecord::Base
|
|||
return :teacher if survey_item_ids.subset? teacher_survey_items.map(&:survey_item_id).to_set
|
||||
return :standard if survey_item_ids.subset? standard_survey_items.map(&:survey_item_id).to_set
|
||||
|
||||
return :parent if parent_survey_items.count.positive?
|
||||
|
||||
:unknown
|
||||
end
|
||||
end
|
||||
|
|
|
|||
24
app/presenters/parent_measure_presenter.rb
Normal file
24
app/presenters/parent_measure_presenter.rb
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ParentMeasurePresenter < MeasurePresenter
|
||||
def measure_id
|
||||
"#{measure.measure_id} (Parent)"
|
||||
end
|
||||
|
||||
def score_for_measure
|
||||
@measure.parent_score(school: @school, academic_year: @academic_year)
|
||||
end
|
||||
|
||||
def data_item_presenters
|
||||
[].tap do |array|
|
||||
array << parent_survey_presenter if measure.parent_survey_items.any?
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parent_survey_presenter
|
||||
ParentSurveyPresenter.new(measure_id: measure.measure_id, survey_items: measure.parent_survey_items,
|
||||
has_sufficient_data: true, school:, academic_year:)
|
||||
end
|
||||
end
|
||||
26
app/presenters/parent_survey_presenter.rb
Normal file
26
app/presenters/parent_survey_presenter.rb
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ParentSurveyPresenter < DataItemPresenter
|
||||
attr_reader :survey_items
|
||||
|
||||
def initialize(measure_id:, survey_items:, has_sufficient_data:, school:, academic_year:)
|
||||
super(measure_id:, has_sufficient_data:, school:, academic_year:)
|
||||
@survey_items = survey_items
|
||||
end
|
||||
|
||||
def title
|
||||
"Parent survey"
|
||||
end
|
||||
|
||||
def id
|
||||
"parent-survey-items-#{measure_id}"
|
||||
end
|
||||
|
||||
def reason_for_insufficiency
|
||||
"low response rate"
|
||||
end
|
||||
|
||||
def descriptions_and_availability
|
||||
survey_items.map(&:description)
|
||||
end
|
||||
end
|
||||
|
|
@ -43,12 +43,22 @@ class SubcategoryPresenter
|
|||
|
||||
def measure_presenters
|
||||
@subcategory.measures.sort_by(&:measure_id).map do |measure|
|
||||
MeasurePresenter.new(measure:, academic_year: @academic_year, school: @school)
|
||||
end
|
||||
out = [MeasurePresenter.new(measure:, academic_year: @academic_year, school: @school)]
|
||||
if parent_gauges_have_displayable_score?(measure:)
|
||||
out << ParentMeasurePresenter.new(measure:, academic_year: @academic_year,
|
||||
school: @school)
|
||||
end
|
||||
out
|
||||
end.flatten
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parent_gauges_have_displayable_score?(measure:)
|
||||
measure.includes_parent_survey_items? && measure.parent_score(school: @school,
|
||||
academic_year: @academic_year).average.positive?
|
||||
end
|
||||
|
||||
def admin_data_values_count
|
||||
@subcategory.measures.map do |measure|
|
||||
measure.scales.map do |scale|
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class Cleaner
|
|||
def filename(headers:, data:, filepath:)
|
||||
output = []
|
||||
survey_item_ids = headers.filter(&:present?).filter do |header|
|
||||
header.start_with?("s-", "t-")
|
||||
header.start_with?("s-", "t-", "p-")
|
||||
end.reject { |item| item.end_with? "-1" }
|
||||
survey_type = SurveyItem.survey_type(survey_item_ids:)
|
||||
range = data.first.academic_year.range
|
||||
|
|
@ -129,7 +129,7 @@ class Cleaner
|
|||
def remove_unwanted_headers(headers:)
|
||||
headers.to_set.to_a.compact.reject do |item|
|
||||
item.start_with? "Q"
|
||||
end.reject { |header| header.match?(/^[st]-\w*-\w*-1$/i) }
|
||||
end.reject { |header| header.match?(/^[stp]-\w*-\w*-1$/i) }
|
||||
end
|
||||
|
||||
def write_csv(data:, output_filepath:, filename:, prefix: "")
|
||||
|
|
@ -152,7 +152,7 @@ class Cleaner
|
|||
def survey_items(headers:)
|
||||
survey_item_ids = headers
|
||||
.filter(&:present?)
|
||||
.filter { |header| header.start_with? "t-", "s-" }
|
||||
.filter { |header| header.start_with? "t-", "s-", "p-" }
|
||||
@survey_items ||= SurveyItem.where(survey_item_id: survey_item_ids)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ class SurveyItemValues
|
|||
def sanitized_headers
|
||||
@sanitized_headers ||= headers.select(&:present?)
|
||||
.reject { |key, _value| key.start_with? "Q" }
|
||||
.reject { |key, _value| key.match?(/^[st]-\w*-\w*-1$/i) }
|
||||
.reject { |key, _value| key.match?(/^[stp]-\w*-\w*-1$/i) }
|
||||
end
|
||||
|
||||
def to_a
|
||||
|
|
@ -228,6 +228,10 @@ class SurveyItemValues
|
|||
.filter(&:present?)
|
||||
.filter { |header| header.start_with? "t-" }.count > 0
|
||||
|
||||
return :parent if headers
|
||||
.filter(&:present?)
|
||||
.filter { |header| header.start_with? "p-" }.count > 0
|
||||
|
||||
:student
|
||||
end
|
||||
|
||||
|
|
@ -236,7 +240,7 @@ class SurveyItemValues
|
|||
end
|
||||
|
||||
def survey_item_ids
|
||||
@survey_item_ids ||= sanitized_headers.filter { |header| header.start_with?("t-", "s-") }
|
||||
@survey_item_ids ||= sanitized_headers.filter { |header| header.start_with?("t-", "s-", "p-") }
|
||||
end
|
||||
|
||||
def valid_duration?
|
||||
|
|
@ -246,6 +250,7 @@ class SurveyItemValues
|
|||
return span_in_seconds >= 300 if survey_type == :teacher
|
||||
return span_in_seconds >= 240 if survey_type == :standard
|
||||
return span_in_seconds >= 100 if survey_type == :short_form
|
||||
return span_in_seconds >= 120 if survey_type == :parent
|
||||
|
||||
true
|
||||
end
|
||||
|
|
@ -261,6 +266,7 @@ class SurveyItemValues
|
|||
return progress >= 11 if survey_type == :standard
|
||||
return progress >= 5 if survey_type == :short_form
|
||||
return progress >= 5 if survey_type == :early_education
|
||||
return true if survey_type == :parent
|
||||
|
||||
false
|
||||
end
|
||||
|
|
@ -269,6 +275,7 @@ class SurveyItemValues
|
|||
return true if grade.nil?
|
||||
|
||||
return true if respondent_type == :teacher
|
||||
return true if survey_type == :parent
|
||||
|
||||
respondents = Respondent.where(school:, academic_year:).first
|
||||
if respondents.present? && respondents.enrollment_by_grade[grade].present?
|
||||
|
|
@ -283,6 +290,7 @@ class SurveyItemValues
|
|||
|
||||
def valid_sd?
|
||||
return true if survey_type == :early_education
|
||||
return true if survey_type == :parent
|
||||
|
||||
survey_item_headers = headers.filter(&:present?).filter { |header| header.start_with?("s-", "t-") }
|
||||
likert_scores = []
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ class SurveyResponsesDataLoader
|
|||
.parse(headers)
|
||||
.first
|
||||
.filter(&:present?)
|
||||
.filter { |header| header.start_with?("t-", "s-") }
|
||||
.filter { |header| header.start_with?("t-", "s-", "p-") }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
139
spec/fixtures/raw/sample_maynard_raw_parent_survey.csv
vendored
Normal file
139
spec/fixtures/raw/sample_maynard_raw_parent_survey.csv
vendored
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -36,6 +36,10 @@ RSpec.describe Cleaner do
|
|||
File.open(Rails.root.join("spec", "fixtures", "raw", "sample_file_with_duplicate_headers.csv"))
|
||||
end
|
||||
|
||||
let(:path_to_sample_raw_parent_file) do
|
||||
File.open(Rails.root.join("spec", "fixtures", "raw", "sample_maynard_raw_parent_survey.csv"))
|
||||
end
|
||||
|
||||
let(:common_headers) do
|
||||
["Recorded Date", "Dese ID", "ResponseID"]
|
||||
end
|
||||
|
|
@ -82,6 +86,51 @@ RSpec.describe Cleaner do
|
|||
survey_item_ids
|
||||
end
|
||||
|
||||
let(:parent_survey_items) do
|
||||
survey_item_ids = (%w[
|
||||
p-socx-q1
|
||||
p-socx-q2
|
||||
p-socx-q3
|
||||
p-socx-q4
|
||||
p-sosu-q1
|
||||
p-sosu-q2
|
||||
p-sosu-q3
|
||||
p-sosu-q4
|
||||
p-tcom-q1
|
||||
p-tcom-q2
|
||||
p-tcom-q3
|
||||
p-comm-q1
|
||||
p-comm-q2
|
||||
p-comm-q3
|
||||
p-comm-q4
|
||||
p-valm-q1
|
||||
p-valm-q2
|
||||
p-valm-q3
|
||||
p-valm-q4
|
||||
p-acpr-q1
|
||||
p-acpr-q2
|
||||
p-acpr-q3
|
||||
p-acpr-q4
|
||||
p-scrp-q3
|
||||
p-cure-q1
|
||||
p-cure-q2
|
||||
p-cure-q3
|
||||
p-cure-q4
|
||||
p-evnt-q1
|
||||
p-evnt-q2
|
||||
p-evnt-q3
|
||||
p-evnt-q4
|
||||
p-phys-q3
|
||||
p-scrp-q1
|
||||
p-scrp-q2
|
||||
] << common_headers).flatten
|
||||
|
||||
survey_item_ids.map do |survey_item_id|
|
||||
create(:survey_item, survey_item_id:)
|
||||
end
|
||||
survey_item_ids
|
||||
end
|
||||
|
||||
before :each do
|
||||
school
|
||||
second_school
|
||||
|
|
@ -198,6 +247,19 @@ RSpec.describe Cleaner do
|
|||
end
|
||||
end
|
||||
|
||||
context "when the file is based on parent survey items" do
|
||||
it "adds the survey type as parent to the filename" do
|
||||
survey_items = SurveyItem.where(survey_item_id: parent_survey_items)
|
||||
|
||||
data = [SurveyItemValues.new(row: { "Recorded Date" => recorded_date, "Dese ID" => "1_740_505" }, headers: parent_survey_items, survey_items:,
|
||||
schools: School.by_dese_id)]
|
||||
filename = Cleaner.new(input_filepath:, output_filepath:, log_filepath:).filename(
|
||||
headers: parent_survey_items, data:, filepath: nil
|
||||
)
|
||||
expect(filename).to eq "maynard.maynard-high-school.parent.2022-23.csv"
|
||||
end
|
||||
end
|
||||
|
||||
context "when there is more than one district" do
|
||||
it "adds all districts to the filename" do
|
||||
survey_items = SurveyItem.where(survey_item_id: teacher_survey_items)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue