feat: add parent survey gauges

mciea-main
Nelson Jovel 1 year ago
parent 695f8b69a2
commit c85ddddd8d

@ -1 +1 @@
3.3.4 3.3.5

@ -24,6 +24,10 @@ class Measure < ActiveRecord::Base
@student_survey_items ||= survey_items.student_survey_items @student_survey_items ||= survey_items.student_survey_items
end end
def parent_survey_items
@parent_survey_items ||= survey_items.parent_survey_items
end
def student_survey_items_with_sufficient_responses(school:, academic_year:) def student_survey_items_with_sufficient_responses(school:, academic_year:)
@student_survey_items_with_sufficient_responses ||= Hash.new do |memo, (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") 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? @includes_admin_data_items ||= admin_data_items.length.positive?
end end
def includes_parent_survey_items?
@includes_parent_survey_items ||= parent_survey_items.length.positive?
end
def score(school:, academic_year:) def score(school:, academic_year:)
@score ||= Hash.new do |memo, (school, academic_year)| @score ||= Hash.new do |memo, (school, academic_year)|
next Score::NIL_SCORE if incalculable_score(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]] @admin_score[[school, academic_year]]
end 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 def warning_low_benchmark
1 1
end end
@ -222,4 +239,9 @@ class Measure < ActiveRecord::Base
def admin_data_averages(school:, academic_year:) def admin_data_averages(school:, academic_year:)
AdminDataValue.where(school:, academic_year:, admin_data_item: admin_data_items).pluck(:likert_score) AdminDataValue.where(school:, academic_year:, admin_data_item: admin_data_items).pluck(:likert_score)
end 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 end

@ -30,6 +30,9 @@ class SurveyItem < ActiveRecord::Base
scope :early_education_survey_items, lambda { scope :early_education_survey_items, lambda {
where("survey_items.survey_item_id LIKE '%-%-es%'") 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| scope :survey_items_for_grade, lambda { |school, academic_year, grade|
includes(:survey_item_responses) includes(:survey_item_responses)
@ -76,6 +79,8 @@ class SurveyItem < ActiveRecord::Base
return :teacher if survey_item_ids.subset? teacher_survey_items.map(&:survey_item_id).to_set 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 :standard if survey_item_ids.subset? standard_survey_items.map(&:survey_item_id).to_set
return :parent if parent_survey_items.count.positive?
:unknown :unknown
end end
end end

@ -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

@ -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 def measure_presenters
@subcategory.measures.sort_by(&:measure_id).map do |measure| @subcategory.measures.sort_by(&:measure_id).map do |measure|
MeasurePresenter.new(measure:, academic_year: @academic_year, school: @school) out = [MeasurePresenter.new(measure:, academic_year: @academic_year, school: @school)]
end if parent_gauges_have_displayable_score?(measure:)
out << ParentMeasurePresenter.new(measure:, academic_year: @academic_year,
school: @school)
end
out
end.flatten
end end
private 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 def admin_data_values_count
@subcategory.measures.map do |measure| @subcategory.measures.map do |measure|
measure.scales.map do |scale| measure.scales.map do |scale|

@ -27,7 +27,7 @@ class Cleaner
def filename(headers:, data:, filepath:) def filename(headers:, data:, filepath:)
output = [] output = []
survey_item_ids = headers.filter(&:present?).filter do |header| 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" } end.reject { |item| item.end_with? "-1" }
survey_type = SurveyItem.survey_type(survey_item_ids:) survey_type = SurveyItem.survey_type(survey_item_ids:)
range = data.first.academic_year.range range = data.first.academic_year.range
@ -129,7 +129,7 @@ class Cleaner
def remove_unwanted_headers(headers:) def remove_unwanted_headers(headers:)
headers.to_set.to_a.compact.reject do |item| headers.to_set.to_a.compact.reject do |item|
item.start_with? "Q" 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 end
def write_csv(data:, output_filepath:, filename:, prefix: "") def write_csv(data:, output_filepath:, filename:, prefix: "")
@ -152,7 +152,7 @@ class Cleaner
def survey_items(headers:) def survey_items(headers:)
survey_item_ids = headers survey_item_ids = headers
.filter(&:present?) .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) @survey_items ||= SurveyItem.where(survey_item_id: survey_item_ids)
end end

@ -208,7 +208,7 @@ class SurveyItemValues
def sanitized_headers def sanitized_headers
@sanitized_headers ||= headers.select(&:present?) @sanitized_headers ||= headers.select(&:present?)
.reject { |key, _value| key.start_with? "Q" } .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 end
def to_a def to_a
@ -228,6 +228,10 @@ class SurveyItemValues
.filter(&:present?) .filter(&:present?)
.filter { |header| header.start_with? "t-" }.count > 0 .filter { |header| header.start_with? "t-" }.count > 0
return :parent if headers
.filter(&:present?)
.filter { |header| header.start_with? "p-" }.count > 0
:student :student
end end
@ -236,7 +240,7 @@ class SurveyItemValues
end end
def survey_item_ids 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 end
def valid_duration? def valid_duration?
@ -246,6 +250,7 @@ class SurveyItemValues
return span_in_seconds >= 300 if survey_type == :teacher return span_in_seconds >= 300 if survey_type == :teacher
return span_in_seconds >= 240 if survey_type == :standard return span_in_seconds >= 240 if survey_type == :standard
return span_in_seconds >= 100 if survey_type == :short_form return span_in_seconds >= 100 if survey_type == :short_form
return span_in_seconds >= 120 if survey_type == :parent
true true
end end
@ -261,6 +266,7 @@ class SurveyItemValues
return progress >= 11 if survey_type == :standard return progress >= 11 if survey_type == :standard
return progress >= 5 if survey_type == :short_form return progress >= 5 if survey_type == :short_form
return progress >= 5 if survey_type == :early_education return progress >= 5 if survey_type == :early_education
return true if survey_type == :parent
false false
end end
@ -269,6 +275,7 @@ class SurveyItemValues
return true if grade.nil? return true if grade.nil?
return true if respondent_type == :teacher return true if respondent_type == :teacher
return true if survey_type == :parent
respondents = Respondent.where(school:, academic_year:).first respondents = Respondent.where(school:, academic_year:).first
if respondents.present? && respondents.enrollment_by_grade[grade].present? if respondents.present? && respondents.enrollment_by_grade[grade].present?
@ -283,6 +290,7 @@ class SurveyItemValues
def valid_sd? def valid_sd?
return true if survey_type == :early_education 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-") } survey_item_headers = headers.filter(&:present?).filter { |header| header.start_with?("s-", "t-") }
likert_scores = [] likert_scores = []

@ -161,7 +161,7 @@ class SurveyResponsesDataLoader
.parse(headers) .parse(headers)
.first .first
.filter(&:present?) .filter(&:present?)
.filter { |header| header.start_with?("t-", "s-") } .filter { |header| header.start_with?("t-", "s-", "p-") }
end end
end end

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")) File.open(Rails.root.join("spec", "fixtures", "raw", "sample_file_with_duplicate_headers.csv"))
end 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 let(:common_headers) do
["Recorded Date", "Dese ID", "ResponseID"] ["Recorded Date", "Dese ID", "ResponseID"]
end end
@ -82,6 +86,51 @@ RSpec.describe Cleaner do
survey_item_ids survey_item_ids
end 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 before :each do
school school
second_school second_school
@ -198,6 +247,19 @@ RSpec.describe Cleaner do
end end
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 context "when there is more than one district" do
it "adds all districts to the filename" do it "adds all districts to the filename" do
survey_items = SurveyItem.where(survey_item_id: teacher_survey_items) survey_items = SurveyItem.where(survey_item_id: teacher_survey_items)

Loading…
Cancel
Save