mirror of
https://github.com/edcommonwealth/sqm-dashboards.git
synced 2026-03-07 21:48:16 -08:00
Add scales to framework. Change calculations to first group and then
average those groupings and the way up the framework. Likert scores for a survey_item are averaged. Then all the survey_items in a scale are averaged. Then student scales in a measure are averaged. And teacher scales in a measure are averaged. Then the average of those two calculations becomes the score for a measure. Then the measures in a subcategory are averaged.
This commit is contained in:
parent
1ca88bf6d1
commit
d4df7cbc06
44 changed files with 1053 additions and 856 deletions
|
|
@ -1,11 +1,11 @@
|
|||
class AdminDataItem < ActiveRecord::Base
|
||||
belongs_to :measure
|
||||
belongs_to :scale
|
||||
|
||||
scope :for_measures, ->(measure) {
|
||||
joins(:measure).where('admin_data_items.measure': measure)
|
||||
scope :for_measures, lambda { |measures|
|
||||
joins(:scale).where('scale.measure': measures)
|
||||
}
|
||||
|
||||
scope :non_hs_items_for_measures, ->(measure) {
|
||||
scope :non_hs_items_for_measures, lambda { |measure|
|
||||
for_measures(measure).where(hs_only_item: false)
|
||||
}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,34 +1,40 @@
|
|||
class Measure < ActiveRecord::Base
|
||||
belongs_to :subcategory
|
||||
has_many :survey_items
|
||||
has_many :admin_data_items
|
||||
|
||||
has_many :scales
|
||||
has_many :admin_data_items, through: :scales
|
||||
has_many :survey_items, through: :scales
|
||||
has_many :survey_item_responses, through: :survey_items
|
||||
|
||||
def self.none_meet_threshold?(school:, academic_year:)
|
||||
none? do |measure|
|
||||
SurveyItemResponse.sufficient_data?(measure: measure, school: school, academic_year: academic_year)
|
||||
end
|
||||
def none_meet_threshold?(school:, academic_year:)
|
||||
!sufficient_data?(school:, academic_year:)
|
||||
end
|
||||
|
||||
def teacher_survey_items
|
||||
@teacher_survey_items ||= survey_items.where("survey_item_id LIKE 't-%'")
|
||||
@teacher_survey_items ||= survey_items.teacher_survey_items
|
||||
end
|
||||
|
||||
def student_survey_items
|
||||
@student_survey_items ||= survey_items.where("survey_item_id LIKE 's-%'")
|
||||
@student_survey_items ||= survey_items.student_survey_items
|
||||
end
|
||||
|
||||
def teacher_scales
|
||||
@teacher_scales ||= scales.teacher_scales
|
||||
end
|
||||
|
||||
def student_scales
|
||||
@student_scales ||= scales.student_scales
|
||||
end
|
||||
|
||||
def includes_teacher_survey_items?
|
||||
@includes_teacher_survey_items ||= teacher_survey_items.any?
|
||||
teacher_survey_items.any?
|
||||
end
|
||||
|
||||
def includes_student_survey_items?
|
||||
@includes_student_survey_items ||= student_survey_items.any?
|
||||
student_survey_items.any?
|
||||
end
|
||||
|
||||
def includes_admin_data_items?
|
||||
@includes_admin_data_items ||= admin_data_items.any?
|
||||
admin_data_items.any?
|
||||
end
|
||||
|
||||
def sources
|
||||
|
|
@ -39,32 +45,61 @@ class Measure < ActiveRecord::Base
|
|||
sources
|
||||
end
|
||||
|
||||
def score(school:, academic_year:)
|
||||
@score ||= Hash.new do |memo|
|
||||
meets_student_threshold = sufficient_student_data?(school:, academic_year:)
|
||||
meets_teacher_threshold = sufficient_teacher_data?(school:, academic_year:)
|
||||
next Score.new(nil, false, false) if !meets_student_threshold && !meets_teacher_threshold
|
||||
|
||||
scores = []
|
||||
scores << teacher_scales.map { |scale| scale.score(school:, academic_year:) }.average if meets_teacher_threshold
|
||||
scores << student_scales.map { |scale| scale.score(school:, academic_year:) }.average if meets_student_threshold
|
||||
memo[[school, academic_year]] = Score.new(scores.average, meets_teacher_threshold, meets_student_threshold)
|
||||
end
|
||||
|
||||
@score[[school, academic_year]]
|
||||
end
|
||||
|
||||
def warning_low_benchmark
|
||||
1
|
||||
end
|
||||
|
||||
def watch_low_benchmark
|
||||
return @watch_low_benchmark unless @watch_low_benchmark.nil?
|
||||
|
||||
@watch_low_benchmark = benchmark(:watch_low_benchmark)
|
||||
@watch_low_benchmark ||= benchmark(:watch_low_benchmark)
|
||||
end
|
||||
|
||||
def growth_low_benchmark
|
||||
return @growth_low_benchmark unless @growth_low_benchmark.nil?
|
||||
|
||||
@growth_low_benchmark = benchmark(:growth_low_benchmark)
|
||||
@growth_low_benchmark ||= benchmark(:growth_low_benchmark)
|
||||
end
|
||||
|
||||
def approval_low_benchmark
|
||||
return @approval_low_benchmark unless @approval_low_benchmark.nil?
|
||||
|
||||
@approval_low_benchmark = benchmark(:approval_low_benchmark)
|
||||
@approval_low_benchmark ||= benchmark(:approval_low_benchmark)
|
||||
end
|
||||
|
||||
def ideal_low_benchmark
|
||||
return @ideal_low_benchmark unless @ideal_low_benchmark.nil?
|
||||
@ideal_low_benchmark ||= benchmark(:ideal_low_benchmark)
|
||||
end
|
||||
|
||||
@ideal_low_benchmark = benchmark(:ideal_low_benchmark)
|
||||
def sufficient_student_data?(school:, academic_year:)
|
||||
return false unless includes_student_survey_items?
|
||||
|
||||
average_response_count = student_survey_items.map do |survey_item|
|
||||
survey_item.survey_item_responses.where(school:, academic_year:).count
|
||||
end.average
|
||||
average_response_count >= SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD
|
||||
end
|
||||
|
||||
def sufficient_teacher_data?(school:, academic_year:)
|
||||
return false unless includes_teacher_survey_items?
|
||||
|
||||
average_response_count = teacher_survey_items.map do |survey_item|
|
||||
survey_item.survey_item_responses.where(school:, academic_year:).count
|
||||
end.average
|
||||
average_response_count >= SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD
|
||||
end
|
||||
|
||||
def sufficient_data?(school:, academic_year:)
|
||||
sufficient_student_data?(school:, academic_year:) || sufficient_teacher_data?(school:, academic_year:)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
class ResponseRate
|
||||
module ResponseRate
|
||||
def initialize(subcategory:, school:, academic_year:)
|
||||
@subcategory = subcategory
|
||||
@school = school
|
||||
|
|
|
|||
21
app/models/scale.rb
Normal file
21
app/models/scale.rb
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
class Scale < ApplicationRecord
|
||||
belongs_to :measure
|
||||
has_many :survey_items
|
||||
has_many :admin_data_items
|
||||
|
||||
def score(school:, academic_year:)
|
||||
@score ||= Hash.new do |memo|
|
||||
memo[[school, academic_year]] = survey_items.map do |survey_item|
|
||||
survey_item.score(school:, academic_year:)
|
||||
end.average
|
||||
end
|
||||
@score[[school, academic_year]]
|
||||
end
|
||||
|
||||
scope :teacher_scales, lambda {
|
||||
where("scale_id LIKE 't-%'")
|
||||
}
|
||||
scope :student_scales, lambda {
|
||||
where("scale_id LIKE 's-%'")
|
||||
}
|
||||
end
|
||||
|
|
@ -1,17 +1,18 @@
|
|||
class StudentResponseRate < ResponseRate
|
||||
def rate
|
||||
super
|
||||
end
|
||||
class StudentResponseRate
|
||||
include ResponseRate
|
||||
|
||||
private
|
||||
|
||||
def survey_item_count
|
||||
@student_survey_item_count ||= SurveyItem.student_survey_items_for_measures(@subcategory.measures).count
|
||||
@student_survey_item_count ||= @subcategory.measures.map { |measure| measure.student_survey_items.count }.sum
|
||||
end
|
||||
|
||||
def response_count
|
||||
@student_response_count ||= SurveyItemResponse.student_responses_for_measures(@subcategory.measures, @school,
|
||||
@academic_year).count
|
||||
@student_response_count ||= @subcategory.measures.map do |measure|
|
||||
measure.student_survey_items.map do |survey_item|
|
||||
survey_item.survey_item_responses.where(school: @school, academic_year: @academic_year).count
|
||||
end.sum
|
||||
end.sum
|
||||
end
|
||||
|
||||
def total_possible_responses
|
||||
|
|
|
|||
|
|
@ -2,4 +2,12 @@ class Subcategory < ActiveRecord::Base
|
|||
belongs_to :category
|
||||
|
||||
has_many :measures
|
||||
|
||||
def score(school:, academic_year:)
|
||||
scores = measures.includes([:survey_items]).map do |measure|
|
||||
measure.score(school:, academic_year:).average
|
||||
end
|
||||
scores = scores.reject(&:nil?)
|
||||
scores.average
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,11 +1,21 @@
|
|||
class SurveyItem < ActiveRecord::Base
|
||||
belongs_to :measure
|
||||
belongs_to :scale
|
||||
has_one :measure, through: :scale
|
||||
|
||||
has_many :survey_item_responses
|
||||
|
||||
scope :student_survey_items_for_measures, lambda { |measures|
|
||||
joins(:measure).where(measure: measures).where("survey_item_id LIKE 's-%'")
|
||||
def score(school:, academic_year:)
|
||||
@score ||= Hash.new do |memo|
|
||||
memo[[school, academic_year]] = survey_item_responses.where(school:, academic_year:).average(:likert_score).to_f
|
||||
end
|
||||
|
||||
@score[[school, academic_year]]
|
||||
end
|
||||
|
||||
scope :student_survey_items, lambda {
|
||||
where("survey_item_id LIKE 's-%'")
|
||||
}
|
||||
scope :teacher_survey_items_for_measures, lambda { |measures|
|
||||
joins(:measure).where(measure: measures).where("survey_item_id LIKE 't-%'")
|
||||
scope :teacher_survey_items, lambda {
|
||||
where("survey_item_id LIKE 't-%'")
|
||||
}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,100 +5,4 @@ class SurveyItemResponse < ActiveRecord::Base
|
|||
belongs_to :academic_year
|
||||
belongs_to :school
|
||||
belongs_to :survey_item
|
||||
|
||||
def self.score_for_subcategory(subcategory:, school:, academic_year:)
|
||||
measures = measures_with_sufficient_data(subcategory: subcategory, school: school, academic_year: academic_year)
|
||||
|
||||
return nil unless measures.size.positive?
|
||||
|
||||
measures.map do |measure|
|
||||
responses_for_measure(measure: measure, school: school, academic_year: academic_year).average(:likert_score)
|
||||
end.average
|
||||
end
|
||||
|
||||
def self.measures_with_sufficient_data(subcategory:, school:, academic_year:)
|
||||
subcategory.measures.select do |measure|
|
||||
sufficient_data?(measure: measure, school: school, academic_year: academic_year)
|
||||
end
|
||||
end
|
||||
|
||||
def self.responses_for_measure(measure:, school:, academic_year:)
|
||||
meets_teacher_threshold = teacher_sufficient_data? measure: measure, school: school, academic_year: academic_year
|
||||
meets_student_threshold = student_sufficient_data? measure: measure, school: school, academic_year: academic_year
|
||||
meets_all_thresholds = meets_teacher_threshold && meets_student_threshold
|
||||
|
||||
if meets_all_thresholds
|
||||
SurveyItemResponse.for_measure(measure, school, academic_year)
|
||||
elsif meets_teacher_threshold
|
||||
SurveyItemResponse.teacher_responses_for_measure(measure, school, academic_year)
|
||||
elsif meets_student_threshold
|
||||
SurveyItemResponse.student_responses_for_measure(measure, school, academic_year)
|
||||
end
|
||||
end
|
||||
|
||||
def self.score_for_measure(measure:, school:, academic_year:)
|
||||
meets_teacher_threshold = teacher_sufficient_data? measure: measure, school: school, academic_year: academic_year
|
||||
meets_student_threshold = student_sufficient_data? measure: measure, school: school, academic_year: academic_year
|
||||
|
||||
survey_item_responses = responses_for_measure(measure: measure, school: school, academic_year: academic_year)
|
||||
|
||||
score_for_measure = survey_item_responses.average(:likert_score) unless survey_item_responses.nil?
|
||||
|
||||
Score.new(score_for_measure, meets_teacher_threshold, meets_student_threshold)
|
||||
end
|
||||
|
||||
def self.sufficient_data?(measure:, school:, academic_year:)
|
||||
meets_teacher_threshold = teacher_sufficient_data? measure: measure, school: school, academic_year: academic_year
|
||||
meets_student_threshold = student_sufficient_data? measure: measure, school: school, academic_year: academic_year
|
||||
meets_teacher_threshold || meets_student_threshold
|
||||
end
|
||||
|
||||
scope :for_measure, lambda { |measure, school, academic_year|
|
||||
joins(:survey_item)
|
||||
.where('survey_items.measure': measure)
|
||||
.where(school: school, academic_year: academic_year)
|
||||
}
|
||||
scope :for_measures, lambda { |measures, school, academic_year|
|
||||
joins(:survey_item)
|
||||
.where('survey_items.measure': measures)
|
||||
.where(school: school, academic_year: academic_year)
|
||||
}
|
||||
|
||||
scope :teacher_responses_for_measure, lambda { |measure, school, academic_year|
|
||||
for_measure(measure, school, academic_year)
|
||||
.where("survey_items.survey_item_id LIKE 't-%'")
|
||||
}
|
||||
scope :teacher_responses_for_measures, lambda { |measures, school, academic_year|
|
||||
for_measures(measures, school, academic_year)
|
||||
.where("survey_items.survey_item_id LIKE 't-%'")
|
||||
}
|
||||
scope :student_responses_for_measure, lambda { |measure, school, academic_year|
|
||||
for_measure(measure, school, academic_year)
|
||||
.where("survey_items.survey_item_id LIKE 's-%'")
|
||||
}
|
||||
|
||||
scope :student_responses_for_measures, lambda { |measures, school, academic_year|
|
||||
for_measures(measures, school, academic_year)
|
||||
.where("survey_items.survey_item_id LIKE 's-%'")
|
||||
}
|
||||
def self.student_sufficient_data?(measure:, school:, academic_year:)
|
||||
if measure.includes_student_survey_items?
|
||||
student_survey_item_responses = SurveyItemResponse.student_responses_for_measure(measure, school, academic_year)
|
||||
average_number_of_survey_item_responses = student_survey_item_responses.count / measure.student_survey_items.count
|
||||
|
||||
meets_student_threshold = average_number_of_survey_item_responses >= STUDENT_RESPONSE_THRESHOLD
|
||||
end
|
||||
!!meets_student_threshold
|
||||
end
|
||||
|
||||
def self.teacher_sufficient_data?(measure:, school:, academic_year:)
|
||||
if measure.includes_teacher_survey_items?
|
||||
teacher_survey_item_responses = SurveyItemResponse.teacher_responses_for_measure(measure, school, academic_year)
|
||||
average_number_of_survey_item_responses = teacher_survey_item_responses.count / measure.teacher_survey_items.count
|
||||
|
||||
meets_teacher_threshold = average_number_of_survey_item_responses >= TEACHER_RESPONSE_THRESHOLD
|
||||
end
|
||||
!!meets_teacher_threshold
|
||||
end
|
||||
private_class_method :measures_with_sufficient_data
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
class TeacherResponseRate < ResponseRate
|
||||
class TeacherResponseRate
|
||||
include ResponseRate
|
||||
|
||||
def rate
|
||||
cap_at_100(super)
|
||||
end
|
||||
|
|
@ -10,12 +12,15 @@ class TeacherResponseRate < ResponseRate
|
|||
end
|
||||
|
||||
def survey_item_count
|
||||
@teacher_survey_item_count ||= SurveyItem.teacher_survey_items_for_measures(@subcategory.measures).count
|
||||
@teacher_survey_item_count ||= @subcategory.measures.map { |measure| measure.teacher_survey_items.count }.sum
|
||||
end
|
||||
|
||||
def response_count
|
||||
@teacher_response_count ||= SurveyItemResponse.teacher_responses_for_measures(@subcategory.measures, @school,
|
||||
@academic_year).count
|
||||
@teacher_response_count ||= @subcategory.measures.map do |measure|
|
||||
measure.teacher_survey_items.map do |survey_item|
|
||||
survey_item.survey_item_responses.where(school: @school, academic_year: @academic_year).count
|
||||
end.sum
|
||||
end.sum
|
||||
end
|
||||
|
||||
def total_possible_responses
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue