From 9837fbf0d05707f29266a9b2c71f7c75c59b31ef Mon Sep 17 00:00:00 2001 From: Nelson Jovel Date: Thu, 26 May 2022 12:05:25 -0700 Subject: [PATCH] If the measure is not based on a type of data(student/teacher) or if there is insufficent data to calculate a score, show a message on the graph indicating such --- app/controllers/analyze_controller.rb | 2 +- .../grouped_bar_column_presenter.rb | 28 +- .../analyze/_grouped_bar_column.html.erb | 19 +- .../grouped_bar_column_presenter_spec.rb | 245 ++++++++++-------- 4 files changed, 177 insertions(+), 117 deletions(-) diff --git a/app/controllers/analyze_controller.rb b/app/controllers/analyze_controller.rb index de36b148..1fe80d04 100644 --- a/app/controllers/analyze_controller.rb +++ b/app/controllers/analyze_controller.rb @@ -6,7 +6,7 @@ class AnalyzeController < SqmApplicationController @subcategory ||= Subcategory.find_by_subcategory_id(params[:subcategory_id]) @subcategory ||= Subcategory.find_by_subcategory_id('1A') - @measure = @subcategory.measures.includes(%i[admin_data_items category])[0] + @measure = @subcategory.measures.order(:measure_id).includes(%i[admin_data_items category])[0] @academic_year ||= AcademicYear.order('range DESC').first end end diff --git a/app/presenters/grouped_bar_column_presenter.rb b/app/presenters/grouped_bar_column_presenter.rb index d797ecb3..fdc90017 100644 --- a/app/presenters/grouped_bar_column_presenter.rb +++ b/app/presenters/grouped_bar_column_presenter.rb @@ -4,7 +4,7 @@ class GroupedBarColumnPresenter def initialize(measure:, score:, position:, type:) @measure = measure - @score = score.average + @score = score @meets_teacher_threshold = score.meets_teacher_threshold? @meets_student_threshold = score.meets_student_threshold? @measure_name = @measure.name @@ -45,7 +45,7 @@ class GroupedBarColumnPresenter end def percentage - (@score - zone.low_benchmark) / (zone.high_benchmark - zone.low_benchmark) + (@score.average - zone.low_benchmark) / (zone.high_benchmark - zone.low_benchmark) end def zone @@ -55,7 +55,7 @@ class GroupedBarColumnPresenter approval_low_benchmark: @measure.approval_low_benchmark, ideal_low_benchmark: @measure.ideal_low_benchmark ) - zones.zone_for_score(@score) + zones.zone_for_score(@score.average) end def label @@ -68,4 +68,26 @@ class GroupedBarColumnPresenter 'All Teachers' end end + + def show_irrelevancy_message? + return true if type == :student && !@measure.includes_student_survey_items? + + return true if type == :teacher && !@measure.includes_teacher_survey_items? + return true if type == :all && !@measure.includes_teacher_survey_items? && !@measure.includes_student_survey_items? + + false + end + + def show_insufficient_data_message? + case type + when :all + !score.meets_teacher_threshold? && !score.meets_student_threshold? + when :student + !score.meets_student_threshold? + when :teacher + !score.meets_teacher_threshold? + else + false + end + end end diff --git a/app/views/analyze/_grouped_bar_column.html.erb b/app/views/analyze/_grouped_bar_column.html.erb index 464601c2..e0b5689a 100644 --- a/app/views/analyze/_grouped_bar_column.html.erb +++ b/app/views/analyze/_grouped_bar_column.html.erb @@ -2,10 +2,27 @@ - <%= presenter.score %> + <%= presenter.score.average %> <%= presenter.label %> + + <% if presenter.show_irrelevancy_message? %> + + + + measure not + based on + <%= presenter.type %> surveys + + <% elsif presenter.show_insufficient_data_message? %> + + + + survey response + rate below 25% + + <% end %> diff --git a/spec/presenters/grouped_bar_column_presenter_spec.rb b/spec/presenters/grouped_bar_column_presenter_spec.rb index 97d0886f..3d33f978 100644 --- a/spec/presenters/grouped_bar_column_presenter_spec.rb +++ b/spec/presenters/grouped_bar_column_presenter_spec.rb @@ -6,7 +6,7 @@ describe GroupedBarColumnPresenter do let(:approval_low_benchmark) { 3.6 } let(:ideal_low_benchmark) { 3.8 } - let(:measure) do + let(:measure_with_student_survey_items) do measure = create( :measure, name: 'Some Title' @@ -22,6 +22,21 @@ describe GroupedBarColumnPresenter do measure end + let(:measure_with_teacher_survey_items) do + measure = create( + :measure, + name: 'Some Title' + ) + scale = create(:scale, measure:) + + create(:teacher_survey_item, scale:, + watch_low_benchmark:, + growth_low_benchmark:, + approval_low_benchmark:, + ideal_low_benchmark:) + + measure + end let(:measure_without_admin_data_items) do create( :measure, @@ -29,159 +44,165 @@ describe GroupedBarColumnPresenter do ) end - let(:presenter) do - GroupedBarColumnPresenter.new measure:, score:, position: 1, type: :all + let(:student_presenter) do + GroupedBarColumnPresenter.new measure: measure_with_student_survey_items, score:, position: 1, type: :student + end + + let(:teacher_presenter) do + GroupedBarColumnPresenter.new measure: measure_with_teacher_survey_items, score:, position: 1, type: :teacher end shared_examples_for 'measure_name' do it 'returns the measure name' do - expect(presenter.measure_name).to eq 'Some Title' + expect(student_presenter.measure_name).to eq 'Some Title' end end - context 'when the score is in the Ideal zone' do - let(:score) { Score.new(4.4, true, true) } + context 'when a measure is based on student survey items' do + context 'when the score is in the Ideal zone' do + let(:score) { Score.new(4.4, true, 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' - end + it 'returns the correct color' do + expect(student_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_height_percentage).to be_within(0.01).of(25.5) - end + it 'returns a bar width equal to the approval zone width plus the proportionate ideal zone width' do + expect(student_presenter.bar_height_percentage).to be_within(0.01).of(25.5) + end - it 'returns a y_offset equal to the ' do - expect(presenter.y_offset).to be_within(0.01).of(8.5) + it 'returns a y_offset equal to the ' do + expect(student_presenter.y_offset).to be_within(0.01).of(8.5) + end end - end - context 'when the score is in the Approval zone' do - let(:score) { Score.new(3.7, true, true) } + context 'when the score is in the Approval zone' do + let(:score) { Score.new(3.7, true, 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' - end + it 'returns the correct color' do + expect(student_presenter.bar_color).to eq 'fill-approval' + end - it 'returns a bar width equal to the proportionate approval zone width' do - expect(presenter.bar_height_percentage).to be_within(0.01).of(8.5) - end + it 'returns a bar width equal to the proportionate approval zone width' do + expect(student_presenter.bar_height_percentage).to be_within(0.01).of(8.5) + end - it 'returns an x-offset of 60%' do - expect(presenter.y_offset).to be_within(0.01).of(25.5) + it 'returns an x-offset of 60%' do + expect(student_presenter.y_offset).to be_within(0.01).of(25.5) + end end - end - context 'when the score is in the Growth zone' do - let(:score) { Score.new(3.2, true, true) } + context 'when the score is in the Growth zone' do + let(:score) { Score.new(3.2, true, 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' - end + it 'returns the correct color' do + expect(student_presenter.bar_color).to eq 'fill-growth' + end + + it 'returns a bar width equal to the proportionate growth zone width' do + expect(student_presenter.bar_height_percentage).to be_within(0.01).of(13.59) + end - it 'returns a bar width equal to the proportionate growth zone width' do - expect(presenter.bar_height_percentage).to be_within(0.01).of(13.59) + context 'in order to achieve the visual effect' do + it 'returns an x-offset equal to 60% minus the bar width' do + expect(student_presenter.y_offset).to eq 34 + end + end 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.y_offset).to eq 34 + context 'when the score is in the Watch zone' do + let(:score) { Score.new(2.9, true, true) } + + it_behaves_like 'measure_name' + + it 'returns the correct color' do + expect(student_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(student_presenter.bar_height_percentage).to eq 34 + end + + context 'in order to achieve the visual effect' do + it 'returns an x-offset equal to 60% minus the bar width' do + expect(student_presenter.y_offset).to eq 34 + end end end - end - context 'when the score is in the Watch zone' do - let(:score) { Score.new(2.9, true, true) } + context 'when the score is in the Warning zone' do + let(:score) { Score.new(1.0, true, 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(student_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(student_presenter.bar_height_percentage).to eq 51 + end + + context 'in order to achieve the visual effect' do + it 'returns an y-offset equal to 60% minus the bar width' do + expect(student_presenter.y_offset).to eq 34 + end + end end - it 'returns a bar width equal to the proportionate watch zone width plus the growth zone width' do - expect(presenter.bar_height_percentage).to eq 34 + context 'when there are insufficient responses to calculate a score' do + let(:score) { Score.new(nil, true, false) } + + it 'indicates it should show the insufficient data message' do + expect(student_presenter.show_insufficient_data_message?).to eq true + end end + context 'when there are enough responses to calculate a score' do + let(:score) { Score.new(nil, true, true) } - context 'in order to achieve the visual effect' do - it 'returns an x-offset equal to 60% minus the bar width' do - expect(presenter.y_offset).to eq 34 + it 'indicates it should show the insufficient data message' do + expect(student_presenter.show_insufficient_data_message?).to eq false end end end + context 'when the presenter type is student but the measure is not based on student surveys' do + let(:score) { Score.new(nil, false, false) } + let(:student_presenter) do + GroupedBarColumnPresenter.new measure: measure_without_admin_data_items, score:, position: 1, type: :student + end + it 'indecates it should show the irrelevancy message' do + expect(student_presenter.show_irrelevancy_message?).to be true + end + end - context 'when the score is in the Warning zone' do - let(:score) { Score.new(1.0, true, true) } - - it_behaves_like 'measure_name' + context 'when the measure is based on teacher survey items' do + context 'when there are insufficient responses to calculate a score' do + let(:score) { Score.new(nil, false, true) } - it 'returns the correct color' do - expect(presenter.bar_color).to eq 'fill-warning' + it 'indicates it should show the insufficient data message' do + expect(teacher_presenter.show_insufficient_data_message?).to eq true + end end + context 'when there are enough responses to calculate a score' do + let(:score) { Score.new(nil, true, true) } - it 'returns a bar width equal to the proportionate warning zone width plus the watch & growth zone widths' do - expect(presenter.bar_height_percentage).to eq 51 + it 'indicates it should show the insufficient data message' do + expect(teacher_presenter.show_insufficient_data_message?).to eq false + end end - - context 'in order to achieve the visual effect' do - it 'returns an y-offset equal to 60% minus the bar width' do - expect(presenter.y_offset).to eq 34 + context 'when the presenter type is teacher but the measure is not based on teacher surveys' do + let(:score) { Score.new(nil, false, false) } + let(:teacher_presenter) do + GroupedBarColumnPresenter.new measure: measure_without_admin_data_items, score:, position: 1, type: :teacher + end + it 'indecates it should show the irrelevancy message' do + expect(teacher_presenter.show_irrelevancy_message?).to be true end end end - - # 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 - # let(:score) { Score.new(nil, false, true) } - # it 'shows a message saying there are insufficient responses' do - # expect(presenter.insufficient_teacher_responses?).to be true - # end - # end - - # context 'when there are sufficient teacher survey item responses' do - # let(:score) { Score.new(nil, true, true) } - # it 'does not show a partial data indicator' do - # expect(presenter.show_teacher_inapplicability_message?).to be true - # end - # end - # end - - # context 'when a measure does not contain teacher survey items' do - # context 'when there are insufficient teacher survey item responses' do - # let(:score) { Score.new(nil, false, true) } - # it 'shows a message saying the measure is not based on teacher survey items' do - # expect(presenter.show_teacher_inapplicability_message?).to be false - # end - # end - # end - - # 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 - # let(:score) { Score.new(nil, true, false) } - # it 'shows a partial data indicator' do - # expect(presenter.show_student_inapplicability_message?).to be true - # end - # end - # context 'when there are sufficient student survey item responses' do - # let(:score) { Score.new(nil, true, true) } - # it 'shows a partial data indicator' do - # expect(presenter.show_student_inapplicability_message?).to be true - # end - # end - # end end