mirror of
https://github.com/edcommonwealth/sqm-dashboards.git
synced 2026-03-07 21:48:16 -08:00
feat: Add income table to the database. Add seeder for income. Add a reference to income from survey item response. Update the loader to import income data from the survey response csv. Refactor analyze controller to extract presenter. Add corresponding specs. Add income graph to analyze page
This commit is contained in:
parent
7373e2e52f
commit
76ebcc6ef3
53 changed files with 2410 additions and 1036 deletions
|
|
@ -1,172 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AnalyzeController < SqmApplicationController
|
||||
before_action :assign_categories, :assign_subcategories, :assign_measures, :assign_academic_years,
|
||||
:races, :selected_races, :graph, :graphs, :background, :race_score_timestamp,
|
||||
:source, :sources, :group, :groups, :selected_grades, :grades, :slice, :selected_genders, :genders, only: [:index]
|
||||
def index; end
|
||||
|
||||
private
|
||||
|
||||
def assign_categories
|
||||
@category ||= Category.find_by_category_id(params[:category])
|
||||
@category ||= Category.order(:category_id).first
|
||||
@categories = Category.all.order(:category_id)
|
||||
end
|
||||
|
||||
def assign_subcategories
|
||||
@subcategories = @category.subcategories.order(:subcategory_id)
|
||||
@subcategory ||= Subcategory.find_by_subcategory_id(params[:subcategory])
|
||||
@subcategory ||= @subcategories.first
|
||||
end
|
||||
|
||||
def assign_measures
|
||||
@measures = @subcategory.measures.order(:measure_id).includes(%i[admin_data_items subcategory])
|
||||
end
|
||||
|
||||
def assign_academic_years
|
||||
@available_academic_years = AcademicYear.order(:range).all
|
||||
year_params = params[:academic_years]
|
||||
@academic_year_params = year_params.split(',') if year_params
|
||||
@selected_academic_years = []
|
||||
@academic_year_params ||= []
|
||||
@academic_year_params.each do |year|
|
||||
@selected_academic_years << AcademicYear.find_by_range(year)
|
||||
end
|
||||
end
|
||||
|
||||
def races
|
||||
@races ||= Race.all.order(designation: :ASC)
|
||||
end
|
||||
|
||||
def selected_races
|
||||
@selected_races ||= begin
|
||||
race_params = params[:races]
|
||||
return @selected_races = races unless race_params
|
||||
|
||||
race_list = race_params.split(',') if race_params
|
||||
if race_list
|
||||
race_list = race_list.map do |race|
|
||||
Race.find_by_slug race
|
||||
end
|
||||
end
|
||||
race_list
|
||||
end
|
||||
end
|
||||
|
||||
def graph
|
||||
graphs.each do |graph|
|
||||
@graph = graph if graph.slug == params[:graph]
|
||||
end
|
||||
|
||||
@graph ||= graphs.first
|
||||
end
|
||||
|
||||
def graphs
|
||||
@graphs ||= [Analyze::Graph::AllData.new, Analyze::Graph::StudentsAndTeachers.new, Analyze::Graph::StudentsByRace.new(races: selected_races),
|
||||
Analyze::Graph::StudentsByGrade.new(grades: selected_grades), Analyze::Graph::StudentsByGender.new(genders: selected_genders)]
|
||||
end
|
||||
|
||||
def background
|
||||
@background ||= BackgroundPresenter.new(num_of_columns: graph.columns.count)
|
||||
end
|
||||
|
||||
def race_score_timestamp
|
||||
@race_score_timestamp ||= begin
|
||||
score = RaceScore.where(school: @school,
|
||||
academic_year: @academic_year).order(updated_at: :DESC).first || Today.new
|
||||
score.updated_at
|
||||
end
|
||||
end
|
||||
|
||||
def source
|
||||
source_param = params[:source]
|
||||
sources.each do |source|
|
||||
@source = source if source.slug == source_param
|
||||
end
|
||||
|
||||
@source ||= sources.first
|
||||
end
|
||||
|
||||
def sources
|
||||
all_data_slices = [Analyze::Slice::AllData.new]
|
||||
all_data_source = Analyze::Source::AllData.new(slices: all_data_slices)
|
||||
|
||||
students_and_teachers = Analyze::Slice::StudentsAndTeachers.new
|
||||
students_by_group = Analyze::Slice::StudentsByGroup.new(races:, grades:)
|
||||
survey_data_slices = [students_and_teachers, students_by_group]
|
||||
survey_data_source = Analyze::Source::SurveyData.new(slices: survey_data_slices)
|
||||
|
||||
@sources = [all_data_source, survey_data_source]
|
||||
end
|
||||
|
||||
def slice
|
||||
slice_param = params[:slice]
|
||||
slices.each do |slice|
|
||||
@slice = slice if slice.slug == slice_param
|
||||
end
|
||||
|
||||
@slice ||= slices.first
|
||||
end
|
||||
|
||||
def slices
|
||||
source.slices
|
||||
end
|
||||
|
||||
def group
|
||||
groups.each do |group|
|
||||
@group = group if group.slug == params[:group]
|
||||
end
|
||||
|
||||
@group ||= groups.first
|
||||
end
|
||||
|
||||
def groups
|
||||
@groups = [Analyze::Group::Race.new, Analyze::Group::Grade.new, Analyze::Group::Gender.new]
|
||||
end
|
||||
|
||||
def selected_grades
|
||||
@selected_grades ||= begin
|
||||
grade_params = params[:grades]
|
||||
return @selected_grades = grades unless grade_params
|
||||
|
||||
grade_list = grade_params.split(',') if grade_params
|
||||
if grade_list
|
||||
grade_list = grade_list.map do |grade|
|
||||
grade.to_i
|
||||
end
|
||||
end
|
||||
grade_list
|
||||
end
|
||||
end
|
||||
|
||||
def grades
|
||||
@grades ||= SurveyItemResponse.where(school: @school, academic_year: @academic_year)
|
||||
.where.not(grade: nil)
|
||||
.group(:grade)
|
||||
.select(:response_id)
|
||||
.distinct(:response_id)
|
||||
.count.reject do |_key, value|
|
||||
value < 10
|
||||
end.keys
|
||||
end
|
||||
|
||||
def selected_genders
|
||||
@selected_genders ||= begin
|
||||
gender_params = params[:genders]
|
||||
return @selected_genders = genders unless gender_params
|
||||
|
||||
gender_list = gender_params.split(',') if gender_params
|
||||
if gender_list
|
||||
gender_list = gender_list.map do |gender|
|
||||
Gender.find_by_designation(gender)
|
||||
end
|
||||
end
|
||||
gender_list
|
||||
end
|
||||
end
|
||||
|
||||
def genders
|
||||
@genders ||= Gender.all
|
||||
def index
|
||||
@presenter = Analyze::Presenter.new(params:, school: @school, academic_year: @academic_year)
|
||||
@background ||= BackgroundPresenter.new(num_of_columns: @presenter.graph.columns.count)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ module AnalyzeHelper
|
|||
end
|
||||
|
||||
def base_url
|
||||
analyze_subcategory_link(district: @district, school: @school, academic_year: @academic_year, category: @category,
|
||||
subcategory: @subcategory)
|
||||
analyze_subcategory_link(district: @district, school: @school, academic_year: @academic_year, category: @presenter.category,
|
||||
subcategory: @presenter.subcategory)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -22,16 +22,17 @@ export default class extends Controller {
|
|||
"&graph=" +
|
||||
this.selected_graph(target) +
|
||||
"&races=" +
|
||||
this.selected_races().join(",") +
|
||||
this.selected_items("race").join(",") +
|
||||
"&genders=" +
|
||||
this.selected_genders().join(",") +
|
||||
this.selected_items("gender").join(",") +
|
||||
"&incomes=" +
|
||||
this.selected_items("income").join(",") +
|
||||
"&grades=" +
|
||||
this.selected_grades().join(",");
|
||||
this.selected_items("grade").join(",");
|
||||
|
||||
this.go_to(url);
|
||||
}
|
||||
|
||||
|
||||
go_to(location) {
|
||||
window.location = location;
|
||||
}
|
||||
|
|
@ -121,58 +122,34 @@ export default class extends Controller {
|
|||
return item.id;
|
||||
})[0];
|
||||
|
||||
const groups = new Map([
|
||||
['gender', 'students-by-gender'],
|
||||
['grade', 'students-by-grade'],
|
||||
['income', 'students-by-income'],
|
||||
['race', 'students-by-race']
|
||||
])
|
||||
|
||||
if (target.name === 'slice' || target.name === 'group') {
|
||||
if (selected_slice === 'students-and-teachers') {
|
||||
return 'students-and-teachers';
|
||||
} else if (this.selected_group() === 'race') {
|
||||
return 'students-by-race';
|
||||
} else if (this.selected_group() === 'gender') {
|
||||
return 'students-by-gender';
|
||||
} else if (this.selected_group() === 'grade') {
|
||||
return 'students-by-grade';
|
||||
}
|
||||
return groups.get(this.selected_group());
|
||||
}
|
||||
|
||||
return window.graph;
|
||||
|
||||
}
|
||||
|
||||
selected_races() {
|
||||
let race_checkboxes = [...document.getElementsByName("race-checkbox")]
|
||||
let races = race_checkboxes
|
||||
selected_items(type) {
|
||||
let checkboxes = [...document.getElementsByName(`${type}-checkbox`)]
|
||||
let items = checkboxes
|
||||
.filter((item) => {
|
||||
return item.checked;
|
||||
})
|
||||
.map((item) => {
|
||||
return item.id;
|
||||
return item.id.replace(`${type}-`, '');
|
||||
});
|
||||
|
||||
return races;
|
||||
}
|
||||
|
||||
selected_grades() {
|
||||
let grade_checkboxes = [...document.getElementsByName("grade-checkbox")]
|
||||
let grades = grade_checkboxes
|
||||
.filter((item) => {
|
||||
return item.checked;
|
||||
})
|
||||
.map((item) => {
|
||||
return item.id.replace('grade-', '');
|
||||
});
|
||||
|
||||
return grades;
|
||||
}
|
||||
|
||||
selected_genders() {
|
||||
let gender_checkboxes = [...document.getElementsByName("gender-checkbox")]
|
||||
let genders = gender_checkboxes
|
||||
.filter((item) => {
|
||||
return item.checked;
|
||||
})
|
||||
.map((item) => {
|
||||
return item.id.replace('gender-', '');
|
||||
});
|
||||
|
||||
return genders;
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
7
app/models/income.rb
Normal file
7
app/models/income.rb
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
class Income < ApplicationRecord
|
||||
scope :by_designation, -> { all.map { |income| [income.designation, income] }.to_h }
|
||||
|
||||
include FriendlyId
|
||||
|
||||
friendly_id :designation, use: [:slugged]
|
||||
end
|
||||
|
|
@ -9,6 +9,7 @@ class SurveyItemResponse < ActiveRecord::Base
|
|||
belongs_to :survey_item, counter_cache: true
|
||||
belongs_to :student, foreign_key: :student_id, optional: true
|
||||
belongs_to :gender
|
||||
belongs_to :income
|
||||
|
||||
has_one :measure, through: :survey_item
|
||||
|
||||
|
|
@ -31,4 +32,9 @@ class SurveyItemResponse < ActiveRecord::Base
|
|||
SurveyItemResponse.where(survey_item: survey_items, school:,
|
||||
academic_year:, income:, grade: school.grades(academic_year:)).group(:survey_item).average(:likert_score)
|
||||
}
|
||||
|
||||
scope :averages_for_income, lambda { |survey_items, school, academic_year, income|
|
||||
SurveyItemResponse.where(survey_item: survey_items, school:,
|
||||
academic_year:, income:).group(:survey_item).average(:likert_score)
|
||||
}
|
||||
end
|
||||
|
|
|
|||
85
app/presenters/analyze/bar_presenter.rb
Normal file
85
app/presenters/analyze/bar_presenter.rb
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Analyze
|
||||
class BarPresenter
|
||||
include AnalyzeHelper
|
||||
attr_reader :score, :x_position, :academic_year, :measure_id, :measure, :color
|
||||
|
||||
MINIMUM_BAR_HEIGHT = 2
|
||||
|
||||
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
|
||||
benchmark_height = analyze_zone_height * 2
|
||||
case zone.type
|
||||
when :ideal, :approval
|
||||
benchmark_height - bar_height_percentage
|
||||
else
|
||||
benchmark_height
|
||||
end
|
||||
end
|
||||
|
||||
def bar_color
|
||||
"fill-#{zone.type}"
|
||||
end
|
||||
|
||||
def bar_height_percentage
|
||||
bar_height = send("#{zone.type}_bar_height_percentage") || 0
|
||||
enforce_minimum_height(bar_height:)
|
||||
end
|
||||
|
||||
def percentage
|
||||
low_benchmark = zone.low_benchmark
|
||||
(score.average - low_benchmark) / (zone.high_benchmark - 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
|
||||
average = score.average || 0
|
||||
|
||||
average.round(6)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def enforce_minimum_height(bar_height:)
|
||||
bar_height < MINIMUM_BAR_HEIGHT ? MINIMUM_BAR_HEIGHT : bar_height
|
||||
end
|
||||
|
||||
def ideal_bar_height_percentage
|
||||
(percentage * zone_height_percentage + zone_height_percentage) * 100
|
||||
end
|
||||
|
||||
def approval_bar_height_percentage
|
||||
(percentage * zone_height_percentage) * 100
|
||||
end
|
||||
|
||||
def growth_bar_height_percentage
|
||||
((1 - percentage) * zone_height_percentage) * 100
|
||||
end
|
||||
|
||||
def watch_bar_height_percentage
|
||||
((1 - percentage) * zone_height_percentage + zone_height_percentage) * 100
|
||||
end
|
||||
|
||||
def warning_bar_height_percentage
|
||||
((1 - percentage) * zone_height_percentage + zone_height_percentage + zone_height_percentage) * 100
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -27,10 +27,10 @@ module Analyze
|
|||
def bars
|
||||
@bars ||= yearly_scores.map.each_with_index do |yearly_score, index|
|
||||
year = yearly_score.year
|
||||
AnalyzeBarPresenter.new(measure:, academic_year: year,
|
||||
score: yearly_score.score,
|
||||
x_position: bar_x(index),
|
||||
color: bar_color(year))
|
||||
Analyze::BarPresenter.new(measure:, academic_year: year,
|
||||
score: yearly_score.score,
|
||||
x_position: bar_x(index),
|
||||
color: bar_color(year))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -27,3 +27,4 @@ module Analyze
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -27,3 +27,4 @@ module Analyze
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
module Analyze
|
||||
module Graph
|
||||
module Column
|
||||
module IncomeColumn
|
||||
module ScoreForIncome
|
||||
def score(year_index)
|
||||
academic_year = academic_years[year_index]
|
||||
averages = SurveyItemResponse.averages_for_income(measure.student_survey_items, school, academic_year,
|
||||
income)
|
||||
average = bubble_up_averages(averages:).round(2)
|
||||
|
||||
scorify(average:, meets_student_threshold: sufficient_student_responses?(academic_year:))
|
||||
end
|
||||
|
||||
def bubble_up_averages(averages:)
|
||||
measure.student_scales.map do |scale|
|
||||
scale.survey_items.map do |survey_item|
|
||||
averages[survey_item]
|
||||
end.remove_blanks.average
|
||||
end.remove_blanks.average
|
||||
end
|
||||
|
||||
def scorify(average:, meets_student_threshold:)
|
||||
return Score::NIL_SCORE unless meets_student_threshold
|
||||
|
||||
Score.new(average:,
|
||||
meets_teacher_threshold: false,
|
||||
meets_student_threshold: true,
|
||||
meets_admin_data_threshold: false)
|
||||
end
|
||||
|
||||
def sufficient_student_responses?(academic_year:)
|
||||
yearly_counts = SurveyItemResponse.where(school:, academic_year:,
|
||||
income:, survey_item: measure.student_survey_items).group(:income).select(:response_id).distinct(:response_id).count
|
||||
yearly_counts.any? do |count|
|
||||
count[1] >= 10
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -27,3 +27,4 @@ module Analyze
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -3,16 +3,64 @@ module Analyze
|
|||
module Column
|
||||
module ScoreForRace
|
||||
def score(year_index)
|
||||
s = ::RaceScore.find_by(measure:, school:, academic_year: academic_years[year_index], race:)
|
||||
average = s.average.round(2) unless s.nil?
|
||||
average ||= 0
|
||||
meets_student_threshold = s.meets_student_threshold? unless s.nil?
|
||||
meets_student_threshold ||= false
|
||||
Score.new(average:,
|
||||
meets_teacher_threshold: false,
|
||||
meets_student_threshold:,
|
||||
academic_year = academic_year_for_year_index(year_index)
|
||||
rate = response_rate(school:, academic_year:, measure:)
|
||||
return Score::NIL_SCORE unless rate.meets_student_threshold
|
||||
|
||||
survey_items = measure.student_survey_items
|
||||
|
||||
averages = grouped_responses(school:, academic_year:, survey_items:, race:)
|
||||
meets_student_threshold = sufficient_responses(school:, academic_year:, race:)
|
||||
scorify(responses: averages, meets_student_threshold:, measure:)
|
||||
end
|
||||
|
||||
def grouped_responses(school:, academic_year:, survey_items:, race:)
|
||||
@grouped_responses ||= Hash.new do |memo, (school, academic_year, survey_items, race)|
|
||||
memo[[school, academic_year, survey_items, race]] =
|
||||
SurveyItemResponse.joins("JOIN student_races on survey_item_responses.student_id = student_races.student_id JOIN students on students.id = student_races.student_id").where(
|
||||
school:, academic_year:, grade: school.grades(academic_year:)
|
||||
).where("student_races.race_id": race.id).group(:survey_item_id).having("count(*) >= 10").average(:likert_score)
|
||||
end
|
||||
|
||||
@grouped_responses[[school, academic_year, survey_items, race]]
|
||||
end
|
||||
|
||||
def response_rate(school:, academic_year:, measure:)
|
||||
subcategory = measure.subcategory
|
||||
@response_rate ||= Hash.new do |memo, (school, academic_year, subcategory)|
|
||||
memo[[school, academic_year, subcategory]] = subcategory.response_rate(school:, academic_year:)
|
||||
end
|
||||
|
||||
@response_rate[[school, academic_year, subcategory]]
|
||||
end
|
||||
|
||||
def scorify(responses:, meets_student_threshold:, measure:)
|
||||
averages = bubble_up_averages(responses:, measure:)
|
||||
average = averages.average.round(2)
|
||||
|
||||
average = 0 unless meets_student_threshold
|
||||
|
||||
Score.new(average:, meets_teacher_threshold: false, meets_student_threshold:,
|
||||
meets_admin_data_threshold: false)
|
||||
end
|
||||
|
||||
def sufficient_responses(school:, academic_year:, race:)
|
||||
@sufficient_responses ||= Hash.new do |memo, (school, academic_year, race)|
|
||||
number_of_students_for_a_racial_group = SurveyItemResponse.joins("JOIN student_races on survey_item_responses.student_id = student_races.student_id JOIN students on students.id = student_races.student_id").where(
|
||||
school:, academic_year:
|
||||
).where("student_races.race_id": race.id).distinct.pluck(:student_id).count
|
||||
memo[[school, academic_year, race]] = number_of_students_for_a_racial_group >= 10
|
||||
end
|
||||
@sufficient_responses[[school, academic_year, race]]
|
||||
end
|
||||
|
||||
def bubble_up_averages(responses:, measure:)
|
||||
measure.student_scales.map do |scale|
|
||||
scale.survey_items.map do |survey_item|
|
||||
responses[survey_item.id]
|
||||
end.remove_blanks.average
|
||||
end.remove_blanks
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
40
app/presenters/analyze/graph/students_by_income.rb
Normal file
40
app/presenters/analyze/graph/students_by_income.rb
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
module Analyze
|
||||
module Graph
|
||||
class StudentsByIncome
|
||||
attr_reader :incomes
|
||||
|
||||
def initialize(incomes:)
|
||||
@incomes = incomes
|
||||
end
|
||||
|
||||
def to_s
|
||||
"Students by income"
|
||||
end
|
||||
|
||||
def slug
|
||||
"students-by-income"
|
||||
end
|
||||
|
||||
def columns
|
||||
[].tap do |array|
|
||||
incomes.each do |income|
|
||||
array << column_for_income_code(code: income.slug)
|
||||
end
|
||||
array << Analyze::Graph::Column::AllStudent
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def column_for_income_code(code:)
|
||||
CFR[code.to_s]
|
||||
end
|
||||
|
||||
CFR = {
|
||||
"economically-disadvantaged-y" => Analyze::Graph::Column::IncomeColumn::Disadvantaged,
|
||||
"economically-disadvantaged-n" => Analyze::Graph::Column::IncomeColumn::NotDisadvantaged,
|
||||
"unknown" => Analyze::Graph::Column::IncomeColumn::Unknown
|
||||
}.freeze
|
||||
end
|
||||
end
|
||||
end
|
||||
13
app/presenters/analyze/group/income.rb
Normal file
13
app/presenters/analyze/group/income.rb
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
module Analyze
|
||||
module Group
|
||||
class Income
|
||||
def name
|
||||
'Income'
|
||||
end
|
||||
|
||||
def slug
|
||||
'income'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
153
app/presenters/analyze/presenter.rb
Normal file
153
app/presenters/analyze/presenter.rb
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
module Analyze
|
||||
class Presenter
|
||||
attr_reader :params, :school, :academic_year
|
||||
|
||||
def initialize(params:, school:, academic_year:)
|
||||
@params = params
|
||||
@school = school
|
||||
@academic_year = academic_year
|
||||
end
|
||||
|
||||
def category
|
||||
@category ||= Category.find_by_category_id(params[:category]) || Category.order(:category_id).first
|
||||
end
|
||||
|
||||
def categories
|
||||
@categories = Category.all.order(:category_id)
|
||||
end
|
||||
|
||||
def subcategory
|
||||
@subcategory ||= Subcategory.find_by_subcategory_id(params[:subcategory]) || subcategories.first
|
||||
end
|
||||
|
||||
def subcategories
|
||||
@subcategories = category.subcategories.order(:subcategory_id)
|
||||
end
|
||||
|
||||
def measures
|
||||
@measures = subcategory.measures.order(:measure_id).includes(%i[admin_data_items subcategory])
|
||||
end
|
||||
|
||||
def academic_years
|
||||
@academic_years = AcademicYear.order(:range).all
|
||||
end
|
||||
|
||||
def selected_academic_years
|
||||
@selected_academic_years ||= begin
|
||||
year_params = params[:academic_years]
|
||||
return [] unless year_params
|
||||
|
||||
year_params.split(",").map { |year| AcademicYear.find_by_range(year) }.compact
|
||||
end
|
||||
end
|
||||
|
||||
def races
|
||||
@races ||= Race.all.order(designation: :ASC)
|
||||
end
|
||||
|
||||
def selected_races
|
||||
@selected_races ||= begin
|
||||
race_params = params[:races]
|
||||
return races unless race_params
|
||||
|
||||
race_params.split(",").map { |race| Race.find_by_slug race }.compact
|
||||
end
|
||||
end
|
||||
|
||||
def graphs
|
||||
@graphs ||= [Analyze::Graph::AllData.new, Analyze::Graph::StudentsAndTeachers.new, Analyze::Graph::StudentsByRace.new(races: selected_races),
|
||||
Analyze::Graph::StudentsByGrade.new(grades: selected_grades), Analyze::Graph::StudentsByGender.new(genders: selected_genders), Analyze::Graph::StudentsByIncome.new(incomes: selected_incomes)]
|
||||
end
|
||||
|
||||
def graph
|
||||
@graph ||= graphs.reduce(graphs.first) do |acc, graph|
|
||||
graph.slug == params[:graph] ? graph : acc
|
||||
end
|
||||
end
|
||||
|
||||
def selected_grades
|
||||
@selected_grades ||= begin
|
||||
grade_params = params[:grades]
|
||||
return grades unless grade_params
|
||||
|
||||
grade_params.split(",").map(&:to_i)
|
||||
end
|
||||
end
|
||||
|
||||
def selected_genders
|
||||
@selected_genders ||= begin
|
||||
gender_params = params[:genders]
|
||||
return genders unless gender_params
|
||||
|
||||
gender_params.split(",").map { |gender| Gender.find_by_designation(gender) }.compact
|
||||
end
|
||||
end
|
||||
|
||||
def genders
|
||||
@genders ||= Gender.all
|
||||
end
|
||||
|
||||
def groups
|
||||
@groups = [Analyze::Group::Gender.new, Analyze::Group::Grade.new, Analyze::Group::Income.new,
|
||||
Analyze::Group::Race.new]
|
||||
end
|
||||
|
||||
def group
|
||||
@group ||= groups.reduce(groups.first) do |acc, group|
|
||||
group.slug == params[:group] ? group : acc
|
||||
end
|
||||
end
|
||||
|
||||
def slice
|
||||
@slice ||= slices.reduce(slices.first) do |acc, slice|
|
||||
slice.slug == params[:slice] ? slice : acc
|
||||
end
|
||||
end
|
||||
|
||||
def slices
|
||||
source.slices
|
||||
end
|
||||
|
||||
def source
|
||||
@source ||= sources.reduce(sources.first) do |acc, source|
|
||||
source.slug == params[:source] ? source : acc
|
||||
end
|
||||
end
|
||||
|
||||
def sources
|
||||
all_data_slices = [Analyze::Slice::AllData.new]
|
||||
all_data_source = Analyze::Source::AllData.new(slices: all_data_slices)
|
||||
|
||||
students_and_teachers = Analyze::Slice::StudentsAndTeachers.new
|
||||
students_by_group = Analyze::Slice::StudentsByGroup.new(races:, grades:)
|
||||
survey_data_slices = [students_and_teachers, students_by_group]
|
||||
survey_data_source = Analyze::Source::SurveyData.new(slices: survey_data_slices)
|
||||
|
||||
@sources = [all_data_source, survey_data_source]
|
||||
end
|
||||
|
||||
def grades
|
||||
@grades ||= SurveyItemResponse.where(school:, academic_year:)
|
||||
.where.not(grade: nil)
|
||||
.group(:grade)
|
||||
.select(:response_id)
|
||||
.distinct(:response_id)
|
||||
.count.reject do |_key, value|
|
||||
value < 10
|
||||
end.keys
|
||||
end
|
||||
|
||||
def incomes
|
||||
@incomes ||= Income.all
|
||||
end
|
||||
|
||||
def selected_incomes
|
||||
@selected_incomes ||= begin
|
||||
income_params = params[:incomes]
|
||||
return incomes unless income_params
|
||||
|
||||
income_params.split(",").map { |income| Income.find_by_slug(income) }.compact
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
module Analyze
|
||||
class Ui
|
||||
attr_reader :params
|
||||
def initialize(params:)
|
||||
@params = params
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AnalyzeBarPresenter
|
||||
include AnalyzeHelper
|
||||
attr_reader :score, :x_position, :academic_year, :measure_id, :measure, :color
|
||||
|
||||
MINIMUM_BAR_HEIGHT = 2
|
||||
|
||||
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
|
||||
benchmark_height = analyze_zone_height * 2
|
||||
case zone.type
|
||||
when :ideal, :approval
|
||||
benchmark_height - bar_height_percentage
|
||||
else
|
||||
benchmark_height
|
||||
end
|
||||
end
|
||||
|
||||
def bar_color
|
||||
"fill-#{zone.type}"
|
||||
end
|
||||
|
||||
def bar_height_percentage
|
||||
bar_height = send("#{zone.type}_bar_height_percentage") || 0
|
||||
enforce_minimum_height(bar_height:)
|
||||
end
|
||||
|
||||
def percentage
|
||||
low_benchmark = zone.low_benchmark
|
||||
(score.average - low_benchmark) / (zone.high_benchmark - 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
|
||||
average = score.average || 0
|
||||
|
||||
average.round(6)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def enforce_minimum_height(bar_height:)
|
||||
bar_height < MINIMUM_BAR_HEIGHT ? MINIMUM_BAR_HEIGHT : bar_height
|
||||
end
|
||||
|
||||
def ideal_bar_height_percentage
|
||||
(percentage * zone_height_percentage + zone_height_percentage) * 100
|
||||
end
|
||||
|
||||
def approval_bar_height_percentage
|
||||
(percentage * zone_height_percentage) * 100
|
||||
end
|
||||
|
||||
def growth_bar_height_percentage
|
||||
((1 - percentage) * zone_height_percentage) * 100
|
||||
end
|
||||
|
||||
def watch_bar_height_percentage
|
||||
((1 - percentage) * zone_height_percentage + zone_height_percentage) * 100
|
||||
end
|
||||
|
||||
def warning_bar_height_percentage
|
||||
((1 - percentage) * zone_height_percentage + zone_height_percentage + zone_height_percentage) * 100
|
||||
end
|
||||
end
|
||||
|
|
@ -1,57 +1,34 @@
|
|||
require "fileutils"
|
||||
class Cleaner
|
||||
attr_reader :input_filepath, :output_filepath, :log_filepath, :clean_csv, :log_csv
|
||||
attr_reader :input_filepath, :output_filepath, :log_filepath, :disaggregation_filepath
|
||||
|
||||
def initialize(input_filepath:, output_filepath:, log_filepath:)
|
||||
def initialize(input_filepath:, output_filepath:, log_filepath:, disaggregation_filepath:)
|
||||
@input_filepath = input_filepath
|
||||
@output_filepath = output_filepath
|
||||
@log_filepath = log_filepath
|
||||
@disaggregation_filepath = disaggregation_filepath
|
||||
initialize_directories
|
||||
end
|
||||
|
||||
def initialize_directories
|
||||
create_ouput_directory
|
||||
create_log_directory
|
||||
end
|
||||
|
||||
def clean
|
||||
Dir.glob(Rails.root.join(input_filepath, "*.csv")).each do |filepath|
|
||||
puts filepath
|
||||
File.open(filepath) do |file|
|
||||
clean_csv = []
|
||||
log_csv = []
|
||||
data = []
|
||||
processed_data = process_raw_file(file:, disaggregation_data:)
|
||||
processed_data in [headers, clean_csv, log_csv, data]
|
||||
return if data.empty?
|
||||
|
||||
headers = CSV.parse(file.first).first
|
||||
filtered_headers = remove_unwanted_headers(headers:)
|
||||
log_headers = (filtered_headers + ["Valid Duration?", "Valid Progress?", "Valid Grade?",
|
||||
"Valid Standard Deviation?"]).flatten
|
||||
|
||||
clean_csv << filtered_headers
|
||||
log_csv << log_headers
|
||||
|
||||
all_survey_items = survey_items(headers:)
|
||||
|
||||
file.lazy.each_slice(1000) do |lines|
|
||||
CSV.parse(lines.join, headers:).map do |row|
|
||||
values = SurveyItemValues.new(row:, headers:, genders:,
|
||||
survey_items: all_survey_items, schools:)
|
||||
next unless values.valid_school?
|
||||
|
||||
data << values
|
||||
values.valid? ? clean_csv << values.to_a : log_csv << (values.to_a << values.valid_duration?.to_s << values.valid_progress?.to_s << values.valid_grade?.to_s << values.valid_sd?.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
unless data.empty?
|
||||
filename = filename(headers:, data:)
|
||||
write_csv(data: clean_csv, output_filepath:, filename:)
|
||||
write_csv(data: log_csv, output_filepath: log_filepath, prefix: "removed.", filename:)
|
||||
end
|
||||
filename = filename(headers:, data:)
|
||||
write_csv(data: clean_csv, output_filepath:, filename:)
|
||||
write_csv(data: log_csv, output_filepath: log_filepath, prefix: "removed.", filename:)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def disaggregation_data
|
||||
@disaggregation_data ||= DisaggregationLoader.new(path: disaggregation_filepath).load
|
||||
end
|
||||
|
||||
def filename(headers:, data:)
|
||||
survey_item_ids = headers.filter(&:present?).filter do |header|
|
||||
header.start_with?("s-", "t-")
|
||||
|
|
@ -66,6 +43,41 @@ class Cleaner
|
|||
districts.join(".").to_s + "." + survey_type.to_s + "." + range + ".csv"
|
||||
end
|
||||
|
||||
def process_raw_file(file:, disaggregation_data:)
|
||||
clean_csv = []
|
||||
log_csv = []
|
||||
data = []
|
||||
|
||||
headers = (CSV.parse(file.first).first << "Raw Income") << "Income"
|
||||
filtered_headers = remove_unwanted_headers(headers:)
|
||||
log_headers = (filtered_headers + ["Valid Duration?", "Valid Progress?", "Valid Grade?",
|
||||
"Valid Standard Deviation?"]).flatten
|
||||
|
||||
clean_csv << filtered_headers
|
||||
log_csv << log_headers
|
||||
|
||||
all_survey_items = survey_items(headers:)
|
||||
|
||||
file.lazy.each_slice(1000) do |lines|
|
||||
CSV.parse(lines.join, headers:).map do |row|
|
||||
values = SurveyItemValues.new(row:, headers:, genders:,
|
||||
survey_items: all_survey_items, schools:, disaggregation_data:)
|
||||
next unless values.valid_school?
|
||||
|
||||
data << values
|
||||
values.valid? ? clean_csv << values.to_a : log_csv << (values.to_a << values.valid_duration?.to_s << values.valid_progress?.to_s << values.valid_grade?.to_s << values.valid_sd?.to_s)
|
||||
end
|
||||
end
|
||||
[headers, clean_csv, log_csv, data]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize_directories
|
||||
create_ouput_directory
|
||||
create_log_directory
|
||||
end
|
||||
|
||||
def remove_unwanted_headers(headers:)
|
||||
headers.to_set.to_a.compact.reject do |item|
|
||||
item.start_with? "Q"
|
||||
|
|
@ -81,34 +93,19 @@ class Cleaner
|
|||
File.write(output_filepath.join(prefix + filename), csv)
|
||||
end
|
||||
|
||||
def process_row(row:)
|
||||
clean_csv << row.to_csv
|
||||
log_csv << row.to_csv
|
||||
end
|
||||
|
||||
def schools
|
||||
@schools ||= School.school_hash
|
||||
end
|
||||
|
||||
def genders
|
||||
@genders ||= begin
|
||||
gender_hash = {}
|
||||
|
||||
Gender.all.each do |gender|
|
||||
gender_hash[gender.qualtrics_code] = gender
|
||||
end
|
||||
gender_hash
|
||||
end
|
||||
@genders ||= Gender.gender_hash
|
||||
end
|
||||
|
||||
def survey_items(headers:)
|
||||
@survey_items ||= SurveyItem.where(survey_item_id: get_survey_item_ids_from_headers(headers:))
|
||||
end
|
||||
|
||||
def get_survey_item_ids_from_headers(headers:)
|
||||
headers
|
||||
.filter(&:present?)
|
||||
.filter { |header| header.start_with? "t-", "s-" }
|
||||
survey_item_ids = headers
|
||||
.filter(&:present?)
|
||||
.filter { |header| header.start_with? "t-", "s-" }
|
||||
@survey_items ||= SurveyItem.where(survey_item_id: survey_item_ids)
|
||||
end
|
||||
|
||||
def create_ouput_directory
|
||||
|
|
@ -118,8 +115,4 @@ class Cleaner
|
|||
def create_log_directory
|
||||
FileUtils.mkdir_p log_filepath
|
||||
end
|
||||
|
||||
def create_file(path:, filename:)
|
||||
FileUtils.touch path.join(filename)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ class DemographicLoader
|
|||
CSV.parse(File.read(filepath), headers: true) do |row|
|
||||
process_race(row:)
|
||||
process_gender(row:)
|
||||
process_income(row:)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -30,6 +31,13 @@ class DemographicLoader
|
|||
gender = ::Gender.find_or_create_by!(qualtrics_code:, designation:)
|
||||
gender.save
|
||||
end
|
||||
|
||||
def self.process_income(row:)
|
||||
designation = row['Income']
|
||||
return unless designation
|
||||
|
||||
Income.find_or_create_by!(designation:)
|
||||
end
|
||||
end
|
||||
|
||||
class KnownRace
|
||||
|
|
|
|||
30
app/services/disaggregation_loader.rb
Normal file
30
app/services/disaggregation_loader.rb
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
class DisaggregationLoader
|
||||
attr_reader :path
|
||||
|
||||
def initialize(path:)
|
||||
@path = path
|
||||
initialize_directory
|
||||
end
|
||||
|
||||
def load
|
||||
data = {}
|
||||
Dir.glob(Rails.root.join(path, "*.csv")).each do |filepath|
|
||||
puts filepath
|
||||
File.open(filepath) do |file|
|
||||
headers = CSV.parse(file.first).first
|
||||
|
||||
file.lazy.each_slice(1000) do |lines|
|
||||
CSV.parse(lines.join, headers:).map do |row|
|
||||
values = DisaggregationRow.new(row:, headers:)
|
||||
data[[values.lasid, values.district, values.academic_year]] = values
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
data
|
||||
end
|
||||
|
||||
def initialize_directory
|
||||
FileUtils.mkdir_p(path)
|
||||
end
|
||||
end
|
||||
35
app/services/disaggregation_row.rb
Normal file
35
app/services/disaggregation_row.rb
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
class DisaggregationRow
|
||||
attr_reader :row, :headers
|
||||
|
||||
def initialize(row:, headers:)
|
||||
@row = row
|
||||
@headers = headers
|
||||
end
|
||||
|
||||
def district
|
||||
@district ||= value_from(pattern: /District/i)
|
||||
end
|
||||
|
||||
def academic_year
|
||||
@academic_year ||= value_from(pattern: /Academic\s*Year/i)
|
||||
end
|
||||
|
||||
def income
|
||||
@income ||= value_from(pattern: /Low\s*Income/i)
|
||||
end
|
||||
|
||||
def lasid
|
||||
@lasid ||= value_from(pattern: /LASID/i)
|
||||
end
|
||||
|
||||
def value_from(pattern:)
|
||||
output = nil
|
||||
matches = headers.select do |header|
|
||||
pattern.match(header)
|
||||
end.map { |item| item.delete("\n") }
|
||||
matches.each do |match|
|
||||
output ||= row[match]
|
||||
end
|
||||
output
|
||||
end
|
||||
end
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
class SurveyItemValues
|
||||
attr_reader :row, :headers, :genders, :survey_items, :schools
|
||||
attr_reader :row, :headers, :genders, :survey_items, :schools, :disaggregation_data
|
||||
|
||||
def initialize(row:, headers:, genders:, survey_items:, schools:)
|
||||
def initialize(row:, headers:, genders:, survey_items:, schools:, disaggregation_data: nil)
|
||||
@row = row
|
||||
@headers = headers
|
||||
@genders = genders
|
||||
@survey_items = survey_items
|
||||
@schools = schools
|
||||
@disaggregation_data = disaggregation_data
|
||||
end
|
||||
|
||||
def dese_id?
|
||||
|
|
@ -93,6 +94,37 @@ class SurveyItemValues
|
|||
genders[gender_code]
|
||||
end
|
||||
|
||||
def lasid
|
||||
@lasid ||= value_from(pattern: /LASID/i)
|
||||
end
|
||||
|
||||
def raw_income
|
||||
@raw_income ||= value_from(pattern: /Low\s*Income|Raw\s*Income/i)
|
||||
return @raw_income if @raw_income.present?
|
||||
|
||||
return "Unknown" unless disaggregation_data.present?
|
||||
|
||||
disaggregation = disaggregation_data[[lasid, district.name, academic_year.range]]
|
||||
return "Unknown" unless disaggregation.present?
|
||||
|
||||
@raw_income ||= disaggregation.income
|
||||
end
|
||||
|
||||
# TODO: - rename these cases
|
||||
def income
|
||||
@income ||= value_from(pattern: /^Income$/i)
|
||||
return @income if @income.present?
|
||||
|
||||
@income ||= case raw_income
|
||||
in /Free\s*Lunch|Reduced\s*Lunch|Low\s*Income/i
|
||||
"Economically Disadvantaged - Y"
|
||||
in /Not\s*Eligible/i
|
||||
"Economically Disadvantaged - N"
|
||||
else
|
||||
"Unknown"
|
||||
end
|
||||
end
|
||||
|
||||
def value_from(pattern:)
|
||||
output = nil
|
||||
matches = headers.select do |header|
|
||||
|
|
@ -106,10 +138,12 @@ class SurveyItemValues
|
|||
|
||||
def to_a
|
||||
copy_likert_scores_from_variant_survey_items
|
||||
row["Income"] = income
|
||||
row["Raw Income"] = raw_income
|
||||
headers.select(&:present?)
|
||||
.reject { |key, _value| key.start_with? "Q" }
|
||||
.reject { |key, _value| key.end_with? "-1" }
|
||||
.map { |header| row[header] }
|
||||
.reject { |key, _value| key.start_with? "Q" }
|
||||
.reject { |key, _value| key.end_with? "-1" }
|
||||
.map { |header| row[header] }
|
||||
end
|
||||
|
||||
def duration
|
||||
|
|
@ -122,17 +156,17 @@ class SurveyItemValues
|
|||
|
||||
def respondent_type
|
||||
return :teacher if headers
|
||||
.filter(&:present?)
|
||||
.filter { |header| header.start_with? "t-" }.count > 0
|
||||
.filter(&:present?)
|
||||
.filter { |header| header.start_with? "t-" }.count > 0
|
||||
|
||||
:student
|
||||
end
|
||||
|
||||
def survey_type
|
||||
survey_item_ids = headers
|
||||
.filter(&:present?)
|
||||
.reject { |header| header.end_with?("-1") }
|
||||
.filter { |header| header.start_with?("t-", "s-") }
|
||||
.filter(&:present?)
|
||||
.reject { |header| header.end_with?("-1") }
|
||||
.filter { |header| header.start_with?("t-", "s-") }
|
||||
|
||||
SurveyItem.survey_type(survey_item_ids:)
|
||||
end
|
||||
|
|
@ -200,3 +234,4 @@ class SurveyItemValues
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -7,12 +7,13 @@ class SurveyResponsesDataLoader
|
|||
headers_array = CSV.parse(headers).first
|
||||
genders = Gender.gender_hash
|
||||
schools = School.school_hash
|
||||
incomes = Income.by_designation
|
||||
all_survey_items = survey_items(headers:)
|
||||
|
||||
file.lazy.each_slice(500) do |lines|
|
||||
survey_item_responses = CSV.parse(lines.join, headers:).map do |row|
|
||||
process_row(row: SurveyItemValues.new(row:, headers: headers_array, genders:, survey_items: all_survey_items, schools:),
|
||||
rules:)
|
||||
rules:, incomes:)
|
||||
end
|
||||
SurveyItemResponse.import survey_item_responses.compact.flatten, batch_size: 500
|
||||
end
|
||||
|
|
@ -24,6 +25,7 @@ class SurveyResponsesDataLoader
|
|||
headers_array = CSV.parse(headers).first
|
||||
genders = Gender.gender_hash
|
||||
schools = School.school_hash
|
||||
incomes = Income.by_designation
|
||||
all_survey_items = survey_items(headers:)
|
||||
|
||||
survey_item_responses = []
|
||||
|
|
@ -34,7 +36,7 @@ class SurveyResponsesDataLoader
|
|||
|
||||
CSV.parse(line, headers:).map do |row|
|
||||
survey_item_responses << process_row(row: SurveyItemValues.new(row:, headers: headers_array, genders:, survey_items: all_survey_items, schools:),
|
||||
rules:)
|
||||
rules:, incomes:)
|
||||
end
|
||||
|
||||
row_count += 1
|
||||
|
|
@ -50,7 +52,7 @@ class SurveyResponsesDataLoader
|
|||
|
||||
private
|
||||
|
||||
def self.process_row(row:, rules:)
|
||||
def self.process_row(row:, rules:, incomes:)
|
||||
return unless row.dese_id?
|
||||
return unless row.school.present?
|
||||
|
||||
|
|
@ -58,10 +60,10 @@ class SurveyResponsesDataLoader
|
|||
return if rule.new(row:).skip_row?
|
||||
end
|
||||
|
||||
process_survey_items(row:)
|
||||
process_survey_items(row:, incomes:)
|
||||
end
|
||||
|
||||
def self.process_survey_items(row:)
|
||||
def self.process_survey_items(row:, incomes:)
|
||||
row.survey_items.map do |survey_item|
|
||||
likert_score = row.likert_score(survey_item_id: survey_item.survey_item_id) || next
|
||||
|
||||
|
|
@ -70,19 +72,20 @@ class SurveyResponsesDataLoader
|
|||
next
|
||||
end
|
||||
response = row.survey_item_response(survey_item:)
|
||||
create_or_update_response(survey_item_response: response, likert_score:, row:, survey_item:)
|
||||
create_or_update_response(survey_item_response: response, likert_score:, row:, survey_item:, incomes:)
|
||||
end.compact
|
||||
end
|
||||
|
||||
def self.create_or_update_response(survey_item_response:, likert_score:, row:, survey_item:)
|
||||
def self.create_or_update_response(survey_item_response:, likert_score:, row:, survey_item:, incomes:)
|
||||
gender = row.gender
|
||||
grade = row.grade
|
||||
income = incomes[row.income]
|
||||
if survey_item_response.present?
|
||||
survey_item_response.update!(likert_score:, grade:, gender:, recorded_date: row.recorded_date)
|
||||
survey_item_response.update!(likert_score:, grade:, gender:, recorded_date: row.recorded_date, income:)
|
||||
[]
|
||||
else
|
||||
SurveyItemResponse.new(response_id: row.response_id, academic_year: row.academic_year, school: row.school, survey_item:,
|
||||
likert_score:, grade:, gender:, recorded_date: row.recorded_date)
|
||||
likert_score:, grade:, gender:, recorded_date: row.recorded_date, income:)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
17
app/views/analyze/_checkboxes.html.erb
Normal file
17
app/views/analyze/_checkboxes.html.erb
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<div class="d-flex align-items-center mx-5">
|
||||
<input
|
||||
id="<%= id %>"
|
||||
class="m-3 <%= name %>-checkbox form-check-input"
|
||||
type="checkbox"
|
||||
name="<%= name %>-checkbox"
|
||||
value="<%= base_url %>"
|
||||
data-action="click->analyze#refresh"
|
||||
<%= selected_items.include?(item) ? "checked" : "" %>
|
||||
<%= @presenter.graph.slug == 'students-and-teachers' || @presenter.source.slug == 'all-data' ? "disabled" : "" %>
|
||||
<%= @presenter.group.slug == name ? "" : "hidden" %>>
|
||||
|
||||
<label for="<%= id %>"
|
||||
<%= @presenter.group.slug == name ? "" : "hidden" %>>
|
||||
<%= label_text %>
|
||||
</label>
|
||||
</div>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<h3 class="sub-header-4 mt-5">Data Filters</h3>
|
||||
<div class="bg-gray p-3" data-controller="analyze">
|
||||
|
||||
<% @sources.each do |source| %>
|
||||
<% @presenter.sources.each do |source| %>
|
||||
|
||||
<input type="radio"
|
||||
id="<%= source.slug %>"
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
name="source"
|
||||
value="<%= base_url %>"
|
||||
data-action="click->analyze#refresh"
|
||||
<%= source.slug == @source.slug ? "checked" : "" %>>
|
||||
<%= source.slug == @presenter.source.slug ? "checked" : "" %>>
|
||||
<label for="<%= source.slug %>"><%= source.to_s %></label>
|
||||
|
||||
<% source.slices.each do | slice | %>
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
name="slice"
|
||||
value="<%= base_url %>"
|
||||
data-action="click->analyze#refresh"
|
||||
<%= slice.slug == @slice.slug ? "checked" : "" %>
|
||||
<%= slice.slug == @presenter.slice.slug ? "checked" : "" %>
|
||||
<%= slice.slug == "all-data" ? "hidden" : "" %>>
|
||||
|
||||
<label for="<%= slice.slug %>"
|
||||
|
|
@ -34,8 +34,8 @@
|
|||
</div>
|
||||
|
||||
<script>
|
||||
window.source = "<%= @source.slug %>";
|
||||
window.slice = "<%= @slice.slug %>";
|
||||
window.group = "<%= @group.slug %>";
|
||||
window.graph = "<%= @graph.slug %>";
|
||||
window.source = "<%= @presenter.source.slug %>";
|
||||
window.slice = "<%= @presenter.slice.slug %>";
|
||||
window.group = "<%= @presenter.group.slug %>";
|
||||
window.graph = "<%= @presenter.graph.slug %>";
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@
|
|||
<p>Select a category & subcategory to analyze measure-level results</p>
|
||||
<select id="select-category" class="mx-3 form-select" data-id="category-dropdown" data-action="analyze#refresh">
|
||||
<% categories.each do |category| %>
|
||||
<option value="<%= analyze_category_link(district: district, school: school, academic_year: academic_year, category: category) %>" <%= category.id == @category.id ? "selected": "" %>><%= "#{category.category_id}: #{category.name}" %></option>
|
||||
<option value="<%= analyze_category_link(district: district, school: school, academic_year: academic_year, category: category) %>" <%= category.id == @presenter.category.id ? "selected": "" %>><%= "#{category.category_id}: #{category.name}" %></option>
|
||||
<% end %>
|
||||
</select>
|
||||
<select id="select-subcategory" class="mx-3 form-select mt-3" data-id="subcategory-dropdown" data-action="analyze#refresh">
|
||||
<% subcategories.each do |subcategory| %>
|
||||
<option value="<%= analyze_subcategory_link(district: district, school: school, academic_year: academic_year, category: category, subcategory: subcategory) %>" <%= subcategory.subcategory_id == @subcategory.subcategory_id ? "selected": "" %>>
|
||||
<option value="<%= analyze_subcategory_link(district: district, school: school, academic_year: academic_year, category: category, subcategory: subcategory) %>" <%= subcategory.subcategory_id == @presenter.subcategory.subcategory_id ? "selected": "" %>>
|
||||
<%= "#{subcategory.subcategory_id}: #{subcategory.name}" %>
|
||||
</option>
|
||||
<% end %>
|
||||
|
|
|
|||
|
|
@ -1,67 +1,23 @@
|
|||
<select id="select-group" name="group" class="mx-4 form-select" data-id="group-dropdown" data-action="analyze#refresh">
|
||||
<% @groups.each do |group| %>
|
||||
<option id="<%= group.slug %>" name="group-option" value="<%= base_url %>" <%= group.slug == @group.slug ? "Selected": "" %>><%= group.name %> </option>
|
||||
<% @presenter.groups.each do |group| %>
|
||||
<option id="<%= group.slug %>" name="group-option" value="<%= base_url %>" <%= group.slug == @presenter.group.slug ? "Selected": "" %>><%= group.name %> </option>
|
||||
<% end %>
|
||||
</select>
|
||||
|
||||
<p class="sub-header-5 mx-4 mt-3 font-size-14"> Select a group </p>
|
||||
|
||||
<% @races.each do |race| %>
|
||||
<div class="d-flex align-items-center mx-5">
|
||||
<input
|
||||
id="<%= race.slug %>"
|
||||
class="m-3 race-checkbox form-check-input"
|
||||
type="checkbox"
|
||||
name="race-checkbox"
|
||||
value="<%= base_url %>"
|
||||
data-action="click->analyze#refresh"
|
||||
<%= @selected_races.map(&:slug).include?(race.slug) ? "checked" : "" %>
|
||||
<%= @graph.slug == 'students-and-teachers' || @source.slug == 'all-data' ? "disabled" : "" %>
|
||||
<%= @group.slug == 'race' ? "" : "hidden" %>>
|
||||
|
||||
<label for="<%= race.qualtrics_code %>"
|
||||
<%= @group.slug == 'race' ? "" : "hidden" %>>
|
||||
<%= race.designation %>
|
||||
</label>
|
||||
</div>
|
||||
<% @presenter.races.each do |race| %>
|
||||
<%= render(partial: "checkboxes", locals: {id: "race-#{race.slug}", item: race, selected_items: @presenter.selected_races, name: "race", label_text: race.designation}) %>
|
||||
<% end %>
|
||||
|
||||
<% @grades.each do |grade| %>
|
||||
<div class="d-flex align-items-center mx-5">
|
||||
<input
|
||||
id="grade-<%= grade %>"
|
||||
class="m-3 grade-checkbox form-check-input"
|
||||
type="checkbox"
|
||||
name="grade-checkbox"
|
||||
value="<%= base_url %>"
|
||||
data-action="click->analyze#refresh"
|
||||
<%= @selected_grades.include?(grade) ? "checked" : "" %>
|
||||
<%= @graph.slug == 'students-and-teachers' || @source.slug == 'all-data' ? "disabled" : "" %>
|
||||
<%= @group.slug == 'grade' ? "" : "hidden" %>>
|
||||
|
||||
<label for="grade-<%= grade %>"
|
||||
<%= @group.slug == 'grade' ? "" : "hidden" %>>
|
||||
<%= grade %>
|
||||
</label>
|
||||
</div>
|
||||
<% @presenter.grades.each do |grade| %>
|
||||
<%= render(partial: "checkboxes", locals: {id: "grade-#{grade}", item: grade, selected_items: @presenter.selected_grades, name: "grade", label_text: grade}) %>
|
||||
<% end %>
|
||||
|
||||
<% @genders.each do |gender| %>
|
||||
<div class="d-flex align-items-center mx-5">
|
||||
<input
|
||||
id="gender-<%= gender.designation %>"
|
||||
class="m-3 gender-checkbox form-check-input"
|
||||
type="checkbox"
|
||||
name="gender-checkbox"
|
||||
value="<%= base_url %>"
|
||||
data-action="click->analyze#refresh"
|
||||
<%= @selected_genders.include?(gender) ? "checked" : "" %>
|
||||
<%= @graph.slug == 'students-and-teachers' || @source.slug == 'all-data' ? "disabled" : "" %>
|
||||
<%= @group.slug == 'gender' ? "" : "hidden" %>>
|
||||
|
||||
<label for="gender-<%= gender %>"
|
||||
<%= @group.slug == 'gender' ? "" : "hidden" %>>
|
||||
<%= gender.designation %>
|
||||
</label>
|
||||
</div>
|
||||
<% @presenter.genders.each do |gender| %>
|
||||
<%= render(partial: "checkboxes", locals: {id: "gender-#{gender.designation}", item: gender, selected_items: @presenter.selected_genders, name: "gender", label_text: gender.designation}) %>
|
||||
<% end %>
|
||||
|
||||
<% @presenter.incomes.each do |income| %>
|
||||
<%= render(partial: "checkboxes", locals: {id: "income-#{income.slug}", item: income, selected_items: @presenter.selected_incomes, name: "income", label_text: income.designation}) %>
|
||||
<% end %>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<svg width="100%" height="<%= svg_height %>">
|
||||
<%= render partial: "graph_background", locals: {background: @background} %>
|
||||
|
||||
<% number_of_columns = @graph.columns.length %>
|
||||
<% @graph.columns.each_with_index do |column, index| %>
|
||||
<% p = column.new(measure: measure, school: @school, academic_years: @selected_academic_years, position: index , number_of_columns:) %>
|
||||
<% number_of_columns = @presenter.graph.columns.length %>
|
||||
<% @presenter.graph.columns.each_with_index do |column, index| %>
|
||||
<% p = column.new(measure: measure, school: @school, academic_years: @presenter.selected_academic_years, position: index , number_of_columns:) %>
|
||||
<%= render partial: "grouped_bar_column", locals: {column: p} %>
|
||||
<% end %>
|
||||
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 463 B After Width: | Height: | Size: 493 B |
|
|
@ -10,7 +10,7 @@
|
|||
data-action="click->analyze#refresh"
|
||||
<% empty_dataset = empty_dataset?(measures: measures, school: school, academic_year: year) %>
|
||||
<% empty_survey_dataset = empty_survey_dataset?(measures: measures, school: school, academic_year: year) %>
|
||||
<% if @graph.slug == 'all-data' %>
|
||||
<% if graph.slug == 'all-data' %>
|
||||
<%= empty_dataset ? "disabled" : "" %>
|
||||
<% else %>
|
||||
<%= empty_survey_dataset ? "disabled" : "" %>
|
||||
|
|
@ -18,13 +18,13 @@
|
|||
>
|
||||
<label class="px-3" for="<%= year.range %>"><%= year.range %></label><br>
|
||||
<div class="bg-color-blue px-3" style="width:20px;height:20px;background-color:<%= colors[index] %>;"></div>
|
||||
<% if @graph.slug == 'all-data' && empty_dataset %>
|
||||
<% if graph.slug == 'all-data' && empty_dataset %>
|
||||
<i class="fa-solid fa-circle-exclamation px-3"
|
||||
data-bs-toggle="popover" data-bs-placement="right"
|
||||
data-bs-content="No admin data OR teacher and student survey response rates below <%= ResponseRateCalculator::TEACHER_RATE_THRESHOLD %>%">
|
||||
</i>
|
||||
<% end %>
|
||||
<% if @graph.slug != 'all-data' && empty_survey_dataset %>
|
||||
<% if graph.slug != 'all-data' && empty_survey_dataset %>
|
||||
<i class="fa-solid fa-circle-exclamation px-3"
|
||||
data-bs-toggle="popover" data-bs-placement="right"
|
||||
data-bs-content="Teacher and student survey response rates below <%= ResponseRateCalculator::TEACHER_RATE_THRESHOLD %>%">
|
||||
|
|
|
|||
|
|
@ -3,19 +3,19 @@
|
|||
<% end %>
|
||||
<div class="graph-content">
|
||||
<div class="breadcrumbs sub-header-4">
|
||||
<%= @category.category_id %>:<%= @category.name %> > <%= @subcategory.subcategory_id %>:<%= @subcategory.name %>
|
||||
<%= @presenter.category.category_id %>:<%= @presenter.category.name %> > <%= @presenter.subcategory.subcategory_id %>:<%= @presenter.subcategory.name %>
|
||||
</div>
|
||||
<hr>
|
||||
</div>
|
||||
<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">
|
||||
<%= 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: "data_filters", locals: {district: @district, school: @school, academic_year: @academic_year, category: @category, subcategory: @subcategory} %>
|
||||
<%= render partial: "focus_area", locals: {categories: @presenter.categories, district: @district, school: @school, academic_year: @academic_year, category: @presenter.category, subcategories: @presenter.subcategories} %>
|
||||
<%= render partial: "school_years", locals: {available_academic_years: @presenter.academic_years, selected_academic_years: @presenter.selected_academic_years, district: @district, school: @school, academic_year: @academic_year, category: @presenter.category, subcategory: @presenter.subcategory, measures: @presenter.measures, graph: @presenter.graph} %>
|
||||
<%= render partial: "data_filters", locals: {district: @district, school: @school, academic_year: @academic_year, category: @presenter.category, subcategory: @presenter.subcategory} %>
|
||||
</div>
|
||||
<% cache [@subcategory, @school, @selected_academic_years, @graph, @selected_races, @race_score_timestamp, @selected_grades, @grades, @selected_genders, @genders] do %>
|
||||
<% cache [@presenter.subcategory, @school, @presenter.selected_academic_years, @presenter.graph, @presenter.selected_races, @presenter.selected_grades, @presenter.grades, @presenter.selected_genders, @presenter.genders] do %>
|
||||
<div class="bg-color-white flex-grow-1 col-9">
|
||||
<% @measures.each do |measure| %>
|
||||
<% @presenter.measures.each do |measure| %>
|
||||
<section class="mb-6">
|
||||
<p class="construct-id">Measure <%= measure.measure_id %></p>
|
||||
<h2> <%= measure.name %> </h2>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
Race Qualtrics Code,Race/Ethnicity,Gender Qualtrics Code,Sex/Gender
|
||||
1,American Indian or Alaskan Native,2,Male
|
||||
2,Asian or Pacific Islander,1,Female
|
||||
3,Black or African American,4,Non-Binary
|
||||
4,Hispanic or Latinx,99,Unknown
|
||||
5,White or Caucasian,,
|
||||
6,Prefer not to disclose,,
|
||||
7,Prefer to self-describe,,
|
||||
8,Middle Eastern,,
|
||||
99,Race/Ethnicity Not Listed,,
|
||||
100,Multiracial,,
|
||||
Race Qualtrics Code,Race/Ethnicity,Gender Qualtrics Code,Sex/Gender,Income
|
||||
1,American Indian or Alaskan Native,2,Male,Economically Disadvantaged - N
|
||||
2,Asian or Pacific Islander,1,Female,Economically Disadvantaged - Y
|
||||
3,Black or African American,4,Non-Binary,Unknown
|
||||
4,Hispanic or Latinx,99,Unknown,
|
||||
5,White or Caucasian,,,
|
||||
6,Prefer not to disclose,,,
|
||||
7,Prefer to self-describe,,,
|
||||
8,Middle Eastern,,,
|
||||
99,Race/Ethnicity Not Listed,,,
|
||||
100,Multiracial,,,
|
||||
|
|
|
|||
|
9
db/migrate/20230620221633_create_incomes.rb
Normal file
9
db/migrate/20230620221633_create_incomes.rb
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
class CreateIncomes < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
create_table :incomes do |t|
|
||||
t.string :designation
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
class AddIncomeToSurveyItemResponses < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
add_reference :survey_item_responses, :income, foreign_key: true
|
||||
end
|
||||
end
|
||||
6
db/migrate/20230630215110_add_slug_to_income.rb
Normal file
6
db/migrate/20230630215110_add_slug_to_income.rb
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
class AddSlugToIncome < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
add_column :incomes, :slug, :string
|
||||
add_index :incomes, :slug, unique: true
|
||||
end
|
||||
end
|
||||
32
db/schema.rb
32
db/schema.rb
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[7.0].define(version: 2023_06_22_224103) do
|
||||
ActiveRecord::Schema[7.0].define(version: 2023_08_07_222503) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pg_stat_statements"
|
||||
enable_extension "plpgsql"
|
||||
|
|
@ -77,6 +77,14 @@ ActiveRecord::Schema[7.0].define(version: 2023_06_22_224103) do
|
|||
t.datetime "updated_at", null: false
|
||||
end
|
||||
|
||||
create_table "incomes", force: :cascade do |t|
|
||||
t.string "designation"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.string "slug"
|
||||
t.index ["slug"], name: "index_incomes_on_slug", unique: true
|
||||
end
|
||||
|
||||
create_table "legacy_attempts", id: :serial, force: :cascade do |t|
|
||||
t.integer "recipient_id"
|
||||
t.integer "schedule_id"
|
||||
|
|
@ -297,21 +305,6 @@ ActiveRecord::Schema[7.0].define(version: 2023_06_22_224103) do
|
|||
t.index ["subcategory_id"], name: "index_measures_on_subcategory_id"
|
||||
end
|
||||
|
||||
create_table "race_scores", force: :cascade do |t|
|
||||
t.bigint "measure_id", null: false
|
||||
t.bigint "school_id", null: false
|
||||
t.bigint "academic_year_id", null: false
|
||||
t.bigint "race_id", null: false
|
||||
t.float "average"
|
||||
t.boolean "meets_student_threshold"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["academic_year_id"], name: "index_race_scores_on_academic_year_id"
|
||||
t.index ["measure_id"], name: "index_race_scores_on_measure_id"
|
||||
t.index ["race_id"], name: "index_race_scores_on_race_id"
|
||||
t.index ["school_id"], name: "index_race_scores_on_school_id"
|
||||
end
|
||||
|
||||
create_table "races", force: :cascade do |t|
|
||||
t.string "designation"
|
||||
t.integer "qualtrics_code"
|
||||
|
|
@ -445,8 +438,10 @@ ActiveRecord::Schema[7.0].define(version: 2023_06_22_224103) do
|
|||
t.integer "grade"
|
||||
t.bigint "gender_id"
|
||||
t.datetime "recorded_date"
|
||||
t.bigint "income_id"
|
||||
t.index ["academic_year_id"], name: "index_survey_item_responses_on_academic_year_id"
|
||||
t.index ["gender_id"], name: "index_survey_item_responses_on_gender_id"
|
||||
t.index ["income_id"], name: "index_survey_item_responses_on_income_id"
|
||||
t.index ["response_id"], name: "index_survey_item_responses_on_response_id"
|
||||
t.index ["school_id", "academic_year_id", "survey_item_id"], name: "by_school_year_and_survey_item"
|
||||
t.index ["school_id", "academic_year_id"], name: "index_survey_item_responses_on_school_id_and_academic_year_id"
|
||||
|
|
@ -480,10 +475,6 @@ ActiveRecord::Schema[7.0].define(version: 2023_06_22_224103) do
|
|||
add_foreign_key "legacy_school_categories", "legacy_categories", column: "category_id"
|
||||
add_foreign_key "legacy_school_categories", "legacy_schools", column: "school_id"
|
||||
add_foreign_key "measures", "subcategories"
|
||||
add_foreign_key "race_scores", "academic_years"
|
||||
add_foreign_key "race_scores", "measures"
|
||||
add_foreign_key "race_scores", "races"
|
||||
add_foreign_key "race_scores", "schools"
|
||||
add_foreign_key "respondents", "academic_years"
|
||||
add_foreign_key "respondents", "schools"
|
||||
add_foreign_key "response_rates", "academic_years"
|
||||
|
|
@ -500,6 +491,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_06_22_224103) do
|
|||
add_foreign_key "subcategories", "categories"
|
||||
add_foreign_key "survey_item_responses", "academic_years"
|
||||
add_foreign_key "survey_item_responses", "genders"
|
||||
add_foreign_key "survey_item_responses", "incomes"
|
||||
add_foreign_key "survey_item_responses", "schools"
|
||||
add_foreign_key "survey_item_responses", "students"
|
||||
add_foreign_key "survey_item_responses", "survey_items"
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ namespace :one_off do
|
|||
desc "load a single file"
|
||||
task load_single_file: :environment do
|
||||
filepath = Rails.root.join("data", "survey_responses",
|
||||
"2021-22_revere_somerville_wareham_student_survey_responses.csv")
|
||||
"2021-22_revere_somerville_wareham_student_survey_responses.csv")
|
||||
puts "=====================> Loading data from csv at path: #{filepath}"
|
||||
SurveyResponsesDataLoader.load_data(filepath:)
|
||||
puts "=====================> Completed loading #{SurveyItemResponse.count} survey responses"
|
||||
|
|
@ -55,7 +55,7 @@ namespace :one_off do
|
|||
desc "load butler results for 2021-22"
|
||||
task load_butler: :environment do
|
||||
["2022-23_butler_student_survey_responses.csv",
|
||||
"2022-23_butler_teacher_survey_responses.csv"].each do |filepath|
|
||||
"2022-23_butler_teacher_survey_responses.csv"].each do |filepath|
|
||||
filepath = Rails.root.join("data", "survey_responses", filepath)
|
||||
puts "=====================> Loading data from csv at path: #{filepath}"
|
||||
SurveyResponsesDataLoader.load_data filepath:
|
||||
|
|
@ -68,7 +68,7 @@ namespace :one_off do
|
|||
desc "load winchester results for 2021-22"
|
||||
task load_winchester: :environment do
|
||||
["2021-22_winchester_student_survey_responses.csv",
|
||||
"2021-22_winchester_teacher_survey_responses.csv"].each do |filepath|
|
||||
"2021-22_winchester_teacher_survey_responses.csv"].each do |filepath|
|
||||
filepath = Rails.root.join("data", "survey_responses", filepath)
|
||||
puts "=====================> Loading data from csv at path: #{filepath}"
|
||||
SurveyResponsesDataLoader.load_data filepath:
|
||||
|
|
@ -114,7 +114,7 @@ namespace :one_off do
|
|||
District.all.map do |district|
|
||||
SurveyItem.all.map do |survey_item|
|
||||
[academic_year.range, survey_item.survey_item_id,
|
||||
survey_item.survey_item_responses.joins(:school).where("school.district": district, academic_year:).count, district.name]
|
||||
survey_item.survey_item_responses.joins(:school).where("school.district": district, academic_year:).count, district.name]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -164,23 +164,23 @@ namespace :one_off do
|
|||
puts "=====================> Completed loading #{Student.count - student_count} students. #{Student.count} total students"
|
||||
|
||||
puts "Resetting race scores"
|
||||
RaceScoreLoader.reset(fast_processing: true, academic_years:, schools: District.find_by_name("Wareham").schools)
|
||||
RaceScoreLoader.reset(fast_processing: false, academic_years: [AcademicYear.find_by_range("2022-23")], schools:)
|
||||
puts "=====================> Completed loading #{RaceScore.count} race scores"
|
||||
|
||||
District.all.each do |district|
|
||||
num_of_respondents = SurveyItemResponse.joins(school: :district).where(academic_year:,
|
||||
"schools.district": district).pluck(:response_id).uniq.count
|
||||
"schools.district": district).pluck(:response_id).uniq.count
|
||||
teacher_respondents = SurveyItemResponse.joins(school: :district).where(academic_year:,
|
||||
survey_item: SurveyItem.where("survey_item_id like ? ", "t-%"), "schools.district": district).pluck(:response_id).uniq.count
|
||||
survey_item: SurveyItem.where("survey_item_id like ? ", "t-%"), "schools.district": district).pluck(:response_id).uniq.count
|
||||
student_respondents = SurveyItemResponse.joins(school: :district).where(academic_year:,
|
||||
survey_item: SurveyItem.where("survey_item_id like ? ", "s-%"), "schools.district": district).pluck(:response_id).uniq.count
|
||||
survey_item: SurveyItem.where("survey_item_id like ? ", "s-%"), "schools.district": district).pluck(:response_id).uniq.count
|
||||
|
||||
response_count = SurveyItemResponse.joins(school: :district).where(academic_year:,
|
||||
"schools.district": district).count
|
||||
"schools.district": district).count
|
||||
student_response_count = SurveyItemResponse.joins(school: :district).joins(:survey_item).where(academic_year:,
|
||||
survey_item: SurveyItem.where("survey_item_id like ? ", "s-%"), "schools.district": district).count
|
||||
survey_item: SurveyItem.where("survey_item_id like ? ", "s-%"), "schools.district": district).count
|
||||
teacher_response_count = SurveyItemResponse.joins(school: :district).joins(:survey_item).where(academic_year:,
|
||||
survey_item: SurveyItem.where("survey_item_id like ? ", "t-%"), "schools.district": district).count
|
||||
survey_item: SurveyItem.where("survey_item_id like ? ", "t-%"), "schools.district": district).count
|
||||
puts "#{district.name} has #{num_of_respondents} respondents"
|
||||
puts "#{district.name} has #{teacher_respondents} teacher respondents"
|
||||
puts "#{district.name} has #{student_respondents} student respondents"
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
"debounce": "^1.2.1",
|
||||
"esbuild": "^0.18.12",
|
||||
"sass": "^1.43.4",
|
||||
"semver": "6.3.1"
|
||||
"semver": "^7.5.2"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "esbuild app/javascript/*.* --bundle --outdir=app/assets/builds",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
FactoryBot.define do
|
||||
factory :income do
|
||||
designation { "MyString" }
|
||||
end
|
||||
|
||||
factory :gender do
|
||||
qualtrics_code { 1 }
|
||||
designation { 'MyString' }
|
||||
|
|
|
|||
501
spec/fixtures/disaggregation/sample_maynard_disaggregation_data.csv
vendored
Normal file
501
spec/fixtures/disaggregation/sample_maynard_disaggregation_data.csv
vendored
Normal file
|
|
@ -0,0 +1,501 @@
|
|||
District,Academic Year,LASID,HispanicLatino,Race,Gender,SpecialEdStatus,In 504 Plan,LowIncome,EL Student First Year
|
||||
Maynard Public Schools,2022-23,1,TRUE,Caucasian,M,Active,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,2,TRUE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,3,FALSE,Black,M,,,Reduced Lunch,
|
||||
Maynard Public Schools,2022-23,4,FALSE,Caucasian,F,,Not 504,,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,5,FALSE,Caucasian,M,Active,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,6,TRUE,Caucasian,M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,7,FALSE,Caucasian,F,,Not 504,Reduced Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,8,FALSE,Caucasian,F,,Not 504,,Does not apply
|
||||
Maynard Public Schools,2022-23,9,FALSE,"Asian,Native American",F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,10,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,11,FALSE,Caucasian,M,,Not 504,Reduced Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,12,FALSE,Caucasian,F,,Not 504,,Does not apply
|
||||
Maynard Public Schools,2022-23,13,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,14,TRUE,Caucasian,F,,,Free Lunch,
|
||||
Maynard Public Schools,2022-23,15,FALSE,Caucasian,F,,,Free Lunch,
|
||||
Maynard Public Schools,2022-23,16,FALSE,Caucasian,M,,,Free Lunch,
|
||||
Maynard Public Schools,2022-23,17,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,18,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,19,FALSE,Caucasian,F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,20,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,21,FALSE,Asian,F,,Not 504,Reduced Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,22,FALSE,Asian,F,,Not 504,Reduced Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,23,TRUE,Caucasian,F,,,Not Eligible,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,24,TRUE,Caucasian,F,,Not 504,Free Lunch,
|
||||
Maynard Public Schools,2022-23,25,TRUE,Caucasian,M,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,26,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,27,TRUE,Caucasian,M,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,28,TRUE,Caucasian,F,,Not 504,Free Lunch,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,29,TRUE,Native American,F,,,Free Lunch,LEP student 1st year
|
||||
Maynard Public Schools,2022-23,30,FALSE,Caucasian,M,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,31,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,32,FALSE,Caucasian,M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,33,FALSE,Caucasian,F,,,Free Lunch,
|
||||
Maynard Public Schools,2022-23,34,FALSE,Caucasian,M,Active,Not 504,Free Lunch,
|
||||
Maynard Public Schools,2022-23,35,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,36,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,37,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,38,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,39,TRUE,"Black, Caucasian",M,Exited,Not 504,Reduced Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,40,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,41,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,42,FALSE,,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,43,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,44,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,45,FALSE,Caucasian,F,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,46,FALSE,Caucasian,M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,47,FALSE,Caucasian,M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,48,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,49,FALSE,Caucasian,M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,50,TRUE,Native American,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,51,TRUE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,52,TRUE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,53,FALSE,Caucasian,M,,504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,54,FALSE,"Caucasian,Pacific Island",M,Active,Not 504,Reduced Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,55,FALSE,"Caucasian,Pacific Island",F,,Not 504,Reduced Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,56,FALSE,Caucasian,F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,57,TRUE,Caucasian,M,Active,,Free Lunch,
|
||||
Maynard Public Schools,2022-23,58,TRUE,Caucasian,M,,,Free Lunch,LEP student 1st year
|
||||
Maynard Public Schools,2022-23,59,TRUE,Caucasian,M,Active,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,60,FALSE,Caucasian,M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,61,TRUE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,62,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,63,FALSE,,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,64,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,65,FALSE,Caucasian,M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,66,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,67,FALSE,"Asian,Caucasian",M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,68,FALSE,"Asian,Caucasian",M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,69,FALSE,Caucasian,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,70,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,71,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,72,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,73,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,74,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,75,FALSE,Caucasian,M,,504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,76,FALSE,Caucasian,M,,504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,77,TRUE,Caucasian,M,,504,Reduced Lunch,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,78,TRUE,Caucasian,M,,504,Reduced Lunch,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,79,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,80,TRUE,Caucasian,F,,,Free Lunch,LEP student 1st year
|
||||
Maynard Public Schools,2022-23,81,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,82,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,83,FALSE,Caucasian,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,84,TRUE,Caucasian,M,,Not 504,Not Eligible,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,85,FALSE,Caucasian,F,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,86,FALSE,Caucasian,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,87,TRUE,Black,F,Active,504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,88,TRUE,Black,F,Active,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,89,TRUE,Black,F,,,Free Lunch,
|
||||
Maynard Public Schools,2022-23,90,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,91,FALSE,Caucasian,M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,92,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,93,FALSE,Caucasian,F,,504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,94,FALSE,Caucasian,M,,,Free Lunch,
|
||||
Maynard Public Schools,2022-23,95,FALSE,Caucasian,F,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,96,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,97,FALSE,Caucasian,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,98,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,99,FALSE,Caucasian,M,,504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,100,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,101,FALSE,Caucasian,F,,,Free Lunch,
|
||||
Maynard Public Schools,2022-23,102,FALSE,Caucasian,F,,,Free Lunch,
|
||||
Maynard Public Schools,2022-23,103,FALSE,Caucasian,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,104,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,105,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,106,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,107,FALSE,Caucasian,M,Active,Not 504,Not Eligible,
|
||||
Maynard Public Schools,2022-23,108,FALSE,Caucasian,M,Eligible,Not 504,Not Eligible,
|
||||
Maynard Public Schools,2022-23,109,FALSE,Caucasian,F,,504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,110,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,111,FALSE,Caucasian,M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,112,FALSE,Caucasian,M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,113,TRUE,Caucasian,F,,,Free Lunch,
|
||||
Maynard Public Schools,2022-23,114,TRUE,Caucasian,M,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,115,TRUE,Caucasian,F,Active,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,116,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,117,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,118,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,119,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,120,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,121,FALSE,"Black, Native American",F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,122,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,123,FALSE,Caucasian,F,,,Reduced Lunch,
|
||||
Maynard Public Schools,2022-23,124,FALSE,Caucasian,M,,,Reduced Lunch,
|
||||
Maynard Public Schools,2022-23,125,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,126,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,127,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,128,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,129,FALSE,"Caucasian, Native American",F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,130,TRUE,Caucasian,F,,Not 504,Free Lunch,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,131,TRUE,Caucasian,M,,Not 504,Free Lunch,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,132,TRUE,Caucasian,F,Active,Not 504,Free Lunch,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,133,TRUE,Caucasian,F,,Not 504,Not Eligible,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,134,TRUE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,135,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,136,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,137,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,138,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,139,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,140,FALSE,Caucasian,M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,141,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,142,TRUE,Caucasian,M,,,Not Eligible,LEP student 1st year
|
||||
Maynard Public Schools,2022-23,143,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,144,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,145,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,146,FALSE,Caucasian,F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,147,FALSE,Caucasian,F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,148,FALSE,Caucasian,M,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,149,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,150,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,151,FALSE,Caucasian,M,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,152,TRUE,Caucasian,M,Active,Not 504,Free Lunch,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,153,TRUE,Caucasian,F,,Not 504,Free Lunch,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,154,FALSE,Caucasian,F,,,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,155,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,156,FALSE,Black,F,,504,Not Eligible,
|
||||
Maynard Public Schools,2022-23,157,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,158,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,159,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,160,FALSE,Caucasian,M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,161,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,162,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,163,TRUE,Caucasian,M,,,Free Lunch,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,164,TRUE,Caucasian,M,,,Free Lunch,LEP student 1st year
|
||||
Maynard Public Schools,2022-23,165,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,166,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,167,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,168,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,169,FALSE,Caucasian,F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,170,FALSE,Caucasian,F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,171,FALSE,Caucasian,F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,172,FALSE,Caucasian,F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,173,FALSE,Caucasian,F,,504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,174,FALSE,Caucasian,F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,175,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,176,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,177,FALSE,Caucasian,M,Exited,504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,178,TRUE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,179,FALSE,Caucasian,M,,,Free Lunch,
|
||||
Maynard Public Schools,2022-23,180,TRUE,Caucasian,F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,181,TRUE,Caucasian,F,,Not 504,Free Lunch,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,182,TRUE,Caucasian,F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,183,TRUE,Caucasian,M,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,184,TRUE,Caucasian,M,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,185,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,186,FALSE,Caucasian,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,187,FALSE,Caucasian,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,188,FALSE,"Asian,Caucasian",M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,189,FALSE,"Asian,Caucasian",M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,190,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,191,FALSE,"Asian,Caucasian",F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,192,FALSE,Black,F,Active,Not 504,Free Lunch,
|
||||
Maynard Public Schools,2022-23,193,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,194,FALSE,Asian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,195,FALSE,Asian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,196,TRUE,Caucasian,F,,,Not Eligible,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,197,FALSE,Caucasian,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,198,FALSE,Caucasian,F,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,199,FALSE,"Asian,Caucasian",M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,200,FALSE,"Asian,Caucasian",F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,201,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,202,FALSE,Caucasian,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,203,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,204,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,205,TRUE,Caucasian,M,,,Free Lunch,
|
||||
Maynard Public Schools,2022-23,206,FALSE,Asian,M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,207,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,208,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,209,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,210,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,211,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,212,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,213,FALSE,Caucasian,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,214,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,215,FALSE,Caucasian,F,Active,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,216,FALSE,Caucasian,M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,217,FALSE,Caucasian,F,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,218,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,219,FALSE,Caucasian,M,Active,Not 504,Reduced Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,220,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,221,TRUE,"Black, Caucasian",M,Active,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,222,TRUE,Caucasian,F,,,Free Lunch,LEP student 1st year
|
||||
Maynard Public Schools,2022-23,223,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,224,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,225,FALSE,Caucasian,F,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,226,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,227,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,228,FALSE,Caucasian,M,,504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,229,FALSE,Caucasian,M,Active,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,230,FALSE,Caucasian,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,231,TRUE,Pacific Island,F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,232,TRUE,Caucasian,F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,233,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,234,FALSE,Caucasian,M,,,Free Lunch,
|
||||
Maynard Public Schools,2022-23,235,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,236,FALSE,Caucasian,M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,237,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,238,TRUE,"Black, Caucasian, Native American",F,Active,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,239,TRUE,Caucasian,M,Active,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,240,FALSE,Caucasian,M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,241,FALSE,Caucasian,F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,242,TRUE,Caucasian,M,,Not 504,Reduced Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,243,TRUE,Caucasian,F,,,Reduced Lunch,LEP student 1st year
|
||||
Maynard Public Schools,2022-23,244,TRUE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,245,FALSE,Caucasian,M,Active,Not 504,Free Lunch,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,246,TRUE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,247,FALSE,"Caucasian,Native American",M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,248,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,249,FALSE,Caucasian,M,,504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,250,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,251,FALSE,"Black, Caucasian",M,Exited,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,252,FALSE,"Black, Caucasian",F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,253,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,254,FALSE,Caucasian,M,Active,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,255,FALSE,Caucasian,F,,504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,256,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,257,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,258,FALSE,Caucasian,M,Active,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,259,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,260,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,261,FALSE,Caucasian,M,,504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,262,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,263,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,264,TRUE,Caucasian,F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,265,TRUE,Caucasian,F,,,Free Lunch,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,266,TRUE,Caucasian,M,,,Free Lunch,LEP student 1st year
|
||||
Maynard Public Schools,2022-23,267,FALSE,Caucasian,F,,Not 504,Free Lunch,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,268,TRUE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,269,FALSE,Caucasian,M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,270,TRUE,Caucasian,M,,504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,271,TRUE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,272,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,273,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,274,FALSE,Caucasian,M,,504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,275,FALSE,Black,M,,,Free Lunch,
|
||||
Maynard Public Schools,2022-23,276,FALSE,Black,F,,,Free Lunch,
|
||||
Maynard Public Schools,2022-23,277,FALSE,Black,F,,,Free Lunch,
|
||||
Maynard Public Schools,2022-23,278,FALSE,Caucasian,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,279,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,280,FALSE,Caucasian,M,,504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,281,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,282,TRUE,Caucasian,M,Active,Not 504,Not Eligible,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,283,TRUE,Caucasian,F,,Not 504,Not Eligible,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,284,TRUE,Caucasian,F,,Not 504,Not Eligible,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,285,TRUE,Caucasian,F,,,Free Lunch,
|
||||
Maynard Public Schools,2022-23,286,TRUE,Caucasian,M,,504,Free Lunch,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,287,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,288,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,289,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,290,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,291,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,292,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,293,FALSE,Caucasian,M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,294,FALSE,Caucasian,M,Active,Not 504,Free Lunch,
|
||||
Maynard Public Schools,2022-23,295,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,296,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,297,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,298,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,299,FALSE,Caucasian,M,Active,504,Not Eligible,
|
||||
Maynard Public Schools,2022-23,300,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,301,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,302,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,303,FALSE,"Asian, Black",M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,304,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,305,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,306,FALSE,Caucasian,F,,504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,307,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,308,FALSE,Caucasian,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,309,TRUE,Caucasian,M,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,310,TRUE,Caucasian,F,,,Free Lunch,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,311,TRUE,Caucasian,F,,,Free Lunch,LEP student 1st year
|
||||
Maynard Public Schools,2022-23,312,TRUE,Caucasian,M,,504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,313,TRUE,Caucasian,M,Active,Not 504,Free Lunch,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,314,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,315,FALSE,Caucasian,M,Active,Not 504,Not Eligible,
|
||||
Maynard Public Schools,2022-23,316,FALSE,Caucasian,M,Exited,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,317,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,318,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,319,FALSE,Caucasian,M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,320,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,321,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,322,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,323,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,324,FALSE,Caucasian,M,,504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,325,FALSE,Caucasian,F,,504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,326,FALSE,Caucasian,F,,504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,327,TRUE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,328,TRUE,Caucasian,M,,,Free Lunch,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,329,TRUE,Caucasian,F,,,Free Lunch,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,330,FALSE,Caucasian,F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,331,FALSE,,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,332,FALSE,Caucasian,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,333,FALSE,Caucasian,M,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,334,FALSE,Caucasian,F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,335,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,336,FALSE,Caucasian,M,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,337,FALSE,Native American,M,Active,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,338,TRUE,Caucasian,M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,339,FALSE,"Black, Caucasian",F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,340,FALSE,"Black, Caucasian",F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,341,TRUE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,342,TRUE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,343,FALSE,Caucasian,F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,344,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,345,FALSE,Caucasian,F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,346,FALSE,Caucasian,F,,504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,347,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,348,FALSE,Caucasian,F,,504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,349,TRUE,Caucasian,F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,350,TRUE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,351,TRUE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,352,FALSE,Caucasian,M,,,Free Lunch,
|
||||
Maynard Public Schools,2022-23,353,FALSE,Caucasian,F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,354,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,355,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,356,FALSE,Caucasian,F,,504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,357,FALSE,Caucasian,M,,504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,358,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,359,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,360,FALSE,Caucasian,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,361,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,362,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,363,FALSE,Caucasian,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,364,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,365,FALSE,Black,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,366,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,367,FALSE,Caucasian,M,,504,Not Eligible,
|
||||
Maynard Public Schools,2022-23,368,FALSE,Caucasian,M,Active,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,369,FALSE,Caucasian,F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,370,FALSE,Caucasian,M,,,Free Lunch,
|
||||
Maynard Public Schools,2022-23,371,FALSE,Caucasian,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,372,FALSE,Caucasian,M,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,373,FALSE,"Asian,Caucasian",F,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,374,FALSE,Caucasian,M,,,Free Lunch,
|
||||
Maynard Public Schools,2022-23,375,FALSE,Caucasian,F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,376,FALSE,Caucasian,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,377,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,378,FALSE,Caucasian,F,Active,Not 504,Not Eligible,
|
||||
Maynard Public Schools,2022-23,379,FALSE,Caucasian,F,Exited,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,380,FALSE,Caucasian,F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,381,FALSE,Caucasian,F,Active,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,382,FALSE,Caucasian,F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,383,TRUE,Black,F,Active,Not 504,Free Lunch,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,384,TRUE,Caucasian,M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,385,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,386,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,387,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,388,FALSE,Caucasian,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,389,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,390,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,391,FALSE,Caucasian,M,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,392,FALSE,Caucasian,M,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,393,FALSE,"Caucasian,Native American",F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,394,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,395,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,396,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,397,FALSE,Caucasian,M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,398,FALSE,Caucasian,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,399,FALSE,"Caucasian,Native American",M,,504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,400,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,401,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,402,FALSE,Caucasian,F,,Not 504,Reduced Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,403,FALSE,Caucasian,M,,Not 504,Reduced Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,404,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,405,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,406,FALSE,Caucasian,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,407,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,408,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,409,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,410,FALSE,Caucasian,M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,411,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,412,TRUE,Native American,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,413,TRUE,Native American,F,,504,Not Eligible,
|
||||
Maynard Public Schools,2022-23,414,FALSE,Caucasian,M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,415,FALSE,Asian,M,,Not 504,Not Eligible,
|
||||
Maynard Public Schools,2022-23,416,TRUE,Native American,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,417,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,418,TRUE,Caucasian,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,419,FALSE,Asian,M,Exited,Not 504,Not Eligible,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,420,FALSE,Asian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,421,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,422,FALSE,"Asian,Caucasian",F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,423,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,424,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,425,FALSE,Caucasian,M,Active,Not 504,Free Lunch,
|
||||
Maynard Public Schools,2022-23,426,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,427,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,428,FALSE,Caucasian,F,,,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,429,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,430,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,431,TRUE,Caucasian,M,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,432,TRUE,Caucasian,M,,,Free Lunch,LEP student 1st year
|
||||
Maynard Public Schools,2022-23,433,TRUE,Native American,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,434,TRUE,Native American,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,435,TRUE,Caucasian,F,,,Free Lunch,
|
||||
Maynard Public Schools,2022-23,436,TRUE,Caucasian,M,Active,Not 504,Free Lunch,
|
||||
Maynard Public Schools,2022-23,437,TRUE,Caucasian,M,,,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,438,TRUE,Caucasian,M,,Not 504,Not Eligible,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,439,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,440,FALSE,Caucasian,M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,441,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,442,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,443,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,444,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,445,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,446,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,447,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,448,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,449,FALSE,Caucasian,M,,504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,450,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,451,TRUE,Caucasian,F,,,Free Lunch,LEP student 1st year
|
||||
Maynard Public Schools,2022-23,452,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,453,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,454,FALSE,Caucasian,M,,504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,455,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,456,FALSE,"Caucasian,Pacific Island",F,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,457,FALSE,Black,M,,,Free Lunch,
|
||||
Maynard Public Schools,2022-23,458,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,459,FALSE,Caucasian,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,460,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,461,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,462,TRUE,Caucasian,F,,,Free Lunch,
|
||||
Maynard Public Schools,2022-23,463,FALSE,Caucasian,F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,464,FALSE,Caucasian,F,,Not 504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,465,FALSE,Caucasian,F,,,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,466,FALSE,Caucasian,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,467,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,468,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,469,FALSE,Caucasian,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,470,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,471,FALSE,Caucasian,N,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,472,FALSE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,473,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,474,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,475,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,476,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,477,TRUE,Caucasian,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,478,TRUE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,479,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,480,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,481,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,482,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,483,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,484,FALSE,Caucasian,M,Active,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,485,FALSE,Caucasian,M,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,486,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,487,FALSE,Caucasian,F,,504,Free Lunch,Does not apply
|
||||
Maynard Public Schools,2022-23,488,TRUE,Caucasian,M,,,Not Eligible,LEP student not 1st year
|
||||
Maynard Public Schools,2022-23,489,FALSE,Caucasian,F,Exited,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,490,FALSE,Caucasian,M,Active,Not 504,Free Lunch,
|
||||
Maynard Public Schools,2022-23,491,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,492,FALSE,Caucasian,F,,504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,493,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,494,TRUE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,495,TRUE,Caucasian,M,Active,Not 504,Free Lunch,
|
||||
Maynard Public Schools,2022-23,496,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,497,TRUE,Caucasian,F,,,Not Eligible,
|
||||
Maynard Public Schools,2022-23,498,FALSE,Caucasian,M,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,499,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
Maynard Public Schools,2022-23,500,FALSE,Caucasian,F,,Not 504,Not Eligible,Does not apply
|
||||
|
36
spec/fixtures/raw/sample_maynard_raw_student_survey.csv
vendored
Normal file
36
spec/fixtures/raw/sample_maynard_raw_student_survey.csv
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
StartDate,EndDate,Status,IPAddress,Progress,Duration (in seconds),Finished,RecordedDate,ResponseId,District,School,LASID,Gender,Race,What grade are you in?,s-emsa-q1,s-emsa-q2,s-emsa-q3,s-tint-q1,s-tint-q2,s-tint-q3,s-tint-q4,s-tint-q5,s-acpr-q1,s-acpr-q2,s-acpr-q3,s-acpr-q4,s-cure-q1,s-cure-q2,s-cure-q3,s-cure-q4,s-sten-q1,s-sten-q2,s-sten-q3,s-sper-q1,s-sper-q2,s-sper-q3,s-sper-q4,s-civp-q1,s-civp-q2,s-civp-q3,s-civp-q4,s-grmi-q1,s-grmi-q2,s-grmi-q3,s-grmi-q4,s-appa-q1,s-appa-q2,s-appa-q3,s-peff-q1,s-peff-q2,s-peff-q3,s-peff-q4,s-peff-q5,s-peff-q6,s-sbel-q1,s-sbel-q2,s-sbel-q3,s-sbel-q4,s-sbel-q5,s-phys-q1,s-phys-q2,s-phys-q3,s-phys-q4,s-vale-q1,s-vale-q2,s-vale-q3,s-vale-q4,s-acst-q1,s-acst-q2,s-acst-q3,s-sust-q1,s-sust-q2,s-grit-q1,s-grit-q2,s-grit-q3,s-grit-q4,s-expa-q1,s-poaf-q1,s-poaf-q2,s-poaf-q3,s-poaf-q4,s-tint-q1-1,s-tint-q2-1,s-tint-q3-1,s-tint-q4-1,s-tint-q5-1,s-acpr-q1-1,s-acpr-q2-1,s-acpr-q3-1,s-acpr-q4-1,s-peff-q1-1,s-peff-q2-1,s-peff-q3-1,s-peff-q4-1,s-peff-q5-1,s-peff-q6-1
|
||||
2023-03-17 7:57:47,2023-03-17 8:09:15,0,71.174.81.214,100,1000,1,2023-03-17T8:9:15,1000,2,1740505,1,2,4,9,3,5,5,,,,,,,,,,,,,,,,,,,,,,,,,4,4,3,5,,,,,,,,,,4,4,2,3,2,5,5,5,5,4,2,2,4,3,2,3,3,5,4,4,3,5,2,3,3,4,4,4,1,2,5,5,,,,,4,4,4,3,4,5
|
||||
2023-03-17 8:02:15,2023-03-17 8:08:02,0,71.174.81.214,25,1000,1,2023-03-17T8:8:3,1001,2,1740505,2,1,5,10,,,,,,,,,,,,,,,,,,,,5,4,4,4,,,,,,,,,2,3,2,,,,,,,4,3,2,4,3,5,5,4,4,4,4,3,5,3,4,3,2,4,3,4,3,3,1,2,2,2,3,,,,,,5,4,4,5,4,4,5,3,3,4
|
||||
2023-03-17 8:00:05,2023-03-17 8:07:39,0,71.174.81.214,24,1000,1,2023-03-17T8:7:39,1002,2,1740505,3,,,9,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,5,4,4,5,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
2023-03-17 8:03:35,2023-03-17 8:15:38,0,71.174.81.214,0,1000,1,2023-03-17T8:15:38,1003,2,1740505,4,1,"1,4",10,4,5,5,,,,,,,,,,3,4,5,5,4,4,3,5,4,3,4,4,3,3,3,2,1,3,2,3,1,1,,,,,,,,,,,,,,,,,,,,,,,5,4,3,5,3,4,3,3,3,5,4,5,4,4,5,5,5,5,5,5,,,,,,
|
||||
2023-03-17 7:57:09,2023-03-17 8:12:26,0,71.174.81.214,,1000,1,2023-03-17T8:12:27,1004,2,1740505,5,1,"5,4",9,4,4,5,,,,,,,,,,,,,,,,,,,,,,,,,4,4,2,4,,,,,,,,,,3,3,3,3,3,5,5,4,5,5,3,3,4,2,2,1,3,2,4,5,4,5,4,3,1,3,4,3,1,3,3,1,,,,,4,4,3,4,2,4
|
||||
2023-03-17 8:01:50,2023-03-17 8:17:51,0,71.174.81.214,100,240,1,2023-03-17T8:17:52,1005,2,1740505,6,1,"5,4",9,4,3,4,,,,,,,,,,4,4,3,3,3,4,4,4,5,2,5,4,4,4,4,4,4,5,3,4,3,5,,,,,,,4,4,4,5,2,,,,,,,,,,,,,,,,,,,,,,,4,1,4,4,4,5,4,4,5,4,5,5,5,5,4
|
||||
2023-03-17 8:01:45,2023-03-17 8:07:59,0,71.174.81.214,100,239,1,2023-03-17T8:8:0,1006,2,1740505,7,1,5,10,,,,,,,,,,,,,5,3,3,5,2,3,3,,,,,4,4,4,4,,,,,,,,,,,,,,4,5,3,4,3,5,5,4,5,5,4,4,4,2,1,1,4,5,4,4,3,4,2,2,3,2,3,,,,,,,,,,4,4,5,4,4,5
|
||||
2023-03-17 9:07:09,2023-03-17 9:20:10,0,71.174.81.214,100,0,1,2023-03-17T9:20:11,1007,2,1740305,8,2,5,7,,,,,,,,,,,,,4,5,5,4,,4,3,,,,,5,4,4,5,,,,,,,,,,,,,,5,5,4,5,5,5,5,5,5,4,5,5,5,3,2,3,3,3,5,4,4,4,3,3,4,,4,,,,,,,,,,4,5,5,5,5,5
|
||||
2023-03-17 8:02:11,2023-03-17 8:29:53,0,71.174.81.214,100,,1,2023-03-17T8:29:53,1008,2,1740505,9,1,"5,4",10,,,,,,,,,,,,,,,,,,,,3,3,2,3,,,,,,,,,2,3,3,,,,,,,3,4,3,3,3,5,5,5,5,4,4,2,3,2,3,2,2,5,2,4,3,3,1,3,3,3,4,,,,,,4,4,4,4,4,4,4,4,2,4
|
||||
2023-03-17 8:00:42,2023-03-17 8:12:00,0,71.174.81.214,100,1000,1,2023-03-17T8:12:0,1009,2,1740505,10,2,5,1,,,,,,,,,,,,,1,4,3,2,1,3,2,,,,,3,3,4,4,,,,,,,,,,,,,,5,5,2,4,5,4,4,5,4,2,2,1,2,2,4,3,5,5,4,4,1,4,1,2,1,3,3,,,,,,,,,,3,4,3,1,1,1
|
||||
2023-03-17 8:03:09,2023-03-17 8:13:27,0,71.174.81.214,100,1000,1,2023-03-17T8:13:28,1010,2,1740505,11,1,5,2,,,,,,,,,,,,,5,3,2,4,2,2,3,,,,,4,5,5,5,,,,,,,,,,,,,,4,4,3,4,4,4,4,4,5,4,3,5,4,2,1,2,4,4,5,4,3,5,4,1,3,3,3,,,,,,,,,,4,3,4,4,4,4
|
||||
2023-03-17 8:23:20,2023-03-17 8:34:00,0,71.174.81.214,100,1000,1,2023-03-17T8:34:0,1011,2,1740505,12,2,3,3,1,2,2,2,3,2,4,2,5,5,3,5,3,3,3,2,3,4,4,2,4,3,5,4,4,4,3,4,3,3,4,3,1,3,,,,,,,,,,,,,,,,,,,,,,,1,2,4,4,3,4,3,2,2,4,4,,,,,,,,,,,,,,,
|
||||
2023-03-17 8:36:36,2023-03-17 8:47:33,0,71.174.81.214,100,1000,1,2023-03-17T8:47:34,1012,2,1740505,13,1,3,4,4,5,4,,,,,,,,,,4,2,3,2,2,3,4,4,5,3,4,2,4,2,3,3,4,3,3,2,1,1,,,,,,,,,,,,5,5,2,4,2,3,3,4,5,4,5,,,,,,,,,,,,4,2,3,3,2,4,4,3,3,,,,,,
|
||||
2023-03-17 8:01:10,2023-03-17 8:09:17,0,71.174.81.214,100,1000,1,2023-03-17T8:9:18,1013,2,1740505,14,1,4,5,4,3,5,,,,,,,,,,3,4,3,4,3,4,4,2,4,4,2,3,1,2,2,4,2,3,5,2,2,1,,,,,,,4,4,3,3,4,,,,,,,,,,,,,,,,,,,,,,,2,1,3,2,5,4,5,2,2,2,3,4,3,3,4
|
||||
2023-03-17 10:06:07,2023-03-17 10:12:54,0,71.174.81.214,100,1000,1,2023-03-17T10:12:56,1014,2,1740505,15,1,5,6,,,,,,,,,,,,,3,3,4,2,1,2,4,,,,,2,2,2,2,,,,,,,,,,,,,,1,2,1,2,3,4,5,3,5,3,1,2,3,2,2,1,2,4,3,2,3,2,4,2,3,2,2,,,,,,,,,,4,4,4,4,4,5
|
||||
2023-03-17 7:57:13,2023-03-17 8:05:02,0,71.174.81.214,100,1000,1,2023-03-17T8:5:2,1015,2,1740505,16,4,5,7,,,,,,,,,,,,,,,,,,,,3,3,3,3,,,,,,,,,3,5,2,,,,,,,2,1,3,2,4,4,4,3,3,2,2,3,4,3,5,5,5,4,3,4,2,4,5,3,1,3,2,,,,,,4,4,3,4,4,5,4,5,5,5
|
||||
2023-03-17 7:57:50,2023-03-17 8:02:53,0,71.174.81.214,100,1000,1,2023-03-17T8:2:54,1016,2,1740505,17,2,5,8,1,1,1,,,,,,,,,,,,,,,,,,,,,,,,,5,2,1,1,,,,,,,,,,5,4,3,3,3,2,4,3,3,5,1,1,1,1,3,5,1,1,4,5,4,1,3,1,3,2,1,1,1,1,1,1,,,,,1,1,1,1,1,2
|
||||
2023-03-17 8:40:22,2023-03-17 8:53:19,0,71.174.81.214,100,1000,0,2023-03-18T8:53:20,1017,2,1740505,18,2,5,9,,,,,,,,,,,,,,,,,3,3,4,,,,,1,1,1,2,4,3,2,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,2,2,1,3,2,3,2,4,,,,,,
|
||||
2023-03-17 8:37:13,2023-03-17 8:43:34,0,71.174.81.214,100,1000,1,2023-03-17T8:43:35,1018,2,1740505,19,2,2,10,,,,,,,,,,,,,,,,,,,,3,3,2,3,,,,,,,,,2,2,1,,,,,,,4,4,4,4,4,4,5,4,5,3,4,3,3,4,5,4,,,3,2,3,4,1,4,2,3,4,,,,,,4,5,5,4,4,4,5,4,3,4
|
||||
2023-03-17 8:36:27,2023-03-17 8:44:07,0,71.174.81.214,100,1000,1,2023-03-17T8:44:8,1019,2,1740505,20,1,2,11,3,4,3,,,,,,,,,,2,3,3,2,4,5,4,3,4,3,5,3,4,4,5,4,5,3,4,5,4,4,,,,,,,3,4,3,4,3,,,,,,,,,,,,,,,,,,,,,,,4,2,,2,4,4,5,2,4,5,5,4,3,3,4
|
||||
2023-03-17 8:33:55,2023-03-17 8:43:13,0,71.174.81.214,100,1000,1,2023-03-17T8:43:14,1020,2,1740505,21,1,5,12,3,1,3,,,,,,,,,,,,,,2,3,3,,4,,4,,4,,4,4,3,4,3,1,5,2,,,,,,,3,4,2,3,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,5,4,5,5,5,4,,4,4,4
|
||||
2023-03-17 8:50:49,2023-03-17 9:13:18,0,71.174.81.214,100,1000,1,2023-03-17T9:13:19,1021,2,1740305,22,2,"5,2",1,,,,,,,,,,,,,2,3,3,3,2,3,3,,,,,3,4,4,4,,,,,,,,,,,,,,2,2,1,3,2,5,5,4,5,4,2,2,2,1,1,1,1,3,3,4,3,4,4,1,1,1,3,,,,,,,,,,4,4,4,4,3,4
|
||||
2023-03-17 7:57:37,2023-03-17 8:04:25,0,71.174.81.214,100,1000,1,2023-03-17T8:4:25,1022,2,1740305,23,1,5,2,,,,,,,,,,,,,,,,,,,,4,4,4,5,,,,,,,,,4,4,5,,,,,,,3,3,4,4,4,4,5,4,5,4,3,3,3,2,2,2,3,3,3,2,3,4,4,3,3,2,4,,,,,,3,2,4,3,3,3,3,4,2,2
|
||||
2023-03-17 8:01:47,2023-03-17 8:08:39,0,71.174.81.214,100,1000,1,2023-03-17T8:8:39,1023,2,1740305,24,1,"2,4",3,4,3,5,,,,,,,,,,2,2,2,2,1,2,2,4,3,2,3,2,3,3,4,3,4,2,3,4,3,2,,,,,,,,,,,,5,4,3,5,3,1,2,2,2,2,4,,,,,,,,,,,,1,2,4,2,2,5,4,1,4,,,,,,
|
||||
2023-03-17 8:37:21,2023-03-17 8:58:16,0,71.174.81.214,100,1000,1,2023-03-17T8:58:16,1024,2,1740305,25,1,5,4,3,3,3,,,,,,,,,,,,,,,,,,,,,,,,,4,4,2,3,,,,,,,,,,3,4,4,3,2,5,5,4,5,3,3,2,3,4,3,3,2,4,4,3,3,3,2,2,3,3,4,3,3,3,3,3,,,,,4,3,2,3,3,3
|
||||
2023-03-17 8:02:25,2023-03-17 8:11:16,0,71.174.81.214,100,1000,0,2023-03-18T8:11:21,1025,2,1740305,26,2,5,5,,,,,,,,,,,,,,,,,3,3,3,4,4,4,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,,1,3,,,,,,4,3,3,3,3,,,,,,,,,,
|
||||
2023-03-17 9:33:54,2023-03-17 9:57:21,0,71.174.81.214,100,1000,1,2023-03-17T9:57:22,1026,2,1740305,27,2,5,6,,,,,,,,,5,5,4,5,,,,,,,,1,2,2,2,,,,,,,,,1,1,2,5,4,4,4,3,5,5,4,,4,2,5,4,4,3,5,3,2,2,1,1,5,3,2,2,3,1,1,1,2,1,3,3,,,,,,,,,,,,,,,
|
||||
2023-03-17 9:48:38,2023-03-17 9:58:45,0,71.174.81.214,100,1000,1,2023-03-17T9:58:45,1027,2,1740305,28,1,5,7,,,,,,,,,2,4,4,2,,,,,,,,3,3,3,5,,,,,,,,,2,3,3,4,3,2,2,3,2,1,3,2,3,2,3,3,3,2,4,3,4,2,2,1,1,5,5,2,3,2,3,5,4,3,2,2,,,,,,,,,,,,,,,
|
||||
2023-03-17 8:36:40,2023-03-17 8:43:21,0,71.174.81.214,100,1000,1,2023-03-17T8:43:22,1028,2,1740305,29,2,5,8,,,,,,,,,,,,,,,,,,,,3,3,2,3,,,,,,,,,4,3,4,,,,,,,3,3,2,4,3,5,4,4,5,4,4,2,3,4,2,5,4,4,3,3,3,3,2,2,3,1,4,,,,,,3,4,4,4,2,4,5,4,3,4
|
||||
2023-03-17 9:40:56,2023-03-17 9:52:52,0,71.174.81.214,100,1000,1,2023-03-17T9:52:52,1029,2,1740305,30,2,5,9,3,4,3,5,3,5,5,5,5,4,4,5,4,4,4,4,2,3,2,4,4,3,4,3,4,3,5,5,5,4,3,5,4,4,,,,,,,,,,,,1,5,4,4,5,4,3,4,3,1,4,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
2023-03-17 9:33:58,2023-03-17 9:48:33,0,71.174.81.214,100,1000,1,2023-03-17T9:48:33,1030,2,1740305,31,2,5,10,,,,,,,,,4,3,5,2,,,,,,,,5,4,4,5,,,,,,,,,5,2,4,4,4,2,2,3,4,2,2,1,1,2,1,2,2,5,5,5,4,4,3,1,1,1,1,3,2,3,3,3,4,4,4,2,,,,,,,,,,,,,,,
|
||||
2023-03-17 8:03:04,2023-03-17 8:23:33,0,71.174.81.214,100,1000,1,2023-03-17T8:23:33,1031,2,1740305,32,1,5,11,1,1,1,3,4,2,4,3,,,,,,,,,,,,,,,,,,,,5,5,5,5,,,,4,4,3,2,3,3,2,3,3,1,3,3,5,3,4,5,3,3,5,1,2,2,1,1,4,5,3,5,4,2,4,2,3,,,,,,,,,,,,,,,
|
||||
2023-03-17 8:33:14,2023-03-17 8:41:01,0,71.174.81.214,100,1000,1,2023-03-17T8:41:2,1032,2,1740305,33,1,5,12,,,,,,,,,,,,,2,1,1,1,2,2,3,,,,,2,1,3,2,,,,,,,,,,,,,,1,1,1,1,1,4,3,4,5,4,1,1,2,3,2,3,4,2,2,3,3,2,2,2,1,2,3,,,,,,,,,,3,2,2,1,2,2
|
||||
2023-03-17 7:57:06,2023-03-17 8:08:35,0,71.174.81.214,100,1000,1,2023-03-17T8:8:35,1033,2,1740505,34,2,5,9,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,2,2,2,2,2,2,2,2,2
|
||||
2023-03-17 7:58:38,2023-03-17 8:12:04,0,71.174.81.214,100,1000,1,2023-03-17T8:12:5,1034,2,1740505,35,2,"5,4",12,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
|
22
spec/fixtures/sample_demographics.csv
vendored
22
spec/fixtures/sample_demographics.csv
vendored
|
|
@ -1,11 +1,11 @@
|
|||
Race Qualtrics Code,Race/Ethnicity,Gender Qualtrics Code,Sex/Gender
|
||||
1,American Indian or Alaskan Native,2,Male
|
||||
2,Asian or Pacific Islander,1,Female
|
||||
3,Black or African American,4,Non-Binary
|
||||
4,Hispanic or Latinx,99,Unknown
|
||||
5,White or Caucasian,,
|
||||
6,Prefer not to disclose,,
|
||||
7,Prefer to self-describe,,
|
||||
8,Middle Eastern,,
|
||||
99,Race/Ethnicity Not Listed,,
|
||||
100,Multiracial,,
|
||||
Race Qualtrics Code,Race/Ethnicity,Gender Qualtrics Code,Sex/Gender,Income
|
||||
1,American Indian or Alaskan Native,2,Male,Economically Disadvantaged – N
|
||||
2,Asian or Pacific Islander,1,Female,Economically Disadvantaged – Y
|
||||
3,Black or African American,4,Non-Binary,Unknown
|
||||
4,Hispanic or Latinx,99,Unknown,
|
||||
5,White or Caucasian,,,
|
||||
6,Prefer not to disclose,,,
|
||||
7,Prefer to self-describe,,,
|
||||
8,Middle Eastern,,,
|
||||
99,Race/Ethnicity Not Listed,,,
|
||||
100,Multiracial,,,
|
||||
|
|
|
|||
|
|
|
@ -1,9 +1,9 @@
|
|||
Start Date,End Date,Response Type,IP Address,Progress,Duration (in seconds),Finished,RecordedDate,ResponseId,LASID,Recipient Last Name,Recipient First Name,Recipient Email,External Data Reference,Location Latitude,Location Longitude,Distribution Channel,User Language,district,school,DESE ID,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,s-emsa-q1,s-emsa-q2,s-emsa-q3,s-tint-q1,s-tint-q2,#N/A,s-tint-q4,s-tint-q5,s-acpr-q1,s-acpr-q2,s-acpr-q3,s-acpr-q4,#N/A,#N/A,s-cure-q3,s-cure-q4,#N/A,s-sten-q2,s-sten-q3,s-sper-q1,s-sper-q2,s-sper-q3,s-sper-q4,s-civp-q1,s-civp-q2,s-civp-q3,s-civp-q4,s-grmi-q1,#N/A,#N/A,s-grmi-q4,s-appa-q1,s-appa-q2,#N/A,s-peff-q1,s-peff-q2,s-peff-q3,s-peff-q4,s-peff-q5,s-peff-q6,s-sbel-q1,s-sbel-q2,s-sbel-q3,s-sbel-q4,s-sbel-q5,s-phys-q1,s-phys-q1-1,s-phys-q2,s-phys-q3,s-phys-q4,s-vale-q1,s-vale-q2,s-vale-q3,s-vale-q4,s-acst-q1,s-acst-q2,s-acst-q3,s-acst-q4,s-acst-q5,s-grit-q1,s-grit-q2,s-grit-q3,s-grit-q4,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,race,What is your race/ethnicity?(Please select all that apply) - Selected Choice,grade,gender
|
||||
2020-09-29 18:28:41,2020-09-29 18:48:28,0,73.249.89.226,6,1186,0,2020-09-30T18:48:50,student_survey_response_1,123456,,,,,,,anonymous,EN,1,8,1500025,,,,dddd,4,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,0,some non-integer response,6,,,,5,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,EN,,,,,1,888,11th,1
|
||||
2021-02-23 15:12:58,2021-02-23 15:13:17,0,50.207.254.114,0,19,0,2021-02-24T15:13:19,student_survey_response_2,234567,,,,,,,anonymous,EN,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,NA,,,,,,,,,,,,,,,,,,,,,EN,,,,,"2,3,4",888,10,
|
||||
2021-03-31 9:50:19,2021-03-31 9:59:01,0,108.7.17.250,100,522,1,2021-03-31T09:59:02,student_survey_response_3,345678,,,,,42.53340149,-70.96530151,anonymous,EN,3,2,1500505,12,4,108,3300,7,1,,,,,,,,,,,,,,2,4,2,1,4,3,3,,,,,3,3,3,3,,,,,NA,,,,,,,,,3,2,3,3,2,1,3,3,4,1,3,3,4,4,2,4,3,3,4,3,3,3,4,3,3,3,3,3,,,,,,,,,,3,4,4,2,3,3,1,,3,,EN,Math teacher,,,,6,888,8,2
|
||||
2021-03-31 9:50:09,2021-03-31 10:00:16,0,67.186.188.168,100,607,1,2021-03-31T10:00:17,student_survey_response_4,456789,,,,,42.63510132,-71.30139923,anonymous,EN,3,2,1500505,12,18,108,2064,7,1,,2,2,1,,,,,,,,,,,,,,,,,,,,,,,,,3,5,3,3,,,,,,,,,,4,4,3,4,5,1,,1,5,1,3,2,4,4,1,2,1,3,2,3,3,3,4,2,5,3,4,5,5,3,3,4,3,,,,,4,4,4,4,3,5,2,,2,,EN,,,,English teacher,7,888,8,3
|
||||
2021-03-31 9:51:39,2021-03-31 10:01:36,0,73.47.153.77,100,596,1,2021-03-31T10:01:36,student_survey_response_5,567890,,,,,42.65820313,-71.30580139,anonymous,EN,3,2,1500505,6,15,109,3710,7,1,,2,2,2,,,,,,,,,,3,3,4,3,3,3,3,4,3,4,3,4,4,5,4,3,4,3,5,2,2,3,,,,,,,,,,,,1,,2,5,1,3,3,2,4,3,5,4,,,,,,,,,,,,5,4,3,4,4,4,4,4,4,,,,,,,2,,2,,EN,,,Social Studies teacher,,"1,2,3,4,5,8,6,7",888,7,4
|
||||
2021-03-31 9:51:39,2021-03-31 10:01:36,0,73.47.153.77,100,596,1,2021-03-31T10:01:37,student_survey_response_6,,,,,,42.65820313,-71.30580139,anonymous,EN,3,2,1500505,6,15,109,3710,7,1,,2,2,2,,,,,,,,,,3,3,4,3,3,3,3,4,3,4,3,4,4,5,4,3,4,3,5,2,2,3,,,,,,,,,,,,1,,2,5,1,3,3,2,4,3,5,4,,,,,,,,,,,,5,4,3,4,4,4,4,4,4,,,,,,,2,,2,,EN,,,Social Studies teacher,,"1,2,3,4,5,8",888,3,NA
|
||||
2021-03-31 9:51:39,2021-03-31 10:01:36,0,73.47.153.77,100,596,1,2021-03-31T10:01:38,student_survey_response_7,,,,,,42.65820313,-71.30580139,anonymous,EN,3,2,1500505,6,15,109,3710,7,1,,2,2,2,,,,,,,,,,3,3,4,3,3,3,3,4,3,4,3,4,4,5,4,3,4,3,5,2,2,3,,,,,,,,,,,,1,,2,5,1,3,3,2,4,3,5,4,,,,,,,,,,,,5,4,3,4,4,4,4,4,4,,,,,,,2,,2,,EN,,,Social Studies teacher,,,,4,
|
||||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"1,2,3,4,5,8",,,
|
||||
Start Date,End Date,Response Type,IP Address,Progress,Duration (in seconds),Finished,RecordedDate,ResponseId,LASID,Recipient Last Name,Recipient First Name,Recipient Email,External Data Reference,Location Latitude,Location Longitude,Distribution Channel,User Language,district,school,DESE ID,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,s-emsa-q1,s-emsa-q2,s-emsa-q3,s-tint-q1,s-tint-q2,#N/A,s-tint-q4,s-tint-q5,s-acpr-q1,s-acpr-q2,s-acpr-q3,s-acpr-q4,#N/A,#N/A,s-cure-q3,s-cure-q4,#N/A,s-sten-q2,s-sten-q3,s-sper-q1,s-sper-q2,s-sper-q3,s-sper-q4,s-civp-q1,s-civp-q2,s-civp-q3,s-civp-q4,s-grmi-q1,#N/A,#N/A,s-grmi-q4,s-appa-q1,s-appa-q2,#N/A,s-peff-q1,s-peff-q2,s-peff-q3,s-peff-q4,s-peff-q5,s-peff-q6,s-sbel-q1,s-sbel-q2,s-sbel-q3,s-sbel-q4,s-sbel-q5,s-phys-q1,s-phys-q1-1,s-phys-q2,s-phys-q3,s-phys-q4,s-vale-q1,s-vale-q2,s-vale-q3,s-vale-q4,s-acst-q1,s-acst-q2,s-acst-q3,s-acst-q4,s-acst-q5,s-grit-q1,s-grit-q2,s-grit-q3,s-grit-q4,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,#N/A,race,What is your race/ethnicity?(Please select all that apply) - Selected Choice,grade,gender,Raw Income,Income
|
||||
2020-09-29 18:28:41,2020-09-29 18:48:28,0,73.249.89.226,6,1186,0,2020-09-30T18:48:50,student_survey_response_1,123456,,,,,,,anonymous,EN,1,8,1500025,,,,dddd,4,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,0,some non-integer response,6,,,,5,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,EN,,,,,1,888,11th,1,Free Lunch,Economically Disadvantaged – Y
|
||||
2021-02-23 15:12:58,2021-02-23 15:13:17,0,50.207.254.114,0,19,0,2021-02-24T15:13:19,student_survey_response_2,234567,,,,,,,anonymous,EN,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,NA,,,,,,,,,,,,,,,,,,,,,EN,,,,,"2,3,4",888,10,,Not Eligible,Economically Disadvantaged – N
|
||||
2021-03-31 9:50:19,2021-03-31 9:59:01,0,108.7.17.250,100,522,1,2021-03-31T09:59:02,student_survey_response_3,345678,,,,,42.53340149,-70.96530151,anonymous,EN,3,2,1500505,12,4,108,3300,7,1,,,,,,,,,,,,,,2,4,2,1,4,3,3,,,,,3,3,3,3,,,,,NA,,,,,,,,,3,2,3,3,2,1,3,3,4,1,3,3,4,4,2,4,3,3,4,3,3,3,4,3,3,3,3,3,,,,,,,,,,3,4,4,2,3,3,1,,3,,EN,Math teacher,,,,6,888,8,2,Reduced Lunch,Economically Disadvantaged – Y
|
||||
2021-03-31 9:50:09,2021-03-31 10:00:16,0,67.186.188.168,100,607,1,2021-03-31T10:00:17,student_survey_response_4,456789,,,,,42.63510132,-71.30139923,anonymous,EN,3,2,1500505,12,18,108,2064,7,1,,2,2,1,,,,,,,,,,,,,,,,,,,,,,,,,3,5,3,3,,,,,,,,,,4,4,3,4,5,1,,1,5,1,3,2,4,4,1,2,1,3,2,3,3,3,4,2,5,3,4,5,5,3,3,4,3,,,,,4,4,4,4,3,5,2,,2,,EN,,,,English teacher,7,888,8,3,,Unknown
|
||||
2021-03-31 9:51:39,2021-03-31 10:01:36,0,73.47.153.77,100,596,1,2021-03-31T10:01:36,student_survey_response_5,567890,,,,,42.65820313,-71.30580139,anonymous,EN,3,2,1500505,6,15,109,3710,7,1,,2,2,2,,,,,,,,,,3,3,4,3,3,3,3,4,3,4,3,4,4,5,4,3,4,3,5,2,2,3,,,,,,,,,,,,1,,2,5,1,3,3,2,4,3,5,4,,,,,,,,,,,,5,4,3,4,4,4,4,4,4,,,,,,,2,,2,,EN,,,Social Studies teacher,,"1,2,3,4,5,8,6,7",888,7,4,Free Lunch,Economically Disadvantaged – Y
|
||||
2021-03-31 9:51:39,2021-03-31 10:01:36,0,73.47.153.77,100,596,1,2021-03-31T10:01:36,student_survey_response_6,,,,,,42.65820313,-71.30580139,anonymous,EN,3,2,1500505,6,15,109,3710,7,1,,2,2,2,,,,,,,,,,3,3,4,3,3,3,3,4,3,4,3,4,4,5,4,3,4,3,5,2,2,3,,,,,,,,,,,,1,,2,5,1,3,3,2,4,3,5,4,,,,,,,,,,,,5,4,3,4,4,4,4,4,4,,,,,,,2,,2,,EN,,,Social Studies teacher,,"1,2,3,4,5,8",888,3,NA,Not Eligible,Economically Disadvantaged – N
|
||||
2021-03-31 9:51:39,2021-03-31 10:01:36,0,73.47.153.77,100,596,1,2021-03-31T10:01:36,student_survey_response_7,,,,,,42.65820313,-71.30580139,anonymous,EN,3,2,1500505,6,15,109,3710,7,1,,2,2,2,,,,,,,,,,3,3,4,3,3,3,3,4,3,4,3,4,4,5,4,3,4,3,5,2,2,3,,,,,,,,,,,,1,,2,5,1,3,3,2,4,3,5,4,,,,,,,,,,,,5,4,3,4,4,4,4,4,4,,,,,,,2,,2,,EN,,,Social Studies teacher,,,,4,,Reduced Lunch,Economically Disadvantaged – Y
|
||||
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"1,2,3,4,5,8",,,,,
|
||||
|
|
|
|||
|
5
spec/models/income_spec.rb
Normal file
5
spec/models/income_spec.rb
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Income, type: :model do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
||||
468
spec/presenters/analyze/presenter_spec.rb
Normal file
468
spec/presenters/analyze/presenter_spec.rb
Normal file
|
|
@ -0,0 +1,468 @@
|
|||
require 'rails_helper'
|
||||
|
||||
describe Analyze::Presenter do
|
||||
let(:category) { create(:category, category_id: '1', name: 'first category') }
|
||||
let(:category_2) { create(:category, category_id: '2', name: 'second category') }
|
||||
let(:wrong_category) { create(:category, category_id: '999', name: 'wrong category') }
|
||||
let(:subcategory) { create(:subcategory, subcategory_id: '1A', name: 'first subcategory', category:) }
|
||||
let(:subcategory_2) { create(:subcategory, subcategory_id: '2A', name: 'second subcategory', category: category_2) }
|
||||
let(:wrong_subcategory) do
|
||||
create(:subcategory, subcategory_id: '99A', name: 'wrong subcategory', category: wrong_category)
|
||||
end
|
||||
let(:measure) { create(:measure, measure_id: '1A-i', name: 'first measure', subcategory:) }
|
||||
let(:measure_2) { create(:measure, measure_id: '2A-i', name: 'second measure', subcategory: subcategory_2) }
|
||||
let(:wrong_measure) do
|
||||
create(:wrong_measure, measure_id: '99A', name: 'wrong measure', subcategory: wrong_subcategory)
|
||||
end
|
||||
let(:scale) { create(:student_scale, measure:) }
|
||||
let(:survey_item) { create(:student_survey_item, scale:) }
|
||||
let(:school) { create(:school) }
|
||||
let(:academic_year) { create(:academic_year) }
|
||||
let(:ay_2021_22) { create(:academic_year, range: '2021-22') }
|
||||
let(:ay_2022_23) { create(:academic_year, range: '2022-23') }
|
||||
let(:wrong_ay) { create(:academic_year, range: '9999-99') }
|
||||
let(:black) { create(:race, designation: 'black', qualtrics_code: 1) }
|
||||
let(:white) { create(:race, designation: 'white', qualtrics_code: 2) }
|
||||
let(:wrong_race) { create(:race, designation: 'wrong race', qualtrics_code: 9999) }
|
||||
let(:female) { create(:gender, designation: 'female', qualtrics_code: 1) }
|
||||
let(:male) { create(:gender, designation: 'male', qualtrics_code: 2) }
|
||||
let(:wrong_gender) { create(:gender, designation: 'wrong_gender', qualtrics_code: 2) }
|
||||
|
||||
context '.category' do
|
||||
before :each do
|
||||
category
|
||||
category_2
|
||||
wrong_category
|
||||
end
|
||||
context 'when a category is provided in the params hash' do
|
||||
it 'returns the category with the given id' do
|
||||
params = { category: '1' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.category).to eq category
|
||||
|
||||
params = { category: '2' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.category).to eq category_2
|
||||
end
|
||||
end
|
||||
|
||||
context 'when no category is provided in the params hash' do
|
||||
it 'returns the first category' do
|
||||
params = {}
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.category).to eq category
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a category that doesnt exist in the database is passed' do
|
||||
it 'returns the first category' do
|
||||
params = { category: '4' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.category).to eq category
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context '.categories' do
|
||||
it 'returns all categories' do
|
||||
params = {}
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.categories).to eq Category.all.order(:category_id)
|
||||
end
|
||||
end
|
||||
|
||||
context '.subcategory' do
|
||||
before :each do
|
||||
category
|
||||
category_2
|
||||
wrong_category
|
||||
subcategory
|
||||
subcategory_2
|
||||
wrong_subcategory
|
||||
end
|
||||
context 'when a subcategory is provided in the params hash' do
|
||||
it 'returns the subcategory with the given id' do
|
||||
params = { subcategory: '1A' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.subcategory).to eq subcategory
|
||||
|
||||
params = { subcategory: '2A' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.subcategory).to eq subcategory_2
|
||||
end
|
||||
end
|
||||
|
||||
context 'when no subcategory is provided in the params hash' do
|
||||
it 'returns the first subcategory' do
|
||||
params = {}
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.subcategory).to eq subcategory
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a subcategory that doesnt exist in the database is passed' do
|
||||
it 'returns the first subcategory' do
|
||||
params = { subcategory: '4A' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.subcategory).to eq subcategory
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context '.subcategories' do
|
||||
before :each do
|
||||
category
|
||||
wrong_category
|
||||
subcategory
|
||||
wrong_subcategory
|
||||
end
|
||||
it 'returns all subcategories for a given category' do
|
||||
params = { category: '1' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.subcategories).to eq category.subcategories.all.order(:subcategory_id)
|
||||
end
|
||||
end
|
||||
|
||||
context '.measures' do
|
||||
before :each do
|
||||
category
|
||||
subcategory
|
||||
measure
|
||||
end
|
||||
|
||||
it 'returns all measures for a given subcategory' do
|
||||
params = { category: '1' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.measures).to eq category.subcategories.flat_map(&:measures)
|
||||
end
|
||||
end
|
||||
|
||||
context '.academic_years' do
|
||||
before :each do
|
||||
ay_2021_22
|
||||
ay_2022_23
|
||||
end
|
||||
|
||||
it 'returns all academic years' do
|
||||
params = {}
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.academic_years).to eq AcademicYear.all.order(:range)
|
||||
end
|
||||
end
|
||||
|
||||
context '.selected_academic_years' do
|
||||
before :each do
|
||||
ay_2021_22
|
||||
ay_2022_23
|
||||
wrong_ay
|
||||
end
|
||||
context 'when no academic years are provided in the params hash' do
|
||||
it 'returns an empty array if no params are provided' do
|
||||
params = {}
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.selected_academic_years).to eq []
|
||||
end
|
||||
end
|
||||
context 'when a single academic year is provided in the params hash' do
|
||||
it 'returns the academic year with the given id' do
|
||||
params = { academic_years: '2021-22' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.selected_academic_years).to eq [AcademicYear.find_by_range('2021-22')]
|
||||
|
||||
params = { academic_years: '2022-23' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.selected_academic_years).to eq [AcademicYear.find_by_range('2022-23')]
|
||||
end
|
||||
end
|
||||
|
||||
context 'when multiple academic years are provided in the params hash' do
|
||||
it 'returns the academic year with the given ids' do
|
||||
params = { academic_years: '2021-22,2022-23' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.selected_academic_years).to eq [AcademicYear.find_by_range('2021-22'),
|
||||
AcademicYear.find_by_range('2022-23')]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context '.races' do
|
||||
before :each do
|
||||
black
|
||||
white
|
||||
wrong_race
|
||||
end
|
||||
|
||||
it 'returns all races' do
|
||||
params = {}
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.races).to eq Race.all.order(designation: :ASC)
|
||||
end
|
||||
end
|
||||
|
||||
context '.selected_races' do
|
||||
before :each do
|
||||
black
|
||||
white
|
||||
wrong_race
|
||||
end
|
||||
context 'when no races are provided in the params hash' do
|
||||
it 'returns an array with all races if no params are provided' do
|
||||
params = {}
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.selected_races).to eq presenter.races
|
||||
end
|
||||
end
|
||||
|
||||
context 'when one race is provided in the params hash' do
|
||||
it 'returns a single race with the given slug' do
|
||||
params = { races: 'white' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.selected_races).to eq [Race.find_by_slug('white')]
|
||||
|
||||
params = { races: 'black' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.selected_races).to eq [Race.find_by_slug('black')]
|
||||
end
|
||||
end
|
||||
|
||||
context 'when multiple races are provided in the params hash' do
|
||||
it 'returns multiple races with the given slugs' do
|
||||
params = { races: 'white,black' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.selected_races).to eq [Race.find_by_slug('white'), Race.find_by_slug('black')]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context '.graph' do
|
||||
context 'when no params are provided' do
|
||||
it 'returns the default(first) graph object' do
|
||||
params = {}
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.graph.to_s).to eq Analyze::Graph::AllData.new.to_s
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a graph is provided in the params hash' do
|
||||
it 'returns the graph object with the given slug' do
|
||||
params = { graph: 'all_data' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.graph.to_s).to eq Analyze::Graph::AllData.new.to_s
|
||||
|
||||
params = { graph: 'students-and-teachers' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.graph.to_s).to eq Analyze::Graph::StudentsAndTeachers.new.to_s
|
||||
|
||||
params = { graph: 'students-by-race' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.graph.to_s).to eq Analyze::Graph::StudentsByRace.new(races: nil).to_s
|
||||
|
||||
params = { graph: 'students-by-grade' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.graph.to_s).to eq Analyze::Graph::StudentsByGrade.new(grades: nil).to_s
|
||||
|
||||
params = { graph: 'students-by-gender' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.graph.to_s).to eq Analyze::Graph::StudentsByGender.new(genders: nil).to_s
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a parameter that does not match a graph is provided' do
|
||||
it 'returns the default(first) graph object' do
|
||||
params = { graph: 'invalid parameter' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.graph.to_s).to eq Analyze::Graph::AllData.new.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context '.grades' do
|
||||
before :each do
|
||||
school
|
||||
academic_year
|
||||
create_list(:survey_item_response, 20, school:, academic_year:, grade: 1, survey_item:)
|
||||
create_list(:survey_item_response, 20, school:, academic_year:, grade: 2, survey_item:)
|
||||
create_list(:survey_item_response, 20, school:, academic_year:, grade: 3, survey_item:)
|
||||
create_list(:survey_item_response, 20, school:, academic_year:, grade: 4, survey_item:)
|
||||
end
|
||||
|
||||
context 'when no grades are provided in the params hash' do
|
||||
it 'returns the set of grades for which there are more than ten responses' do
|
||||
params = {}
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.grades).to eq [1, 2, 3, 4]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context '.selected_grades' do
|
||||
before :each do
|
||||
school
|
||||
academic_year
|
||||
create_list(:survey_item_response, 20, school:, academic_year:, grade: 1, survey_item:)
|
||||
create_list(:survey_item_response, 20, school:, academic_year:, grade: 2, survey_item:)
|
||||
create_list(:survey_item_response, 20, school:, academic_year:, grade: 3, survey_item:)
|
||||
create_list(:survey_item_response, 20, school:, academic_year:, grade: 4, survey_item:)
|
||||
end
|
||||
|
||||
context 'when no grades are provided in the params hash' do
|
||||
it 'returns only the set of grades selected even if other grades have sufficient responses' do
|
||||
params = { grades: '1,2,3' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.selected_grades).to eq [1, 2, 3]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context '.selected_genders' do
|
||||
before :each do
|
||||
male
|
||||
female
|
||||
wrong_gender
|
||||
end
|
||||
context 'when no parameters are provided' do
|
||||
it 'returns all genders' do
|
||||
params = {}
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.selected_genders).to eq Gender.all
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a single gender is provided in the params hash' do
|
||||
it 'returns the gender with the given designation' do
|
||||
params = { genders: 'female' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.selected_genders).to eq [Gender.find_by_designation('female')]
|
||||
|
||||
params = { genders: 'male' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.selected_genders).to eq [Gender.find_by_designation('male')]
|
||||
end
|
||||
end
|
||||
|
||||
context 'when multiple genders are provided in the params hash' do
|
||||
it 'returns multilple genders with the given designations' do
|
||||
params = { genders: 'female,male' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.selected_genders).to eq [Gender.find_by_designation('female'),
|
||||
Gender.find_by_designation('male')]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context '.genders' do
|
||||
before :each do
|
||||
female
|
||||
male
|
||||
end
|
||||
|
||||
it 'returns all genders' do
|
||||
params = {}
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.genders).to eq Gender.all
|
||||
end
|
||||
end
|
||||
|
||||
context '.group' do
|
||||
context 'when no parameters are provided' do
|
||||
it 'returns the first item in the list of groups' do
|
||||
params = {}
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.group.slug).to eq presenter.groups.first.slug
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a group is provided in the params hash' do
|
||||
it 'returns the group with the given slug' do
|
||||
params = { group: 'gender' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.group.slug).to eq 'gender'
|
||||
|
||||
params = { group: 'grade' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.group.slug).to eq 'grade'
|
||||
|
||||
params = { group: 'race' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.group.slug).to eq 'race'
|
||||
|
||||
# Not yet implemented
|
||||
# params = { group: 'income' }
|
||||
# presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
# expect(presenter.group.slug).to eq 'income'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a parameter that does not match a group is provided' do
|
||||
it 'returns the first item in the list of groups' do
|
||||
params = { group: 'invalid group' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.group.slug).to eq presenter.groups.first.slug
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context '.slice' do
|
||||
context 'when no parameters are provided' do
|
||||
it 'returns the first item in the list of slices' do
|
||||
params = {}
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.slice.slug).to eq 'all-data'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a slice is provided in the params hash' do
|
||||
it 'returns the slice with the given slug' do
|
||||
params = { source: 'survey-data-only', slice: 'students-and-teachers' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.slice.slug).to eq 'students-and-teachers'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a slice is provided but the source is left blank ' do
|
||||
it 'returns the slice from the default source (all-data)' do
|
||||
params = { slice: 'students-and-teachers' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.slice.slug).to eq 'all-data'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a parameter that does not match a slice is provided' do
|
||||
it 'it returns the first slice from the chosen source' do
|
||||
params = { source: 'survey-data-only', slice: 'invalid-slice' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.slice.slug).to eq 'students-and-teachers'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context '.source' do
|
||||
context 'when no parameters are provided' do
|
||||
it 'returns the first item in the list of sources' do
|
||||
params = {}
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.slice.slug).to eq 'all-data'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a source is provided in the params hash' do
|
||||
it 'returns the source with the given slug' do
|
||||
params = { source: 'all-data' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.source.slug).to eq 'all-data'
|
||||
|
||||
params = { source: 'survey-data-only' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.source.slug).to eq 'survey-data-only'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a parameter that does not match a source is provided' do
|
||||
it 'returns the first item in the list of sources' do
|
||||
params = { source: 'invalid-source' }
|
||||
presenter = Analyze::Presenter.new(params:, school:, academic_year:)
|
||||
expect(presenter.slice.slug).to eq 'all-data'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -227,12 +227,12 @@ describe GroupedBarColumnPresenter do
|
|||
# it_behaves_like 'y_offset'
|
||||
|
||||
context 'and the score is right at the approval low benchmark' do
|
||||
it "where bar would normally have a height of 0, we inflate the height to be at least the minimum bar height of #{AnalyzeBarPresenter::MINIMUM_BAR_HEIGHT}" do
|
||||
expect(student_presenter.bars[year_index].bar_height_percentage).to be_within(0.01).of(AnalyzeBarPresenter::MINIMUM_BAR_HEIGHT)
|
||||
it "where bar would normally have a height of 0, we inflate the height to be at least the minimum bar height of #{Analyze::BarPresenter::MINIMUM_BAR_HEIGHT}" do
|
||||
expect(student_presenter.bars[year_index].bar_height_percentage).to be_within(0.01).of(Analyze::BarPresenter::MINIMUM_BAR_HEIGHT)
|
||||
end
|
||||
|
||||
it "where the bar would normally start at the approval low benchmark, it shifts up to accomodate it being grown to the minimum bar height of #{AnalyzeBarPresenter::MINIMUM_BAR_HEIGHT}" do
|
||||
expect(student_presenter.bars[year_index].y_offset).to be_within(0.01).of(analyze_zone_height * 2 - AnalyzeBarPresenter::MINIMUM_BAR_HEIGHT)
|
||||
it "where the bar would normally start at the approval low benchmark, it shifts up to accomodate it being grown to the minimum bar height of #{Analyze::BarPresenter::MINIMUM_BAR_HEIGHT}" do
|
||||
expect(student_presenter.bars[year_index].y_offset).to be_within(0.01).of(analyze_zone_height * 2 - Analyze::BarPresenter::MINIMUM_BAR_HEIGHT)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -257,8 +257,8 @@ describe GroupedBarColumnPresenter do
|
|||
academic_year:, likert_score: 4)
|
||||
end
|
||||
|
||||
it "it rounds to the the minimum bar height of #{AnalyzeBarPresenter::MINIMUM_BAR_HEIGHT} " do
|
||||
expect(student_presenter.bars[year_index].bar_height_percentage).to be_within(0.01).of(AnalyzeBarPresenter::MINIMUM_BAR_HEIGHT)
|
||||
it "it rounds to the the minimum bar height of #{Analyze::BarPresenter::MINIMUM_BAR_HEIGHT} " do
|
||||
expect(student_presenter.bars[year_index].bar_height_percentage).to be_within(0.01).of(Analyze::BarPresenter::MINIMUM_BAR_HEIGHT)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,29 +1,46 @@
|
|||
require 'rails_helper'
|
||||
require 'fileutils'
|
||||
require "rails_helper"
|
||||
require "fileutils"
|
||||
|
||||
RSpec.describe Cleaner do
|
||||
let(:district) { create(:district, name: 'District1') }
|
||||
let(:second_district) { create(:district, name: 'District2') }
|
||||
let(:district) { create(:district, name: "Maynard Public Schools") }
|
||||
let(:second_district) { create(:district, name: "District2") }
|
||||
let(:school) { create(:school, dese_id: 1_740_505, district:) }
|
||||
let(:second_school) { create(:school, dese_id: 222_222, district: second_district) }
|
||||
let(:second_school) { create(:school, dese_id: 1_740_305, district:) }
|
||||
let(:third_school) { create(:school, dese_id: 222_222, district: second_district) }
|
||||
|
||||
let(:academic_year) { create(:academic_year, range: '2022-23') }
|
||||
let(:respondents) { create(:respondent, school:, academic_year:, nine: 40, ten: 40, eleven: 40, twelve: 40) }
|
||||
let(:recorded_date) { '2023-04-01' }
|
||||
let(:academic_year) { create(:academic_year, range: "2022-23") }
|
||||
let(:respondents) do
|
||||
create(:respondent, school:, academic_year:, one: 0, nine: 40, ten: 40, eleven: 40, twelve: 40)
|
||||
create(:respondent, school: second_school, academic_year:, one: 0, four: 40, five: 40, six: 40, seven: 40,
|
||||
eight: 40)
|
||||
end
|
||||
let(:recorded_date) { "2023-04-01" }
|
||||
let(:input_filepath) do
|
||||
Rails.root.join('spec', 'fixtures', 'raw')
|
||||
Rails.root.join("spec", "fixtures", "raw")
|
||||
end
|
||||
|
||||
let(:output_filepath) do
|
||||
Rails.root.join('tmp', 'spec', 'clean')
|
||||
Rails.root.join("tmp", "spec", "clean")
|
||||
end
|
||||
|
||||
let(:log_filepath) do
|
||||
Rails.root.join('tmp', 'spec', 'removed')
|
||||
Rails.root.join("tmp", "spec", "removed")
|
||||
end
|
||||
|
||||
let(:disaggregation_filepath) do
|
||||
Rails.root.join("spec", "fixtures", "disaggregation")
|
||||
end
|
||||
|
||||
let(:path_to_sample_disaggregation_file) do
|
||||
File.open(Rails.root.join("spec", "fixtures", "disaggregation", "sample_maynard_disaggregation_data.csv"))
|
||||
end
|
||||
|
||||
let(:path_to_sample_raw_file) do
|
||||
File.open(Rails.root.join("spec", "fixtures", "raw", "sample_maynard_raw_student_survey.csv"))
|
||||
end
|
||||
|
||||
let(:common_headers) do
|
||||
['Recorded Date', 'Dese ID', 'ResponseID']
|
||||
["Recorded Date", "DeseID", "ResponseID"]
|
||||
end
|
||||
|
||||
let(:standard_survey_items) do
|
||||
|
|
@ -41,16 +58,16 @@ RSpec.describe Cleaner do
|
|||
end
|
||||
|
||||
let(:short_form_survey_items) do
|
||||
([create(:survey_item, survey_item_id: 's-phys-q1', on_short_form: true),
|
||||
create(:survey_item, survey_item_id: 's-phys-q2', on_short_form: true),
|
||||
create(:survey_item, survey_item_id: 's-phys-q3',
|
||||
([create(:survey_item, survey_item_id: "s-phys-q1", on_short_form: true),
|
||||
create(:survey_item, survey_item_id: "s-phys-q2", on_short_form: true),
|
||||
create(:survey_item, survey_item_id: "s-phys-q3",
|
||||
on_short_form: true)].map(&:survey_item_id) << common_headers).flatten
|
||||
end
|
||||
|
||||
let(:early_education_survey_items) do
|
||||
([create(:survey_item, survey_item_id: 's-emsa-es1'),
|
||||
create(:survey_item, survey_item_id: 's-emsa-es2'),
|
||||
create(:survey_item, survey_item_id: 's-emsa-es3')].map(&:survey_item_id) << common_headers).flatten
|
||||
([create(:survey_item, survey_item_id: "s-emsa-es1"),
|
||||
create(:survey_item, survey_item_id: "s-emsa-es2"),
|
||||
create(:survey_item, survey_item_id: "s-emsa-es3")].map(&:survey_item_id) << common_headers).flatten
|
||||
end
|
||||
|
||||
let(:teacher_survey_items) do
|
||||
|
|
@ -71,6 +88,7 @@ RSpec.describe Cleaner do
|
|||
before :each do
|
||||
school
|
||||
second_school
|
||||
third_school
|
||||
standard_survey_items
|
||||
short_form_survey_items
|
||||
early_education_survey_items
|
||||
|
|
@ -79,84 +97,231 @@ RSpec.describe Cleaner do
|
|||
respondents
|
||||
end
|
||||
|
||||
context 'Creating a new Cleaner' do
|
||||
it 'creates a directory for the clean data' do
|
||||
Cleaner.new(input_filepath:, output_filepath:, log_filepath:).clean
|
||||
context "Creating a new Cleaner" do
|
||||
it "creates a directory for the clean data" do
|
||||
Cleaner.new(input_filepath:, output_filepath:, log_filepath:, disaggregation_filepath:).clean
|
||||
expect(output_filepath).to exist
|
||||
end
|
||||
|
||||
it 'creates a directory for the removed data' do
|
||||
Cleaner.new(input_filepath:, output_filepath:, log_filepath:).clean
|
||||
it "creates a directory for the removed data" do
|
||||
Cleaner.new(input_filepath:, output_filepath:, log_filepath:, disaggregation_filepath:).clean
|
||||
expect(log_filepath).to exist
|
||||
end
|
||||
end
|
||||
|
||||
context '.filename' do
|
||||
context 'defines a filename in the format: [district].[early_ed/short_form/standard/teacher].[year as 2022-23]' do
|
||||
context 'when the file is based on standard survey items' do
|
||||
it 'adds the survey type as standard to the filename' do
|
||||
context ".process_raw_file" do
|
||||
it "sorts data into valid and invalid csvs" do
|
||||
cleaner = Cleaner.new(input_filepath:, output_filepath:, log_filepath:, disaggregation_filepath:)
|
||||
processed_data = cleaner.process_raw_file(
|
||||
file: path_to_sample_raw_file, disaggregation_data: cleaner.disaggregation_data
|
||||
)
|
||||
processed_data in [headers, clean_csv, log_csv, data]
|
||||
|
||||
reads_headers_from_raw_csv(processed_data)
|
||||
|
||||
valid_rows = %w[1000 1001 1004 1005 1008 1017 1018 1019 1020 1024 1025 1026
|
||||
1027 1028]
|
||||
valid_rows.each do |response_id|
|
||||
valid_row = data.find { |row| row.response_id == response_id }
|
||||
expect(valid_row.valid?).to eq true
|
||||
end
|
||||
|
||||
invalid_rows = %w[1002 1003 1006 1007 1009 1010 1011 1012 1013 1014 1015 1016 1021 1022 1023 1029 1030 1031 1032
|
||||
1033 1034]
|
||||
invalid_rows.each do |response_id|
|
||||
invalid_row = data.find { |row| row.response_id == response_id }
|
||||
expect(invalid_row.valid?).to eq false
|
||||
end
|
||||
|
||||
expect(clean_csv.length).to eq valid_rows.length + 1 # headers + rows
|
||||
expect(log_csv.length).to eq invalid_rows.length + 1 # headers + rows
|
||||
|
||||
csv_contains_the_correct_rows(clean_csv, valid_rows)
|
||||
csv_contains_the_correct_rows(log_csv, invalid_rows)
|
||||
invalid_rows_are_rejected_for_the_correct_reasons(data)
|
||||
end
|
||||
|
||||
it "adds dissaggregation data to the cleaned file " do
|
||||
cleaner = Cleaner.new(input_filepath:, output_filepath:, log_filepath:, disaggregation_filepath:)
|
||||
processed_data = cleaner.process_raw_file(
|
||||
file: path_to_sample_raw_file, disaggregation_data: cleaner.disaggregation_data
|
||||
)
|
||||
processed_data in [headers, clean_csv, log_csv, data]
|
||||
expect(clean_csv.second.last).to eq "Economically Disadvantaged - Y"
|
||||
|
||||
one_thousand = data.find { |row| row.response_id == "1000" }
|
||||
expect(one_thousand.income).to eq "Economically Disadvantaged - Y"
|
||||
|
||||
one_thousand_one = data.find { |row| row.response_id == "1001" }
|
||||
expect(one_thousand_one.income).to eq "Economically Disadvantaged - N"
|
||||
end
|
||||
end
|
||||
|
||||
context ".filename" do
|
||||
context "defines a filename in the format: [district].[early_ed/short_form/standard/teacher].[year as 2022-23]" do
|
||||
context "when the file is based on standard survey items" do
|
||||
it "adds the survey type as standard to the filename" do
|
||||
survey_items = SurveyItem.where(survey_item_id: standard_survey_items)
|
||||
|
||||
data = [SurveyItemValues.new(row: { 'Recorded Date' => recorded_date, 'Dese ID' => '1_740_505' }, headers: standard_survey_items, genders: nil, survey_items:,
|
||||
data = [SurveyItemValues.new(row: { "Recorded Date" => recorded_date, "Dese ID" => "1_740_505" }, headers: standard_survey_items, genders: nil, survey_items:,
|
||||
schools: School.school_hash)]
|
||||
filename = Cleaner.new(input_filepath:, output_filepath:, log_filepath:).filename(
|
||||
filename = Cleaner.new(input_filepath:, output_filepath:, log_filepath:, disaggregation_filepath:).filename(
|
||||
headers: standard_survey_items, data:
|
||||
)
|
||||
expect(filename).to eq 'district1.standard.2022-23.csv'
|
||||
expect(filename).to eq "maynard.standard.2022-23.csv"
|
||||
end
|
||||
|
||||
context 'when the file is based on short form survey items' do
|
||||
it 'adds the survey type as short form to the filename' do
|
||||
context "when the file is based on short form survey items" do
|
||||
it "adds the survey type as short form to the filename" do
|
||||
survey_items = SurveyItem.where(survey_item_id: short_form_survey_items)
|
||||
|
||||
data = [SurveyItemValues.new(row: { 'Recorded Date' => recorded_date, 'Dese ID' => '1_740_505' }, headers: short_form_survey_items, genders: nil, survey_items:,
|
||||
data = [SurveyItemValues.new(row: { "Recorded Date" => recorded_date, "Dese ID" => "1_740_505" }, headers: short_form_survey_items, genders: nil, survey_items:,
|
||||
schools: School.school_hash)]
|
||||
filename = Cleaner.new(input_filepath:, output_filepath:, log_filepath:).filename(
|
||||
filename = Cleaner.new(input_filepath:, output_filepath:, log_filepath:, disaggregation_filepath:).filename(
|
||||
headers: short_form_survey_items, data:
|
||||
)
|
||||
expect(filename).to eq 'district1.short_form.2022-23.csv'
|
||||
expect(filename).to eq "maynard.short_form.2022-23.csv"
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the file is based on early education survey items' do
|
||||
it 'adds the survey type as early education to the filename' do
|
||||
context "when the file is based on early education survey items" do
|
||||
it "adds the survey type as early education to the filename" do
|
||||
survey_items = SurveyItem.where(survey_item_id: early_education_survey_items)
|
||||
|
||||
data = [SurveyItemValues.new(row: { 'Recorded Date' => recorded_date, 'Dese ID' => '1_740_505' }, headers: early_education_survey_items, genders: nil, survey_items:,
|
||||
data = [SurveyItemValues.new(row: { "Recorded Date" => recorded_date, "Dese ID" => "1_740_505" }, headers: early_education_survey_items, genders: nil, survey_items:,
|
||||
schools: School.school_hash)]
|
||||
filename = Cleaner.new(input_filepath:, output_filepath:, log_filepath:).filename(
|
||||
filename = Cleaner.new(input_filepath:, output_filepath:, log_filepath:, disaggregation_filepath:).filename(
|
||||
headers: early_education_survey_items, data:
|
||||
)
|
||||
expect(filename).to eq 'district1.early_education.2022-23.csv'
|
||||
expect(filename).to eq "maynard.early_education.2022-23.csv"
|
||||
end
|
||||
end
|
||||
context 'when the file is based on teacher survey items' do
|
||||
it 'adds the survey type as teacher to the filename' do
|
||||
context "when the file is based on teacher survey items" do
|
||||
it "adds the survey type as teacher to the filename" do
|
||||
survey_items = SurveyItem.where(survey_item_id: teacher_survey_items)
|
||||
|
||||
data = [SurveyItemValues.new(row: { 'Recorded Date' => recorded_date, 'Dese ID' => '1_740_505' }, headers: teacher_survey_items, genders: nil, survey_items:,
|
||||
data = [SurveyItemValues.new(row: { "Recorded Date" => recorded_date, "Dese ID" => "1_740_505" }, headers: teacher_survey_items, genders: nil, survey_items:,
|
||||
schools: School.school_hash)]
|
||||
filename = Cleaner.new(input_filepath:, output_filepath:, log_filepath:).filename(
|
||||
filename = Cleaner.new(input_filepath:, output_filepath:, log_filepath:, disaggregation_filepath:).filename(
|
||||
headers: teacher_survey_items, data:
|
||||
)
|
||||
expect(filename).to eq 'district1.teacher.2022-23.csv'
|
||||
expect(filename).to eq "maynard.teacher.2022-23.csv"
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is more than one district' do
|
||||
it 'adds all districts to the filename' do
|
||||
context "when there is more than one district" do
|
||||
it "adds all districts to the filename" do
|
||||
survey_items = SurveyItem.where(survey_item_id: teacher_survey_items)
|
||||
|
||||
data = [SurveyItemValues.new(row: { 'Recorded Date' => recorded_date, 'Dese ID' => '1_740_505' }, headers: teacher_survey_items, genders: nil, survey_items:, schools: School.school_hash),
|
||||
SurveyItemValues.new(row: { 'Recorded Date' => recorded_date, 'Dese ID' => '222_222' },
|
||||
data = [SurveyItemValues.new(row: { "Recorded Date" => recorded_date, "Dese ID" => "1_740_505" }, headers: teacher_survey_items, genders: nil, survey_items:, schools: School.school_hash),
|
||||
SurveyItemValues.new(row: { "Recorded Date" => recorded_date, "Dese ID" => "222_222" },
|
||||
headers: teacher_survey_items, genders: nil, survey_items:, schools: School.school_hash)]
|
||||
filename = Cleaner.new(input_filepath:, output_filepath:, log_filepath:).filename(
|
||||
filename = Cleaner.new(input_filepath:, output_filepath:, log_filepath:, disaggregation_filepath:).filename(
|
||||
headers: teacher_survey_items, data:
|
||||
)
|
||||
expect(filename).to eq 'district1.district2.teacher.2022-23.csv'
|
||||
expect(filename).to eq "maynard.district2.teacher.2022-23.csv"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def reads_headers_from_raw_csv(processed_data)
|
||||
processed_data in [headers, clean_csv, log_csv, data]
|
||||
expect(headers).to eq ["StartDate", "EndDate", "Status", "IPAddress", "Progress", "Duration (in seconds)",
|
||||
"Finished", "RecordedDate", "ResponseId", "District", "School",
|
||||
"LASID", "Gender", "Race", "What grade are you in?", "s-emsa-q1", "s-emsa-q2", "s-emsa-q3", "s-tint-q1",
|
||||
"s-tint-q2", "s-tint-q3", "s-tint-q4", "s-tint-q5", "s-acpr-q1", "s-acpr-q2",
|
||||
"s-acpr-q3", "s-acpr-q4", "s-cure-q1", "s-cure-q2", "s-cure-q3", "s-cure-q4", "s-sten-q1", "s-sten-q2",
|
||||
"s-sten-q3", "s-sper-q1", "s-sper-q2", "s-sper-q3", "s-sper-q4", "s-civp-q1", "s-civp-q2", "s-civp-q3",
|
||||
"s-civp-q4", "s-grmi-q1", "s-grmi-q2", "s-grmi-q3", "s-grmi-q4", "s-appa-q1", "s-appa-q2", "s-appa-q3",
|
||||
"s-peff-q1", "s-peff-q2", "s-peff-q3", "s-peff-q4", "s-peff-q5", "s-peff-q6", "s-sbel-q1", "s-sbel-q2",
|
||||
"s-sbel-q3", "s-sbel-q4", "s-sbel-q5", "s-phys-q1", "s-phys-q2", "s-phys-q3", "s-phys-q4", "s-vale-q1",
|
||||
"s-vale-q2", "s-vale-q3", "s-vale-q4", "s-acst-q1", "s-acst-q2", "s-acst-q3", "s-sust-q1", "s-sust-q2",
|
||||
"s-grit-q1", "s-grit-q2", "s-grit-q3", "s-grit-q4", "s-expa-q1", "s-poaf-q1", "s-poaf-q2", "s-poaf-q3",
|
||||
"s-poaf-q4", "s-tint-q1-1", "s-tint-q2-1", "s-tint-q3-1", "s-tint-q4-1", "s-tint-q5-1", "s-acpr-q1-1",
|
||||
"s-acpr-q2-1", "s-acpr-q3-1", "s-acpr-q4-1", "s-peff-q1-1", "s-peff-q2-1", "s-peff-q3-1", "s-peff-q4-1",
|
||||
"s-peff-q5-1", "s-peff-q6-1", "Raw Income", "Income"]
|
||||
end
|
||||
|
||||
def invalid_rows_are_rejected_for_the_correct_reasons(data)
|
||||
one_thousand_two = data.find { |row| row.response_id == "1002" }
|
||||
expect(one_thousand_two.valid_progress?).to eq false
|
||||
expect(one_thousand_two.valid_duration?).to eq true
|
||||
expect(one_thousand_two.valid_grade?).to eq true
|
||||
expect(one_thousand_two.valid_sd?).to eq true
|
||||
|
||||
one_thousand_three = data.find { |row| row.response_id == "1003" }
|
||||
expect(one_thousand_three.valid_progress?).to eq false
|
||||
expect(one_thousand_three.valid_duration?).to eq true
|
||||
expect(one_thousand_three.valid_grade?).to eq true
|
||||
expect(one_thousand_three.valid_sd?).to eq true
|
||||
|
||||
one_thousand_six = data.find { |row| row.response_id == "1006" }
|
||||
expect(one_thousand_six.valid_progress?).to eq true
|
||||
expect(one_thousand_six.valid_duration?).to eq false
|
||||
expect(one_thousand_six.valid_grade?).to eq true
|
||||
expect(one_thousand_six.valid_sd?).to eq true
|
||||
|
||||
one_thousand_seven = data.find { |row| row.response_id == "1007" }
|
||||
expect(one_thousand_seven.valid_progress?).to eq true
|
||||
expect(one_thousand_seven.valid_duration?).to eq false
|
||||
expect(one_thousand_seven.valid_grade?).to eq true
|
||||
expect(one_thousand_seven.valid_sd?).to eq true
|
||||
|
||||
one_thousand_seven = data.find { |row| row.response_id == "1007" }
|
||||
expect(one_thousand_seven.valid_progress?).to eq true
|
||||
expect(one_thousand_seven.valid_duration?).to eq false
|
||||
expect(one_thousand_seven.valid_grade?).to eq true
|
||||
expect(one_thousand_seven.valid_sd?).to eq true
|
||||
|
||||
one_thousand_nine = data.find { |row| row.response_id == "1009" }
|
||||
expect(one_thousand_nine.valid_progress?).to eq true
|
||||
expect(one_thousand_nine.valid_duration?).to eq true
|
||||
expect(one_thousand_nine.valid_grade?).to eq false
|
||||
expect(one_thousand_nine.valid_sd?).to eq true
|
||||
|
||||
one_thousand_ten = data.find { |row| row.response_id == "1010" }
|
||||
expect(one_thousand_ten.valid_progress?).to eq true
|
||||
expect(one_thousand_ten.valid_duration?).to eq true
|
||||
expect(one_thousand_ten.valid_grade?).to eq false
|
||||
expect(one_thousand_ten.valid_sd?).to eq true
|
||||
|
||||
one_thousand_eleven = data.find { |row| row.response_id == "1011" }
|
||||
expect(one_thousand_eleven.valid_progress?).to eq true
|
||||
expect(one_thousand_eleven.valid_duration?).to eq true
|
||||
expect(one_thousand_eleven.valid_grade?).to eq false
|
||||
expect(one_thousand_eleven.valid_sd?).to eq true
|
||||
|
||||
one_thousand_twenty_two = data.find { |row| row.response_id == "1022" }
|
||||
expect(one_thousand_twenty_two.valid_progress?).to eq true
|
||||
expect(one_thousand_twenty_two.valid_duration?).to eq true
|
||||
expect(one_thousand_twenty_two.valid_grade?).to eq false
|
||||
expect(one_thousand_twenty_two.valid_sd?).to eq true
|
||||
|
||||
one_thousand_twenty_three = data.find { |row| row.response_id == "1023" }
|
||||
expect(one_thousand_twenty_three.valid_progress?).to eq true
|
||||
expect(one_thousand_twenty_three.valid_duration?).to eq true
|
||||
expect(one_thousand_twenty_three.valid_grade?).to eq false
|
||||
expect(one_thousand_twenty_three.valid_sd?).to eq true
|
||||
|
||||
one_thousand_thirty_three = data.find { |row| row.response_id == "1033" }
|
||||
expect(one_thousand_thirty_three.valid_progress?).to eq true
|
||||
expect(one_thousand_thirty_three.valid_duration?).to eq true
|
||||
expect(one_thousand_thirty_three.valid_grade?).to eq true
|
||||
expect(one_thousand_thirty_three.valid_sd?).to eq false
|
||||
|
||||
one_thousand_thirty_four = data.find { |row| row.response_id == "1034" }
|
||||
expect(one_thousand_thirty_four.valid_progress?).to eq true
|
||||
expect(one_thousand_thirty_four.valid_duration?).to eq true
|
||||
expect(one_thousand_thirty_four.valid_grade?).to eq true
|
||||
expect(one_thousand_thirty_four.valid_sd?).to eq false
|
||||
end
|
||||
|
||||
def csv_contains_the_correct_rows(csv, rows)
|
||||
rows.each_with_index do |row, index|
|
||||
response_id = 8 # eigth column
|
||||
expect(csv[index + 1][response_id]).to eq row
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -13,6 +13,10 @@ describe DemographicLoader do
|
|||
}
|
||||
end
|
||||
|
||||
let(:incomes) do
|
||||
['Economically Disadvantaged – N', 'Economically Disadvantaged – Y', 'Unknown']
|
||||
end
|
||||
|
||||
before :each do
|
||||
DemographicLoader.load_data(filepath:)
|
||||
end
|
||||
|
|
@ -46,5 +50,12 @@ describe DemographicLoader do
|
|||
expect(Gender.find_by_designation(key).qualtrics_code).to eq value
|
||||
end
|
||||
end
|
||||
|
||||
it 'loads all the income designations' do
|
||||
expect(Income.all.count).to eq 3
|
||||
incomes.each do |income|
|
||||
expect(Income.find_by_designation(income).designation).to eq income
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
35
spec/services/disaggregation_loader_spec.rb
Normal file
35
spec/services/disaggregation_loader_spec.rb
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
require "rails_helper"
|
||||
require "fileutils"
|
||||
|
||||
RSpec.describe DisaggregationLoader do
|
||||
let(:path) do
|
||||
Rails.root.join("spec", "fixtures", "disaggregation")
|
||||
end
|
||||
let(:academic_year) { create(:academic_year, range: "2022-23") }
|
||||
let(:district) { create(:district, name: "Maynard Public Schools") }
|
||||
context ".load" do
|
||||
it "loads data from the file into a hash" do
|
||||
data = DisaggregationLoader.new(path:).load
|
||||
expect(data.values.first.lasid).to eq("1")
|
||||
expect(data.values.first.academic_year).to eq("2022-23")
|
||||
expect(data.values.first.district).to eq("Maynard Public Schools")
|
||||
expect(data.values.first.income).to eq("Free Lunch")
|
||||
|
||||
expect(data.values.last.lasid).to eq("500")
|
||||
expect(data.values.last.academic_year).to eq("2022-23")
|
||||
expect(data.values.last.district).to eq("Maynard Public Schools")
|
||||
expect(data.values.last.income).to eq("Not Eligible")
|
||||
|
||||
expect(data[["1", "Maynard Public Schools", "2022-23"]].income).to eq("Free Lunch")
|
||||
expect(data[["2", "Maynard Public Schools", "2022-23"]].income).to eq("Not Eligible")
|
||||
expect(data[["3", "Maynard Public Schools", "2022-23"]].income).to eq("Reduced Lunch")
|
||||
end
|
||||
end
|
||||
|
||||
context "Creating a new loader" do
|
||||
it "creates a directory for the loader file" do
|
||||
DisaggregationLoader.new(path:)
|
||||
expect(path).to exist
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
require 'rails_helper'
|
||||
|
||||
describe RaceScoreLoader do
|
||||
let(:measure) { create(:measure, :with_student_survey_items) }
|
||||
let(:school) { create(:school) }
|
||||
let(:academic_year) { create(:academic_year) }
|
||||
let(:race) { create(:race) }
|
||||
let(:students) do
|
||||
[].tap do |arr|
|
||||
10.times do
|
||||
s = create(:student)
|
||||
s.races << race
|
||||
s.save
|
||||
arr << s
|
||||
end
|
||||
end
|
||||
end
|
||||
let(:survey_item_1) { measure.survey_items[0] }
|
||||
let(:survey_item_2) { measure.survey_items[1] }
|
||||
let(:survey_item_3) { measure.survey_items[2] }
|
||||
let(:response_rate) do
|
||||
create(:response_rate, school:, academic_year:, subcategory: measure.subcategory, meets_student_threshold: true)
|
||||
end
|
||||
|
||||
# I'm not sure how to securely make available the key_derivation_salt for github actions. Disabling the tests
|
||||
context 'when survey item responses exist' do
|
||||
before :each do
|
||||
response_rate
|
||||
students.each do |student|
|
||||
create(:survey_item_response, school:, academic_year:, likert_score: 2, survey_item: survey_item_1, student:)
|
||||
end
|
||||
students.each do |student|
|
||||
create(:survey_item_response, school:, academic_year:, likert_score: 3, survey_item: survey_item_2, student:)
|
||||
end
|
||||
|
||||
RaceScoreLoader.reset
|
||||
end
|
||||
it 'returns a list of averages' do
|
||||
expect(measure.student_survey_items.count).to eq 2
|
||||
american_indian_score = RaceScore.find_by(measure:, school:, academic_year:, race:)
|
||||
expect(american_indian_score.average).to eq 2.5
|
||||
expect(american_indian_score.meets_student_threshold).to eq true
|
||||
end
|
||||
|
||||
it 'is idempotent' do
|
||||
original_count = RaceScore.count
|
||||
RaceScoreLoader.reset
|
||||
new_count = RaceScore.count
|
||||
expect(original_count).to eq new_count
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there NOT sufficient survey item responses' do
|
||||
before :each do
|
||||
response_rate
|
||||
9.times do |index|
|
||||
create(:survey_item_response, school:, academic_year:, likert_score: 2, survey_item: survey_item_1,
|
||||
student: students[index])
|
||||
end
|
||||
|
||||
9.times do |index|
|
||||
create(:survey_item_response, school:, academic_year:, likert_score: 3, survey_item: survey_item_2,
|
||||
student: students[index])
|
||||
end
|
||||
|
||||
RaceScoreLoader.reset
|
||||
end
|
||||
it 'returns a list of averages' do
|
||||
expect(measure.student_survey_items.count).to eq 2
|
||||
|
||||
expect(SurveyItemResponse.count).to eq 18
|
||||
rs = RaceScore.find_by(measure:, school:, academic_year:, race:)
|
||||
expect(rs.average).to eq 0
|
||||
expect(rs.meets_student_threshold).to eq false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,34 +1,30 @@
|
|||
require 'rails_helper'
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe SurveyItemValues, type: :model do
|
||||
let(:headers) do
|
||||
['StartDate', 'EndDate', 'Status', 'IPAddress', 'Progress', 'Duration (in seconds)', 'Finished', 'RecordedDate',
|
||||
'ResponseId', 'RecipientLastName', 'RecipientFirstName', 'RecipientEmail', 'ExternalReference', 'LocationLatitude', 'LocationLongitude', 'DistributionChannel', 'UserLanguage', 'District', 'School- Lee', 'School- Maynard', 'LASID', 'Grade', 's-emsa-q1', 's-emsa-q2', 's-emsa-q3', 's-tint-q1', 's-tint-q2', 's-tint-q3', 's-tint-q4', 's-tint-q5', 's-acpr-q1', 's-acpr-q2', 's-acpr-q3', 's-acpr-q4', 's-cure-q1', 's-cure-q2', 's-cure-q3', 's-cure-q4', 's-sten-q1', 's-sten-q2', 's-sten-q3', 's-sper-q1', 's-sper-q2', 's-sper-q3', 's-sper-q4', 's-civp-q1', 's-civp-q2', 's-civp-q3', 's-civp-q4', 's-grmi-q1', 's-grmi-q2', 's-grmi-q3', 's-grmi-q4', 's-appa-q1', 's-appa-q2', 's-appa-q3', 's-peff-q1', 's-peff-q2', 's-peff-q3', 's-peff-q4', 's-peff-q5', 's-peff-q6', 's-sbel-q1', 's-sbel-q2', 's-sbel-q3', 's-sbel-q4', 's-sbel-q5', 's-phys-q1', 's-phys-q2', 's-phys-q3', 's-phys-q4', 's-vale-q1', 's-vale-q2', 's-vale-q3', 's-vale-q4', 's-acst-q1', 's-acst-q2', 's-acst-q3', 's-sust-q1', 's-sust-q2', 's-grit-q1', 's-grit-q2', 's-grit-q3', 's-grit-q4', 's-expa-q1', 's-poaf-q1', 's-poaf-q2', 's-poaf-q3', 's-poaf-q4', 's-tint-q1-1', 's-tint-q2-1', 's-tint-q3-1', 's-tint-q4-1', 's-tint-q5-1', 's-acpr-q1-1', 's-acpr-q2-1', 's-acpr-q3-1', 's-acpr-q4-1', 's-peff-q1-1', 's-peff-q2-1', 's-peff-q3-1', 's-peff-q4-1', 's-peff-q5-1', 's-peff-q6-1', 'Gender', 'Race']
|
||||
["StartDate", "EndDate", "Status", "IPAddress", "Progress", "Duration (in seconds)", "Finished", "RecordedDate",
|
||||
"ResponseId", "RecipientLastName", "RecipientFirstName", "RecipientEmail", "ExternalReference", "LocationLatitude", "LocationLongitude", "DistributionChannel", "UserLanguage", "District", "School- Lee", "School- Maynard", "LASID", "Grade", "s-emsa-q1", "s-emsa-q2", "s-emsa-q3", "s-tint-q1", "s-tint-q2", "s-tint-q3", "s-tint-q4", "s-tint-q5", "s-acpr-q1", "s-acpr-q2", "s-acpr-q3", "s-acpr-q4", "s-cure-q1", "s-cure-q2", "s-cure-q3", "s-cure-q4", "s-sten-q1", "s-sten-q2", "s-sten-q3", "s-sper-q1", "s-sper-q2", "s-sper-q3", "s-sper-q4", "s-civp-q1", "s-civp-q2", "s-civp-q3", "s-civp-q4", "s-grmi-q1", "s-grmi-q2", "s-grmi-q3", "s-grmi-q4", "s-appa-q1", "s-appa-q2", "s-appa-q3", "s-peff-q1", "s-peff-q2", "s-peff-q3", "s-peff-q4", "s-peff-q5", "s-peff-q6", "s-sbel-q1", "s-sbel-q2", "s-sbel-q3", "s-sbel-q4", "s-sbel-q5", "s-phys-q1", "s-phys-q2", "s-phys-q3", "s-phys-q4", "s-vale-q1", "s-vale-q2", "s-vale-q3", "s-vale-q4", "s-acst-q1", "s-acst-q2", "s-acst-q3", "s-sust-q1", "s-sust-q2", "s-grit-q1", "s-grit-q2", "s-grit-q3", "s-grit-q4", "s-expa-q1", "s-poaf-q1", "s-poaf-q2", "s-poaf-q3", "s-poaf-q4", "s-tint-q1-1", "s-tint-q2-1", "s-tint-q3-1", "s-tint-q4-1", "s-tint-q5-1", "s-acpr-q1-1", "s-acpr-q2-1", "s-acpr-q3-1", "s-acpr-q4-1", "s-peff-q1-1", "s-peff-q2-1", "s-peff-q3-1", "s-peff-q4-1", "s-peff-q5-1", "s-peff-q6-1", "Gender", "Race"]
|
||||
end
|
||||
let(:genders) do
|
||||
create(:gender, qualtrics_code: 1)
|
||||
gender_hash = {}
|
||||
|
||||
Gender.all.each do |gender|
|
||||
gender_hash[gender.qualtrics_code] = gender
|
||||
end
|
||||
gender_hash
|
||||
Gender.gender_hash
|
||||
end
|
||||
let(:survey_items) { [] }
|
||||
let(:district) { create(:district, name: "Attleboro") }
|
||||
let(:attleboro) do
|
||||
create(:school, name: 'Attleboro', dese_id: 1234)
|
||||
create(:school, name: "Attleboro", dese_id: 1234, district:)
|
||||
end
|
||||
let(:attleboro_respondents) do
|
||||
create(:respondent, school: attleboro, academic_year: ay_2022_23, nine: 40, ten: 40, eleven: 40, twelve: 40)
|
||||
end
|
||||
let(:schools) { School.school_hash }
|
||||
let(:recorded_date) { '2023-04-01' }
|
||||
let(:recorded_date) { "2023-04-01" }
|
||||
let(:ay_2022_23) do
|
||||
create(:academic_year, range: '2022-23')
|
||||
create(:academic_year, range: "2022-23")
|
||||
end
|
||||
|
||||
let(:common_headers) do
|
||||
['Recorded Date', 'DeseID', 'ResponseID', 'Duration (in seconds)', 'Gender', 'Grade']
|
||||
["Recorded Date", "DeseID", "ResponseID", "Duration (in seconds)", "Gender", "Grade"]
|
||||
end
|
||||
|
||||
let(:standard_survey_items) do
|
||||
|
|
@ -46,9 +42,9 @@ RSpec.describe SurveyItemValues, type: :model do
|
|||
end
|
||||
|
||||
let(:short_form_survey_items) do
|
||||
survey_item_ids = [create(:survey_item, survey_item_id: 's-phys-q1', on_short_form: true),
|
||||
create(:survey_item, survey_item_id: 's-phys-q2', on_short_form: true),
|
||||
create(:survey_item, survey_item_id: 's-phys-q3',
|
||||
survey_item_ids = [create(:survey_item, survey_item_id: "s-phys-q1", on_short_form: true),
|
||||
create(:survey_item, survey_item_id: "s-phys-q2", on_short_form: true),
|
||||
create(:survey_item, survey_item_id: "s-phys-q3",
|
||||
on_short_form: true)].map(&:survey_item_id)
|
||||
survey_item_ids.map do |survey_item_id|
|
||||
create(:survey_item, survey_item_id:)
|
||||
|
|
@ -57,9 +53,9 @@ RSpec.describe SurveyItemValues, type: :model do
|
|||
end
|
||||
|
||||
let(:early_education_survey_items) do
|
||||
survey_item_ids = [create(:survey_item, survey_item_id: 's-emsa-es1'),
|
||||
create(:survey_item, survey_item_id: 's-emsa-es2'),
|
||||
create(:survey_item, survey_item_id: 's-emsa-es3')].map(&:survey_item_id)
|
||||
survey_item_ids = [create(:survey_item, survey_item_id: "s-emsa-es1"),
|
||||
create(:survey_item, survey_item_id: "s-emsa-es2"),
|
||||
create(:survey_item, survey_item_id: "s-emsa-es3")].map(&:survey_item_id)
|
||||
survey_item_ids.map do |survey_item_id|
|
||||
create(:survey_item, survey_item_id:)
|
||||
end
|
||||
|
|
@ -81,279 +77,321 @@ RSpec.describe SurveyItemValues, type: :model do
|
|||
(survey_item_ids << common_headers).flatten
|
||||
end
|
||||
|
||||
context '.recorded_date' do
|
||||
it 'returns the recorded date' do
|
||||
row = { 'RecordedDate' => '2017-01-01' }
|
||||
context ".recorded_date" do
|
||||
it "returns the recorded date" do
|
||||
row = { "RecordedDate" => "2017-01-01" }
|
||||
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:)
|
||||
expect(values.recorded_date).to eq Date.parse('2017-01-01')
|
||||
expect(values.recorded_date).to eq Date.parse("2017-01-01")
|
||||
|
||||
headers = ['Recorded Date']
|
||||
row = { 'Recorded Date' => '2017-01-02' }
|
||||
headers = ["Recorded Date"]
|
||||
row = { "Recorded Date" => "2017-01-02" }
|
||||
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:)
|
||||
expect(values.recorded_date).to eq Date.parse('2017-01-02')
|
||||
expect(values.recorded_date).to eq Date.parse("2017-01-02")
|
||||
end
|
||||
end
|
||||
|
||||
context '.school' do
|
||||
it 'returns the school that maps to the dese id provided' do
|
||||
context ".school" do
|
||||
it "returns the school that maps to the dese id provided" do
|
||||
attleboro
|
||||
headers = ['Dese ID']
|
||||
row = { 'Dese ID' => '1234' }
|
||||
row = { "Dese ID" => "1234" }
|
||||
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:)
|
||||
expect(values.school).to eq attleboro
|
||||
|
||||
headers = ['School']
|
||||
row = { 'School' => '1234' }
|
||||
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:)
|
||||
expect(values.school).to eq attleboro
|
||||
|
||||
headers = ['School- Attleboro']
|
||||
row = { 'School- Attleboro' => '1234' }
|
||||
row = { "DeseID" => "1234" }
|
||||
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:)
|
||||
expect(values.school).to eq attleboro
|
||||
end
|
||||
end
|
||||
|
||||
context '.grade' do
|
||||
it 'returns the grade that maps to the grade provided' do
|
||||
row = { 'Grade' => '1' }
|
||||
context ".grade" do
|
||||
it "returns the grade that maps to the grade provided" do
|
||||
row = { "Grade" => "1" }
|
||||
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:)
|
||||
expect(values.grade).to eq 1
|
||||
end
|
||||
end
|
||||
|
||||
context '.gender' do
|
||||
it 'returns the grade that maps to the grade provided' do
|
||||
row = { 'Gender' => '1' }
|
||||
context ".gender" do
|
||||
it "returns the grade that maps to the grade provided" do
|
||||
row = { "Gender" => "1" }
|
||||
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:)
|
||||
expect(values.gender.qualtrics_code).to eq 1
|
||||
end
|
||||
end
|
||||
|
||||
context '.dese_id' do
|
||||
it 'returns the dese id for the id provided' do
|
||||
headers = ['Dese ID']
|
||||
row = { 'Dese ID' => '11' }
|
||||
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:)
|
||||
expect(values.dese_id).to eq 11
|
||||
headers = ['School']
|
||||
row = { 'School' => '22' }
|
||||
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:)
|
||||
expect(values.dese_id).to eq 22
|
||||
context ".respondent_type" do
|
||||
it "reads header to find the survey type" do
|
||||
headers = %w[s-sbel-q5 s-phys-q2 RecordedDate]
|
||||
values = SurveyItemValues.new(row: {}, headers:, genders:, survey_items:, schools:)
|
||||
expect(values.respondent_type).to eq :student
|
||||
|
||||
headers = %w[t-sbel-q5 t-phys-q2]
|
||||
values = SurveyItemValues.new(row: {}, headers:, genders:, survey_items:, schools:)
|
||||
expect(values.respondent_type).to eq :teacher
|
||||
end
|
||||
end
|
||||
|
||||
# context '.survey_type' do
|
||||
# it 'reads header to find the survey type' do
|
||||
# headers = %w[s-sbel-q5 s-phys-q2 RecordedDate]
|
||||
# values = SurveyItemValues.new(row: {}, headers:, genders:, survey_items:, schools:)
|
||||
# expect(values.respondent_type).to eq :student
|
||||
|
||||
# headers = %w[t-sbel-q5 t-phys-q2]
|
||||
# values = SurveyItemValues.new(row: {}, headers:, genders:, survey_items:, schools:)
|
||||
# expect(values.respondent_type).to eq :teacher
|
||||
# end
|
||||
# end
|
||||
|
||||
context '.survey_type' do
|
||||
context 'when survey type is standard form' do
|
||||
it 'returns the survey type' do
|
||||
context ".survey_type" do
|
||||
context "when survey type is standard form" do
|
||||
it "returns the survey type" do
|
||||
headers = standard_survey_items
|
||||
values = SurveyItemValues.new(row: {}, headers:, genders:, survey_items:, schools:)
|
||||
expect(values.survey_type).to eq :standard
|
||||
end
|
||||
end
|
||||
context 'when survey type is teacher form' do
|
||||
it 'returns the survey type' do
|
||||
context "when survey type is teacher form" do
|
||||
it "returns the survey type" do
|
||||
headers = teacher_survey_items
|
||||
values = SurveyItemValues.new(row: {}, headers:, genders:, survey_items:, schools:)
|
||||
expect(values.survey_type).to eq :teacher
|
||||
end
|
||||
end
|
||||
|
||||
context 'when survey type is short form' do
|
||||
it 'returns the survey type' do
|
||||
context "when survey type is short form" do
|
||||
it "returns the survey type" do
|
||||
headers = short_form_survey_items
|
||||
values = SurveyItemValues.new(row: {}, headers:, genders:, survey_items:, schools:)
|
||||
expect(values.survey_type).to eq :short_form
|
||||
end
|
||||
end
|
||||
|
||||
context 'when survey type is early education' do
|
||||
it 'returns the survey type' do
|
||||
context "when survey type is early education" do
|
||||
it "returns the survey type" do
|
||||
headers = early_education_survey_items
|
||||
values = SurveyItemValues.new(row: {}, headers:, genders:, survey_items:, schools:)
|
||||
expect(values.survey_type).to eq :early_education
|
||||
end
|
||||
end
|
||||
end
|
||||
context '.valid_duration' do
|
||||
context 'when duration is valid' do
|
||||
it 'returns true' do
|
||||
|
||||
context ".income" do
|
||||
context "when no disaggregation data is provided" do
|
||||
it "returns an empty string " do
|
||||
disaggregation_data = {}
|
||||
values = SurveyItemValues.new(row: {}, headers:, genders:, survey_items:, schools:, disaggregation_data:)
|
||||
expect(values.income).to eq "Unknown"
|
||||
end
|
||||
end
|
||||
|
||||
context "when disaggregation data is provided" do
|
||||
before :each do
|
||||
attleboro
|
||||
ay_2022_23
|
||||
end
|
||||
|
||||
it "translates Free Lunch to Economically Disadvantaged - Y" do
|
||||
headers = ["District", "Academic Year", "LASID", "LowIncome"]
|
||||
row = { "District" => "Attleboro", "AcademicYear" => "2022-23", "LASID" => "1", "LowIncome" => "Free Lunch" }
|
||||
disaggregation_data = { %w[1 Attleboro 2022-23] => DisaggregationRow.new(row:, headers:) }
|
||||
|
||||
headers = ["LASID", "Dese Id", "RecordedDate"]
|
||||
row = { "LASID" => "1", "DESE ID" => "1234", "RecordedDate" => "2023-1-1" }
|
||||
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:,
|
||||
disaggregation_data:)
|
||||
expect(values.income).to eq "Economically Disadvantaged - Y"
|
||||
end
|
||||
|
||||
it "translates Reduced Lunch to Economically Disadvantaged - Y" do
|
||||
headers = ["District", "Academic Year", "LASID", "LowIncome"]
|
||||
row = { "District" => "Attleboro", "AcademicYear" => "2022-23", "LASID" => "1", "LowIncome" => "Reduced Lunch" }
|
||||
disaggregation_data = { %w[1 Attleboro 2022-23] => DisaggregationRow.new(row:, headers:) }
|
||||
|
||||
headers = ["LASID", "Dese Id", "RecordedDate"]
|
||||
row = { "LASID" => "1", "DESE ID" => "1234", "RecordedDate" => "2023-1-1" }
|
||||
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:,
|
||||
disaggregation_data:)
|
||||
expect(values.income).to eq "Economically Disadvantaged - Y"
|
||||
end
|
||||
|
||||
it "translates LowIncome to Economically Disadvantaged - Y" do
|
||||
headers = ["District", "Academic Year", "LASID", "LowIncome"]
|
||||
row = { "District" => "Attleboro", "AcademicYear" => "2022-23", "LASID" => "1", "LowIncome" => "LowIncome" }
|
||||
disaggregation_data = { %w[1 Attleboro 2022-23] => DisaggregationRow.new(row:, headers:) }
|
||||
|
||||
headers = ["LASID", "Dese Id", "RecordedDate"]
|
||||
row = { "LASID" => "1", "DESE ID" => "1234", "RecordedDate" => "2023-1-1" }
|
||||
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:,
|
||||
disaggregation_data:)
|
||||
expect(values.income).to eq "Economically Disadvantaged - Y"
|
||||
end
|
||||
|
||||
it "translates Not Eligible to Economically Disadvantaged - N" do
|
||||
headers = ["District", "Academic Year", "LASID", "LowIncome"]
|
||||
row = { "District" => "Attleboro", "AcademicYear" => "2022-23", "LASID" => "1", "LowIncome" => "Not Eligible" }
|
||||
disaggregation_data = { %w[1 Attleboro 2022-23] => DisaggregationRow.new(row:, headers:) }
|
||||
|
||||
headers = ["LASID", "Dese Id", "RecordedDate"]
|
||||
row = { "LASID" => "1", "DESE ID" => "1234", "RecordedDate" => "2023-1-1" }
|
||||
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:,
|
||||
disaggregation_data:)
|
||||
expect(values.income).to eq "Economically Disadvantaged - N"
|
||||
end
|
||||
|
||||
it "translates blanks to Unknown" do
|
||||
headers = ["District", "Academic Year", "LASID", "LowIncome"]
|
||||
row = { "District" => "Attleboro", "AcademicYear" => "2022-23", "LASID" => "1", "LowIncome" => "" }
|
||||
disaggregation_data = { %w[1 Attleboro 2022-23] => DisaggregationRow.new(row:, headers:) }
|
||||
|
||||
headers = ["LASID", "Dese Id", "RecordedDate"]
|
||||
row = { "LASID" => "1", "DESE ID" => "1234", "RecordedDate" => "2023-1-1" }
|
||||
values = SurveyItemValues.new(row:, headers:, genders:, survey_items:, schools:,
|
||||
disaggregation_data:)
|
||||
expect(values.income).to eq "Unknown"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context ".valid_duration" do
|
||||
context "when duration is valid" do
|
||||
it "returns true" do
|
||||
headers = standard_survey_items
|
||||
values = SurveyItemValues.new(row: { 'Duration (in seconds)' => '240', 'Gender' => 'Male' }, headers:, genders:, survey_items:,
|
||||
values = SurveyItemValues.new(row: { "Duration (in seconds)" => "240", "Gender" => "Male" }, headers:, genders:, survey_items:,
|
||||
schools:)
|
||||
expect(values.valid_duration?).to eq true
|
||||
|
||||
headers = teacher_survey_items
|
||||
values = SurveyItemValues.new(row: { 'Duration (in seconds)' => '300' }, headers:, genders:, survey_items:,
|
||||
values = SurveyItemValues.new(row: { "Duration (in seconds)" => "300" }, headers:, genders:, survey_items:,
|
||||
schools:)
|
||||
expect(values.valid_duration?).to eq true
|
||||
|
||||
headers = short_form_survey_items
|
||||
values = SurveyItemValues.new(row: { 'Duration (in seconds)' => '100' }, headers:, genders:, survey_items:,
|
||||
values = SurveyItemValues.new(row: { "Duration (in seconds)" => "100" }, headers:, genders:, survey_items:,
|
||||
schools:)
|
||||
expect(values.valid_duration?).to eq true
|
||||
|
||||
# When duration is blank or N/A or NA, we don't have enough information to kick out the row as invalid so we keep it in
|
||||
headers = short_form_survey_items
|
||||
values = SurveyItemValues.new(row: { 'Duration (in seconds)' => '' }, headers:, genders:, survey_items:,
|
||||
values = SurveyItemValues.new(row: { "Duration (in seconds)" => "" }, headers:, genders:, survey_items:,
|
||||
schools:)
|
||||
expect(values.valid_duration?).to eq true
|
||||
|
||||
headers = short_form_survey_items
|
||||
values = SurveyItemValues.new(row: { 'Duration (in seconds)' => 'N/A' }, headers:, genders:, survey_items:,
|
||||
values = SurveyItemValues.new(row: { "Duration (in seconds)" => "N/A" }, headers:, genders:, survey_items:,
|
||||
schools:)
|
||||
expect(values.valid_duration?).to eq true
|
||||
|
||||
headers = short_form_survey_items
|
||||
values = SurveyItemValues.new(row: { 'Duration (in seconds)' => 'NA' }, headers:, genders:, survey_items:,
|
||||
schools:)
|
||||
end
|
||||
end
|
||||
end
|
||||
context '.valid_duration' do
|
||||
context 'when duration is valid' do
|
||||
it 'returns true' do
|
||||
headers = ['s-sbel-q5', 's-phys-q2', 'RecordedDate', 'Duration (in seconds)']
|
||||
values = SurveyItemValues.new(row: { 'Duration (in seconds)' => '240' }, headers:, genders:, survey_items:,
|
||||
schools:)
|
||||
expect(values.valid_duration?).to eq true
|
||||
|
||||
headers = ['t-sbel-q5', 't-phys-q2', 'Duration (in seconds)']
|
||||
values = SurveyItemValues.new(row: { 'Duration (in seconds)' => '300' }, headers:, genders:, survey_items:,
|
||||
values = SurveyItemValues.new(row: { "Duration (in seconds)" => "NA" }, headers:, genders:, survey_items:,
|
||||
schools:)
|
||||
expect(values.valid_duration?).to eq true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when duration is invalid' do
|
||||
it 'returns false' do
|
||||
context "when duration is invalid" do
|
||||
it "returns false" do
|
||||
headers = standard_survey_items
|
||||
# headers = ['s-sbel-q5', 's-phys-q2', 'RecordedDate', 'Duration (in seconds)']
|
||||
values = SurveyItemValues.new(row: { 'Duration (in seconds)' => '239' }, headers:, genders:, survey_items:,
|
||||
values = SurveyItemValues.new(row: { "Duration (in seconds)" => "239" }, headers:, genders:, survey_items:,
|
||||
schools:)
|
||||
expect(values.valid_duration?).to eq false
|
||||
|
||||
headers = teacher_survey_items
|
||||
values = SurveyItemValues.new(row: { 'Duration (in seconds)' => '299' }, headers:, genders:, survey_items:,
|
||||
values = SurveyItemValues.new(row: { "Duration (in seconds)" => "299" }, headers:, genders:, survey_items:,
|
||||
schools:)
|
||||
expect(values.valid_duration?).to eq false
|
||||
headers = short_form_survey_items
|
||||
values = SurveyItemValues.new(row: { 'Duration (in seconds)' => '99' }, headers:, genders:, survey_items:,
|
||||
values = SurveyItemValues.new(row: { "Duration (in seconds)" => "99" }, headers:, genders:, survey_items:,
|
||||
schools:)
|
||||
expect(values.valid_duration?).to eq false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context '.valid_progress' do
|
||||
context 'when progress is valid' do
|
||||
it 'returns true' do
|
||||
context ".valid_progress" do
|
||||
context "when progress is valid" do
|
||||
it "returns true" do
|
||||
headers = %w[s-sbel-q5 s-phys-q2 RecordedDate]
|
||||
values = SurveyItemValues.new(row: { 'Progress' => '25' }, headers:, genders:, survey_items:,
|
||||
values = SurveyItemValues.new(row: { "Progress" => "25" }, headers:, genders:, survey_items:,
|
||||
schools:)
|
||||
expect(values.valid_progress?).to eq true
|
||||
|
||||
# When progress is blank or N/A or NA, we don't have enough information to kick out the row as invalid so we keep it in
|
||||
headers = %w[s-sbel-q5 s-phys-q2 RecordedDate]
|
||||
values = SurveyItemValues.new(row: { 'Progress' => '' }, headers:, genders:, survey_items:,
|
||||
values = SurveyItemValues.new(row: { "Progress" => "" }, headers:, genders:, survey_items:,
|
||||
schools:)
|
||||
expect(values.valid_progress?).to eq true
|
||||
|
||||
headers = %w[s-sbel-q5 s-phys-q2 RecordedDate]
|
||||
values = SurveyItemValues.new(row: { 'Progress' => 'N/A' }, headers:, genders:, survey_items:,
|
||||
values = SurveyItemValues.new(row: { "Progress" => "N/A" }, headers:, genders:, survey_items:,
|
||||
schools:)
|
||||
expect(values.valid_progress?).to eq true
|
||||
|
||||
headers = %w[s-sbel-q5 s-phys-q2 RecordedDate]
|
||||
values = SurveyItemValues.new(row: { 'Progress' => 'NA' }, headers:, genders:, survey_items:,
|
||||
values = SurveyItemValues.new(row: { "Progress" => "NA" }, headers:, genders:, survey_items:,
|
||||
schools:)
|
||||
expect(values.valid_progress?).to eq true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when progress is invalid' do
|
||||
it 'returns false' do
|
||||
context "when progress is invalid" do
|
||||
it "returns false" do
|
||||
headers = %w[s-sbel-q5 s-phys-q2 RecordedDate]
|
||||
values = SurveyItemValues.new(row: { 'Progress' => '24' }, headers:, genders:, survey_items:,
|
||||
values = SurveyItemValues.new(row: { "Progress" => "24" }, headers:, genders:, survey_items:,
|
||||
schools:)
|
||||
expect(values.valid_progress?).to eq false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context '.valid_grade?' do
|
||||
context 'when grade is valid' do
|
||||
context ".valid_grade?" do
|
||||
context "when grade is valid" do
|
||||
before :each do
|
||||
attleboro
|
||||
attleboro_respondents
|
||||
end
|
||||
it 'returns true for students' do
|
||||
it "returns true for students" do
|
||||
headers = %w[s-sbel-q5 s-phys-q2 grade RecordedDate]
|
||||
values = SurveyItemValues.new(row: { 'grade' => '9', 'RecordedDate' => recorded_date, 'Dese ID' => '1234' }, headers:, genders:, survey_items:,
|
||||
values = SurveyItemValues.new(row: { "grade" => "9", "RecordedDate" => recorded_date, "Dese ID" => "1234" }, headers:, genders:, survey_items:,
|
||||
schools:)
|
||||
|
||||
expect(values.valid_grade?).to eq true
|
||||
end
|
||||
it 'returns true for teachers' do
|
||||
it "returns true for teachers" do
|
||||
headers = %w[t-sbel-q5 t-phys-q2 grade RecordedDate]
|
||||
values = SurveyItemValues.new(row: { 'RecordedDate' => recorded_date, 'Dese ID' => '1234' }, headers:, genders:, survey_items:,
|
||||
values = SurveyItemValues.new(row: { "RecordedDate" => recorded_date, "Dese ID" => "1234" }, headers:, genders:, survey_items:,
|
||||
schools:)
|
||||
expect(values.valid_grade?).to eq true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when grade is invalid' do
|
||||
context "when grade is invalid" do
|
||||
before :each do
|
||||
attleboro
|
||||
attleboro_respondents
|
||||
end
|
||||
it 'returns false' do
|
||||
it "returns false" do
|
||||
headers = %w[s-sbel-q5 s-phys-q2 grade RecordedDate]
|
||||
values = SurveyItemValues.new(row: { 'grade' => '2', 'RecordedDate' => recorded_date, 'Dese ID' => '1234' }, headers:, genders:, survey_items:,
|
||||
values = SurveyItemValues.new(row: { "grade" => "2", "RecordedDate" => recorded_date, "Dese ID" => "1234" }, headers:, genders:, survey_items:,
|
||||
schools: School.school_hash)
|
||||
expect(values.valid_grade?).to eq false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context '.valid_sd?' do
|
||||
context 'when the standard deviation is valid' do
|
||||
it 'returns true for student questions' do
|
||||
context ".valid_sd?" do
|
||||
context "when the standard deviation is valid" do
|
||||
it "returns true for student questions" do
|
||||
headers = %w[s-sbel-q5 s-phys-q1 s-phys-q2 RecordedDate]
|
||||
values = SurveyItemValues.new(row: { 'RecordedDate' => recorded_date, 'Dese ID' => '1234', 's-sbel-q5' => '1', 's-phys-q1' => '', 's-phys-q2' => '5' }, headers:, genders:, survey_items:,
|
||||
values = SurveyItemValues.new(row: { "RecordedDate" => recorded_date, "Dese ID" => "1234", "s-sbel-q5" => "1", "s-phys-q1" => "", "s-phys-q2" => "5" }, headers:, genders:, survey_items:,
|
||||
schools: School.school_hash)
|
||||
expect(values.valid_sd?).to eq true
|
||||
end
|
||||
it 'returns true for teacher questions' do
|
||||
it "returns true for teacher questions" do
|
||||
headers = %w[t-sbel-q5 t-phys-q2]
|
||||
values = SurveyItemValues.new(row: { 'RecordedDate' => recorded_date, 'Dese ID' => '1234', 't-sbel-q5' => '1', 't-phys-q2' => '5' }, headers:, genders:, survey_items:,
|
||||
values = SurveyItemValues.new(row: { "RecordedDate" => recorded_date, "Dese ID" => "1234", "t-sbel-q5" => "1", "t-phys-q2" => "5" }, headers:, genders:, survey_items:,
|
||||
schools: School.school_hash)
|
||||
expect(values.valid_sd?).to eq true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the standard deviation is invalid' do
|
||||
it 'returns false for student questions' do
|
||||
context "when the standard deviation is invalid" do
|
||||
it "returns false for student questions" do
|
||||
headers = %w[s-sbel-q5 s-phys-q1 s-phys-q2 RecordedDate]
|
||||
values = SurveyItemValues.new(row: { 'RecordedDate' => recorded_date, 'Dese ID' => '1234', 's-sbel-q5' => '1', 's-phys-q2' => '', 's-phys-q2' => '1' }, headers:, genders:, survey_items:,
|
||||
values = SurveyItemValues.new(row: { "RecordedDate" => recorded_date, "Dese ID" => "1234", "s-sbel-q5" => "1", "s-phys-q2" => "1" }, headers:, genders:, survey_items:,
|
||||
schools: School.school_hash)
|
||||
expect(values.valid_sd?).to eq false
|
||||
end
|
||||
it 'returns false for teacher questions' do
|
||||
it "returns false for teacher questions" do
|
||||
headers = %w[t-sbel-q5 t-phys-q1 t-phys-q2 RecordedDate]
|
||||
values = SurveyItemValues.new(row: { 'RecordedDate' => recorded_date, 'Dese ID' => '1234', 't-sbel-q5' => '1', 't-phys-q2' => '', 't-phys-q2' => '1' }, headers:, genders:, survey_items:,
|
||||
values = SurveyItemValues.new(row: { "RecordedDate" => recorded_date, "Dese ID" => "1234", "t-sbel-q5" => "1", "t-phys-q2" => "1" }, headers:, genders:, survey_items:,
|
||||
schools: School.school_hash)
|
||||
expect(values.valid_sd?).to eq false
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,53 +1,59 @@
|
|||
require "rails_helper"
|
||||
require 'rails_helper'
|
||||
|
||||
describe SurveyResponsesDataLoader do
|
||||
let(:path_to_teacher_responses) { Rails.root.join("spec", "fixtures", "test_2020-21_teacher_survey_responses.csv") }
|
||||
let(:path_to_student_responses) { Rails.root.join("spec", "fixtures", "test_2020-21_student_survey_responses.csv") }
|
||||
let(:path_to_teacher_responses) { Rails.root.join('spec', 'fixtures', 'test_2020-21_teacher_survey_responses.csv') }
|
||||
let(:path_to_student_responses) { Rails.root.join('spec', 'fixtures', 'test_2020-21_student_survey_responses.csv') }
|
||||
let(:path_to_butler_student_responses) do
|
||||
Rails.root.join("spec", "fixtures", "test_2022-23_butler_student_survey_responses.csv")
|
||||
Rails.root.join('spec', 'fixtures', 'test_2022-23_butler_student_survey_responses.csv')
|
||||
end
|
||||
|
||||
let(:ay_2020_21) { create(:academic_year, range: "2020-21") }
|
||||
let(:ay_2022_23) { create(:academic_year, range: "2022-23") }
|
||||
let(:ay_2020_21) { create(:academic_year, range: '2020-21') }
|
||||
let(:ay_2022_23) { create(:academic_year, range: '2022-23') }
|
||||
|
||||
let(:school) { create(:school, name: "Lee Elementary School", slug: "lee-elementary-school", dese_id: 1_500_025) }
|
||||
let(:lowell) { create(:district, name: "Lowell", slug: "lowell") }
|
||||
let(:second_school) { create(:school, name: "Lee Middle High School", slug: "lee-middle-high-school", dese_id: 1_500_505, district: lowell) }
|
||||
let(:school) { create(:school, name: 'Lee Elementary School', slug: 'lee-elementary-school', dese_id: 1_500_025) }
|
||||
let(:lowell) { create(:district, name: 'Lowell', slug: 'lowell') }
|
||||
let(:second_school) do
|
||||
create(:school, name: 'Lee Middle High School', slug: 'lee-middle-high-school', dese_id: 1_500_505,
|
||||
district: lowell)
|
||||
end
|
||||
let(:butler_school) do
|
||||
create(:school, name: "Butler Elementary School", slug: "butler-elementary-school", dese_id: 1_600_310,
|
||||
district: lowell)
|
||||
create(:school, name: 'Butler Elementary School', slug: 'butler-elementary-school', dese_id: 1_600_310,
|
||||
district: lowell)
|
||||
end
|
||||
|
||||
let(:t_pcom_q3) { create(:survey_item, survey_item_id: "t-pcom-q3") }
|
||||
let(:t_pcom_q2) { create(:survey_item, survey_item_id: "t-pcom-q2") }
|
||||
let(:t_coll_q1) { create(:survey_item, survey_item_id: "t-coll-q1") }
|
||||
let(:t_coll_q2) { create(:survey_item, survey_item_id: "t-coll-q2") }
|
||||
let(:t_coll_q3) { create(:survey_item, survey_item_id: "t-coll-q3") }
|
||||
let(:t_sach_q1) { create(:survey_item, survey_item_id: "t-sach-q1") }
|
||||
let(:t_sach_q2) { create(:survey_item, survey_item_id: "t-sach-q2") }
|
||||
let(:t_sach_q3) { create(:survey_item, survey_item_id: "t-sach-q3") }
|
||||
let(:t_pcom_q3) { create(:survey_item, survey_item_id: 't-pcom-q3') }
|
||||
let(:t_pcom_q2) { create(:survey_item, survey_item_id: 't-pcom-q2') }
|
||||
let(:t_coll_q1) { create(:survey_item, survey_item_id: 't-coll-q1') }
|
||||
let(:t_coll_q2) { create(:survey_item, survey_item_id: 't-coll-q2') }
|
||||
let(:t_coll_q3) { create(:survey_item, survey_item_id: 't-coll-q3') }
|
||||
let(:t_sach_q1) { create(:survey_item, survey_item_id: 't-sach-q1') }
|
||||
let(:t_sach_q2) { create(:survey_item, survey_item_id: 't-sach-q2') }
|
||||
let(:t_sach_q3) { create(:survey_item, survey_item_id: 't-sach-q3') }
|
||||
|
||||
let(:s_phys_q1) { create(:survey_item, survey_item_id: "s-phys-q1") }
|
||||
let(:s_phys_q2) { create(:survey_item, survey_item_id: "s-phys-q2") }
|
||||
let(:s_phys_q3) { create(:survey_item, survey_item_id: "s-phys-q3") }
|
||||
let(:s_phys_q4) { create(:survey_item, survey_item_id: "s-phys-q4") }
|
||||
let(:s_vale_q1) { create(:survey_item, survey_item_id: "s-phys-q1") }
|
||||
let(:s_vale_q2) { create(:survey_item, survey_item_id: "s-phys-q2") }
|
||||
let(:s_vale_q3) { create(:survey_item, survey_item_id: "s-phys-q3") }
|
||||
let(:s_vale_q4) { create(:survey_item, survey_item_id: "s-phys-q4") }
|
||||
let(:s_acst_q1) { create(:survey_item, survey_item_id: "s-acst-q1") }
|
||||
let(:s_acst_q2) { create(:survey_item, survey_item_id: "s-acst-q2") }
|
||||
let(:s_acst_q3) { create(:survey_item, survey_item_id: "s-acst-q3") }
|
||||
let(:s_acst_q4) { create(:survey_item, survey_item_id: "s-acst-q4") }
|
||||
let(:s_emsa_q1) { create(:survey_item, survey_item_id: "s-emsa-q1") }
|
||||
let(:s_emsa_q2) { create(:survey_item, survey_item_id: "s-emsa-q2") }
|
||||
let(:s_emsa_q3) { create(:survey_item, survey_item_id: "s-emsa-q3") }
|
||||
let(:s_phys_q1) { create(:survey_item, survey_item_id: 's-phys-q1') }
|
||||
let(:s_phys_q2) { create(:survey_item, survey_item_id: 's-phys-q2') }
|
||||
let(:s_phys_q3) { create(:survey_item, survey_item_id: 's-phys-q3') }
|
||||
let(:s_phys_q4) { create(:survey_item, survey_item_id: 's-phys-q4') }
|
||||
let(:s_vale_q1) { create(:survey_item, survey_item_id: 's-phys-q1') }
|
||||
let(:s_vale_q2) { create(:survey_item, survey_item_id: 's-phys-q2') }
|
||||
let(:s_vale_q3) { create(:survey_item, survey_item_id: 's-phys-q3') }
|
||||
let(:s_vale_q4) { create(:survey_item, survey_item_id: 's-phys-q4') }
|
||||
let(:s_acst_q1) { create(:survey_item, survey_item_id: 's-acst-q1') }
|
||||
let(:s_acst_q2) { create(:survey_item, survey_item_id: 's-acst-q2') }
|
||||
let(:s_acst_q3) { create(:survey_item, survey_item_id: 's-acst-q3') }
|
||||
let(:s_acst_q4) { create(:survey_item, survey_item_id: 's-acst-q4') }
|
||||
let(:s_emsa_q1) { create(:survey_item, survey_item_id: 's-emsa-q1') }
|
||||
let(:s_emsa_q2) { create(:survey_item, survey_item_id: 's-emsa-q2') }
|
||||
let(:s_emsa_q3) { create(:survey_item, survey_item_id: 's-emsa-q3') }
|
||||
|
||||
let(:female) { create(:gender, qualtrics_code: 1) }
|
||||
let(:male) { create(:gender, qualtrics_code: 2) }
|
||||
let(:another_gender) { create(:gender, qualtrics_code: 3) }
|
||||
let(:non_binary) { create(:gender, qualtrics_code: 4) }
|
||||
let(:unknown_gender) { create(:gender, qualtrics_code: 99) }
|
||||
let(:low_income) { create(:income, designation: 'Economically Disadvantaged – Y') }
|
||||
let(:high_income) { create(:income, designation: 'Economically Disadvantaged – N') }
|
||||
let(:unknown_income) { create(:income, designation: 'Unknown') }
|
||||
|
||||
let(:setup) do
|
||||
ay_2020_21
|
||||
|
|
@ -83,18 +89,21 @@ describe SurveyResponsesDataLoader do
|
|||
another_gender
|
||||
non_binary
|
||||
unknown_gender
|
||||
low_income
|
||||
high_income
|
||||
unknown_income
|
||||
end
|
||||
|
||||
before :each do
|
||||
setup
|
||||
end
|
||||
|
||||
describe "loading teacher survey responses" do
|
||||
describe 'loading teacher survey responses' do
|
||||
before do
|
||||
SurveyResponsesDataLoader.load_data filepath: path_to_teacher_responses
|
||||
end
|
||||
|
||||
it "ensures teacher responses load correctly" do
|
||||
it 'ensures teacher responses load correctly' do
|
||||
assigns_academic_year_to_survey_item_responses
|
||||
assigns_school_to_the_survey_item_responses
|
||||
assigns_recorded_date_to_teacher_responses
|
||||
|
|
@ -105,12 +114,12 @@ describe SurveyResponsesDataLoader do
|
|||
end
|
||||
end
|
||||
|
||||
describe "student survey responses" do
|
||||
describe 'student survey responses' do
|
||||
before do
|
||||
SurveyResponsesDataLoader.load_data filepath: path_to_student_responses
|
||||
end
|
||||
|
||||
it "ensures student responses load correctly" do
|
||||
it 'ensures student responses load correctly' do
|
||||
assigns_academic_year_to_student_survey_item_responses
|
||||
assigns_school_to_student_survey_item_responses
|
||||
assigns_recorded_date_to_student_responses
|
||||
|
|
@ -119,85 +128,86 @@ describe SurveyResponsesDataLoader do
|
|||
captures_likert_scores_for_student_survey_item_responses
|
||||
assigns_grade_level_to_responses
|
||||
assigns_gender_to_responses
|
||||
assigns_income_to_responses
|
||||
is_idempotent_for_students
|
||||
end
|
||||
|
||||
context "when updating student survey responses from another csv file" do
|
||||
context 'when updating student survey responses from another csv file' do
|
||||
before :each do
|
||||
SurveyResponsesDataLoader.load_data filepath: Rails.root.join("spec", "fixtures",
|
||||
"secondary_test_2020-21_student_survey_responses.csv")
|
||||
SurveyResponsesDataLoader.load_data filepath: Rails.root.join('spec', 'fixtures',
|
||||
'secondary_test_2020-21_student_survey_responses.csv')
|
||||
end
|
||||
it "updates the likert score to the score on the new csv file" do
|
||||
s_emsa_q1 = SurveyItem.find_by_survey_item_id "s-emsa-q1"
|
||||
expect(SurveyItemResponse.where(response_id: "student_survey_response_3",
|
||||
survey_item: s_emsa_q1).first.likert_score).to eq 1
|
||||
expect(SurveyItemResponse.where(response_id: "student_survey_response_4",
|
||||
survey_item: s_emsa_q1).first.likert_score).to eq 1
|
||||
expect(SurveyItemResponse.where(response_id: "student_survey_response_5",
|
||||
survey_item: s_emsa_q1).first.likert_score).to eq 1
|
||||
it 'updates the likert score to the score on the new csv file' do
|
||||
s_emsa_q1 = SurveyItem.find_by_survey_item_id 's-emsa-q1'
|
||||
expect(SurveyItemResponse.where(response_id: 'student_survey_response_3',
|
||||
survey_item: s_emsa_q1).first.likert_score).to eq 1
|
||||
expect(SurveyItemResponse.where(response_id: 'student_survey_response_4',
|
||||
survey_item: s_emsa_q1).first.likert_score).to eq 1
|
||||
expect(SurveyItemResponse.where(response_id: 'student_survey_response_5',
|
||||
survey_item: s_emsa_q1).first.likert_score).to eq 1
|
||||
|
||||
expect(SurveyItemResponse.where(response_id: "student_survey_response_5",
|
||||
survey_item: s_acst_q3).first.likert_score).to eq 4
|
||||
expect(SurveyItemResponse.where(response_id: 'student_survey_response_5',
|
||||
survey_item: s_acst_q3).first.likert_score).to eq 4
|
||||
end
|
||||
end
|
||||
end
|
||||
# figure out why this is failing
|
||||
describe "when using Lowell rules to skip rows in the csv file" do
|
||||
describe 'when using Lowell rules to skip rows in the csv file' do
|
||||
before :each do
|
||||
SurveyResponsesDataLoader.load_data filepath: path_to_student_responses,
|
||||
rules: [Rule::SkipNonLowellSchools]
|
||||
rules: [Rule::SkipNonLowellSchools]
|
||||
end
|
||||
|
||||
it "rejects any non-lowell school" do
|
||||
expect(SurveyItemResponse.where(response_id: "student_survey_response_1").count).to eq 0
|
||||
it 'rejects any non-lowell school' do
|
||||
expect(SurveyItemResponse.where(response_id: 'student_survey_response_1').count).to eq 0
|
||||
expect(SurveyItemResponse.count).to eq 69
|
||||
end
|
||||
|
||||
it "loads the correct number of responses for lowell schools" do
|
||||
expect(SurveyItemResponse.where(response_id: "student_survey_response_2").count).to eq 0
|
||||
expect(SurveyItemResponse.where(response_id: "student_survey_response_3").count).to eq 12
|
||||
expect(SurveyItemResponse.where(response_id: "student_survey_response_4").count).to eq 15
|
||||
expect(SurveyItemResponse.where(response_id: "student_survey_response_5").count).to eq 14
|
||||
it 'loads the correct number of responses for lowell schools' do
|
||||
expect(SurveyItemResponse.where(response_id: 'student_survey_response_2').count).to eq 0
|
||||
expect(SurveyItemResponse.where(response_id: 'student_survey_response_3').count).to eq 12
|
||||
expect(SurveyItemResponse.where(response_id: 'student_survey_response_4').count).to eq 15
|
||||
expect(SurveyItemResponse.where(response_id: 'student_survey_response_5').count).to eq 14
|
||||
end
|
||||
|
||||
context "when loading 22-23 butler survey responses" do
|
||||
context 'when loading 22-23 butler survey responses' do
|
||||
before :each do
|
||||
SurveyResponsesDataLoader.load_data filepath: path_to_butler_student_responses,
|
||||
rules: [Rule::SkipNonLowellSchools]
|
||||
rules: [Rule::SkipNonLowellSchools]
|
||||
end
|
||||
|
||||
it "loads all the responses for Butler" do
|
||||
it 'loads all the responses for Butler' do
|
||||
expect(SurveyItemResponse.where(school: butler_school).count).to eq 56
|
||||
end
|
||||
|
||||
it "blank entries for grade get loaded as nils, not zero values" do
|
||||
expect(SurveyItemResponse.where(response_id: "butler_student_survey_response_1").first.grade).to eq 7
|
||||
expect(SurveyItemResponse.where(response_id: "butler_student_survey_response_2").first.grade).to eq 7
|
||||
expect(SurveyItemResponse.where(response_id: "butler_student_survey_response_3").first.grade).to eq 7
|
||||
expect(SurveyItemResponse.where(response_id: "butler_student_survey_response_4").first.grade).to eq 5
|
||||
expect(SurveyItemResponse.where(response_id: "butler_student_survey_response_5").first.grade).to eq 7
|
||||
expect(SurveyItemResponse.where(response_id: "butler_student_survey_response_6").first.grade).to eq 6
|
||||
expect(SurveyItemResponse.where(response_id: "butler_student_survey_response_7").first.grade).to eq nil
|
||||
expect(SurveyItemResponse.where(response_id: "butler_student_survey_response_8").first.grade).to eq 0
|
||||
it 'blank entries for grade get loaded as nils, not zero values' do
|
||||
expect(SurveyItemResponse.where(response_id: 'butler_student_survey_response_1').first.grade).to eq 7
|
||||
expect(SurveyItemResponse.where(response_id: 'butler_student_survey_response_2').first.grade).to eq 7
|
||||
expect(SurveyItemResponse.where(response_id: 'butler_student_survey_response_3').first.grade).to eq 7
|
||||
expect(SurveyItemResponse.where(response_id: 'butler_student_survey_response_4').first.grade).to eq 5
|
||||
expect(SurveyItemResponse.where(response_id: 'butler_student_survey_response_5').first.grade).to eq 7
|
||||
expect(SurveyItemResponse.where(response_id: 'butler_student_survey_response_6').first.grade).to eq 6
|
||||
expect(SurveyItemResponse.where(response_id: 'butler_student_survey_response_7').first.grade).to eq nil
|
||||
expect(SurveyItemResponse.where(response_id: 'butler_student_survey_response_8').first.grade).to eq 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def assigns_academic_year_to_survey_item_responses
|
||||
expect(SurveyItemResponse.find_by_response_id("teacher_survey_response_1").academic_year).to eq ay_2020_21
|
||||
expect(SurveyItemResponse.find_by_response_id('teacher_survey_response_1').academic_year).to eq ay_2020_21
|
||||
end
|
||||
|
||||
def assigns_school_to_the_survey_item_responses
|
||||
expect(SurveyItemResponse.find_by_response_id("teacher_survey_response_1").school).to eq school
|
||||
expect(SurveyItemResponse.find_by_response_id('teacher_survey_response_1').school).to eq school
|
||||
end
|
||||
|
||||
def loads_survey_item_responses_for_a_given_survey_response
|
||||
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_1").count).to eq 5
|
||||
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_2").count).to eq 0
|
||||
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_3").count).to eq 8
|
||||
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_4").count).to eq 8
|
||||
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_5").count).to eq 8
|
||||
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_1').count).to eq 5
|
||||
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_2').count).to eq 0
|
||||
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_3').count).to eq 8
|
||||
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_4').count).to eq 8
|
||||
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_5').count).to eq 8
|
||||
end
|
||||
|
||||
def loads_all_survey_item_responses_for_a_given_survey_item
|
||||
|
|
@ -206,20 +216,20 @@ def loads_all_survey_item_responses_for_a_given_survey_item
|
|||
end
|
||||
|
||||
def captures_likert_scores_for_survey_item_responses
|
||||
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_1").where(survey_item: t_pcom_q2)).to be_empty
|
||||
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_1").where(survey_item: t_pcom_q3).first.likert_score).to eq 3
|
||||
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_1').where(survey_item: t_pcom_q2)).to be_empty
|
||||
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_1').where(survey_item: t_pcom_q3).first.likert_score).to eq 3
|
||||
|
||||
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_2").where(survey_item: t_pcom_q2)).to be_empty
|
||||
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_2").where(survey_item: t_pcom_q3)).to be_empty
|
||||
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_2').where(survey_item: t_pcom_q2)).to be_empty
|
||||
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_2').where(survey_item: t_pcom_q3)).to be_empty
|
||||
|
||||
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_3").where(survey_item: t_pcom_q2).first.likert_score).to eq 5
|
||||
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_3").where(survey_item: t_pcom_q3).first.likert_score).to eq 5
|
||||
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_3').where(survey_item: t_pcom_q2).first.likert_score).to eq 5
|
||||
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_3').where(survey_item: t_pcom_q3).first.likert_score).to eq 5
|
||||
|
||||
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_4").where(survey_item: t_pcom_q2).first.likert_score).to eq 4
|
||||
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_4").where(survey_item: t_pcom_q3).first.likert_score).to eq 4
|
||||
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_4').where(survey_item: t_pcom_q2).first.likert_score).to eq 4
|
||||
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_4').where(survey_item: t_pcom_q3).first.likert_score).to eq 4
|
||||
|
||||
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_5").where(survey_item: t_pcom_q2).first.likert_score).to eq 2
|
||||
expect(SurveyItemResponse.where(response_id: "teacher_survey_response_5").where(survey_item: t_pcom_q3).first.likert_score).to eq 4
|
||||
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_5').where(survey_item: t_pcom_q2).first.likert_score).to eq 2
|
||||
expect(SurveyItemResponse.where(response_id: 'teacher_survey_response_5').where(survey_item: t_pcom_q3).first.likert_score).to eq 4
|
||||
end
|
||||
|
||||
def is_idempotent
|
||||
|
|
@ -231,19 +241,19 @@ def is_idempotent
|
|||
end
|
||||
|
||||
def assigns_academic_year_to_student_survey_item_responses
|
||||
expect(SurveyItemResponse.find_by_response_id("student_survey_response_3").academic_year).to eq ay_2020_21
|
||||
expect(SurveyItemResponse.find_by_response_id('student_survey_response_3').academic_year).to eq ay_2020_21
|
||||
end
|
||||
|
||||
def assigns_school_to_student_survey_item_responses
|
||||
expect(SurveyItemResponse.find_by_response_id("student_survey_response_3").school).to eq second_school
|
||||
expect(SurveyItemResponse.find_by_response_id('student_survey_response_3').school).to eq second_school
|
||||
end
|
||||
|
||||
def loads_student_survey_item_response_values
|
||||
expect(SurveyItemResponse.where(response_id: "student_survey_response_1").count).to eq 3
|
||||
expect(SurveyItemResponse.where(response_id: "student_survey_response_2").count).to eq 0
|
||||
expect(SurveyItemResponse.where(response_id: "student_survey_response_3").count).to eq 12
|
||||
expect(SurveyItemResponse.where(response_id: "student_survey_response_4").count).to eq 15
|
||||
expect(SurveyItemResponse.where(response_id: "student_survey_response_5").count).to eq 14
|
||||
expect(SurveyItemResponse.where(response_id: 'student_survey_response_1').count).to eq 3
|
||||
expect(SurveyItemResponse.where(response_id: 'student_survey_response_2').count).to eq 0
|
||||
expect(SurveyItemResponse.where(response_id: 'student_survey_response_3').count).to eq 12
|
||||
expect(SurveyItemResponse.where(response_id: 'student_survey_response_4').count).to eq 15
|
||||
expect(SurveyItemResponse.where(response_id: 'student_survey_response_5').count).to eq 14
|
||||
end
|
||||
|
||||
def student_survey_item_response_count_matches_expected
|
||||
|
|
@ -252,20 +262,20 @@ def student_survey_item_response_count_matches_expected
|
|||
end
|
||||
|
||||
def captures_likert_scores_for_student_survey_item_responses
|
||||
expect(SurveyItemResponse.where(response_id: "student_survey_response_1").where(survey_item: s_phys_q1).first.likert_score).to eq 3
|
||||
expect(SurveyItemResponse.where(response_id: "student_survey_response_1").where(survey_item: s_phys_q2)).to be_empty
|
||||
expect(SurveyItemResponse.where(response_id: 'student_survey_response_1').where(survey_item: s_phys_q1).first.likert_score).to eq 3
|
||||
expect(SurveyItemResponse.where(response_id: 'student_survey_response_1').where(survey_item: s_phys_q2)).to be_empty
|
||||
|
||||
expect(SurveyItemResponse.where(response_id: "student_survey_response_2").where(survey_item: s_phys_q1)).to be_empty
|
||||
expect(SurveyItemResponse.where(response_id: "student_survey_response_2").where(survey_item: s_phys_q2)).to be_empty
|
||||
expect(SurveyItemResponse.where(response_id: 'student_survey_response_2').where(survey_item: s_phys_q1)).to be_empty
|
||||
expect(SurveyItemResponse.where(response_id: 'student_survey_response_2').where(survey_item: s_phys_q2)).to be_empty
|
||||
|
||||
expect(SurveyItemResponse.where(response_id: "student_survey_response_3").where(survey_item: s_phys_q1).first.likert_score).to eq 1
|
||||
expect(SurveyItemResponse.where(response_id: "student_survey_response_3").where(survey_item: s_phys_q2).first.likert_score).to eq 3
|
||||
expect(SurveyItemResponse.where(response_id: 'student_survey_response_3').where(survey_item: s_phys_q1).first.likert_score).to eq 1
|
||||
expect(SurveyItemResponse.where(response_id: 'student_survey_response_3').where(survey_item: s_phys_q2).first.likert_score).to eq 3
|
||||
|
||||
expect(SurveyItemResponse.where(response_id: "student_survey_response_4").where(survey_item: s_phys_q1).first.likert_score).to eq 1
|
||||
expect(SurveyItemResponse.where(response_id: "student_survey_response_4").where(survey_item: s_phys_q2).first.likert_score).to eq 1
|
||||
expect(SurveyItemResponse.where(response_id: 'student_survey_response_4').where(survey_item: s_phys_q1).first.likert_score).to eq 1
|
||||
expect(SurveyItemResponse.where(response_id: 'student_survey_response_4').where(survey_item: s_phys_q2).first.likert_score).to eq 1
|
||||
|
||||
expect(SurveyItemResponse.where(response_id: "student_survey_response_5").where(survey_item: s_phys_q1).first.likert_score).to eq 1
|
||||
expect(SurveyItemResponse.where(response_id: "student_survey_response_5").where(survey_item: s_phys_q2).first.likert_score).to eq 2
|
||||
expect(SurveyItemResponse.where(response_id: 'student_survey_response_5').where(survey_item: s_phys_q1).first.likert_score).to eq 1
|
||||
expect(SurveyItemResponse.where(response_id: 'student_survey_response_5').where(survey_item: s_phys_q2).first.likert_score).to eq 2
|
||||
end
|
||||
|
||||
def is_idempotent_for_students
|
||||
|
|
@ -277,26 +287,26 @@ def is_idempotent_for_students
|
|||
end
|
||||
|
||||
def assigns_grade_level_to_responses
|
||||
results = {"student_survey_response_1" => 11,
|
||||
"student_survey_response_3" => 8,
|
||||
"student_survey_response_4" => 8,
|
||||
"student_survey_response_5" => 7,
|
||||
"student_survey_response_6" => 3,
|
||||
"student_survey_response_7" => 4}
|
||||
results = { 'student_survey_response_1' => 11,
|
||||
'student_survey_response_3' => 8,
|
||||
'student_survey_response_4' => 8,
|
||||
'student_survey_response_5' => 7,
|
||||
'student_survey_response_6' => 3,
|
||||
'student_survey_response_7' => 4 }
|
||||
results.each do |key, value|
|
||||
expect(SurveyItemResponse.where(response_id: key).all? do |response|
|
||||
response.grade == value
|
||||
end).to eq true
|
||||
response.grade == value
|
||||
end).to eq true
|
||||
end
|
||||
end
|
||||
|
||||
def assigns_gender_to_responses
|
||||
results = {"student_survey_response_1" => female,
|
||||
"student_survey_response_3" => male,
|
||||
"student_survey_response_4" => non_binary,
|
||||
"student_survey_response_5" => non_binary,
|
||||
"student_survey_response_6" => unknown_gender,
|
||||
"student_survey_response_7" => unknown_gender}
|
||||
results = { 'student_survey_response_1' => female,
|
||||
'student_survey_response_3' => male,
|
||||
'student_survey_response_4' => non_binary,
|
||||
'student_survey_response_5' => non_binary,
|
||||
'student_survey_response_6' => unknown_gender,
|
||||
'student_survey_response_7' => unknown_gender }
|
||||
|
||||
results.each do |key, value|
|
||||
expect(SurveyItemResponse.where(response_id: key).first.gender).to eq value
|
||||
|
|
@ -304,23 +314,36 @@ def assigns_gender_to_responses
|
|||
end
|
||||
|
||||
def assigns_recorded_date_to_student_responses
|
||||
results = {"student_survey_response_1" => "2020-09-30T18:48:50",
|
||||
"student_survey_response_3" => "2021-03-31T09:59:02",
|
||||
"student_survey_response_4" => "2021-03-31T10:00:17",
|
||||
"student_survey_response_5" => "2021-03-31T10:01:36",
|
||||
"student_survey_response_6" => "2021-03-31T10:01:37",
|
||||
"student_survey_response_7" => "2021-03-31T10:01:38"}
|
||||
results = { 'student_survey_response_1' => '2020-09-30T18:48:50',
|
||||
'student_survey_response_3' => '2021-03-31T09:59:02',
|
||||
'student_survey_response_4' => '2021-03-31T10:00:17',
|
||||
'student_survey_response_5' => '2021-03-31T10:01:36',
|
||||
'student_survey_response_6' => '2021-03-31T10:01:37',
|
||||
'student_survey_response_7' => '2021-03-31T10:01:38' }
|
||||
results.each do |key, value|
|
||||
expect(SurveyItemResponse.find_by_response_id(key).recorded_date).to eq Date.parse(value)
|
||||
end
|
||||
end
|
||||
|
||||
def assigns_recorded_date_to_teacher_responses
|
||||
results = {"teacher_survey_response_1" => "2020-10-16 11:09:03",
|
||||
"teacher_survey_response_3" => "2020-12-06 8:36:52",
|
||||
"teacher_survey_response_4" => "2020-12-06 8:51:25",
|
||||
"teacher_survey_response_5" => "2020-12-06 8:55:58"}
|
||||
results = { 'teacher_survey_response_1' => '2020-10-16 11:09:03',
|
||||
'teacher_survey_response_3' => '2020-12-06 8:36:52',
|
||||
'teacher_survey_response_4' => '2020-12-06 8:51:25',
|
||||
'teacher_survey_response_5' => '2020-12-06 8:55:58' }
|
||||
results.each do |key, value|
|
||||
expect(SurveyItemResponse.find_by_response_id(key).recorded_date).to eq Date.parse(value)
|
||||
end
|
||||
end
|
||||
|
||||
def assigns_income_to_responses
|
||||
results = { 'student_survey_response_1' => low_income,
|
||||
'student_survey_response_3' => low_income,
|
||||
'student_survey_response_4' => unknown_income,
|
||||
'student_survey_response_5' => low_income,
|
||||
'student_survey_response_6' => high_income,
|
||||
'student_survey_response_7' => low_income }
|
||||
|
||||
results.each do |key, value|
|
||||
expect(SurveyItemResponse.where(response_id: key).first.income).to eq value
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,25 +1,20 @@
|
|||
require 'rails_helper'
|
||||
require "rails_helper"
|
||||
include AnalyzeHelper
|
||||
include Analyze::Graph
|
||||
describe 'analyze/index' do
|
||||
describe "analyze/index" do
|
||||
subject { Nokogiri::HTML(rendered) }
|
||||
let(:category) { create(:category) }
|
||||
let(:subcategory) { create(:subcategory, category:) }
|
||||
let(:school) { create(:school) }
|
||||
let(:academic_year) { create(:academic_year) }
|
||||
let(:races) do
|
||||
DemographicLoader.load_data(filepath: 'spec/fixtures/sample_demographics.csv')
|
||||
DemographicLoader.load_data(filepath: "spec/fixtures/sample_demographics.csv")
|
||||
Race.all
|
||||
end
|
||||
let(:graph) { StudentsAndTeachers.new }
|
||||
let(:graphs) do
|
||||
[StudentsAndTeachers.new, StudentsByRace.new(races:)]
|
||||
end
|
||||
let(:background) { BackgroundPresenter.new(num_of_columns: graph.columns.count) }
|
||||
let(:selected_races) { races }
|
||||
|
||||
let(:support_for_teaching) do
|
||||
measure = create(:measure, name: 'Support For Teaching Development & Growth', measure_id: '1A-I', subcategory:)
|
||||
measure = create(:measure, name: "Support For Teaching Development & Growth", measure_id: "1A-I", subcategory:)
|
||||
scale = create(:scale, measure:)
|
||||
create(:student_survey_item,
|
||||
scale:,
|
||||
|
|
@ -31,7 +26,7 @@ describe 'analyze/index' do
|
|||
end
|
||||
|
||||
let(:effective_leadership) do
|
||||
measure = create(:measure, name: 'Effective Leadership', measure_id: '1A-II', subcategory:)
|
||||
measure = create(:measure, name: "Effective Leadership", measure_id: "1A-II", subcategory:)
|
||||
scale = create(:scale, measure:)
|
||||
create(:teacher_survey_item,
|
||||
scale:,
|
||||
|
|
@ -43,7 +38,7 @@ describe 'analyze/index' do
|
|||
end
|
||||
|
||||
let(:professional_qualifications) do
|
||||
measure = create(:measure, name: 'Professional Qualifications', measure_id: '1A-III', subcategory:)
|
||||
measure = create(:measure, name: "Professional Qualifications", measure_id: "1A-III", subcategory:)
|
||||
scale = create(:scale, measure:)
|
||||
create(:admin_data_item,
|
||||
scale:,
|
||||
|
|
@ -54,73 +49,30 @@ describe 'analyze/index' do
|
|||
measure
|
||||
end
|
||||
|
||||
let(:sources) do
|
||||
[Analyze::Source::SurveyData.new(slices:)]
|
||||
end
|
||||
|
||||
let(:slices) do
|
||||
students_and_teachers = Analyze::Slice::StudentsAndTeachers.new
|
||||
students_by_group = Analyze::Slice::StudentsByGroup.new(races:, grades:)
|
||||
[students_and_teachers, students_by_group]
|
||||
end
|
||||
|
||||
let(:slice) do
|
||||
slices.first
|
||||
end
|
||||
|
||||
let(:groups) do
|
||||
[Analyze::Group::Race.new, Analyze::Group::Grade.new]
|
||||
end
|
||||
|
||||
let(:group) do
|
||||
groups.first
|
||||
end
|
||||
|
||||
let(:grades) do
|
||||
(1..12).to_a
|
||||
end
|
||||
|
||||
let(:genders) do
|
||||
DemographicLoader.load_data(filepath: 'spec/fixtures/sample_demographics.csv')
|
||||
DemographicLoader.load_data(filepath: "spec/fixtures/sample_demographics.csv")
|
||||
Gender.all
|
||||
end
|
||||
|
||||
let(:selected_genders) do
|
||||
genders
|
||||
end
|
||||
|
||||
let(:selected_grades) do
|
||||
grades
|
||||
end
|
||||
let(:respondent) { create(:respondent, school:, academic_year:) }
|
||||
|
||||
before :each do
|
||||
assign :races, races
|
||||
assign :selected_races, selected_races
|
||||
assign :graph, graph
|
||||
assign :graphs, graphs
|
||||
assign :background, background
|
||||
races
|
||||
category
|
||||
subcategory
|
||||
support_for_teaching
|
||||
effective_leadership
|
||||
professional_qualifications
|
||||
respondent
|
||||
assign :academic_year, academic_year
|
||||
assign :available_academic_years, [academic_year]
|
||||
assign :selected_academic_years, [academic_year]
|
||||
assign :district, create(:district)
|
||||
assign :school, school
|
||||
assign :category, category
|
||||
assign :categories, [category]
|
||||
assign :subcategory, subcategory
|
||||
assign :subcategories, category.subcategories
|
||||
assign :measures, [support_for_teaching, effective_leadership, professional_qualifications]
|
||||
assign :sources, sources
|
||||
assign :source, sources.first
|
||||
assign :groups, groups
|
||||
assign :group, group
|
||||
assign :slice, slice
|
||||
assign :grades, grades
|
||||
assign :selected_grades, selected_grades
|
||||
assign :genders, genders
|
||||
assign :selected_genders, selected_genders
|
||||
create(:respondent, school:, academic_year:)
|
||||
assign :presenter,
|
||||
Analyze::Presenter.new(school:, academic_year:,
|
||||
params: { category: category.category_id, subcategory: subcategory.subcategory_id, races: "american-indian-or-alaskan-native,asian-or-pacific-islander,black-or-african-american,hispanic-or-latinx,middle-eastern,multiracial,race-ethnicity-not-listed,white-or-caucasian", source: "survey-data-only", slice: "students-and-teachers", group: "race", graph: "students-by-race" })
|
||||
assign :background, BackgroundPresenter.new(num_of_columns: 4)
|
||||
end
|
||||
context 'when all the presenters have a nil score' do
|
||||
context "when all the presenters have a nil score" do
|
||||
before do
|
||||
render
|
||||
end
|
||||
|
|
@ -139,63 +91,72 @@ describe 'analyze/index' do
|
|||
# ]
|
||||
# end
|
||||
|
||||
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
|
||||
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 27
|
||||
|
||||
displayed_variance_rows = subject.css('[data-for-measure-id]')
|
||||
expect(displayed_variance_rows.first.attribute('data-for-measure-id').value).to eq '1A-I'
|
||||
displayed_variance_rows = subject.css("[data-for-measure-id]")
|
||||
expect(displayed_variance_rows.first.attribute("data-for-measure-id").value).to eq "1A-I"
|
||||
|
||||
displayed_academic_years = subject.css('[data-for-academic-year]')
|
||||
displayed_academic_years = subject.css("[data-for-academic-year]")
|
||||
expect(displayed_academic_years.count).to eq 0
|
||||
|
||||
displayed_variance_labels = subject.css('[data-grouped-bar-label]')
|
||||
expect(displayed_variance_labels.count).to eq 18
|
||||
expect(displayed_variance_labels.first.inner_text).to include 'All'
|
||||
expect(displayed_variance_labels[1].inner_text).to include 'Students'
|
||||
expect(displayed_variance_labels.last.inner_text).to include 'Data'
|
||||
displayed_variance_labels = subject.css("[data-grouped-bar-label]")
|
||||
|
||||
expect(displayed_variance_labels.count).to eq 39
|
||||
expect(displayed_variance_labels.first.inner_text).to include "American"
|
||||
expect(displayed_variance_labels.text).to include "Indian"
|
||||
expect(displayed_variance_labels.text).to include "Asian"
|
||||
expect(displayed_variance_labels.text).to include "Black"
|
||||
expect(displayed_variance_labels.text).to include "White"
|
||||
expect(displayed_variance_labels.text).to include "Hispanic"
|
||||
expect(displayed_variance_labels.text).to include "Middle"
|
||||
expect(displayed_variance_labels.text).to include "Eastern"
|
||||
expect(displayed_variance_labels.text).to include "Multiracial"
|
||||
expect(displayed_variance_labels.text).to include "Not"
|
||||
expect(displayed_variance_labels.text).to include "Listed"
|
||||
end
|
||||
|
||||
it 'displays all measures for the first subcategory' do
|
||||
expect(rendered).to have_text '1A-I'
|
||||
expect(rendered).to have_text '1A-II'
|
||||
expect(rendered).to have_text '1A-III'
|
||||
it "displays all measures for the first subcategory" do
|
||||
expect(rendered).to have_text "1A-I"
|
||||
expect(rendered).to have_text "1A-II"
|
||||
expect(rendered).to have_text "1A-III"
|
||||
end
|
||||
|
||||
it 'displays user interface controls' do
|
||||
expect(subject).to have_text 'Focus Area'
|
||||
expect(subject).to have_css '#select-category'
|
||||
expect(subject).to have_css '#select-subcategory'
|
||||
it "displays user interface controls" do
|
||||
expect(subject).to have_text "Focus Area"
|
||||
expect(subject).to have_css "#select-category"
|
||||
expect(subject).to have_css "#select-subcategory"
|
||||
expect(subject).to have_css "##{academic_year.range}"
|
||||
end
|
||||
|
||||
it 'displays disabled checkboxes for years that dont have data' do
|
||||
it "displays disabled checkboxes for years that dont have data" do
|
||||
year_checkbox = subject.css("##{academic_year.range}").first
|
||||
expect(year_checkbox.name).to eq 'input'
|
||||
expect(academic_year.range).to eq '2050-51'
|
||||
expect(year_checkbox).to have_attribute 'disabled'
|
||||
expect(year_checkbox.name).to eq "input"
|
||||
expect(academic_year.range).to eq "2050-51"
|
||||
expect(year_checkbox).to have_attribute "disabled"
|
||||
end
|
||||
|
||||
it 'displays a radio box selector for each type of data filter' do
|
||||
expect(subject).to have_css '#students-and-teachers'
|
||||
expect(subject).to have_css '#students-by-group'
|
||||
it "displays a radio box selector for each type of data filter" do
|
||||
expect(subject).to have_css "#students-and-teachers"
|
||||
expect(subject).to have_css "#students-by-group"
|
||||
end
|
||||
|
||||
it 'displays a checkbox for each race designation' do
|
||||
race_slugs = %w[american-indian-or-alaskan-native asian-or-pacific-islander black-or-african-american
|
||||
hispanic-or-latinx middle-eastern multiracial race-ethnicity-not-listed white-or-caucasian]
|
||||
it "displays a checkbox for each race designation" do
|
||||
race_slugs = %w[race-american-indian-or-alaskan-native race-asian-or-pacific-islander race-black-or-african-american
|
||||
race-hispanic-or-latinx race-middle-eastern race-multiracial race-race-ethnicity-not-listed race-white-or-caucasian]
|
||||
race_slugs.each do |slug|
|
||||
expect(subject).to have_css("//input[@type='checkbox'][@id='#{slug}']")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when presenters have a displayable score' do
|
||||
context "when presenters have a displayable score" do
|
||||
before do
|
||||
render
|
||||
end
|
||||
|
||||
context 'when displaying a student and teacher graph' do
|
||||
context "when displaying a student and teacher graph" do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3931,12 +3931,12 @@ saxes@^6.0.0:
|
|||
dependencies:
|
||||
xmlchars "^2.2.0"
|
||||
|
||||
semver@6.3.1, semver@^6.0.0, semver@^6.3.0, semver@^6.3.1:
|
||||
semver@^6.0.0, semver@^6.3.0, semver@^6.3.1:
|
||||
version "6.3.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
||||
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
||||
|
||||
semver@^7.5.3:
|
||||
semver@^7.5.2, semver@^7.5.3:
|
||||
version "7.5.4"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
|
||||
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
|
||||
|
|
@ -4381,11 +4381,6 @@ xml-name-validator@^4.0.0:
|
|||
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835"
|
||||
integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==
|
||||
|
||||
xmlchars@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
|
||||
integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
|
||||
|
||||
y18n@^5.0.5:
|
||||
version "5.0.8"
|
||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue