to survey table. We no longer check or keep track of the survey type. Instead we look in the database to see if a survey item has at least 10 responses. If it does, that survey item was presented to the respondent and we count it, and all responses when calculating the response rate. Remove response rate timestamp from caching logic because we no longer add the response rate to the database. All response rates are calculated on the fly Update three_b_two scraper to use teacher only numbers swap over to using https://profiles.doe.mass.edu/statereport/gradesubjectstaffing.aspx as the source of staffing informationpull/1/head
parent
56b55bf5b6
commit
128748addd
@ -1,66 +1,54 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class StudentResponseRateCalculator < ResponseRateCalculator
|
class StudentResponseRateCalculator < ResponseRateCalculator
|
||||||
private
|
|
||||||
|
|
||||||
def raw_response_rate
|
def raw_response_rate
|
||||||
# def rate
|
rates_by_grade.length.positive? ? rates_by_grade.average : 0
|
||||||
# check to see if enrollment data is available
|
|
||||||
# if not, run the dese loader to get the data
|
|
||||||
# then upload the enrollment data into the db
|
|
||||||
#
|
|
||||||
# if you still don't see enrollment for the school, raise an error and return 100 from this method
|
|
||||||
#
|
|
||||||
# Get the enrollment information from the db
|
|
||||||
# Get the list of all grades
|
|
||||||
# For each grade, get the survey items with data
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# All methods below will need to specify a grade
|
|
||||||
|
|
||||||
(average_responses_per_survey_item / total_possible_responses.to_f * 100).round
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def survey_item_count
|
def rates_by_grade
|
||||||
@survey_item_count ||= begin
|
@rates_by_grade ||= counts_by_grade.map do |grade, num_of_students_in_grade|
|
||||||
survey_items = SurveyItem.includes(%i[scale
|
sufficient_survey_items = survey_items_with_sufficient_responses(grade:).keys
|
||||||
measure]).student_survey_items.where("scale.measure": @subcategory.measures)
|
actual_response_count_for_grade = SurveyItemResponse.where(school:, academic_year:, grade:,
|
||||||
survey_items = survey_items.where(on_short_form: true) if survey.form == 'short'
|
survey_item: sufficient_survey_items).count.to_f
|
||||||
survey_items = survey_items.reject do |survey_item|
|
count_of_survey_items_with_sufficient_responses = survey_item_count(grade:)
|
||||||
survey_item.survey_item_responses.where(school:, academic_year:).none?
|
if count_of_survey_items_with_sufficient_responses.nil? || count_of_survey_items_with_sufficient_responses.zero? || num_of_students_in_grade.nil? || num_of_students_in_grade.zero?
|
||||||
|
next nil
|
||||||
end
|
end
|
||||||
survey_items.count
|
|
||||||
end
|
actual_response_count_for_grade / count_of_survey_items_with_sufficient_responses / num_of_students_in_grade * 100
|
||||||
|
end.compact
|
||||||
end
|
end
|
||||||
|
|
||||||
def response_count
|
def counts_by_grade
|
||||||
@response_count ||= @subcategory.measures.map do |measure|
|
@counts_by_grade ||= respondents.counts_by_grade
|
||||||
measure.student_survey_items.map do |survey_item|
|
end
|
||||||
next 0 if survey.form == 'short' && survey_item.on_short_form == false
|
|
||||||
|
def survey_items_have_sufficient_responses?
|
||||||
|
rates_by_grade.length.positive?
|
||||||
|
end
|
||||||
|
|
||||||
survey_item.survey_item_responses.where(school:,
|
def survey_items_with_sufficient_responses(grade:)
|
||||||
academic_year:).exclude_boston.count
|
SurveyItem.joins('inner join survey_item_responses on survey_item_responses.survey_item_id = survey_items.id')
|
||||||
end.sum
|
.student_survey_items
|
||||||
end.sum
|
.where("survey_item_responses.school": school, "survey_item_responses.academic_year": academic_year, "survey_item_responses.grade": grade, "survey_item_responses.survey_item_id": subcategory.survey_items.student_survey_items)
|
||||||
|
.group('survey_items.id')
|
||||||
|
.having('count(*) >= 10')
|
||||||
|
.count
|
||||||
|
end
|
||||||
|
|
||||||
|
def survey_item_count(grade:)
|
||||||
|
survey_items_with_sufficient_responses(grade:).count
|
||||||
|
end
|
||||||
|
|
||||||
|
def respondents
|
||||||
|
@respondents ||= Respondent.find_by(school:, academic_year:)
|
||||||
end
|
end
|
||||||
|
|
||||||
def total_possible_responses
|
def total_possible_responses
|
||||||
@total_possible_responses ||= begin
|
@total_possible_responses ||= begin
|
||||||
total_responses = Respondent.find_by(school:, academic_year:)
|
return 0 unless respondents.present?
|
||||||
return 0 unless total_responses.present?
|
|
||||||
|
|
||||||
total_responses.total_students
|
respondents.total_students
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def grades_with_sufficient_responses
|
|
||||||
SurveyItemResponse.where(school:, academic_year:,
|
|
||||||
survey_item: subcategory.survey_items.student_survey_items).where.not(grade: nil)
|
|
||||||
.group(:grade)
|
|
||||||
.select(:response_id)
|
|
||||||
.distinct(:response_id)
|
|
||||||
.count.reject do |_key, value|
|
|
||||||
value < 10
|
|
||||||
end.keys
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@ -0,0 +1,35 @@
|
|||||||
|
require 'watir'
|
||||||
|
|
||||||
|
module Dese
|
||||||
|
class Staffing
|
||||||
|
include Dese::Scraper
|
||||||
|
attr_reader :filepath
|
||||||
|
|
||||||
|
def initialize(filepath: Rails.root.join('data', 'staffing', 'staffing.csv'))
|
||||||
|
@filepath = filepath
|
||||||
|
end
|
||||||
|
|
||||||
|
def run_all
|
||||||
|
scrape_staffing(filepath:)
|
||||||
|
end
|
||||||
|
|
||||||
|
def scrape_staffing(filepath:)
|
||||||
|
headers = ['Raw likert calculation', 'Likert Score', 'Admin Data Item', 'Academic Year',
|
||||||
|
'School Name', 'DESE ID',
|
||||||
|
'PK-2 (#)', '3-5 (#)', '6-8 (#)', '9-12 (#)', 'Multiple Grades (#)',
|
||||||
|
'All Grades (#)', 'FTE Count']
|
||||||
|
write_headers(filepath:, headers:)
|
||||||
|
run do |academic_year|
|
||||||
|
admin_data_item_id = 'NA'
|
||||||
|
url = 'https://profiles.doe.mass.edu/statereport/gradesubjectstaffing.aspx'
|
||||||
|
range = academic_year.range
|
||||||
|
selectors = { 'ctl00_ContentPlaceHolder1_ddReportType' => 'School',
|
||||||
|
'ctl00_ContentPlaceHolder1_ddYear' => range,
|
||||||
|
'ctl00_ContentPlaceHolder1_ddDisplay' => 'Full-time Equivalents' }
|
||||||
|
submit_id = 'btnViewReport'
|
||||||
|
calculation = ->(_headers, _items) { 'NA' }
|
||||||
|
Prerequisites.new(filepath, url, selectors, submit_id, admin_data_item_id, calculation)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -1,29 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class ResponseRateLoader
|
|
||||||
def self.reset(schools: School.all, academic_years: AcademicYear.all, subcategories: Subcategory.all)
|
|
||||||
subcategories.each do |subcategory|
|
|
||||||
schools.each do |school|
|
|
||||||
academic_years.each do |academic_year|
|
|
||||||
process_response_rate(subcategory:, school:, academic_year:)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def self.process_response_rate(subcategory:, school:, academic_year:)
|
|
||||||
student = StudentResponseRateCalculator.new(subcategory:, school:, academic_year:)
|
|
||||||
teacher = TeacherResponseRateCalculator.new(subcategory:, school:, academic_year:)
|
|
||||||
|
|
||||||
response_rate = ResponseRate.find_or_create_by!(subcategory:, school:, academic_year:)
|
|
||||||
|
|
||||||
response_rate.update!(student_response_rate: student.rate,
|
|
||||||
teacher_response_rate: teacher.rate,
|
|
||||||
meets_student_threshold: student.meets_student_threshold?,
|
|
||||||
meets_teacher_threshold: teacher.meets_teacher_threshold?)
|
|
||||||
end
|
|
||||||
|
|
||||||
private_class_method :process_response_rate
|
|
||||||
end
|
|
||||||
@ -1,34 +1,27 @@
|
|||||||
<% content_for :title do %>
|
<% content_for :title do %>
|
||||||
<h1 class="sub-header-2 color-white m-0"> Analysis of <%= @school.name %> </h1>
|
<h1 class="sub-header-2 color-white m-0"> Analysis of <%= @school.name %> </h1>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="graph-content">
|
<div class="graph-content">
|
||||||
<div class="breadcrumbs sub-header-4">
|
<div class="breadcrumbs sub-header-4">
|
||||||
<%= @category.category_id %>:<%= @category.name %> > <%= @subcategory.subcategory_id %>:<%= @subcategory.name %>
|
<%= @category.category_id %>:<%= @category.name %> > <%= @subcategory.subcategory_id %>:<%= @subcategory.name %>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-flex flex-row pt-5 row">
|
<div class="d-flex flex-row pt-5 row">
|
||||||
<div class="d-flex flex-column flex-grow-6 bg-color-white col-3 px-5" data-controller="analyze">
|
<div class="d-flex flex-column flex-grow-6 bg-color-white col-3 px-5" data-controller="analyze">
|
||||||
<%= render partial: "focus_area", locals: {categories: @categories, district: @district, school: @school, academic_year: @academic_year, category: @category, subcategories: @subcategories} %>
|
<%= render partial: "focus_area", locals: {categories: @categories, district: @district, school: @school, academic_year: @academic_year, category: @category, subcategories: @subcategories} %>
|
||||||
|
|
||||||
<%= render partial: "school_years", locals: {available_academic_years: @available_academic_years, selected_academic_years: @selected_academic_years, district: @district, school: @school, academic_year: @academic_year, category: @category, subcategory: @subcategory, measures: @measures} %>
|
<%= render partial: "school_years", locals: {available_academic_years: @available_academic_years, selected_academic_years: @selected_academic_years, district: @district, school: @school, academic_year: @academic_year, category: @category, subcategory: @subcategory, measures: @measures} %>
|
||||||
|
|
||||||
<%= render partial: "data_filters", locals: {district: @district, school: @school, academic_year: @academic_year, category: @category, subcategory: @subcategory} %>
|
<%= render partial: "data_filters", locals: {district: @district, school: @school, academic_year: @academic_year, category: @category, subcategory: @subcategory} %>
|
||||||
</div>
|
</div>
|
||||||
|
<% cache [@subcategory, @school, @selected_academic_years, @graph, @selected_races, @race_score_timestamp, @selected_grades, @grades, @selected_genders, @genders] do %>
|
||||||
<% cache [@subcategory, @school, @selected_academic_years, @response_rate_timestamp, @graph, @selected_races, @race_score_timestamp, @selected_grades, @grades, @selected_genders, @genders] do %>
|
|
||||||
<div class="bg-color-white flex-grow-1 col-9">
|
<div class="bg-color-white flex-grow-1 col-9">
|
||||||
<% @measures.each do |measure| %>
|
<% @measures.each do |measure| %>
|
||||||
<section class="mb-6">
|
<section class="mb-6">
|
||||||
<p class="construct-id">Measure <%= measure.measure_id %></p>
|
<p class="construct-id">Measure <%= measure.measure_id %></p>
|
||||||
<h2> <%= measure.name %> </h2>
|
<h2> <%= measure.name %> </h2>
|
||||||
|
|
||||||
<%= render partial: "grouped_bar_chart" , locals: { measure: measure} %>
|
<%= render partial: "grouped_bar_chart" , locals: { measure: measure} %>
|
||||||
</section>
|
</section>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,108 +1,94 @@
|
|||||||
<% content_for :navigation do %>
|
<% content_for :navigation do %>
|
||||||
<h2 class="sub-header-2 color-white m-0">Areas Of Interest</h2>
|
<h2 class="sub-header-2 color-white m-0">Areas Of Interest</h2>
|
||||||
|
<select id="select-academic-year" class="form-select" name="academic-year">
|
||||||
<select id="select-academic-year" class="form-select" name="academic-year">
|
<% @academic_years.each do |year| %>
|
||||||
<% @academic_years.each do |year| %>
|
<option value="<%= district_school_overview_index_path(@district, @school, {year: year.range}) %>" <%= @academic_year == year ? "selected" : nil %>><%= year.formatted_range %></option>
|
||||||
<option value="<%= district_school_overview_index_path(@district, @school, {year: year.range}) %>" <%= @academic_year == year ? "selected" : nil %>><%= year.formatted_range %></option>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
</select>
|
</select>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
<% cache do %>
|
||||||
<% cache do %>
|
<svg class="d-none">
|
||||||
<svg class="d-none">
|
<symbol viewBox="0 0 24 24" id="warning-harvey-ball">
|
||||||
|
<circle cx="12" cy="12" r="11.5" fill="white" stroke="none" />
|
||||||
<symbol viewBox="0 0 24 24" id="warning-harvey-ball">
|
<path d="
|
||||||
<circle cx="12" cy="12" r="11.5" fill="white" stroke="none" />
|
|
||||||
<path d="
|
|
||||||
M 12 0
|
M 12 0
|
||||||
A 12 12 0 0 1 24 12
|
A 12 12 0 0 1 24 12
|
||||||
L 12 12
|
L 12 12
|
||||||
L 12 0"
|
L 12 0"
|
||||||
stroke="none"
|
stroke="none"
|
||||||
/>
|
/>
|
||||||
<circle cx="12" cy="12" r="11.5" fill="none" />
|
<circle cx="12" cy="12" r="11.5" fill="none" />
|
||||||
</symbol>
|
</symbol>
|
||||||
|
<symbol viewBox="0 0 24 24" id="watch-harvey-ball">
|
||||||
<symbol viewBox="0 0 24 24" id="watch-harvey-ball">
|
<circle cx="12" cy="12" r="11.5" fill="white" stroke="none" />
|
||||||
<circle cx="12" cy="12" r="11.5" fill="white" stroke="none" />
|
<path d="
|
||||||
<path d="
|
|
||||||
M 12 0
|
M 12 0
|
||||||
A 12 12 0 1 1 12 24
|
A 12 12 0 1 1 12 24
|
||||||
L 12 12
|
L 12 12
|
||||||
L 12 0"
|
L 12 0"
|
||||||
stroke="none"
|
stroke="none"
|
||||||
/>
|
/>
|
||||||
<circle cx="12" cy="12" r="11.5" fill="none" />
|
<circle cx="12" cy="12" r="11.5" fill="none" />
|
||||||
</symbol>
|
</symbol>
|
||||||
|
<symbol viewBox="0 0 24 24" id="growth-harvey-ball">
|
||||||
<symbol viewBox="0 0 24 24" id="growth-harvey-ball">
|
<circle cx="12" cy="12" r="11.5" fill="white" stroke="none" />
|
||||||
<circle cx="12" cy="12" r="11.5" fill="white" stroke="none" />
|
<path d="
|
||||||
<path d="
|
|
||||||
M 12 0
|
M 12 0
|
||||||
A 12 12 0 1 1 0 12
|
A 12 12 0 1 1 0 12
|
||||||
L 12 12
|
L 12 12
|
||||||
L 12 0"
|
L 12 0"
|
||||||
stroke="none"
|
stroke="none"
|
||||||
/>
|
/>
|
||||||
<circle cx="12" cy="12" r="11.5" fill="none" />
|
<circle cx="12" cy="12" r="11.5" fill="none" />
|
||||||
</symbol>
|
</symbol>
|
||||||
|
<symbol viewBox="0 0 24 24" id="approval-harvey-ball">
|
||||||
<symbol viewBox="0 0 24 24" id="approval-harvey-ball">
|
<circle cx="12" cy="12" r="11.5" />
|
||||||
<circle cx="12" cy="12" r="11.5" />
|
<path d="M19 8C19 8.28125 18.875 8.53125 18.6875 8.71875L10.6875 16.7188C10.5 16.9062 10.25 17 10 17C9.71875 17 9.46875 16.9062 9.28125 16.7188L5.28125 12.7188C5.09375 12.5312 5 12.2812 5 12C5 11.4375 5.4375 11 6 11C6.25 11 6.5 11.125 6.6875 11.3125L10 14.5938L17.2812 7.3125C17.4688 7.125 17.7188 7 18 7C18.5312 7 19 7.4375 19 8Z"
|
||||||
<path d="M19 8C19 8.28125 18.875 8.53125 18.6875 8.71875L10.6875 16.7188C10.5 16.9062 10.25 17 10 17C9.71875 17 9.46875 16.9062 9.28125 16.7188L5.28125 12.7188C5.09375 12.5312 5 12.2812 5 12C5 11.4375 5.4375 11 6 11C6.25 11 6.5 11.125 6.6875 11.3125L10 14.5938L17.2812 7.3125C17.4688 7.125 17.7188 7 18 7C18.5312 7 19 7.4375 19 8Z"
|
|
||||||
stroke-width=".5" stroke="white" fill="white" />
|
stroke-width=".5" stroke="white" fill="white" />
|
||||||
</symbol>
|
</symbol>
|
||||||
|
<symbol viewBox="0 0 24 24" id="ideal-harvey-ball">
|
||||||
<symbol viewBox="0 0 24 24" id="ideal-harvey-ball">
|
<circle cx="12" cy="12" r="11.5" />
|
||||||
<circle cx="12" cy="12" r="11.5" />
|
<path d="M9.28125 11.7188C9.46875 11.9062 9.71875 12 10 12C10.25 12 10.5 11.9062 10.6875 11.7188L15.6875 6.71875C15.875 6.53125 16 6.28125 16 6C16 5.4375 15.5312 5 15 5C14.7188 5 14.4688 5.125 14.2812 5.3125L10 9.59375L8.1875 7.8125C8 7.625 7.75 7.5 7.5 7.5C6.9375 7.5 6.5 7.9375 6.5 8.5C6.5 8.78125 6.59375 9.03125 6.78125 9.21875L9.28125 11.7188ZM19 10C19 9.4375 18.5312 9 18 9C17.7188 9 17.4688 9.125 17.2812 9.3125L10 16.5938L6.6875 13.3125C6.5 13.125 6.25 13 6 13C5.4375 13 5 13.4375 5 14C5 14.2812 5.09375 14.5312 5.28125 14.7188L9.28125 18.7188C9.46875 18.9062 9.71875 19 10 19C10.25 19 10.5 18.9062 10.6875 18.7188L18.6875 10.7188C18.875 10.5312 19 10.2812 19 10Z"
|
||||||
<path d="M9.28125 11.7188C9.46875 11.9062 9.71875 12 10 12C10.25 12 10.5 11.9062 10.6875 11.7188L15.6875 6.71875C15.875 6.53125 16 6.28125 16 6C16 5.4375 15.5312 5 15 5C14.7188 5 14.4688 5.125 14.2812 5.3125L10 9.59375L8.1875 7.8125C8 7.625 7.75 7.5 7.5 7.5C6.9375 7.5 6.5 7.9375 6.5 8.5C6.5 8.78125 6.59375 9.03125 6.78125 9.21875L9.28125 11.7188ZM19 10C19 9.4375 18.5312 9 18 9C17.7188 9 17.4688 9.125 17.2812 9.3125L10 16.5938L6.6875 13.3125C6.5 13.125 6.25 13 6 13C5.4375 13 5 13.4375 5 14C5 14.2812 5.09375 14.5312 5.28125 14.7188L9.28125 18.7188C9.46875 18.9062 9.71875 19 10 19C10.25 19 10.5 18.9062 10.6875 18.7188L18.6875 10.7188C18.875 10.5312 19 10.2812 19 10Z"
|
|
||||||
stroke-width=".5" stroke="white" fill="white" />
|
stroke-width=".5" stroke="white" fill="white" />
|
||||||
</symbol>
|
</symbol>
|
||||||
|
<symbol viewBox="0 0 24 24" id="insufficient_data-harvey-ball">
|
||||||
<symbol viewBox="0 0 24 24" id="insufficient_data-harvey-ball">
|
<circle cx="12" cy="12" r="11.5" />
|
||||||
<circle cx="12" cy="12" r="11.5" />
|
</symbol>
|
||||||
</symbol>
|
</svg>
|
||||||
</svg>
|
<% end %>
|
||||||
<% end %>
|
<% cache [@school, @academic_year] do %>
|
||||||
|
<div class="card">
|
||||||
<% cache [@school, @academic_year, @response_rate_timestamp] do %>
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
<div class="card">
|
<h2 class="sub-header-2">School Quality Framework Indicators</h2>
|
||||||
<div class="d-flex justify-content-between align-items-center">
|
<div class="harvey-ball-legend">
|
||||||
<h2 class="sub-header-2">School Quality Framework Indicators</h2>
|
<div class="font-size-14">Warning</div>
|
||||||
|
<svg class="ms-3 me-1" width="16" height="16" xmlns="http://www.w3.org/2000/svg">
|
||||||
<div class="harvey-ball-legend">
|
<use class="harvey-ball harvey-ball--warning" xlink:href="#warning-harvey-ball"></use>
|
||||||
<div class="font-size-14">Warning</div>
|
</svg>
|
||||||
<svg class="ms-3 me-1" width="16" height="16" xmlns="http://www.w3.org/2000/svg">
|
<svg class="mx-1" width="16" height="16" xmlns="http://www.w3.org/2000/svg">
|
||||||
<use class="harvey-ball harvey-ball--warning" xlink:href="#warning-harvey-ball"></use>
|
<use class="harvey-ball harvey-ball--watch" xlink:href="#watch-harvey-ball"></use>
|
||||||
</svg>
|
</svg>
|
||||||
<svg class="mx-1" width="16" height="16" xmlns="http://www.w3.org/2000/svg">
|
<svg class="mx-1" width="16" height="16" xmlns="http://www.w3.org/2000/svg">
|
||||||
<use class="harvey-ball harvey-ball--watch" xlink:href="#watch-harvey-ball"></use>
|
<use class="harvey-ball harvey-ball--growth" xlink:href="#growth-harvey-ball"></use>
|
||||||
</svg>
|
</svg>
|
||||||
<svg class="mx-1" width="16" height="16" xmlns="http://www.w3.org/2000/svg">
|
<svg class="mx-1" width="16" height="16" xmlns="http://www.w3.org/2000/svg">
|
||||||
<use class="harvey-ball harvey-ball--growth" xlink:href="#growth-harvey-ball"></use>
|
<use class="harvey-ball harvey-ball--approval" xlink:href="#approval-harvey-ball"></use>
|
||||||
</svg>
|
</svg>
|
||||||
<svg class="mx-1" width="16" height="16" xmlns="http://www.w3.org/2000/svg">
|
<svg class="ms-1 me-3" width="16" height="16" xmlns="http://www.w3.org/2000/svg">
|
||||||
<use class="harvey-ball harvey-ball--approval" xlink:href="#approval-harvey-ball"></use>
|
<use class="harvey-ball harvey-ball--ideal" xlink:href="#ideal-harvey-ball"></use>
|
||||||
</svg>
|
</svg>
|
||||||
<svg class="ms-1 me-3" width="16" height="16" xmlns="http://www.w3.org/2000/svg">
|
<div class="font-size-14">Ideal</div>
|
||||||
<use class="harvey-ball harvey-ball--ideal" xlink:href="#ideal-harvey-ball"></use>
|
</div>
|
||||||
</svg>
|
|
||||||
<div class="font-size-14">Ideal</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<%= render partial: "quality_framework_indicators", locals: { category_presenters: @category_presenters }, cached: true %>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="card">
|
||||||
<%= render partial: "quality_framework_indicators", locals: { category_presenters: @category_presenters }, cached: true %>
|
<h2 class="sub-header-2 mb-4">Distance From Benchmark</h2>
|
||||||
</div>
|
<%= render partial: "variance_chart", locals: { presenters: @variance_chart_row_presenters } , cached: true %>
|
||||||
|
</div>
|
||||||
<div class="card">
|
<% if @district == District.find_by_name("Boston") %>
|
||||||
<h2 class="sub-header-2 mb-4">Distance From Benchmark</h2>
|
<%= render partial: 'layouts/boston_modal' %>
|
||||||
|
<% elsif @has_empty_dataset %>
|
||||||
<%= render partial: "variance_chart", locals: { presenters: @variance_chart_row_presenters } , cached: true %>
|
<%= render partial: 'layouts/empty_dataset_modal' %>
|
||||||
</div>
|
<% end %>
|
||||||
|
<% end %>
|
||||||
<% if @district == District.find_by_name("Boston") %>
|
|
||||||
<%= render partial: 'layouts/boston_modal' %>
|
|
||||||
<% elsif @has_empty_dataset %>
|
|
||||||
<%= render partial: 'layouts/empty_dataset_modal' %>
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,17 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe Respondent, type: :model do
|
||||||
|
describe 'grade_counts' do
|
||||||
|
let(:single_grade_of_respondents) { create(:respondent, one: 10) }
|
||||||
|
let(:two_grades_of_respondents) { create(:respondent, pk: 10, k: 5, one: 0, two: 0, three: 0) }
|
||||||
|
let(:three_grades_of_respondents) { create(:respondent, one: 10, two: 5, twelve: 6, eleven: 0) }
|
||||||
|
context 'when the student respondents include one or more counts for the number of respondents' do
|
||||||
|
it 'returns a hash with only the grades that have a non-zero count of students' do
|
||||||
|
expect(single_grade_of_respondents.one).to eq(10)
|
||||||
|
expect(single_grade_of_respondents.counts_by_grade).to eq({ 1 => 10 })
|
||||||
|
expect(two_grades_of_respondents.counts_by_grade).to eq({ -1 => 10, 0 => 5 })
|
||||||
|
expect(three_grades_of_respondents.counts_by_grade).to eq({ 1 => 10, 2 => 5, 12 => 6 })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -1,140 +0,0 @@
|
|||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
describe ResponseRateLoader do
|
|
||||||
let(:school) { create(:school, name: 'milford-high-school') }
|
|
||||||
let(:academic_year) { create(:academic_year, range: '2020-21') }
|
|
||||||
let(:respondent) do
|
|
||||||
respondent = create(:respondent, school:, academic_year:)
|
|
||||||
respondent.total_students = 10
|
|
||||||
respondent.total_teachers = 10
|
|
||||||
respondent.save
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:short_form_survey) do
|
|
||||||
survey = create(:survey, school:, academic_year:)
|
|
||||||
survey.form = :short
|
|
||||||
survey.save
|
|
||||||
survey
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:subcategory) { create(:subcategory, subcategory_id: '5D', name: 'Health') }
|
|
||||||
let(:measure) { create(:measure, measure_id: '5D-ii', subcategory:) }
|
|
||||||
|
|
||||||
let(:s_acst_q1) { create(:survey_item, survey_item_id: 's-acst-q1', scale: s_acst) }
|
|
||||||
let(:s_acst_q2) { create(:survey_item, survey_item_id: 's-acst-q2', scale: s_acst, on_short_form: true) } # short form
|
|
||||||
let(:s_acst_q3) { create(:survey_item, survey_item_id: 's-acst-q3', scale: s_acst) }
|
|
||||||
let(:s_poaf_q1) { create(:survey_item, survey_item_id: 's-poaf-q1', scale: s_poaf) }
|
|
||||||
let(:s_poaf_q2) { create(:survey_item, survey_item_id: 's-poaf-q2', scale: s_poaf) }
|
|
||||||
let(:s_poaf_q3) { create(:survey_item, survey_item_id: 's-poaf-q3', scale: s_poaf, on_short_form: true) } # short form
|
|
||||||
let(:s_poaf_q4) { create(:survey_item, survey_item_id: 's-poaf-q4', scale: s_poaf) }
|
|
||||||
let(:t_phya_q2) { create(:survey_item, survey_item_id: 't-phya-q2', scale: t_phya) }
|
|
||||||
let(:t_phya_q3) { create(:survey_item, survey_item_id: 't-phya-q3', scale: t_phya) }
|
|
||||||
|
|
||||||
let(:s_acst) { create(:scale, scale_id: 's-acst', measure:) }
|
|
||||||
let(:s_poaf) { create(:scale, scale_id: 's-poaf', measure:) }
|
|
||||||
let(:t_phya) { create(:scale, scale_id: 't-phya', measure:) }
|
|
||||||
let(:response_rate) { ResponseRate.find_by(school:, academic_year:) }
|
|
||||||
before do
|
|
||||||
short_form_survey
|
|
||||||
respondent
|
|
||||||
end
|
|
||||||
|
|
||||||
after do
|
|
||||||
DatabaseCleaner.clean
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'self.reset' do
|
|
||||||
context 'When resetting response rates' do
|
|
||||||
context 'and half the students responded to each question' do
|
|
||||||
before :each do
|
|
||||||
create_list(:survey_item_response, 5, survey_item: s_acst_q1, likert_score: 3, school:, academic_year:)
|
|
||||||
create_list(:survey_item_response, 5, survey_item: s_acst_q2, likert_score: 3, school:, academic_year:)
|
|
||||||
create_list(:survey_item_response, 5, survey_item: s_acst_q3, likert_score: 3, school:, academic_year:)
|
|
||||||
create_list(:survey_item_response, 5, survey_item: s_poaf_q1, likert_score: 3, school:, academic_year:)
|
|
||||||
create_list(:survey_item_response, 5, survey_item: s_poaf_q2, likert_score: 3, school:, academic_year:)
|
|
||||||
create_list(:survey_item_response, 5, survey_item: s_poaf_q3, likert_score: 3, school:, academic_year:)
|
|
||||||
create_list(:survey_item_response, 5, survey_item: s_poaf_q4, likert_score: 3, school:, academic_year:)
|
|
||||||
create_list(:survey_item_response, 5, survey_item: t_phya_q2, likert_score: 3, school:, academic_year:)
|
|
||||||
create_list(:survey_item_response, 5, survey_item: t_phya_q3, likert_score: 3, school:, academic_year:)
|
|
||||||
|
|
||||||
ResponseRateLoader.reset(schools: [school], academic_years: [academic_year])
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'populates the database with response rates' do
|
|
||||||
expect(s_acst_q1.survey_item_id).to eq 's-acst-q1'
|
|
||||||
expect(subcategory.subcategory_id).to eq '5D'
|
|
||||||
expect(subcategory.name).to eq 'Health'
|
|
||||||
expect(s_acst.score(school:, academic_year:)).to eq 3
|
|
||||||
expect(s_poaf.score(school:, academic_year:)).to eq 3
|
|
||||||
expect(t_phya.score(school:, academic_year:)).to eq 3
|
|
||||||
expect(response_rate.student_response_rate).to eq 50
|
|
||||||
expect(response_rate.teacher_response_rate).to eq 50
|
|
||||||
expect(response_rate.meets_student_threshold).to be true
|
|
||||||
expect(response_rate.meets_teacher_threshold).to be true
|
|
||||||
end
|
|
||||||
context 'when running the loader a second time' do
|
|
||||||
it 'is idempotent' do
|
|
||||||
response_count = ResponseRate.count
|
|
||||||
ResponseRateLoader.reset(schools: [school], academic_years: [academic_year])
|
|
||||||
second_count = ResponseRate.count
|
|
||||||
|
|
||||||
expect(response_count).to eq second_count
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'and only the first question was asked; e.g. its on a short form and this is marked as a short form school' do
|
|
||||||
before do
|
|
||||||
create_list(:survey_item_response, 5, survey_item: s_acst_q1, likert_score: 3, school:, academic_year:)
|
|
||||||
s_acst_q1.update(on_short_form: true)
|
|
||||||
create_list(:survey_item_response, 5, survey_item: s_poaf_q1, likert_score: 3, school:, academic_year:)
|
|
||||||
create_list(:survey_item_response, 5, survey_item: t_phya_q2, likert_score: 3, school:, academic_year:)
|
|
||||||
|
|
||||||
ResponseRateLoader.reset(schools: [school], academic_years: [academic_year])
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'only takes into account the first question and ignores the other questions in the scale' do
|
|
||||||
expect(response_rate.student_response_rate).to eq 50
|
|
||||||
expect(response_rate.teacher_response_rate).to eq 50
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'and no respondent entry exists for the school and year' do
|
|
||||||
before do
|
|
||||||
Respondent.delete_all
|
|
||||||
create_list(:survey_item_response, 5, survey_item: s_acst_q1, likert_score: 3, school:, academic_year:)
|
|
||||||
create_list(:survey_item_response, 5, survey_item: s_poaf_q1, likert_score: 3, school:, academic_year:)
|
|
||||||
create_list(:survey_item_response, 5, survey_item: t_phya_q2, likert_score: 3, school:, academic_year:)
|
|
||||||
|
|
||||||
ResponseRateLoader.reset(schools: [school], academic_years: [academic_year])
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'since no score can be calculated, it returns a default of 100' do
|
|
||||||
expect(response_rate.student_response_rate).to eq 100
|
|
||||||
expect(response_rate.teacher_response_rate).to eq 100
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'and the school took the short form student survey' do
|
|
||||||
before do
|
|
||||||
create_list(:survey_item_response, 1, survey_item: s_acst_q1, likert_score: 3, school:, academic_year:)
|
|
||||||
create_list(:survey_item_response, 6, survey_item: s_acst_q2, likert_score: 3, school:, academic_year:) # short form
|
|
||||||
create_list(:survey_item_response, 1, survey_item: s_acst_q3, likert_score: 3, school:, academic_year:)
|
|
||||||
create_list(:survey_item_response, 1, survey_item: s_poaf_q1, likert_score: 3, school:, academic_year:)
|
|
||||||
create_list(:survey_item_response, 1, survey_item: s_poaf_q2, likert_score: 3, school:, academic_year:)
|
|
||||||
create_list(:survey_item_response, 6, survey_item: s_poaf_q3, likert_score: 3, school:, academic_year:) # short form
|
|
||||||
create_list(:survey_item_response, 1, survey_item: s_poaf_q4, likert_score: 3, school:, academic_year:)
|
|
||||||
create_list(:survey_item_response, 1, survey_item: t_phya_q2, likert_score: 3, school:, academic_year:)
|
|
||||||
create_list(:survey_item_response, 1, survey_item: t_phya_q3, likert_score: 3, school:, academic_year:)
|
|
||||||
short_form_survey
|
|
||||||
|
|
||||||
ResponseRateLoader.reset(schools: [school], academic_years: [academic_year])
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'only counts responses from survey items on the short form' do
|
|
||||||
expect(response_rate.student_response_rate).to eq 60
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
Loading…
Reference in new issue