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.
pull/1/head
rebuilt 4 years ago
parent 1ca88bf6d1
commit d4df7cbc06

@ -26,10 +26,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Bump ruby version to 3.1.0 - Bump ruby version to 3.1.0
- Add previous year data. - Add previous year data.
remove AcademicYear "2021-22" - Add scale to framework. Calculations for scores bubble up through the framework.
`ay = AcademicYear.find_by_range("2021-22") `
`ay.destroy`
seed database with new AcademicYear
`bundle exec rake db:seed`
load previous year survey responses
`bundle exec rake data:load_survey_responses`

@ -7,7 +7,7 @@ class OverviewController < SqmApplicationController
private private
def presenter_for_measure(measure) def presenter_for_measure(measure)
score = SurveyItemResponse.score_for_measure(measure: measure, school: @school, academic_year: @academic_year) score = measure.score(school: @school, academic_year: @academic_year)
VarianceChartRowPresenter.new(measure: measure, score: score) VarianceChartRowPresenter.new(measure: measure, score: score)
end end

@ -16,7 +16,7 @@ class SqmApplicationController < ApplicationController
@schools = School.includes([:district]).where(district: @district).order(:name) @schools = School.includes([:district]).where(district: @district).order(:name)
@academic_year = AcademicYear.find_by_range params[:year] @academic_year = AcademicYear.find_by_range params[:year]
@academic_years = AcademicYear.all.order(range: :desc) @academic_years = AcademicYear.all.order(range: :desc)
@has_empty_dataset = Measure.none_meet_threshold? school: @school, academic_year: @academic_year @has_empty_dataset = Measure.all.all? do |measure| measure.none_meet_threshold? school: @school, academic_year: @academic_year end
end end
def district_slug def district_slug

@ -3,7 +3,7 @@ require 'csv'
class Seeder class Seeder
def seed_academic_years(*academic_year_ranges) def seed_academic_years(*academic_year_ranges)
academic_year_ranges.each do |range| academic_year_ranges.each do |range|
AcademicYear.find_or_create_by! range: range AcademicYear.find_or_create_by! range:
end end
end end
@ -89,8 +89,11 @@ class Seeder
measure.save! measure.save!
data_item_id = row['Survey Item ID'].strip data_item_id = row['Survey Item ID'].strip
scale_id = data_item_id.split('-')[0..1].join('-')
scale = Scale.find_or_create_by! scale_id: scale_id, measure: measure
if %w[Teachers Students].include? row['Source'] if %w[Teachers Students].include? row['Source']
survey_item = SurveyItem.find_or_create_by! survey_item_id: data_item_id, measure: measure survey_item = SurveyItem.where(survey_item_id: data_item_id, scale:).first_or_create
survey_item.watch_low_benchmark = watch_low if watch_low survey_item.watch_low_benchmark = watch_low if watch_low
survey_item.growth_low_benchmark = growth_low if growth_low survey_item.growth_low_benchmark = growth_low if growth_low
survey_item.approval_low_benchmark = approval_low if approval_low survey_item.approval_low_benchmark = approval_low if approval_low
@ -99,7 +102,7 @@ class Seeder
end end
if row['Source'] == 'Admin Data' if row['Source'] == 'Admin Data'
admin_data_item = AdminDataItem.find_or_create_by! admin_data_item_id: data_item_id, measure: measure admin_data_item = AdminDataItem.where(admin_data_item_id: data_item_id, scale:).first_or_create
admin_data_item.watch_low_benchmark = watch_low if watch_low admin_data_item.watch_low_benchmark = watch_low if watch_low
admin_data_item.growth_low_benchmark = growth_low if growth_low admin_data_item.growth_low_benchmark = growth_low if growth_low
admin_data_item.approval_low_benchmark = approval_low if approval_low admin_data_item.approval_low_benchmark = approval_low if approval_low

@ -1,11 +1,11 @@
class AdminDataItem < ActiveRecord::Base class AdminDataItem < ActiveRecord::Base
belongs_to :measure belongs_to :scale
scope :for_measures, ->(measure) { scope :for_measures, lambda { |measures|
joins(:measure).where('admin_data_items.measure': measure) 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) for_measures(measure).where(hs_only_item: false)
} }
end end

@ -1,34 +1,40 @@
class Measure < ActiveRecord::Base class Measure < ActiveRecord::Base
belongs_to :subcategory belongs_to :subcategory
has_many :survey_items has_many :scales
has_many :admin_data_items has_many :admin_data_items, through: :scales
has_many :survey_items, through: :scales
has_many :survey_item_responses, through: :survey_items has_many :survey_item_responses, through: :survey_items
def self.none_meet_threshold?(school:, academic_year:) def none_meet_threshold?(school:, academic_year:)
none? do |measure| !sufficient_data?(school:, academic_year:)
SurveyItemResponse.sufficient_data?(measure: measure, school: school, academic_year: academic_year)
end
end end
def teacher_survey_items def teacher_survey_items
@teacher_survey_items ||= survey_items.where("survey_item_id LIKE 't-%'") @teacher_survey_items ||= survey_items.teacher_survey_items
end end
def student_survey_items 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 end
def includes_teacher_survey_items? def includes_teacher_survey_items?
@includes_teacher_survey_items ||= teacher_survey_items.any? teacher_survey_items.any?
end end
def includes_student_survey_items? def includes_student_survey_items?
@includes_student_survey_items ||= student_survey_items.any? student_survey_items.any?
end end
def includes_admin_data_items? def includes_admin_data_items?
@includes_admin_data_items ||= admin_data_items.any? admin_data_items.any?
end end
def sources def sources
@ -39,32 +45,61 @@ class Measure < ActiveRecord::Base
sources sources
end 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 def warning_low_benchmark
1 1
end end
def watch_low_benchmark 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 end
def growth_low_benchmark 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 end
def approval_low_benchmark 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 end
def ideal_low_benchmark def ideal_low_benchmark
return @ideal_low_benchmark unless @ideal_low_benchmark.nil? @ideal_low_benchmark ||= benchmark(:ideal_low_benchmark)
end
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
@ideal_low_benchmark = benchmark(:ideal_low_benchmark) def sufficient_data?(school:, academic_year:)
sufficient_student_data?(school:, academic_year:) || sufficient_teacher_data?(school:, academic_year:)
end end
private private

@ -1,4 +1,4 @@
class ResponseRate module ResponseRate
def initialize(subcategory:, school:, academic_year:) def initialize(subcategory:, school:, academic_year:)
@subcategory = subcategory @subcategory = subcategory
@school = school @school = school

@ -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 class StudentResponseRate
def rate include ResponseRate
super
end
private private
def survey_item_count 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 end
def response_count def response_count
@student_response_count ||= SurveyItemResponse.student_responses_for_measures(@subcategory.measures, @school, @student_response_count ||= @subcategory.measures.map do |measure|
@academic_year).count 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 end
def total_possible_responses def total_possible_responses

@ -2,4 +2,12 @@ class Subcategory < ActiveRecord::Base
belongs_to :category belongs_to :category
has_many :measures 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 end

@ -1,11 +1,21 @@
class SurveyItem < ActiveRecord::Base class SurveyItem < ActiveRecord::Base
belongs_to :measure belongs_to :scale
has_one :measure, through: :scale
has_many :survey_item_responses has_many :survey_item_responses
scope :student_survey_items_for_measures, lambda { |measures| def score(school:, academic_year:)
joins(:measure).where(measure: measures).where("survey_item_id LIKE 's-%'") @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| scope :teacher_survey_items, lambda {
joins(:measure).where(measure: measures).where("survey_item_id LIKE 't-%'") where("survey_item_id LIKE 't-%'")
} }
end end

@ -5,100 +5,4 @@ class SurveyItemResponse < ActiveRecord::Base
belongs_to :academic_year belongs_to :academic_year
belongs_to :school belongs_to :school
belongs_to :survey_item 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 end

@ -1,4 +1,6 @@
class TeacherResponseRate < ResponseRate class TeacherResponseRate
include ResponseRate
def rate def rate
cap_at_100(super) cap_at_100(super)
end end
@ -10,12 +12,15 @@ class TeacherResponseRate < ResponseRate
end end
def survey_item_count 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 end
def response_count def response_count
@teacher_response_count ||= SurveyItemResponse.teacher_responses_for_measures(@subcategory.measures, @school, @teacher_response_count ||= @subcategory.measures.map do |measure|
@academic_year).count 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 end
def total_possible_responses def total_possible_responses

@ -1,6 +1,6 @@
class GaugePresenter class GaugePresenter
def initialize(scale:, score:) def initialize(zones:, score:)
@scale = scale @zones = zones
@score = score @score = score
end end
@ -17,7 +17,7 @@ class GaugePresenter
end end
def key_benchmark_percentage def key_benchmark_percentage
percentage_for @scale.approval_zone.low_benchmark percentage_for @zones.approval_zone.low_benchmark
end end
def boundary_percentage_for(zone) def boundary_percentage_for(zone)
@ -38,31 +38,31 @@ class GaugePresenter
private private
def watch_low_boundary def watch_low_boundary
percentage_for @scale.watch_zone.low_benchmark percentage_for @zones.watch_zone.low_benchmark
end end
def growth_low_boundary def growth_low_boundary
percentage_for @scale.growth_zone.low_benchmark percentage_for @zones.growth_zone.low_benchmark
end end
def approval_low_boundary def approval_low_boundary
percentage_for @scale.approval_zone.low_benchmark percentage_for @zones.approval_zone.low_benchmark
end end
def ideal_low_boundary def ideal_low_boundary
percentage_for @scale.ideal_zone.low_benchmark percentage_for @zones.ideal_zone.low_benchmark
end end
def zone def zone
@scale.zone_for_score(@score) @zones.zone_for_score(@score)
end end
def percentage_for(number) def percentage_for(number)
return nil if number.nil? return nil if number.nil?
scale_minimum = @scale.warning_zone.low_benchmark zones_minimum = @zones.warning_zone.low_benchmark
scale_maximum = @scale.ideal_zone.high_benchmark zones_maximum = @zones.ideal_zone.high_benchmark
(number - scale_minimum) / (scale_maximum - scale_minimum) (number - zones_minimum) / (zones_maximum - zones_minimum)
end end
end end

@ -18,7 +18,7 @@ class MeasurePresenter
end end
def gauge_presenter def gauge_presenter
GaugePresenter.new(scale: scale, score: score_for_measure.average) GaugePresenter.new(zones:, score: score_for_measure.average)
end end
def data_item_accordion_id def data_item_accordion_id
@ -45,11 +45,11 @@ class MeasurePresenter
private private
def score_for_measure def score_for_measure
@score ||= SurveyItemResponse.score_for_measure(measure: @measure, academic_year: @academic_year, school: @school) @score ||= @measure.score(school: @school, academic_year: @academic_year)
end end
def scale def zones
Scale.new( Zones.new(
watch_low_benchmark: @measure.watch_low_benchmark, watch_low_benchmark: @measure.watch_low_benchmark,
growth_low_benchmark: @measure.growth_low_benchmark, growth_low_benchmark: @measure.growth_low_benchmark,
approval_low_benchmark: @measure.approval_low_benchmark, approval_low_benchmark: @measure.approval_low_benchmark,

@ -1,9 +1,9 @@
class SubcategoryCardPresenter class SubcategoryCardPresenter
attr_reader :name attr_reader :name
def initialize(name:, scale:, score:) def initialize(name:, zones:, score:)
@name = name @name = name
@scale = scale @zones = zones
@score = score @score = score
end end
@ -22,6 +22,6 @@ class SubcategoryCardPresenter
private private
def zone def zone
@scale.zone_for_score(@score) @zones.zone_for_score(@score)
end end
end end

@ -22,16 +22,15 @@ class SubcategoryPresenter
end end
def gauge_presenter def gauge_presenter
GaugePresenter.new(scale:, score: average_score) GaugePresenter.new(zones:, score: average_score)
end end
def subcategory_card_presenter def subcategory_card_presenter
SubcategoryCardPresenter.new(name: @subcategory.name, scale:, score: average_score) SubcategoryCardPresenter.new(name: @subcategory.name, zones:, score: average_score)
end end
def average_score def average_score
@average_score ||= SurveyItemResponse.score_for_subcategory(subcategory: @subcategory, school: @school, @average_score ||= @subcategory.score(school: @school, academic_year: @academic_year)
academic_year: @academic_year)
end end
def student_response_rate def student_response_rate
@ -48,7 +47,7 @@ class SubcategoryPresenter
end end
def measure_presenters def measure_presenters
@subcategory.measures.includes([:admin_data_items]).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) MeasurePresenter.new(measure:, academic_year: @academic_year, school: @school)
end end
end end
@ -56,19 +55,17 @@ class SubcategoryPresenter
private private
def admin_data_item_count def admin_data_item_count
if @school.is_hs return AdminDataItem.for_measures(@subcategory.measures).count if @school.is_hs
AdminDataItem.for_measures(@subcategory.measures).count
else AdminDataItem.non_hs_items_for_measures(@subcategory.measures).count
AdminDataItem.non_hs_items_for_measures(@subcategory.measures).count
end
end end
def format_a_non_applicable_rate(rate) def format_a_non_applicable_rate(rate)
rate == [0, 0] ? %w[N A] : rate rate == [0, 0] ? %w[N A] : rate
end end
def scale def zones
Scale.new( Zones.new(
watch_low_benchmark: measures.map(&:watch_low_benchmark).average, watch_low_benchmark: measures.map(&:watch_low_benchmark).average,
growth_low_benchmark: measures.map(&:growth_low_benchmark).average, growth_low_benchmark: measures.map(&:growth_low_benchmark).average,
approval_low_benchmark: measures.map(&:approval_low_benchmark).average, approval_low_benchmark: measures.map(&:approval_low_benchmark).average,

@ -96,12 +96,12 @@ class VarianceChartRowPresenter
end end
def zone def zone
scale = Scale.new( zones = Zones.new(
watch_low_benchmark: @measure.watch_low_benchmark, watch_low_benchmark: @measure.watch_low_benchmark,
growth_low_benchmark: @measure.growth_low_benchmark, growth_low_benchmark: @measure.growth_low_benchmark,
approval_low_benchmark: @measure.approval_low_benchmark, approval_low_benchmark: @measure.approval_low_benchmark,
ideal_low_benchmark: @measure.ideal_low_benchmark ideal_low_benchmark: @measure.ideal_low_benchmark
) )
scale.zone_for_score(@score) zones.zone_for_score(@score)
end end
end end

@ -1,4 +1,4 @@
class Scale class Zones
def initialize(watch_low_benchmark:, growth_low_benchmark:, approval_low_benchmark:, ideal_low_benchmark:) def initialize(watch_low_benchmark:, growth_low_benchmark:, approval_low_benchmark:, ideal_low_benchmark:)
@watch_low_benchmark = watch_low_benchmark @watch_low_benchmark = watch_low_benchmark
@growth_low_benchmark = growth_low_benchmark @growth_low_benchmark = growth_low_benchmark
@ -34,9 +34,10 @@ class Scale
end end
def zone_for_score(score) def zone_for_score(score)
return insufficient_data if score.nil?
return insufficient_data if score.is_a?(Float) && score.nan?
case score case score
when nil
insufficient_data
when ideal_zone.low_benchmark..ideal_zone.high_benchmark when ideal_zone.low_benchmark..ideal_zone.high_benchmark
ideal_zone ideal_zone
when approval_zone.low_benchmark..approval_zone.high_benchmark when approval_zone.low_benchmark..approval_zone.high_benchmark

@ -1,7 +1,7 @@
Rails.application.configure do Rails.application.configure do
config.after_initialize do config.after_initialize do
Bullet.enable = true Bullet.enable = true
Bullet.alert = true Bullet.alert = false
Bullet.bullet_logger = true Bullet.bullet_logger = true
Bullet.console = true Bullet.console = true
# Bullet.growl = true # Bullet.growl = true

@ -0,0 +1,10 @@
class CreateScales < ActiveRecord::Migration[7.0]
def change
create_table :scales do |t|
t.string :scale_id, index: { unique: true }, null: false
t.references :measure, null: false, foreign_key: true
t.timestamps
end
end
end

@ -0,0 +1,6 @@
class AddScaleToSurveyItem < ActiveRecord::Migration[7.0]
def change
add_reference :survey_items, :scale, null: false, foreign_key: true
remove_reference :survey_items, :measure, null: false, foreign_key: true
end
end

@ -0,0 +1,7 @@
class AddScaleToAdminDataItem < ActiveRecord::Migration[7.0]
def change
add_reference :admin_data_items, :scale, null: false, foreign_key: true
remove_reference :admin_data_items, :measure
add_index :admin_data_items, :admin_data_item_id, unique: true
end
end

@ -10,10 +10,8 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2022_02_11_122234) do ActiveRecord::Schema[7.0].define(version: 2022_02_17_170442) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "pg_stat_statements"
enable_extension "plpgsql" enable_extension "plpgsql"
create_table "academic_years", id: :serial, force: :cascade do |t| create_table "academic_years", id: :serial, force: :cascade do |t|
@ -22,16 +20,18 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do
end end
create_table "admin_data_items", force: :cascade do |t| create_table "admin_data_items", force: :cascade do |t|
t.integer "measure_id", null: false
t.string "admin_data_item_id", null: false t.string "admin_data_item_id", null: false
t.string "description" t.string "description"
t.datetime "created_at", precision: 6, null: false t.datetime "created_at", null: false
t.datetime "updated_at", precision: 6, null: false t.datetime "updated_at", null: false
t.float "watch_low_benchmark" t.float "watch_low_benchmark"
t.float "growth_low_benchmark" t.float "growth_low_benchmark"
t.float "approval_low_benchmark" t.float "approval_low_benchmark"
t.float "ideal_low_benchmark" t.float "ideal_low_benchmark"
t.boolean "hs_only_item", default: false t.boolean "hs_only_item", default: false
t.bigint "scale_id", null: false
t.index ["admin_data_item_id"], name: "index_admin_data_items_on_admin_data_item_id", unique: true
t.index ["scale_id"], name: "index_admin_data_items_on_scale_id"
end end
create_table "categories", id: :serial, force: :cascade do |t| create_table "categories", id: :serial, force: :cascade do |t|
@ -39,8 +39,8 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do
t.text "description" t.text "description"
t.string "slug" t.string "slug"
t.integer "sort_index" t.integer "sort_index"
t.datetime "created_at", precision: 6, null: false t.datetime "created_at", null: false
t.datetime "updated_at", precision: 6, null: false t.datetime "updated_at", null: false
t.string "category_id", null: false t.string "category_id", null: false
t.string "short_description" t.string "short_description"
t.index ["slug"], name: "index_categories_on_slug", unique: true t.index ["slug"], name: "index_categories_on_slug", unique: true
@ -50,22 +50,22 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do
t.string "name" t.string "name"
t.string "slug" t.string "slug"
t.integer "qualtrics_code" t.integer "qualtrics_code"
t.datetime "created_at", precision: 6, null: false t.datetime "created_at", null: false
t.datetime "updated_at", precision: 6, null: false t.datetime "updated_at", null: false
end end
create_table "legacy_attempts", id: :serial, force: :cascade do |t| create_table "legacy_attempts", id: :serial, force: :cascade do |t|
t.integer "recipient_id" t.integer "recipient_id"
t.integer "schedule_id" t.integer "schedule_id"
t.integer "recipient_schedule_id" t.integer "recipient_schedule_id"
t.datetime "sent_at" t.datetime "sent_at", precision: nil
t.datetime "responded_at" t.datetime "responded_at", precision: nil
t.integer "question_id" t.integer "question_id"
t.integer "translation_id" t.integer "translation_id"
t.integer "answer_index" t.integer "answer_index"
t.integer "open_response_id" t.integer "open_response_id"
t.datetime "created_at", null: false t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", null: false t.datetime "updated_at", precision: nil, null: false
t.text "twilio_details" t.text "twilio_details"
t.string "twilio_sid" t.string "twilio_sid"
t.integer "student_id" t.integer "student_id"
@ -78,8 +78,8 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do
t.text "description" t.text "description"
t.string "external_id" t.string "external_id"
t.integer "parent_category_id" t.integer "parent_category_id"
t.datetime "created_at", null: false t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", null: false t.datetime "updated_at", precision: nil, null: false
t.string "slug" t.string "slug"
t.float "benchmark" t.float "benchmark"
t.string "benchmark_description" t.string "benchmark_description"
@ -90,8 +90,8 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do
create_table "legacy_districts", id: :serial, force: :cascade do |t| create_table "legacy_districts", id: :serial, force: :cascade do |t|
t.string "name" t.string "name"
t.integer "state_id" t.integer "state_id"
t.datetime "created_at", null: false t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", null: false t.datetime "updated_at", precision: nil, null: false
t.string "slug" t.string "slug"
t.integer "qualtrics_code" t.integer "qualtrics_code"
t.index ["slug"], name: "index_legacy_districts_on_slug", unique: true t.index ["slug"], name: "index_legacy_districts_on_slug", unique: true
@ -101,8 +101,8 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do
t.string "name" t.string "name"
t.text "description" t.text "description"
t.text "question_ids" t.text "question_ids"
t.datetime "created_at", null: false t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", null: false t.datetime "updated_at", precision: nil, null: false
end end
create_table "legacy_questions", id: :serial, force: :cascade do |t| create_table "legacy_questions", id: :serial, force: :cascade do |t|
@ -113,8 +113,8 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do
t.string "option4" t.string "option4"
t.string "option5" t.string "option5"
t.integer "category_id" t.integer "category_id"
t.datetime "created_at", null: false t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", null: false t.datetime "updated_at", precision: nil, null: false
t.integer "target_group", default: 0 t.integer "target_group", default: 0
t.boolean "for_recipient_students", default: false t.boolean "for_recipient_students", default: false
t.boolean "reverse", default: false t.boolean "reverse", default: false
@ -126,8 +126,8 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do
t.string "name" t.string "name"
t.text "description" t.text "description"
t.text "recipient_ids" t.text "recipient_ids"
t.datetime "created_at", null: false t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", null: false t.datetime "updated_at", precision: nil, null: false
t.index ["school_id"], name: "index_legacy_recipient_lists_on_school_id" t.index ["school_id"], name: "index_legacy_recipient_lists_on_school_id"
end end
@ -136,10 +136,10 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do
t.integer "schedule_id" t.integer "schedule_id"
t.text "upcoming_question_ids" t.text "upcoming_question_ids"
t.text "attempted_question_ids" t.text "attempted_question_ids"
t.datetime "last_attempt_at" t.datetime "last_attempt_at", precision: nil
t.datetime "created_at", null: false t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", null: false t.datetime "updated_at", precision: nil, null: false
t.datetime "next_attempt_at" t.datetime "next_attempt_at", precision: nil
t.string "queued_question_ids" t.string "queued_question_ids"
end end
@ -154,8 +154,8 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do
t.string "income" t.string "income"
t.boolean "opted_out", default: false t.boolean "opted_out", default: false
t.integer "school_id" t.integer "school_id"
t.datetime "created_at", null: false t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", null: false t.datetime "updated_at", precision: nil, null: false
t.string "email" t.string "email"
t.string "slug" t.string "slug"
t.integer "attempts_count", default: 0 t.integer "attempts_count", default: 0
@ -176,8 +176,8 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do
t.boolean "random", default: false t.boolean "random", default: false
t.integer "recipient_list_id" t.integer "recipient_list_id"
t.integer "question_list_id" t.integer "question_list_id"
t.datetime "created_at", null: false t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", null: false t.datetime "updated_at", precision: nil, null: false
t.integer "time", default: 960 t.integer "time", default: 960
t.index ["school_id"], name: "index_legacy_schedules_on_school_id" t.index ["school_id"], name: "index_legacy_schedules_on_school_id"
end end
@ -188,8 +188,8 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do
t.integer "attempt_count", default: 0 t.integer "attempt_count", default: 0
t.integer "response_count", default: 0 t.integer "response_count", default: 0
t.integer "answer_index_total", default: 0 t.integer "answer_index_total", default: 0
t.datetime "created_at", null: false t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", null: false t.datetime "updated_at", precision: nil, null: false
t.float "nonlikert" t.float "nonlikert"
t.float "zscore" t.float "zscore"
t.string "year" t.string "year"
@ -207,16 +207,16 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do
t.integer "response_count" t.integer "response_count"
t.float "response_rate" t.float "response_rate"
t.string "year" t.string "year"
t.datetime "created_at", null: false t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", null: false t.datetime "updated_at", precision: nil, null: false
t.integer "response_total" t.integer "response_total"
end end
create_table "legacy_schools", id: :serial, force: :cascade do |t| create_table "legacy_schools", id: :serial, force: :cascade do |t|
t.string "name" t.string "name"
t.integer "district_id" t.integer "district_id"
t.datetime "created_at", null: false t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", null: false t.datetime "updated_at", precision: nil, null: false
t.text "description" t.text "description"
t.string "slug" t.string "slug"
t.integer "student_count" t.integer "student_count"
@ -233,31 +233,31 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do
t.string "age" t.string "age"
t.string "ethnicity" t.string "ethnicity"
t.integer "recipient_id" t.integer "recipient_id"
t.datetime "created_at", null: false t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", null: false t.datetime "updated_at", precision: nil, null: false
end end
create_table "legacy_user_schools", id: :serial, force: :cascade do |t| create_table "legacy_user_schools", id: :serial, force: :cascade do |t|
t.integer "user_id" t.integer "user_id"
t.integer "school_id" t.integer "school_id"
t.integer "district_id" t.integer "district_id"
t.datetime "created_at", null: false t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", null: false t.datetime "updated_at", precision: nil, null: false
end end
create_table "legacy_users", id: :serial, force: :cascade do |t| create_table "legacy_users", id: :serial, force: :cascade do |t|
t.string "email", default: "", null: false t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false t.string "encrypted_password", default: "", null: false
t.string "reset_password_token" t.string "reset_password_token"
t.datetime "reset_password_sent_at" t.datetime "reset_password_sent_at", precision: nil
t.datetime "remember_created_at" t.datetime "remember_created_at", precision: nil
t.integer "sign_in_count", default: 0, null: false t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at" t.datetime "current_sign_in_at", precision: nil
t.datetime "last_sign_in_at" t.datetime "last_sign_in_at", precision: nil
t.inet "current_sign_in_ip" t.inet "current_sign_in_ip"
t.inet "last_sign_in_ip" t.inet "last_sign_in_ip"
t.datetime "created_at", null: false t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", null: false t.datetime "updated_at", precision: nil, null: false
t.index ["email"], name: "index_legacy_users_on_email", unique: true t.index ["email"], name: "index_legacy_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_legacy_users_on_reset_password_token", unique: true t.index ["reset_password_token"], name: "index_legacy_users_on_reset_password_token", unique: true
end end
@ -267,8 +267,8 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do
t.string "name" t.string "name"
t.integer "subcategory_id", null: false t.integer "subcategory_id", null: false
t.text "description" t.text "description"
t.datetime "created_at", precision: 6, null: false t.datetime "created_at", null: false
t.datetime "updated_at", precision: 6, null: false t.datetime "updated_at", null: false
t.index ["measure_id"], name: "index_measures_on_measure_id" t.index ["measure_id"], name: "index_measures_on_measure_id"
t.index ["subcategory_id"], name: "index_measures_on_subcategory_id" t.index ["subcategory_id"], name: "index_measures_on_subcategory_id"
end end
@ -278,20 +278,29 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do
t.bigint "academic_year_id", null: false t.bigint "academic_year_id", null: false
t.float "total_students" t.float "total_students"
t.float "total_teachers" t.float "total_teachers"
t.datetime "created_at", precision: 6, null: false t.datetime "created_at", null: false
t.datetime "updated_at", precision: 6, null: false t.datetime "updated_at", null: false
t.index ["academic_year_id"], name: "index_respondents_on_academic_year_id" t.index ["academic_year_id"], name: "index_respondents_on_academic_year_id"
t.index ["school_id"], name: "index_respondents_on_school_id" t.index ["school_id"], name: "index_respondents_on_school_id"
end end
create_table "scales", force: :cascade do |t|
t.string "scale_id", null: false
t.bigint "measure_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["measure_id"], name: "index_scales_on_measure_id"
t.index ["scale_id"], name: "index_scales_on_scale_id", unique: true
end
create_table "schools", force: :cascade do |t| create_table "schools", force: :cascade do |t|
t.string "name" t.string "name"
t.integer "district_id" t.integer "district_id"
t.text "description" t.text "description"
t.string "slug" t.string "slug"
t.integer "qualtrics_code" t.integer "qualtrics_code"
t.datetime "created_at", precision: 6, null: false t.datetime "created_at", null: false
t.datetime "updated_at", precision: 6, null: false t.datetime "updated_at", null: false
t.integer "dese_id", null: false t.integer "dese_id", null: false
t.boolean "is_hs", default: false t.boolean "is_hs", default: false
t.index ["dese_id"], name: "index_schools_on_dese_id", unique: true t.index ["dese_id"], name: "index_schools_on_dese_id", unique: true
@ -301,8 +310,8 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do
t.string "name" t.string "name"
t.integer "category_id" t.integer "category_id"
t.text "description" t.text "description"
t.datetime "created_at", precision: 6, null: false t.datetime "created_at", null: false
t.datetime "updated_at", precision: 6, null: false t.datetime "updated_at", null: false
t.string "subcategory_id", null: false t.string "subcategory_id", null: false
end end
@ -312,8 +321,8 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do
t.integer "survey_item_id", null: false t.integer "survey_item_id", null: false
t.string "response_id", null: false t.string "response_id", null: false
t.integer "academic_year_id", null: false t.integer "academic_year_id", null: false
t.datetime "created_at", precision: 6, null: false t.datetime "created_at", null: false
t.datetime "updated_at", precision: 6, null: false t.datetime "updated_at", null: false
t.index ["academic_year_id"], name: "index_survey_item_responses_on_academic_year_id" t.index ["academic_year_id"], name: "index_survey_item_responses_on_academic_year_id"
t.index ["response_id"], name: "index_survey_item_responses_on_response_id" t.index ["response_id"], name: "index_survey_item_responses_on_response_id"
t.index ["school_id"], name: "index_survey_item_responses_on_school_id" t.index ["school_id"], name: "index_survey_item_responses_on_school_id"
@ -321,20 +330,20 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do
end end
create_table "survey_items", id: :serial, force: :cascade do |t| create_table "survey_items", id: :serial, force: :cascade do |t|
t.integer "measure_id", null: false
t.string "survey_item_id", null: false t.string "survey_item_id", null: false
t.string "prompt" t.string "prompt"
t.datetime "created_at", precision: 6, null: false t.datetime "created_at", null: false
t.datetime "updated_at", precision: 6, null: false t.datetime "updated_at", null: false
t.float "watch_low_benchmark" t.float "watch_low_benchmark"
t.float "growth_low_benchmark" t.float "growth_low_benchmark"
t.float "approval_low_benchmark" t.float "approval_low_benchmark"
t.float "ideal_low_benchmark" t.float "ideal_low_benchmark"
t.index ["measure_id"], name: "index_survey_items_on_measure_id" t.bigint "scale_id", null: false
t.index ["scale_id"], name: "index_survey_items_on_scale_id"
t.index ["survey_item_id"], name: "index_survey_items_on_survey_item_id" t.index ["survey_item_id"], name: "index_survey_items_on_survey_item_id"
end end
add_foreign_key "admin_data_items", "measures" add_foreign_key "admin_data_items", "scales"
add_foreign_key "legacy_recipient_lists", "legacy_schools", column: "school_id" add_foreign_key "legacy_recipient_lists", "legacy_schools", column: "school_id"
add_foreign_key "legacy_schedules", "legacy_schools", column: "school_id" add_foreign_key "legacy_schedules", "legacy_schools", column: "school_id"
add_foreign_key "legacy_school_categories", "legacy_categories", column: "category_id" add_foreign_key "legacy_school_categories", "legacy_categories", column: "category_id"
@ -342,9 +351,10 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do
add_foreign_key "measures", "subcategories" add_foreign_key "measures", "subcategories"
add_foreign_key "respondents", "academic_years" add_foreign_key "respondents", "academic_years"
add_foreign_key "respondents", "schools" add_foreign_key "respondents", "schools"
add_foreign_key "scales", "measures"
add_foreign_key "subcategories", "categories" add_foreign_key "subcategories", "categories"
add_foreign_key "survey_item_responses", "academic_years" add_foreign_key "survey_item_responses", "academic_years"
add_foreign_key "survey_item_responses", "schools" add_foreign_key "survey_item_responses", "schools"
add_foreign_key "survey_item_responses", "survey_items" add_foreign_key "survey_item_responses", "survey_items"
add_foreign_key "survey_items", "measures" add_foreign_key "survey_items", "scales"
end end

@ -0,0 +1,26 @@
# Application decision record 2
# Add scale to framework
## Status
Completed
## Context
The calculations needed to be reworked. When calculating a score for a measure, we had looped through all the survey_items_responses for a measure and taken the average for all of them. Doing it that way means student responses are given more weight than teacher responses since the number of students responses will outnumber teacher responses. Another consequence is that survey_items with more questions will be prioritized over ones with fewer questions since they will create more survey item responses.
Story: #181205114 and #181209931
## Decision
Change the calculations so that scores bubble up through the framework hierarchy.
## Consequences
Added scales to framework. Changed calculations to first group based on the most atomic bits of the framework and then
average those groupings as we go up the framework. First, likert scores are averaged for
all survey item responses in a survey item. 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. The average of student and teacher scales becomes the score for a measure. Then the measures in a
subcategory are averaged.

@ -13,7 +13,7 @@ FactoryBot.define do
factory :academic_year do factory :academic_year do
range { '2050-51' } range { '2050-51' }
initialize_with { AcademicYear.find_or_initialize_by(range: range) } initialize_with { AcademicYear.find_or_initialize_by(range:) }
end end
factory :category, class: 'Category' do factory :category, class: 'Category' do
@ -35,9 +35,10 @@ FactoryBot.define do
measures_count { 2 } measures_count { 2 }
end end
after(:create) do |subcategory, evaluator| after(:create) do |subcategory, evaluator|
create_list(:measure, evaluator.measures_count, subcategory: subcategory).each do |measure| create_list(:measure, evaluator.measures_count, subcategory:).each do |measure|
survey_item = create(:teacher_survey_item, measure: measure) scale = create(:scale, measure: measure)
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD, survey_item: survey_item) survey_item = create(:teacher_survey_item, scale:)
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD, survey_item:)
end end
end end
end end
@ -54,9 +55,20 @@ FactoryBot.define do
# end # end
end end
factory :scale do
measure
scale_id { "A Scale #{rand}" }
factory :teacher_scale do
scale_id {"t-#{rand}"}
end
factory :student_scale do
scale_id {"s-#{rand}"}
end
end
factory :survey_item do factory :survey_item do
scale
prompt { 'What do YOU think?' } prompt { 'What do YOU think?' }
measure
factory :teacher_survey_item do factory :teacher_survey_item do
survey_item_id { "t-#{rand}" } survey_item_id { "t-#{rand}" }
watch_low_benchmark { 2.0 } watch_low_benchmark { 2.0 }
@ -84,7 +96,7 @@ FactoryBot.define do
factory :admin_data_item do factory :admin_data_item do
admin_data_item_id { rand.to_s } admin_data_item_id { rand.to_s }
description { rand.to_s } description { rand.to_s }
measure scale
end end
factory :respondent do factory :respondent do

@ -123,9 +123,11 @@ describe Seeder do
before do before do
school_culture_category = create(:category, category_id: '2', sort_index: -1) school_culture_category = create(:category, category_id: '2', sort_index: -1)
safety_subcategory = create(:subcategory, subcategory_id: '2A', category: school_culture_category) safety_subcategory = create(:subcategory, subcategory_id: '2A', category: school_culture_category)
student_physical_safety_measure = create(:measure, measure_id: '2A-i', subcategory: safety_subcategory) physical_safety_measure = create(:measure, measure_id: '2A-i', subcategory: safety_subcategory)
create(:survey_item, survey_item_id: 's-phys-q1', measure: student_physical_safety_measure) student_physical_safety_scale = create(:scale, scale_id: 's-phys', measure: physical_safety_measure)
create(:admin_data_item, admin_data_item_id: 'a-phys-i1', measure: student_physical_safety_measure) create(:survey_item, survey_item_id: 's-phys-q1', scale: student_physical_safety_scale)
admin_physical_safety_scale = create(:scale, scale_id: 'a-phys', measure: physical_safety_measure)
create(:admin_data_item, admin_data_item_id: 'a-phys-i1', scale: admin_physical_safety_scale)
end end
it 'creates new objects as necessary' do it 'creates new objects as necessary' do
@ -133,10 +135,12 @@ describe Seeder do
seeder.seed_sqm_framework sample_sqm_framework_csv seeder.seed_sqm_framework sample_sqm_framework_csv
end.to change { Category.count }.by(4) end.to change { Category.count }.by(4)
.and change { Subcategory.count }.by(15) .and change { Subcategory.count }.by(15)
.and change { Measure.count }.by(31) .and change { Measure.count }.by(31).and change {
.and change { Scale.count
SurveyItem.count }.by(51)
}.by(136) .and change {
SurveyItem.count
}.by(136)
.and change { .and change {
AdminDataItem.count AdminDataItem.count
}.by(32) }.by(32)
@ -174,6 +178,14 @@ describe Seeder do
expect(measure.description).to eq 'This is a measure description.' expect(measure.description).to eq 'This is a measure description.'
end end
it 'updates scale references' do
scale = Scale.find_by_scale_id 't-pcom'
measure = Measure.find_by_measure_id '1A-iii'
survey_item = SurveyItem.find_by_survey_item_id 't-pcom-q1'
expect(scale.measure).to eq measure
expect(scale.survey_items).to include survey_item
end
it 'does not overwrite the survey item benchmarks with admin data benchmarks' do it 'does not overwrite the survey item benchmarks with admin data benchmarks' do
survey_item = SurveyItem.find_by_survey_item_id 't-prep-q1' survey_item = SurveyItem.find_by_survey_item_id 't-prep-q1'
expect(survey_item.approval_low_benchmark).to eq 3.5 expect(survey_item.approval_low_benchmark).to eq 3.5

@ -2,6 +2,12 @@ require 'rails_helper'
RSpec.describe Measure, type: :model do RSpec.describe Measure, type: :model do
let(:measure) { create(:measure) } let(:measure) { create(:measure) }
let(:scale) { create(:scale, measure:) }
let(:teacher_scale) { create(:teacher_scale, measure:) }
let(:student_scale) { create(:student_scale, measure:) }
let(:school) { create(:school) }
let(:academic_year) { create(:academic_year) }
let(:admin_watch_low_benchmark) { 2.0 } let(:admin_watch_low_benchmark) { 2.0 }
let(:admin_growth_low_benchmark) { 3.0 } let(:admin_growth_low_benchmark) { 3.0 }
let(:admin_approval_low_benchmark) { 4.0 } let(:admin_approval_low_benchmark) { 4.0 }
@ -17,166 +23,450 @@ RSpec.describe Measure, type: :model do
let(:teacher_approval_low_benchmark) { 3.2 } let(:teacher_approval_low_benchmark) { 3.2 }
let(:teacher_ideal_low_benchmark) { 4.2 } let(:teacher_ideal_low_benchmark) { 4.2 }
context 'when a measure includes only one admin data item' do describe 'benchmarks' do
before do context 'when a measure includes only one admin data item' do
create(:admin_data_item, measure: measure, before do
watch_low_benchmark: admin_watch_low_benchmark, create(:admin_data_item, scale:,
growth_low_benchmark: admin_growth_low_benchmark, watch_low_benchmark: admin_watch_low_benchmark,
approval_low_benchmark: admin_approval_low_benchmark, growth_low_benchmark: admin_growth_low_benchmark,
ideal_low_benchmark: admin_ideal_low_benchmark) approval_low_benchmark: admin_approval_low_benchmark,
end ideal_low_benchmark: admin_ideal_low_benchmark)
it 'returns a watch low benchmark equal to the admin data item watch low benchmark' do end
expect(measure.watch_low_benchmark).to be 2.0 it 'returns a watch low benchmark equal to the admin data item watch low benchmark' do
end expect(measure.watch_low_benchmark).to be 2.0
end
it 'returns the source as an admin_data_item' do it 'returns the source as an admin_data_item' do
expect(measure.sources).to eq [:admin_data] expect(measure.sources).to eq [:admin_data]
end
end end
end
context 'when a measure includes only student survey items' do context 'when a measure includes only student survey items' do
before do before do
create(:student_survey_item, measure: measure, create(:student_survey_item, scale:,
watch_low_benchmark: student_watch_low_benchmark, watch_low_benchmark: student_watch_low_benchmark,
growth_low_benchmark: student_growth_low_benchmark, growth_low_benchmark: student_growth_low_benchmark,
approval_low_benchmark: student_approval_low_benchmark, approval_low_benchmark: student_approval_low_benchmark,
ideal_low_benchmark: student_ideal_low_benchmark) ideal_low_benchmark: student_ideal_low_benchmark)
end end
it 'returns a watch low benchmark equal to the student survey item watch low benchmark ' do it 'returns a watch low benchmark equal to the student survey item watch low benchmark ' do
expect(measure.watch_low_benchmark).to be 1.5 expect(measure.watch_low_benchmark).to be 1.5
end
it 'returns a warning low benchmark equal to the student survey item warning low benchmark ' do
expect(measure.warning_low_benchmark).to eq 1
end
it 'returns the source as student_surveys' do
expect(measure.sources).to eq [:student_surveys]
end
end end
it 'returns a warning low benchmark equal to the student survey item warning low benchmark ' do
expect(measure.warning_low_benchmark).to eq 1
end
it 'returns the source as student_surveys' do
expect(measure.sources).to eq [:student_surveys]
end
end
context 'when a measure includes only teacher survey items' do context 'when a measure includes only teacher survey items' do
before do before do
create(:teacher_survey_item, measure: measure, create(:teacher_survey_item, scale:,
watch_low_benchmark: teacher_watch_low_benchmark, watch_low_benchmark: teacher_watch_low_benchmark,
growth_low_benchmark: teacher_growth_low_benchmark, growth_low_benchmark: teacher_growth_low_benchmark,
approval_low_benchmark: teacher_approval_low_benchmark, approval_low_benchmark: teacher_approval_low_benchmark,
ideal_low_benchmark: teacher_ideal_low_benchmark) ideal_low_benchmark: teacher_ideal_low_benchmark)
end end
it 'returns a watch low benchmark equal to the teacher survey item watch low benchmark ' do it 'returns a watch low benchmark equal to the teacher survey item watch low benchmark ' do
expect(measure.watch_low_benchmark).to be 1.2 expect(measure.watch_low_benchmark).to be 1.2
end
it 'returns a warning low benchmark equal to the teacher survey item warning low benchmark ' do
expect(measure.warning_low_benchmark).to eq 1
end
it 'returns the source as teacher_surveys' do
expect(measure.sources).to eq [:teacher_surveys]
end
end end
it 'returns a warning low benchmark equal to the teacher survey item warning low benchmark ' do
expect(measure.warning_low_benchmark).to eq 1
end
it 'returns the source as teacher_surveys' do
expect(measure.sources).to eq [:teacher_surveys]
end
end
context 'when a measure includes admin data and student survey items' do context 'when a measure includes admin data and student survey items' do
before do before do
create_list(:admin_data_item, 3, measure: measure, create_list(:admin_data_item, 3, scale:,
watch_low_benchmark: admin_watch_low_benchmark, watch_low_benchmark: admin_watch_low_benchmark,
growth_low_benchmark: admin_growth_low_benchmark, growth_low_benchmark: admin_growth_low_benchmark,
approval_low_benchmark: admin_approval_low_benchmark, approval_low_benchmark: admin_approval_low_benchmark,
ideal_low_benchmark: admin_ideal_low_benchmark) ideal_low_benchmark: admin_ideal_low_benchmark)
create(:student_survey_item, measure: measure,
watch_low_benchmark: student_watch_low_benchmark,
growth_low_benchmark: student_growth_low_benchmark,
approval_low_benchmark: student_approval_low_benchmark,
ideal_low_benchmark: student_ideal_low_benchmark)
end
it 'returns the average of admin and student benchmarks where each admin data item has a weight of 1 and student survey items all together have a weight of 1' do create(:student_survey_item, scale:,
# (2*3 + 1.5)/4 watch_low_benchmark: student_watch_low_benchmark,
expect(measure.watch_low_benchmark).to be 1.875 growth_low_benchmark: student_growth_low_benchmark,
end approval_low_benchmark: student_approval_low_benchmark,
it 'returns the source as admin and student survey items' do ideal_low_benchmark: student_ideal_low_benchmark)
expect(measure.sources).to eq [:admin_data, :student_surveys] end
end
end
context 'when a measure includes admin data and teacher survey items' do it 'returns the average of admin and student benchmarks where each admin data item has a weight of 1 and student survey items all together have a weight of 1' do
before do # (2*3 + 1.5)/4
create_list(:admin_data_item, 3, measure: measure, expect(measure.watch_low_benchmark).to be 1.875
watch_low_benchmark: admin_watch_low_benchmark, end
growth_low_benchmark: admin_growth_low_benchmark, it 'returns the source as admin and student survey items' do
approval_low_benchmark: admin_approval_low_benchmark, expect(measure.sources).to eq %i[admin_data student_surveys]
ideal_low_benchmark: admin_ideal_low_benchmark) end
create(:teacher_survey_item, measure: measure,
watch_low_benchmark: teacher_watch_low_benchmark,
growth_low_benchmark: teacher_growth_low_benchmark,
approval_low_benchmark: teacher_approval_low_benchmark,
ideal_low_benchmark: teacher_ideal_low_benchmark)
end end
it 'returns the average of admin and teacher benchmarks where each admin data item has a weight of 1 and teacher survey items all together have a weight of 1' do context 'when a measure includes admin data and teacher survey items' do
# (2*3 + 1.2)/4 before do
expect(measure.watch_low_benchmark).to be 1.8 create_list(:admin_data_item, 3, scale:,
end watch_low_benchmark: admin_watch_low_benchmark,
it 'returns the source as admin and teacher survey items' do growth_low_benchmark: admin_growth_low_benchmark,
expect(measure.sources).to eq [:admin_data, :teacher_surveys] approval_low_benchmark: admin_approval_low_benchmark,
end ideal_low_benchmark: admin_ideal_low_benchmark)
end
create(:teacher_survey_item, scale:,
watch_low_benchmark: teacher_watch_low_benchmark,
growth_low_benchmark: teacher_growth_low_benchmark,
approval_low_benchmark: teacher_approval_low_benchmark,
ideal_low_benchmark: teacher_ideal_low_benchmark)
end
context 'when a measure includes student and teacher survey items' do it 'returns the average of admin and teacher benchmarks where each admin data item has a weight of 1 and teacher survey items all together have a weight of 1' do
before do # (2*3 + 1.2)/4
create_list(:student_survey_item, 3, measure: measure, expect(measure.watch_low_benchmark).to be 1.8
watch_low_benchmark: student_watch_low_benchmark, end
growth_low_benchmark: student_growth_low_benchmark, it 'returns the source as admin and teacher survey items' do
approval_low_benchmark: student_approval_low_benchmark, expect(measure.sources).to eq %i[admin_data teacher_surveys]
ideal_low_benchmark: student_ideal_low_benchmark) end
create_list(:teacher_survey_item, 3, measure: measure,
watch_low_benchmark: teacher_watch_low_benchmark,
growth_low_benchmark: teacher_growth_low_benchmark,
approval_low_benchmark: teacher_approval_low_benchmark,
ideal_low_benchmark: teacher_ideal_low_benchmark)
end end
it 'returns the average of student and teacher benchmarks where teacher survey items all together have a weight of 1 and all student survey items have a weight of 1' do context 'when a measure includes student and teacher survey items' do
# (1.2+ 1.5)/2 before do
expect(measure.watch_low_benchmark).to be 1.35 create_list(:student_survey_item, 3, scale:,
watch_low_benchmark: student_watch_low_benchmark,
growth_low_benchmark: student_growth_low_benchmark,
approval_low_benchmark: student_approval_low_benchmark,
ideal_low_benchmark: student_ideal_low_benchmark)
create_list(:teacher_survey_item, 3, scale:,
watch_low_benchmark: teacher_watch_low_benchmark,
growth_low_benchmark: teacher_growth_low_benchmark,
approval_low_benchmark: teacher_approval_low_benchmark,
ideal_low_benchmark: teacher_ideal_low_benchmark)
end
it 'returns the average of student and teacher benchmarks where teacher survey items all together have a weight of 1 and all student survey items have a weight of 1' do
# (1.2+ 1.5)/2
expect(measure.watch_low_benchmark).to be 1.35
end
it 'returns the source as student and teacher survey items' do
expect(measure.sources).to eq %i[student_surveys teacher_surveys]
end
end end
it 'returns the source as student and teacher survey items' do
expect(measure.sources).to eq [:student_surveys, :teacher_surveys] context 'when a measure includes admin data and student and teacher survey items' do
before do
create_list(:admin_data_item, 3, scale:,
watch_low_benchmark: admin_watch_low_benchmark,
growth_low_benchmark: admin_growth_low_benchmark,
approval_low_benchmark: admin_approval_low_benchmark,
ideal_low_benchmark: admin_ideal_low_benchmark)
create_list(:student_survey_item, 3, scale:,
watch_low_benchmark: student_watch_low_benchmark,
growth_low_benchmark: student_growth_low_benchmark,
approval_low_benchmark: student_approval_low_benchmark,
ideal_low_benchmark: student_ideal_low_benchmark)
create_list(:teacher_survey_item, 3, scale:,
watch_low_benchmark: teacher_watch_low_benchmark,
growth_low_benchmark: teacher_growth_low_benchmark,
approval_low_benchmark: teacher_approval_low_benchmark,
ideal_low_benchmark: teacher_ideal_low_benchmark)
end
it 'returns the average of admin and teacher benchmarks where each admin data item has a weight of 1 and teacher survey items all together have a weight of 1, and student surveys have a weight of 1' do
# (2 * 3 + 1.2 + 1.5)/ 5
expect(measure.watch_low_benchmark).to be_within(0.001).of 1.74
# (3 * 3 + 2.2 + 2.5)/ 5
expect(measure.growth_low_benchmark).to be_within(0.001).of 2.74
# (4 * 3 + 3.2 + 3.5)/ 5
expect(measure.approval_low_benchmark).to be_within(0.001).of 3.74
# (5 * 3 + 4.2 + 4.5)/ 5
expect(measure.ideal_low_benchmark).to be_within(0.001).of 4.74
end
it 'returns the source as admin student and teacher survey items' do
expect(measure.sources).to eq %i[admin_data student_surveys teacher_surveys]
end
end end
end end
context 'when a measure includes admin data and student and teacher survey items' do describe '.score' do
before do context 'when the measure includes only teacher data' do
create_list(:admin_data_item, 3, measure: measure, let(:teacher_survey_item_1) { create(:teacher_survey_item, scale: teacher_scale) }
watch_low_benchmark: admin_watch_low_benchmark, let(:teacher_survey_item_2) { create(:teacher_survey_item, scale: teacher_scale) }
growth_low_benchmark: admin_growth_low_benchmark, let(:teacher_survey_item_3) { create(:teacher_survey_item, scale: teacher_scale) }
approval_low_benchmark: admin_approval_low_benchmark,
ideal_low_benchmark: admin_ideal_low_benchmark) context "and the number of responses for each of the measure's survey items meets the teacher threshold of 17" do
create_list(:student_survey_item, 3, measure: measure, before :each do
watch_low_benchmark: student_watch_low_benchmark, create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD,
growth_low_benchmark: student_growth_low_benchmark, survey_item: teacher_survey_item_1, academic_year:, school:, likert_score: 3)
approval_low_benchmark: student_approval_low_benchmark, create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD,
ideal_low_benchmark: student_ideal_low_benchmark) survey_item: teacher_survey_item_2, academic_year:, school:, likert_score: 4)
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD,
create_list(:teacher_survey_item, 3, measure: measure, survey_item: teacher_survey_item_3, academic_year:, school:, likert_score: 5)
watch_low_benchmark: teacher_watch_low_benchmark, end
growth_low_benchmark: teacher_growth_low_benchmark,
approval_low_benchmark: teacher_approval_low_benchmark, it 'returns the average of the likert scores of the survey items' do
ideal_low_benchmark: teacher_ideal_low_benchmark) expect(measure.score(school:, academic_year:).average).to eq 4
end
it 'affirms that the result meets the teacher threshold' do
expect(measure.score(school:, academic_year:).meets_teacher_threshold?).to be true
end
it 'reports the result does not meeet student threshold' do
expect(measure.score(school:, academic_year:).meets_student_threshold?).to be false
end
end
context "and the average number of responses across the measure's survey items meets the teacher threshold of 17" do
before :each do
create_list(:survey_item_response, 19, survey_item: teacher_survey_item_1, academic_year:, school:,
likert_score: 3)
create_list(:survey_item_response, 16, survey_item: teacher_survey_item_2, academic_year:, school:,
likert_score: 4)
create_list(:survey_item_response, 16, survey_item: teacher_survey_item_3, academic_year:, school:,
likert_score: 5)
end
it 'returns the average of the likert scores of the survey items' do
average_score = 4
expect(measure.score(school:, academic_year:).average).to be_within(0.001).of(average_score)
end
end
context "and none of the measure's survey items meets the teacher threshold of 17" do
before :each do
create_list(:survey_item_response, 16, survey_item: teacher_survey_item_1, academic_year:, school:,
likert_score: rand)
create_list(:survey_item_response, 16, survey_item: teacher_survey_item_2, academic_year:, school:,
likert_score: rand)
create_list(:survey_item_response, 16, survey_item: teacher_survey_item_3, academic_year:, school:,
likert_score: rand)
end
it 'returns nil' do
expect(measure.score(school:, academic_year:).average).to be_nil
end
it 'affirms that the result does not meet the threshold' do
expect(measure.score(school:, academic_year:).meets_teacher_threshold?).to be false
end
end
context "and the average number of responses across the measure's survey items does not meet the teacher threshold of 17" do
before :each do
create_list(:survey_item_response, 18, survey_item: teacher_survey_item_1, academic_year:, school:,
likert_score: rand)
create_list(:survey_item_response, 16, survey_item: teacher_survey_item_2, academic_year:, school:,
likert_score: rand)
create_list(:survey_item_response, 16, survey_item: teacher_survey_item_3, academic_year:, school:,
likert_score: rand)
end
it 'returns nil' do
expect(measure.score(school:, academic_year:).average).to be_nil
end
it 'affirms that the result does not meet the threshold' do
expect(measure.score(school:, academic_year:).meets_teacher_threshold?).to be false
end
end
end end
it 'returns the average of admin and teacher benchmarks where each admin data item has a weight of 1 and teacher survey items all together have a weight of 1, and student surveys have a weight of 1' do context 'when the measure includes only student data' do
# (2 * 3 + 1.2 + 1.5)/ 5 let(:student_survey_item_1) { create(:student_survey_item, scale: student_scale) }
expect(measure.watch_low_benchmark).to be_within(0.001).of 1.74 let(:student_survey_item_2) { create(:student_survey_item, scale: student_scale) }
# (3 * 3 + 2.2 + 2.5)/ 5 let(:student_survey_item_3) { create(:student_survey_item, scale: student_scale) }
expect(measure.growth_low_benchmark).to be_within(0.001).of 2.74
# (4 * 3 + 3.2 + 3.5)/ 5 context "and the number of responses for each of the measure's survey items meets the student threshold of 196" do
expect(measure.approval_low_benchmark).to be_within(0.001).of 3.74 before :each do
# (5 * 3 + 4.2 + 4.5)/ 5 create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD,
expect(measure.ideal_low_benchmark).to be_within(0.001).of 4.74 survey_item: student_survey_item_1, academic_year:, school:, likert_score: 3)
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD,
survey_item: student_survey_item_2, academic_year:, school:, likert_score: 4)
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD,
survey_item: student_survey_item_3, academic_year:, school:, likert_score: 5)
end
it 'returns the average of the likert scores of the survey items' do
expect(measure.score(school:, academic_year:).average).to eq 4
end
it 'affirms that the result meets the student threshold' do
expect(measure.score(school:, academic_year:).meets_student_threshold?).to be true
end
it 'notes that the result does not meet the teacher threshold' do
expect(measure.score(school:, academic_year:).meets_teacher_threshold?).to be false
end
end
context "and the average number of responses across the measure's survey items meets the student threshold of 196" do
before :each do
create_list(:survey_item_response, 200, survey_item: student_survey_item_1, academic_year:,
school:, likert_score: 3)
create_list(:survey_item_response, 195, survey_item: student_survey_item_2, academic_year:,
school:, likert_score: 4)
create_list(:survey_item_response, 193, survey_item: student_survey_item_3, academic_year:,
school:, likert_score: 5)
end
it 'returns the average of the likert scores of the survey items' do
average_score = 4
expect(measure.score(school:, academic_year:).average).to be_within(0.001).of(average_score)
end
end
context "and none of the measure's survey items meets the student threshold of 196" do
before :each do
create_list(:survey_item_response, 195, survey_item: student_survey_item_1, academic_year:,
school:, likert_score: rand)
create_list(:survey_item_response, 195, survey_item: student_survey_item_2, academic_year:,
school:, likert_score: rand)
create_list(:survey_item_response, 195, survey_item: student_survey_item_3, academic_year:,
school:, likert_score: rand)
end
it 'returns nil' do
expect(measure.score(school:, academic_year:).average).to be_nil
end
it 'affirms that the result does not meet the threshold' do
expect(measure.score(school:, academic_year:).meets_student_threshold?).to be false
end
end
context "and the average number of responses across the measure's survey items does not meet the student threshold of 196" do
before :each do
create_list(:survey_item_response, 200, survey_item: student_survey_item_1, academic_year:,
school:, likert_score: rand)
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD,
survey_item: student_survey_item_2, academic_year:, school:, likert_score: rand)
create_list(:survey_item_response, 191, survey_item: student_survey_item_3, academic_year:,
school:, likert_score: rand)
end
it 'returns nil' do
expect(measure.score(school:, academic_year:).average).to be_nil
end
it 'affirms that the result does not meet the threshold' do
expect(measure.score(school:, academic_year:).meets_student_threshold?).to be false
end
end
end end
it 'returns the source as admin student and teacher survey items' do context 'when the measure includes both teacher and student data' do
expect(measure.sources).to eq [:admin_data, :student_surveys, :teacher_surveys] let(:teacher_survey_item_1) { create(:teacher_survey_item, scale: teacher_scale) }
let(:student_survey_item_1) { create(:student_survey_item, scale: student_scale) }
context 'and there is sufficient teacher data and sufficient student data' do
before :each do
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD,
survey_item: teacher_survey_item_1, academic_year:, school:, likert_score: 5)
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD,
survey_item: student_survey_item_1, academic_year:, school:, likert_score: 5)
end
it 'returns the average of the likert scores of the survey items' do
expect(measure.score(school:, academic_year:).average).to eq 5
end
it 'affirms that the result does meet the thresholds' do
expect(measure.score(school:, academic_year:).meets_teacher_threshold?).to be true
end
context 'and a different measure in the same year exists' do
before :each do
different_measure = create(:measure)
different_scale = create(:teacher_scale, measure: different_measure)
different_teacher_survey_item = create(:teacher_survey_item, scale: different_scale)
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD,
survey_item: different_teacher_survey_item, academic_year:, school:, likert_score: 1)
end
it 'affirms the additional measures do not change the scores for the original measure' do
expect(measure.score(school:, academic_year:).average).to eq 5
end
end
context 'and data exists for the same measure but a different school' do
before do
different_school = create(:school)
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD,
survey_item: teacher_survey_item_1, academic_year:, school: different_school, likert_score: 1)
end
it 'affirms the data for the different school does not affect the average for the original school' do
expect(measure.score(school:, academic_year:).average).to eq 5
end
end
context 'and data exists for the same measure but a different year' do
before do
different_year = create(:academic_year, range: '1111-12')
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD,
survey_item: teacher_survey_item_1, academic_year: different_year, school:, likert_score: 1)
end
it 'affirms the data for the different year does not affect the average for the original year' do
expect(measure.score(school:, academic_year:).average).to eq 5
end
end
end
context 'and there is sufficient teacher data and insufficient student data' do
before :each do
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD,
survey_item: teacher_survey_item_1, academic_year:, school:, likert_score: 5)
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD - 1,
survey_item: student_survey_item_1, academic_year:, school:, likert_score: 1)
end
it 'returns the average of the likert scores of the teacher survey items' do
expect(measure.score(school:, academic_year:).average).to eq 5
end
it 'affirms that the result meets the teacher threshold but not the student threshold' do
expect(measure.score(school:, academic_year:).meets_teacher_threshold?).to be true
expect(measure.score(school:, academic_year:).meets_student_threshold?).to be false
end
end
context 'and there is insufficient teacher data and sufficient student data' do
before :each do
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD - 1,
survey_item: teacher_survey_item_1, academic_year:, school:, likert_score: 1)
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD,
survey_item: student_survey_item_1, academic_year:, school:, likert_score: 5)
end
it 'returns the average of the likert scores of the student survey items' do
expect(measure.score(school:, academic_year:).average).to eq 5
end
it 'affirms that the result meets the student threshold but not the teacher threshold' do
expect(measure.score(school:, academic_year:).meets_teacher_threshold?).to be false
expect(measure.score(school:, academic_year:).meets_student_threshold?).to be true
end
end
context 'and there is insufficient teacher data and insufficient student data' do
before :each do
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD - 1,
survey_item: teacher_survey_item_1, academic_year:, school:)
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD - 1,
survey_item: student_survey_item_1, academic_year:, school:)
end
it 'returns nil' do
expect(measure.score(school:, academic_year:).average).to be_nil
end
it 'affirms that the result does not meet either threshold' do
expect(measure.score(school:, academic_year:).meets_teacher_threshold?).to be false
expect(measure.score(school:, academic_year:).meets_student_threshold?).to be false
end
end
end end
end end
end end

@ -11,13 +11,16 @@ describe ResponseRate, type: :model do
describe StudentResponseRate do describe StudentResponseRate do
let(:subcategory) { create(:subcategory) } let(:subcategory) { create(:subcategory) }
let(:sufficient_measure_1) { create(:measure, subcategory: subcategory) } let(:sufficient_measure_1) { create(:measure, subcategory: subcategory) }
let(:sufficient_scale_1) { create(:scale, measure: sufficient_measure_1) }
let(:sufficient_measure_2) { create(:measure, subcategory: subcategory) } let(:sufficient_measure_2) { create(:measure, subcategory: subcategory) }
let(:sufficient_scale_2) { create(:scale, measure: sufficient_measure_2) }
let(:insufficient_measure) { create(:measure, subcategory: subcategory) } let(:insufficient_measure) { create(:measure, subcategory: subcategory) }
let(:sufficient_teacher_survey_item) { create(:teacher_survey_item, measure: sufficient_measure_1) } let(:insufficient_scale) { create(:scale, measure: insufficient_measure) }
let(:sufficient_student_survey_item_1) { create(:student_survey_item, measure: sufficient_measure_1) } let(:sufficient_teacher_survey_item) { create(:teacher_survey_item, scale: sufficient_scale_1) }
let(:insufficient_teacher_survey_item) { create(:teacher_survey_item, measure: insufficient_measure) } let(:sufficient_student_survey_item_1) { create(:student_survey_item, scale: sufficient_scale_1) }
let(:sufficient_student_survey_item_2) { create(:student_survey_item, measure: sufficient_measure_2) } let(:insufficient_teacher_survey_item) { create(:teacher_survey_item, scale: insufficient_scale) }
let(:insufficient_student_survey_item) { create(:student_survey_item, measure: insufficient_measure) } let(:sufficient_student_survey_item_2) { create(:student_survey_item, scale: sufficient_scale_2) }
let(:insufficient_student_survey_item) { create(:student_survey_item, scale: insufficient_scale) }
before :each do before :each do
survey_respondents survey_respondents
@ -44,14 +47,17 @@ describe ResponseRate, type: :model do
describe TeacherResponseRate do describe TeacherResponseRate do
let(:subcategory) { create(:subcategory) } let(:subcategory) { create(:subcategory) }
let(:sufficient_measure_1) { create(:measure, subcategory: subcategory) } let(:sufficient_measure_1) { create(:measure, subcategory: subcategory) }
let(:sufficient_scale_1) { create(:scale, measure: sufficient_measure_1) }
let(:sufficient_measure_2) { create(:measure, subcategory: subcategory) } let(:sufficient_measure_2) { create(:measure, subcategory: subcategory) }
let(:sufficient_scale_2) { create(:scale, measure: sufficient_measure_2) }
let(:insufficient_measure) { create(:measure, subcategory: subcategory) } let(:insufficient_measure) { create(:measure, subcategory: subcategory) }
let(:sufficient_teacher_survey_item_1) { create(:teacher_survey_item, measure: sufficient_measure_1) } let(:insufficient_scale) { create(:scale, measure: insufficient_measure) }
let(:sufficient_teacher_survey_item_2) { create(:teacher_survey_item, measure: sufficient_measure_1) } let(:sufficient_teacher_survey_item_1) { create(:teacher_survey_item, scale: sufficient_scale_1) }
let(:sufficient_teacher_survey_item_3) { create(:teacher_survey_item, measure: sufficient_measure_1) } let(:sufficient_teacher_survey_item_2) { create(:teacher_survey_item, scale: sufficient_scale_1) }
let(:sufficient_student_survey_item_1) { create(:student_survey_item, measure: sufficient_measure_1) } let(:sufficient_teacher_survey_item_3) { create(:teacher_survey_item, scale: sufficient_scale_1) }
let(:insufficient_teacher_survey_item) { create(:teacher_survey_item, measure: insufficient_measure) } let(:sufficient_student_survey_item_1) { create(:student_survey_item, scale: sufficient_scale_1) }
let(:insufficient_student_survey_item) { create(:student_survey_item, measure: insufficient_measure) } let(:insufficient_teacher_survey_item) { create(:teacher_survey_item, scale: insufficient_scale) }
let(:insufficient_student_survey_item) { create(:student_survey_item, scale: insufficient_scale) }
before :each do before :each do
survey_respondents survey_respondents

@ -0,0 +1,41 @@
require 'rails_helper'
RSpec.describe Scale, type: :model do
let(:school) { create(:school) }
let(:academic_year) { create(:academic_year) }
let(:scale) { create(:scale) }
describe '.score' do
let(:teacher_survey_item_1) { create(:teacher_survey_item, scale:) }
let(:teacher_survey_item_2) { create(:teacher_survey_item, scale:) }
let(:teacher_survey_item_3) { create(:teacher_survey_item, scale:) }
before :each do
create(:survey_item_response,
survey_item: teacher_survey_item_1, academic_year:, school:, likert_score: 3)
create(:survey_item_response,
survey_item: teacher_survey_item_2, academic_year:, school:, likert_score: 4)
create(:survey_item_response,
survey_item: teacher_survey_item_3, academic_year:, school:, likert_score: 5)
end
it 'returns the average of the likert scores of the survey items' do
expect(scale.score(school:, academic_year:)).to eq 4
end
context 'when other scales exist' do
before :each do
create(:survey_item_response,
academic_year:, school:, likert_score: 1)
create(:survey_item_response,
academic_year:, school:, likert_score: 1)
create(:survey_item_response,
academic_year:, school:, likert_score: 1)
end
it 'does not affect the score for the original scale' do
expect(scale.score(school:, academic_year:)).to eq 4
end
end
end
end

@ -0,0 +1,60 @@
require 'rails_helper'
RSpec.describe Subcategory, type: :model do
let(:school) { create(:school) }
let(:academic_year) { create(:academic_year) }
let(:subcategory) { create(:subcategory) }
let(:measure_1) { create(:measure, subcategory: subcategory) }
let(:teacher_scale) { create(:teacher_scale, measure: measure_1) }
let(:measure_2) { create(:measure, subcategory: subcategory) }
let(:student_scale) { create(:student_scale, measure: measure_2) }
describe '.score' do
let(:teacher_survey_item_1) { create(:teacher_survey_item, scale: teacher_scale) }
let(:teacher_survey_item_2) { create(:teacher_survey_item, scale: teacher_scale) }
let(:teacher_survey_item_3) { create(:teacher_survey_item, scale: teacher_scale) }
let(:student_survey_item_1) { create(:student_survey_item, scale: student_scale) }
let(:student_survey_item_2) { create(:student_survey_item, scale: student_scale) }
let(:student_survey_item_3) { create(:student_survey_item, scale: student_scale) }
let(:student_survey_item_4) { create(:student_survey_item, scale: student_scale) }
let(:student_survey_item_5) { create(:student_survey_item, scale: student_scale) }
before :each do
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD,
survey_item: teacher_survey_item_1, academic_year:, school:, likert_score: 2)
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD,
survey_item: teacher_survey_item_2, academic_year:, school:, likert_score: 3)
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD,
survey_item: teacher_survey_item_3, academic_year:, school:, likert_score: 4)
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD,
survey_item: student_survey_item_1, academic_year:, school:, likert_score: 1)
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD,
survey_item: student_survey_item_2, academic_year:, school:, likert_score: 2)
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD,
survey_item: student_survey_item_3, academic_year:, school:, likert_score: 3)
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD,
survey_item: student_survey_item_4, academic_year:, school:, likert_score: 4)
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD,
survey_item: student_survey_item_5, academic_year:, school:, likert_score: 5)
end
it 'returns the average of the likert scores of the measures' do
expect(subcategory.score(school:, academic_year:)).to eq 3
end
context 'when other subcategories exist' do
before :each do
create(:survey_item_response,
academic_year:, school:, likert_score: 1)
create(:survey_item_response,
academic_year:, school:, likert_score: 1)
create(:survey_item_response,
academic_year:, school:, likert_score: 1)
end
it 'does not affect the score for the original scale' do
expect(subcategory.score(school:, academic_year:)).to eq 3
end
end
end
end

@ -1,337 +0,0 @@
require 'rails_helper'
describe SurveyItemResponse, type: :model do
let(:school) { create(:school) }
let(:ay) { create(:academic_year) }
describe '.score_for_measure' do
let(:measure) { create(:measure) }
context 'when the measure includes only teacher data' do
let(:teacher_survey_item_1) { create(:teacher_survey_item, measure: measure) }
let(:teacher_survey_item_2) { create(:teacher_survey_item, measure: measure) }
let(:teacher_survey_item_3) { create(:teacher_survey_item, measure: measure) }
context "and the number of responses for each of the measure's survey items meets the teacher threshold of 17" do
before :each do
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD,
survey_item: teacher_survey_item_1, academic_year: ay, school: school, likert_score: 3)
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD,
survey_item: teacher_survey_item_2, academic_year: ay, school: school, likert_score: 4)
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD,
survey_item: teacher_survey_item_3, academic_year: ay, school: school, likert_score: 5)
end
it 'returns the average of the likert scores of the survey items' do
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).average).to eq 4
end
it 'affirms that the result meets the teacher threshold' do
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).meets_teacher_threshold?).to be true
end
it 'reports the result does not meeet student threshold' do
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).meets_student_threshold?).to be false
end
end
context "and the average number of responses across the measure's survey items meets the teacher threshold of 17" do
before :each do
create_list(:survey_item_response, 19, survey_item: teacher_survey_item_1, academic_year: ay, school: school,
likert_score: 3)
create_list(:survey_item_response, 16, survey_item: teacher_survey_item_2, academic_year: ay, school: school,
likert_score: 4)
create_list(:survey_item_response, 16, survey_item: teacher_survey_item_3, academic_year: ay, school: school,
likert_score: 5)
end
it 'returns the average of the likert scores of the survey items' do
average_score = 3.941
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).average).to be_within(0.001).of(average_score)
end
end
context "and none of the measure's survey items meets the teacher threshold of 17" do
before :each do
create_list(:survey_item_response, 16, survey_item: teacher_survey_item_1, academic_year: ay, school: school,
likert_score: rand)
create_list(:survey_item_response, 16, survey_item: teacher_survey_item_2, academic_year: ay, school: school,
likert_score: rand)
create_list(:survey_item_response, 16, survey_item: teacher_survey_item_3, academic_year: ay, school: school,
likert_score: rand)
end
it 'returns nil' do
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).average).to be_nil
end
it 'affirms that the result does not meet the threshold' do
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).meets_teacher_threshold?).to be false
end
end
context "and the average number of responses across the measure's survey items does not meet the teacher threshold of 17" do
before :each do
create_list(:survey_item_response, 18, survey_item: teacher_survey_item_1, academic_year: ay, school: school,
likert_score: rand)
create_list(:survey_item_response, 16, survey_item: teacher_survey_item_2, academic_year: ay, school: school,
likert_score: rand)
create_list(:survey_item_response, 16, survey_item: teacher_survey_item_3, academic_year: ay, school: school,
likert_score: rand)
end
it 'returns nil' do
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).average).to be_nil
end
it 'affirms that the result does not meet the threshold' do
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).meets_teacher_threshold?).to be false
end
end
end
context 'when the measure includes only student data' do
let(:student_survey_item_1) { create(:student_survey_item, measure: measure) }
let(:student_survey_item_2) { create(:student_survey_item, measure: measure) }
let(:student_survey_item_3) { create(:student_survey_item, measure: measure) }
context "and the number of responses for each of the measure's survey items meets the student threshold of 196" do
before :each do
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD,
survey_item: student_survey_item_1, academic_year: ay, school: school, likert_score: 3)
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD,
survey_item: student_survey_item_2, academic_year: ay, school: school, likert_score: 4)
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD,
survey_item: student_survey_item_3, academic_year: ay, school: school, likert_score: 5)
end
it 'returns the average of the likert scores of the survey items' do
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).average).to eq 4
end
it 'affirms that the result meets the student threshold' do
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).meets_student_threshold?).to be true
end
it 'notes that the result does not meet the teacher threshold' do
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).meets_teacher_threshold?).to be false
end
end
context "and the average number of responses across the measure's survey items meets the student threshold of 196" do
before :each do
create_list(:survey_item_response, 200, survey_item: student_survey_item_1, academic_year: ay,
school: school, likert_score: 3)
create_list(:survey_item_response, 195, survey_item: student_survey_item_2, academic_year: ay,
school: school, likert_score: 4)
create_list(:survey_item_response, 193, survey_item: student_survey_item_3, academic_year: ay,
school: school, likert_score: 5)
end
it 'returns the average of the likert scores of the survey items' do
average_score = 3.988
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).average).to be_within(0.001).of(average_score)
end
end
context "and none of the measure's survey items meets the student threshold of 196" do
before :each do
create_list(:survey_item_response, 195, survey_item: student_survey_item_1, academic_year: ay,
school: school, likert_score: rand)
create_list(:survey_item_response, 195, survey_item: student_survey_item_2, academic_year: ay,
school: school, likert_score: rand)
create_list(:survey_item_response, 195, survey_item: student_survey_item_3, academic_year: ay,
school: school, likert_score: rand)
end
it 'returns nil' do
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).average).to be_nil
end
it 'affirms that the result does not meet the threshold' do
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).meets_student_threshold?).to be false
end
end
context "and the average number of responses across the measure's survey items does not meet the student threshold of 196" do
before :each do
create_list(:survey_item_response, 200, survey_item: student_survey_item_1, academic_year: ay,
school: school, likert_score: rand)
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD,
survey_item: student_survey_item_2, academic_year: ay, school: school, likert_score: rand)
create_list(:survey_item_response, 191, survey_item: student_survey_item_3, academic_year: ay,
school: school, likert_score: rand)
end
it 'returns nil' do
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).average).to be_nil
end
it 'affirms that the result does not meet the threshold' do
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).meets_student_threshold?).to be false
end
end
end
context 'when the measure includes both teacher and student data' do
let(:teacher_survey_item_1) { create(:teacher_survey_item, measure: measure) }
let(:student_survey_item_1) { create(:student_survey_item, measure: measure) }
context 'and there is sufficient teacher data and sufficient student data' do
before :each do
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD,
survey_item: teacher_survey_item_1, academic_year: ay, school: school, likert_score: 5)
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD,
survey_item: student_survey_item_1, academic_year: ay, school: school, likert_score: 5)
end
it 'returns the average of the likert scores of the survey items' do
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).average).to eq 5
end
it 'affirms that the result does meet the thresholds' do
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).meets_teacher_threshold?).to be true
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).meets_student_threshold?).to be true
end
end
context 'and there is sufficient teacher data and insufficient student data' do
before :each do
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD,
survey_item: teacher_survey_item_1, academic_year: ay, school: school, likert_score: 5)
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD - 1,
survey_item: student_survey_item_1, academic_year: ay, school: school, likert_score: 1)
end
it 'returns the average of the likert scores of the teacher survey items' do
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).average).to eq 5
end
it 'affirms that the result meets the teacher threshold but not the student threshold' do
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).meets_teacher_threshold?).to be true
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).meets_student_threshold?).to be false
end
end
context 'and there is insufficient teacher data and sufficient student data' do
before :each do
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD - 1,
survey_item: teacher_survey_item_1, academic_year: ay, school: school, likert_score: 1)
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD,
survey_item: student_survey_item_1, academic_year: ay, school: school, likert_score: 5)
end
it 'returns the average of the likert scores of the student survey items' do
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).average).to eq 5
end
it 'affirms that the result meets the student threshold but not the teacher threshold' do
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).meets_teacher_threshold?).to be false
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).meets_student_threshold?).to be true
end
end
context 'and there is insufficient teacher data and insufficient student data' do
before :each do
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD - 1,
survey_item: teacher_survey_item_1, academic_year: ay, school: school)
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD - 1,
survey_item: student_survey_item_1, academic_year: ay, school: school)
end
it 'returns nil' do
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).average).to be_nil
end
it 'affirms that the result does not meet either threshold' do
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).meets_teacher_threshold?).to be false
expect(SurveyItemResponse.score_for_measure(measure: measure, school: school,
academic_year: ay).meets_student_threshold?).to be false
end
end
end
end
describe '.score_for_subcategory' do
let(:subcategory) { create(:subcategory) }
let(:sufficient_measure_1) { create(:measure, subcategory: subcategory) }
let(:sufficient_measure_2) { create(:measure, subcategory: subcategory) }
let(:insufficient_measure) { create(:measure, subcategory: subcategory) }
let(:sufficient_teacher_survey_item) { create(:teacher_survey_item, measure: sufficient_measure_1) }
let(:insufficient_teacher_survey_item) { create(:teacher_survey_item, measure: insufficient_measure) }
let(:sufficient_student_survey_item) { create(:student_survey_item, measure: sufficient_measure_2) }
let(:insufficient_student_survey_item) { create(:student_survey_item, measure: insufficient_measure) }
before :each do
largest_threshold = [SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD,
SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD].max
create_list(:survey_item_response, largest_threshold, survey_item: sufficient_teacher_survey_item,
academic_year: ay, school: school, likert_score: 1)
create_list(:survey_item_response, largest_threshold, survey_item: sufficient_student_survey_item,
academic_year: ay, school: school, likert_score: 4)
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD - 1,
survey_item: insufficient_teacher_survey_item, academic_year: ay, school: school, likert_score: 1)
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD - 1,
survey_item: insufficient_student_survey_item, academic_year: ay, school: school, likert_score: 1)
end
it 'returns the average score of all survey item responses for measures meeting their respective thresholds' do
expect(SurveyItemResponse.score_for_subcategory(subcategory: subcategory, school: school,
academic_year: ay)).to eq 2.5
end
end
describe '.responses_for_measure' do
let(:subcategory) { create(:subcategory) }
let(:sufficient_measure_1) { create(:measure, subcategory: subcategory) }
let(:sufficient_measure_2) { create(:measure, subcategory: subcategory) }
let(:insufficient_measure) { create(:measure, subcategory: subcategory) }
let(:sufficient_teacher_survey_item) { create(:teacher_survey_item, measure: sufficient_measure_1) }
let(:insufficient_teacher_survey_item) { create(:teacher_survey_item, measure: insufficient_measure) }
let(:sufficient_student_survey_item) { create(:student_survey_item, measure: sufficient_measure_2) }
let(:insufficient_student_survey_item) { create(:student_survey_item, measure: insufficient_measure) }
before :each do
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD, survey_item: sufficient_teacher_survey_item,
academic_year: ay, school: school, likert_score: 1)
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD, survey_item: sufficient_student_survey_item,
academic_year: ay, school: school, likert_score: 4)
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD - 1,
survey_item: insufficient_teacher_survey_item, academic_year: ay, school: school, likert_score: 1)
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD - 1,
survey_item: insufficient_student_survey_item, academic_year: ay, school: school, likert_score: 1)
end
it 'returns only responses in a measure that meets the low threshold' do
expect(SurveyItemResponse.responses_for_measure(measure: sufficient_measure_1, school: school, academic_year: ay).count).to eq SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD
expect(SurveyItemResponse.responses_for_measure(measure: sufficient_measure_2, school: school, academic_year: ay).count).to eq SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD
expect(SurveyItemResponse.responses_for_measure(measure: insufficient_measure, school: school, academic_year: ay)).to be nil
end
end
end

@ -0,0 +1,38 @@
require 'rails_helper'
RSpec.describe SurveyItem, type: :model do
let(:school) { create(:school) }
let(:academic_year) { create(:academic_year) }
let(:scale) { create(:scale) }
describe '.score' do
let(:teacher_survey_item) { create(:teacher_survey_item, scale:) }
before :each do
create(:survey_item_response,
survey_item: teacher_survey_item, academic_year:, school:, likert_score: 3)
create(:survey_item_response,
survey_item: teacher_survey_item, academic_year:, school:, likert_score: 4)
create(:survey_item_response,
survey_item: teacher_survey_item, academic_year:, school:, likert_score: 5)
end
it 'returns the average of the likert scores of the survey items' do
expect(teacher_survey_item.score(school:, academic_year:)).to eq 4
end
context 'when other scales exist' do
before :each do
create(:survey_item_response,
academic_year:, school:, likert_score: 1)
create(:survey_item_response,
academic_year:, school:, likert_score: 1)
create(:survey_item_response,
academic_year:, school:, likert_score: 1)
end
it 'does not affect the score for the original scale' do
expect(scale.score(school:, academic_year:)).to eq 4
end
end
end
end

@ -1,8 +1,8 @@
require 'rails_helper' require 'rails_helper'
describe GaugePresenter do describe GaugePresenter do
let(:scale) do let(:zones) do
Scale.new( Zones.new(
watch_low_benchmark: 1.5, watch_low_benchmark: 1.5,
growth_low_benchmark: 2.5, growth_low_benchmark: 2.5,
approval_low_benchmark: 3.5, approval_low_benchmark: 3.5,
@ -11,7 +11,7 @@ describe GaugePresenter do
end end
let(:score) { 3 } let(:score) { 3 }
let(:gauge_presenter) { GaugePresenter.new(scale: scale, score: score) } let(:gauge_presenter) { GaugePresenter.new(zones: zones, score: score) }
it 'returns the key benchmark percentage for the gauge' do it 'returns the key benchmark percentage for the gauge' do
expect(gauge_presenter.key_benchmark_percentage).to eq 0.625 expect(gauge_presenter.key_benchmark_percentage).to eq 0.625
@ -98,8 +98,8 @@ describe GaugePresenter do
end end
context 'when there are no benchmarks or score for the gauge' do context 'when there are no benchmarks or score for the gauge' do
let(:scale) do let(:zones) do
Scale.new( Zones.new(
watch_low_benchmark: nil, watch_low_benchmark: nil,
growth_low_benchmark: nil, growth_low_benchmark: nil,
approval_low_benchmark: nil, approval_low_benchmark: nil,

@ -4,7 +4,10 @@ describe MeasurePresenter do
let(:academic_year) { create(:academic_year, range: '1989-90') } let(:academic_year) { create(:academic_year, range: '1989-90') }
let(:school) { create(:school, name: 'Best School') } let(:school) { create(:school, name: 'Best School') }
let(:measure) { create(:measure, measure_id: 'measure-id') } let(:measure) { create(:measure, measure_id: 'measure-id') }
let(:measure_presenter) { MeasurePresenter.new(measure: measure, academic_year: academic_year, school: school) } let(:teacher_scale) { create(:teacher_scale, measure:) }
let(:student_scale) { create(:student_scale, measure:) }
let(:admin_scale) { create(:scale, measure:) }
let(:measure_presenter) { MeasurePresenter.new(measure:, academic_year:, school:) }
it 'returns the id of the measure' do it 'returns the id of the measure' do
expect(measure_presenter.id).to eq 'measure-id' expect(measure_presenter.id).to eq 'measure-id'
@ -16,13 +19,13 @@ describe MeasurePresenter do
context 'when the measure contains only teacher data' do context 'when the measure contains only teacher data' do
before :each do before :each do
survey_item1 = create(:teacher_survey_item, measure: measure, prompt: 'A teacher survey item prompt') survey_item1 = create(:teacher_survey_item, scale: teacher_scale, prompt: 'A teacher survey item prompt')
survey_item2 = create(:teacher_survey_item, measure: measure, prompt: 'Another teacher survey item prompt') survey_item2 = create(:teacher_survey_item, scale: teacher_scale, prompt: 'Another teacher survey item prompt')
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD, survey_item: survey_item1, create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD, survey_item: survey_item1,
academic_year: academic_year, school: school, likert_score: 1) academic_year:, school:, likert_score: 1)
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD, survey_item: survey_item2, create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD, survey_item: survey_item2,
academic_year: academic_year, school: school, likert_score: 5) academic_year:, school:, likert_score: 5)
end end
it 'creates a gauge presenter that presents the average likert score' do it 'creates a gauge presenter that presents the average likert score' do
@ -41,10 +44,10 @@ describe MeasurePresenter do
context 'when the measure contains both teacher data and admin data' do context 'when the measure contains both teacher data and admin data' do
before :each do before :each do
create(:teacher_survey_item, measure: measure, prompt: 'A teacher survey item prompt') create(:teacher_survey_item, scale: teacher_scale, prompt: 'A teacher survey item prompt')
create(:teacher_survey_item, measure: measure, prompt: 'Another teacher survey item prompt') create(:teacher_survey_item, scale: teacher_scale, prompt: 'Another teacher survey item prompt')
create(:admin_data_item, measure: measure, description: 'An admin data item description') create(:admin_data_item, scale: admin_scale, description: 'An admin data item description')
create(:admin_data_item, measure: measure, description: 'Another admin data item description') create(:admin_data_item, scale: admin_scale, description: 'Another admin data item description')
end end
it 'returns a list of data item presenters with two elements' do it 'returns a list of data item presenters with two elements' do
@ -68,13 +71,13 @@ describe MeasurePresenter do
context 'when the measure has partial data for teachers and students' do context 'when the measure has partial data for teachers and students' do
before :each do before :each do
teacher_survey_item = create(:teacher_survey_item, measure: measure) teacher_survey_item = create(:teacher_survey_item, scale: teacher_scale)
student_survey_item = create(:student_survey_item, measure: measure) student_survey_item = create(:student_survey_item, scale: student_scale)
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD, create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD,
survey_item: teacher_survey_item, academic_year: academic_year, school: school) survey_item: teacher_survey_item, academic_year:, school:)
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD - 1, create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD - 1,
survey_item: student_survey_item, academic_year: academic_year, school: school) survey_item: student_survey_item, academic_year:, school:)
end end
it 'tracks which parts of the data are sufficient' do it 'tracks which parts of the data are sufficient' do
@ -91,9 +94,9 @@ describe MeasurePresenter do
context 'when the measure has insufficient admin data and insufficient teacher/student data' do context 'when the measure has insufficient admin data and insufficient teacher/student data' do
before :each do before :each do
create(:admin_data_item, measure: measure) create(:admin_data_item, scale: admin_scale)
create(:teacher_survey_item, measure: measure) create(:teacher_survey_item, scale: teacher_scale)
create(:student_survey_item, measure: measure) create(:student_survey_item, scale: student_scale)
end end
it 'tracks the reason for their insufficiency' do it 'tracks the reason for their insufficiency' do

@ -1,30 +1,30 @@
require 'rails_helper' require 'rails_helper'
describe Scale do describe Zones do
describe '#zone_for_score' do describe '#zone_for_score' do
let(:scale) do let(:zones) do
Scale.new watch_low_benchmark: 1.5, growth_low_benchmark: 2.5, approval_low_benchmark: 3.5, Zones.new watch_low_benchmark: 1.5, growth_low_benchmark: 2.5, approval_low_benchmark: 3.5,
ideal_low_benchmark: 4.5 ideal_low_benchmark: 4.5
end end
context 'when the score is 1.0' do context 'when the score is 1.0' do
it 'returns the warning zone' do it 'returns the warning zone' do
expect(scale.zone_for_score(1.0)).to eq scale.warning_zone expect(zones.zone_for_score(1.0)).to eq zones.warning_zone
end end
end end
context 'when the score is 5.0' do context 'when the score is 5.0' do
it 'returns the ideal zone' do it 'returns the ideal zone' do
expect(scale.zone_for_score(5.0)).to eq scale.ideal_zone expect(zones.zone_for_score(5.0)).to eq zones.ideal_zone
end end
end end
context 'when the score is right on the benchmark' do context 'when the score is right on the benchmark' do
it 'returns the higher zone' do it 'returns the higher zone' do
expect(scale.zone_for_score(1.5)).to eq scale.watch_zone expect(zones.zone_for_score(1.5)).to eq zones.watch_zone
expect(scale.zone_for_score(2.5)).to eq scale.growth_zone expect(zones.zone_for_score(2.5)).to eq zones.growth_zone
expect(scale.zone_for_score(3.5)).to eq scale.approval_zone expect(zones.zone_for_score(3.5)).to eq zones.approval_zone
expect(scale.zone_for_score(4.5)).to eq scale.ideal_zone expect(zones.zone_for_score(4.5)).to eq zones.ideal_zone
end end
end end
end end

@ -1,8 +1,8 @@
require 'rails_helper' require 'rails_helper'
describe SubcategoryCardPresenter do describe SubcategoryCardPresenter do
let(:scale) do let(:zones) do
Scale.new( Zones.new(
watch_low_benchmark: 1.5, watch_low_benchmark: 1.5,
growth_low_benchmark: 2.5, growth_low_benchmark: 2.5,
approval_low_benchmark: 3.5, approval_low_benchmark: 3.5,
@ -10,7 +10,7 @@ describe SubcategoryCardPresenter do
) )
end end
let(:subcategory_card_presenter) { SubcategoryCardPresenter.new(name: 'Card name', scale: scale, score: score) } let(:subcategory_card_presenter) { SubcategoryCardPresenter.new(name: 'Card name', zones: zones, score: score) }
context 'when the given score is in the Warning zone for the given scale' do context 'when the given score is in the Warning zone for the given scale' do
let(:score) { 1 } let(:score) { 1 }

@ -13,9 +13,11 @@ describe SubcategoryPresenter do
let(:subcategory_presenter) do let(:subcategory_presenter) do
survey_respondents survey_respondents
measure1 = create(:measure, subcategory:) measure1 = create(:measure, subcategory:)
survey_item1 = create(:teacher_survey_item, measure: measure1, watch_low_benchmark: 4, growth_low_benchmark: 4.25, teacher_scale_1 = create(:teacher_scale, measure: measure1)
student_scale_1 = create(:student_scale, measure: measure1)
survey_item1 = create(:teacher_survey_item, scale: teacher_scale_1, watch_low_benchmark: 4, growth_low_benchmark: 4.25,
approval_low_benchmark: 4.5, ideal_low_benchmark: 4.75) approval_low_benchmark: 4.5, ideal_low_benchmark: 4.75)
survey_item2 = create(:student_survey_item, measure: measure1, watch_low_benchmark: 4, growth_low_benchmark: 4.25, survey_item2 = create(:student_survey_item, scale: student_scale_1, watch_low_benchmark: 4, growth_low_benchmark: 4.25,
approval_low_benchmark: 4.5, ideal_low_benchmark: 4.75) approval_low_benchmark: 4.5, ideal_low_benchmark: 4.75)
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD, survey_item: survey_item1, create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD, survey_item: survey_item1,
academic_year:, school:, likert_score: 1) academic_year:, school:, likert_score: 1)
@ -27,9 +29,12 @@ describe SubcategoryPresenter do
academic_year:, school:, likert_score: 3) academic_year:, school:, likert_score: 3)
measure2 = create(:measure, subcategory:) measure2 = create(:measure, subcategory:)
survey_item3 = create(:teacher_survey_item, measure: measure2, watch_low_benchmark: 1.25, teacher_scale_2 = create(:teacher_scale, measure: measure2)
student_scale_2 = create(:student_scale, measure: measure2)
survey_item3 = create(:teacher_survey_item, scale: teacher_scale_2, watch_low_benchmark: 1.25,
growth_low_benchmark: 1.5, approval_low_benchmark: 1.75, ideal_low_benchmark: 2.0) growth_low_benchmark: 1.5, approval_low_benchmark: 1.75, ideal_low_benchmark: 2.0)
survey_item4 = create(:student_survey_item, measure: measure2, watch_low_benchmark: 1.25, survey_item4 = create(:student_survey_item, scale: student_scale_2, watch_low_benchmark: 1.25,
growth_low_benchmark: 1.5, approval_low_benchmark: 1.75, ideal_low_benchmark: 2.0) growth_low_benchmark: 1.5, approval_low_benchmark: 1.75, ideal_low_benchmark: 2.0)
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD, survey_item: survey_item3, create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD, survey_item: survey_item3,
academic_year:, school:, likert_score: 1) academic_year:, school:, likert_score: 1)
@ -100,8 +105,9 @@ describe SubcategoryPresenter do
context 'and the measure does not include high-school-only admin data items' do context 'and the measure does not include high-school-only admin data items' do
before do before do
measure_of_only_admin_data = create(:measure, subcategory:) measure_of_only_admin_data = create(:measure, subcategory:)
create(:admin_data_item, measure: measure_of_only_admin_data, hs_only_item: false) scale_of_only_admin_data = create(:scale, measure: measure_of_only_admin_data)
create(:admin_data_item, measure: measure_of_only_admin_data, hs_only_item: false) create(:admin_data_item, scale: scale_of_only_admin_data, hs_only_item: false)
create(:admin_data_item, scale: scale_of_only_admin_data, hs_only_item: false)
end end
it 'returns the admin collection rate' do it 'returns the admin collection rate' do
expect(subcategory_presenter.admin_collection_rate).to eq [0, 2] expect(subcategory_presenter.admin_collection_rate).to eq [0, 2]
@ -111,8 +117,9 @@ describe SubcategoryPresenter do
context 'and the measure includes high-school-only items' do context 'and the measure includes high-school-only items' do
before do before do
measure_of_only_admin_data = create(:measure, subcategory:) measure_of_only_admin_data = create(:measure, subcategory:)
create(:admin_data_item, measure: measure_of_only_admin_data, hs_only_item: true) scale_of_only_admin_data = create(:scale, measure: measure_of_only_admin_data)
create(:admin_data_item, measure: measure_of_only_admin_data, hs_only_item: true) create(:admin_data_item, scale: scale_of_only_admin_data, hs_only_item: true)
create(:admin_data_item, scale: scale_of_only_admin_data, hs_only_item: true)
end end
it 'returns the admin collection rate' do it 'returns the admin collection rate' do
expect(subcategory_presenter.admin_collection_rate).to eq %w[N A] expect(subcategory_presenter.admin_collection_rate).to eq %w[N A]
@ -126,8 +133,9 @@ describe SubcategoryPresenter do
school.is_hs = true school.is_hs = true
school.save school.save
measure_of_only_admin_data = create(:measure, subcategory:) measure_of_only_admin_data = create(:measure, subcategory:)
create(:admin_data_item, measure: measure_of_only_admin_data, hs_only_item: false) scale_of_only_admin_data = create(:scale, measure: measure_of_only_admin_data)
create(:admin_data_item, measure: measure_of_only_admin_data, hs_only_item: false) create(:admin_data_item, scale: scale_of_only_admin_data, hs_only_item: false)
create(:admin_data_item, scale: scale_of_only_admin_data, hs_only_item: false)
end end
it 'returns the admin collection rate' do it 'returns the admin collection rate' do
expect(subcategory_presenter.admin_collection_rate).to eq [0, 2] expect(subcategory_presenter.admin_collection_rate).to eq [0, 2]
@ -139,8 +147,9 @@ describe SubcategoryPresenter do
school.is_hs = true school.is_hs = true
school.save school.save
measure_of_only_admin_data = create(:measure, subcategory:) measure_of_only_admin_data = create(:measure, subcategory:)
create(:admin_data_item, measure: measure_of_only_admin_data, hs_only_item: true) scale_of_only_admin_data = create(:scale, measure: measure_of_only_admin_data)
create(:admin_data_item, measure: measure_of_only_admin_data, hs_only_item: true) create(:admin_data_item, scale: scale_of_only_admin_data, hs_only_item: true)
create(:admin_data_item, scale: scale_of_only_admin_data, hs_only_item: true)
end end
it 'returns the admin collection rate' do it 'returns the admin collection rate' do
expect(subcategory_presenter.admin_collection_rate).to eq [0, 2] expect(subcategory_presenter.admin_collection_rate).to eq [0, 2]

@ -11,12 +11,13 @@ describe VarianceChartRowPresenter do
:measure, :measure,
name: 'Some Title' name: 'Some Title'
) )
scale = create(:scale, measure:)
create(:student_survey_item, measure: measure, create(:student_survey_item, scale:,
watch_low_benchmark: watch_low_benchmark, watch_low_benchmark:,
growth_low_benchmark: growth_low_benchmark, growth_low_benchmark:,
approval_low_benchmark: approval_low_benchmark, approval_low_benchmark:,
ideal_low_benchmark: ideal_low_benchmark) ideal_low_benchmark:)
measure measure
end end
@ -29,7 +30,7 @@ describe VarianceChartRowPresenter do
end end
let(:presenter) do let(:presenter) do
VarianceChartRowPresenter.new measure: measure, score: score VarianceChartRowPresenter.new measure:, score:
end end
shared_examples_for 'measure_name' do shared_examples_for 'measure_name' do
@ -155,8 +156,9 @@ describe VarianceChartRowPresenter do
:measure, :measure,
name: 'Some Title' name: 'Some Title'
) )
scale_with_admin_data = create(:scale, measure: measure_with_admin_data)
create :admin_data_item, create :admin_data_item,
measure: measure_with_admin_data, scale: scale_with_admin_data,
watch_low_benchmark: watch_low_benchmark, watch_low_benchmark: watch_low_benchmark,
growth_low_benchmark: growth_low_benchmark, growth_low_benchmark: growth_low_benchmark,
approval_low_benchmark: approval_low_benchmark, approval_low_benchmark: approval_low_benchmark,
@ -172,7 +174,8 @@ describe VarianceChartRowPresenter do
context 'when a measure contains teacher survey items' do context 'when a measure contains teacher survey items' do
before :each do before :each do
create :teacher_survey_item, measure: measure scale = create(:scale, measure:)
create :teacher_survey_item, scale:
end end
context 'when there are insufficient teacher survey item responses' do context 'when there are insufficient teacher survey item responses' do
@ -193,7 +196,8 @@ describe VarianceChartRowPresenter do
context 'when a measure contains student survey items' do context 'when a measure contains student survey items' do
before :each do before :each do
create :student_survey_item, measure: measure scale = create(:scale, measure:)
create :student_survey_item, scale:
end end
context 'when there are insufficient student survey item responses' do context 'when there are insufficient student survey item responses' do
@ -205,7 +209,8 @@ describe VarianceChartRowPresenter do
context 'where there are also admin data items' do context 'where there are also admin data items' do
before :each do before :each do
create :admin_data_item, measure: measure scale = create(:scale, measure:)
create :admin_data_item, scale:
end end
it 'returns the sources for partial results of administrative data and student survey results' do it 'returns the sources for partial results of administrative data and student survey results' do
@ -224,12 +229,13 @@ describe VarianceChartRowPresenter do
context 'sorting scores' do context 'sorting scores' do
it 'selects a longer bar before a shorter bar for measures in the approval/ideal zones' do it 'selects a longer bar before a shorter bar for measures in the approval/ideal zones' do
scale_with_student_survey_items = create(:scale, measure:)
create(:student_survey_item, create(:student_survey_item,
measure: measure, scale: scale_with_student_survey_items,
watch_low_benchmark: watch_low_benchmark, watch_low_benchmark:,
growth_low_benchmark: growth_low_benchmark, growth_low_benchmark:,
approval_low_benchmark: approval_low_benchmark, approval_low_benchmark:,
ideal_low_benchmark: ideal_low_benchmark) ideal_low_benchmark:)
approval_presenter = VarianceChartRowPresenter.new measure: measure, score: Score.new(3.7, true, true) approval_presenter = VarianceChartRowPresenter.new measure: measure, score: Score.new(3.7, true, true)
ideal_presenter = VarianceChartRowPresenter.new measure: measure, score: Score.new(4.4, true, true) ideal_presenter = VarianceChartRowPresenter.new measure: measure, score: Score.new(4.4, true, true)
expect(ideal_presenter <=> approval_presenter).to be < 0 expect(ideal_presenter <=> approval_presenter).to be < 0

@ -9,19 +9,20 @@ describe 'District Admin', js: true do
let(:category) { Category.find_by_name('Teachers & Leadership') } let(:category) { Category.find_by_name('Teachers & Leadership') }
let(:subcategory) { Subcategory.find_by_name('Teachers & The Teaching Environment') } let(:subcategory) { Subcategory.find_by_name('Teachers & The Teaching Environment') }
let(:measures_for_subcategory) { Measure.where(subcategory:) } let(:measures_for_subcategory) { Measure.where(subcategory:) }
let(:survey_items_for_subcategory) { SurveyItem.where(measure: measures_for_subcategory) } let(:scales_for_subcategory) {Scale.where(measure: measures_for_subcategory)}
let(:survey_items_for_subcategory) { SurveyItem.where(scale: scales_for_subcategory) }
let(:measure_1A_i) { Measure.find_by_measure_id('1A-i') } let(:measure_1A_i) { Measure.find_by_measure_id('1A-i') }
let(:measure_2A_i) { Measure.find_by_measure_id('2A-i') } let(:measure_2A_i) { Measure.find_by_measure_id('2A-i') }
let(:measure_4C_i) { Measure.find_by_measure_id('4C-i') } let(:measure_4C_i) { Measure.find_by_measure_id('4C-i') }
let(:measure_with_no_survey_responses) { Measure.find_by_measure_id('3A-i') } let(:measure_with_no_survey_responses) { Measure.find_by_measure_id('3A-i') }
let(:survey_item_1_for_measure_1A_i) { SurveyItem.create measure: measure_1A_i, survey_item_id: rand.to_s } let(:survey_item_1_for_measure_1A_i) { SurveyItem.create scale: measure_1A_i.scales.first, survey_item_id: rand.to_s }
let(:survey_item_2_for_measure_1A_i) { SurveyItem.create measure: measure_1A_i, survey_item_id: rand.to_s } let(:survey_item_2_for_measure_1A_i) { SurveyItem.create scale: measure_1A_i.scales.first, survey_item_id: rand.to_s }
let(:survey_items_for_measure_1A_i) { SurveyItem.where(measure: measure_1A_i) } let(:survey_items_for_measure_1A_i) { measure_1A_i.survey_items }
let(:survey_items_for_measure_2A_i) { SurveyItem.where(measure: measure_2A_i) } let(:survey_items_for_measure_2A_i) { measure_2A_i.survey_items }
let(:survey_items_for_measure_4C_i) { SurveyItem.where(measure: measure_4C_i) } let(:survey_items_for_measure_4C_i) { measure_4C_i.survey_items }
let(:ay_2020_21) { AcademicYear.find_by_range '2020-21' } let(:ay_2020_21) { AcademicYear.find_by_range '2020-21' }
let(:ay_2019_20) { AcademicYear.find_by_range '2019-20' } let(:ay_2019_20) { AcademicYear.find_by_range '2019-20' }

@ -6,6 +6,7 @@ describe 'SQM Application' do
let(:academic_year) { create(:academic_year) } let(:academic_year) { create(:academic_year) }
let(:category) { create(:category) } let(:category) { create(:category) }
let(:measure) { create(:measure) } let(:measure) { create(:measure) }
let(:scale) {create(:scale, measure:)}
before :each do before :each do
driven_by :rack_test driven_by :rack_test
@ -26,7 +27,7 @@ describe 'SQM Application' do
context 'at least one measure meets its threshold' do context 'at least one measure meets its threshold' do
before :each do before :each do
teacher_survey_item = create(:teacher_survey_item, measure: measure) teacher_survey_item = create(:teacher_survey_item, scale:)
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD, create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD,
survey_item: teacher_survey_item, academic_year: academic_year, school: school) survey_item: teacher_survey_item, academic_year: academic_year, school: school)
end end

@ -6,32 +6,36 @@ describe 'categories/show' do
school = create(:school, name: 'Best School') school = create(:school, name: 'Best School')
category = create(:category, name: 'Some Category', description: 'Some description of the category') category = create(:category, name: 'Some Category', description: 'Some description of the category')
category_presenter = CategoryPresenter.new(category: category) category_presenter = CategoryPresenter.new(category:)
subcategory1 = create(:subcategory, category: category, name: 'A subcategory', subcategory1 = create(:subcategory, category:, name: 'A subcategory',
description: 'Some description of the subcategory') description: 'Some description of the subcategory')
subcategory2 = create(:subcategory_with_measures, category: category, name: 'Another subcategory', subcategory2 = create(:subcategory_with_measures, category:, name: 'Another subcategory',
description: 'Another description of the subcategory') description: 'Another description of the subcategory')
measure1 = create(:measure, subcategory: subcategory1) measure1 = create(:measure, subcategory: subcategory1)
survey_item1 = create(:student_survey_item, measure: measure1, watch_low_benchmark: 1.5, growth_low_benchmark: 2.5, scale1 = create(:student_scale, measure: measure1)
survey_item1 = create(:student_survey_item, scale: scale1, watch_low_benchmark: 1.5, growth_low_benchmark: 2.5,
approval_low_benchmark: 3.5, ideal_low_benchmark: 4.5) approval_low_benchmark: 3.5, ideal_low_benchmark: 4.5)
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD, survey_item: survey_item1, create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD, survey_item: survey_item1,
academic_year: academic_year, school: school, likert_score: 1) academic_year:, school:, likert_score: 3)
measure2 = create(:measure, name: 'The second measure name', description: 'The second measure description', measure2 = create(:measure, name: 'The second measure name', description: 'The second measure description',
subcategory: subcategory1) subcategory: subcategory2)
student_survey_item = create(:student_survey_item, measure: measure2, prompt: 'Some prompt for student data', scale2 = create(:student_scale, measure: measure2)
scale3 = create(:teacher_scale, measure: measure2)
student_survey_item = create(:student_survey_item, scale: scale2, prompt: 'Some prompt for student data',
watch_low_benchmark: 1.5, growth_low_benchmark: 2.5, approval_low_benchmark: 3.5, ideal_low_benchmark: 4.5) watch_low_benchmark: 1.5, growth_low_benchmark: 2.5, approval_low_benchmark: 3.5, ideal_low_benchmark: 4.5)
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD, create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD,
survey_item: student_survey_item, academic_year: academic_year, school: school, likert_score: 5) survey_item: student_survey_item, academic_year:, school:, likert_score: 5)
teacher_survey_item = create(:teacher_survey_item, measure: measure2, prompt: 'Some prompt for teacher data') teacher_survey_item = create(:teacher_survey_item, scale: scale3, prompt: 'Some prompt for teacher data')
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD, create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD,
survey_item: teacher_survey_item, academic_year: academic_year, school: school, likert_score: 3) survey_item: teacher_survey_item, academic_year:, school:, likert_score: 3)
admin_data_item = create(:admin_data_item, measure: measure2, description: 'Some admin data item description', admin_data_scale = create(:scale, measure: measure2)
watch_low_benchmark: 1.5, growth_low_benchmark: 2.5, approval_low_benchmark: 3.5, ideal_low_benchmark: 4.5) create(:admin_data_item, scale: admin_data_scale, description: 'Some admin data item description',
watch_low_benchmark: 1.5, growth_low_benchmark: 2.5, approval_low_benchmark: 3.5, ideal_low_benchmark: 4.5)
assign :category, category_presenter assign :category, category_presenter
assign :categories, [category_presenter] assign :categories, [category_presenter]
@ -44,45 +48,45 @@ describe 'categories/show' do
end end
it 'renders the category name and description' do it 'renders the category name and description' do
expect(rendered).to match /Some Category/ expect(rendered).to match(/Some Category/)
expect(rendered).to match /Some description of the category/ expect(rendered).to match(/Some description of the category/)
end end
context 'for each subcategory' do context 'for each subcategory' do
it 'renders the subcategory name' do it 'renders the subcategory name' do
expect(rendered).to match /A subcategory/ expect(rendered).to match(/A subcategory/)
expect(rendered).to match /Another subcategory/ expect(rendered).to match(/Another subcategory/)
end end
it 'renders the subcategory description' do it 'renders the subcategory description' do
expect(rendered).to match /Some description of the subcategory/ expect(rendered).to match(/Some description of the subcategory/)
expect(rendered).to match /Another description of the subcategory/ expect(rendered).to match(/Another description of the subcategory/)
end end
it 'renders the zone title and fill color for the gauge graph' do it 'renders the zone title and fill color for the gauge graph' do
expect(rendered).to match /Growth/ expect(rendered).to match(/Growth/)
expect(rendered).to match /fill-growth/ expect(rendered).to match(/fill-growth/)
end end
end end
context 'for each measure' do context 'for each measure' do
it 'renders the measure name' do it 'renders the measure name' do
expect(rendered).to match /The second measure name/ expect(rendered).to match(/The second measure name/)
end end
it 'renders the measure description' do it 'renders the measure description' do
expect(rendered).to match /The second measure description/ expect(rendered).to match(/The second measure description/)
end end
it 'renders a gauge graph and the zone title color' do it 'renders a gauge graph and the zone title color' do
expect(rendered).to match /Ideal/ expect(rendered).to match(/Approval/)
expect(rendered).to match /fill-ideal/ expect(rendered).to match(/fill-approval/)
end end
it 'renders the prompts for survey items and admin data that make up the measure' do it 'renders the prompts for survey items and admin data that make up the measure' do
expect(rendered).to match /Some prompt for student data/ expect(rendered).to match(/Some prompt for student data/)
expect(rendered).to match /Some prompt for teacher data/ expect(rendered).to match(/Some prompt for teacher data/)
expect(rendered).to match /Some admin data item description/ expect(rendered).to match(/Some admin data item description/)
end end
end end
end end

@ -5,8 +5,9 @@ describe 'overview/index' do
let(:support_for_teaching) do let(:support_for_teaching) do
measure = create(:measure, name: 'Support For Teaching Development & Growth', measure_id: '1') measure = create(:measure, name: 'Support For Teaching Development & Growth', measure_id: '1')
scale = create(:scale, measure:)
create(:student_survey_item, create(:student_survey_item,
measure: measure, scale:,
watch_low_benchmark: 1.5, watch_low_benchmark: 1.5,
growth_low_benchmark: 2.5, growth_low_benchmark: 2.5,
approval_low_benchmark: 3.5, approval_low_benchmark: 3.5,
@ -16,8 +17,9 @@ describe 'overview/index' do
let(:effective_leadership) do let(:effective_leadership) do
measure = create(:measure, name: 'Effective Leadership', measure_id: '2') measure = create(:measure, name: 'Effective Leadership', measure_id: '2')
scale = create(:scale, measure:)
create(:teacher_survey_item, create(:teacher_survey_item,
measure: measure, scale:,
watch_low_benchmark: 1.5, watch_low_benchmark: 1.5,
growth_low_benchmark: 2.5, growth_low_benchmark: 2.5,
approval_low_benchmark: 3.5, approval_low_benchmark: 3.5,
@ -27,8 +29,9 @@ describe 'overview/index' do
let(:professional_qualifications) do let(:professional_qualifications) do
measure = create(:measure, name: 'Professional Qualifications', measure_id: '3') measure = create(:measure, name: 'Professional Qualifications', measure_id: '3')
scale = create(:scale, measure:)
create(:admin_data_item, create(:admin_data_item,
measure: measure, scale:,
watch_low_benchmark: 1.5, watch_low_benchmark: 1.5,
growth_low_benchmark: 2.5, growth_low_benchmark: 2.5,
approval_low_benchmark: 3.5, approval_low_benchmark: 3.5,
@ -71,14 +74,15 @@ describe 'overview/index' do
context 'when all the presenters have a non-nil score' do context 'when all the presenters have a non-nil score' do
let(:variance_chart_row_presenters) do let(:variance_chart_row_presenters) do
measure = create(:measure, name: 'Display Me', measure_id: 'display-me') measure = create(:measure, name: 'Display Me', measure_id: 'display-me')
scale = create(:scale, measure:)
create(:student_survey_item, create(:student_survey_item,
measure: measure, scale:,
watch_low_benchmark: 1.5, watch_low_benchmark: 1.5,
growth_low_benchmark: 2.5, growth_low_benchmark: 2.5,
approval_low_benchmark: 3.5, approval_low_benchmark: 3.5,
ideal_low_benchmark: 4.5) ideal_low_benchmark: 4.5)
[ [
VarianceChartRowPresenter.new(measure: measure, VarianceChartRowPresenter.new(measure:,
score: Score.new(rand)) score: Score.new(rand))
] ]
end end

@ -6,8 +6,17 @@ describe 'overview/_variance_chart.html.erb' do
let(:survey_items1) { [create(:student_survey_item)] } let(:survey_items1) { [create(:student_survey_item)] }
let(:survey_items2) { [create(:student_survey_item)] } let(:survey_items2) { [create(:student_survey_item)] }
let(:higher_scoring_measure) { create(:measure, survey_items: survey_items1) } let(:higher_scoring_measure) do
let(:lower_scoring_measure) { create(:measure, survey_items: survey_items2) } measure = create(:measure)
create(:scale, measure:, survey_items: survey_items1)
measure
end
let(:lower_scoring_measure) do
measure = create(:measure)
create(:scale, measure:, survey_items: survey_items2)
measure
end
before :each do before :each do
presenters = [ presenters = [
@ -15,7 +24,7 @@ describe 'overview/_variance_chart.html.erb' do
VarianceChartRowPresenter.new(measure: higher_scoring_measure, score: Score.new(5)) VarianceChartRowPresenter.new(measure: higher_scoring_measure, score: Score.new(5))
] ]
render partial: 'variance_chart', locals: { presenters: presenters } render partial: 'variance_chart', locals: { presenters: }
end end
it 'displays higher scoring measures above lower scoring measures' do it 'displays higher scoring measures above lower scoring measures' do
@ -41,7 +50,7 @@ describe 'overview/_variance_chart.html.erb' do
VarianceChartRowPresenter.new(measure: another_measure_lacking_score, score: Score.new(nil)) VarianceChartRowPresenter.new(measure: another_measure_lacking_score, score: Score.new(nil))
] ]
render partial: 'variance_chart', locals: { presenters: presenters } render partial: 'variance_chart', locals: { presenters: }
end end
it "displays the text 'insufficient data' for an empty dataset" do it "displays the text 'insufficient data' for an empty dataset" do

Loading…
Cancel
Save