diff --git a/app/assets/stylesheets/partials/_overview.scss b/app/assets/stylesheets/partials/_overview.scss index 8a9156ec..271eeb6b 100644 --- a/app/assets/stylesheets/partials/_overview.scss +++ b/app/assets/stylesheets/partials/_overview.scss @@ -54,6 +54,15 @@ grid-template-rows: repeat(4, max-content); } +.school-quality-frameworks-parent { + display: grid; + row-gap: map-get($spacers, 4); + column-gap: map-get($spacers, 5); + grid-auto-flow: column; + grid-template-columns: 250px 250px 250px; + grid-template-rows: repeat(4, max-content); +} + .zone-header { @extend .sub-header-4; } diff --git a/app/controllers/analyze_controller.rb b/app/controllers/analyze_controller.rb index d50837ea..ac8efaf4 100644 --- a/app/controllers/analyze_controller.rb +++ b/app/controllers/analyze_controller.rb @@ -3,7 +3,7 @@ class AnalyzeController < SqmApplicationController def index @presenter = Analyze::Presenter.new(params:, school: @school, academic_year: @academic_year) - @background ||= BackgroundPresenter.new(num_of_columns: @presenter.graph.columns.count) + @background ||= Analyze::BackgroundPresenter.new(num_of_columns: @presenter.graph.columns.count) @academic_year = @presenter.selected_academic_years&.first || AcademicYear.last end end diff --git a/app/controllers/overview_controller.rb b/app/controllers/overview_controller.rb index 28dc4c6c..1bbf4d4e 100644 --- a/app/controllers/overview_controller.rb +++ b/app/controllers/overview_controller.rb @@ -1,42 +1,19 @@ # frozen_string_literal: true class OverviewController < SqmApplicationController - before_action :check_empty_dataset, only: [:index] helper VarianceHelper def index - @variance_chart_row_presenters = measures.map(&method(:presenter_for_measure)) - @category_presenters = categories.map { |category| CategoryPresenter.new(category:) } - @student_response_rate_presenter = ResponseRatePresenter.new(focus: :student, school: @school, - academic_year: @academic_year) - @teacher_response_rate_presenter = ResponseRatePresenter.new(focus: :teacher, school: @school, - academic_year: @academic_year) - end - - private - - def presenter_for_measure(measure) - score = measure.score(school: @school, academic_year: @academic_year) - - VarianceChartRowPresenter.new(measure:, score:) - end - - def check_empty_dataset - @has_empty_dataset = subcategories.none? do |subcategory| - response_rate = subcategory.response_rate(school: @school, academic_year: @academic_year) - response_rate.meets_student_threshold || response_rate.meets_teacher_threshold - end - end - - def measures - @measures ||= subcategories.flat_map(&:measures) - end - - def subcategories - @subcategories ||= categories.flat_map(&:subcategories) - end - - def categories - @categories ||= Category.sorted.includes(%i[measures scales admin_data_items subcategories]) + @page = if params[:view] == "student" || params[:view].nil? + Overview::OverviewPresenter.new(params:, school: @school, academic_year: @academic_year) + else + Overview::ParentOverviewPresenter.new(params:, school: @school, academic_year: @academic_year) + end + + @has_empty_dataset = @page.empty_dataset? + @variance_chart_row_presenters = @page.variance_chart_row_presenters + @category_presenters = @page.category_presenters + @student_response_rate_presenter = @page.student_response_rate_presenter + @teacher_response_rate_presenter = @page.teacher_response_rate_presenter end end diff --git a/app/models/scale.rb b/app/models/scale.rb index ded3e9e0..bb2bc2d9 100644 --- a/app/models/scale.rb +++ b/app/models/scale.rb @@ -2,6 +2,7 @@ class Scale < ApplicationRecord belongs_to :measure, counter_cache: true + has_one :category, through: :measure has_many :survey_items has_many :survey_item_responses, through: :survey_items has_many :admin_data_items @@ -19,6 +20,18 @@ class Scale < ApplicationRecord @score[[school, academic_year]] end + def parent_score(school:, academic_year:) + @parent_score ||= Hash.new do |memo, (school, academic_year)| + memo[[school, academic_year]] = begin + items = [] + items << collect_survey_item_average(survey_items.parent_survey_items, school, academic_year) + + items.remove_blanks.average + end + end + @parent_score[[school, academic_year]] + end + scope :teacher_scales, lambda { where("scale_id LIKE 't-%'") } diff --git a/app/presenters/background_presenter.rb b/app/presenters/analyze/background_presenter.rb similarity index 95% rename from app/presenters/background_presenter.rb rename to app/presenters/analyze/background_presenter.rb index c150b87a..4a4b2462 100644 --- a/app/presenters/background_presenter.rb +++ b/app/presenters/analyze/background_presenter.rb @@ -1,4 +1,4 @@ -class BackgroundPresenter +class Analyze::BackgroundPresenter include AnalyzeHelper attr_reader :num_of_columns diff --git a/app/presenters/category_presenter.rb b/app/presenters/category_presenter.rb index 707db708..ef91edc9 100644 --- a/app/presenters/category_presenter.rb +++ b/app/presenters/category_presenter.rb @@ -45,6 +45,23 @@ class CategoryPresenter end end + def harvey_scorecard_presenters(school:, academic_year:) + @category.subcategories.map do |subcategory| + measures = subcategory.measures + 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, + ideal_low_benchmark: measures.map(&:ideal_low_benchmark).average + ) + + Overview::ScorecardPresenter.new(construct: subcategory, + zones:, + score: subcategory.score(school:, academic_year:), + id: subcategory.subcategory_id) + end + end + def to_model @category end @@ -52,18 +69,18 @@ class CategoryPresenter private def colors - { 'Teachers & Leadership': 'blue', - 'School Culture': 'red', - 'Resources': 'black', - 'Academic Learning': 'lime', - 'Community & Wellbeing': 'teal' } + { '1': "blue", + '2': "red", + '3': "black", + '4': "lime", + '5': "teal" } end def classes - { 'Teachers & Leadership': 'apple-alt', - 'School Culture': 'school', - 'Resources': 'users-cog', - 'Academic Learning': 'graduation-cap', - 'Community & Wellbeing': 'heart' } + { '1': "apple-alt", + '2': "school", + '3': "users-cog", + '4': "graduation-cap", + '5': "heart" } end end diff --git a/app/presenters/measure_presenter.rb b/app/presenters/measure_presenter.rb index a1dfbf20..0067769a 100644 --- a/app/presenters/measure_presenter.rb +++ b/app/presenters/measure_presenter.rb @@ -9,6 +9,7 @@ class MeasurePresenter @school = school @name = measure.name @description = measure.description + @id = measure.measure_id end def title diff --git a/app/presenters/overview/overview_presenter.rb b/app/presenters/overview/overview_presenter.rb new file mode 100644 index 00000000..31ca5cf6 --- /dev/null +++ b/app/presenters/overview/overview_presenter.rb @@ -0,0 +1,58 @@ +class Overview::OverviewPresenter + attr_reader :view, :school, :academic_year + + def initialize(params:, school:, academic_year:) + @view = params[:view] || "student" + @school = school + @academic_year = academic_year + end + + def variance_chart_row_presenters + measures.map(&method(:presenter_for_measure)) + end + + def category_presenters + categories.map { |category| CategoryPresenter.new(category:) } + end + + def measures + @measures ||= subcategories.flat_map(&:measures) + end + + def subcategories + @subcategories ||= categories.flat_map(&:subcategories) + end + + def framework_indicator_class + "school-quality-frameworks" + end + + def show_response_rates + view == "student" + end + + def categories + Category.sorted.includes(%i[measures scales admin_data_items subcategories]) + end + + def student_response_rate_presenter + ResponseRatePresenter.new(focus: :student, school: @school, academic_year: @academic_year) + end + + def teacher_response_rate_presenter + ResponseRatePresenter.new(focus: :teacher, school: @school, academic_year: @academic_year) + end + + def presenter_for_measure(measure) + score = measure.score(school: @school, academic_year: @academic_year) + + Overview::VarianceChartRowPresenter.new(measure:, score:) + end + + def empty_dataset? + subcategories.none? do |subcategory| + response_rate = subcategory.response_rate(school: @school, academic_year: @academic_year) + response_rate.meets_student_threshold || response_rate.meets_teacher_threshold + end + end +end diff --git a/app/presenters/overview/parent_overview_presenter.rb b/app/presenters/overview/parent_overview_presenter.rb new file mode 100644 index 00000000..bfd68aba --- /dev/null +++ b/app/presenters/overview/parent_overview_presenter.rb @@ -0,0 +1,15 @@ +class Overview::ParentOverviewPresenter < Overview::OverviewPresenter + def categories + Category.sorted.includes(%i[measures scales admin_data_items subcategories]).select do |category| + category.survey_items.parent_survey_items.count.positive? + end + end + + def category_presenters + categories.map { |category| ParentCategoryPresenter.new(category:) } + end + + def framework_indicator_class + "school-quality-frameworks-parent" + end +end diff --git a/app/presenters/subcategory_card_presenter.rb b/app/presenters/overview/scorecard_presenter.rb similarity index 52% rename from app/presenters/subcategory_card_presenter.rb rename to app/presenters/overview/scorecard_presenter.rb index 92b0e0dc..68755e9b 100644 --- a/app/presenters/subcategory_card_presenter.rb +++ b/app/presenters/overview/scorecard_presenter.rb @@ -1,13 +1,12 @@ # frozen_string_literal: true -class SubcategoryCardPresenter - attr_reader :name, :subcategory, :category, :subcategory_id - - def initialize(subcategory:, zones:, score:) - @name = subcategory.name - @subcategory = subcategory - @category = subcategory.category - @subcategory_id = subcategory.subcategory_id +class Overview::ScorecardPresenter + attr_reader :name, :construct, :category, :id + + def initialize(construct:, zones:, score:, id:) + @name = construct.name + @category = construct.category + @id = id @zones = zones @score = score end @@ -25,7 +24,7 @@ class SubcategoryCardPresenter end def to_model - subcategory + construct end private diff --git a/app/presenters/variance_chart_row_presenter.rb b/app/presenters/overview/variance_chart_row_presenter.rb similarity index 91% rename from app/presenters/variance_chart_row_presenter.rb rename to app/presenters/overview/variance_chart_row_presenter.rb index 72def602..b8a5db6d 100644 --- a/app/presenters/variance_chart_row_presenter.rb +++ b/app/presenters/overview/variance_chart_row_presenter.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class VarianceChartRowPresenter +class Overview::VarianceChartRowPresenter include Comparable attr_reader :score, :measure_name, :measure_id, :category @@ -30,7 +30,7 @@ class VarianceChartRowPresenter def x_offset case zone.type when :ideal, :approval - '60%' + "60%" else "#{((0.6 - bar_width_percentage) * 100).abs.round(2)}%" end @@ -57,9 +57,9 @@ class VarianceChartRowPresenter def partial_data_sources [].tap do |sources| - sources << 'teacher survey results' if @measure.includes_teacher_survey_items? && !@meets_teacher_threshold - sources << 'student survey results' if @measure.includes_student_survey_items? && !@meets_student_threshold - sources << 'administrative data' if @measure.includes_admin_data_items? + sources << "teacher survey results" if @measure.includes_teacher_survey_items? && !@meets_teacher_threshold + sources << "student survey results" if @measure.includes_student_survey_items? && !@meets_student_threshold + sources << "administrative data" if @measure.includes_admin_data_items? end end diff --git a/app/presenters/parent_category_presenter.rb b/app/presenters/parent_category_presenter.rb new file mode 100644 index 00000000..6d17401a --- /dev/null +++ b/app/presenters/parent_category_presenter.rb @@ -0,0 +1,18 @@ +class ParentCategoryPresenter < CategoryPresenter + def harvey_scorecard_presenters(school:, academic_year:) + @category.scales.parent_scales.map do |scale| + measure = scale.measure + 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 + ) + + Overview::ScorecardPresenter.new(construct: scale, + zones:, + score: scale.parent_score(school:, academic_year:), + id: scale.scale_id) + end + end +end diff --git a/app/presenters/parent_scale_presenter.rb b/app/presenters/parent_scale_presenter.rb index c8dbf319..96dc0435 100644 --- a/app/presenters/parent_scale_presenter.rb +++ b/app/presenters/parent_scale_presenter.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -class ParentScalePresenter < MeasurePresenter - attr_reader :scale, :academic_year, :school, :name, :description +class ParentScalePresenter + attr_reader :scale, :academic_year, :school, :name, :description, :id def initialize(scale:, academic_year:, school:) @scale = scale @@ -9,6 +9,7 @@ class ParentScalePresenter < MeasurePresenter @school = school @name = scale.name @description = scale.description + @id = scale.scale_id end def title diff --git a/app/presenters/subcategory_presenter.rb b/app/presenters/subcategory_presenter.rb index 30252da7..9bbb3650 100644 --- a/app/presenters/subcategory_presenter.rb +++ b/app/presenters/subcategory_presenter.rb @@ -16,10 +16,6 @@ class SubcategoryPresenter GaugePresenter.new(zones:, score: average_score) end - def subcategory_card_presenter - SubcategoryCardPresenter.new(subcategory: @subcategory, zones:, score: average_score) - end - def average_score @average_score ||= @subcategory.score(school: @school, academic_year: @academic_year) end diff --git a/app/views/overview/_harvey_scorecard.html.erb b/app/views/overview/_harvey_scorecard.html.erb new file mode 100644 index 00000000..cbd4171e --- /dev/null +++ b/app/views/overview/_harvey_scorecard.html.erb @@ -0,0 +1,7 @@ +
+ data-bs-placement="top" data-bs-content="This subcategory is not displayed due to limited availability of school data and/or low survey response rates."> + + + + <%= link_to(harvey_scorecard.name, district_school_category_path( @district, @school, harvey_scorecard.category, {year: @academic_year.range, anchor: "#{harvey_scorecard.id}"})) %> +
diff --git a/app/views/overview/_quality_framework_indicators.erb b/app/views/overview/_quality_framework_indicators.erb index f172c168..2562d8fc 100644 --- a/app/views/overview/_quality_framework_indicators.erb +++ b/app/views/overview/_quality_framework_indicators.erb @@ -1,22 +1,24 @@ -
- <% category_presenters.each do |category_presenter| %> -
- -
-
-

- <%= link_to [@district, @school, category_presenter, { year: @academic_year.range }] do %> - <%= category_presenter.name %> - <% end %> -

-
+
+
+ <% category_presenters.each do |category_presenter| %> +
+ +
+
+

+ <%= link_to [@district, @school, category_presenter, { year: @academic_year.range }] do %> + <%= category_presenter.name %> + <% end %> +

+
-

<%= category_presenter.short_description %>

+

<%= category_presenter.short_description %>

-
-
- <%= render partial: 'subcategory_card', collection: category_presenter.subcategories(academic_year: @academic_year, school: @school).map(&:subcategory_card_presenter) %> +
+
+ <%= render partial: 'harvey_scorecard', collection: category_presenter.harvey_scorecard_presenters(school: @school, academic_year: @academic_year) %> +
-
- <% end %> + <% end %> +
diff --git a/app/views/overview/_student_parent_toggle.html.erb b/app/views/overview/_student_parent_toggle.html.erb index 637db33e..c1ac2ba2 100644 --- a/app/views/overview/_student_parent_toggle.html.erb +++ b/app/views/overview/_student_parent_toggle.html.erb @@ -1,7 +1,10 @@ -
- - - - - -
+
+<%= link_to(district_school_overview_index_path(@district, @school, year: @academic_year.range, view: "student")) do %> + > + +<% end %> +<%= link_to(district_school_overview_index_path(@district, @school, year: @academic_year.range, view: "parent")) do %> + > + +<% end %> +
diff --git a/app/views/overview/_subcategory_card.html.erb b/app/views/overview/_subcategory_card.html.erb deleted file mode 100644 index 10a6fdc7..00000000 --- a/app/views/overview/_subcategory_card.html.erb +++ /dev/null @@ -1,7 +0,0 @@ -
- data-bs-placement="top" data-bs-content="This subcategory is not displayed due to limited availability of school data and/or low survey response rates."> - - - - <%= link_to(subcategory_card.name, district_school_category_path( @district, @school, subcategory_card.category, {year: @academic_year.range, anchor: "#{subcategory_card.subcategory_id}"})) %> -
diff --git a/app/views/overview/index.html.erb b/app/views/overview/index.html.erb index 5950423c..9955487a 100644 --- a/app/views/overview/index.html.erb +++ b/app/views/overview/index.html.erb @@ -18,10 +18,12 @@
<%= render partial: "quality_framework_indicators", locals: { category_presenters: @category_presenters } %> +<% if @page.show_response_rates %>
<%= render partial: "response_rate", locals: {response_rate_presenter: @student_response_rate_presenter} %> <%= render partial: "response_rate", locals: {response_rate_presenter: @teacher_response_rate_presenter} %>
+ <% end %>
diff --git a/spec/presenters/scorecard_presenter_spec.rb b/spec/presenters/scorecard_presenter_spec.rb new file mode 100644 index 00000000..3ce76dff --- /dev/null +++ b/spec/presenters/scorecard_presenter_spec.rb @@ -0,0 +1,89 @@ +require "rails_helper" + +describe Overview::ScorecardPresenter do + 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 + + let(:subcategory_card_presenter) do + subcategory = Subcategory.new(name: "A subcategory") + Overview::ScorecardPresenter.new(construct: subcategory, zones:, score:, id: subcategory.subcategory_id) + end + + context "when the given score is in the Warning zone for the given scale" do + let(:score) { 1 } + + it "returns the icon that represents the zone" do + expect(subcategory_card_presenter.harvey_ball_icon).to eq "warning-harvey-ball" + end + + it "returns the color class of the zone" do + expect(subcategory_card_presenter.color).to eq "warning" + end + end + + context "when the given score is in the Watch zone for the given scale" do + let(:score) { 2 } + + it "returns the icon that represents the zone" do + expect(subcategory_card_presenter.harvey_ball_icon).to eq "watch-harvey-ball" + end + + it "returns the color class of the zone" do + expect(subcategory_card_presenter.color).to eq "watch" + end + end + + context "when the given score is in the Growth zone for the given scale" do + let(:score) { 3 } + + it "returns the icon that represents the zone" do + expect(subcategory_card_presenter.harvey_ball_icon).to eq "growth-harvey-ball" + end + + it "returns the color class of the zone" do + expect(subcategory_card_presenter.color).to eq "growth" + end + end + + context "when the given score is in the Approval zone for the given scale" do + let(:score) { 4 } + + it "returns the icon that represents the zone" do + expect(subcategory_card_presenter.harvey_ball_icon).to eq "approval-harvey-ball" + end + + it "returns the color class of the zone" do + expect(subcategory_card_presenter.color).to eq "approval" + end + end + + context "when the given score is in the Ideal zone for the given scale" do + let(:score) { 5 } + + it "returns the icon that represents the zone" do + expect(subcategory_card_presenter.harvey_ball_icon).to eq "ideal-harvey-ball" + end + + it "returns the color class of the zone" do + expect(subcategory_card_presenter.color).to eq "ideal" + end + end + + context "when the given score is invalid for the given scale" do + let(:score) { 0 } + + it "returns the icon that represents the zone" do + expect(subcategory_card_presenter.harvey_ball_icon).to eq "insufficient_data-harvey-ball" + end + + it "reports that there is insufficient data" do + expect(subcategory_card_presenter.insufficient_data?).to be true + end + end +end diff --git a/spec/presenters/subcategory_card_presenter_spec.rb b/spec/presenters/subcategory_card_presenter_spec.rb deleted file mode 100644 index 627f7934..00000000 --- a/spec/presenters/subcategory_card_presenter_spec.rb +++ /dev/null @@ -1,86 +0,0 @@ -require 'rails_helper' - -describe SubcategoryCardPresenter do - 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 - - let(:subcategory_card_presenter) { SubcategoryCardPresenter.new(subcategory: Subcategory.new(name: 'A subcategory'), zones:, score:) } - - context 'when the given score is in the Warning zone for the given scale' do - let(:score) { 1 } - - it 'returns the icon that represents the zone' do - expect(subcategory_card_presenter.harvey_ball_icon).to eq 'warning-harvey-ball' - end - - it 'returns the color class of the zone' do - expect(subcategory_card_presenter.color).to eq 'warning' - end - end - - context 'when the given score is in the Watch zone for the given scale' do - let(:score) { 2 } - - it 'returns the icon that represents the zone' do - expect(subcategory_card_presenter.harvey_ball_icon).to eq 'watch-harvey-ball' - end - - it 'returns the color class of the zone' do - expect(subcategory_card_presenter.color).to eq 'watch' - end - end - - context 'when the given score is in the Growth zone for the given scale' do - let(:score) { 3 } - - it 'returns the icon that represents the zone' do - expect(subcategory_card_presenter.harvey_ball_icon).to eq 'growth-harvey-ball' - end - - it 'returns the color class of the zone' do - expect(subcategory_card_presenter.color).to eq 'growth' - end - end - - context 'when the given score is in the Approval zone for the given scale' do - let(:score) { 4 } - - it 'returns the icon that represents the zone' do - expect(subcategory_card_presenter.harvey_ball_icon).to eq 'approval-harvey-ball' - end - - it 'returns the color class of the zone' do - expect(subcategory_card_presenter.color).to eq 'approval' - end - end - - context 'when the given score is in the Ideal zone for the given scale' do - let(:score) { 5 } - - it 'returns the icon that represents the zone' do - expect(subcategory_card_presenter.harvey_ball_icon).to eq 'ideal-harvey-ball' - end - - it 'returns the color class of the zone' do - expect(subcategory_card_presenter.color).to eq 'ideal' - end - end - - context 'when the given score is invalid for the given scale' do - let(:score) { 0 } - - it 'returns the icon that represents the zone' do - expect(subcategory_card_presenter.harvey_ball_icon).to eq 'insufficient_data-harvey-ball' - end - - it 'reports that there is insufficient data' do - expect(subcategory_card_presenter.insufficient_data?).to be true - end - end -end diff --git a/spec/presenters/subcategory_presenter_spec.rb b/spec/presenters/subcategory_presenter_spec.rb index dfd0c22d..a225fdeb 100644 --- a/spec/presenters/subcategory_presenter_spec.rb +++ b/spec/presenters/subcategory_presenter_spec.rb @@ -85,16 +85,6 @@ describe SubcategoryPresenter do expect(subcategory_presenter.measure_presenters.count).to eq subcategory.measures.count end - context "When there are no measures populated with student or teacher surveys" do - let(:empty_subcategory) { create :subcategory } - let(:empty_subcategory_presenter) do - SubcategoryPresenter.new(subcategory: empty_subcategory, academic_year:, school:) - end - it "should make a subcategory presenter return insufficient data" do - expect(empty_subcategory_presenter.subcategory_card_presenter.insufficient_data?).to eq true - end - end - def create_survey_item_responses_for_different_years_and_schools(survey_item) create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD, survey_item:, school: worst_school, likert_score: 1) diff --git a/spec/presenters/variance_chart_row_presenter_spec.rb b/spec/presenters/variance_chart_row_presenter_spec.rb index 579dee33..7e937783 100644 --- a/spec/presenters/variance_chart_row_presenter_spec.rb +++ b/spec/presenters/variance_chart_row_presenter_spec.rb @@ -1,6 +1,6 @@ -require 'rails_helper' +require "rails_helper" -describe VarianceChartRowPresenter do +describe Overview::VarianceChartRowPresenter do let(:watch_low_benchmark) { 2.9 } let(:growth_low_benchmark) { 3.1 } let(:approval_low_benchmark) { 3.6 } @@ -9,7 +9,7 @@ describe VarianceChartRowPresenter do let(:measure) do measure = create( :measure, - name: 'Some Title' + name: "Some Title" ) scale = create(:scale, measure:) @@ -25,210 +25,210 @@ describe VarianceChartRowPresenter do let(:measure_without_admin_data_items) do create( :measure, - name: 'Some Title' + name: "Some Title" ) end let(:presenter) do - VarianceChartRowPresenter.new measure:, score: + Overview::VarianceChartRowPresenter.new measure:, score: end - shared_examples_for 'measure_name' do - it 'returns the measure name' do - expect(presenter.measure_name).to eq 'Some Title' + shared_examples_for "measure_name" do + it "returns the measure name" do + expect(presenter.measure_name).to eq "Some Title" end end - context 'when the score is in the Ideal zone' do + context "when the score is in the Ideal zone" do let(:score) { Score.new(average: 4.4, meets_teacher_threshold: true, meets_student_threshold: true) } - it_behaves_like 'measure_name' + it_behaves_like "measure_name" - it 'returns the correct color' do - expect(presenter.bar_color).to eq 'fill-ideal' + it "returns the correct color" do + expect(presenter.bar_color).to eq "fill-ideal" end - it 'returns a bar width equal to the approval zone width plus the proportionate ideal zone width' do - expect(presenter.bar_width).to eq '30.0%' + it "returns a bar width equal to the approval zone width plus the proportionate ideal zone width" do + expect(presenter.bar_width).to eq "30.0%" end - it 'returns an x-offset of 60%' do - expect(presenter.x_offset).to eq '60%' + it "returns an x-offset of 60%" do + expect(presenter.x_offset).to eq "60%" end end - context 'when the score is in the Approval zone' do + context "when the score is in the Approval zone" do let(:score) { Score.new(average: 3.7, meets_teacher_threshold: true, meets_student_threshold: true) } - it_behaves_like 'measure_name' + it_behaves_like "measure_name" - it 'returns the correct color' do - expect(presenter.bar_color).to eq 'fill-approval' + it "returns the correct color" do + expect(presenter.bar_color).to eq "fill-approval" end - it 'returns a bar width equal to the proportionate approval zone width' do - expect(presenter.bar_width).to eq '10.0%' + it "returns a bar width equal to the proportionate approval zone width" do + expect(presenter.bar_width).to eq "10.0%" end - it 'returns an x-offset of 60%' do - expect(presenter.x_offset).to eq '60%' + it "returns an x-offset of 60%" do + expect(presenter.x_offset).to eq "60%" end end - context 'when the score is in the Growth zone' do + context "when the score is in the Growth zone" do let(:score) { Score.new(average: 3.2, meets_teacher_threshold: true, meets_student_threshold: true) } - it_behaves_like 'measure_name' + it_behaves_like "measure_name" - it 'returns the correct color' do - expect(presenter.bar_color).to eq 'fill-growth' + it "returns the correct color" do + expect(presenter.bar_color).to eq "fill-growth" end - it 'returns a bar width equal to the proportionate growth zone width' do - expect(presenter.bar_width).to eq '16.0%' + it "returns a bar width equal to the proportionate growth zone width" do + expect(presenter.bar_width).to eq "16.0%" end - context 'in order to achieve the visual effect' do - it 'returns an x-offset equal to 60% minus the bar width' do - expect(presenter.x_offset).to eq '44.0%' + context "in order to achieve the visual effect" do + it "returns an x-offset equal to 60% minus the bar width" do + expect(presenter.x_offset).to eq "44.0%" end end end - context 'when the score is in the Watch zone' do + context "when the score is in the Watch zone" do let(:score) { Score.new(average: 2.9, meets_teacher_threshold: true, meets_student_threshold: true) } - it_behaves_like 'measure_name' + it_behaves_like "measure_name" - it 'returns the correct color' do - expect(presenter.bar_color).to eq 'fill-watch' + it "returns the correct color" do + expect(presenter.bar_color).to eq "fill-watch" end - it 'returns a bar width equal to the proportionate watch zone width plus the growth zone width' do - expect(presenter.bar_width).to eq '40.0%' + it "returns a bar width equal to the proportionate watch zone width plus the growth zone width" do + expect(presenter.bar_width).to eq "40.0%" end - context 'in order to achieve the visual effect' do - it 'returns an x-offset equal to 60% minus the bar width' do - expect(presenter.x_offset).to eq '20.0%' + context "in order to achieve the visual effect" do + it "returns an x-offset equal to 60% minus the bar width" do + expect(presenter.x_offset).to eq "20.0%" end end end - context 'when the score is in the Warning zone' do + context "when the score is in the Warning zone" do let(:score) { Score.new(average: 1.0, meets_teacher_threshold: true, meets_student_threshold: true) } - it_behaves_like 'measure_name' + it_behaves_like "measure_name" - it 'returns the correct color' do - expect(presenter.bar_color).to eq 'fill-warning' + it "returns the correct color" do + expect(presenter.bar_color).to eq "fill-warning" end - it 'returns a bar width equal to the proportionate warning zone width plus the watch & growth zone widths' do - expect(presenter.bar_width).to eq '60.0%' + it "returns a bar width equal to the proportionate warning zone width plus the watch & growth zone widths" do + expect(presenter.bar_width).to eq "60.0%" end - context 'in order to achieve the visual effect' do - it 'returns an x-offset equal to 60% minus the bar width' do - expect(presenter.x_offset).to eq '0.0%' + context "in order to achieve the visual effect" do + it "returns an x-offset equal to 60% minus the bar width" do + expect(presenter.x_offset).to eq "0.0%" end end end - context 'when a measure does not contain admin data items' do - let(:score) { Score.new(average: nil, meets_teacher_threshold: false, meets_student_threshold: false) } + context "when a measure does not contain admin data items" do + let(:score) { Score.new(average: nil, meets_teacher_threshold: false, meets_student_threshold: false) } - it 'it does not show a partial data indicator' do - presenter_without_admin_data = VarianceChartRowPresenter.new measure: measure_without_admin_data_items, - score: score + it "it does not show a partial data indicator" do + presenter_without_admin_data = Overview::VarianceChartRowPresenter.new(measure: measure_without_admin_data_items, + score:) expect(presenter_without_admin_data.show_partial_data_indicator?).to be false end end - context 'when a measure contains admin data items' do + context "when a measure contains admin data items" do before :each do end - let(:score) { Score.new(average: nil, meets_teacher_threshold: false, meets_student_threshold: false) } + let(:score) { Score.new(average: nil, meets_teacher_threshold: false, meets_student_threshold: false) } - it 'shows a partial data indicator' do + it "shows a partial data indicator" do measure_with_admin_data = create( :measure, - name: 'Some Title' + name: "Some Title" ) scale_with_admin_data = create(:scale, measure: measure_with_admin_data) - create :admin_data_item, + create(:admin_data_item, scale: scale_with_admin_data, - watch_low_benchmark: watch_low_benchmark, - growth_low_benchmark: growth_low_benchmark, - approval_low_benchmark: approval_low_benchmark, - ideal_low_benchmark: ideal_low_benchmark - admin_data_presenter = VarianceChartRowPresenter.new measure: measure_with_admin_data, - score: Score.new( - average: 3.7, meets_teacher_threshold: true, meets_student_threshold: true - ) + watch_low_benchmark:, + growth_low_benchmark:, + approval_low_benchmark:, + ideal_low_benchmark:) + admin_data_presenter = Overview::VarianceChartRowPresenter.new measure: measure_with_admin_data, + score: Score.new( + average: 3.7, meets_teacher_threshold: true, meets_student_threshold: true + ) expect(admin_data_presenter.show_partial_data_indicator?).to be true - expect(admin_data_presenter.partial_data_sources).to eq ['administrative data'] + expect(admin_data_presenter.partial_data_sources).to eq ["administrative data"] end end - context 'when a measure contains teacher survey items' do + context "when a measure contains teacher survey items" do before :each do scale = create(:scale, measure:) create :teacher_survey_item, scale: end - context 'when there are insufficient teacher survey item responses' do + context "when there are insufficient teacher survey item responses" do let(:score) { Score.new(average: nil, meets_teacher_threshold: false, meets_student_threshold: true) } - it 'shows a partial data indicator' do + it "shows a partial data indicator" do expect(presenter.show_partial_data_indicator?).to be true - expect(presenter.partial_data_sources).to eq ['teacher survey results'] + expect(presenter.partial_data_sources).to eq ["teacher survey results"] end end - context 'when there are sufficient teacher survey item responses' do + context "when there are sufficient teacher survey item responses" do let(:score) { Score.new(average: nil, meets_teacher_threshold: true, meets_student_threshold: true) } - it 'does not show a partial data indicator' do + it "does not show a partial data indicator" do expect(presenter.show_partial_data_indicator?).to be false end end end - context 'when a measure contains student survey items' do + context "when a measure contains student survey items" do before :each do scale = create(:scale, measure:) create :student_survey_item, scale: end - context 'when there are insufficient student survey item responses' do + context "when there are insufficient student survey item responses" do let(:score) { Score.new(average: nil, meets_teacher_threshold: true, meets_student_threshold: false) } - it 'shows a partial data indicator' do + it "shows a partial data indicator" do expect(presenter.show_partial_data_indicator?).to be true - expect(presenter.partial_data_sources).to eq ['student survey results'] + expect(presenter.partial_data_sources).to eq ["student survey results"] end - context 'where there are also admin data items' do + context "where there are also admin data items" do before :each do 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 - expect(presenter.partial_data_sources).to eq ['student survey results', 'administrative data'] + it "returns the sources for partial results of administrative data and student survey results" do + expect(presenter.partial_data_sources).to eq ["student survey results", "administrative data"] end end end - context 'When there are sufficient student survey item responses' do + context "When there are sufficient student survey item responses" do let(:score) { Score.new(average: nil, meets_teacher_threshold: true, meets_student_threshold: true) } - it 'does not show a partial data indicator' do + it "does not show a partial data indicator" do expect(presenter.show_partial_data_indicator?).to be false end end end - context 'sorting scores' do - it 'selects a longer bar before a shorter bar for measures in the approval/ideal zones' 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, scale: scale_with_student_survey_items, @@ -236,15 +236,27 @@ describe VarianceChartRowPresenter do growth_low_benchmark:, approval_low_benchmark:, ideal_low_benchmark:) - approval_presenter = VarianceChartRowPresenter.new measure: measure, score: Score.new(average: 3.7, meets_teacher_threshold: true,meets_student_threshold: true) - ideal_presenter = VarianceChartRowPresenter.new measure: measure, score: Score.new(average: 4.4, meets_teacher_threshold: true, meets_student_threshold: true) + approval_presenter = Overview::VarianceChartRowPresenter.new measure:, + score: Score.new( + average: 3.7, meets_teacher_threshold: true, meets_student_threshold: true + ) + ideal_presenter = Overview::VarianceChartRowPresenter.new measure:, + score: Score.new( + average: 4.4, meets_teacher_threshold: true, meets_student_threshold: true + ) expect(ideal_presenter <=> approval_presenter).to be < 0 expect([approval_presenter, ideal_presenter].sort).to eq [ideal_presenter, approval_presenter] end - it 'selects a warning bar below a ideal bar' do - warning_presenter = VarianceChartRowPresenter.new measure: measure, score: Score.new(average: 1.0, meets_teacher_threshold: true, meets_student_threshold: true) - ideal_presenter = VarianceChartRowPresenter.new measure: measure, score: Score.new(average: 5.0, meets_teacher_threshold: true, meets_student_threshold: true) + it "selects a warning bar below a ideal bar" do + warning_presenter = Overview::VarianceChartRowPresenter.new measure:, + score: Score.new( + average: 1.0, meets_teacher_threshold: true, meets_student_threshold: true + ) + ideal_presenter = Overview::VarianceChartRowPresenter.new measure:, + score: Score.new( + average: 5.0, meets_teacher_threshold: true, meets_student_threshold: true + ) expect(warning_presenter <=> ideal_presenter).to be > 0 expect([warning_presenter, ideal_presenter].sort).to eq [ideal_presenter, warning_presenter] end diff --git a/spec/views/overview/index.html.erb_spec.rb b/spec/views/overview/index.html.erb_spec.rb index 375744d3..a74ba6cd 100644 --- a/spec/views/overview/index.html.erb_spec.rb +++ b/spec/views/overview/index.html.erb_spec.rb @@ -40,31 +40,33 @@ describe "overview/index" do measure end - before :each do - assign :category_presenters, [] - assign :variance_chart_row_presenters, variance_chart_row_presenters - @academic_year = create(:academic_year) - assign :academic_years, [@academic_year] - @district = create(:district) - @school = create(:school) - @student_response_rate_presenter = ResponseRatePresenter.new(focus: :student, school: @school, - academic_year: @academic_year) - @teacher_response_rate_presenter = ResponseRatePresenter.new(focus: :teacher, school: @school, - academic_year: @academic_year) - - Respondent.create!(school: @school, academic_year: @academic_year, total_students: 40, total_teachers: 40) - ResponseRate.create!(subcategory: Subcategory.first, school: @school, academic_year: @academic_year, - student_response_rate: 100, teacher_response_rate: 100, meets_student_threshold: true, meets_teacher_threshold: true) - - render - end - context "when some presenters have a nil score" do + before :each do + assign :category_presenters, [] + assign :variance_chart_row_presenters, variance_chart_row_presenters + @academic_year = create(:academic_year) + assign :academic_years, [@academic_year] + @district = create(:district) + @school = create(:school) + assign :page, + Overview::OverviewPresenter.new(params: { view: "student" }, school: @school, + academic_year: @academic_year) + @student_response_rate_presenter = ResponseRatePresenter.new(focus: :student, school: @school, + academic_year: @academic_year) + @teacher_response_rate_presenter = ResponseRatePresenter.new(focus: :teacher, school: @school, + academic_year: @academic_year) + + Respondent.create!(school: @school, academic_year: @academic_year, total_students: 40, total_teachers: 40) + ResponseRate.create!(subcategory: Subcategory.first, school: @school, academic_year: @academic_year, + student_response_rate: 100, teacher_response_rate: 100, meets_student_threshold: true, meets_teacher_threshold: true) + + render + end let(:variance_chart_row_presenters) do [ - VarianceChartRowPresenter.new(measure: support_for_teaching, score: Score.new), - VarianceChartRowPresenter.new(measure: effective_leadership, score: Score.new(average: rand)), - VarianceChartRowPresenter.new(measure: professional_qualifications, score: Score.new) + Overview::VarianceChartRowPresenter.new(measure: support_for_teaching, score: Score.new), + Overview::VarianceChartRowPresenter.new(measure: effective_leadership, score: Score.new(average: rand)), + Overview::VarianceChartRowPresenter.new(measure: professional_qualifications, score: Score.new) ] end @@ -84,6 +86,27 @@ describe "overview/index" do end context "when all the presenters have a non-nil score" do + before :each do + assign :category_presenters, [] + assign :variance_chart_row_presenters, variance_chart_row_presenters + @academic_year = create(:academic_year) + assign :academic_years, [@academic_year] + @district = create(:district) + @school = create(:school) + assign :page, + Overview::OverviewPresenter.new(params: { view: "student" }, school: @school, + academic_year: @academic_year) + @student_response_rate_presenter = ResponseRatePresenter.new(focus: :student, school: @school, + academic_year: @academic_year) + @teacher_response_rate_presenter = ResponseRatePresenter.new(focus: :teacher, school: @school, + academic_year: @academic_year) + + Respondent.create!(school: @school, academic_year: @academic_year, total_students: 40, total_teachers: 40) + ResponseRate.create!(subcategory: Subcategory.first, school: @school, academic_year: @academic_year, + student_response_rate: 100, teacher_response_rate: 100, meets_student_threshold: true, meets_teacher_threshold: true) + + render + end let(:variance_chart_row_presenters) do measure = create(:measure, name: "Display Me", measure_id: "display-me") scale = create(:scale, measure:) @@ -94,8 +117,8 @@ describe "overview/index" do approval_low_benchmark: 3.5, ideal_low_benchmark: 4.5) [ - VarianceChartRowPresenter.new(measure:, - score: Score.new(average: rand)) + Overview::VarianceChartRowPresenter.new(measure:, + score: Score.new(average: rand)) ] end @@ -113,4 +136,88 @@ describe "overview/index" do expect(displayed_variance_labels.first.inner_text).to include "Display Me" end end + + context "when the default view is shown" 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, + scale:, + watch_low_benchmark: 1.5, + growth_low_benchmark: 2.5, + approval_low_benchmark: 3.5, + ideal_low_benchmark: 4.5) + [ + Overview::VarianceChartRowPresenter.new(measure:, + score: Score.new(average: rand)) + ] + end + + before :each do + assign :category_presenters, [] + assign :variance_chart_row_presenters, variance_chart_row_presenters + @academic_year = create(:academic_year) + assign :academic_years, [@academic_year] + @district = create(:district) + @school = create(:school) + assign :page, + Overview::OverviewPresenter.new(params: { view: "student" }, school: @school, + academic_year: @academic_year) + @student_response_rate_presenter = ResponseRatePresenter.new(focus: :student, school: @school, + academic_year: @academic_year) + @teacher_response_rate_presenter = ResponseRatePresenter.new(focus: :teacher, school: @school, + academic_year: @academic_year) + + Respondent.create!(school: @school, academic_year: @academic_year, total_students: 40, total_teachers: 40) + ResponseRate.create!(subcategory: Subcategory.first, school: @school, academic_year: @academic_year, + student_response_rate: 100, teacher_response_rate: 100, meets_student_threshold: true, meets_teacher_threshold: true) + + render + end + it "shows the view with the students & teachers button active" do + expect(subject.css("input[id='student_and_teacher_btn'][checked='checked']").count).to eq 1 + end + end + + context "when the parent view is shown" 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, + scale:, + watch_low_benchmark: 1.5, + growth_low_benchmark: 2.5, + approval_low_benchmark: 3.5, + ideal_low_benchmark: 4.5) + [ + Overview::VarianceChartRowPresenter.new(measure:, + score: Score.new(average: rand)) + ] + end + + before :each do + assign :category_presenters, [] + assign :variance_chart_row_presenters, variance_chart_row_presenters + @academic_year = create(:academic_year) + assign :academic_years, [@academic_year] + @district = create(:district) + @school = create(:school) + assign :page, + Overview::OverviewPresenter.new(params: { view: "parent" }, school: @school, + academic_year: @academic_year) + @student_response_rate_presenter = ResponseRatePresenter.new(focus: :student, school: @school, + academic_year: @academic_year) + @teacher_response_rate_presenter = ResponseRatePresenter.new(focus: :teacher, school: @school, + academic_year: @academic_year) + + Respondent.create!(school: @school, academic_year: @academic_year, total_students: 40, total_teachers: 40) + ResponseRate.create!(subcategory: Subcategory.first, school: @school, academic_year: @academic_year, + student_response_rate: 100, teacher_response_rate: 100, meets_student_threshold: true, meets_teacher_threshold: true) + + render + end + it "shows the view with the students & teachers button active" do + expect(subject.css("input[id='parent_btn'][checked='checked']").count).to eq 1 + end + end end diff --git a/spec/views/overview/variance_chart.html.erb_spec.rb b/spec/views/overview/variance_chart.html.erb_spec.rb index 81c86b72..a1547fa6 100644 --- a/spec/views/overview/variance_chart.html.erb_spec.rb +++ b/spec/views/overview/variance_chart.html.erb_spec.rb @@ -27,8 +27,8 @@ describe "overview/_variance_chart.html.erb" do before :each do presenters = [ - VarianceChartRowPresenter.new(measure: lower_scoring_measure, score: Score.new(average: 1)), - VarianceChartRowPresenter.new(measure: higher_scoring_measure, score: Score.new(average: 5)) + Overview::VarianceChartRowPresenter.new(measure: lower_scoring_measure, score: Score.new(average: 1)), + Overview::VarianceChartRowPresenter.new(measure: higher_scoring_measure, score: Score.new(average: 5)) ] render partial: "variance_chart", locals: { presenters: } @@ -53,8 +53,8 @@ describe "overview/_variance_chart.html.erb" do measure_lacking_score = create(:measure) another_measure_lacking_score = create(:measure) presenters = [ - VarianceChartRowPresenter.new(measure: measure_lacking_score, score: Score.new(average: nil)), - VarianceChartRowPresenter.new(measure: another_measure_lacking_score, score: Score.new(average: nil)) + Overview::VarianceChartRowPresenter.new(measure: measure_lacking_score, score: Score.new(average: nil)), + Overview::VarianceChartRowPresenter.new(measure: another_measure_lacking_score, score: Score.new(average: nil)) ] render partial: "variance_chart", locals: { presenters: }