diff --git a/CHANGELOG.md b/CHANGELOG.md index dfeba1ff..cc60e9f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 - Add previous year data. - remove AcademicYear "2021-22" - `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` +- Add scale to framework. Calculations for scores bubble up through the framework. diff --git a/app/controllers/overview_controller.rb b/app/controllers/overview_controller.rb index 31a37e5e..c70a5d3a 100644 --- a/app/controllers/overview_controller.rb +++ b/app/controllers/overview_controller.rb @@ -7,7 +7,7 @@ class OverviewController < SqmApplicationController private 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) end diff --git a/app/controllers/sqm_application_controller.rb b/app/controllers/sqm_application_controller.rb index 2edf5f57..e0719c0c 100644 --- a/app/controllers/sqm_application_controller.rb +++ b/app/controllers/sqm_application_controller.rb @@ -16,7 +16,7 @@ class SqmApplicationController < ApplicationController @schools = School.includes([:district]).where(district: @district).order(:name) @academic_year = AcademicYear.find_by_range params[:year] @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 def district_slug diff --git a/app/lib/seeder.rb b/app/lib/seeder.rb index 6840592c..bd09bdc5 100644 --- a/app/lib/seeder.rb +++ b/app/lib/seeder.rb @@ -3,7 +3,7 @@ require 'csv' class Seeder def seed_academic_years(*academic_year_ranges) academic_year_ranges.each do |range| - AcademicYear.find_or_create_by! range: range + AcademicYear.find_or_create_by! range: end end @@ -89,8 +89,11 @@ class Seeder measure.save! 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'] - 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.growth_low_benchmark = growth_low if growth_low survey_item.approval_low_benchmark = approval_low if approval_low @@ -99,7 +102,7 @@ class Seeder end 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.growth_low_benchmark = growth_low if growth_low admin_data_item.approval_low_benchmark = approval_low if approval_low diff --git a/app/models/admin_data_item.rb b/app/models/admin_data_item.rb index 08249a73..1898ff90 100644 --- a/app/models/admin_data_item.rb +++ b/app/models/admin_data_item.rb @@ -1,11 +1,11 @@ class AdminDataItem < ActiveRecord::Base - belongs_to :measure + belongs_to :scale - scope :for_measures, ->(measure) { - joins(:measure).where('admin_data_items.measure': measure) + scope :for_measures, lambda { |measures| + joins(:scale).where('scale.measure': measures) } - scope :non_hs_items_for_measures, ->(measure) { + scope :non_hs_items_for_measures, lambda { |measure| for_measures(measure).where(hs_only_item: false) } end diff --git a/app/models/measure.rb b/app/models/measure.rb index 36e54703..ed68b745 100644 --- a/app/models/measure.rb +++ b/app/models/measure.rb @@ -1,34 +1,40 @@ class Measure < ActiveRecord::Base belongs_to :subcategory - has_many :survey_items - has_many :admin_data_items - + has_many :scales + has_many :admin_data_items, through: :scales + has_many :survey_items, through: :scales has_many :survey_item_responses, through: :survey_items - def self.none_meet_threshold?(school:, academic_year:) - none? do |measure| - SurveyItemResponse.sufficient_data?(measure: measure, school: school, academic_year: academic_year) - end + def none_meet_threshold?(school:, academic_year:) + !sufficient_data?(school:, academic_year:) end def teacher_survey_items - @teacher_survey_items ||= survey_items.where("survey_item_id LIKE 't-%'") + @teacher_survey_items ||= survey_items.teacher_survey_items end def student_survey_items - @student_survey_items ||= survey_items.where("survey_item_id LIKE 's-%'") + @student_survey_items ||= survey_items.student_survey_items + end + + def teacher_scales + @teacher_scales ||= scales.teacher_scales + end + + def student_scales + @student_scales ||= scales.student_scales end def includes_teacher_survey_items? - @includes_teacher_survey_items ||= teacher_survey_items.any? + teacher_survey_items.any? end def includes_student_survey_items? - @includes_student_survey_items ||= student_survey_items.any? + student_survey_items.any? end def includes_admin_data_items? - @includes_admin_data_items ||= admin_data_items.any? + admin_data_items.any? end def sources @@ -39,32 +45,61 @@ class Measure < ActiveRecord::Base sources end + def score(school:, academic_year:) + @score ||= Hash.new do |memo| + meets_student_threshold = sufficient_student_data?(school:, academic_year:) + meets_teacher_threshold = sufficient_teacher_data?(school:, academic_year:) + next Score.new(nil, false, false) if !meets_student_threshold && !meets_teacher_threshold + + scores = [] + scores << teacher_scales.map { |scale| scale.score(school:, academic_year:) }.average if meets_teacher_threshold + scores << student_scales.map { |scale| scale.score(school:, academic_year:) }.average if meets_student_threshold + memo[[school, academic_year]] = Score.new(scores.average, meets_teacher_threshold, meets_student_threshold) + end + + @score[[school, academic_year]] + end + def warning_low_benchmark 1 end def watch_low_benchmark - return @watch_low_benchmark unless @watch_low_benchmark.nil? - - @watch_low_benchmark = benchmark(:watch_low_benchmark) + @watch_low_benchmark ||= benchmark(:watch_low_benchmark) end def growth_low_benchmark - return @growth_low_benchmark unless @growth_low_benchmark.nil? - - @growth_low_benchmark = benchmark(:growth_low_benchmark) + @growth_low_benchmark ||= benchmark(:growth_low_benchmark) end def approval_low_benchmark - return @approval_low_benchmark unless @approval_low_benchmark.nil? - - @approval_low_benchmark = benchmark(:approval_low_benchmark) + @approval_low_benchmark ||= benchmark(:approval_low_benchmark) end def ideal_low_benchmark - return @ideal_low_benchmark unless @ideal_low_benchmark.nil? + @ideal_low_benchmark ||= benchmark(:ideal_low_benchmark) + end + + 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 private diff --git a/app/models/response_rate.rb b/app/models/response_rate.rb index f66db842..22c1b2c5 100644 --- a/app/models/response_rate.rb +++ b/app/models/response_rate.rb @@ -1,4 +1,4 @@ -class ResponseRate +module ResponseRate def initialize(subcategory:, school:, academic_year:) @subcategory = subcategory @school = school diff --git a/app/models/scale.rb b/app/models/scale.rb new file mode 100644 index 00000000..6976c497 --- /dev/null +++ b/app/models/scale.rb @@ -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 diff --git a/app/models/student_response_rate.rb b/app/models/student_response_rate.rb index c353d1f6..42cb3a81 100644 --- a/app/models/student_response_rate.rb +++ b/app/models/student_response_rate.rb @@ -1,17 +1,18 @@ -class StudentResponseRate < ResponseRate - def rate - super - end +class StudentResponseRate + include ResponseRate private def survey_item_count - @student_survey_item_count ||= SurveyItem.student_survey_items_for_measures(@subcategory.measures).count + @student_survey_item_count ||= @subcategory.measures.map { |measure| measure.student_survey_items.count }.sum end def response_count - @student_response_count ||= SurveyItemResponse.student_responses_for_measures(@subcategory.measures, @school, - @academic_year).count + @student_response_count ||= @subcategory.measures.map do |measure| + measure.student_survey_items.map do |survey_item| + survey_item.survey_item_responses.where(school: @school, academic_year: @academic_year).count + end.sum + end.sum end def total_possible_responses diff --git a/app/models/subcategory.rb b/app/models/subcategory.rb index c563f253..d6789d9e 100644 --- a/app/models/subcategory.rb +++ b/app/models/subcategory.rb @@ -2,4 +2,12 @@ class Subcategory < ActiveRecord::Base belongs_to :category has_many :measures + + def score(school:, academic_year:) + scores = measures.includes([:survey_items]).map do |measure| + measure.score(school:, academic_year:).average + end + scores = scores.reject(&:nil?) + scores.average + end end diff --git a/app/models/survey_item.rb b/app/models/survey_item.rb index 6e7a585e..1ad2e2e2 100644 --- a/app/models/survey_item.rb +++ b/app/models/survey_item.rb @@ -1,11 +1,21 @@ class SurveyItem < ActiveRecord::Base - belongs_to :measure + belongs_to :scale + has_one :measure, through: :scale + has_many :survey_item_responses - scope :student_survey_items_for_measures, lambda { |measures| - joins(:measure).where(measure: measures).where("survey_item_id LIKE 's-%'") + def score(school:, academic_year:) + @score ||= Hash.new do |memo| + memo[[school, academic_year]] = survey_item_responses.where(school:, academic_year:).average(:likert_score).to_f + end + + @score[[school, academic_year]] + end + + scope :student_survey_items, lambda { + where("survey_item_id LIKE 's-%'") } - scope :teacher_survey_items_for_measures, lambda { |measures| - joins(:measure).where(measure: measures).where("survey_item_id LIKE 't-%'") + scope :teacher_survey_items, lambda { + where("survey_item_id LIKE 't-%'") } end diff --git a/app/models/survey_item_response.rb b/app/models/survey_item_response.rb index 801cff08..03fbd142 100644 --- a/app/models/survey_item_response.rb +++ b/app/models/survey_item_response.rb @@ -5,100 +5,4 @@ class SurveyItemResponse < ActiveRecord::Base belongs_to :academic_year belongs_to :school belongs_to :survey_item - - def self.score_for_subcategory(subcategory:, school:, academic_year:) - measures = measures_with_sufficient_data(subcategory: subcategory, school: school, academic_year: academic_year) - - return nil unless measures.size.positive? - - measures.map do |measure| - responses_for_measure(measure: measure, school: school, academic_year: academic_year).average(:likert_score) - end.average - end - - def self.measures_with_sufficient_data(subcategory:, school:, academic_year:) - subcategory.measures.select do |measure| - sufficient_data?(measure: measure, school: school, academic_year: academic_year) - end - end - - def self.responses_for_measure(measure:, school:, academic_year:) - meets_teacher_threshold = teacher_sufficient_data? measure: measure, school: school, academic_year: academic_year - meets_student_threshold = student_sufficient_data? measure: measure, school: school, academic_year: academic_year - meets_all_thresholds = meets_teacher_threshold && meets_student_threshold - - if meets_all_thresholds - SurveyItemResponse.for_measure(measure, school, academic_year) - elsif meets_teacher_threshold - SurveyItemResponse.teacher_responses_for_measure(measure, school, academic_year) - elsif meets_student_threshold - SurveyItemResponse.student_responses_for_measure(measure, school, academic_year) - end - end - - def self.score_for_measure(measure:, school:, academic_year:) - meets_teacher_threshold = teacher_sufficient_data? measure: measure, school: school, academic_year: academic_year - meets_student_threshold = student_sufficient_data? measure: measure, school: school, academic_year: academic_year - - survey_item_responses = responses_for_measure(measure: measure, school: school, academic_year: academic_year) - - score_for_measure = survey_item_responses.average(:likert_score) unless survey_item_responses.nil? - - Score.new(score_for_measure, meets_teacher_threshold, meets_student_threshold) - end - - def self.sufficient_data?(measure:, school:, academic_year:) - meets_teacher_threshold = teacher_sufficient_data? measure: measure, school: school, academic_year: academic_year - meets_student_threshold = student_sufficient_data? measure: measure, school: school, academic_year: academic_year - meets_teacher_threshold || meets_student_threshold - end - - scope :for_measure, lambda { |measure, school, academic_year| - joins(:survey_item) - .where('survey_items.measure': measure) - .where(school: school, academic_year: academic_year) - } - scope :for_measures, lambda { |measures, school, academic_year| - joins(:survey_item) - .where('survey_items.measure': measures) - .where(school: school, academic_year: academic_year) - } - - scope :teacher_responses_for_measure, lambda { |measure, school, academic_year| - for_measure(measure, school, academic_year) - .where("survey_items.survey_item_id LIKE 't-%'") - } - scope :teacher_responses_for_measures, lambda { |measures, school, academic_year| - for_measures(measures, school, academic_year) - .where("survey_items.survey_item_id LIKE 't-%'") - } - scope :student_responses_for_measure, lambda { |measure, school, academic_year| - for_measure(measure, school, academic_year) - .where("survey_items.survey_item_id LIKE 's-%'") - } - - scope :student_responses_for_measures, lambda { |measures, school, academic_year| - for_measures(measures, school, academic_year) - .where("survey_items.survey_item_id LIKE 's-%'") - } - def self.student_sufficient_data?(measure:, school:, academic_year:) - if measure.includes_student_survey_items? - student_survey_item_responses = SurveyItemResponse.student_responses_for_measure(measure, school, academic_year) - average_number_of_survey_item_responses = student_survey_item_responses.count / measure.student_survey_items.count - - meets_student_threshold = average_number_of_survey_item_responses >= STUDENT_RESPONSE_THRESHOLD - end - !!meets_student_threshold - end - - def self.teacher_sufficient_data?(measure:, school:, academic_year:) - if measure.includes_teacher_survey_items? - teacher_survey_item_responses = SurveyItemResponse.teacher_responses_for_measure(measure, school, academic_year) - average_number_of_survey_item_responses = teacher_survey_item_responses.count / measure.teacher_survey_items.count - - meets_teacher_threshold = average_number_of_survey_item_responses >= TEACHER_RESPONSE_THRESHOLD - end - !!meets_teacher_threshold - end - private_class_method :measures_with_sufficient_data end diff --git a/app/models/teacher_response_rate.rb b/app/models/teacher_response_rate.rb index 021f5141..e38d0ef6 100644 --- a/app/models/teacher_response_rate.rb +++ b/app/models/teacher_response_rate.rb @@ -1,4 +1,6 @@ -class TeacherResponseRate < ResponseRate +class TeacherResponseRate + include ResponseRate + def rate cap_at_100(super) end @@ -10,12 +12,15 @@ class TeacherResponseRate < ResponseRate end def survey_item_count - @teacher_survey_item_count ||= SurveyItem.teacher_survey_items_for_measures(@subcategory.measures).count + @teacher_survey_item_count ||= @subcategory.measures.map { |measure| measure.teacher_survey_items.count }.sum end def response_count - @teacher_response_count ||= SurveyItemResponse.teacher_responses_for_measures(@subcategory.measures, @school, - @academic_year).count + @teacher_response_count ||= @subcategory.measures.map do |measure| + measure.teacher_survey_items.map do |survey_item| + survey_item.survey_item_responses.where(school: @school, academic_year: @academic_year).count + end.sum + end.sum end def total_possible_responses diff --git a/app/presenters/gauge_presenter.rb b/app/presenters/gauge_presenter.rb index 4f826bb5..e0bda7bc 100644 --- a/app/presenters/gauge_presenter.rb +++ b/app/presenters/gauge_presenter.rb @@ -1,6 +1,6 @@ class GaugePresenter - def initialize(scale:, score:) - @scale = scale + def initialize(zones:, score:) + @zones = zones @score = score end @@ -17,7 +17,7 @@ class GaugePresenter end def key_benchmark_percentage - percentage_for @scale.approval_zone.low_benchmark + percentage_for @zones.approval_zone.low_benchmark end def boundary_percentage_for(zone) @@ -38,31 +38,31 @@ class GaugePresenter private def watch_low_boundary - percentage_for @scale.watch_zone.low_benchmark + percentage_for @zones.watch_zone.low_benchmark end def growth_low_boundary - percentage_for @scale.growth_zone.low_benchmark + percentage_for @zones.growth_zone.low_benchmark end def approval_low_boundary - percentage_for @scale.approval_zone.low_benchmark + percentage_for @zones.approval_zone.low_benchmark end def ideal_low_boundary - percentage_for @scale.ideal_zone.low_benchmark + percentage_for @zones.ideal_zone.low_benchmark end def zone - @scale.zone_for_score(@score) + @zones.zone_for_score(@score) end def percentage_for(number) return nil if number.nil? - scale_minimum = @scale.warning_zone.low_benchmark - scale_maximum = @scale.ideal_zone.high_benchmark + zones_minimum = @zones.warning_zone.low_benchmark + zones_maximum = @zones.ideal_zone.high_benchmark - (number - scale_minimum) / (scale_maximum - scale_minimum) + (number - zones_minimum) / (zones_maximum - zones_minimum) end end diff --git a/app/presenters/measure_presenter.rb b/app/presenters/measure_presenter.rb index 1c7bd2a2..1553abe1 100644 --- a/app/presenters/measure_presenter.rb +++ b/app/presenters/measure_presenter.rb @@ -18,7 +18,7 @@ class MeasurePresenter end def gauge_presenter - GaugePresenter.new(scale: scale, score: score_for_measure.average) + GaugePresenter.new(zones:, score: score_for_measure.average) end def data_item_accordion_id @@ -45,11 +45,11 @@ class MeasurePresenter private 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 - def scale - Scale.new( + def zones + Zones.new( watch_low_benchmark: @measure.watch_low_benchmark, growth_low_benchmark: @measure.growth_low_benchmark, approval_low_benchmark: @measure.approval_low_benchmark, diff --git a/app/presenters/subcategory_card_presenter.rb b/app/presenters/subcategory_card_presenter.rb index bc7458ba..7ef83d65 100644 --- a/app/presenters/subcategory_card_presenter.rb +++ b/app/presenters/subcategory_card_presenter.rb @@ -1,9 +1,9 @@ class SubcategoryCardPresenter attr_reader :name - def initialize(name:, scale:, score:) + def initialize(name:, zones:, score:) @name = name - @scale = scale + @zones = zones @score = score end @@ -22,6 +22,6 @@ class SubcategoryCardPresenter private def zone - @scale.zone_for_score(@score) + @zones.zone_for_score(@score) end end diff --git a/app/presenters/subcategory_presenter.rb b/app/presenters/subcategory_presenter.rb index 02d61bbb..0eb3268d 100644 --- a/app/presenters/subcategory_presenter.rb +++ b/app/presenters/subcategory_presenter.rb @@ -22,16 +22,15 @@ class SubcategoryPresenter end def gauge_presenter - GaugePresenter.new(scale:, score: average_score) + GaugePresenter.new(zones:, score: average_score) end def subcategory_card_presenter - SubcategoryCardPresenter.new(name: @subcategory.name, scale:, score: average_score) + SubcategoryCardPresenter.new(name: @subcategory.name, zones:, score: average_score) end def average_score - @average_score ||= SurveyItemResponse.score_for_subcategory(subcategory: @subcategory, school: @school, - academic_year: @academic_year) + @average_score ||= @subcategory.score(school: @school, academic_year: @academic_year) end def student_response_rate @@ -48,7 +47,7 @@ class SubcategoryPresenter end 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) end end @@ -56,19 +55,17 @@ class SubcategoryPresenter private def admin_data_item_count - if @school.is_hs - AdminDataItem.for_measures(@subcategory.measures).count - else - AdminDataItem.non_hs_items_for_measures(@subcategory.measures).count - end + return AdminDataItem.for_measures(@subcategory.measures).count if @school.is_hs + + AdminDataItem.non_hs_items_for_measures(@subcategory.measures).count end def format_a_non_applicable_rate(rate) rate == [0, 0] ? %w[N A] : rate end - def scale - Scale.new( + def zones + Zones.new( watch_low_benchmark: measures.map(&:watch_low_benchmark).average, growth_low_benchmark: measures.map(&:growth_low_benchmark).average, approval_low_benchmark: measures.map(&:approval_low_benchmark).average, diff --git a/app/presenters/variance_chart_row_presenter.rb b/app/presenters/variance_chart_row_presenter.rb index 4eaea895..75e6e294 100644 --- a/app/presenters/variance_chart_row_presenter.rb +++ b/app/presenters/variance_chart_row_presenter.rb @@ -96,12 +96,12 @@ class VarianceChartRowPresenter end def zone - scale = Scale.new( + zones = Zones.new( watch_low_benchmark: @measure.watch_low_benchmark, growth_low_benchmark: @measure.growth_low_benchmark, approval_low_benchmark: @measure.approval_low_benchmark, ideal_low_benchmark: @measure.ideal_low_benchmark ) - scale.zone_for_score(@score) + zones.zone_for_score(@score) end end diff --git a/app/presenters/scale.rb b/app/presenters/zones.rb similarity index 92% rename from app/presenters/scale.rb rename to app/presenters/zones.rb index f5aa674d..0f4e115c 100644 --- a/app/presenters/scale.rb +++ b/app/presenters/zones.rb @@ -1,4 +1,4 @@ -class Scale +class Zones def initialize(watch_low_benchmark:, growth_low_benchmark:, approval_low_benchmark:, ideal_low_benchmark:) @watch_low_benchmark = watch_low_benchmark @growth_low_benchmark = growth_low_benchmark @@ -34,9 +34,10 @@ class Scale end def zone_for_score(score) + return insufficient_data if score.nil? + return insufficient_data if score.is_a?(Float) && score.nan? + case score - when nil - insufficient_data when ideal_zone.low_benchmark..ideal_zone.high_benchmark ideal_zone when approval_zone.low_benchmark..approval_zone.high_benchmark diff --git a/config/environments/development.rb b/config/environments/development.rb index e53f1908..dff2e70f 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,7 +1,7 @@ Rails.application.configure do config.after_initialize do Bullet.enable = true - Bullet.alert = true + Bullet.alert = false Bullet.bullet_logger = true Bullet.console = true # Bullet.growl = true diff --git a/db/migrate/20220217123648_create_scales.rb b/db/migrate/20220217123648_create_scales.rb new file mode 100644 index 00000000..2b16608e --- /dev/null +++ b/db/migrate/20220217123648_create_scales.rb @@ -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 diff --git a/db/migrate/20220217164706_add_scale_to_survey_item.rb b/db/migrate/20220217164706_add_scale_to_survey_item.rb new file mode 100644 index 00000000..b8f9e637 --- /dev/null +++ b/db/migrate/20220217164706_add_scale_to_survey_item.rb @@ -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 diff --git a/db/migrate/20220217170442_add_scale_to_admin_data_item.rb b/db/migrate/20220217170442_add_scale_to_admin_data_item.rb new file mode 100644 index 00000000..a40f94b3 --- /dev/null +++ b/db/migrate/20220217170442_add_scale_to_admin_data_item.rb @@ -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 diff --git a/db/schema.rb b/db/schema.rb index 7032a35b..e5425e8b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,10 +10,8 @@ # # 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 - enable_extension "pg_stat_statements" enable_extension "plpgsql" create_table "academic_years", id: :serial, force: :cascade do |t| @@ -22,16 +20,18 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do end 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 "description" - t.datetime "created_at", precision: 6, null: false - t.datetime "updated_at", precision: 6, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.float "watch_low_benchmark" t.float "growth_low_benchmark" t.float "approval_low_benchmark" t.float "ideal_low_benchmark" 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 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.string "slug" t.integer "sort_index" - t.datetime "created_at", precision: 6, null: false - t.datetime "updated_at", precision: 6, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.string "category_id", null: false t.string "short_description" 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 "slug" t.integer "qualtrics_code" - t.datetime "created_at", precision: 6, null: false - t.datetime "updated_at", precision: 6, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end create_table "legacy_attempts", id: :serial, force: :cascade do |t| t.integer "recipient_id" t.integer "schedule_id" t.integer "recipient_schedule_id" - t.datetime "sent_at" - t.datetime "responded_at" + t.datetime "sent_at", precision: nil + t.datetime "responded_at", precision: nil t.integer "question_id" t.integer "translation_id" t.integer "answer_index" t.integer "open_response_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.text "twilio_details" t.string "twilio_sid" t.integer "student_id" @@ -78,8 +78,8 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do t.text "description" t.string "external_id" t.integer "parent_category_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.string "slug" t.float "benchmark" 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| t.string "name" t.integer "state_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.string "slug" t.integer "qualtrics_code" 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.text "description" t.text "question_ids" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false end 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 "option5" t.integer "category_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.integer "target_group", default: 0 t.boolean "for_recipient_students", default: false t.boolean "reverse", default: false @@ -126,8 +126,8 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do t.string "name" t.text "description" t.text "recipient_ids" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.index ["school_id"], name: "index_legacy_recipient_lists_on_school_id" end @@ -136,10 +136,10 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do t.integer "schedule_id" t.text "upcoming_question_ids" t.text "attempted_question_ids" - t.datetime "last_attempt_at" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.datetime "next_attempt_at" + t.datetime "last_attempt_at", precision: nil + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false + t.datetime "next_attempt_at", precision: nil t.string "queued_question_ids" end @@ -154,8 +154,8 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do t.string "income" t.boolean "opted_out", default: false t.integer "school_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.string "email" t.string "slug" 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.integer "recipient_list_id" t.integer "question_list_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.integer "time", default: 960 t.index ["school_id"], name: "index_legacy_schedules_on_school_id" end @@ -188,8 +188,8 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do t.integer "attempt_count", default: 0 t.integer "response_count", default: 0 t.integer "answer_index_total", default: 0 - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.float "nonlikert" t.float "zscore" t.string "year" @@ -207,16 +207,16 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do t.integer "response_count" t.float "response_rate" t.string "year" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.integer "response_total" end create_table "legacy_schools", id: :serial, force: :cascade do |t| t.string "name" t.integer "district_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false t.text "description" t.string "slug" t.integer "student_count" @@ -233,31 +233,31 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do t.string "age" t.string "ethnicity" t.integer "recipient_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false end create_table "legacy_user_schools", id: :serial, force: :cascade do |t| t.integer "user_id" t.integer "school_id" t.integer "district_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false end create_table "legacy_users", id: :serial, force: :cascade do |t| t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false t.string "reset_password_token" - t.datetime "reset_password_sent_at" - t.datetime "remember_created_at" + t.datetime "reset_password_sent_at", precision: nil + t.datetime "remember_created_at", precision: nil t.integer "sign_in_count", default: 0, null: false - t.datetime "current_sign_in_at" - t.datetime "last_sign_in_at" + t.datetime "current_sign_in_at", precision: nil + t.datetime "last_sign_in_at", precision: nil t.inet "current_sign_in_ip" t.inet "last_sign_in_ip" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", precision: nil, null: false + t.datetime "updated_at", precision: nil, null: false 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 end @@ -267,8 +267,8 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do t.string "name" t.integer "subcategory_id", null: false t.text "description" - t.datetime "created_at", precision: 6, null: false - t.datetime "updated_at", precision: 6, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.index ["measure_id"], name: "index_measures_on_measure_id" t.index ["subcategory_id"], name: "index_measures_on_subcategory_id" end @@ -278,20 +278,29 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do t.bigint "academic_year_id", null: false t.float "total_students" t.float "total_teachers" - t.datetime "created_at", precision: 6, null: false - t.datetime "updated_at", precision: 6, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.index ["academic_year_id"], name: "index_respondents_on_academic_year_id" t.index ["school_id"], name: "index_respondents_on_school_id" 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| t.string "name" t.integer "district_id" t.text "description" t.string "slug" t.integer "qualtrics_code" - t.datetime "created_at", precision: 6, null: false - t.datetime "updated_at", precision: 6, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.integer "dese_id", null: false t.boolean "is_hs", default: false 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.integer "category_id" t.text "description" - t.datetime "created_at", precision: 6, null: false - t.datetime "updated_at", precision: 6, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.string "subcategory_id", null: false end @@ -312,8 +321,8 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do t.integer "survey_item_id", null: false t.string "response_id", null: false t.integer "academic_year_id", null: false - t.datetime "created_at", precision: 6, null: false - t.datetime "updated_at", precision: 6, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false 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 ["school_id"], name: "index_survey_item_responses_on_school_id" @@ -321,20 +330,20 @@ ActiveRecord::Schema.define(version: 2022_02_11_122234) do end 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 "prompt" - t.datetime "created_at", precision: 6, null: false - t.datetime "updated_at", precision: 6, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.float "watch_low_benchmark" t.float "growth_low_benchmark" t.float "approval_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" 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_schedules", "legacy_schools", column: "school_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 "respondents", "academic_years" add_foreign_key "respondents", "schools" + add_foreign_key "scales", "measures" add_foreign_key "subcategories", "categories" add_foreign_key "survey_item_responses", "academic_years" add_foreign_key "survey_item_responses", "schools" add_foreign_key "survey_item_responses", "survey_items" - add_foreign_key "survey_items", "measures" + add_foreign_key "survey_items", "scales" end diff --git a/doc/architectural_decision_records/2.md b/doc/architectural_decision_records/2.md new file mode 100644 index 00000000..33991b8b --- /dev/null +++ b/doc/architectural_decision_records/2.md @@ -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. diff --git a/spec/factories.rb b/spec/factories.rb index 8e3c0042..26f8795b 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -13,7 +13,7 @@ FactoryBot.define do factory :academic_year do range { '2050-51' } - initialize_with { AcademicYear.find_or_initialize_by(range: range) } + initialize_with { AcademicYear.find_or_initialize_by(range:) } end factory :category, class: 'Category' do @@ -35,9 +35,10 @@ FactoryBot.define do measures_count { 2 } end after(:create) do |subcategory, evaluator| - create_list(:measure, evaluator.measures_count, subcategory: subcategory).each do |measure| - survey_item = create(:teacher_survey_item, measure: measure) - create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD, survey_item: survey_item) + create_list(:measure, evaluator.measures_count, subcategory:).each do |measure| + scale = create(:scale, measure: measure) + survey_item = create(:teacher_survey_item, scale:) + create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD, survey_item:) end end end @@ -54,9 +55,20 @@ FactoryBot.define do # 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 + scale prompt { 'What do YOU think?' } - measure factory :teacher_survey_item do survey_item_id { "t-#{rand}" } watch_low_benchmark { 2.0 } @@ -84,7 +96,7 @@ FactoryBot.define do factory :admin_data_item do admin_data_item_id { rand.to_s } description { rand.to_s } - measure + scale end factory :respondent do diff --git a/spec/lib/seeder_spec.rb b/spec/lib/seeder_spec.rb index 1c0c0316..694b3062 100644 --- a/spec/lib/seeder_spec.rb +++ b/spec/lib/seeder_spec.rb @@ -123,9 +123,11 @@ describe Seeder do before do school_culture_category = create(:category, category_id: '2', sort_index: -1) safety_subcategory = create(:subcategory, subcategory_id: '2A', category: school_culture_category) - student_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) - create(:admin_data_item, admin_data_item_id: 'a-phys-i1', measure: student_physical_safety_measure) + physical_safety_measure = create(:measure, measure_id: '2A-i', subcategory: safety_subcategory) + student_physical_safety_scale = create(:scale, scale_id: 's-phys', measure: 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 it 'creates new objects as necessary' do @@ -133,10 +135,12 @@ describe Seeder do seeder.seed_sqm_framework sample_sqm_framework_csv end.to change { Category.count }.by(4) .and change { Subcategory.count }.by(15) - .and change { Measure.count }.by(31) - .and change { - SurveyItem.count - }.by(136) + .and change { Measure.count }.by(31).and change { + Scale.count + }.by(51) + .and change { + SurveyItem.count + }.by(136) .and change { AdminDataItem.count }.by(32) @@ -174,6 +178,14 @@ describe Seeder do expect(measure.description).to eq 'This is a measure description.' 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 survey_item = SurveyItem.find_by_survey_item_id 't-prep-q1' expect(survey_item.approval_low_benchmark).to eq 3.5 diff --git a/spec/models/measure_spec.rb b/spec/models/measure_spec.rb index 2eba286c..79174e83 100644 --- a/spec/models/measure_spec.rb +++ b/spec/models/measure_spec.rb @@ -2,6 +2,12 @@ require 'rails_helper' RSpec.describe Measure, type: :model do 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_growth_low_benchmark) { 3.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_ideal_low_benchmark) { 4.2 } - context 'when a measure includes only one admin data item' do - before do - create(:admin_data_item, measure: measure, - 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) - end - it 'returns a watch low benchmark equal to the admin data item watch low benchmark' do - expect(measure.watch_low_benchmark).to be 2.0 - end + describe 'benchmarks' do + context 'when a measure includes only one admin data item' do + before do + create(:admin_data_item, 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) + end + it 'returns a watch low benchmark equal to the admin data item watch low benchmark' do + expect(measure.watch_low_benchmark).to be 2.0 + end - it 'returns the source as an admin_data_item' do - expect(measure.sources).to eq [:admin_data] + it 'returns the source as an admin_data_item' do + expect(measure.sources).to eq [:admin_data] + end end - end - context 'when a measure includes only student survey items' do - before do - 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 a watch low benchmark equal to the student survey item watch low benchmark ' do - expect(measure.watch_low_benchmark).to be 1.5 + context 'when a measure includes only student survey items' do + before do + create(:student_survey_item, 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) + end + 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 + 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 - 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 - before do - 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 - 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 + context 'when a measure includes only teacher survey items' do + before do + 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 + 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 + 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 - 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 - before do - create_list(:admin_data_item, 3, measure: measure, - 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(: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 + context 'when a measure includes admin data and student 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) - 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 - # (2*3 + 1.5)/4 - expect(measure.watch_low_benchmark).to be 1.875 - end - it 'returns the source as admin and student survey items' do - expect(measure.sources).to eq [:admin_data, :student_surveys] - end - end + create(:student_survey_item, 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) + end - context 'when a measure includes admin data and teacher survey items' do - before do - create_list(:admin_data_item, 3, measure: measure, - 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(: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) + 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 + # (2*3 + 1.5)/4 + expect(measure.watch_low_benchmark).to be 1.875 + end + it 'returns the source as admin and student survey items' do + expect(measure.sources).to eq %i[admin_data student_surveys] + 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 - # (2*3 + 1.2)/4 - expect(measure.watch_low_benchmark).to be 1.8 - end - it 'returns the source as admin and teacher survey items' do - expect(measure.sources).to eq [:admin_data, :teacher_surveys] - end - end + context 'when a measure includes admin data 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(: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 - before do - create_list(:student_survey_item, 3, 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) - - 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) + 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 + # (2*3 + 1.2)/4 + expect(measure.watch_low_benchmark).to be 1.8 + end + it 'returns the source as admin and teacher survey items' do + expect(measure.sources).to eq %i[admin_data teacher_surveys] + 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 - # (1.2+ 1.5)/2 - expect(measure.watch_low_benchmark).to be 1.35 + context 'when a measure includes student and teacher survey items' do + before do + 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 - 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 - context 'when a measure includes admin data and student and teacher survey items' do - before do - create_list(:admin_data_item, 3, measure: measure, - 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, 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) - - 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) + describe '.score' do + context 'when the measure includes only teacher data' 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) } + + 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:, school:, likert_score: 3) + create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD, + survey_item: teacher_survey_item_2, academic_year:, school:, likert_score: 4) + create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD, + 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(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 - 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 + context 'when the measure includes only student data' do + 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) } + + 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:, 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 - it 'returns the source as admin student and teacher survey items' do - expect(measure.sources).to eq [:admin_data, :student_surveys, :teacher_surveys] + context 'when the measure includes both teacher and student data' do + 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 diff --git a/spec/models/response_rate_spec.rb b/spec/models/response_rate_spec.rb index ab81082c..ae5fefd1 100644 --- a/spec/models/response_rate_spec.rb +++ b/spec/models/response_rate_spec.rb @@ -11,13 +11,16 @@ describe ResponseRate, type: :model do describe StudentResponseRate do let(:subcategory) { create(: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_scale_2) { create(:scale, measure: sufficient_measure_2) } let(:insufficient_measure) { create(:measure, subcategory: subcategory) } - let(:sufficient_teacher_survey_item) { create(:teacher_survey_item, measure: sufficient_measure_1) } - let(:sufficient_student_survey_item_1) { create(:student_survey_item, measure: sufficient_measure_1) } - let(:insufficient_teacher_survey_item) { create(:teacher_survey_item, measure: insufficient_measure) } - let(:sufficient_student_survey_item_2) { create(:student_survey_item, measure: sufficient_measure_2) } - let(:insufficient_student_survey_item) { create(:student_survey_item, measure: insufficient_measure) } + let(:insufficient_scale) { create(:scale, measure: insufficient_measure) } + let(:sufficient_teacher_survey_item) { create(:teacher_survey_item, scale: sufficient_scale_1) } + let(:sufficient_student_survey_item_1) { create(:student_survey_item, scale: sufficient_scale_1) } + let(:insufficient_teacher_survey_item) { create(:teacher_survey_item, scale: insufficient_scale) } + 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 survey_respondents @@ -44,14 +47,17 @@ describe ResponseRate, type: :model do describe TeacherResponseRate do let(:subcategory) { create(: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_scale_2) { create(:scale, measure: sufficient_measure_2) } let(:insufficient_measure) { create(:measure, subcategory: subcategory) } - let(:sufficient_teacher_survey_item_1) { create(:teacher_survey_item, measure: sufficient_measure_1) } - let(:sufficient_teacher_survey_item_2) { create(:teacher_survey_item, measure: sufficient_measure_1) } - let(:sufficient_teacher_survey_item_3) { create(:teacher_survey_item, measure: sufficient_measure_1) } - let(:sufficient_student_survey_item_1) { create(:student_survey_item, measure: sufficient_measure_1) } - let(:insufficient_teacher_survey_item) { create(:teacher_survey_item, measure: insufficient_measure) } - let(:insufficient_student_survey_item) { create(:student_survey_item, measure: insufficient_measure) } + let(:insufficient_scale) { create(:scale, measure: insufficient_measure) } + let(:sufficient_teacher_survey_item_1) { create(:teacher_survey_item, scale: sufficient_scale_1) } + let(:sufficient_teacher_survey_item_2) { create(:teacher_survey_item, scale: sufficient_scale_1) } + let(:sufficient_teacher_survey_item_3) { create(:teacher_survey_item, scale: sufficient_scale_1) } + let(:sufficient_student_survey_item_1) { create(:student_survey_item, scale: sufficient_scale_1) } + 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 survey_respondents diff --git a/spec/models/scale_spec.rb b/spec/models/scale_spec.rb new file mode 100644 index 00000000..2c0ce12e --- /dev/null +++ b/spec/models/scale_spec.rb @@ -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 diff --git a/spec/models/subcategory_spec.rb b/spec/models/subcategory_spec.rb new file mode 100644 index 00000000..549d7bff --- /dev/null +++ b/spec/models/subcategory_spec.rb @@ -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 diff --git a/spec/models/survey_item_response_spec.rb b/spec/models/survey_item_response_spec.rb deleted file mode 100644 index 1e68125a..00000000 --- a/spec/models/survey_item_response_spec.rb +++ /dev/null @@ -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 diff --git a/spec/models/survey_item_spec.rb b/spec/models/survey_item_spec.rb new file mode 100644 index 00000000..22e26f67 --- /dev/null +++ b/spec/models/survey_item_spec.rb @@ -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 diff --git a/spec/presenters/gauge_presenter_spec.rb b/spec/presenters/gauge_presenter_spec.rb index 24fe9061..504ccfe1 100644 --- a/spec/presenters/gauge_presenter_spec.rb +++ b/spec/presenters/gauge_presenter_spec.rb @@ -1,8 +1,8 @@ require 'rails_helper' describe GaugePresenter do - let(:scale) do - Scale.new( + let(:zones) do + Zones.new( watch_low_benchmark: 1.5, growth_low_benchmark: 2.5, approval_low_benchmark: 3.5, @@ -11,7 +11,7 @@ describe GaugePresenter do end 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 expect(gauge_presenter.key_benchmark_percentage).to eq 0.625 @@ -98,8 +98,8 @@ describe GaugePresenter do end context 'when there are no benchmarks or score for the gauge' do - let(:scale) do - Scale.new( + let(:zones) do + Zones.new( watch_low_benchmark: nil, growth_low_benchmark: nil, approval_low_benchmark: nil, diff --git a/spec/presenters/measure_presenter_spec.rb b/spec/presenters/measure_presenter_spec.rb index d60ef666..b130272f 100644 --- a/spec/presenters/measure_presenter_spec.rb +++ b/spec/presenters/measure_presenter_spec.rb @@ -4,7 +4,10 @@ describe MeasurePresenter do let(:academic_year) { create(:academic_year, range: '1989-90') } let(:school) { create(:school, name: 'Best School') } 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 expect(measure_presenter.id).to eq 'measure-id' @@ -16,13 +19,13 @@ describe MeasurePresenter do context 'when the measure contains only teacher data' do before :each do - survey_item1 = create(:teacher_survey_item, measure: measure, prompt: 'A teacher survey item prompt') - survey_item2 = create(:teacher_survey_item, measure: measure, prompt: 'Another 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, scale: teacher_scale, prompt: 'Another teacher survey item prompt') 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, - academic_year: academic_year, school: school, likert_score: 5) + academic_year:, school:, likert_score: 5) end 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 before :each do - create(:teacher_survey_item, measure: measure, prompt: 'A teacher survey item prompt') - create(:teacher_survey_item, measure: measure, prompt: 'Another teacher survey item prompt') - create(:admin_data_item, measure: measure, description: 'An admin data item description') - create(:admin_data_item, measure: measure, description: 'Another admin data item description') + create(:teacher_survey_item, scale: teacher_scale, prompt: 'A teacher survey item prompt') + create(:teacher_survey_item, scale: teacher_scale, prompt: 'Another teacher survey item prompt') + create(:admin_data_item, scale: admin_scale, description: 'An admin data item description') + create(:admin_data_item, scale: admin_scale, description: 'Another admin data item description') end 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 before :each do - teacher_survey_item = create(:teacher_survey_item, measure: measure) - student_survey_item = create(:student_survey_item, measure: measure) + teacher_survey_item = create(:teacher_survey_item, scale: teacher_scale) + student_survey_item = create(:student_survey_item, scale: student_scale) 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, - survey_item: student_survey_item, academic_year: academic_year, school: school) + survey_item: student_survey_item, academic_year:, school:) end 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 before :each do - create(:admin_data_item, measure: measure) - create(:teacher_survey_item, measure: measure) - create(:student_survey_item, measure: measure) + create(:admin_data_item, scale: admin_scale) + create(:teacher_survey_item, scale: teacher_scale) + create(:student_survey_item, scale: student_scale) end it 'tracks the reason for their insufficiency' do diff --git a/spec/presenters/scale_spec.rb b/spec/presenters/scale_spec.rb index 09052e20..802af3f8 100644 --- a/spec/presenters/scale_spec.rb +++ b/spec/presenters/scale_spec.rb @@ -1,30 +1,30 @@ require 'rails_helper' -describe Scale do +describe Zones do describe '#zone_for_score' do - let(:scale) do - Scale.new watch_low_benchmark: 1.5, growth_low_benchmark: 2.5, approval_low_benchmark: 3.5, + let(:zones) do + Zones.new watch_low_benchmark: 1.5, growth_low_benchmark: 2.5, approval_low_benchmark: 3.5, ideal_low_benchmark: 4.5 end context 'when the score is 1.0' 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 context 'when the score is 5.0' 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 context 'when the score is right on the benchmark' do it 'returns the higher zone' do - expect(scale.zone_for_score(1.5)).to eq scale.watch_zone - expect(scale.zone_for_score(2.5)).to eq scale.growth_zone - expect(scale.zone_for_score(3.5)).to eq scale.approval_zone - expect(scale.zone_for_score(4.5)).to eq scale.ideal_zone + expect(zones.zone_for_score(1.5)).to eq zones.watch_zone + expect(zones.zone_for_score(2.5)).to eq zones.growth_zone + expect(zones.zone_for_score(3.5)).to eq zones.approval_zone + expect(zones.zone_for_score(4.5)).to eq zones.ideal_zone end end end diff --git a/spec/presenters/subcategory_card_presenter_spec.rb b/spec/presenters/subcategory_card_presenter_spec.rb index 78d272b1..cdd5471c 100644 --- a/spec/presenters/subcategory_card_presenter_spec.rb +++ b/spec/presenters/subcategory_card_presenter_spec.rb @@ -1,8 +1,8 @@ require 'rails_helper' describe SubcategoryCardPresenter do - let(:scale) do - Scale.new( + let(:zones) do + Zones.new( watch_low_benchmark: 1.5, growth_low_benchmark: 2.5, approval_low_benchmark: 3.5, @@ -10,7 +10,7 @@ describe SubcategoryCardPresenter do ) 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 let(:score) { 1 } diff --git a/spec/presenters/subcategory_presenter_spec.rb b/spec/presenters/subcategory_presenter_spec.rb index 6ae83d0a..1dbb1602 100644 --- a/spec/presenters/subcategory_presenter_spec.rb +++ b/spec/presenters/subcategory_presenter_spec.rb @@ -13,9 +13,11 @@ describe SubcategoryPresenter do let(:subcategory_presenter) do survey_respondents 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) - 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) create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD, survey_item: survey_item1, academic_year:, school:, likert_score: 1) @@ -27,9 +29,12 @@ describe SubcategoryPresenter do academic_year:, school:, likert_score: 3) 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) - 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) create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD, survey_item: survey_item3, 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 before do measure_of_only_admin_data = create(:measure, subcategory:) - create(:admin_data_item, measure: measure_of_only_admin_data, hs_only_item: false) - 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, 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 it 'returns the admin collection rate' do 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 before do measure_of_only_admin_data = create(:measure, subcategory:) - create(:admin_data_item, measure: measure_of_only_admin_data, hs_only_item: true) - 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, 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 it 'returns the admin collection rate' do expect(subcategory_presenter.admin_collection_rate).to eq %w[N A] @@ -126,8 +133,9 @@ describe SubcategoryPresenter do school.is_hs = true school.save measure_of_only_admin_data = create(:measure, subcategory:) - create(:admin_data_item, measure: measure_of_only_admin_data, hs_only_item: false) - 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, 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 it 'returns the admin collection rate' do expect(subcategory_presenter.admin_collection_rate).to eq [0, 2] @@ -139,8 +147,9 @@ describe SubcategoryPresenter do school.is_hs = true school.save measure_of_only_admin_data = create(:measure, subcategory:) - create(:admin_data_item, measure: measure_of_only_admin_data, hs_only_item: true) - 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, 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 it 'returns the admin collection rate' do expect(subcategory_presenter.admin_collection_rate).to eq [0, 2] diff --git a/spec/presenters/variance_chart_row_presenter_spec.rb b/spec/presenters/variance_chart_row_presenter_spec.rb index 46ced909..5340407c 100644 --- a/spec/presenters/variance_chart_row_presenter_spec.rb +++ b/spec/presenters/variance_chart_row_presenter_spec.rb @@ -11,12 +11,13 @@ describe VarianceChartRowPresenter do :measure, name: 'Some Title' ) + scale = create(:scale, measure:) - create(:student_survey_item, measure: measure, - watch_low_benchmark: watch_low_benchmark, - growth_low_benchmark: growth_low_benchmark, - approval_low_benchmark: approval_low_benchmark, - ideal_low_benchmark: ideal_low_benchmark) + create(:student_survey_item, scale:, + watch_low_benchmark:, + growth_low_benchmark:, + approval_low_benchmark:, + ideal_low_benchmark:) measure end @@ -29,7 +30,7 @@ describe VarianceChartRowPresenter do end let(:presenter) do - VarianceChartRowPresenter.new measure: measure, score: score + VarianceChartRowPresenter.new measure:, score: end shared_examples_for 'measure_name' do @@ -155,8 +156,9 @@ describe VarianceChartRowPresenter do :measure, name: 'Some Title' ) + scale_with_admin_data = create(:scale, measure: measure_with_admin_data) create :admin_data_item, - measure: measure_with_admin_data, + scale: scale_with_admin_data, watch_low_benchmark: watch_low_benchmark, growth_low_benchmark: growth_low_benchmark, approval_low_benchmark: approval_low_benchmark, @@ -172,7 +174,8 @@ describe VarianceChartRowPresenter do context 'when a measure contains teacher survey items' do before :each do - create :teacher_survey_item, measure: measure + scale = create(:scale, measure:) + create :teacher_survey_item, scale: end 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 before :each do - create :student_survey_item, measure: measure + scale = create(:scale, measure:) + create :student_survey_item, scale: end 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 before :each do - create :admin_data_item, measure: measure + scale = create(:scale, measure:) + create :admin_data_item, scale: end 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 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, - measure: measure, - watch_low_benchmark: watch_low_benchmark, - growth_low_benchmark: growth_low_benchmark, - approval_low_benchmark: approval_low_benchmark, - ideal_low_benchmark: ideal_low_benchmark) + scale: scale_with_student_survey_items, + watch_low_benchmark:, + growth_low_benchmark:, + approval_low_benchmark:, + ideal_low_benchmark:) 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) expect(ideal_presenter <=> approval_presenter).to be < 0 diff --git a/spec/system/journey_spec.rb b/spec/system/journey_spec.rb index d92aa0a6..baf98cad 100644 --- a/spec/system/journey_spec.rb +++ b/spec/system/journey_spec.rb @@ -9,19 +9,20 @@ describe 'District Admin', js: true do let(:category) { Category.find_by_name('Teachers & Leadership') } let(:subcategory) { Subcategory.find_by_name('Teachers & The Teaching Environment') } 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_2A_i) { Measure.find_by_measure_id('2A-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(:survey_item_1_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 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 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_2A_i) { SurveyItem.where(measure: measure_2A_i) } - let(:survey_items_for_measure_4C_i) { SurveyItem.where(measure: measure_4C_i) } + let(:survey_items_for_measure_1A_i) { measure_1A_i.survey_items } + let(:survey_items_for_measure_2A_i) { measure_2A_i.survey_items } + let(:survey_items_for_measure_4C_i) { measure_4C_i.survey_items } let(:ay_2020_21) { AcademicYear.find_by_range '2020-21' } let(:ay_2019_20) { AcademicYear.find_by_range '2019-20' } diff --git a/spec/system/sqm_application_spec.rb b/spec/system/sqm_application_spec.rb index 928f4fd1..1d20c885 100644 --- a/spec/system/sqm_application_spec.rb +++ b/spec/system/sqm_application_spec.rb @@ -6,6 +6,7 @@ describe 'SQM Application' do let(:academic_year) { create(:academic_year) } let(:category) { create(:category) } let(:measure) { create(:measure) } + let(:scale) {create(:scale, measure:)} before :each do driven_by :rack_test @@ -26,7 +27,7 @@ describe 'SQM Application' do context 'at least one measure meets its threshold' 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, survey_item: teacher_survey_item, academic_year: academic_year, school: school) end diff --git a/spec/views/categories/show.html.erb_spec.rb b/spec/views/categories/show.html.erb_spec.rb index afe3612a..3e508745 100644 --- a/spec/views/categories/show.html.erb_spec.rb +++ b/spec/views/categories/show.html.erb_spec.rb @@ -6,32 +6,36 @@ describe 'categories/show' do school = create(:school, name: 'Best School') 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') - 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') 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) 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', - subcategory: subcategory1) - student_survey_item = create(:student_survey_item, measure: measure2, prompt: 'Some prompt for student data', + subcategory: subcategory2) + 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) 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, - 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', - watch_low_benchmark: 1.5, growth_low_benchmark: 2.5, approval_low_benchmark: 3.5, ideal_low_benchmark: 4.5) + admin_data_scale = create(:scale, measure: measure2) + 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 :categories, [category_presenter] @@ -44,45 +48,45 @@ describe 'categories/show' do end it 'renders the category name and description' do - expect(rendered).to match /Some Category/ - expect(rendered).to match /Some description of the category/ + expect(rendered).to match(/Some Category/) + expect(rendered).to match(/Some description of the category/) end context 'for each subcategory' do it 'renders the subcategory name' do - expect(rendered).to match /A subcategory/ - expect(rendered).to match /Another subcategory/ + expect(rendered).to match(/A subcategory/) + expect(rendered).to match(/Another subcategory/) end it 'renders the subcategory description' do - expect(rendered).to match /Some description of the subcategory/ - expect(rendered).to match /Another description of the subcategory/ + expect(rendered).to match(/Some description of the subcategory/) + expect(rendered).to match(/Another description of the subcategory/) end it 'renders the zone title and fill color for the gauge graph' do - expect(rendered).to match /Growth/ - expect(rendered).to match /fill-growth/ + expect(rendered).to match(/Growth/) + expect(rendered).to match(/fill-growth/) end end context 'for each measure' do it 'renders the measure name' do - expect(rendered).to match /The second measure name/ + expect(rendered).to match(/The second measure name/) end it 'renders the measure description' do - expect(rendered).to match /The second measure description/ + expect(rendered).to match(/The second measure description/) end it 'renders a gauge graph and the zone title color' do - expect(rendered).to match /Ideal/ - expect(rendered).to match /fill-ideal/ + expect(rendered).to match(/Approval/) + expect(rendered).to match(/fill-approval/) end 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 teacher data/ - expect(rendered).to match /Some admin data item description/ + expect(rendered).to match(/Some prompt for student data/) + expect(rendered).to match(/Some prompt for teacher data/) + expect(rendered).to match(/Some admin data item description/) end end end diff --git a/spec/views/overview/index.html.erb_spec.rb b/spec/views/overview/index.html.erb_spec.rb index e0bb1936..2d103557 100644 --- a/spec/views/overview/index.html.erb_spec.rb +++ b/spec/views/overview/index.html.erb_spec.rb @@ -5,8 +5,9 @@ describe 'overview/index' do let(:support_for_teaching) do measure = create(:measure, name: 'Support For Teaching Development & Growth', measure_id: '1') + scale = create(:scale, measure:) create(:student_survey_item, - measure: measure, + scale:, watch_low_benchmark: 1.5, growth_low_benchmark: 2.5, approval_low_benchmark: 3.5, @@ -16,8 +17,9 @@ describe 'overview/index' do let(:effective_leadership) do measure = create(:measure, name: 'Effective Leadership', measure_id: '2') + scale = create(:scale, measure:) create(:teacher_survey_item, - measure: measure, + scale:, watch_low_benchmark: 1.5, growth_low_benchmark: 2.5, approval_low_benchmark: 3.5, @@ -27,8 +29,9 @@ describe 'overview/index' do let(:professional_qualifications) do measure = create(:measure, name: 'Professional Qualifications', measure_id: '3') + scale = create(:scale, measure:) create(:admin_data_item, - measure: measure, + scale:, watch_low_benchmark: 1.5, growth_low_benchmark: 2.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 let(:variance_chart_row_presenters) do measure = create(:measure, name: 'Display Me', measure_id: 'display-me') + scale = create(:scale, measure:) create(:student_survey_item, - measure: measure, + scale:, watch_low_benchmark: 1.5, growth_low_benchmark: 2.5, approval_low_benchmark: 3.5, ideal_low_benchmark: 4.5) [ - VarianceChartRowPresenter.new(measure: measure, + VarianceChartRowPresenter.new(measure:, score: Score.new(rand)) ] end diff --git a/spec/views/overview/variance_chart.html.erb_spec.rb b/spec/views/overview/variance_chart.html.erb_spec.rb index 78fdc92a..efa5c515 100644 --- a/spec/views/overview/variance_chart.html.erb_spec.rb +++ b/spec/views/overview/variance_chart.html.erb_spec.rb @@ -6,8 +6,17 @@ describe 'overview/_variance_chart.html.erb' do let(:survey_items1) { [create(:student_survey_item)] } let(:survey_items2) { [create(:student_survey_item)] } - let(:higher_scoring_measure) { create(:measure, survey_items: survey_items1) } - let(:lower_scoring_measure) { create(:measure, survey_items: survey_items2) } + let(:higher_scoring_measure) do + 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 presenters = [ @@ -15,7 +24,7 @@ describe 'overview/_variance_chart.html.erb' do VarianceChartRowPresenter.new(measure: higher_scoring_measure, score: Score.new(5)) ] - render partial: 'variance_chart', locals: { presenters: presenters } + render partial: 'variance_chart', locals: { presenters: } end 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)) ] - render partial: 'variance_chart', locals: { presenters: presenters } + render partial: 'variance_chart', locals: { presenters: } end it "displays the text 'insufficient data' for an empty dataset" do