Create academic year checkbox selectors. Refresh page with years selected on

checkbox change.  Draw bar graphs for each academic year selected.
Center bar graphs in their column.  Color the columns to match the
sample colors on the checkboxes.  Add scores on beta to top of graph.
Automatically display the most recent year of data for the district.
Modify logic for the insufficient data message or the 'measure not based
on student/teacher surveys' message so it only shows if there are no
bars with data to display.
pull/1/head
rebuilt 4 years ago
parent 30c97f4428
commit 50256cacce

@ -27,20 +27,20 @@ module AnalyzeHelper
1 1
end end
def grouped_chart_width def grouped_chart_column_width
graph_width / data_sources graph_width / data_sources
end end
def grouped_chart_divider_x(position) def column_end_x(position)
zone_label_width + (grouped_chart_width * position) zone_label_width + (grouped_chart_column_width * position)
end end
def bar_label_height def column_start_x(position)
(100 - ((100 - analyze_graph_height) / 2)) column_end_x(position - 1)
end end
def bar_label_x(position) def bar_label_height
zone_label_width + (grouped_chart_width * position) - (grouped_chart_width / 2) (100 - ((100 - analyze_graph_height) / 2))
end end
def analyze_zone_height def analyze_zone_height
@ -66,4 +66,8 @@ module AnalyzeHelper
def analyze_subcategory_link(district:, school:, academic_year:, category:, subcategory:) def analyze_subcategory_link(district:, school:, academic_year:, category:, subcategory:)
"/districts/#{district.slug}/schools/#{school.slug}/analyze?year=#{academic_year.range}&category=#{category.category_id}&subcategory=#{subcategory.subcategory_id}" "/districts/#{district.slug}/schools/#{school.slug}/analyze?year=#{academic_year.range}&category=#{category.category_id}&subcategory=#{subcategory.subcategory_id}"
end end
def colors
@colors ||= ['#49416D', '#FFC857', '#920020', '#00B0B3', '#B2D236', '#595959']
end
end end

@ -0,0 +1,2 @@
module ColorHelper
end

@ -47,7 +47,7 @@ class Measure < ActiveRecord::Base
end end
def score(school:, academic_year:) def score(school:, academic_year:)
@score ||= Hash.new do |memo| @score ||= Hash.new do |memo, (school, academic_year)|
meets_student_threshold = sufficient_student_data?(school:, academic_year:) meets_student_threshold = sufficient_student_data?(school:, academic_year:)
meets_teacher_threshold = sufficient_teacher_data?(school:, academic_year:) meets_teacher_threshold = sufficient_teacher_data?(school:, academic_year:)
meets_admin_data_threshold = all_admin_data_collected?(school:, academic_year:) meets_admin_data_threshold = all_admin_data_collected?(school:, academic_year:)
@ -73,23 +73,27 @@ class Measure < ActiveRecord::Base
end end
def student_score(school:, academic_year:) def student_score(school:, academic_year:)
@student_score ||= begin @student_score ||= Hash.new do |memo, (school, academic_year)|
meets_student_threshold = sufficient_student_data?(school:, academic_year:) meets_student_threshold = sufficient_student_data?(school:, academic_year:)
meets_teacher_threshold = sufficient_teacher_data?(school:, academic_year:) meets_teacher_threshold = sufficient_teacher_data?(school:, academic_year:)
meets_admin_data_threshold = all_admin_data_collected?(school:, academic_year:) meets_admin_data_threshold = all_admin_data_collected?(school:, academic_year:)
average = collect_survey_scale_average(student_scales, school, academic_year) if meets_student_threshold average = collect_survey_scale_average(student_scales, school, academic_year) if meets_student_threshold
Score.new(average, meets_teacher_threshold, meets_student_threshold, meets_admin_data_threshold) memo[[school, academic_year]] = Score.new(average, meets_teacher_threshold, meets_student_threshold, meets_admin_data_threshold)
end end
@student_score[[school, academic_year]]
end end
def teacher_score(school:, academic_year:) def teacher_score(school:, academic_year:)
@teacher_score ||= begin @teacher_score ||= Hash.new do |memo, (school, academic_year)|
meets_student_threshold = sufficient_student_data?(school:, academic_year:) meets_student_threshold = sufficient_student_data?(school:, academic_year:)
meets_teacher_threshold = sufficient_teacher_data?(school:, academic_year:) meets_teacher_threshold = sufficient_teacher_data?(school:, academic_year:)
meets_admin_data_threshold = all_admin_data_collected?(school:, academic_year:) meets_admin_data_threshold = all_admin_data_collected?(school:, academic_year:)
average = collect_survey_scale_average(teacher_scales, school, academic_year) if meets_teacher_threshold average = collect_survey_scale_average(teacher_scales, school, academic_year) if meets_teacher_threshold
Score.new(average, meets_teacher_threshold, meets_student_threshold, meets_admin_data_threshold) memo[[school, academic_year]] = Score.new(average, meets_teacher_threshold, meets_student_threshold, meets_admin_data_threshold)
end end
@teacher_score[[school, academic_year]]
end end
def warning_low_benchmark def warning_low_benchmark

@ -5,7 +5,7 @@ class Scale < ApplicationRecord
has_many :admin_data_items has_many :admin_data_items
def score(school:, academic_year:) def score(school:, academic_year:)
@score ||= Hash.new do |memo| @score ||= Hash.new do |memo, (school, academic_year)|
memo[[school, academic_year]] = begin memo[[school, academic_year]] = begin
items = [] items = []
items << collect_survey_item_average(student_survey_items(school:, academic_year:), school, academic_year) items << collect_survey_item_average(student_survey_items(school:, academic_year:), school, academic_year)

@ -12,7 +12,7 @@ class Subcategory < ActiveRecord::Base
end end
def student_response_rate(school:, academic_year:) def student_response_rate(school:, academic_year:)
@student_response_rate ||= Hash.new do |memo| @student_response_rate ||= Hash.new do |memo, (school, academic_year)|
memo[[school, academic_year]] = StudentResponseRate.new(subcategory: self, school:, academic_year:) memo[[school, academic_year]] = StudentResponseRate.new(subcategory: self, school:, academic_year:)
end end
@ -20,7 +20,7 @@ class Subcategory < ActiveRecord::Base
end end
def teacher_response_rate(school:, academic_year:) def teacher_response_rate(school:, academic_year:)
@teacher_response_rate ||= Hash.new do |memo| @teacher_response_rate ||= Hash.new do |memo, (school, academic_year)|
memo[[school, academic_year]] = TeacherResponseRate.new(subcategory: self, school:, academic_year:) memo[[school, academic_year]] = TeacherResponseRate.new(subcategory: self, school:, academic_year:)
end end

@ -6,11 +6,10 @@ class SurveyItem < ActiveRecord::Base
has_many :survey_item_responses has_many :survey_item_responses
def score(school:, academic_year:) def score(school:, academic_year:)
@score ||= Hash.new do |memo| @score ||= Hash.new do |memo, (school, academic_year)|
memo[[school, academic_year]] = survey_item_responses.where(school:, memo[[school, academic_year]] =
academic_year:).exclude_boston.average(:likert_score).to_f survey_item_responses.exclude_boston.where(school:, academic_year:).average(:likert_score).to_f
end end
@score[[school, academic_year]] @score[[school, academic_year]]
end end

@ -0,0 +1,63 @@
class AnalyzeBarPresenter
include AnalyzeHelper
attr_reader :score, :x_position, :academic_year, :measure_id, :measure, :color
def initialize(measure:, academic_year:, score:, x_position:, color:)
@score = score
@x_position = x_position
@academic_year = academic_year
@measure = measure
@measure_id = measure.measure_id
@color = color
end
def y_offset
case zone.type
when :ideal, :approval
(analyze_zone_height * 2) - bar_height_percentage
else
(analyze_zone_height * 2)
end
end
def bar_color
"fill-#{zone.type}"
end
def bar_height_percentage
case zone.type
when :ideal
(percentage * zone_height_percentage + zone_height_percentage) * 100
when :approval
(percentage * zone_height_percentage) * 100
when :growth
((1 - percentage) * zone_height_percentage) * 100
when :watch
((1 - percentage) * zone_height_percentage + zone_height_percentage) * 100
when :warning
((1 - percentage) * zone_height_percentage + zone_height_percentage + zone_height_percentage) * 100
else
0.0
end
end
def percentage
(score.average - zone.low_benchmark) / (zone.high_benchmark - zone.low_benchmark)
end
def zone
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
)
zones.zone_for_score(score.average)
end
def average
return 0 if score.average.nil?
score.average.round(2)
end
end

@ -1,78 +1,102 @@
class GroupedBarColumnPresenter class GroupedBarColumnPresenter
include AnalyzeHelper include AnalyzeHelper
attr_reader :score, :measure_name, :measure_id, :category, :position, :measure, :school, :academic_year include ColorHelper
def initialize(measure:, school:, academic_year:, position:) attr_reader :measure_name, :measure_id, :category, :position, :measure, :school, :academic_years
def initialize(measure:, school:, academic_years:, position:)
@measure = measure @measure = measure
@measure_name = @measure.name @measure_name = @measure.name
@measure_id = @measure.measure_id @measure_id = @measure.measure_id
@category = @measure.subcategory.category @category = @measure.subcategory.category
@school = school @school = school
@academic_year = academic_year @academic_years = academic_years
@position = position @position = position
end end
def score def score(year_index)
measure.score(school:, academic_year:) measure.score(school:, academic_year: academic_years[year_index])
end end
def y_offset def bars
case zone.type @bars ||= yearly_scores.map.each_with_index do |item, index|
when :ideal, :approval year = item[0]
(analyze_zone_height * 2) - bar_height_percentage score = item[1]
else AnalyzeBarPresenter.new(measure:, academic_year: year, score:, x_position: bar_x(index),
(analyze_zone_height * 2) color: bar_color(year))
end end
end end
def bar_color def label
"fill-#{zone.type}" 'All Survey Data'
end end
def bar_height_percentage def basis
case zone.type ''
when :ideal end
(percentage * zone_height_percentage + zone_height_percentage) * 100
when :approval def show_irrelevancy_message?
(percentage * zone_height_percentage) * 100 !measure.includes_teacher_survey_items? && !measure.includes_student_survey_items?
when :growth end
((1 - percentage) * zone_height_percentage) * 100
when :watch def show_insufficient_data_message?
((1 - percentage) * zone_height_percentage + zone_height_percentage) * 100 scores = academic_years.map do |year|
when :warning measure.score(school:, academic_year: year)
((1 - percentage) * zone_height_percentage + zone_height_percentage + zone_height_percentage) * 100
else
0.0
end end
scores.all? { |score| !score.meets_teacher_threshold? && !score.meets_student_threshold? }
end end
def percentage def column_midpoint
(score.average - zone.low_benchmark) / (zone.high_benchmark - zone.low_benchmark) zone_label_width + (grouped_chart_column_width * (position + 1)) - (grouped_chart_column_width / 2)
end end
def zone def bar_width
zones = Zones.new( 3.5
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
)
zones.zone_for_score(score.average)
end end
def label def message_x
'All Survey Data' column_midpoint - message_width / 2
end end
def basis def message_y
'' 17
end end
def show_irrelevancy_message? def message_width
!@measure.includes_teacher_survey_items? && !@measure.includes_student_survey_items? 20
end end
def show_insufficient_data_message? def message_height
!score.meets_teacher_threshold? && !score.meets_student_threshold? 34
end
def column_end_x
zone_label_width + (grouped_chart_column_width * (position + 1))
end
def column_start_x
zone_label_width + (grouped_chart_column_width * position)
end
private
def yearly_scores
yearly_scores = academic_years.each_with_index.map do |year, index|
[year, score(index)]
end
yearly_scores.reject do |yearly_score|
score = yearly_score[1]
score.average.nil? || score.average.zero? || score.average.nan?
end
end
def bar_color(year)
@available_academic_years ||= AcademicYear.order(:range).all
colors[@available_academic_years.find_index(year)]
end
def bar_x(index)
column_start_x + (index * bar_width * 1.2) + ((column_end_x - column_start_x) - (yearly_scores.size * bar_width * 1.2)) / 2
end end
end end

@ -8,14 +8,18 @@ class StudentGroupedBarColumnPresenter < GroupedBarColumnPresenter
end end
def show_irrelevancy_message? def show_irrelevancy_message?
!@measure.includes_student_survey_items? !measure.includes_student_survey_items?
end end
def show_insufficient_data_message? def show_insufficient_data_message?
!score.meets_student_threshold? scores = academic_years.map do |year|
measure.score(school:, academic_year: year)
end
scores.all? { |score| !score.meets_student_threshold? }
end end
def score def score(year_index)
measure.student_score(school:, academic_year:) measure.student_score(school:, academic_year: academic_years[year_index])
end end
end end

@ -8,14 +8,18 @@ class TeacherGroupedBarColumnPresenter < GroupedBarColumnPresenter
end end
def show_irrelevancy_message? def show_irrelevancy_message?
!@measure.includes_teacher_survey_items? !measure.includes_teacher_survey_items?
end end
def show_insufficient_data_message? def show_insufficient_data_message?
!score.meets_teacher_threshold? scores = academic_years.map do |year|
measure.score(school:, academic_year: year)
end
scores.all? { |score| !score.meets_teacher_threshold? }
end end
def score def score(year_index)
measure.teacher_score(school:, academic_year:) measure.teacher_score(school:, academic_year: academic_years[year_index])
end end
end end

@ -3,8 +3,8 @@
<rect x="0" y="0" width="100%" height="<%= analyze_zone_height * 2 %>%" fill="#edecf0"/> <rect x="0" y="0" width="100%" height="<%= analyze_zone_height * 2 %>%" fill="#edecf0"/>
<rect x="0" y="<%= analyze_zone_height * 2 %>%" width="100%" height="<%= analyze_zone_height * 3 %>%" fill="#fffaee"/> <rect x="0" y="<%= analyze_zone_height * 2 %>%" width="100%" height="<%= analyze_zone_height * 3 %>%" fill="#fffaee"/>
<rect x="0" y="0" width="100%" height="<%= analyze_graph_height %>%" fill="none" stroke="grey"/> <rect x="0" y="0" width="100%" height="<%= analyze_graph_height %>%" fill="none" stroke="grey"/>
<line x1="<%= grouped_chart_divider_x(1) %>%" y1="0" x2="<%= grouped_chart_divider_x(1) %>%" y2="85%" stroke="grey" stroke-width="1" stroke-dasharray="5,2"/> <line x1="<%= column_end_x(1) %>%" y1="0" x2="<%= column_end_x(1) %>%" y2="85%" stroke="grey" stroke-width="1" stroke-dasharray="5,2"/>
<line x1="<%= grouped_chart_divider_x(2) %>%" y1="0" x2="<%= grouped_chart_divider_x(2) %>%" y2="85%" stroke="grey" stroke-width="1" stroke-dasharray="5,2"/> <line x1="<%= column_end_x(2) %>%" y1="0" x2="<%= column_end_x(2) %>%" y2="85%" stroke="grey" stroke-width="1" stroke-dasharray="5,2"/>
<rect x="0" y="<%= benchmark_y %>%" width="100%" height="<%= benchmark_height %>%" fill="black"/> <rect x="0" y="<%= benchmark_y %>%" width="100%" height="<%= benchmark_height %>%" fill="black"/>
<g id="zone-dividers" stroke-width="1" > <g id="zone-dividers" stroke-width="1" >
<line x1="0" y1="17%" x2="100%" y2="17%" stroke="white" /> <line x1="0" y1="17%" x2="100%" y2="17%" stroke="white" />
@ -31,11 +31,10 @@
</g> </g>
</g> </g>
<% presenter = StudentGroupedBarColumnPresenter.new(measure: measure, school: @school, academic_year: @academic_year, position: 1) %> <% presenters = [StudentGroupedBarColumnPresenter, TeacherGroupedBarColumnPresenter, GroupedBarColumnPresenter] %>
<%= render partial: "grouped_bar_column", locals: {presenter: presenter} %> <% presenters.each_with_index do |presenter, index| %>
<% presenter = TeacherGroupedBarColumnPresenter.new(measure: measure, school: @school, academic_year: @academic_year, position: 2) %> <% p = presenter.new(measure: measure, school: @school, academic_years: @selected_academic_years, position: index ) %>
<%= render partial: "grouped_bar_column", locals: {presenter: presenter} %> <%= render partial: "grouped_bar_column", locals: {presenter: p} %>
<% presenter = GroupedBarColumnPresenter.new(measure: measure, school: @school, academic_year: @academic_year, position: 3) %> <% end %>
<%= render partial: "grouped_bar_column", locals: {presenter: presenter} %>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

@ -1,30 +1,33 @@
<g id="grouped-bar-column"> <g class="grouped-bar-column" data-for-measure-id="<%= presenter.measure.measure_id %>">
<rect x="<%= bar_label_x(presenter.position) - 2.5 %>%" y="<%= presenter.y_offset %>%" width="5%" height="<%= presenter.bar_height_percentage %>%" fill="#3E3A38" data-for-measure-id="<%= presenter.measure_id %>"/> <% presenter.bars.each_with_index do |bar, index| %>
<rect data-for-academic-year="<%= bar.academic_year.range %>" x="<%= bar.x_position %>%" y="<%= bar.y_offset %>%" width="<%= presenter.bar_width %>%" height="<%= bar.bar_height_percentage %>%" fill="<%= bar.color %>" />
<% if ENV["SCORES"].present? && ENV["SCORES"].upcase == "SHOW" %> <% if ENV["SCORES"].present? && ENV["SCORES"].upcase == "SHOW" %>
<text x="<%= bar_label_x(presenter.position) %>%" y="<%= 5 %>%" text-anchor="middle" dominant-baseline="middle" > <text x="<%= bar.x_position + 1 %>%" y="5%" text-anchor="middle" dominant-baseline="middle" >
<%= presenter.score.average %> <%= bar.average %>
</text> </text>
<% end %>
<% end %> <% end %>
<text class="graph-footer" x="<%= bar_label_x(presenter.position) %>%" y="<%= bar_label_height %>%" text-anchor="middle" dominant-baseline="middle" data-grouped-bar-label="<%= presenter.label %>">
<text class="graph-footer" x="<%= presenter.column_midpoint %>%" y="<%= bar_label_height %>%" text-anchor="middle" dominant-baseline="middle" data-grouped-bar-label="<%= presenter.label %>">
<%= presenter.label %> <%= presenter.label %>
</text> </text>
<% if presenter.show_irrelevancy_message? %> <% if presenter.show_irrelevancy_message? %>
<rect x="<%= bar_label_x(presenter.position) - 10 %>%" y="<%= 17 %>%" rx="15" ry="15" width="20%" height="<%= 34 %>%" fill="white" stroke="gray"/> <rect x="<%= presenter.message_x %>%" y="<%= presenter.message_y %>%" rx="15" ry="15" width="<%= presenter.message_width %>%" height="<%= presenter.message_height %>%" fill="white" stroke="gray"/>
<text x="<%= bar_label_x(presenter.position) %>%" y="<%= 20 %>%" text-anchor="middle"> <text x="<%= presenter.column_midpoint %>%" y="<%= 20 %>%" text-anchor="middle">
<tspan x="<%= bar_label_x(presenter.position) %>%" y="29%">measure not</tspan> <tspan x="<%= presenter.column_midpoint %>%" y="29%">measure not</tspan>
<tspan x="<%= bar_label_x(presenter.position) %>%" y="34%">based on</tspan> <tspan x="<%= presenter.column_midpoint %>%" y="34%">based on</tspan>
<tspan x="<%= bar_label_x(presenter.position) %>%" y="39%"><%= presenter.basis %> surveys </tspan> <tspan x="<%= presenter.column_midpoint %>%" y="39%"><%= presenter.basis %> surveys </tspan>
</text> </text>
<% elsif presenter.show_insufficient_data_message? %> <% elsif presenter.show_insufficient_data_message? %>
<rect x="<%= bar_label_x(presenter.position) - 10 %>%" y="<%= 17 %>%" rx="15" ry="15" width="20%" height="<%= 34 %>%" fill="white" stroke="gray" /> <rect x="<%= presenter.message_x %>%" y="<%= presenter.message_y %>%" rx="15" ry="15" width="<%= presenter.message_width %>%" height="<%= presenter.message_height %>%" fill="white" stroke="gray" />
<text x="<%= bar_label_x(presenter.position) %>%" y="<%= 20 %>%" text-anchor="middle" > <text x="<%= presenter.column_midpoint %>%" y="<%= 20 %>%" text-anchor="middle" >
<tspan x="<%= bar_label_x(presenter.position) %>%" y="29%">survey response</tspan> <tspan x="<%= presenter.column_midpoint %>%" y="29%">survey response</tspan>
<tspan x="<%= bar_label_x(presenter.position) %>%" y="34%">rate below 25%</tspan> <tspan x="<%= presenter.column_midpoint %>%" y="34%">rate below 25%</tspan>
</text> </text>
<% end %> <% end %>
</g> </g>

@ -25,11 +25,11 @@
</select> </select>
<h3 class="sub-header-4 mt-5">School Years</h3> <h3 class="sub-header-4 mt-5">School Years</h3>
<% @available_academic_years.each do | year | %> <% @available_academic_years.each_with_index do | year, index | %>
<div class="d-flex justify-content-start align-items-center mt-1" data-controller="analyze"> <div class="d-flex justify-content-start align-items-center mt-1" data-controller="analyze">
<input type="checkbox" id="<%= year.range %>" name="year-checkbox" value="<%= analyze_subcategory_link(district: @district, school: @school, academic_year: @academic_year, category: @category, subcategory: @subcategory) %>" <%= @selected_academic_years.include?(year) ? "checked" : "" %> data-action="click->analyze#refresh"> <input type="checkbox" id="<%= year.range %>" name="year-checkbox" value="<%= analyze_subcategory_link(district: @district, school: @school, academic_year: @academic_year, category: @category, subcategory: @subcategory) %>" <%= @selected_academic_years.include?(year) ? "checked" : "" %> data-action="click->analyze#refresh">
<label class="px-3" for="<%= year.range %>" ><%= year.range %></label><br> <label class="px-3" for="<%= year.range %>" ><%= year.range %></label><br>
<div class="bg-color-blue" style="width:20px;height:20px;"></div> <div class="bg-color-blue" style="width:20px;height:20px;background-color:<%= colors[index] %>;"></div>
</div> </div>
<% end %> <% end %>
</div> </div>

@ -1,8 +1,12 @@
require 'rails_helper' require 'rails_helper'
include AnalyzeHelper
describe GroupedBarColumnPresenter do describe GroupedBarColumnPresenter do
let(:school) { create(:school) } let(:school) { create(:school) }
let(:academic_year) { create(:academic_year) } let(:academic_year) { create(:academic_year, range: '1900-01') }
let(:another_academic_year) { create(:academic_year, range: '2000-01') }
let(:academic_years) { [academic_year, another_academic_year] }
let(:year_index) { academic_years.find_index(academic_year) }
let(:watch_low_benchmark) { 2 } let(:watch_low_benchmark) { 2 }
let(:growth_low_benchmark) { 3 } let(:growth_low_benchmark) { 3 }
let(:approval_low_benchmark) { 4 } let(:approval_low_benchmark) { 4 }
@ -58,18 +62,18 @@ describe GroupedBarColumnPresenter do
end end
let(:student_presenter) do let(:student_presenter) do
StudentGroupedBarColumnPresenter.new measure: measure_with_student_survey_items, school:, academic_year:, StudentGroupedBarColumnPresenter.new measure: measure_with_student_survey_items, school:, academic_years:,
position: 1 position: 0
end end
let(:teacher_presenter) do let(:teacher_presenter) do
TeacherGroupedBarColumnPresenter.new measure: measure_with_teacher_survey_items, school:, academic_year:, TeacherGroupedBarColumnPresenter.new measure: measure_with_teacher_survey_items, school:, academic_years:,
position: 1 position: 0
end end
let(:all_data_presenter) do let(:all_data_presenter) do
GroupedBarColumnPresenter.new measure: measure_composed_of_student_and_teacher_items, school:, academic_year:, GroupedBarColumnPresenter.new measure: measure_composed_of_student_and_teacher_items, school:, academic_years:,
position: 1 position: 0
end end
before do before do
@ -83,132 +87,198 @@ describe GroupedBarColumnPresenter do
end end
end end
context 'when a measure is based on student survey items' do shared_examples_for 'column_midpoint' do
context 'when the score is in the Ideal zone' do it 'return an x position centered in the width of the column' do
expect(student_presenter.column_midpoint).to eq 29
end
end
shared_examples_for 'bar_color' do
it 'returns the correct color' do
expect(student_presenter.bars[year_index].color).to eq colors[year_index]
end
end
shared_examples_for 'y_offset' do
it 'bar will be based on the approval low benchmark boundary' do
expect(student_presenter.bars[year_index].y_offset).to be_within(0.01).of(34)
end
end
context 'for a grouped column presenter with both student and teacher responses' do
context 'with a single year'
before do
create(:survey_item_response, survey_item: student_survey_item_for_composite_measure, school:,
academic_year:, likert_score: 4)
create(:survey_item_response, survey_item: student_survey_item_for_composite_measure, school:,
academic_year:, likert_score: 5)
end
it 'returns a score that is an average of the likert scores ' do
expect(all_data_presenter.score(0).average).to eq 4.5
expect(all_data_presenter.score(1).average).to eq nil
expect(all_data_presenter.academic_years[0].range).to be academic_year.range
expect(all_data_presenter.academic_years[1].range).to be another_academic_year.range
end
context 'when more than one year exists' do
before do before do
create(:survey_item_response, survey_item: student_survey_item, school:, create(:survey_item_response, survey_item: student_survey_item_for_composite_measure, school:,
academic_year:, likert_score: 5) academic_year: another_academic_year, likert_score: 5)
create(:survey_item_response, survey_item: student_survey_item, school:, create(:survey_item_response, survey_item: student_survey_item_for_composite_measure, school:,
academic_year:, likert_score: 4) academic_year: another_academic_year, likert_score: 3)
end
it 'returns independent scores for each year of data' do
expect(all_data_presenter.score(0).average).to eq 4.5
expect(all_data_presenter.score(1).average).to eq 4
end end
end
end
context 'when a measure is based on student survey items' do
before do
year_index = academic_years.find_index(academic_year)
end
context 'when there is insufficient data to show a score' do
it_behaves_like 'measure_name' it_behaves_like 'measure_name'
it_behaves_like 'column_midpoint'
it 'returns the correct color' do it 'returns an emtpy set of bars' do
expect(student_presenter.bar_color).to eq 'fill-ideal' expect(student_presenter.bars).to eq []
end end
it 'returns a bar width equal to the approval zone width plus the proportionate ideal zone width' do it 'returns an emty score' do
expect(student_presenter.bar_height_percentage).to be_within(0.01).of(17) expect(student_presenter.score(year_index).average).to eq nil
end end
it 'returns a y_offset equal to the ' do it 'shows the irrelevancy message ' do
expect(student_presenter.y_offset).to be_within(0.01).of(17) expect(student_presenter.show_irrelevancy_message?).to eq true
end
it 'shows the insufficient data message' do
expect(student_presenter.show_insufficient_data_message?).to eq true
end end
end end
context 'when the score is in the Approval zone' do context 'when the score is in the Ideal zone' do
before do before do
create(:survey_item_response, survey_item: student_survey_item, school:,
academic_year:, likert_score: 5)
create(:survey_item_response, survey_item: student_survey_item, school:, create(:survey_item_response, survey_item: student_survey_item, school:,
academic_year:, likert_score: 4) academic_year:, likert_score: 4)
end end
it_behaves_like 'measure_name' it_behaves_like 'measure_name'
it_behaves_like 'column_midpoint'
it_behaves_like 'bar_color'
it 'returns the correct color' do it 'returns a bar width equal to the approval zone width plus the proportionate ideal zone width' do
expect(student_presenter.bar_color).to eq 'fill-approval' expect(student_presenter.bars[year_index].bar_height_percentage).to be_within(0.01).of(17)
end end
it 'returns a bar width equal to the proportionate approval zone width' do it 'returns a y_offset equal to the ' do
expect(student_presenter.bar_height_percentage).to be_within(0.01).of(0) expect(student_presenter.bars[0].y_offset).to be_within(0.01).of(17)
end end
it 'returns an x-offset of 60%' do it 'returns a text representation of the type of survey the bars are based on' do
expect(student_presenter.y_offset).to be_within(0.01).of(34) expect(student_presenter.basis).to eq 'student'
end end
end
context 'when the score is in the Growth zone' do it 'returns only bars that have a numeric score' do
before do expect(student_presenter.bars.count).to be 1
create(:survey_item_response, survey_item: student_survey_item, school:,
academic_year:, likert_score: 3)
end end
it_behaves_like 'measure_name' it 'returns an explanatory bar label' do
expect(student_presenter.label).to eq 'All Students'
end
it 'returns the correct color' do it 'does not show a message that the data source is irrelevant for this measure' do
expect(student_presenter.bar_color).to eq 'fill-growth' expect(student_presenter.show_irrelevancy_message?).to be false
end end
it 'returns a bar width equal to the proportionate growth zone width' do it 'does not show a message about insufficient responses' do
expect(student_presenter.bar_height_percentage).to be_within(0.01).of(17) expect(student_presenter.show_insufficient_data_message?).to be false
end end
context 'in order to achieve the visual effect' do context 'when there is more than one years worth of data to show' do
it 'returns an x-offset equal to 60% minus the bar width' do before do
expect(student_presenter.y_offset).to eq 34 create(:survey_item_response, survey_item: student_survey_item, school:,
academic_year: another_academic_year, likert_score: 3)
create(:survey_item_response, survey_item: student_survey_item, school:,
academic_year: another_academic_year, likert_score: 4)
end
it 'returns only bars that have a numeric score' do
expect(student_presenter.bars.count).to be 2
end end
end end
end end
context 'when the score is in the Watch zone' do context 'when the score is in the Approval zone' do
before do before do
create(:survey_item_response, survey_item: student_survey_item, school:, create(:survey_item_response, survey_item: student_survey_item, school:,
academic_year:, likert_score: 2) academic_year:, likert_score: 4)
end end
it_behaves_like 'measure_name' it_behaves_like 'measure_name'
it_behaves_like 'column_midpoint'
it_behaves_like 'bar_color'
it_behaves_like 'y_offset'
it 'returns the correct color' do context 'and the score is right at the approval low benchmark' do
expect(student_presenter.bar_color).to eq 'fill-watch' it 'bar will have a height of 0' do
end expect(student_presenter.bars[year_index].bar_height_percentage).to be_within(0.01).of(0)
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 'bar will be based on the approval low benchmark boundary' do
it 'returns an x-offset equal to 60% minus the bar width' do expect(student_presenter.bars[year_index].y_offset).to be_within(0.01).of(34)
expect(student_presenter.y_offset).to eq 34
end end
end end
end end
context 'when the score is in the Growth zone' do
context 'when the score is in the Warning zone' do
before do before do
create(:survey_item_response, survey_item: student_survey_item, school:, create(:survey_item_response, survey_item: student_survey_item, school:,
academic_year:, likert_score: 1) academic_year:, likert_score: 3)
end end
it_behaves_like 'measure_name' it_behaves_like 'measure_name'
it_behaves_like 'column_midpoint'
it_behaves_like 'bar_color'
it_behaves_like 'y_offset'
it 'returns the correct color' do it 'returns a bar width equal to the proportionate growth zone width' do
expect(student_presenter.bar_color).to eq 'fill-warning' expect(student_presenter.bars[year_index].bar_height_percentage).to be_within(0.01).of(17)
end end
end
it 'returns a bar width equal to the proportionate warning zone width plus the watch & growth zone widths' do context 'when the score is in the Watch zone' do
expect(student_presenter.bar_height_percentage).to eq 51 before do
create(:survey_item_response, survey_item: student_survey_item, school:,
academic_year:, likert_score: 2)
end end
context 'in order to achieve the visual effect' do it_behaves_like 'measure_name'
it 'returns an y-offset equal to 60% minus the bar width' do it_behaves_like 'column_midpoint'
expect(student_presenter.y_offset).to eq 34 it_behaves_like 'bar_color'
end it_behaves_like 'y_offset'
end
end
context 'when there are insufficient responses to calculate a score' do it 'returns a bar width equal to the proportionate watch zone width plus the growth zone width' do
it 'indicates it should show the insufficient data message' do expect(student_presenter.bars[year_index].bar_height_percentage).to eq 34
expect(student_presenter.show_insufficient_data_message?).to eq true
end end
end end
context 'when there are enough responses to calculate a score' do context 'when the score is in the Warning zone' do
before do before do
create(:survey_item_response, survey_item: student_survey_item, school:, create(:survey_item_response, survey_item: student_survey_item, school:,
academic_year:) academic_year:, likert_score: 1)
end end
it 'indicates it should show the insufficient data message' do
expect(student_presenter.show_insufficient_data_message?).to eq false it_behaves_like 'measure_name'
it_behaves_like 'column_midpoint'
it_behaves_like 'bar_color'
it_behaves_like 'y_offset'
it 'returns a bar width equal to the proportionate warning zone width plus the watch & growth zone widths' do
expect(student_presenter.bars[year_index].bar_height_percentage).to eq 51
end end
end end
end end

@ -78,10 +78,15 @@ describe 'analyze/index' do
# end # end
it 'displays a set of grouped bars for each presenter' do it 'displays a set of grouped bars for each presenter' do
displayed_variance_columns = subject.css('.grouped-bar-column')
expect(displayed_variance_columns.count).to eq 9
displayed_variance_rows = subject.css('[data-for-measure-id]') displayed_variance_rows = subject.css('[data-for-measure-id]')
expect(displayed_variance_rows.count).to eq 9
expect(displayed_variance_rows.first.attribute('data-for-measure-id').value).to eq '1A-I' expect(displayed_variance_rows.first.attribute('data-for-measure-id').value).to eq '1A-I'
# displayed_variance_rows = subject.css('data-for-academic-year')
# expect(displayed_variance_rows.count).to eq 9
displayed_variance_labels = subject.css('[data-grouped-bar-label]') displayed_variance_labels = subject.css('[data-grouped-bar-label]')
expect(displayed_variance_labels.count).to eq 9 expect(displayed_variance_labels.count).to eq 9
expect(displayed_variance_labels.first.inner_text).to include 'All Students' expect(displayed_variance_labels.first.inner_text).to include 'All Students'

Loading…
Cancel
Save