Modify behavior of insufficient data indicators for admin data items. Now we show indicators in line with the admin data item descriptions to indicate which items are missing data

pull/1/head
rebuilt 3 years ago
parent 7c0794f261
commit deaf13c976

@ -60,3 +60,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- Modify behavior of insufficient data indicators for admin data items. Now we show indicators in line with the admin data item descriptions to indicate which items are missing data

@ -0,0 +1,2 @@
class DataAvailability < Struct.new(:id, :description, :available?)
end

@ -63,7 +63,7 @@ class Measure < ActiveRecord::Base
@score ||= Hash.new do |memo, (school, academic_year)|
meets_student_threshold = sufficient_student_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 = any_admin_data_collected?(school:, academic_year:)
lacks_sufficient_survey_data = !meets_student_threshold && !meets_teacher_threshold
incalculable_score = lacks_sufficient_survey_data && !includes_admin_data_items?
@ -137,17 +137,18 @@ class Measure < ActiveRecord::Base
@ideal_low_benchmark ||= benchmark(:ideal_low_benchmark)
end
def all_admin_data_collected?(school:, academic_year:)
@all_admin_data_collected ||= Hash.new do |memo, (school, academic_year)|
total_possible_admin_data_items = scales.map { |scale| scale.admin_data_items.count }.sum
private
def any_admin_data_collected?(school:, academic_year:)
@any_admin_data_collected ||= Hash.new do |memo, (school, academic_year)|
total_collected_admin_data_items = scales.map do |scale|
scale.admin_data_items.map do |admin_data_item|
admin_data_item.admin_data_values.where(school:, academic_year:).count
end
end.flatten.sum
memo[[school, academic_year]] = total_possible_admin_data_items == total_collected_admin_data_items
memo[[school, academic_year]] = total_collected_admin_data_items > 0
end
@all_admin_data_collected[[school, academic_year]]
@any_admin_data_collected[[school, academic_year]]
end
def sufficient_survey_responses?(school:, academic_year:)
@ -158,12 +159,10 @@ class Measure < ActiveRecord::Base
@sufficient_survey_responses[[school, academic_year]]
end
private
def scorify(average:, school:, academic_year:)
meets_student_threshold = sufficient_student_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 = any_admin_data_collected?(school:, academic_year:)
Score.new(average, meets_teacher_threshold, meets_student_threshold, meets_admin_data_threshold)
end

@ -1,6 +1,6 @@
class AdminDataPresenter < DataItemPresenter
def initialize(measure_id:, admin_data_items:, has_sufficient_data:)
super(measure_id:, has_sufficient_data:)
def initialize(measure_id:, admin_data_items:, has_sufficient_data:, school:, academic_year:)
super(measure_id:, has_sufficient_data:, school:, academic_year:)
@admin_data_items = admin_data_items
end
@ -19,4 +19,11 @@ class AdminDataPresenter < DataItemPresenter
def reason_for_insufficiency
'limited availability'
end
def descriptions_and_availability
@admin_data_items.map do |admin_data_item|
DataAvailability.new(admin_data_item.admin_data_item_id, admin_data_item.description,
admin_data_item.admin_data_values.where(school:, academic_year:).count > 0)
end
end
end

@ -1,7 +1,11 @@
class DataItemPresenter
def initialize(measure_id:, has_sufficient_data:)
attr_reader :measure_id, :has_sufficient_data, :school, :academic_year
def initialize(measure_id:, has_sufficient_data:, school:, academic_year:)
@measure_id = measure_id
@has_sufficient_data = has_sufficient_data
@school = school
@academic_year = academic_year
end
def data_item_accordion_id

@ -1,4 +1,6 @@
class MeasurePresenter
attr_reader :measure, :academic_year, :school
def initialize(measure:, academic_year:, school:)
@measure = measure
@academic_year = academic_year
@ -29,15 +31,15 @@ class MeasurePresenter
[].tap do |array|
if @measure.student_survey_items.any?
array << StudentSurveyPresenter.new(measure_id: @measure.measure_id, survey_items: @measure.student_survey_items,
has_sufficient_data: score_for_measure.meets_student_threshold?)
has_sufficient_data: score_for_measure.meets_student_threshold?, school:, academic_year:)
end
if @measure.teacher_survey_items.any?
array << TeacherSurveyPresenter.new(measure_id: @measure.measure_id, survey_items: @measure.teacher_survey_items,
has_sufficient_data: score_for_measure.meets_teacher_threshold?)
has_sufficient_data: score_for_measure.meets_teacher_threshold?, school:, academic_year:)
end
if @measure.admin_data_items.any?
array << AdminDataPresenter.new(measure_id: @measure.measure_id,
admin_data_items: @measure.admin_data_items, has_sufficient_data: score_for_measure.meets_admin_data_threshold?)
admin_data_items: @measure.admin_data_items, has_sufficient_data: score_for_measure.meets_admin_data_threshold?, school:, academic_year:)
end
end
end

@ -1,6 +1,6 @@
class StudentSurveyPresenter < DataItemPresenter
def initialize(measure_id:, survey_items:, has_sufficient_data:)
super(measure_id:, has_sufficient_data:)
def initialize(measure_id:, survey_items:, has_sufficient_data:, school:, academic_year:)
super(measure_id:, has_sufficient_data:, school:, academic_year:)
@survey_items = survey_items
end
@ -19,4 +19,10 @@ class StudentSurveyPresenter < DataItemPresenter
def reason_for_insufficiency
'low response rate'
end
def descriptions_and_availability
@survey_items.map do |survey_item|
DataAvailability.new(survey_item.survey_item_id, survey_item.prompt, true)
end
end
end

@ -1,6 +1,6 @@
class TeacherSurveyPresenter < DataItemPresenter
def initialize(measure_id:, survey_items:, has_sufficient_data:)
super(measure_id:, has_sufficient_data:)
def initialize(measure_id:, survey_items:, has_sufficient_data:, school:, academic_year:)
super(measure_id:, has_sufficient_data:, school:, academic_year:)
@survey_items = survey_items
end
@ -21,4 +21,10 @@ class TeacherSurveyPresenter < DataItemPresenter
def reason_for_insufficiency
'low response rate'
end
def descriptions_and_availability
@survey_items.map do |survey_item|
DataAvailability.new(survey_item.survey_item_id, survey_item.prompt, true)
end
end
end

@ -9,7 +9,7 @@
>
<%= data_item_section.title %>
<% unless data_item_section.sufficient_data? %>
&nbsp;<i class="fa-solid fa-circle-exclamation"></i>
&nbsp;<i class="fa-solid fa-circle-exclamation" data-exclamation-point="<%= data_item_section.id %>"></i>
<% end %>
</button>
</h3>
@ -22,13 +22,17 @@
>
<div class="accordion-body measure-accordion-body font-cabin font-size-14 weight-400">
<% unless data_item_section.sufficient_data? %>
<div class="alert alert-secondary" role="alert">
<div class="alert alert-secondary" role="alert" data-insufficient-data-message="<%= data_item_section.id + '-' + data_item_section.reason_for_insufficiency %>">
Data not included due to <%= data_item_section.reason_for_insufficiency %>
</div>
<% end %>
<ul>
<% data_item_section.item_descriptions.each do |description| %>
<li><%= description %></li>
<% data_item_section.descriptions_and_availability.each do |data| %>
<li><%= data.description %>
<% unless data.available? %>
&nbsp;<i class="fa-solid fa-circle-exclamation" data-missing-data="<%= data.id %>"></i>
<% end %>
</li>
<% end %>
</ul>
</div>

@ -10,5 +10,4 @@ Winchester,Winchester High School,3440505,3-A-i,a-reso-i1,Average class size,17,
Attleboro,Attleboro High School,160505,3-A-ii,a-sust-i3,Student to instructional support staff ratio,15.28896673,0.4,43.4,,2018-19
Milford,Woodland Elementary School,1850090,3-A-ii,a-sust-i3,Student to instructional support staff ratio,22.85714286,2.106649112,43.4,,2018-19
Revere,Beachmont Elementary School,2480013,3-A-ii,a-sust-i3,Student to instructional support staff ratio,38,3.5,43.4,,2018-19
Winchester,Winchester High School,3440505,3-A-ii,a-sust-i3,Student to instructional support staff ratio,135.9,5,43.4,,2018-19
Attleboro,Attleboro High School,160505,3-A-i,a-sust-i3,Average class size,20.6,100,20,,2018-19
Winchester,Winchester High School,3440505,3-A-ii,a-sust-i3,Student to instructional support staff ratio,135.9,100,43.4,,2018-19

1 District School DESE ID Category Item ID NonLikert Title NL_Value LikertScore Benchmark Data Type Academic Year
10 Attleboro Attleboro High School 160505 3-A-ii a-sust-i3 Student to instructional support staff ratio 15.28896673 0.4 43.4 2018-19
11 Milford Woodland Elementary School 1850090 3-A-ii a-sust-i3 Student to instructional support staff ratio 22.85714286 2.106649112 43.4 2018-19
12 Revere Beachmont Elementary School 2480013 3-A-ii a-sust-i3 Student to instructional support staff ratio 38 3.5 43.4 2018-19
13 Winchester Winchester High School 3440505 3-A-ii a-sust-i3 Student to instructional support staff ratio 135.9 5 100 43.4 2018-19
Attleboro Attleboro High School 160505 3-A-i a-sust-i3 Average class size 20.6 100 20 2018-19

@ -0,0 +1,77 @@
require 'rails_helper'
describe AdminDataPresenter do
let(:school) { School.find_by_slug 'milford-high-school' }
let(:academic_year) { AcademicYear.find_by_range '2021-22' }
let(:measure_1A_i) { Measure.find_by_measure_id '1A-i' }
let(:measure_1A_iii) { Measure.find_by_measure_id '1A-iii' }
describe '#item_description' do
before :each do
Rails.application.load_seed
end
after :each do
DatabaseCleaner.clean
end
context 'When the presenter is based on measure 1A-1' do
it 'returns a list of survey prompts for teacher survey items' do
expect(AdminDataPresenter.new(measure_id: measure_1A_i.measure_id, admin_data_items: measure_1A_i.admin_data_items,
has_sufficient_data: true, school:, academic_year:).item_descriptions).to eq [
'Percentage teachers with 5+ years of experience', 'Percentage teachers National Board certified', 'Percentage teachers teaching in area of licensure'
]
end
context 'When the measure is missing all admin data values' do
it 'if it lacks sufficient data, it shows a warning ' do
expect(AdminDataPresenter.new(measure_id: measure_1A_i.measure_id, admin_data_items: measure_1A_i.admin_data_items,
has_sufficient_data: false, school:, academic_year:).sufficient_data?).to eq false
end
end
context 'When the measure has at least one admin data value' do
it 'if it lacks sufficient data, it doesnt show a warning ' do
expect(AdminDataPresenter.new(measure_id: measure_1A_i.measure_id, admin_data_items: measure_1A_i.admin_data_items,
has_sufficient_data: true, school:, academic_year:).sufficient_data?).to eq true
end
end
end
context 'When the presenter is based on measure 1A-iii' do
it 'returns a message hiding the actual prompts. Instead it presents a message telling the user they can ask for more information' do
expect(AdminDataPresenter.new(measure_id: measure_1A_iii.measure_id, admin_data_items: measure_1A_iii.admin_data_items,
has_sufficient_data: true, school:, academic_year:).item_descriptions).to eq [
'Percent teacher returning (excluding retirement)', 'Percent teachers with 10+ days absent'
]
end
end
end
describe '#descriptions_and_availibility' do
before :each do
Rails.application.load_seed
end
after :each do
DatabaseCleaner.clean
end
context 'when there are any matching values for admin data items' do
before do
admin_data_item = measure_1A_i.admin_data_items.first
create(:admin_data_value, admin_data_item:, school:, academic_year:)
end
it 'returns a list of admin data items and whether there is a matching value' do
expect(
AdminDataPresenter.new(
measure_id: measure_1A_i.measure_id, admin_data_items: measure_1A_i.admin_data_items, has_sufficient_data: true, school:, academic_year:
).descriptions_and_availability
).to eq [
DataAvailability.new('a-exp-i1', 'Percentage teachers with 5+ years of experience', true),
DataAvailability.new('a-exp-i2', 'Percentage teachers National Board certified', false),
DataAvailability.new('a-exp-i3', 'Percentage teachers teaching in area of licensure', false)
]
end
end
context 'when there are NO matching values for admin data items' do
end
end
end

@ -1,8 +1,11 @@
require 'rails_helper'
describe TeacherSurveyPresenter do
let(:school) { School.first }
let(:academic_year) { AcademicYear.first }
let(:measure_1A_i) { Measure.find_by_measure_id '1A-i' }
let(:measure_1B_i) { Measure.find_by_measure_id '1B-i' }
describe '#item_description' do
before :each do
Rails.application.load_seed
@ -15,7 +18,7 @@ describe TeacherSurveyPresenter do
context 'When the presenter is based on measure 1A-1' do
it 'returns a list of survey prompts for teacher survey items' do
expect(TeacherSurveyPresenter.new(measure_id: measure_1A_i.measure_id, survey_items: measure_1A_i.teacher_survey_items,
has_sufficient_data: true).item_descriptions).to eq [
has_sufficient_data: true, school:, academic_year:).item_descriptions).to eq [
'Given your preparation for teaching how comfortable are you teaching at the grade-level you have been assigned?',
'How prepared are you for teaching the topics that you are expected to teach in your assignment?',
'How confident are you in working with the student body at your school?'
@ -26,7 +29,7 @@ describe TeacherSurveyPresenter do
context 'When the presenter is based on measure 1B-i' do
it 'returns a message hiding the actual prompts. Instead it presents a message telling the user they can ask for more information' do
expect(TeacherSurveyPresenter.new(measure_id: measure_1B_i.measure_id, survey_items: measure_1B_i.teacher_survey_items,
has_sufficient_data: true).item_descriptions).to eq [
has_sufficient_data: true, school:, academic_year:).item_descriptions).to eq [
'Items available upon request to MCIEA.'
]
end

@ -5,6 +5,7 @@ describe AdminDataLoader do
let(:ay_2018_19) { AcademicYear.find_by_range '2018-19' }
let(:attleboro) { School.find_by_dese_id 160_505 }
let(:winchester) { School.find_by_dese_id 3_440_505 }
let(:beachmont) { School.find_by_dese_id 2_480_013 }
let(:chronic_absense_rate) { AdminDataItem.find_by_admin_data_item_id 'a-vale-i1' }
let(:student_to_instructor_ratio) { AdminDataItem.find_by_admin_data_item_id 'a-sust-i3' }
@ -28,7 +29,7 @@ describe AdminDataLoader do
# it 'assigns the school to the admin data value' do
expect(AdminDataValue.first.school).to eq attleboro
expect(AdminDataValue.last.school).to eq winchester
expect(AdminDataValue.last.school).to eq beachmont
# end
# it 'links the admin data value to the correct admin data item' do
@ -37,12 +38,14 @@ describe AdminDataLoader do
# end
# it 'loads all the admin data values in the target csv file' do
expect(AdminDataValue.count).to eq 11
expect(AdminDataValue.count).to eq 10
# end
# it 'captures the likert score ' do
expect(AdminDataValue.first.likert_score).to eq 3.03
expect(AdminDataValue.last.likert_score).to eq 5
expect(AdminDataValue.find_by(school: attleboro, academic_year: ay_2018_19,
admin_data_item: chronic_absense_rate).likert_score).to eq 3.03
expect(AdminDataValue.find_by(school: beachmont, academic_year: ay_2018_19,
admin_data_item: student_to_instructor_ratio).likert_score).to eq 3.5
# end
# it 'rounds up any likert_scores between 0 and 1 (non-inclusive) to 1' do
@ -52,7 +55,7 @@ describe AdminDataLoader do
# it 'rejects importing rows with a value of 0' do
expect(AdminDataValue.where(school: attleboro, academic_year: ay_2018_19,
admin_data_item: AdminDataItem.find_by_admin_data_item_id('a-reso-i1'))).not_to exist
expect(AdminDataValue.where(school: attleboro, academic_year: ay_2018_19,
expect(AdminDataValue.where(school: winchester, academic_year: ay_2018_19,
admin_data_item: AdminDataItem.find_by_admin_data_item_id('a-sust-i3'))).not_to exist
# end
end

@ -60,7 +60,6 @@ describe 'analyze/index' do
render
end
context 'when all the presenters have a nil score' do
# let(:grouped_bar_column_presenters) do
# measure = create(:measure, name: 'Display Me', measure_id: 'display-me')
@ -114,7 +113,4 @@ describe 'analyze/index' do
expect(year_checkbox).to have_attribute 'disabled'
end
end
context 'when presenters have a score' do
end
end

@ -2,41 +2,71 @@ require 'rails_helper'
include GaugeHelper
describe 'categories/show' do
subject { Nokogiri::HTML(rendered) }
let(:academic_year) { create(:academic_year, range: '1989-90') }
let(:school) { create(:school, name: 'Best School') }
let(:category) { create(:category, name: 'Some Category', description: 'Some description of the category') }
let(:category_presenter) { CategoryPresenter.new(category:) }
let(:subcategory1) do
create(:subcategory, category:, name: 'A subcategory', description: 'Some description of the subcategory')
end
let(:subcategory2) do
create(:subcategory, category:, name: 'Another subcategory',
description: 'Another description of the subcategory')
end
let(:measure1) { create(:measure, subcategory: subcategory1) }
let(:scale1) { create(:student_scale, measure: measure1) }
let(:survey_item1) do
create(:student_survey_item, scale: scale1, watch_low_benchmark: 1.5, growth_low_benchmark: 2.5,
approval_low_benchmark: 3.5, ideal_low_benchmark: 4.5)
end
let(:measure2) do
create(:measure, name: 'The second measure name', description: 'The second measure description', measure_id: '1A-i',
subcategory: subcategory2)
end
let(:scale2) { create(:student_scale, measure: measure2) }
let(:scale3) { create(:teacher_scale, measure: measure2) }
let(:student_survey_item) do
create(:student_survey_item, scale: scale2, prompt: 'Some prompt for student data',
watch_low_benchmark: 1.5, growth_low_benchmark: 2.5, approval_low_benchmark: 3.5, ideal_low_benchmark: 4.5)
end
let(:teacher_survey_item) { create(:teacher_survey_item, scale: scale3, prompt: 'Some prompt for teacher data') }
let(:admin_data_scale) { create(:scale, measure: measure2) }
let(:admin_data_item) do
create(:admin_data_item, scale: admin_data_scale, description: 'Some admin data item description',
watch_low_benchmark: 1.5, growth_low_benchmark: 2.5, approval_low_benchmark: 3.5, ideal_low_benchmark: 4.5)
end
before :each do
academic_year = create(:academic_year, range: '1989-90')
school = create(:school, name: 'Best School')
academic_year
school
category = create(:category, name: 'Some Category', description: 'Some description of the category')
category_presenter = CategoryPresenter.new(category:)
category
category_presenter
subcategory1 = create(:subcategory, category:, name: 'A subcategory',
description: 'Some description of the subcategory')
subcategory2 = create(:subcategory, category:, name: 'Another subcategory',
description: 'Another description of the subcategory')
subcategory1
subcategory2
measure1 = create(:measure, subcategory: subcategory1)
scale1 = create(:student_scale, measure: measure1)
survey_item1 = create(:student_survey_item, scale: scale1, watch_low_benchmark: 1.5, growth_low_benchmark: 2.5,
approval_low_benchmark: 3.5, ideal_low_benchmark: 4.5)
measure1
scale1
survey_item1
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD, survey_item: survey_item1,
academic_year:, school:, likert_score: 3)
measure2 = create(:measure, name: 'The second measure name', description: 'The second measure description',
subcategory: subcategory2)
scale2 = create(:student_scale, measure: measure2)
scale3 = create(:teacher_scale, measure: measure2)
student_survey_item = create(:student_survey_item, scale: scale2, prompt: 'Some prompt for student data',
watch_low_benchmark: 1.5, growth_low_benchmark: 2.5, approval_low_benchmark: 3.5, ideal_low_benchmark: 4.5)
measure2
scale2
scale3
student_survey_item
create_list(:survey_item_response, SurveyItemResponse::STUDENT_RESPONSE_THRESHOLD,
survey_item: student_survey_item, academic_year:, school:, likert_score: 5)
teacher_survey_item = create(:teacher_survey_item, scale: scale3, prompt: 'Some prompt for teacher data')
teacher_survey_item
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD,
survey_item: teacher_survey_item, academic_year:, school:, likert_score: 3)
admin_data_scale = create(:scale, measure: measure2)
create(:admin_data_item, scale: admin_data_scale, description: 'Some admin data item description',
watch_low_benchmark: 1.5, growth_low_benchmark: 2.5, approval_low_benchmark: 3.5, ideal_low_benchmark: 4.5)
admin_data_scale
admin_data_item
assign :category, category_presenter
assign :categories, [category_presenter]
@ -47,15 +77,22 @@ describe 'categories/show' do
create(:respondent, school:, academic_year:)
create(:survey, school:, academic_year:)
render
end
it 'renders the category name and description' do
expect(rendered).to match(/Some Category/)
expect(rendered).to match(/Some description of the category/)
context 'for each category' do
before do
render
end
it 'renders the category name and description' do
expect(rendered).to match(/Some Category/)
expect(rendered).to match(/Some description of the category/)
end
end
context 'for each subcategory' do
before do
render
end
it 'renders the subcategory name' do
expect(rendered).to match(/A subcategory/)
expect(rendered).to match(/Another subcategory/)
@ -73,6 +110,9 @@ describe 'categories/show' do
end
context 'for each measure' do
before do
render
end
it 'renders the measure name' do
expect(rendered).to match(/The second measure name/)
end
@ -92,4 +132,43 @@ describe 'categories/show' do
expect(rendered).to match(/Some admin data item description/)
end
end
context 'when the measure does NOT have admin data values' do
before do
render
end
it 'renders the insufficient data exclamation point icon' do
exclamation_points = subject.css('[data-exclamation-point]')
expect(exclamation_points.first.attribute('data-exclamation-point').value).to eq 'admin-data-items-1A-i'
end
it 'renders the insufficient data message' do
exclamation_points = subject.css('[data-insufficient-data-message]')
expect(exclamation_points.first.attribute('data-insufficient-data-message').value).to eq 'admin-data-items-1A-i-limited availability'
end
it 'renders the insufficient data exclamation point icons next to the description of the missing admin data item' do
exclamation_points = subject.css('[data-missing-data]')
expect(exclamation_points.first.attribute('data-missing-data').value).to eq "#{admin_data_item.admin_data_item_id}"
end
end
context 'when the measure DOES have admin data values' do
before :each do
create(:admin_data_value, admin_data_item:, school:, academic_year:, likert_score: 2)
render
end
it 'does not render the insufficient data exclamation point icon' do
exclamation_points = subject.css('[data-exclamation-point]')
expect(exclamation_points.count).to eq 0
end
it 'does not render the insufficient data message ' do
exclamation_points = subject.css('[data-insufficient-data-message]')
expect(exclamation_points.count).to eq 0
end
it 'Does NOT render the insufficient data exclamation point icons next to the description of the missing admin data item' do
exclamation_points = subject.css('[data-missing-data]')
expect(exclamation_points.count).to eq 0
end
end
end

Loading…
Cancel
Save